mirror of
https://github.com/ENSL/NS.git
synced 2024-11-21 20:21:14 +00:00
Marine Relocation
* The AI Commander can now relocate to a nearby empty hive at the start of a match, or if the current base is almost lost
This commit is contained in:
parent
c2e41c2011
commit
91231ac069
12 changed files with 602 additions and 18 deletions
|
@ -469,6 +469,32 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
}
|
||||
|
||||
int NumPlayersOnTeam = AITAC_GetNumActivePlayersOnTeam(pBot->Player->GetTeam());
|
||||
|
||||
if (AICOMM_ShouldCommanderRelocate(pBot))
|
||||
{
|
||||
Vector RelocationPoint = pBot->RelocationSpot;
|
||||
|
||||
int DesiredRelocationPlayers = imini(3, (int)ceilf((float)NumPlayersOnTeam * 0.5f));
|
||||
|
||||
const AvHAIHiveDefinition* RelocationHive = AITAC_GetHiveNearestLocation(RelocationPoint);
|
||||
|
||||
if (RelocationHive)
|
||||
{
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, RelocationHive->HiveEdict, ORDERPURPOSE_SECURE_HIVE);
|
||||
|
||||
if (NumAssignedPlayers < DesiredRelocationPlayers)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, RelocationHive->FloorLocation);
|
||||
|
||||
if (!FNullEnt(NewAssignee))
|
||||
{
|
||||
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, RelocationHive->HiveEdict, ORDERPURPOSE_SECURE_HIVE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DesiredPlayers = imini(2, (int)ceilf((float)NumPlayersOnTeam *0.5f));
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
@ -498,8 +524,6 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
|
||||
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot))
|
||||
{
|
||||
|
||||
|
||||
DeployableSearchFilter ResNodeFilter;
|
||||
ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
ResNodeFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
|
@ -2164,7 +2188,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
|
|||
AvHAIBuildableStructure ExistingPG;
|
||||
AvHAIBuildableStructure ExistingTF;
|
||||
|
||||
Vector OutpostLocation = (ExistingStructure.IsValid() && ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY) ? ExistingStructure.Location : HiveToSecure->FloorLocation;
|
||||
Vector OutpostLocation = (ExistingStructure.IsValid() && (ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY || ExistingStructure.Purpose == STRUCTURE_PURPOSE_BASE)) ? ExistingStructure.Location : HiveToSecure->FloorLocation;
|
||||
|
||||
if (HiveToSecure->HiveResNodeRef && HiveToSecure->HiveResNodeRef->OwningTeam == TEAM_IND)
|
||||
{
|
||||
|
@ -2375,6 +2399,23 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair)
|
|||
|
||||
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot)
|
||||
{
|
||||
DeployableSearchFilter OldStuffFilter;
|
||||
OldStuffFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
OldStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
OldStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
OldStuffFilter.MinSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
|
||||
OldStuffFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE;
|
||||
|
||||
Vector BaseLocation = (!vIsZero(pBot->RelocationSpot)) ? pBot->RelocationSpot : AITAC_GetTeamStartingLocation(pBot->Player->GetTeam());
|
||||
|
||||
AvHAIBuildableStructure OldBaseStructure = AITAC_FindFurthestDeployableFromLocation(BaseLocation, &OldStuffFilter);
|
||||
|
||||
if (OldBaseStructure.IsValid())
|
||||
{
|
||||
return AICOMM_RecycleStructure(pBot, &OldBaseStructure);
|
||||
}
|
||||
|
||||
|
||||
DeployableSearchFilter UnreachableFilter;
|
||||
UnreachableFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
UnreachableFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
|
@ -3252,8 +3293,36 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot)
|
|||
|
||||
AICOMM_UpdatePlayerOrders(pBot);
|
||||
|
||||
if (AICOMM_CheckForNextRecycleAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextSupportAction(pBot)) { return; }
|
||||
|
||||
if (AICOMM_ShouldCommanderRelocate(pBot))
|
||||
{
|
||||
if (vIsZero(pBot->RelocationSpot) || !AITAC_IsRelocationPointStillValid(pBot->Player->GetTeam(), pBot->RelocationSpot))
|
||||
{
|
||||
pBot->RelocationSpot = AITAC_FindNewTeamRelocationPoint(pBot->Player->GetTeam());
|
||||
|
||||
if (!vIsZero(pBot->RelocationSpot))
|
||||
{
|
||||
const AvHAIHiveDefinition* RelocationHive = AITAC_GetHiveNearestLocation(pBot->RelocationSpot);
|
||||
|
||||
if (RelocationHive)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "We're relocating to %s, lads", RelocationHive->HiveName);
|
||||
BotSay(pBot, true, 1.0f, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!vIsZero(pBot->RelocationSpot))
|
||||
{
|
||||
if (AICOMM_CheckForNextRelocationAction(pBot)) { return; }
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (AICOMM_CheckForNextRecycleAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextBuildAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextResearchAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextSupplyAction(pBot)) { return; }
|
||||
|
@ -3343,6 +3412,9 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
|
|||
|
||||
const vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
|
||||
|
||||
Vector TeamStartLocation = AITAC_GetTeamStartingLocation(CommanderTeam);
|
||||
Vector RelocationPoint = CommanderBot->RelocationSpot;
|
||||
|
||||
for (auto it = Hives.begin(); it != Hives.end(); it++)
|
||||
{
|
||||
const AvHAIHiveDefinition* Hive = (*it);
|
||||
|
@ -3351,6 +3423,16 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
|
|||
|
||||
if (AICOMM_IsHiveFullySecured(CommanderBot, Hive, false)) { continue; }
|
||||
|
||||
// If the hive is close enough that we could siege it from our base, then don't bother securing it
|
||||
if (vDist2DSq(TeamStartLocation, Hive->FloorLocation) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { continue; }
|
||||
|
||||
// If the hive is close enough that we could siege it from our base, then don't bother securing it
|
||||
if (!vIsZero(CommanderBot->RelocationSpot))
|
||||
{
|
||||
// Likewise, don't secure if we're planning to move there. We will build the base instead
|
||||
if (vDist2DSq(CommanderBot->RelocationSpot, Hive->FloorLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) { continue; }
|
||||
}
|
||||
|
||||
Vector SecureLocation = Hive->FloorLocation;
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
|
@ -3364,7 +3446,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
|
|||
|
||||
AvHAIBuildableStructure ExistingStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
if (ExistingStructure.IsValid() && ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY)
|
||||
if (ExistingStructure.IsValid() && (ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY || ExistingStructure.Purpose == STRUCTURE_PURPOSE_BASE))
|
||||
{
|
||||
SecureLocation = ExistingStructure.Location;
|
||||
}
|
||||
|
@ -3615,4 +3697,230 @@ void AICOMM_ReceiveChatRequest(AvHAIPlayer* Commander, edict_t* Requestor, const
|
|||
{
|
||||
Commander->ActiveRequests.push_back(NewRequest);
|
||||
}
|
||||
}
|
||||
|
||||
bool AICOMM_ShouldCommanderRelocate(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeamNumber Team = pBot->Player->GetTeam();
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(Team);
|
||||
|
||||
edict_t* CurrentCommChair = AITAC_GetCommChair(Team);
|
||||
|
||||
if (FNullEnt(CurrentCommChair)) { return false; }
|
||||
|
||||
Vector CurrentTeamStartLocation = CurrentCommChair->v.origin;
|
||||
|
||||
DeployableSearchFilter BaseStructureFilter;
|
||||
BaseStructureFilter.DeployableTeam = Team;
|
||||
BaseStructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
BaseStructureFilter.DeployableTypes = (STRUCTURE_MARINE_OBSERVATORY | STRUCTURE_MARINE_ARMSLAB | STRUCTURE_MARINE_ADVARMOURY | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
|
||||
BaseStructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
BaseStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
vector<AvHAIBuildableStructure> AllBaseStructures = AITAC_FindAllDeployables(CurrentTeamStartLocation, &BaseStructureFilter);
|
||||
|
||||
bool bBaseWellEstablished = false;
|
||||
bool bObservatoryExists = false;
|
||||
|
||||
for (auto it = AllBaseStructures.begin(); it != AllBaseStructures.end(); it++)
|
||||
{
|
||||
if (it->StructureType == STRUCTURE_MARINE_OBSERVATORY)
|
||||
{
|
||||
bObservatoryExists = true;
|
||||
}
|
||||
|
||||
bBaseWellEstablished = true;
|
||||
}
|
||||
|
||||
// If we can beacon then don't relocate
|
||||
if (bBaseWellEstablished && bObservatoryExists) { return false; }
|
||||
|
||||
// If our base is well established, then relocate if the base is overrun and lost
|
||||
if (bBaseWellEstablished)
|
||||
{
|
||||
DeployableSearchFilter BaseStructureFilter;
|
||||
BaseStructureFilter.DeployableTypes = (STRUCTURE_MARINE_INFANTRYPORTAL | STRUCTURE_MARINE_COMMCHAIR);
|
||||
BaseStructureFilter.DeployableTeam = Team;
|
||||
BaseStructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
BaseStructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
vector<AvHAIBuildableStructure> BaseStructures = AITAC_FindAllDeployables(CurrentTeamStartLocation, &BaseStructureFilter);
|
||||
|
||||
bool bHasInfantryPortals = false;
|
||||
bool bBaseUnderAttack = false;
|
||||
|
||||
for (auto it = BaseStructures.begin(); it != BaseStructures.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure ThisStructure = (*it);
|
||||
|
||||
if (ThisStructure.StructureStatusFlags & STRUCTURE_STATUS_UNDERATTACK)
|
||||
{
|
||||
bBaseUnderAttack = true;
|
||||
}
|
||||
|
||||
if (ThisStructure.StructureType == STRUCTURE_MARINE_INFANTRYPORTAL)
|
||||
{
|
||||
bHasInfantryPortals = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bBaseUnderAttack && bHasInfantryPortals) { return false; }
|
||||
|
||||
int EnemyStrength = 0;
|
||||
int DefenderStrength = AITAC_GetNumPlayersOfTeamInArea(Team, CurrentTeamStartLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
||||
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
|
||||
{
|
||||
EnemyStrength = AITAC_GetNumPlayersOfTeamInArea(EnemyTeam, CurrentTeamStartLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER);
|
||||
}
|
||||
else
|
||||
{
|
||||
int NumSkulks = AITAC_GetNumPlayersOfTeamAndClassInArea(EnemyTeam, CurrentTeamStartLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_ALIEN_PLAYER1);
|
||||
int NumFades = AITAC_GetNumPlayersOfTeamAndClassInArea(EnemyTeam, CurrentTeamStartLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_ALIEN_PLAYER4);
|
||||
int NumOnos = AITAC_GetNumPlayersOfTeamAndClassInArea(EnemyTeam, CurrentTeamStartLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_ALIEN_PLAYER5);
|
||||
|
||||
EnemyStrength = NumSkulks + (NumFades * 2) + (NumOnos * 2);
|
||||
}
|
||||
|
||||
if (EnemyStrength >= 3 && EnemyStrength >= DefenderStrength * 3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't relocate if we're already located in a hive
|
||||
const AvHAIHiveDefinition* NearestHive = AITAC_GetHiveNearestLocation(CurrentTeamStartLocation);
|
||||
|
||||
if (NearestHive && NearestHive->Status == HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, CurrentTeamStartLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vIsZero(pBot->RelocationSpot))
|
||||
{
|
||||
if (AITAC_IsRelocationCompleted(Team, pBot->RelocationSpot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DeployableSearchFilter InfPortalFilter;
|
||||
InfPortalFilter.DeployableTeam = Team;
|
||||
InfPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
|
||||
InfPortalFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
InfPortalFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
InfPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
if (AITAC_DeployableExistsAtLocation(pBot->RelocationSpot, &InfPortalFilter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the match has been going on for a minute and we haven't made any progress in relocation then give up, or we risk losing everything
|
||||
if (AIMGR_GetMatchLength() > 60.0f)
|
||||
{
|
||||
return AITAC_GetNumPlayersOfTeamInArea(Team, pBot->RelocationSpot, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
|
||||
Vector RelocationPoint = pBot->RelocationSpot;
|
||||
|
||||
if (vIsZero(RelocationPoint)) { return false; }
|
||||
|
||||
edict_t* CurrentCommChair = AITAC_GetCommChair(BotTeam);
|
||||
|
||||
if (FNullEnt(CurrentCommChair)) { return false; }
|
||||
|
||||
DeployableSearchFilter OrigInfPortalFilter;
|
||||
OrigInfPortalFilter.DeployableTeam = BotTeam;
|
||||
OrigInfPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
|
||||
OrigInfPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
// First ensure we have one infantry portal in our starting location, in case it goes horribly wrong
|
||||
if (!AITAC_DeployableExistsAtLocation(CurrentCommChair->v.origin, &OrigInfPortalFilter))
|
||||
{
|
||||
return AICOMM_BuildInfantryPortal(pBot, CurrentCommChair);
|
||||
}
|
||||
|
||||
// Don't do anything more if we don't have anyone at the relocation point yet, but we can drop RTs if needed
|
||||
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, RelocationPoint, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER) == 0)
|
||||
{
|
||||
const AvHAIResourceNode* CappableNode = AICOMM_GetNearestResourceNodeCapOpportunity(BotTeam, CurrentCommChair->v.origin);
|
||||
|
||||
if (CappableNode)
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, CappableNode->Location);
|
||||
|
||||
if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kResourceTowerCost) + 10) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DeployableSearchFilter NewBaseFilter;
|
||||
NewBaseFilter.DeployableTeam = BotTeam;
|
||||
NewBaseFilter.DeployableTypes = STRUCTURE_MARINE_COMMCHAIR;
|
||||
NewBaseFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
NewBaseFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure RelocationCommChair = AITAC_FindClosestDeployableToLocation(RelocationPoint, &NewBaseFilter);
|
||||
|
||||
if (!RelocationCommChair.IsValid())
|
||||
{
|
||||
Vector BuildPoint = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), RelocationPoint, UTIL_MetresToGoldSrcUnits(2.0f));
|
||||
|
||||
if (vIsZero(BuildPoint))
|
||||
{
|
||||
BuildPoint = UTIL_ProjectPointToNavmesh(RelocationPoint, Vector(500.0f, 500.0f, 500.0f), GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
}
|
||||
|
||||
if (!vIsZero(BuildPoint))
|
||||
{
|
||||
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_COMMCHAIR, BuildPoint, STRUCTURE_PURPOSE_BASE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(RelocationCommChair.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { return false; }
|
||||
|
||||
NewBaseFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
|
||||
NewBaseFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(8.0f);
|
||||
NewBaseFilter.IncludeStatusFlags = STRUCTURE_STATUS_NONE;
|
||||
|
||||
int NumInfPortals = AITAC_GetNumDeployablesNearLocation(RelocationCommChair.Location, &NewBaseFilter);
|
||||
|
||||
if (NumInfPortals < 2)
|
||||
{
|
||||
return AICOMM_BuildInfantryPortal(pBot, RelocationCommChair.edict);
|
||||
}
|
||||
|
||||
DeployableSearchFilter OldStuffFilter;
|
||||
OldStuffFilter.DeployableTeam = BotTeam;
|
||||
OldStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
OldStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
OldStuffFilter.MinSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
|
||||
OldStuffFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE;
|
||||
|
||||
vector<AvHAIBuildableStructure> OldBaseStructures = AITAC_FindAllDeployables(pBot->RelocationSpot, &OldStuffFilter);
|
||||
|
||||
for (auto it = OldBaseStructures.begin(); it != OldBaseStructures.end(); it++)
|
||||
{
|
||||
if (it->edict != CurrentCommChair)
|
||||
{
|
||||
return AICOMM_RecycleStructure(pBot, &(*it));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -41,6 +41,7 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
|
|||
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot);
|
||||
|
||||
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
|
||||
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
|
||||
|
@ -71,4 +72,6 @@ bool AICOMM_ShouldBeacon(AvHAIPlayer* pBot);
|
|||
|
||||
void AICOMM_ReceiveChatRequest(AvHAIPlayer* Commander, edict_t* Requestor, const char* Request);
|
||||
|
||||
bool AICOMM_ShouldCommanderRelocate(AvHAIPlayer* pBot);
|
||||
|
||||
#endif // AVH_AI_COMMANDER_H
|
|
@ -12,6 +12,8 @@ BotFillTiming CurrentBotFillTiming = FILLTIMING_ALLHUMANS;
|
|||
|
||||
float MaxAIMatchTimeMinutes = 90.0f;
|
||||
|
||||
bool bRelocationAllowed = false;
|
||||
|
||||
std::unordered_map<std::string, TeamSizeDefinitions> TeamSizeMap;
|
||||
|
||||
bot_skill BotSkillLevels[4];
|
||||
|
@ -90,6 +92,11 @@ bool CONFIG_IsOnosAllowed()
|
|||
return avh_botallowonos.value > 0;
|
||||
}
|
||||
|
||||
bool CONFIG_IsRelocationAllowed()
|
||||
{
|
||||
return bRelocationAllowed;
|
||||
}
|
||||
|
||||
float CONFIG_GetMaxStuckTime()
|
||||
{
|
||||
return avh_botmaxstucktime.value;
|
||||
|
@ -328,6 +335,14 @@ void CONFIG_ParseConfigFile()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!stricmp(keyChar, "AllowRelocation"))
|
||||
{
|
||||
const char* ValueChar = value.c_str();
|
||||
bRelocationAllowed = (!stricmp(ValueChar, "True") || !stricmp(ValueChar, "T"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!stricmp(keyChar, "MaxAIMatchTime"))
|
||||
{
|
||||
float MaxMinutes = std::stof(value.c_str());
|
||||
|
|
|
@ -43,6 +43,8 @@ bool CONFIG_IsLerkAllowed();
|
|||
bool CONFIG_IsFadeAllowed();
|
||||
bool CONFIG_IsOnosAllowed();
|
||||
|
||||
bool CONFIG_IsRelocationAllowed();
|
||||
|
||||
// Returns the max time a bot is allowed to be stuck before suiciding (0 means forever)
|
||||
float CONFIG_GetMaxStuckTime();
|
||||
|
||||
|
|
|
@ -814,6 +814,8 @@ typedef struct AVH_AI_PLAYER
|
|||
|
||||
int DebugValue = 0; // Used for debugging the bot
|
||||
|
||||
Vector RelocationSpot = ZERO_VECTOR; // If the bot is commanding and wants to relocate, then this is where they plan to go
|
||||
|
||||
} AvHAIPlayer;
|
||||
|
||||
typedef struct _AVH_AI_SQUAD
|
||||
|
|
|
@ -5010,7 +5010,6 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
|
|||
}
|
||||
|
||||
BotMoveLookAt(pBot, LookLocation, true);
|
||||
//BotDirectLookAt(pBot, LookLocation);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1926,6 +1926,11 @@ void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole)
|
|||
AITASK_ClearBotTask(pBot, &pBot->SecondaryBotTask);
|
||||
|
||||
pBot->BotRole = NewRole;
|
||||
|
||||
if (NewRole != BOT_ROLE_COMMAND && IsPlayerCommander(pBot->Edict))
|
||||
{
|
||||
BotStopCommanderMode(pBot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2072,10 +2077,37 @@ bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot)
|
|||
|
||||
AvHPlayer* CurrentCommander = BotTeam->GetCommanderPlayer();
|
||||
|
||||
// Don't go commander if we already have one, and it's not us
|
||||
if (CurrentCommander)
|
||||
{
|
||||
return CurrentCommander == pBot->Player;
|
||||
// Don't go commander if we already have one, and it's not us
|
||||
if (CurrentCommander != pBot->Player) { return false; }
|
||||
|
||||
if (!AICOMM_ShouldCommanderRelocate(pBot))
|
||||
{
|
||||
// If it is us, then check if we're relocating. If we are, and we're in the old chair, then we should stop commanding
|
||||
|
||||
Vector TeamRelocationPoint = pBot->RelocationSpot;
|
||||
|
||||
if (vIsZero(TeamRelocationPoint)) { return true; }
|
||||
|
||||
if (AITAC_IsRelocationCompleted(BotTeamNumber, TeamRelocationPoint))
|
||||
{
|
||||
DeployableSearchFilter CommChairFilter;
|
||||
CommChairFilter.DeployableTeam = BotTeamNumber;
|
||||
CommChairFilter.DeployableTypes = STRUCTURE_MARINE_COMMCHAIR;
|
||||
CommChairFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
CommChairFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure RelocationCommChair = AITAC_FindClosestDeployableToLocation(pBot->RelocationSpot, &CommChairFilter);
|
||||
|
||||
// Only command if we're in the relocation chair, otherwise don't
|
||||
return !RelocationCommChair.IsValid() || RelocationCommChair.edict == AITAC_GetCommChair(BotTeamNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't go commander if there is another bot already taking command
|
||||
|
@ -6793,6 +6825,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
|
||||
vector<AvHAIBuildableStructure> EnemyStructures = AITAC_FindAllDeployables(ThisHive->FloorLocation, &EnemyStuffFilter);
|
||||
|
||||
bool bIsRelocationHive = false; // If true, the marines have relocated here so don't try to retake it: requires a base attack task instead
|
||||
|
||||
// Enemy hasn't built anything here, so doesn't need clearing
|
||||
if (ThisHive->OwningTeam != EnemyTeam && EnemyStructures.size() == 0) { continue; }
|
||||
|
||||
|
@ -6802,6 +6836,9 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
{
|
||||
switch (StructureIt->StructureType)
|
||||
{
|
||||
case STRUCTURE_MARINE_INFANTRYPORTAL:
|
||||
bIsRelocationHive = true;
|
||||
break;
|
||||
case STRUCTURE_MARINE_PHASEGATE:
|
||||
ThisStrength += 2;
|
||||
break;
|
||||
|
@ -6819,7 +6856,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
}
|
||||
}
|
||||
|
||||
if (!HiveToSecure || ThisStrength < MaxHiveStrength)
|
||||
if (!bIsRelocationHive && (!HiveToSecure || ThisStrength < MaxHiveStrength))
|
||||
{
|
||||
HiveToSecure = ThisHive;
|
||||
MaxHiveStrength = ThisStrength;
|
||||
|
@ -6853,8 +6890,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FIND ANY LAST ENEMIES TO KILL AND END GAME
|
||||
|
||||
|
@ -7415,19 +7451,19 @@ void AIPlayerSetWantsAndNeedsAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_DEFENCE) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_DEFENCE))
|
||||
{
|
||||
AITASK_SetEvolveTask(pBot, Task, pBot->CurrentFloorPosition, AlienGetDesiredUpgrade(pBot, HIVE_TECH_DEFENCE), true);
|
||||
pBot->Edict->v.impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_DEFENCE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_MOVEMENT) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_MOVEMENT))
|
||||
{
|
||||
AITASK_SetEvolveTask(pBot, Task, pBot->CurrentFloorPosition, AlienGetDesiredUpgrade(pBot, HIVE_TECH_MOVEMENT), true);
|
||||
pBot->Edict->v.impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_MOVEMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_SENSORY) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_SENSORY))
|
||||
{
|
||||
AITASK_SetEvolveTask(pBot, Task, pBot->CurrentFloorPosition, AlienGetDesiredUpgrade(pBot, HIVE_TECH_SENSORY), true);
|
||||
pBot->Edict->v.impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_SENSORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1590,4 +1590,9 @@ void AIMGR_SetFrameDelta(float NewValue)
|
|||
float AIMGR_GetFrameDelta()
|
||||
{
|
||||
return CurrentFrameDelta;
|
||||
}
|
||||
|
||||
float AIMGR_GetMatchLength()
|
||||
{
|
||||
return (gpGlobals->time - GetGameRules()->GetTimeGameStarted());
|
||||
}
|
|
@ -97,6 +97,8 @@ AvHClassType AIMGR_GetTeamType(const AvHTeamNumber Team);
|
|||
AvHTeamNumber AIMGR_GetTeamANumber();
|
||||
AvHTeamNumber AIMGR_GetTeamBNumber();
|
||||
|
||||
float AIMGR_GetMatchLength();
|
||||
|
||||
AvHTeam* AIMGR_GetTeamRef(const AvHTeamNumber Team);
|
||||
|
||||
// Returns all NS AI players. Does not include third-party bots
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "AvHAIConstants.h"
|
||||
#include "AvHAIPlayerManager.h"
|
||||
#include "AvHAIConfig.h"
|
||||
#include "AvHAICommander.h"
|
||||
|
||||
#include "AvHGamerules.h"
|
||||
#include "AvHServerUtil.h"
|
||||
|
@ -54,6 +55,9 @@ unsigned int ItemRefreshFrame = 0;
|
|||
Vector TeamAStartingLocation = ZERO_VECTOR;
|
||||
Vector TeamBStartingLocation = ZERO_VECTOR;
|
||||
|
||||
Vector TeamARelocationPoint = ZERO_VECTOR;
|
||||
Vector TeamBRelocationPoint = ZERO_VECTOR;
|
||||
|
||||
extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular)
|
||||
extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles
|
||||
|
||||
|
@ -1184,6 +1188,27 @@ void AITAC_RefreshHiveData()
|
|||
}
|
||||
}
|
||||
|
||||
Vector AITAC_GetTeamRelocationPoint(AvHTeamNumber Team)
|
||||
{
|
||||
Vector CurrentRelocationPoint = (Team == GetGameRules()->GetTeamANumber()) ? TeamARelocationPoint : TeamBRelocationPoint;
|
||||
|
||||
if (!AITAC_IsRelocationPointStillValid(Team, CurrentRelocationPoint))
|
||||
{
|
||||
CurrentRelocationPoint = AITAC_FindNewTeamRelocationPoint(Team);
|
||||
}
|
||||
|
||||
if (Team == GetGameRules()->GetTeamANumber())
|
||||
{
|
||||
TeamARelocationPoint = CurrentRelocationPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
TeamBRelocationPoint = CurrentRelocationPoint;
|
||||
}
|
||||
|
||||
return (Team == GetGameRules()->GetTeamANumber()) ? TeamARelocationPoint : TeamBRelocationPoint;
|
||||
}
|
||||
|
||||
Vector AITAC_GetTeamStartingLocation(AvHTeamNumber Team)
|
||||
{
|
||||
if (vIsZero(TeamAStartingLocation) || vIsZero(TeamBStartingLocation))
|
||||
|
@ -4379,7 +4404,6 @@ edict_t* AITAC_GetCommChair(AvHTeamNumber Team)
|
|||
ChairFilter.DeployableTypes = STRUCTURE_MARINE_COMMCHAIR;
|
||||
ChairFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
ChairFilter.DeployableTeam = Team;
|
||||
ChairFilter.ReachabilityTeam = TEAM_IND;
|
||||
|
||||
vector<AvHAIBuildableStructure> CommChairs = AITAC_FindAllDeployables(ZERO_VECTOR, &ChairFilter);
|
||||
|
||||
|
@ -4391,8 +4415,10 @@ edict_t* AITAC_GetCommChair(AvHTeamNumber Team)
|
|||
{
|
||||
AvHCommandStation* ChairRef = dynamic_cast<AvHCommandStation*>((*it).EntityRef);
|
||||
|
||||
if (!ChairRef) { continue; }
|
||||
|
||||
// Idle animation will be 3 if the chair is in use (closed animation). See AvHCommandStation::GetIdleAnimation
|
||||
if (ChairRef && ChairRef->GetIdleAnimation() == 3)
|
||||
if (ChairRef->GetIdleAnimation() == 3)
|
||||
{
|
||||
MainCommChair = ChairRef->edict();
|
||||
}
|
||||
|
@ -5750,4 +5776,157 @@ Vector AITAC_GetGatherLocationForSquad(AvHAISquad* Squad)
|
|||
}
|
||||
|
||||
return ZERO_VECTOR;
|
||||
}
|
||||
|
||||
Vector AITAC_FindNewTeamRelocationPoint(AvHTeamNumber Team)
|
||||
{
|
||||
if (!CONFIG_IsRelocationAllowed()) { return ZERO_VECTOR; }
|
||||
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(Team);
|
||||
|
||||
// Only relocate if:
|
||||
// There is a hive to relocate to with a marine ready to build
|
||||
// The current base is overrun and lost
|
||||
// Or we decide we want to, and the current base isn't too built up
|
||||
|
||||
Vector CurrentTeamStartLocation = AITAC_GetTeamStartingLocation(Team);
|
||||
|
||||
const AvHAIHiveDefinition* RelocationHive = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
|
||||
|
||||
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
|
||||
{
|
||||
const AvHAIHiveDefinition* ThisHive = (*it);
|
||||
|
||||
// Obviously don't relocate to an active enemy hive...
|
||||
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
|
||||
// Don't relocate if we're already located close to this hive
|
||||
if (vDist2DSq(CurrentTeamStartLocation, ThisHive->FloorLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) { continue; }
|
||||
|
||||
// Don't relocate if the enemy has a foothold here
|
||||
DeployableSearchFilter EnemyStuff;
|
||||
EnemyStuff.DeployableTeam = EnemyTeam;
|
||||
EnemyStuff.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
EnemyStuff.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
EnemyStuff.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_INFANTRYPORTAL | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_ALIEN_OFFENCECHAMBER);
|
||||
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuff)) { continue; }
|
||||
|
||||
const AvHAIHiveDefinition* NearestEnemyHive = AITAC_GetActiveHiveNearestLocation(EnemyTeam, ThisHive->FloorLocation);
|
||||
|
||||
float ThisDist = 0.0f;
|
||||
|
||||
// Either pick an empty hive furthest from the nearest enemy hive (if they have one)
|
||||
// Or the closest one to us if the enemy don't (e.g. it's MvM)
|
||||
|
||||
if (NearestEnemyHive)
|
||||
{
|
||||
ThisDist = vDist2DSq(NearestEnemyHive->FloorLocation, ThisHive->FloorLocation);
|
||||
|
||||
if (!RelocationHive || ThisDist > MinDist)
|
||||
{
|
||||
RelocationHive = ThisHive;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThisDist = vDist2DSq(CurrentTeamStartLocation, ThisHive->FloorLocation);
|
||||
|
||||
if (!RelocationHive || ThisDist < MinDist)
|
||||
{
|
||||
RelocationHive = ThisHive;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// No hives to relocate to
|
||||
if (!RelocationHive) { return ZERO_VECTOR; }
|
||||
|
||||
return RelocationHive->FloorLocation;
|
||||
|
||||
}
|
||||
|
||||
bool AITAC_IsRelocationPointStillValid(AvHTeamNumber RelocationTeam, Vector RelocationPoint)
|
||||
{
|
||||
if (vIsZero(RelocationPoint)) { return false; }
|
||||
|
||||
const AvHAIHiveDefinition* ThisHive = AITAC_GetHiveNearestLocation(RelocationPoint);
|
||||
|
||||
// Obviously don't relocate to an active enemy hive...
|
||||
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { return false; }
|
||||
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(RelocationTeam);
|
||||
|
||||
// Don't relocate if the enemy has a foothold here
|
||||
DeployableSearchFilter EnemyStuff;
|
||||
EnemyStuff.DeployableTeam = EnemyTeam;
|
||||
EnemyStuff.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
EnemyStuff.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
EnemyStuff.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_INFANTRYPORTAL | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_ALIEN_OFFENCECHAMBER);
|
||||
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuff)) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AITAC_IsRelocationCompleted(AvHTeamNumber RelocationTeam, Vector RelocationPoint)
|
||||
{
|
||||
if (vIsZero(RelocationPoint)) { return true; }
|
||||
|
||||
// Don't relocate if the enemy has a foothold here
|
||||
DeployableSearchFilter BaseStuffFilter;
|
||||
BaseStuffFilter.DeployableTeam = RelocationTeam;
|
||||
BaseStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
BaseStuffFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
BaseStuffFilter.DeployableTypes = (STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_INFANTRYPORTAL);
|
||||
BaseStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
edict_t* RelocationChair = nullptr;
|
||||
edict_t* CurrentCommChair = AITAC_GetCommChair(RelocationTeam);
|
||||
int NumInfPortals = 0;
|
||||
|
||||
vector<AvHAIBuildableStructure> RelocationStructures = AITAC_FindAllDeployables(RelocationPoint, &BaseStuffFilter);
|
||||
|
||||
for (auto it = RelocationStructures.begin(); it != RelocationStructures.end(); it++)
|
||||
{
|
||||
if (it->StructureType == STRUCTURE_MARINE_COMMCHAIR)
|
||||
{
|
||||
RelocationChair = it->edict;
|
||||
}
|
||||
|
||||
if (it->StructureType == STRUCTURE_MARINE_INFANTRYPORTAL)
|
||||
{
|
||||
NumInfPortals++;
|
||||
}
|
||||
}
|
||||
|
||||
if (FNullEnt(RelocationChair) || NumInfPortals < 2) { return false; }
|
||||
|
||||
DeployableSearchFilter OldStuffFilter;
|
||||
OldStuffFilter.DeployableTeam = RelocationTeam;
|
||||
OldStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
OldStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
OldStuffFilter.MinSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
|
||||
OldStuffFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE;
|
||||
|
||||
vector<AvHAIBuildableStructure> AllOldStructures = AITAC_FindAllDeployables(RelocationPoint, &OldStuffFilter);
|
||||
|
||||
for (auto it = AllOldStructures.begin(); it != AllOldStructures.end(); it++)
|
||||
{
|
||||
if (it->edict != CurrentCommChair) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -63,6 +63,7 @@ Vector AITAC_GetCommChairLocation(AvHTeamNumber Team);
|
|||
edict_t* AITAC_GetCommChair(AvHTeamNumber Team);
|
||||
|
||||
Vector AITAC_GetTeamStartingLocation(AvHTeamNumber Team);
|
||||
Vector AITAC_GetTeamRelocationPoint(AvHTeamNumber Team);
|
||||
|
||||
AvHAIResourceNode* AITAC_GetRandomResourceNode(AvHTeamNumber SearchingTeam, const unsigned int ReachabilityFlags);
|
||||
|
||||
|
@ -211,4 +212,8 @@ AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, edict_t* TaskTarget, B
|
|||
AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, Vector TaskLocation, BotTaskType ObjectiveType);
|
||||
Vector AITAC_GetGatherLocationForSquad(AvHAISquad* Squad);
|
||||
|
||||
Vector AITAC_FindNewTeamRelocationPoint(AvHTeamNumber Team);
|
||||
bool AITAC_IsRelocationPointStillValid(AvHTeamNumber RelocationTeam, Vector RelocationPoint);
|
||||
bool AITAC_IsRelocationCompleted(AvHTeamNumber RelocationTeam, Vector RelocationPoint);
|
||||
|
||||
#endif
|
|
@ -1928,9 +1928,34 @@ void BotProgressTakeCommandTask(AvHAIPlayer* pBot)
|
|||
// Don't take command if we already have a commander
|
||||
if (pBot->Player->GetCommander()) { return; }
|
||||
|
||||
edict_t* CommChair = AITAC_GetCommChair(pBot->Player->GetTeam());
|
||||
edict_t* CommChair = nullptr;
|
||||
|
||||
if (!CommChair) { return; }
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
Vector RelocationPoint = pBot->RelocationSpot;
|
||||
|
||||
if (!vIsZero(RelocationPoint) && AITAC_IsRelocationCompleted(BotTeam, RelocationPoint))
|
||||
{
|
||||
DeployableSearchFilter RelocationChairFilter;
|
||||
RelocationChairFilter.DeployableTeam = BotTeam;
|
||||
RelocationChairFilter.DeployableTypes = STRUCTURE_MARINE_COMMCHAIR;
|
||||
RelocationChairFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
RelocationChairFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
RelocationChairFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure RelocationChair = AITAC_FindClosestDeployableToLocation(RelocationPoint, &RelocationChairFilter);
|
||||
|
||||
if (RelocationChair.IsValid())
|
||||
{
|
||||
CommChair = RelocationChair.edict;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CommChair = AITAC_GetCommChair(BotTeam);
|
||||
}
|
||||
|
||||
if (FNullEnt(CommChair)) { return; }
|
||||
|
||||
float DistFromChair = vDist2DSq(pBot->Edict->v.origin, CommChair->v.origin);
|
||||
|
||||
|
@ -2719,6 +2744,9 @@ void AlienProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (PhaseGate.IsValid())
|
||||
{
|
||||
// If the phase gate is next to an electrified structure, and we are a skulk or lerk, then attack
|
||||
// The electrified structure instead. I might change this to avoid it altogether, but there's nothing
|
||||
// wrong with trying to chip away at the TF if you're a skulk
|
||||
if (bAvoidElectrified)
|
||||
{
|
||||
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
|
|
Loading…
Reference in a new issue