mirror of
https://github.com/ENSL/NS.git
synced 2025-03-14 14:41:15 +00:00
Reimplemented regular marine AI
This commit is contained in:
parent
32e7a74db5
commit
933ed063f0
15 changed files with 1651 additions and 216 deletions
|
@ -7,6 +7,7 @@
|
|||
#include "AvHAINavigation.h"
|
||||
#include "AvHAITask.h"
|
||||
#include "AvHAIHelper.h"
|
||||
#include "AvHAIPlayerManager.h"
|
||||
|
||||
#include "../AvHSharedUtil.h"
|
||||
#include "../AvHServerUtil.h"
|
||||
|
@ -170,6 +171,373 @@ bool AICOMM_IssueBuildOrder(AvHAIPlayer* pBot, edict_t* Recipient, edict_t* Targ
|
|||
return true;
|
||||
}
|
||||
|
||||
void AICOMM_AssignNewPlayerOrder(AvHAIPlayer* pBot, edict_t* Assignee, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose)
|
||||
{
|
||||
if (FNullEnt(Assignee) || FNullEnt(TargetEntity) || OrderPurpose == ORDERPURPOSE_NONE) { return; }
|
||||
|
||||
// Clear any existing order we have for this player
|
||||
for (auto it = pBot->ActiveOrders.begin(); it != pBot->ActiveOrders.end();)
|
||||
{
|
||||
if (it->Assignee == Assignee)
|
||||
{
|
||||
it = pBot->ActiveOrders.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
ai_commander_order NewOrder;
|
||||
NewOrder.Assignee = Assignee;
|
||||
NewOrder.OrderTarget = TargetEntity;
|
||||
NewOrder.OrderPurpose = OrderPurpose;
|
||||
|
||||
AICOMM_IssueOrderForAssignedJob(pBot, &NewOrder);
|
||||
|
||||
pBot->ActiveOrders.push_back(NewOrder);
|
||||
}
|
||||
|
||||
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order)
|
||||
{
|
||||
if (Order->OrderPurpose == ORDERPURPOSE_SIEGE_HIVE || Order->OrderPurpose == ORDERPURPOSE_SECURE_HIVE)
|
||||
{
|
||||
bool bIsSiegeHiveOrder = (Order->OrderPurpose == ORDERPURPOSE_SIEGE_HIVE);
|
||||
const AvHAIHiveDefinition* Hive = AITAC_GetHiveFromEdict(Order->OrderTarget);
|
||||
|
||||
if (Hive)
|
||||
{
|
||||
Vector OrderLocation = Hive->FloorLocation;
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
StructureFilter.MaxSearchRadius = (bIsSiegeHiveOrder) ? UTIL_MetresToGoldSrcUnits(25.0f) : UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure* NearestToHive = AITAC_FindClosestDeployableToLocation(Hive->Location, &StructureFilter);
|
||||
|
||||
if (NearestToHive)
|
||||
{
|
||||
if (!(NearestToHive->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED))
|
||||
{
|
||||
AICOMM_IssueBuildOrder(pBot, Order->Assignee, NearestToHive->edict);
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
Order->LastPlayerDistance = vDist2DSq(Order->Assignee->v.origin, NearestToHive->Location);
|
||||
Order->OrderLocation = NearestToHive->Location;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector MoveLoc = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestToHive->Location, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
|
||||
AICOMM_IssueMovementOrder(pBot, Order->Assignee, MoveLoc);
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
Order->LastPlayerDistance = vDist2DSq(Order->Assignee->v.origin, MoveLoc);
|
||||
Order->OrderLocation = MoveLoc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector MoveLoc = (bIsSiegeHiveOrder) ? UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Hive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(25.0f)) : Hive->FloorLocation;
|
||||
|
||||
AICOMM_IssueMovementOrder(pBot, Order->Assignee, MoveLoc);
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
Order->LastPlayerDistance = vDist2DSq(Order->Assignee->v.origin, MoveLoc);
|
||||
Order->OrderLocation = MoveLoc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Order->OrderPurpose == ORDERPURPOSE_SECURE_RESNODE)
|
||||
{
|
||||
const AvHAIResourceNode* ResNode = AITAC_GetResourceNodeFromEdict(Order->OrderTarget);
|
||||
|
||||
if (ResNode)
|
||||
{
|
||||
if (ResNode->OwningTeam == pBot->Player->GetTeam() && ResNode->ActiveTowerEntity && !UTIL_StructureIsFullyBuilt(ResNode->ActiveTowerEntity))
|
||||
{
|
||||
AICOMM_IssueBuildOrder(pBot, Order->Assignee, ResNode->ActiveTowerEntity);
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
Order->LastPlayerDistance = vDist2DSq(Order->Assignee->v.origin, ResNode->Location);
|
||||
Order->OrderLocation = ResNode->Location;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector MoveLoc = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ResNode->Location, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
|
||||
AICOMM_IssueMovementOrder(pBot, Order->Assignee, MoveLoc);
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
Order->LastPlayerDistance = vDist2DSq(Order->Assignee->v.origin, MoveLoc);
|
||||
Order->OrderLocation = MoveLoc;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int AICOMM_GetNumPlayersAssignedToOrder(AvHAIPlayer* pBot, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
for (auto it = pBot->ActiveOrders.begin(); it != pBot->ActiveOrders.end(); it++)
|
||||
{
|
||||
if (it->OrderTarget == TargetEntity && it->OrderPurpose == OrderPurpose)
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool AICOMM_IsOrderStillValid(AvHAIPlayer* pBot, ai_commander_order* Order)
|
||||
{
|
||||
if (FNullEnt(Order->Assignee) || FNullEnt(Order->OrderTarget) || !IsPlayerActiveInGame(Order->Assignee) || Order->OrderPurpose == ORDERPURPOSE_NONE) { return false; }
|
||||
|
||||
switch (Order->OrderPurpose)
|
||||
{
|
||||
case ORDERPURPOSE_SECURE_HIVE:
|
||||
{
|
||||
const AvHAIHiveDefinition* Hive = AITAC_GetHiveFromEdict(Order->OrderTarget);
|
||||
|
||||
if (!Hive || Hive->Status != HIVE_STATUS_UNBUILT) { return false; }
|
||||
|
||||
return !AICOMM_IsHiveFullySecured(pBot, Hive, false);
|
||||
}
|
||||
break;
|
||||
case ORDERPURPOSE_SIEGE_HIVE:
|
||||
{
|
||||
const AvHAIHiveDefinition* Hive = AITAC_GetHiveFromEdict(Order->OrderTarget);
|
||||
|
||||
// Hive has been destroyed, no longer needs sieging
|
||||
if (!Hive || Hive->Status == HIVE_STATUS_UNBUILT) { return false; }
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f);
|
||||
|
||||
// Check that any siege structure exists. This will avoid situations where commander keeps ordering marines to a hive that is too well defended
|
||||
bool bSiegeStructureExists = AITAC_DeployableExistsAtLocation(Hive->Location, &StructureFilter);
|
||||
|
||||
return bSiegeStructureExists;
|
||||
|
||||
}
|
||||
break;
|
||||
case ORDERPURPOSE_SECURE_RESNODE:
|
||||
{
|
||||
const AvHAIResourceNode* ResNode = AITAC_GetResourceNodeFromEdict(Order->OrderTarget);
|
||||
|
||||
if (!ResNode) { return false; }
|
||||
|
||||
return (ResNode->OwningTeam != pBot->Player->GetTeam() || !ResNode->ActiveTowerEntity || !UTIL_StructureIsFullyBuilt(ResNode->ActiveTowerEntity));
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order)
|
||||
{
|
||||
float NewDist = vDist2DSq(Order->Assignee->v.origin, Order->OrderLocation);
|
||||
float OldDist = Order->LastPlayerDistance;
|
||||
Order->LastPlayerDistance = NewDist;
|
||||
|
||||
if (gpGlobals->time - Order->LastReminderTime < MIN_COMMANDER_REMIND_TIME) { return false; }
|
||||
|
||||
if (Order->OrderPurpose == ORDERPURPOSE_SECURE_RESNODE)
|
||||
{
|
||||
if (vDist2DSq(Order->Assignee->v.origin, Order->OrderTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f))) { return false; }
|
||||
}
|
||||
|
||||
if (Order->OrderPurpose == ORDERPURPOSE_SIEGE_HIVE)
|
||||
{
|
||||
if (vDist2DSq(Order->Assignee->v.origin, Order->OrderTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(25.0f))) { return false; }
|
||||
}
|
||||
|
||||
if (Order->OrderPurpose == ORDERPURPOSE_SECURE_HIVE)
|
||||
{
|
||||
if (vDist2DSq(Order->Assignee->v.origin, Order->OrderTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { return false; }
|
||||
}
|
||||
|
||||
return NewDist >= OldDist;
|
||||
}
|
||||
|
||||
void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
||||
{
|
||||
// Clear out any orders which aren't relevant any more
|
||||
for (auto it = pBot->ActiveOrders.begin(); it != pBot->ActiveOrders.end();)
|
||||
{
|
||||
if (!AICOMM_IsOrderStillValid(pBot, &(*it)))
|
||||
{
|
||||
it = pBot->ActiveOrders.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the person we're ordering around isn't doing as they're told, then issue them a reminder
|
||||
if (AICOMM_DoesPlayerOrderNeedReminder(pBot, &(*it)))
|
||||
{
|
||||
AICOMM_IssueOrderForAssignedJob(pBot, &(*it));
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
int NumPlayersOnTeam = AITAC_GetNumActivePlayersOnTeam(pBot->Player->GetTeam());
|
||||
int DesiredPlayers = imini(2, (int)ceilf((float)NumPlayersOnTeam *0.5f));
|
||||
|
||||
const AvHAIHiveDefinition* SiegedHive = AITAC_GetNearestHiveUnderActiveSiege(pBot->Player->GetTeam(), AITAC_GetCommChairLocation(pBot->Player->GetTeam()));
|
||||
|
||||
if (SiegedHive)
|
||||
{
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE);
|
||||
int NumSiegingPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE);
|
||||
|
||||
if ((NumAssignedPlayers + NumSiegingPlayers) < DesiredPlayers)
|
||||
{
|
||||
for (int i = 0; i < DesiredPlayers - (NumAssignedPlayers + NumSiegingPlayers); i++)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, SiegedHive->FloorLocation);
|
||||
|
||||
if (!FNullEnt(NewAssignee))
|
||||
{
|
||||
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
|
||||
|
||||
AvHAIHiveDefinition* EmptyHive = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = Hives.begin(); it != Hives.end(); it++)
|
||||
{
|
||||
AvHAIHiveDefinition* ThisHive = (*it);
|
||||
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
if (AICOMM_IsHiveFullySecured(pBot, ThisHive, false)) { continue; }
|
||||
|
||||
int NumPlayersSecuring = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ThisHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE);
|
||||
|
||||
if ((NumPlayersSecuring + NumAssignedPlayers) < DesiredPlayers)
|
||||
{
|
||||
float ThisDist = vDist2DSq(AITAC_GetCommChairLocation(pBot->Player->GetTeam()), ThisHive->Location);
|
||||
|
||||
if (!EmptyHive || ThisDist < MinDist)
|
||||
{
|
||||
EmptyHive = ThisHive;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EmptyHive)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, EmptyHive->FloorLocation);
|
||||
|
||||
if (!FNullEnt(NewAssignee))
|
||||
{
|
||||
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, EmptyHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE);
|
||||
}
|
||||
}
|
||||
|
||||
DeployableSearchFilter ResNodeFilter;
|
||||
ResNodeFilter.DeployableTeam = TEAM_IND;
|
||||
ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
ResNodeFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
|
||||
const AvHAIResourceNode* ResNode = AITAC_FindNearestResourceNodeToLocation(AITAC_GetCommChairLocation(pBot->Player->GetTeam()), &ResNodeFilter);
|
||||
|
||||
if (ResNode)
|
||||
{
|
||||
int NumPlayersSecuring = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ResNode->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ResNode->ResourceEntity->edict(), ORDERPURPOSE_SECURE_RESNODE);
|
||||
|
||||
if ((NumPlayersSecuring + NumAssignedPlayers) < 1)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, ResNode->Location);
|
||||
|
||||
if (!FNullEnt(NewAssignee))
|
||||
{
|
||||
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, ResNode->ResourceEntity->edict(), ORDERPURPOSE_SECURE_RESNODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation)
|
||||
{
|
||||
edict_t* Result = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
vector<AvHPlayer*> PlayerList = AIMGR_GetAllPlayersOnTeam(pBot->Player->GetTeam());
|
||||
|
||||
// First, remove all players who are dead or otherwise not active with boots on the ground (e.g. commander, or being digested)
|
||||
for (auto it = PlayerList.begin(); it != PlayerList.end();)
|
||||
{
|
||||
AvHPlayer* PlayerRef = (*it);
|
||||
|
||||
if (!IsPlayerActiveInGame(PlayerRef->edict()))
|
||||
{
|
||||
it = PlayerList.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Next, erase all players with orders so we only have a list of players without orders assigned to them
|
||||
for (auto it = pBot->ActiveOrders.begin(); it != pBot->ActiveOrders.end(); it++)
|
||||
{
|
||||
AvHPlayer* ThisPlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(it->Assignee));
|
||||
|
||||
if (!ThisPlayer) { continue; }
|
||||
|
||||
std::vector<AvHPlayer*>::iterator FoundPlayer = std::find(PlayerList.begin(), PlayerList.end(), ThisPlayer);
|
||||
|
||||
if (FoundPlayer != PlayerList.end())
|
||||
{
|
||||
PlayerList.erase(FoundPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
// Now rank them by distance and return the result
|
||||
for (auto it = PlayerList.begin(); it != PlayerList.end(); it++)
|
||||
{
|
||||
edict_t* PlayerEdict = (*it)->edict();
|
||||
|
||||
float ThisDist = vDist2DSq(PlayerEdict->v.origin, SearchLocation);
|
||||
|
||||
if (!Result || ThisDist < MinDist)
|
||||
{
|
||||
Result = PlayerEdict;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
|
||||
}
|
||||
|
||||
bool AICOMM_IssueSecureHiveOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIHiveDefinition* HiveToSecure)
|
||||
{
|
||||
if (!HiveToSecure || FNullEnt(Recipient) || !IsPlayerActiveInGame(Recipient)) { return false; }
|
||||
|
@ -510,6 +878,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
|
|||
}
|
||||
}
|
||||
|
||||
if (pBot->Player->GetResources() < 30) { return false; }
|
||||
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMSLAB;
|
||||
StructureFilter.MaxSearchRadius = 0.0f;
|
||||
|
||||
|
@ -944,6 +1314,12 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
if (vIsZero(NextBuildPosition))
|
||||
{
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
|
||||
if (vIsZero(NextBuildPosition))
|
||||
{
|
||||
// Fall-back, this could end up putting the structure in dodgy spots but better than not placing it at all
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExistingPG)
|
||||
|
@ -953,6 +1329,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
|
||||
if (!ExistingTF)
|
||||
{
|
||||
if (vDist2DSq(NextBuildPosition, HiveToSiege->Location) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f))) { return true; }
|
||||
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
|
||||
}
|
||||
|
||||
|
@ -980,13 +1357,22 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
{
|
||||
SiegeLocation = ExistingTF->Location;
|
||||
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (vIsZero(NextBuildPosition))
|
||||
{
|
||||
// Reduce radius to avoid putting it on the other side of a wall or something
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
|
||||
if (vIsZero(NextBuildPosition))
|
||||
{
|
||||
// Fall-back, this could end up putting the structure in dodgy spots but better than not placing it at all
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// Don't put the turret out of siege range
|
||||
if (vDist2DSq(NextBuildPosition, HiveToSiege->Location) > sqrf(kSiegeTurretRange)) { return true; }
|
||||
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
|
||||
}
|
||||
|
||||
|
@ -1037,7 +1423,12 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
|
|||
|
||||
if (!ExistingPG)
|
||||
{
|
||||
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (vIsZero(BuildLocation))
|
||||
{
|
||||
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
}
|
||||
|
||||
if (!vIsZero(BuildLocation))
|
||||
{
|
||||
|
@ -1057,7 +1448,12 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
|
|||
|
||||
if (!ExistingTF)
|
||||
{
|
||||
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
|
||||
if (vIsZero(BuildLocation))
|
||||
{
|
||||
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
}
|
||||
|
||||
if (!vIsZero(BuildLocation))
|
||||
{
|
||||
|
@ -1440,6 +1836,8 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot)
|
|||
|
||||
if (gpGlobals->time < pBot->next_commander_action_time) { return; }
|
||||
|
||||
AICOMM_UpdatePlayerOrders(pBot);
|
||||
|
||||
if (AICOMM_CheckForNextRecycleAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextSupportAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextBuildAction(pBot, &pBot->BuildAction)) { return; }
|
||||
|
@ -1479,6 +1877,15 @@ bool AICOMM_IsCommanderActionValid(AvHAIPlayer* pBot, commander_action* Action)
|
|||
|
||||
bool AICOMM_ShouldCommanderLeaveChair(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (pBot->BotRole != BOT_ROLE_COMMAND) { return true; }
|
||||
|
||||
if (AIMGR_GetCommanderMode() == COMMANDERMODE_DISABLED) { return true; }
|
||||
|
||||
if (AIMGR_GetCommanderMode() == COMMANDERMODE_IFNOHUMAN)
|
||||
{
|
||||
if (AIMGR_GetNumHumanPlayersOnTeam(pBot->Player->GetTeam()) > 0) { return true;}
|
||||
}
|
||||
|
||||
int NumAliveMarinesInBase = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), AITAC_GetCommChairLocation(pBot->Player->GetTeam()), UTIL_MetresToGoldSrcUnits(30.0f), true, pBot->Edict, AVH_USER3_NONE);
|
||||
|
||||
if (NumAliveMarinesInBase > 0) { return false; }
|
||||
|
@ -1524,48 +1931,30 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
|
|||
|
||||
if (Hive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
|
||||
if (AICOMM_IsHiveFullySecured(CommanderBot, Hive)) { continue; }
|
||||
if (AICOMM_IsHiveFullySecured(CommanderBot, Hive, true)) { continue; }
|
||||
|
||||
if (AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f)) == nullptr) { continue; }
|
||||
Vector SecureLocation = Hive->FloorLocation;
|
||||
|
||||
if (AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f)))
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTeam = CommanderTeam;
|
||||
StructureFilter.ReachabilityTeam = CommanderTeam;
|
||||
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure* ExistingStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
if (ExistingStructure && UTIL_QuickTrace(nullptr, UTIL_GetCentreOfEntity(ExistingStructure->edict), Hive->Location))
|
||||
{
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTeam = CommanderTeam;
|
||||
StructureFilter.ReachabilityTeam = CommanderTeam;
|
||||
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure* PG = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
bool bCanSeePG = (!PG || AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(PG->edict), UTIL_MetresToGoldSrcUnits(10.0f)));
|
||||
|
||||
if (!bCanSeePG)
|
||||
{
|
||||
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
|
||||
|
||||
AvHAIBuildableStructure* TF = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
bool bNeedsElectrifying = false;
|
||||
|
||||
if (TF)
|
||||
{
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
|
||||
|
||||
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)));
|
||||
|
||||
if (!bNeedsElectrifying && !bCanSeePG && !bCanSeeTF) { continue; }
|
||||
}
|
||||
|
||||
SecureLocation = ExistingStructure->Location;
|
||||
}
|
||||
|
||||
float MarineDist = (ExistingStructure) ? UTIL_MetresToGoldSrcUnits(5.0f) : UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
if (AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, SecureLocation, MarineDist) == nullptr) { continue; }
|
||||
|
||||
float ThisDist = vDist2DSq(Hive->FloorLocation, SearchLocation);
|
||||
|
||||
if (!Result || ThisDist < MinDist)
|
||||
|
@ -1579,7 +1968,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
|
|||
return Result;
|
||||
}
|
||||
|
||||
bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinition* Hive)
|
||||
bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinition* Hive, bool bIncludeElectrical)
|
||||
{
|
||||
AvHTeamNumber CommanderTeam = CommanderBot->Player->GetTeam();
|
||||
|
||||
|
@ -1595,6 +1984,7 @@ bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinit
|
|||
SearchFilter.DeployableTeam = CommanderTeam;
|
||||
SearchFilter.ReachabilityTeam = CommanderTeam;
|
||||
SearchFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
SearchFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
SearchFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
SearchFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
|
@ -1604,7 +1994,7 @@ bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinit
|
|||
{
|
||||
AvHAIBuildableStructure* Structure = (*it);
|
||||
|
||||
if (Structure->StructureType == STRUCTURE_MARINE_TURRETFACTORY)
|
||||
if (Structure->StructureType == STRUCTURE_MARINE_PHASEGATE)
|
||||
{
|
||||
bHasPhaseGate = true;
|
||||
}
|
||||
|
@ -1619,16 +2009,13 @@ bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinit
|
|||
|
||||
NumTurrets = AITAC_GetNumDeployablesNearLocation(Structure->Location, &SearchFilter);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AvHAIResourceNode* ResNode = Hive->HiveResNodeRef;
|
||||
|
||||
bool bSecuredResNode = (!ResNode || (ResNode->bIsOccupied && ResNode->OwningTeam == CommanderTeam));
|
||||
bool bSecuredResNode = (!ResNode || (ResNode->bIsOccupied && ResNode->OwningTeam == CommanderTeam && UTIL_StructureIsFullyBuilt(ResNode->ActiveTowerEntity)));
|
||||
|
||||
bool bShouldElectrifyResNode = (ResNode && bSecuredResNode && CommanderBot->Player->GetResources() > 100 && AITAC_ElectricalResearchIsAvailable(ResNode->ActiveTowerEntity));
|
||||
|
||||
return ((!bPhaseGatesAvailable || bHasPhaseGate) && bHasTurretFactory && bTurretFactoryElectrified && NumTurrets >= 5 && bSecuredResNode && !bShouldElectrifyResNode);
|
||||
return ((!bPhaseGatesAvailable || bHasPhaseGate) && bHasTurretFactory && (!bIncludeElectrical || bTurretFactoryElectrified) && NumTurrets >= 5 && bSecuredResNode);
|
||||
}
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "AvHAIConstants.h"
|
||||
|
||||
static const float MIN_COMMANDER_REMIND_TIME = 20.0f; // How frequently the commander can nag a player to do something, if they don't think they're doing it
|
||||
|
||||
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);
|
||||
|
@ -19,11 +21,19 @@ bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureTo
|
|||
bool AICOMM_RecycleStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToRecycle);
|
||||
|
||||
bool AICOMM_IssueMovementOrder(AvHAIPlayer* pBot, edict_t* Recipient, const Vector MoveLocation);
|
||||
bool AICOMM_IssueBuildOrder(AvHAIPlayer* pBot, edict_t* Recipient, edict_t* TargetStructure);
|
||||
bool AICOMM_IssueBuildOrder(AvHAIPlayer* pBot, edict_t* Recipient, edict_t* TargetStructuree);
|
||||
bool AICOMM_IssueSecureHiveOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIHiveDefinition* HiveToSecure);
|
||||
bool AICOMM_IssueSiegeHiveOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIHiveDefinition* HiveToSiege, const Vector SiegePosition);
|
||||
bool AICOMM_IssueSecureResNodeOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIResourceNode* ResNode);
|
||||
|
||||
void AICOMM_AssignNewPlayerOrder(AvHAIPlayer* pBot, edict_t* Assignee, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose);
|
||||
int AICOMM_GetNumPlayersAssignedToOrder(AvHAIPlayer* pBot, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose);
|
||||
bool AICOMM_IsOrderStillValid(AvHAIPlayer* pBot, ai_commander_order* Order);
|
||||
void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot);
|
||||
edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation);
|
||||
bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order);
|
||||
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order);
|
||||
|
||||
void AICOMM_ClearAction(commander_action* Action);
|
||||
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action);
|
||||
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
|
||||
|
@ -46,7 +56,7 @@ ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edic
|
|||
void AICOMM_CheckNewRequests(AvHAIPlayer* pBot);
|
||||
bool AICOMM_IsRequestValid(ai_commander_request* Request);
|
||||
|
||||
bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinition* Hive);
|
||||
bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinition* Hive, bool bIncludeElectrical);
|
||||
|
||||
bool AICOMM_ShouldCommanderLeaveChair(AvHAIPlayer* pBot);
|
||||
|
||||
|
|
|
@ -14,15 +14,6 @@ typedef enum _BOTFILLMODE
|
|||
|
||||
} BotFillMode;
|
||||
|
||||
// Bot commander mode, should the bot go commander and when
|
||||
typedef enum _COMMANDERMODE
|
||||
{
|
||||
COMMANDERMODE_NEVER = 0, // Bot never tries to command
|
||||
COMMANDERMODE_IFNOHUMAN, // Bot only commands if no human is on the marine team
|
||||
COMMANDERMODE_ALWAYS // Bot will always take command if no human does after CommanderWaitTime expires
|
||||
|
||||
} CommanderMode;
|
||||
|
||||
// Each map can have a desired marine and alien team size
|
||||
typedef struct _TEAMSIZEDEFINITIONS
|
||||
{
|
||||
|
|
|
@ -171,6 +171,36 @@ typedef enum _STRUCTUREPURPOSE
|
|||
|
||||
} StructurePurpose;
|
||||
|
||||
typedef enum _AVHAICOMMANDERMODE
|
||||
{
|
||||
COMMANDERMODE_DISABLED, // AI Commander not allowed
|
||||
COMMANDERMODE_IFNOHUMAN, // AI Commander only allowed if no humans are on the marine team
|
||||
COMMANDERMODE_ENABLED // AI Commander allowed if no human takes charge (following grace period)
|
||||
} AvHAICommanderMode;
|
||||
|
||||
// Bot's role on the team. For marines, this only governs what they do when left to their own devices.
|
||||
// Marine bots will always listen to orders from the commander regardless of role.
|
||||
typedef enum _AVHAIBOTROLE
|
||||
{
|
||||
BOT_ROLE_NONE, // No defined role
|
||||
|
||||
// General Roles
|
||||
|
||||
BOT_ROLE_FIND_RESOURCES, // Will hunt for uncapped resource nodes and cap them. Will attack enemy resource towers
|
||||
BOT_ROLE_SWEEPER, // Defensive role to protect infrastructure and build at base. Will patrol to keep outposts secure
|
||||
BOT_ROLE_ASSAULT, // Will go to attack the hive and other alien structures
|
||||
|
||||
// Marine-only Roles
|
||||
|
||||
BOT_ROLE_COMMAND, // Will attempt to take command
|
||||
BOT_ROLE_BOMBARDIER, // Bot is armed with a GL and wants to wreck your shit
|
||||
|
||||
// Alien-only roles
|
||||
|
||||
BOT_ROLE_BUILDER, // Will focus on building chambers and hives. Stays gorge most of the time
|
||||
BOT_ROLE_HARASS // Focuses on taking down enemy resource nodes and hunting the enemy
|
||||
} AvHAIBotRole;
|
||||
|
||||
typedef struct _OFF_MESH_CONN
|
||||
{
|
||||
unsigned int ConnectionRefs[2];
|
||||
|
@ -349,27 +379,6 @@ typedef enum
|
|||
}
|
||||
BotAttackResult;
|
||||
|
||||
// Bot's role on the team. For marines, this only governs what they do when left to their own devices.
|
||||
// Marine bots will always listen to orders from the commander regardless of role.
|
||||
enum BotRole
|
||||
{
|
||||
BOT_ROLE_NONE, // No defined role
|
||||
|
||||
// Marine Roles
|
||||
|
||||
BOT_ROLE_COMMAND, // Will attempt to take command
|
||||
BOT_ROLE_FIND_RESOURCES, // Will hunt for uncapped resource nodes and cap them. Will attack enemy resource towers
|
||||
BOT_ROLE_SWEEPER, // Defensive role to protect infrastructure and build at base. Will patrol to keep outposts secure
|
||||
BOT_ROLE_ASSAULT, // Will go to attack the hive and other alien structures
|
||||
BOT_ROLE_BOMBARDIER, // Bot is armed with a GL and wants to wreck your shit
|
||||
|
||||
// Alien roles
|
||||
|
||||
BOT_ROLE_RES_CAPPER, // Will hunt for uncapped nodes or ones held by the enemy and cap them
|
||||
BOT_ROLE_BUILDER, // Will focus on building chambers and hives. Stays gorge most of the time
|
||||
BOT_ROLE_HARASS, // Focuses on taking down enemy resource nodes and hunting the enemy
|
||||
BOT_ROLE_DESTROYER // Will go fade/onos when it can, focuses on attacking critical infrastructure
|
||||
};
|
||||
|
||||
// Bot path node. A path will be several of these strung together to lead the bot to its destination
|
||||
typedef struct _BOT_PATH_NODE
|
||||
|
@ -541,6 +550,24 @@ typedef struct _COMMANDER_ACTION
|
|||
|
||||
} commander_action;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ORDERPURPOSE_NONE,
|
||||
ORDERPURPOSE_SECURE_HIVE,
|
||||
ORDERPURPOSE_SIEGE_HIVE,
|
||||
ORDERPURPOSE_SECURE_RESNODE
|
||||
} AvHAIOrderPurpose;
|
||||
|
||||
typedef struct _AI_COMMANDER_ORDER
|
||||
{
|
||||
edict_t* Assignee = nullptr;
|
||||
AvHAIOrderPurpose OrderPurpose = ORDERPURPOSE_NONE;
|
||||
edict_t* OrderTarget = nullptr;
|
||||
Vector OrderLocation = g_vecZero;
|
||||
float LastReminderTime = 0.0f;
|
||||
float LastPlayerDistance = 0.0f;
|
||||
} ai_commander_order;
|
||||
|
||||
typedef struct _AI_COMMANDER_REQUEST
|
||||
{
|
||||
bool bNewRequest = false; // Is this a new request just come in?
|
||||
|
@ -593,6 +620,8 @@ typedef struct AVH_AI_PLAYER
|
|||
AvHAIPlayerTask WantsAndNeedsTask;
|
||||
AvHAIPlayerTask CommanderTask; // Task assigned by the commander
|
||||
|
||||
float BotNextTaskEvaluationTime = 0.0f;
|
||||
|
||||
bot_skill BotSkillSettings;
|
||||
|
||||
char PathStatus[128]; // Debug used to help figure out what's going on with a bot's path finding
|
||||
|
@ -607,6 +636,7 @@ typedef struct AVH_AI_PLAYER
|
|||
commander_action* CurrentAction;
|
||||
|
||||
vector<ai_commander_request> ActiveRequests;
|
||||
vector<ai_commander_order> ActiveOrders;
|
||||
|
||||
float next_commander_action_time = 0.0f;
|
||||
|
||||
|
@ -633,6 +663,9 @@ typedef struct AVH_AI_PLAYER
|
|||
Vector ViewForwardVector = g_vecZero; // Bot's current forward unit vector
|
||||
Vector LastSafeLocation = g_vecZero;
|
||||
|
||||
AvHAIBotRole BotRole = BOT_ROLE_NONE;
|
||||
|
||||
|
||||
} AvHAIPlayer;
|
||||
|
||||
|
||||
|
|
|
@ -426,4 +426,47 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int
|
|||
WRITE_BYTE(250); // brightness
|
||||
WRITE_BYTE(5); // speed
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string)
|
||||
{
|
||||
|
||||
|
||||
// higher level wrapper for hudtextparms TE_TEXTMESSAGEs. This function is meant to be called
|
||||
// every frame, since the duration of the display is roughly worth the duration of a video
|
||||
// frame. The X and Y coordinates are unary fractions which are bound to this rule:
|
||||
// 0: top of the screen (Y) or left of the screen (X), left aligned text
|
||||
// 1: bottom of the screen (Y) or right of the screen (X), right aligned text
|
||||
// -1(only one negative value possible): center of the screen (X and Y), centered text
|
||||
// Any value ranging from 0 to 1 will represent a valid position on the screen.
|
||||
|
||||
//static short duration;
|
||||
|
||||
if (FNullEnt(pEntity)) { return; }
|
||||
|
||||
//duration = (int)GAME_GetServerMSecVal() * 256 / 750; // compute text message duration
|
||||
//if (duration < 5)
|
||||
// duration = 5;
|
||||
|
||||
MESSAGE_BEGIN(MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, NULL, pEntity);
|
||||
WRITE_BYTE(TE_TEXTMESSAGE);
|
||||
WRITE_BYTE(channel); // channel
|
||||
WRITE_SHORT((int)(x * 8192.0f)); // x coordinates * 8192
|
||||
WRITE_SHORT((int)(y * 8192.0f)); // y coordinates * 8192
|
||||
WRITE_BYTE(0); // effect (fade in/out)
|
||||
WRITE_BYTE(r); // initial RED
|
||||
WRITE_BYTE(g); // initial GREEN
|
||||
WRITE_BYTE(b); // initial BLUE
|
||||
WRITE_BYTE(1); // initial ALPHA
|
||||
WRITE_BYTE(r); // effect RED
|
||||
WRITE_BYTE(g); // effect GREEN
|
||||
WRITE_BYTE(b); // effect BLUE
|
||||
WRITE_BYTE(1); // effect ALPHA
|
||||
WRITE_SHORT(0); // fade-in time in seconds * 256
|
||||
WRITE_SHORT(0); // fade-out time in seconds * 256
|
||||
WRITE_SHORT(1); // hold time in seconds * 256
|
||||
WRITE_STRING(string);//string); // send the string
|
||||
MESSAGE_END(); // end
|
||||
|
||||
return;
|
||||
}
|
|
@ -44,4 +44,6 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int
|
|||
// Draws a coloured line using RGB input, between start and end for the given player (pEntity) for given number of seconds
|
||||
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds, int r, int g, int b);
|
||||
|
||||
void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string);
|
||||
|
||||
#endif
|
|
@ -468,7 +468,7 @@ void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure)
|
|||
}
|
||||
|
||||
// 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);
|
||||
unsigned int NewObstacleRef = UTIL_AddTemporaryObstacle(BUILDING_NAV_MESH, UTIL_GetCentreOfEntity(Structure->edict), Radius * 1.1f, 100.0f, DT_TILECACHE_NULL_AREA);
|
||||
|
||||
if (NewObstacleRef > 0)
|
||||
{
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
#include "AvHAITactical.h"
|
||||
#include "AvHAITask.h"
|
||||
#include "AvHAICommander.h"
|
||||
#include "AvHAIPlayerManager.h"
|
||||
|
||||
#include "../AvHGamerules.h"
|
||||
#include "../AvHMessage.h"
|
||||
|
||||
extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular)
|
||||
|
@ -1511,6 +1513,546 @@ void DroneThink(AvHAIPlayer* pBot)
|
|||
//AIDEBUG_DrawBotPath(pBot);
|
||||
}
|
||||
|
||||
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole)
|
||||
{
|
||||
if (NewRole != pBot->BotRole)
|
||||
{
|
||||
AITASK_ClearBotTask(pBot, &pBot->PrimaryBotTask);
|
||||
AITASK_ClearBotTask(pBot, &pBot->SecondaryBotTask);
|
||||
|
||||
pBot->BotRole = NewRole;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAIPlayerCORole(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UpdateAIPlayerDMRole(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UpdateAIAlienPlayerNSRole(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHAICommanderMode CurrentCommanderMode = AIMGR_GetCommanderMode();
|
||||
|
||||
// Don't go commander if bots are not allowed to
|
||||
if (CurrentCommanderMode == COMMANDERMODE_DISABLED) { return false; }
|
||||
|
||||
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...
|
||||
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
|
||||
if (CurrentCommanderMode == COMMANDERMODE_IFNOHUMAN && AIMGR_GetNumHumanPlayersOnTeam(BotTeamNumber) > 0) { return false; }
|
||||
|
||||
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 there is another bot already taking command
|
||||
if (AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeamNumber, BOT_ROLE_COMMAND, pBot) > 0) { return false; }
|
||||
|
||||
float ThisBotDist = vDist2DSq(pBot->Edict->v.origin, AITAC_GetCommChairLocation(BotTeamNumber));
|
||||
|
||||
// Only go commander if we're the closest bot to the chair
|
||||
vector <AvHAIPlayer*> BotList = AIMGR_GetAIPlayersOnTeam(BotTeamNumber);
|
||||
|
||||
for (auto it = BotList.begin(); it != BotList.end(); it++)
|
||||
{
|
||||
AvHAIPlayer* OtherBot = (*it);
|
||||
|
||||
float OtherBotDist = vDist2DSq(OtherBot->Edict->v.origin, AITAC_GetCommChairLocation(BotTeamNumber));
|
||||
|
||||
if (OtherBot != pBot && IsPlayerActiveInGame(pBot->Edict) && OtherBotDist < ThisBotDist)
|
||||
{
|
||||
// We aren't the closest, let the other guy take command
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We must be the closest!
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateAIMarinePlayerNSRole(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeamNumber BotTeamNumber = pBot->Player->GetTeam();
|
||||
|
||||
if (BotTeamNumber == TEAM_IND)
|
||||
{
|
||||
SetNewAIPlayerRole(pBot, BOT_ROLE_NONE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldAIPlayerTakeCommand(pBot))
|
||||
{
|
||||
// We're going to go commander!
|
||||
SetNewAIPlayerRole(pBot, BOT_ROLE_COMMAND);
|
||||
return;
|
||||
}
|
||||
|
||||
int NumSweeperBots = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeamNumber, BOT_ROLE_SWEEPER, pBot);
|
||||
|
||||
// Always have a sweeper
|
||||
if (NumSweeperBots < 1)
|
||||
{
|
||||
SetNewAIPlayerRole(pBot, BOT_ROLE_SWEEPER);
|
||||
return;
|
||||
}
|
||||
|
||||
// Always go bombardier if we have a grenade launcher
|
||||
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL))
|
||||
{
|
||||
SetNewAIPlayerRole(pBot, BOT_ROLE_BOMBARDIER);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we own less than half the res nodes in the map, then we want 2 marines to cap them. Otherwise, have 1
|
||||
float ResNodeOwnership = AITAC_GetTeamResNodeOwnership(BotTeamNumber);
|
||||
|
||||
int DesiredResCappers = (ResNodeOwnership < 0.5f) ? 2 : 1;
|
||||
|
||||
int NumCappers = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeamNumber, BOT_ROLE_FIND_RESOURCES, pBot);
|
||||
|
||||
if (NumCappers < DesiredResCappers)
|
||||
{
|
||||
SetNewAIPlayerRole(pBot, BOT_ROLE_FIND_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
// Everyone else goes assault
|
||||
SetNewAIPlayerRole(pBot, BOT_ROLE_ASSAULT);
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerNSThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeam* BotTeam = GetGameRules()->GetTeam(pBot->Player->GetTeam());
|
||||
|
||||
if (!BotTeam) { return; }
|
||||
|
||||
if (BotTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)
|
||||
{
|
||||
AIPlayerNSMarineThink(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
AIPlayerNSAlienThink(pBot);
|
||||
}
|
||||
}
|
||||
|
||||
AvHAIPlayerTask* AIPlayerGetNextTask(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
// Any orders issued by the commander take priority over everything else
|
||||
if (pBot->CommanderTask.TaskType != TASK_NONE)
|
||||
{
|
||||
if (pBot->SecondaryBotTask.bTaskIsUrgent)
|
||||
{
|
||||
return &pBot->SecondaryBotTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &pBot->CommanderTask;
|
||||
}
|
||||
}
|
||||
|
||||
// Prioritise healing our friends (heal tasks are only valid if the target is close by anyway)
|
||||
if (pBot->SecondaryBotTask.TaskType == TASK_HEAL)
|
||||
{
|
||||
return &pBot->SecondaryBotTask;
|
||||
}
|
||||
|
||||
if (AITASK_IsTaskUrgent(pBot, &pBot->WantsAndNeedsTask))
|
||||
{
|
||||
return &pBot->WantsAndNeedsTask;
|
||||
}
|
||||
|
||||
if (AITASK_IsTaskUrgent(pBot, &pBot->PrimaryBotTask))
|
||||
{
|
||||
return &pBot->PrimaryBotTask;
|
||||
}
|
||||
|
||||
if (AITASK_IsTaskUrgent(pBot, &pBot->SecondaryBotTask))
|
||||
{
|
||||
return &pBot->SecondaryBotTask;
|
||||
}
|
||||
|
||||
if (pBot->WantsAndNeedsTask.TaskType != TASK_NONE)
|
||||
{
|
||||
return &pBot->WantsAndNeedsTask;
|
||||
}
|
||||
|
||||
if (pBot->SecondaryBotTask.TaskType != TASK_NONE)
|
||||
{
|
||||
return &pBot->SecondaryBotTask;
|
||||
}
|
||||
|
||||
return &pBot->PrimaryBotTask;
|
||||
}
|
||||
|
||||
void AIPlayerNSMarineThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
UpdateAIMarinePlayerNSRole(pBot);
|
||||
|
||||
if (pBot->BotRole == BOT_ROLE_COMMAND)
|
||||
{
|
||||
AICOMM_CommanderThink(pBot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pBot->CurrentTask) { pBot->CurrentTask = &pBot->PrimaryBotTask; }
|
||||
|
||||
if (gpGlobals->time < pBot->BotNextTaskEvaluationTime)
|
||||
{
|
||||
if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE)
|
||||
{
|
||||
BotProgressTask(pBot, pBot->CurrentTask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f);
|
||||
|
||||
AITASK_BotUpdateAndClearTasks(pBot);
|
||||
|
||||
AIPlayerSetPrimaryMarineTask(pBot, &pBot->PrimaryBotTask);
|
||||
AIPlayerSetSecondaryMarineTask(pBot, &pBot->SecondaryBotTask);
|
||||
|
||||
pBot->CurrentTask = AIPlayerGetNextTask(pBot);
|
||||
|
||||
if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE)
|
||||
{
|
||||
BotProgressTask(pBot, pBot->CurrentTask);
|
||||
}
|
||||
|
||||
if (pBot->DesiredCombatWeapon == WEAPON_NONE)
|
||||
{
|
||||
pBot->DesiredCombatWeapon = BotMarineChooseBestWeapon(pBot, nullptr);
|
||||
}
|
||||
|
||||
if (pBot->CommanderTask.TaskType != TASK_NONE)
|
||||
{
|
||||
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CommanderTask.TaskLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
switch (pBot->BotRole)
|
||||
{
|
||||
case BOT_ROLE_SWEEPER:
|
||||
AIPlayerSetMarineSweeperPrimaryTask(pBot, Task);
|
||||
return;
|
||||
case BOT_ROLE_FIND_RESOURCES:
|
||||
AIPlayerSetMarineCapperPrimaryTask(pBot, Task);
|
||||
return;
|
||||
case BOT_ROLE_ASSAULT:
|
||||
AIPlayerSetMarineAssaultPrimaryTask(pBot, Task);
|
||||
return;
|
||||
case BOT_ROLE_BOMBARDIER:
|
||||
AIPlayerSetMarineBombardierPrimaryTask(pBot, Task);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
if (Task->TaskType == TASK_GUARD) { return; }
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
Vector CommChairLocation = AITAC_GetCommChairLocation(BotTeam);
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
StructureFilter.DeployableTeam = BotTeam;
|
||||
StructureFilter.ReachabilityTeam = BotTeam;
|
||||
StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
|
||||
if (AITAC_GetNumDeployablesNearLocation(CommChairLocation, &StructureFilter) < 2)
|
||||
{
|
||||
Task->TaskType = TASK_GUARD;
|
||||
Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, CommChairLocation, UTIL_MetresToGoldSrcUnits(10.0f));
|
||||
Task->bTaskIsUrgent = false;
|
||||
Task->TaskLength = frandrange(20.0f, 30.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
AvHAIBuildableStructure* NearestPG = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &StructureFilter);
|
||||
|
||||
vector<AvHAIBuildableStructure*> AllPG = AITAC_FindAllDeployables(pBot->Edict->v.origin, &StructureFilter);
|
||||
|
||||
AvHAIBuildableStructure* RandomPG = nullptr;
|
||||
int HighestRand = 0;
|
||||
|
||||
for (auto it = AllPG.begin(); it != AllPG.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure* ThisStruct = (*it);
|
||||
|
||||
if (ThisStruct == NearestPG) { continue; }
|
||||
|
||||
int ThisRand = irandrange(0, 100);
|
||||
|
||||
if (!RandomPG || ThisRand > HighestRand)
|
||||
{
|
||||
RandomPG = ThisStruct;
|
||||
HighestRand = ThisRand;
|
||||
}
|
||||
}
|
||||
|
||||
if (RandomPG)
|
||||
{
|
||||
Task->TaskType = TASK_GUARD;
|
||||
Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, RandomPG->Location, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
Task->bTaskIsUrgent = false;
|
||||
Task->TaskLength = frandrange(20.0f, 30.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetMarineCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
DeployableSearchFilter NodeFilter;
|
||||
NodeFilter.DeployableTeam = TEAM_IND;
|
||||
NodeFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
NodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
|
||||
AvHAIResourceNode* NearestNode = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
vector<AvHAIResourceNode*> UnclaimedResourceNodes = AITAC_GetAllMatchingResourceNodes(pBot->Edict->v.origin, &NodeFilter);
|
||||
|
||||
for (auto it = UnclaimedResourceNodes.begin(); it != UnclaimedResourceNodes.end(); it++)
|
||||
{
|
||||
AvHAIResourceNode* ResNode = (*it);
|
||||
int NumCappers = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ResNode->Location, UTIL_MetresToGoldSrcUnits(4.0), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
||||
// Only want one capper to grab an empty one
|
||||
if (NumCappers == 0)
|
||||
{
|
||||
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ResNode->Location);
|
||||
|
||||
if (!NearestNode || ThisDist < MinDist)
|
||||
{
|
||||
NearestNode = ResNode;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NearestNode)
|
||||
{
|
||||
AITASK_SetCapResNodeTask(pBot, Task, NearestNode, false);
|
||||
return;
|
||||
}
|
||||
|
||||
MinDist = 0.0f;
|
||||
|
||||
NodeFilter.DeployableTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam());
|
||||
|
||||
vector<AvHAIResourceNode*> EnemyResourceNodes = AITAC_GetAllMatchingResourceNodes(pBot->Edict->v.origin, &NodeFilter);
|
||||
|
||||
for (auto it = EnemyResourceNodes.begin(); it != EnemyResourceNodes.end(); it++)
|
||||
{
|
||||
AvHAIResourceNode* ResNode = (*it);
|
||||
int NumCappers = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ResNode->Location, UTIL_MetresToGoldSrcUnits(4.0), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
||||
// Allow for 2 cappers to attack an enemy resource node
|
||||
if (NumCappers < 2)
|
||||
{
|
||||
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ResNode->Location);
|
||||
|
||||
if (!NearestNode || ThisDist < MinDist)
|
||||
{
|
||||
NearestNode = ResNode;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NearestNode)
|
||||
{
|
||||
AITASK_SetCapResNodeTask(pBot, Task, NearestNode, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// No res nodes to cap, go do assault stuff
|
||||
AIPlayerSetMarineAssaultPrimaryTask(pBot, Task);
|
||||
}
|
||||
|
||||
void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
// Go attack sieged hive
|
||||
const AvHAIHiveDefinition* ActiveSiegeHive = AITAC_GetNearestHiveUnderActiveSiege(pBot->Player->GetTeam(), pBot->Edict->v.origin);
|
||||
|
||||
if (ActiveSiegeHive)
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, ActiveSiegeHive->HiveEntity->edict(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Go to empty hive without other marines in it
|
||||
|
||||
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
|
||||
|
||||
AvHAIHiveDefinition* NearestEmptyHive = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
|
||||
{
|
||||
AvHAIHiveDefinition* ThisHive = (*it);
|
||||
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
|
||||
int NumMarinesSecuring = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ThisHive->Location, UTIL_MetresToGoldSrcUnits(15.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
||||
if (NumMarinesSecuring < 2)
|
||||
{
|
||||
float ThisDist = vDist2DSq(ThisHive->Location, pBot->Edict->v.origin);
|
||||
|
||||
if (!NearestEmptyHive || ThisDist < MinDist)
|
||||
{
|
||||
NearestEmptyHive = ThisHive;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NearestEmptyHive)
|
||||
{
|
||||
AITASK_SetSecureHiveTask(pBot, Task, NearestEmptyHive->HiveEntity->edict(), NearestEmptyHive->FloorLocation, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Go to a good siege location if phase gates available
|
||||
|
||||
if (AITAC_PhaseGatesAvailable(pBot->Player->GetTeam()))
|
||||
{
|
||||
const AvHAIHiveDefinition* ActiveHive = AITAC_GetActiveHiveNearestLocation(pBot->Edict->v.origin);
|
||||
|
||||
if (ActiveHive)
|
||||
{
|
||||
if (Task->TaskType != TASK_MOVE)
|
||||
{
|
||||
AITASK_SetMoveTask(pBot, Task, UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, ActiveHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(20.0f)), false);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
// Go attack sieged hive
|
||||
|
||||
// Go clear res nodes
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
// Find any nearby unbuilt structures
|
||||
DeployableSearchFilter UnbuiltFilter;
|
||||
UnbuiltFilter.DeployableTypes = SEARCH_ALL_MARINE_STRUCTURES;
|
||||
UnbuiltFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
UnbuiltFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
UnbuiltFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
UnbuiltFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_COMPLETED;
|
||||
UnbuiltFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
|
||||
|
||||
vector <AvHAIBuildableStructure*> BuildableStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &UnbuiltFilter);
|
||||
|
||||
AvHAIBuildableStructure* NearestStructure = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = BuildableStructures.begin(); it != BuildableStructures.end(); it++)
|
||||
{
|
||||
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), (*it)->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
||||
int NumDesiredBuilders = (vDist2DSq((*it)->Location, AITAC_GetCommChairLocation(pBot->Player->GetTeam())) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) ? 1 : 2;
|
||||
|
||||
if (NumBuilders < NumDesiredBuilders)
|
||||
{
|
||||
float ThisDist = vDist2DSq((*it)->Location, pBot->Edict->v.origin);
|
||||
if (!NearestStructure || ThisDist < MinDist)
|
||||
{
|
||||
NearestStructure = (*it);
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NearestStructure)
|
||||
{
|
||||
AITASK_SetBuildTask(pBot, Task, NearestStructure->edict, false);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
UpdateAIAlienPlayerNSRole(pBot);
|
||||
}
|
||||
|
||||
void AIPlayerCOThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerDMThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
switch (GetGameRules()->GetMapMode())
|
||||
{
|
||||
case MAP_MODE_NS:
|
||||
AIPlayerNSThink(pBot);
|
||||
break;
|
||||
case MAP_MODE_CO:
|
||||
AIPlayerCOThink(pBot);
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
AITASK_BotUpdateAndClearTasks(pBot);
|
||||
|
@ -1579,10 +2121,10 @@ void UpdateCommanderOrders(AvHAIPlayer* pBot)
|
|||
switch (it->GetOrderType())
|
||||
{
|
||||
case ORDERTYPEL_MOVE:
|
||||
AITASK_SetMoveTask(pBot, &pBot->CommanderTask, OrderLocation, true);
|
||||
AIPlayerReceiveMoveOrder(pBot, OrderLocation);
|
||||
break;
|
||||
case ORDERTYPET_BUILD:
|
||||
AITASK_SetBuildTask(pBot, &pBot->CommanderTask, INDEXENT(it->GetTargetIndex()), true);
|
||||
AIPlayerReceiveBuildOrder(pBot, INDEXENT(it->GetTargetIndex()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1591,6 +2133,47 @@ void UpdateCommanderOrders(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
void AIPlayerReceiveBuildOrder(AvHAIPlayer* pBot, edict_t* BuildTarget)
|
||||
{
|
||||
AITASK_SetBuildTask(pBot, &pBot->CommanderTask, BuildTarget, true);
|
||||
}
|
||||
|
||||
void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination)
|
||||
{
|
||||
|
||||
const AvHAIResourceNode* ResNodeRef = AITAC_GetNearestResourceNodeToLocation(Destination);
|
||||
|
||||
// We've been asked to go to a resource node if the movement order is near it
|
||||
if (ResNodeRef && vDist2DSq(ResNodeRef->Location, Destination) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
// If this resource node doesn't belong to us, or the tower isn't fully built, interpret the order as a "cap this node" order
|
||||
if (ResNodeRef->OwningTeam != pBot->Player->GetTeam() || FNullEnt(ResNodeRef->ActiveTowerEntity) || !UTIL_StructureIsFullyBuilt(ResNodeRef->ActiveTowerEntity))
|
||||
{
|
||||
AITASK_SetCapResNodeTask(pBot, &pBot->CommanderTask, ResNodeRef, false);
|
||||
pBot->CommanderTask.bIssuedByCommander = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const AvHAIHiveDefinition* HiveRef = AITAC_GetHiveNearestLocation(Destination);
|
||||
|
||||
// Have we been asked to go to an empty hive? If so, then treat the order as a "help secure this hive" command
|
||||
if (HiveRef && HiveRef->Status == HIVE_STATUS_UNBUILT && vDist2DSq(HiveRef->Location, Destination) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
|
||||
{
|
||||
if (!AICOMM_IsHiveFullySecured(pBot, HiveRef, false))
|
||||
{
|
||||
AITASK_SetSecureHiveTask(pBot, &pBot->CommanderTask, HiveRef->HiveEntity->edict(), Destination, false);
|
||||
pBot->CommanderTask.bIssuedByCommander = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, treat as a normal move order. Go there and wait a bit to see what the commander wants to do next
|
||||
AITASK_SetMoveTask(pBot, &pBot->CommanderTask, Destination, true);
|
||||
pBot->CommanderTask.bIssuedByCommander = true;
|
||||
|
||||
}
|
||||
|
||||
void BotStopCommanderMode(AvHAIPlayer* pBot)
|
||||
{
|
||||
// Thanks EterniumDev (Alien) for logic to allow commander AI to leave the chair and build structures when needed
|
||||
|
|
|
@ -53,10 +53,29 @@ void UpdateBotChat(AvHAIPlayer* pBot);
|
|||
void ClearBotInputs(AvHAIPlayer* pBot);
|
||||
void StartNewBotFrame(AvHAIPlayer* pBot);
|
||||
|
||||
void AIPlayerThink(AvHAIPlayer* pBot);
|
||||
// Think routine for regular NS game mode
|
||||
void AIPlayerNSThink(AvHAIPlayer* pBot);
|
||||
void AIPlayerNSMarineThink(AvHAIPlayer* pBot);
|
||||
void AIPlayerNSAlienThink(AvHAIPlayer* pBot);
|
||||
// Think routine for the combat game mode
|
||||
void AIPlayerCOThink(AvHAIPlayer* pBot);
|
||||
// Think routine for the deathmatch game mode (e.g. when playing CS maps)
|
||||
void AIPlayerDMThink(AvHAIPlayer* pBot);
|
||||
|
||||
void TestNavThink(AvHAIPlayer* pBot);
|
||||
void DroneThink(AvHAIPlayer* pBot);
|
||||
void CustomThink(AvHAIPlayer* pBot);
|
||||
|
||||
AvHAIPlayerTask* AIPlayerGetNextTask(AvHAIPlayer* pBot);
|
||||
void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetMarineCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot);
|
||||
|
||||
bool ShouldBotThink(AvHAIPlayer* pBot);
|
||||
|
@ -64,7 +83,17 @@ bool ShouldBotThink(AvHAIPlayer* pBot);
|
|||
void BotResumePlay(AvHAIPlayer* pBot);
|
||||
|
||||
void UpdateCommanderOrders(AvHAIPlayer* pBot);
|
||||
void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination);
|
||||
void AIPlayerReceiveBuildOrder(AvHAIPlayer* pBot, edict_t* BuildTarget);
|
||||
|
||||
void BotStopCommanderMode(AvHAIPlayer* pBot);
|
||||
|
||||
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole);
|
||||
void UpdateAIMarinePlayerNSRole(AvHAIPlayer* pBot);
|
||||
void UpdateAIAlienPlayerNSRole(AvHAIPlayer* pBot);
|
||||
void UpdateAIPlayerCORole(AvHAIPlayer* pBot);
|
||||
void UpdateAIPlayerDMRole(AvHAIPlayer* pBot);
|
||||
|
||||
bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot);
|
||||
|
||||
#endif
|
|
@ -68,6 +68,21 @@ string BotNames[MAX_PLAYERS] = { "MrRobot",
|
|||
"TerminalFerocity"
|
||||
};
|
||||
|
||||
AvHAICommanderMode AIMGR_GetCommanderMode()
|
||||
{
|
||||
if (avh_botcommandermode.value == 0)
|
||||
{
|
||||
return COMMANDERMODE_DISABLED;
|
||||
}
|
||||
|
||||
if (avh_botcommandermode.value == 1)
|
||||
{
|
||||
return COMMANDERMODE_IFNOHUMAN;
|
||||
}
|
||||
|
||||
return COMMANDERMODE_ENABLED;
|
||||
|
||||
}
|
||||
|
||||
void AIMGR_UpdateAIPlayerCounts()
|
||||
{
|
||||
|
@ -472,7 +487,10 @@ byte BotThrottledMsec(AvHAIPlayer* inAIPlayer)
|
|||
if (newmsec > 255)
|
||||
{
|
||||
newmsec = 255;
|
||||
}
|
||||
}
|
||||
|
||||
// save the command time
|
||||
inAIPlayer->f_previous_command_time = gpGlobals->time;
|
||||
|
||||
return (byte)newmsec;
|
||||
}
|
||||
|
@ -547,17 +565,10 @@ void AIMGR_UpdateAIPlayers()
|
|||
|
||||
UpdateBotChat(bot);
|
||||
|
||||
CustomThink(bot);
|
||||
AIPlayerThink(bot);
|
||||
|
||||
AIDEBUG_DrawPath(DebugPath, 0.0f);
|
||||
|
||||
AvHAIWeapon DesiredWeapon = (bot->DesiredMoveWeapon != WEAPON_NONE) ? bot->DesiredMoveWeapon : bot->DesiredCombatWeapon;
|
||||
|
||||
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(bot->Player) != DesiredWeapon)
|
||||
{
|
||||
BotSwitchToWeapon(bot, DesiredWeapon);
|
||||
}
|
||||
|
||||
BotUpdateDesiredViewRotation(bot);
|
||||
}
|
||||
else
|
||||
|
@ -567,10 +578,7 @@ void AIMGR_UpdateAIPlayers()
|
|||
}
|
||||
|
||||
// Needed to correctly handle client prediction and physics calculations
|
||||
byte adjustedmsec = BotThrottledMsec(bot);
|
||||
|
||||
// save the command time
|
||||
bot->f_previous_command_time = gpGlobals->time;
|
||||
byte adjustedmsec = BotThrottledMsec(bot);
|
||||
|
||||
// Simulate PM_PlayerMove so client prediction and stuff can be executed correctly.
|
||||
RUN_AI_MOVE(bot->Edict, bot->Edict->v.v_angle, bot->ForwardMove,
|
||||
|
@ -596,6 +604,29 @@ int AIMGR_GetNumAIPlayers()
|
|||
return ActiveAIPlayers.size();
|
||||
}
|
||||
|
||||
vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
vector<AvHPlayer*> Result;
|
||||
|
||||
for (int i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
edict_t* PlayerEdict = INDEXENT(i);
|
||||
|
||||
if (!FNullEnt(PlayerEdict) && PlayerEdict->v.team == Team)
|
||||
{
|
||||
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
|
||||
|
||||
if (PlayerRef)
|
||||
{
|
||||
Result.push_back(PlayerRef);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
int Result = 0;
|
||||
|
@ -611,6 +642,46 @@ int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team)
|
|||
return Result;
|
||||
}
|
||||
|
||||
int AIMGR_GetNumHumanPlayersOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
vector<AvHPlayer*> TeamPlayers = AIMGR_GetAllPlayersOnTeam(Team);
|
||||
|
||||
for (auto it = TeamPlayers.begin(); it != TeamPlayers.end(); it++)
|
||||
{
|
||||
AvHPlayer* ThisPlayer = (*it);
|
||||
edict_t* PlayerEdict = ThisPlayer->edict();
|
||||
|
||||
if (!(PlayerEdict->v.flags & FL_FAKECLIENT))
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int AIMGR_GetNumAIPlayersWithRoleOnTeam(AvHTeamNumber Team, AvHAIBotRole Role, AvHAIPlayer* IgnoreAIPlayer)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
|
||||
{
|
||||
if (&(*it) == IgnoreAIPlayer) { continue; }
|
||||
|
||||
if (it->Player->GetTeam() == Team)
|
||||
{
|
||||
if (it->BotRole == Role)
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
|
||||
|
@ -662,11 +733,15 @@ void AIMGR_ResetRound()
|
|||
|
||||
void AIMGR_RoundStarted()
|
||||
{
|
||||
AITAC_PopulateResourceNodes();
|
||||
AITAC_PopulateHiveData();
|
||||
|
||||
AITAC_RefreshResourceNodes();
|
||||
|
||||
AITAC_RefreshHiveData();
|
||||
|
||||
UTIL_UpdateTileCache();
|
||||
|
||||
AITAC_RefreshResourceNodes();
|
||||
|
||||
}
|
||||
|
||||
void AIMGR_ClearBotData()
|
||||
|
|
|
@ -36,6 +36,8 @@ void AIMGR_UpdateTeamBalance();
|
|||
// Called by UpdateAIPlayerCounts. If auto-mode is fill teams, will add/remove bots needed to maintain minimum player counts and balance
|
||||
void AIMGR_UpdateFillTeams();
|
||||
|
||||
vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team);
|
||||
|
||||
// How many AI players are in the game (does not include third-party bots like RCBot/Whichbot)
|
||||
int AIMGR_GetNumAIPlayers();
|
||||
// Returns true if an AI player is on the requested team (does NOT include third-party bots like RCBot/Whichbot)
|
||||
|
@ -43,11 +45,16 @@ int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team);
|
|||
|
||||
void AIMGR_UpdateAIMapData();
|
||||
|
||||
AvHAICommanderMode AIMGR_GetCommanderMode();
|
||||
|
||||
void AIDEBUG_SetDebugVector1(const Vector NewVector);
|
||||
void AIDEBUG_SetDebugVector2(const Vector NewVector);
|
||||
void AIDEBUG_TestPathFind();
|
||||
|
||||
int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team);
|
||||
int AIMGR_GetNumHumanPlayersOnTeam(AvHTeamNumber Team);
|
||||
|
||||
int AIMGR_GetNumAIPlayersWithRoleOnTeam(AvHTeamNumber Team, AvHAIBotRole Role, AvHAIPlayer* IgnoreAIPlayer);
|
||||
|
||||
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);
|
||||
|
||||
|
|
|
@ -653,22 +653,28 @@ Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive)
|
|||
}
|
||||
}
|
||||
|
||||
void AITAC_PopulateHiveData()
|
||||
{
|
||||
Hives.clear();
|
||||
|
||||
FOR_ALL_ENTITIES(kesTeamHive, AvHHive*)
|
||||
|
||||
AvHAIHiveDefinition NewHive;
|
||||
NewHive.HiveEntity = theEntity;
|
||||
NewHive.Location = theEntity->pev->origin;
|
||||
NewHive.HiveResNodeRef = AITAC_GetNearestResourceNodeToLocation(theEntity->pev->origin);
|
||||
NewHive.FloorLocation = UTIL_GetFloorUnderEntity(theEntity->edict()); // Some hives are suspended in the air, this is the floor location directly beneath it
|
||||
|
||||
Hives.push_back(NewHive);
|
||||
|
||||
END_FOR_ALL_ENTITIES(kesTeamHive)
|
||||
}
|
||||
|
||||
void AITAC_RefreshHiveData()
|
||||
{
|
||||
if (Hives.size() == 0)
|
||||
{
|
||||
FOR_ALL_ENTITIES(kesTeamHive, AvHHive*)
|
||||
|
||||
AvHAIHiveDefinition NewHive;
|
||||
NewHive.HiveEntity = theEntity;
|
||||
NewHive.Location = theEntity->pev->origin;
|
||||
NewHive.HiveResNodeRef = AITAC_GetNearestResourceNodeToLocation(theEntity->pev->origin);
|
||||
NewHive.FloorLocation = UTIL_GetFloorUnderEntity(theEntity->edict()); // Some hives are suspended in the air, this is the floor location directly beneath it
|
||||
|
||||
Hives.push_back(NewHive);
|
||||
|
||||
END_FOR_ALL_ENTITIES(kesTeamHive)
|
||||
|
||||
AITAC_PopulateHiveData();
|
||||
}
|
||||
|
||||
int NextRefresh = 0;
|
||||
|
@ -1024,23 +1030,30 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
|
|||
|
||||
}
|
||||
|
||||
void AITAC_PopulateResourceNodes()
|
||||
{
|
||||
ResourceNodes.clear();
|
||||
|
||||
FOR_ALL_ENTITIES(kesFuncResource, AvHFuncResource*)
|
||||
|
||||
AvHAIResourceNode NewResNode;
|
||||
NewResNode.ResourceEntity = theEntity;
|
||||
NewResNode.Location = theEntity->pev->origin;
|
||||
NewResNode.TeamAReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
NewResNode.TeamBReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
NewResNode.bReachabilityMarkedDirty = true;
|
||||
NewResNode.NextReachabilityRefreshTime = 0.0f;
|
||||
|
||||
ResourceNodes.push_back(NewResNode);
|
||||
|
||||
END_FOR_ALL_ENTITIES(kesFuncResource)
|
||||
}
|
||||
|
||||
void AITAC_RefreshResourceNodes()
|
||||
{
|
||||
if (ResourceNodes.size() == 0)
|
||||
{
|
||||
FOR_ALL_ENTITIES(kesFuncResource, AvHFuncResource*)
|
||||
|
||||
AvHAIResourceNode NewResNode;
|
||||
NewResNode.ResourceEntity = theEntity;
|
||||
NewResNode.Location = theEntity->pev->origin;
|
||||
NewResNode.TeamAReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
NewResNode.TeamBReachabilityFlags = AI_REACHABILITY_NONE;
|
||||
NewResNode.bReachabilityMarkedDirty = true;
|
||||
NewResNode.NextReachabilityRefreshTime = 0.0f;
|
||||
|
||||
ResourceNodes.push_back(NewResNode);
|
||||
|
||||
END_FOR_ALL_ENTITIES(kesFuncResource)
|
||||
AITAC_PopulateResourceNodes();
|
||||
}
|
||||
|
||||
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
|
||||
|
@ -2259,6 +2272,19 @@ AvHAIHiveDefinition* AITAC_GetHiveFromEdict(const edict_t* Edict)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
AvHAIResourceNode* AITAC_GetResourceNodeFromEdict(const edict_t* Edict)
|
||||
{
|
||||
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
|
||||
{
|
||||
if (it->ResourceEntity->edict() == Edict)
|
||||
{
|
||||
return &(*it);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const AvHAIHiveDefinition* AITAC_GetHiveNearestLocation(const Vector SearchLocation)
|
||||
{
|
||||
AvHAIHiveDefinition* Result = nullptr;
|
||||
|
@ -2339,6 +2365,116 @@ AvHAIResourceNode* AITAC_GetNearestResourceNodeToLocation(const Vector Location)
|
|||
return Result;
|
||||
}
|
||||
|
||||
float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team)
|
||||
{
|
||||
int NumViableResNodes = 0;
|
||||
int NumOwnedResNodes = 0;
|
||||
|
||||
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
|
||||
{
|
||||
unsigned int CheckReachabilityFlags = (it->TeamAReachabilityFlags | it->TeamBReachabilityFlags);
|
||||
|
||||
if (Team != TEAM_IND)
|
||||
{
|
||||
CheckReachabilityFlags = (Team == GetGameRules()->GetTeamANumber()) ? it->TeamAReachabilityFlags : it->TeamBReachabilityFlags;
|
||||
}
|
||||
|
||||
if (CheckReachabilityFlags == AI_REACHABILITY_UNREACHABLE) { continue; }
|
||||
|
||||
NumViableResNodes++;
|
||||
|
||||
if (it->OwningTeam == Team)
|
||||
{
|
||||
NumOwnedResNodes++;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no viable resource nodes, then report we own them all to avoid divide by zero
|
||||
if (NumViableResNodes == 0) { return 1.0f; }
|
||||
|
||||
return (float)NumOwnedResNodes / (float)NumViableResNodes;
|
||||
}
|
||||
|
||||
int AITAC_GetNumResourceNodesNearLocation(const Vector Location, const DeployableSearchFilter* Filter)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
float MinDistSq = sqrf(Filter->MinSearchRadius);
|
||||
float MaxDistSq = sqrf(Filter->MaxSearchRadius);
|
||||
|
||||
bool bUseMinDist = MinDistSq > 0.1f;
|
||||
bool bUseMaxDist = MaxDistSq > 0.1f;
|
||||
|
||||
float CurrMinDist = 0;
|
||||
|
||||
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
|
||||
{
|
||||
if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE)
|
||||
{
|
||||
unsigned int CheckReachabilityFlags = (it->TeamAReachabilityFlags | it->TeamBReachabilityFlags);
|
||||
|
||||
if (Filter->ReachabilityTeam != TEAM_IND)
|
||||
{
|
||||
CheckReachabilityFlags = (Filter->ReachabilityTeam == GetGameRules()->GetTeamANumber()) ? it->TeamAReachabilityFlags : it->TeamBReachabilityFlags;
|
||||
}
|
||||
|
||||
if (!(CheckReachabilityFlags & Filter->ReachabilityFlags)) { continue; }
|
||||
}
|
||||
|
||||
|
||||
if (it->OwningTeam != Filter->DeployableTeam) { continue; }
|
||||
|
||||
float DistSq = (Filter->bConsiderPhaseDistance) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(it->Location, Location)) : vDist2DSq(it->Location, Location);
|
||||
|
||||
if ((!bUseMinDist || DistSq >= MinDistSq) && (!bUseMaxDist || DistSq <= MaxDistSq) && (!Result || DistSq < CurrMinDist))
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
vector<AvHAIResourceNode*> AITAC_GetAllMatchingResourceNodes(const Vector Location, const DeployableSearchFilter* Filter)
|
||||
{
|
||||
vector<AvHAIResourceNode*> Results;
|
||||
|
||||
float MinDistSq = sqrf(Filter->MinSearchRadius);
|
||||
float MaxDistSq = sqrf(Filter->MaxSearchRadius);
|
||||
|
||||
bool bUseMinDist = MinDistSq > 0.1f;
|
||||
bool bUseMaxDist = MaxDistSq > 0.1f;
|
||||
|
||||
float CurrMinDist = 0;
|
||||
|
||||
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
|
||||
{
|
||||
if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE)
|
||||
{
|
||||
unsigned int CheckReachabilityFlags = (it->TeamAReachabilityFlags | it->TeamBReachabilityFlags);
|
||||
|
||||
if (Filter->ReachabilityTeam != TEAM_IND)
|
||||
{
|
||||
CheckReachabilityFlags = (Filter->ReachabilityTeam == GetGameRules()->GetTeamANumber()) ? it->TeamAReachabilityFlags : it->TeamBReachabilityFlags;
|
||||
}
|
||||
|
||||
if (!(CheckReachabilityFlags & Filter->ReachabilityFlags)) { continue; }
|
||||
}
|
||||
|
||||
|
||||
if (it->OwningTeam != Filter->DeployableTeam) { continue; }
|
||||
|
||||
float DistSq = (Filter->bConsiderPhaseDistance) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(it->Location, Location)) : vDist2DSq(it->Location, Location);
|
||||
|
||||
if ((!bUseMinDist || DistSq >= MinDistSq) && (!bUseMaxDist || DistSq <= MaxDistSq))
|
||||
{
|
||||
Results.push_back(&(*it));
|
||||
}
|
||||
}
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
AvHAIResourceNode* AITAC_FindNearestResourceNodeToLocation(const Vector Location, const DeployableSearchFilter* Filter)
|
||||
{
|
||||
AvHAIResourceNode* Result = nullptr;
|
||||
|
@ -2380,6 +2516,22 @@ AvHAIResourceNode* AITAC_FindNearestResourceNodeToLocation(const Vector Location
|
|||
|
||||
}
|
||||
|
||||
int AITAC_GetNumActivePlayersOnTeam(const AvHTeamNumber Team)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
for (int i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
edict_t* PlayerEdict = INDEXENT(i);
|
||||
|
||||
if (!FNullEnt(PlayerEdict) && !PlayerEdict->free && IsPlayerActiveInGame(PlayerEdict)) { Result++; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int AITAC_GetNumPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 IgnoreClass)
|
||||
{
|
||||
int Result = 0;
|
||||
|
@ -2996,7 +3148,7 @@ const AvHAIHiveDefinition* AITAC_GetNearestHiveUnderActiveSiege(AvHTeamNumber Si
|
|||
DeployableSearchFilter SiegeFilter;
|
||||
SiegeFilter.DeployableTypes = STRUCTURE_MARINE_ADVTURRETFACTORY;
|
||||
SiegeFilter.DeployableTeam = SiegingTeam;
|
||||
SiegeFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
|
||||
SiegeFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f);
|
||||
SiegeFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
SiegeFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
|
@ -3031,7 +3183,7 @@ edict_t* AITAC_GetMarineEligibleToBuildSiege(AvHTeamNumber Team, const AvHAIHive
|
|||
|
||||
edict_t* Result = nullptr;
|
||||
|
||||
vector<AvHPlayer*> TeamPlayers = AITAC_GetAllPlayersOnTeam(Team);
|
||||
vector<AvHPlayer*> TeamPlayers = AIMGR_GetAllPlayersOnTeam(Team);
|
||||
|
||||
float MinDist = 0.0f;
|
||||
|
||||
|
@ -3062,7 +3214,7 @@ edict_t* AITAC_GetNearestHiddenPlayerInLocation(AvHTeamNumber Team, const Vector
|
|||
edict_t* Result = nullptr;
|
||||
float MaxRadiusSq = sqrf(MaxRadius);
|
||||
|
||||
vector<AvHPlayer*> TeamPlayers = AITAC_GetAllPlayersOnTeam(Team);
|
||||
vector<AvHPlayer*> TeamPlayers = AIMGR_GetAllPlayersOnTeam(Team);
|
||||
|
||||
float MinDist = 0.0f;
|
||||
|
||||
|
@ -3088,28 +3240,7 @@ edict_t* AITAC_GetNearestHiddenPlayerInLocation(AvHTeamNumber Team, const Vector
|
|||
return Result;
|
||||
}
|
||||
|
||||
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
vector<AvHPlayer*> Result;
|
||||
|
||||
for (int i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
edict_t* PlayerEdict = INDEXENT(i);
|
||||
|
||||
if (!FNullEnt(PlayerEdict) && PlayerEdict->v.team == Team)
|
||||
{
|
||||
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
|
||||
|
||||
if (PlayerRef)
|
||||
{
|
||||
Result.push_back(PlayerRef);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes()
|
||||
{
|
||||
|
@ -3139,7 +3270,7 @@ bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, fl
|
|||
{
|
||||
float distSq = sqrf(SearchRadius);
|
||||
|
||||
vector<AvHPlayer*> Players = AITAC_GetAllPlayersOnTeam(Team);
|
||||
vector<AvHPlayer*> Players = AIMGR_GetAllPlayersOnTeam(Team);
|
||||
|
||||
for (auto it = Players.begin(); it != Players.end(); it++)
|
||||
{
|
||||
|
|
|
@ -27,7 +27,9 @@ AvHAIBuildableStructure* AITAC_FindFurthestDeployableFromLocation(const Vector&
|
|||
AvHAIBuildableStructure* AITAC_GetDeployableRefFromEdict(const edict_t* Structure);
|
||||
AvHAIBuildableStructure* AITAC_GetNearestDeployableDirectlyReachable(AvHAIPlayer* pBot, const Vector Location, const DeployableSearchFilter* Filter);
|
||||
int AITAC_GetNumDeployablesNearLocation(const Vector& Location, const DeployableSearchFilter* Filter);
|
||||
void AITAC_PopulateHiveData();
|
||||
void AITAC_RefreshHiveData();
|
||||
void AITAC_PopulateResourceNodes();
|
||||
void AITAC_RefreshResourceNodes();
|
||||
void AITAC_UpdateMapAIData();
|
||||
void AITAC_CheckNavMeshModified();
|
||||
|
@ -102,14 +104,20 @@ bool UTIL_StructureIsRecycling(edict_t* Structure);
|
|||
bool AITAC_StructureCanBeUpgraded(edict_t* Structure);
|
||||
|
||||
AvHAIHiveDefinition* AITAC_GetHiveFromEdict(const edict_t* Edict);
|
||||
AvHAIResourceNode* AITAC_GetResourceNodeFromEdict(const edict_t* Edict);
|
||||
|
||||
// What percentage of all viable (can be reached by the requested team) resource nodes does the team currently own? Expressed as 0.0 - 1.0
|
||||
float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team);
|
||||
int AITAC_GetNumResourceNodesNearLocation(const Vector Location, const DeployableSearchFilter* Filter);
|
||||
AvHAIResourceNode* AITAC_FindNearestResourceNodeToLocation(const Vector Location, const DeployableSearchFilter* Filter);
|
||||
AvHAIResourceNode* AITAC_GetNearestResourceNodeToLocation(const Vector Location);
|
||||
vector<AvHAIResourceNode*> AITAC_GetAllMatchingResourceNodes(const Vector Location, const DeployableSearchFilter* Filter);
|
||||
|
||||
bool UTIL_IsBuildableStructureStillReachable(AvHAIPlayer* pBot, const edict_t* Structure);
|
||||
bool UTIL_IsDroppedItemStillReachable(AvHAIPlayer* pBot, const edict_t* Item);
|
||||
AvHAIWeapon UTIL_GetWeaponTypeFromEdict(const edict_t* ItemEdict);
|
||||
|
||||
int AITAC_GetNumActivePlayersOnTeam(const AvHTeamNumber Team);
|
||||
int AITAC_GetNumPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 IgnoreClass);
|
||||
int AITAC_GetNumPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer);
|
||||
edict_t* AITAC_GetNearestPlayerOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
|
||||
|
@ -143,7 +151,6 @@ int AITAC_GetNumDeadPlayersOnTeam(const AvHTeamNumber Team);
|
|||
const AvHAIHiveDefinition* AITAC_GetNearestHiveUnderActiveSiege(AvHTeamNumber SiegingTeam, const Vector SearchLocation);
|
||||
edict_t* AITAC_GetMarineEligibleToBuildSiege(AvHTeamNumber Team, const AvHAIHiveDefinition* Hive);
|
||||
|
||||
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeam(AvHTeamNumber Team);
|
||||
edict_t* AITAC_GetNearestHiddenPlayerInLocation(AvHTeamNumber Team, const Vector Location, const float MaxRadius);
|
||||
|
||||
const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes();
|
||||
|
|
|
@ -332,6 +332,17 @@ bool AITASK_IsTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
}
|
||||
case TASK_REINFORCE_STRUCTURE:
|
||||
return AITASK_IsReinforceStructureTaskStillValid(pBot, Task);
|
||||
case TASK_SECURE_HIVE:
|
||||
{
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
{
|
||||
return AITASK_IsMarineSecureHiveTaskStillValid(pBot, Task);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case TASK_DEFEND:
|
||||
return AITASK_IsDefendTaskStillValid(pBot, Task);
|
||||
case TASK_WELD:
|
||||
|
@ -548,11 +559,15 @@ bool AITASK_IsMarineBuildTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
return false;
|
||||
}
|
||||
|
||||
// Always go build if commanded to, regardless of how many are already working on it
|
||||
if (!Task->bIssuedByCommander)
|
||||
{
|
||||
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea((AvHTeamNumber)pBot->Edict->v.team, Task->TaskTarget->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), false, pBot->Edict, AVH_USER3_NONE);
|
||||
|
||||
// Only one marine should build stuff if it's near the marine base. If not, then two for safety
|
||||
int NumDesiredBuilders = (vDist2DSq(Task->TaskTarget->v.origin, AITAC_GetCommChairLocation(pBot->Player->GetTeam())) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) ? 1 : 2;
|
||||
|
||||
if (NumBuilders >= 2)
|
||||
if (NumBuilders >= NumDesiredBuilders)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -764,20 +779,17 @@ bool AITASK_IsMarineCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
// Always obey commander orders even if there's a bunch of other marines already there
|
||||
if (!Task->bIssuedByCommander)
|
||||
{
|
||||
int NumMarinesNearby = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), Task->TaskLocation, UTIL_MetresToGoldSrcUnits(4.0f), false, pBot->Edict, AVH_USER3_NONE);
|
||||
int DesiredNumCappers = (ResNodeIndex->OwningTeam == AIMGR_GetEnemyTeam(pBot->Player->GetTeam())) ? 2 : 1;
|
||||
int NumMarinesNearby = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), Task->TaskLocation, UTIL_MetresToGoldSrcUnits(4.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
||||
if (NumMarinesNearby >= 2 && vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) > sqrf(UTIL_MetresToGoldSrcUnits(4.0f))) { return false; }
|
||||
if (NumMarinesNearby >= DesiredNumCappers && vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) > sqrf(UTIL_MetresToGoldSrcUnits(4.0f))) { return false; }
|
||||
}
|
||||
|
||||
if (ResNodeIndex->bIsOccupied)
|
||||
{
|
||||
if (ResNodeIndex->OwningTeam == pBot->Player->GetTeam() && !FNullEnt(ResNodeIndex->ActiveTowerEntity))
|
||||
if (ResNodeIndex->OwningTeam == pBot->Player->GetTeam())
|
||||
{
|
||||
return !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
return (FNullEnt(ResNodeIndex->ActiveTowerEntity) || !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -876,6 +888,67 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
if (!Task || FNullEnt(Task->TaskTarget) || IsPlayerAlien(pBot->Edict)) { return false; }
|
||||
|
||||
AvHAIHiveDefinition* HiveToSecure = AITAC_GetHiveFromEdict(Task->TaskTarget);
|
||||
|
||||
if (!HiveToSecure || HiveToSecure->Status != HIVE_STATUS_UNBUILT) { return false; }
|
||||
|
||||
// A marine bot will consider their "secure hive" task completed if the following structures have been fully built:
|
||||
// Phase gate (only if tech available)
|
||||
// Turret factory (regular or advanced)
|
||||
// 5 turrets
|
||||
// Resource node has been capped by the bot's team
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
bool bPhaseGatesAvailable = AITAC_PhaseGatesAvailable(BotTeam);
|
||||
|
||||
bool bHasPhaseGate = false;
|
||||
bool bHasTurretFactory = false;
|
||||
bool bTurretFactoryElectrified = false;
|
||||
int NumTurrets = 0;
|
||||
|
||||
DeployableSearchFilter SearchFilter;
|
||||
SearchFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
|
||||
SearchFilter.DeployableTeam = BotTeam;
|
||||
SearchFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
SearchFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
SearchFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
vector<AvHAIBuildableStructure*> HiveStructures = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &SearchFilter);
|
||||
|
||||
for (auto it = HiveStructures.begin(); it != HiveStructures.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure* Structure = (*it);
|
||||
|
||||
if (Structure->StructureType == STRUCTURE_MARINE_TURRETFACTORY)
|
||||
{
|
||||
bHasPhaseGate = true;
|
||||
}
|
||||
|
||||
if (Structure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || Structure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
|
||||
{
|
||||
bHasTurretFactory = true;
|
||||
|
||||
SearchFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
|
||||
SearchFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(8.0f);
|
||||
|
||||
NumTurrets = AITAC_GetNumDeployablesNearLocation(Structure->Location, &SearchFilter);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AvHAIResourceNode* ResNode = HiveToSecure->HiveResNodeRef;
|
||||
|
||||
bool bSecuredResNode = (!ResNode || (ResNode->OwningTeam == BotTeam && !FNullEnt(ResNode->ActiveTowerEntity) && UTIL_StructureIsFullyBuilt(ResNode->ActiveTowerEntity)));
|
||||
|
||||
return !((!bPhaseGatesAvailable || bHasPhaseGate) && bHasTurretFactory && NumTurrets >= 5 && bSecuredResNode);
|
||||
}
|
||||
|
||||
bool AITASK_IsEvolveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
if (!Task || Task->Evolution == MESSAGE_NULL || !IsPlayerAlien(pBot->Edict)) { return false; }
|
||||
|
@ -1438,6 +1511,88 @@ void BotProgressResupplyTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
}
|
||||
|
||||
void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget)
|
||||
{
|
||||
if (!pBot || !IsPlayerActiveInGame(pBot->Edict) || FNullEnt(BuildTarget) || UTIL_StructureIsFullyBuilt(BuildTarget)) { return; }
|
||||
|
||||
if (IsPlayerAlien(pBot->Edict) && !IsPlayerGorge(pBot->Edict)) { return; }
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
{
|
||||
// If we're not already building
|
||||
if (pBot->Edict->v.viewmodel != 0)
|
||||
{
|
||||
// If someone else is building, then we will guard
|
||||
edict_t* OtherBuilder = AITAC_GetClosestPlayerOnTeamWithLOS(pBot->Player->GetTeam(), BuildTarget->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), pBot->Edict);
|
||||
|
||||
if (!FNullEnt(OtherBuilder) && OtherBuilder->v.weaponmodel == 0)
|
||||
{
|
||||
BotGuardLocation(pBot, BuildTarget->v.origin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPlayerInUseRange(pBot->Edict, BuildTarget))
|
||||
{
|
||||
// If we were ducking before then keep ducking
|
||||
if (pBot->Edict->v.oldbuttons & IN_DUCK)
|
||||
{
|
||||
pBot->Button |= IN_DUCK;
|
||||
}
|
||||
|
||||
BotUseObject(pBot, BuildTarget, true);
|
||||
|
||||
// Haven't started building, maybe not quite looking at the right angle
|
||||
if (pBot->Edict->v.weaponmodel != 0)
|
||||
{
|
||||
if (vDist2DSq(pBot->Edict->v.origin, BuildTarget->v.origin) > sqrf(60.0f))
|
||||
{
|
||||
MoveDirectlyTo(pBot, BuildTarget->v.origin);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector NewViewPoint = UTIL_GetRandomPointInBoundingBox(BuildTarget->v.absmin, BuildTarget->v.absmax);
|
||||
|
||||
BotLookAt(pBot, NewViewPoint);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Might need to duck if it's an infantry portal
|
||||
if (vDist2DSq(pBot->Edict->v.origin, BuildTarget->v.origin) < sqrf(max_player_use_reach))
|
||||
{
|
||||
if (BuildTarget->v.origin > pBot->Edict->v.origin)
|
||||
{
|
||||
BotJump(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
pBot->Button |= IN_DUCK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MoveTo(pBot, BuildTarget->v.origin, MOVESTYLE_NORMAL);
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
{
|
||||
if (gpGlobals->time - pBot->LastCombatTime > 5.0f)
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
}
|
||||
}
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, BuildTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
BotLookAt(pBot, UTIL_GetCentreOfEntity(BuildTarget));
|
||||
}
|
||||
}
|
||||
|
||||
void MarineProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
edict_t* pEdict = pBot->Edict;
|
||||
|
@ -2314,70 +2469,66 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (!Hive) { return; }
|
||||
|
||||
bool bWaitForBuildingPlacement = false;
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
|
||||
StructureFilter.DeployableTypes = SEARCH_ALL_MARINE_STRUCTURES;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
StructureFilter.DeployableTeam = BotTeam;
|
||||
StructureFilter.ReachabilityTeam = BotTeam;
|
||||
StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_COMPLETED;
|
||||
|
||||
AvHAIBuildableStructure* TF = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
|
||||
vector<AvHAIBuildableStructure*> BuildableStructures = AITAC_FindAllDeployables(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
if (!TF || !(TF->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { bWaitForBuildingPlacement = true; }
|
||||
AvHAIBuildableStructure* StructureToBuild = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
bool bPhaseGatesAvailable = AITAC_ResearchIsComplete(pBot->Player->GetTeam(), TECH_PHASE_GATE);
|
||||
|
||||
if (bPhaseGatesAvailable && !bWaitForBuildingPlacement)
|
||||
for (auto it = BuildableStructures.begin(); it != BuildableStructures.end(); it++)
|
||||
{
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
|
||||
AvHAIBuildableStructure* PhaseGate = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
if (!PhaseGate || !(TF->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { bWaitForBuildingPlacement = true; }
|
||||
}
|
||||
|
||||
if (!bWaitForBuildingPlacement)
|
||||
{
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
|
||||
|
||||
int NumTurrets = AITAC_GetNumDeployablesNearLocation(TF->Location, &StructureFilter);
|
||||
|
||||
if (NumTurrets < 5) { bWaitForBuildingPlacement = true; }
|
||||
}
|
||||
|
||||
if (bWaitForBuildingPlacement)
|
||||
{
|
||||
if (TF)
|
||||
{
|
||||
BotGuardLocation(pBot, TF->Location);
|
||||
}
|
||||
else
|
||||
{
|
||||
BotGuardLocation(pBot, Task->TaskLocation);
|
||||
}
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
|
||||
{
|
||||
AIPlayerBuildStructure(pBot, ThisStructure->edict);
|
||||
return;
|
||||
}
|
||||
|
||||
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location);
|
||||
|
||||
if (!StructureToBuild || ThisDist < MinDist)
|
||||
{
|
||||
StructureToBuild = ThisStructure;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToBuild)
|
||||
{
|
||||
AIPlayerBuildStructure(pBot, StructureToBuild->edict);
|
||||
return;
|
||||
}
|
||||
|
||||
const AvHAIResourceNode* ResNode = Hive->HiveResNodeRef;
|
||||
|
||||
if (ResNode && ResNode->OwningTeam != pBot->Player->GetTeam())
|
||||
if (ResNode && ResNode->bIsOccupied)
|
||||
{
|
||||
if (ResNode->bIsOccupied)
|
||||
if (ResNode->OwningTeam != BotTeam)
|
||||
{
|
||||
BotAttackTarget(pBot, ResNode->ActiveTowerEntity);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
BotGuardLocation(pBot, ResNode->Location);
|
||||
if (!UTIL_StructureIsFullyBuilt(ResNode->ActiveTowerEntity))
|
||||
{
|
||||
AIPlayerBuildStructure(pBot, ResNode->ActiveTowerEntity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BotGuardLocation(pBot, Task->TaskLocation);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -2413,24 +2564,7 @@ void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
if (!UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
|
||||
{
|
||||
// Now we're committed, don't get distracted
|
||||
Task->bTaskIsUrgent = true;
|
||||
if (UTIL_PlayerHasLOSToEntity(pBot->Edict, ResNodeIndex->ActiveTowerEntity, max_player_use_reach, true))
|
||||
{
|
||||
BotUseObject(pBot, ResNodeIndex->ActiveTowerEntity, true);
|
||||
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->ActiveTowerEntity->v.origin) > sqrf(50.0f))
|
||||
{
|
||||
MoveDirectlyTo(pBot, ResNodeIndex->ActiveTowerEntity->v.origin);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MoveTo(pBot, ResNodeIndex->ActiveTowerEntity->v.origin, MOVESTYLE_NORMAL);
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->ActiveTowerEntity->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
BotLookAt(pBot, UTIL_GetCentreOfEntity(ResNodeIndex->ActiveTowerEntity));
|
||||
}
|
||||
AIPlayerBuildStructure(pBot, ResNodeIndex->ActiveTowerEntity);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ bool AITASK_IsDefendTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
|||
bool AITASK_IsEvolveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
bool AITASK_IsAlienGetHealthTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
bool AITASK_IsAlienHealTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
@ -87,6 +88,8 @@ void MarineProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
|||
void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget);
|
||||
|
||||
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
void AlienProgressGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
|
Loading…
Reference in a new issue