mirror of
https://github.com/ENSL/NS.git
synced 2024-11-10 07:11:38 +00:00
Commander + nav improvements
This commit is contained in:
parent
7dcd3ca1d8
commit
eacf2b174f
10 changed files with 477 additions and 185 deletions
|
@ -1089,6 +1089,96 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
|
||||
|
||||
int NumDesiredWelders = 1;
|
||||
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
|
||||
|
||||
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
|
||||
|
||||
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
|
||||
{
|
||||
AvHAIResourceNode* ThisNode = (*it);
|
||||
|
||||
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisNode->TeamAReachabilityFlags : ThisNode->TeamBReachabilityFlags;
|
||||
|
||||
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
|
||||
{
|
||||
NumDesiredWelders++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
|
||||
|
||||
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
|
||||
{
|
||||
AvHAIHiveDefinition* ThisHive = (*it);
|
||||
|
||||
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisHive->TeamAReachabilityFlags : ThisHive->TeamBReachabilityFlags;
|
||||
|
||||
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
|
||||
{
|
||||
NumDesiredWelders++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NumDesiredWelders = imini(NumDesiredWelders, (AIMGR_GetNumPlayersOnTeam(CommanderTeam) / 2));
|
||||
|
||||
if (NumTeamWelders < NumDesiredWelders)
|
||||
{
|
||||
DeployableSearchFilter ArmouryFilter;
|
||||
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
|
||||
ArmouryFilter.DeployableTeam = CommanderTeam;
|
||||
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
|
||||
|
||||
if (NearestArmoury)
|
||||
{
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_WELDER, DeployLocation);
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't drop stuff if we badly need resource nodes
|
||||
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot)) { return false; }
|
||||
|
||||
if (pBot->Player->GetResources() > (30 + BALANCE_VAR(kShotgunCost)))
|
||||
{
|
||||
int NumDesiredShotguns = (int)ceilf(AIMGR_GetNumPlayersOnTeam(CommanderTeam) * 0.33f);
|
||||
int NumShottysInPlay = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_SHOTGUN);
|
||||
|
||||
if (NumShottysInPlay < NumDesiredShotguns)
|
||||
{
|
||||
DeployableSearchFilter ArmouryFilter;
|
||||
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
|
||||
ArmouryFilter.DeployableTeam = CommanderTeam;
|
||||
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
|
||||
|
||||
if (NearestArmoury)
|
||||
{
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_SHOTGUN, DeployLocation);
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
|
||||
|
@ -1776,66 +1866,8 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
|
||||
}
|
||||
|
||||
// We didn't find any requests outstanding, see if we want to pro-actively drop stuff for our team
|
||||
if (!NextRequest)
|
||||
{
|
||||
int NumDesiredWelders = 1;
|
||||
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
|
||||
|
||||
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
|
||||
|
||||
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
|
||||
{
|
||||
AvHAIResourceNode* ThisNode = (*it);
|
||||
|
||||
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisNode->TeamAReachabilityFlags : ThisNode->TeamBReachabilityFlags;
|
||||
|
||||
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
|
||||
{
|
||||
NumDesiredWelders++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
|
||||
|
||||
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
|
||||
{
|
||||
AvHAIHiveDefinition* ThisHive = (*it);
|
||||
|
||||
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisHive->TeamAReachabilityFlags : ThisHive->TeamBReachabilityFlags;
|
||||
|
||||
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
|
||||
{
|
||||
NumDesiredWelders++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NumDesiredWelders = imini(NumDesiredWelders, (AIMGR_GetNumPlayersOnTeam(CommanderTeam) / 2));
|
||||
|
||||
if (NumTeamWelders < NumDesiredWelders)
|
||||
{
|
||||
DeployableSearchFilter ArmouryFilter;
|
||||
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
|
||||
ArmouryFilter.DeployableTeam = CommanderTeam;
|
||||
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
|
||||
|
||||
if (NearestArmoury)
|
||||
{
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_WELDER, DeployLocation);
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// We didn't find any unresponded requests outstanding
|
||||
if (!NextRequest) { return false; }
|
||||
|
||||
edict_t* Requestor = NextRequest->Requestor;
|
||||
|
||||
|
@ -2050,6 +2082,7 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot)
|
|||
if (AICOMM_CheckForNextSupportAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextBuildAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextResearchAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextSupplyAction(pBot)) { return; }
|
||||
}
|
||||
|
||||
bool AICOMM_IsCommanderActionValid(AvHAIPlayer* pBot, commander_action* Action)
|
||||
|
|
|
@ -35,10 +35,13 @@ bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* O
|
|||
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order);
|
||||
|
||||
void AICOMM_ClearAction(commander_action* Action);
|
||||
|
||||
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot);
|
||||
|
||||
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
|
||||
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
|
||||
void AICOMM_SetDeployStructureAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableStructureType StructureToBuild, const Vector Location, bool bIsUrgent);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "../AvHEntities.h"
|
||||
|
||||
static const float commander_action_cooldown = 1.0f;
|
||||
static const float min_request_spam_time = 5.0f;
|
||||
static const float min_request_spam_time = 10.0f;
|
||||
|
||||
constexpr auto MAX_AI_PATH_SIZE = 512; // Maximum number of points allowed in a path (this should be enough for any sized map)
|
||||
static const int MAX_NAV_MESHES = 8; // Max number of nav meshes allowed. Currently 3 are used (one for building placement, one for the onos, and a regular one for everyone else)
|
||||
|
|
|
@ -1653,7 +1653,7 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
|
|||
{
|
||||
NextPathNode.Location = hit.vecEndPos;
|
||||
|
||||
if (CurrFlags != SAMPLE_POLYFLAGS_JUMP)
|
||||
if (CurrFlags != SAMPLE_POLYFLAGS_JUMP && CurrFlags != SAMPLE_POLYFLAGS_WALLCLIMB)
|
||||
{
|
||||
NextPathNode.Location.z += 20.0f;
|
||||
}
|
||||
|
@ -1664,7 +1664,8 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
|
|||
if (CurrFlags == SAMPLE_POLYFLAGS_WALLCLIMB || CurrFlags == SAMPLE_POLYFLAGS_LADDER)
|
||||
{
|
||||
float NewRequiredZ = UTIL_FindZHeightForWallClimb(path.back().Location, NextPathNode.Location, head_hull);
|
||||
NextPathNode.requiredZ = fmaxf(NewRequiredZ, NextPathNode.Location.z);
|
||||
//NextPathNode.requiredZ = fmaxf(NewRequiredZ, NextPathNode.Location.z);
|
||||
NextPathNode.requiredZ = NewRequiredZ;
|
||||
|
||||
if (CurrFlags == SAMPLE_POLYFLAGS_LADDER)
|
||||
{
|
||||
|
@ -1813,7 +1814,7 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
|
|||
|
||||
NextPathNode.Location = AdjustPointForPathfinding(NextPathNode.Location);
|
||||
|
||||
if (CurrFlags != SAMPLE_POLYFLAGS_JUMP || NextPathNode.FromLocation.z > NextPathNode.Location.z)
|
||||
if ((CurrFlags != SAMPLE_POLYFLAGS_JUMP && CurrFlags != SAMPLE_POLYFLAGS_WALLCLIMB) || NextPathNode.FromLocation.z > NextPathNode.Location.z)
|
||||
{
|
||||
|
||||
NextPathNode.Location.z += 17.0f;
|
||||
|
@ -1951,6 +1952,11 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition;
|
||||
|
||||
edict_t* pEdict = pBot->Edict;
|
||||
|
@ -1976,6 +1982,7 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
switch (CurrentNavFlag)
|
||||
{
|
||||
case SAMPLE_POLYFLAGS_WALK:
|
||||
{
|
||||
if (!bIsAtFinalPathPoint)
|
||||
{
|
||||
return (bAtOrPastDestination || (vDist2D(pEdict->v.origin, MoveTo) <= 8.0f && (fabs(pBot->CurrentFloorPosition.z - MoveTo.z) < 50.0f)));
|
||||
|
@ -1984,12 +1991,14 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
{
|
||||
return ((vDist2D(pEdict->v.origin, MoveTo) < playerRadius && bDestIsDirectlyReachable) || bAtOrPastDestination);
|
||||
}
|
||||
}
|
||||
case SAMPLE_POLYFLAGS_BLOCKED:
|
||||
case SAMPLE_POLYFLAGS_TEAM1STRUCTURE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2STRUCTURE:
|
||||
return bAtOrPastDestination;
|
||||
case SAMPLE_POLYFLAGS_FALL:
|
||||
case SAMPLE_POLYFLAGS_JUMP:
|
||||
{
|
||||
if (!bIsAtFinalPathPoint)
|
||||
{
|
||||
Vector thisMoveDir = UTIL_GetVectorNormal2D(MoveTo - MoveFrom);
|
||||
|
@ -2010,9 +2019,11 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
{
|
||||
return (vDist2D(pEdict->v.origin, MoveTo) <= playerRadius && (pEdict->v.origin.z - MoveTo.z) < 50.0f && pBot->BotNavInfo.IsOnGround);
|
||||
}
|
||||
}
|
||||
case SAMPLE_POLYFLAGS_WALLCLIMB:
|
||||
return (bAtOrPastDestination && fabs(pBot->CollisionHullTopLocation.z - MoveTo.z) < fabs(MoveFrom.z - MoveTo.z));
|
||||
return bAtOrPastDestination && fabs(pEdict->v.origin.z - MoveTo.z) < 50.0f;
|
||||
case SAMPLE_POLYFLAGS_LADDER:
|
||||
{
|
||||
if (MoveTo.z > MoveFrom.z)
|
||||
{
|
||||
return ((BotPoly == DestinationPoly) && UTIL_QuickTrace(pEdict, pEdict->v.origin, MoveTo));
|
||||
|
@ -2021,6 +2032,7 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
{
|
||||
return (fabs(pBot->CollisionHullBottomLocation.z - MoveTo.z) < 50.0f);
|
||||
}
|
||||
}
|
||||
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
|
||||
return (vDist2DSq(pBot->CurrentFloorPosition, MoveTo) < sqrf(32.0f));
|
||||
|
@ -4045,6 +4057,7 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
|
|||
|
||||
if (VelocityDot > 0.7f)
|
||||
{
|
||||
// This was causing issues in tight areas, rethink this
|
||||
//BotJump(pBot);
|
||||
}
|
||||
}
|
||||
|
@ -5745,6 +5758,12 @@ void BotFollowSwimPath(AvHAIPlayer* pBot)
|
|||
return;
|
||||
}
|
||||
|
||||
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
|
||||
{
|
||||
ClearBotPath(pBot);
|
||||
return;
|
||||
}
|
||||
|
||||
nav_status* BotNavInfo = &pBot->BotNavInfo;
|
||||
edict_t* pEdict = pBot->Edict;
|
||||
|
||||
|
@ -5826,6 +5845,12 @@ void BotFollowPath(AvHAIPlayer* pBot)
|
|||
return;
|
||||
}
|
||||
|
||||
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
|
||||
{
|
||||
ClearBotPath(pBot);
|
||||
return;
|
||||
}
|
||||
|
||||
nav_status* BotNavInfo = &pBot->BotNavInfo;
|
||||
edict_t* pEdict = pBot->Edict;
|
||||
|
||||
|
@ -8136,6 +8161,8 @@ void NAV_SetBreakMovementTask(AvHAIPlayer* pBot, edict_t* EntityToBreak, DoorTri
|
|||
|
||||
void NAV_SetWeldMovementTask(AvHAIPlayer* pBot, edict_t* EntityToWeld, DoorTrigger* TriggerToActivate)
|
||||
{
|
||||
if (IsPlayerAlien(pBot->Edict)) { return; }
|
||||
|
||||
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
|
||||
|
||||
if (MoveTask->TaskType == MOVE_TASK_WELD && MoveTask->TaskTarget == EntityToWeld) { return; }
|
||||
|
|
|
@ -2497,6 +2497,7 @@ void AIPlayerNSMarineThink(AvHAIPlayer* pBot)
|
|||
|
||||
AIPlayerSetPrimaryMarineTask(pBot, &pBot->PrimaryBotTask);
|
||||
AIPlayerSetSecondaryMarineTask(pBot, &pBot->SecondaryBotTask);
|
||||
AIPlayerSetWantsAndNeedsMarineTask(pBot, &pBot->WantsAndNeedsTask);
|
||||
}
|
||||
|
||||
pBot->CurrentTask = AIPlayerGetNextTask(pBot);
|
||||
|
@ -3162,6 +3163,107 @@ void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
|
||||
}
|
||||
|
||||
void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
if (Task->TaskType == TASK_RESUPPLY || Task->TaskType == TASK_GET_HEALTH || Task->TaskType == TASK_GET_AMMO) { return; }
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
bool bNeedsHealth = pBot->Edict->v.health < (pBot->Edict->v.max_health * 0.9f);
|
||||
bool bNeedsAmmo = UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < (UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player) * 0.9f);
|
||||
|
||||
if (!bNeedsHealth && !bNeedsAmmo)
|
||||
{
|
||||
AITASK_ClearBotTask(pBot, Task);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bTaskIsUrgent = (pBot->Edict->v.health < (pBot->Edict->v.max_health * 0.7f)) || (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player));
|
||||
|
||||
float SearchRadius = (bTaskIsUrgent) ? UTIL_MetresToGoldSrcUnits(5.0f) : UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIDroppedItem* NearestHealthPack = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_HEALTHPACK, BotTeam, pBot->BotNavInfo.NavProfile.ReachabilityFlag, 0.0f, SearchRadius, false);
|
||||
AvHAIDroppedItem* NearestAmmoPack = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_AMMO, BotTeam, pBot->BotNavInfo.NavProfile.ReachabilityFlag, 0.0f, SearchRadius, false);
|
||||
|
||||
if (bNeedsHealth && NearestHealthPack)
|
||||
{
|
||||
AITASK_SetPickupTask(pBot, Task, NearestHealthPack->edict, bTaskIsUrgent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bNeedsAmmo && NearestAmmoPack)
|
||||
{
|
||||
AITASK_SetPickupTask(pBot, Task, NearestAmmoPack->edict, bTaskIsUrgent);
|
||||
return;
|
||||
}
|
||||
|
||||
DeployableSearchFilter NearestArmouryFilter;
|
||||
NearestArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
|
||||
NearestArmouryFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
NearestArmouryFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
NearestArmouryFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
NearestArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
NearestArmouryFilter.MaxSearchRadius = (bTaskIsUrgent) ? UTIL_MetresToGoldSrcUnits(20.0f) : UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &NearestArmouryFilter);
|
||||
|
||||
// If we don't really need health or ammo and there isn't a handy armoury nearby, just get on with things
|
||||
if (!NearestArmoury && !bTaskIsUrgent)
|
||||
{
|
||||
AITASK_ClearBotTask(pBot, Task);
|
||||
return;
|
||||
}
|
||||
|
||||
// We really need some health or ammo, either hit the armoury, or ask for a resupply
|
||||
if (NearestArmoury)
|
||||
{
|
||||
Task->TaskType = TASK_RESUPPLY;
|
||||
Task->bTaskIsUrgent = true;
|
||||
Task->TaskLocation = NearestArmoury->Location;
|
||||
Task->TaskTarget = NearestArmoury->edict;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AITASK_ClearBotTask(pBot, Task);
|
||||
if (bNeedsHealth)
|
||||
{
|
||||
AIPlayerRequestHealth(pBot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bNeedsAmmo)
|
||||
{
|
||||
AIPlayerRequestAmmo(pBot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AIPlayerRequestHealth(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (gpGlobals->time - pBot->LastRequestTime < min_request_spam_time) { return; }
|
||||
|
||||
pBot->Impulse = SAYING_4;
|
||||
pBot->LastRequestTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
void AIPlayerRequestAmmo(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (gpGlobals->time - pBot->LastRequestTime < min_request_spam_time) { return; }
|
||||
|
||||
pBot->Impulse = SAYING_5;
|
||||
pBot->LastRequestTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
void AIPlayerRequestOrder(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (gpGlobals->time - pBot->LastRequestTime < min_request_spam_time) { return; }
|
||||
|
||||
pBot->Impulse = SAYING_6;
|
||||
pBot->LastRequestTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
// Find any nearby unbuilt structures
|
||||
|
@ -3205,8 +3307,51 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
}
|
||||
|
||||
bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
if (!Task || Task->TaskType == TASK_NONE) { return false; }
|
||||
|
||||
if (!AITASK_IsTaskStillValid(pBot, Task)) { return false; }
|
||||
|
||||
if (IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return true; }
|
||||
|
||||
// If we're already capping a node, are at the node and there is an unfinished tower on there, then finish the job and don't move on yet
|
||||
if (Task->TaskType == TASK_CAP_RESNODE)
|
||||
{
|
||||
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
|
||||
|
||||
if (ResNodeIndex && ResNodeIndex->OwningTeam == BotTeam)
|
||||
{
|
||||
if (!FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (!pBot->CurrentTask) { pBot->CurrentTask = &pBot->PrimaryBotTask; }
|
||||
|
||||
if (pBot->CurrentEnemy > -1)
|
||||
{
|
||||
if (AlienCombatThink(pBot)) { return; }
|
||||
}
|
||||
|
||||
if (AIPlayerMustFinishCurrentTask(pBot, pBot->CurrentTask))
|
||||
{
|
||||
BotProgressTask(pBot, pBot->CurrentTask);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAIAlienPlayerNSRole(pBot);
|
||||
|
||||
AvHAIHiveDefinition* HiveToBuild = nullptr;
|
||||
|
@ -3217,8 +3362,6 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!pBot->CurrentTask) { pBot->CurrentTask = &pBot->PrimaryBotTask; }
|
||||
|
||||
if (gpGlobals->time >= pBot->BotNextTaskEvaluationTime)
|
||||
{
|
||||
pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f);
|
||||
|
@ -3231,11 +3374,6 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
|
|||
|
||||
pBot->CurrentTask = AIPlayerGetNextTask(pBot);
|
||||
|
||||
if (pBot->CurrentEnemy > -1)
|
||||
{
|
||||
if (AlienCombatThink(pBot)) { return; }
|
||||
}
|
||||
|
||||
if (pBot->LastCombatTime > 5.0f)
|
||||
{
|
||||
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_DEFENCE) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_DEFENCE))
|
||||
|
@ -3594,7 +3732,7 @@ void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
DeployableSearchFilter ExistingReinforcementFilter;
|
||||
ExistingReinforcementFilter.DeployableTeam = BotTeam;
|
||||
ExistingReinforcementFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
|
||||
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
|
||||
vector<AvHAIBuildableStructure*> AllReinforcingStructures = AITAC_FindAllDeployables(ThisHive->FloorLocation, &ExistingReinforcementFilter);
|
||||
|
||||
|
@ -3713,6 +3851,7 @@ void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
if (!FNullEnt(TowerToReinforce))
|
||||
{
|
||||
AITASK_SetReinforceStructureTask(pBot, Task, TowerToReinforce, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3723,6 +3862,23 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
AvHAIResourceNode* NodeToCap = nullptr;
|
||||
|
||||
// If we're already capping a node, are at the node and there is an unfinished tower on there, then finish the job and don't move on yet
|
||||
if (Task->TaskType == TASK_CAP_RESNODE)
|
||||
{
|
||||
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
|
||||
|
||||
if (ResNodeIndex && ResNodeIndex->OwningTeam == BotTeam)
|
||||
{
|
||||
if (!FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
|
||||
{
|
||||
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float ResourcesRequired = BALANCE_VAR(kResourceTowerCost);
|
||||
|
||||
if (!IsPlayerGorge(pBot->Edict))
|
||||
|
@ -3898,6 +4054,46 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
|
||||
if (IsPlayerGorge(pBot->Edict) && gpGlobals->time - pBot->LastCombatTime > 5.0f)
|
||||
{
|
||||
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_ONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pBot->Player->GetResources() >= BALANCE_VAR(kOnosCost))
|
||||
{
|
||||
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
|
||||
|
||||
if (NumOnos < 2)
|
||||
{
|
||||
const AvHAIHiveDefinition* NearestHive = AITAC_GetNearestTeamHive(BotTeam, pBot->Edict->v.origin, true);
|
||||
|
||||
if (NearestHive)
|
||||
{
|
||||
AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_FIVE, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pBot->Player->GetResources() >= BALANCE_VAR(kFadeCost))
|
||||
{
|
||||
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
|
||||
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
|
||||
|
||||
if (NumFades < 2 || NumOnos >= 2)
|
||||
{
|
||||
const AvHAIHiveDefinition* NearestHive = AITAC_GetNearestTeamHive(BotTeam, pBot->Edict->v.origin, true);
|
||||
|
||||
if (NearestHive)
|
||||
{
|
||||
AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_FOUR, true);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Vector EnemyBaseLocation = AITAC_GetTeamStartingLocation(EnemyTeam);
|
||||
|
||||
AvHAIHiveDefinition* HiveToGuard = nullptr;
|
||||
|
@ -3923,7 +4119,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
if (IsPlayerSkulk(pBot->Edict) || IsPlayerLerk(pBot->Edict))
|
||||
{
|
||||
EnemyStuffFilter.ExcludeStatusFlags |= STRUCTURE_STATUS_RECYCLING;
|
||||
EnemyStuffFilter.ExcludeStatusFlags |= STRUCTURE_STATUS_ELECTRIFIED;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -3931,6 +4127,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
EnemyStuffFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
|
||||
}
|
||||
|
||||
bool bShouldGuardEmptyHive = pBot->Player->GetUser3() < AVH_USER3_ALIEN_PLAYER3;
|
||||
|
||||
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
|
||||
{
|
||||
AvHAIHiveDefinition* ThisHive = (*it);
|
||||
|
@ -3951,6 +4149,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!bShouldGuardEmptyHive) { continue; }
|
||||
|
||||
DeployableSearchFilter FriendlyStuffFilter;
|
||||
|
||||
FriendlyStuffFilter.DeployableTeam = BotTeam;
|
||||
|
@ -3959,6 +4159,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
|
||||
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &FriendlyStuffFilter)) { continue; }
|
||||
|
||||
if (AITAC_GetNumPlayersOfTeamAndClassInArea(BotTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(20.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2) == 0) { continue; }
|
||||
|
||||
bool bNeedsExtraGuards = true;
|
||||
int NumGuards = 0;
|
||||
|
||||
|
@ -3999,126 +4201,75 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
MaxGuardDist = ThisDist;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The purpose of this is to ensure we only guard one empty hive at a time, otherwise all the assault bots will be sitting around in empty hives and not pressuring marines
|
||||
// If we have an empty hive already being guarded, then this bool will ensure the bot doesn't go guard an empty hive even if there are 2
|
||||
bShouldGuardEmptyHive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Favour attacking an enemy outpost over guarding an empty hive if fade or onos
|
||||
if (pBot->Player->GetUser3() > AVH_USER3_ALIEN_PLAYER3)
|
||||
if (bShouldGuardEmptyHive && HiveToGuard)
|
||||
{
|
||||
if (HiveToSecure)
|
||||
{
|
||||
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
vector<AvHAIBuildableStructure*> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
|
||||
|
||||
AvHAIBuildableStructure* StructureToAttack = nullptr;
|
||||
|
||||
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
// First prioritise phase gates or alien OCs
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
|
||||
{
|
||||
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
|
||||
|
||||
// Then prioritise turret factories
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
|
||||
{
|
||||
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
|
||||
|
||||
// Then target any other structures
|
||||
if (!StructureToAttack || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack)
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack->edict, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (HiveToGuard)
|
||||
{
|
||||
Task->TaskType = TASK_GUARD;
|
||||
Task->TaskLocation = HiveToGuard->FloorLocation;
|
||||
Task->TaskTarget = HiveToGuard->HiveEntity->edict();
|
||||
return;
|
||||
}
|
||||
Task->TaskType = TASK_GUARD;
|
||||
Task->TaskLocation = HiveToGuard->FloorLocation;
|
||||
Task->TaskTarget = HiveToGuard->HiveEntity->edict();
|
||||
return;
|
||||
}
|
||||
// We're a skulk or lerk
|
||||
else
|
||||
else if (HiveToSecure)
|
||||
{
|
||||
if (HiveToGuard)
|
||||
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
|
||||
// Don't attack electrified structures as skulk
|
||||
if (pBot->Player->GetUser3() < AVH_USER3_ALIEN_PLAYER4)
|
||||
{
|
||||
Task->TaskType = TASK_GUARD;
|
||||
Task->TaskLocation = HiveToGuard->FloorLocation;
|
||||
Task->TaskTarget = HiveToGuard->HiveEntity->edict();
|
||||
return;
|
||||
}
|
||||
else if (HiveToSecure)
|
||||
{
|
||||
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_ELECTRIFIED;
|
||||
vector<AvHAIBuildableStructure*> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
|
||||
}
|
||||
|
||||
AvHAIBuildableStructure* StructureToAttack = nullptr;
|
||||
vector<AvHAIBuildableStructure*> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
|
||||
|
||||
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
|
||||
AvHAIBuildableStructure* StructureToAttack = nullptr;
|
||||
|
||||
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
// First prioritise phase gates or alien OCs
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
|
||||
{
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
// First prioritise phase gates or alien OCs
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
|
||||
{
|
||||
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
|
||||
|
||||
// Then prioritise turret factories
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
|
||||
{
|
||||
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
|
||||
|
||||
// Then target any other structures
|
||||
if (!StructureToAttack || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack)
|
||||
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
|
||||
|
||||
// Then prioritise turret factories
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack->edict, false);
|
||||
return;
|
||||
if (!StructureToAttack || StructureToAttack->StructureType != ThisStructure->StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack && (StructureToAttack->StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure->StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
|
||||
|
||||
// Then target any other structures
|
||||
if (!StructureToAttack || vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack->Location))
|
||||
{
|
||||
StructureToAttack = ThisStructure;
|
||||
}
|
||||
}
|
||||
|
||||
if (StructureToAttack)
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack->edict, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ void TestNavThink(AvHAIPlayer* pBot);
|
|||
void DroneThink(AvHAIPlayer* pBot);
|
||||
void CustomThink(AvHAIPlayer* pBot);
|
||||
|
||||
bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
AvHAIPlayerTask* AIPlayerGetNextTask(AvHAIPlayer* pBot);
|
||||
void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
@ -77,6 +78,7 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
|
|||
void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
||||
void AIPlayerSetPrimaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
|
||||
|
@ -96,6 +98,10 @@ void UpdateCommanderOrders(AvHAIPlayer* pBot);
|
|||
void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination);
|
||||
void AIPlayerReceiveBuildOrder(AvHAIPlayer* pBot, edict_t* BuildTarget);
|
||||
|
||||
void AIPlayerRequestHealth(AvHAIPlayer* pBot);
|
||||
void AIPlayerRequestAmmo(AvHAIPlayer* pBot);
|
||||
void AIPlayerRequestOrder(AvHAIPlayer* pBot);
|
||||
|
||||
void BotStopCommanderMode(AvHAIPlayer* pBot);
|
||||
|
||||
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole);
|
||||
|
|
|
@ -236,6 +236,8 @@ void AIMGR_UpdateFillTeams()
|
|||
|
||||
if (TeamSizeA < NumDesiredTeamA && TeamSizeA <= TeamSizeB)
|
||||
{
|
||||
// Don't add a bot if we have any stuck in the ready room, wait for teams to resolve themselves
|
||||
if (AIMGR_GetNumAIPlayersOnTeam(TEAM_IND) > 0) { return; }
|
||||
AIMGR_AddAIPlayerToTeam(1);
|
||||
return;
|
||||
}
|
||||
|
@ -251,6 +253,8 @@ void AIMGR_UpdateFillTeams()
|
|||
|
||||
if (TeamSizeB < NumDesiredTeamB && TeamSizeB <= TeamSizeA)
|
||||
{
|
||||
// Don't add a bot if we have any stuck in the ready room, wait for teams to resolve themselves
|
||||
if (AIMGR_GetNumAIPlayersOnTeam(TEAM_IND) > 0) { return; }
|
||||
AIMGR_AddAIPlayerToTeam(2);
|
||||
return;
|
||||
}
|
||||
|
@ -849,6 +853,8 @@ void AIMGR_NewMap()
|
|||
{
|
||||
if (avh_botsenabled.value == 0) { return; } // Do nothing if we're not using bots
|
||||
|
||||
ActiveAIPlayers.clear();
|
||||
|
||||
AIStartedTime = gpGlobals->time;
|
||||
ALERT(at_console, "AI Manager New Map\n");
|
||||
|
||||
|
|
|
@ -3267,6 +3267,21 @@ int AITAC_GetNumHives()
|
|||
return Hives.size();
|
||||
}
|
||||
|
||||
int AITAC_GetNumTeamHives(AvHTeamNumber Team, bool bFullyCompletedOnly)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
for (auto it = Hives.begin(); it != Hives.end(); it++)
|
||||
{
|
||||
if (it->OwningTeam == Team && (!bFullyCompletedOnly || it->Status == HIVE_STATUS_BUILT))
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
AvHMessageID UTIL_StructureTypeToImpulseCommand(const AvHAIDeployableStructureType StructureType)
|
||||
{
|
||||
switch (StructureType)
|
||||
|
@ -3716,6 +3731,28 @@ const vector<AvHAIHiveDefinition*> AITAC_GetAllHives()
|
|||
return Results;
|
||||
}
|
||||
|
||||
const AvHAIHiveDefinition* AITAC_GetNearestTeamHive(AvHTeamNumber Team, const Vector SearchLocation, bool bFullyBuiltOnly)
|
||||
{
|
||||
AvHAIHiveDefinition* Result = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = Hives.begin(); it != Hives.end(); it++)
|
||||
{
|
||||
if (it->OwningTeam == Team && (!bFullyBuiltOnly || it->Status == HIVE_STATUS_BUILT))
|
||||
{
|
||||
float ThisDist = vDist2DSq(it->FloorLocation, SearchLocation);
|
||||
|
||||
if (!Result || ThisDist < MinDist)
|
||||
{
|
||||
Result = &(*it);
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
const vector<AvHAIHiveDefinition*> AITAC_GetAllTeamHives(AvHTeamNumber Team, bool bFullyBuiltOnly)
|
||||
{
|
||||
vector<AvHAIHiveDefinition*> Results;
|
||||
|
@ -3760,6 +3797,16 @@ bool AITAC_IsAlienHarasserNeeded(AvHAIPlayer* pBot)
|
|||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
if (pBot->BotRole == BOT_ROLE_ASSAULT)
|
||||
{
|
||||
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
|
||||
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
|
||||
|
||||
int NumTeamHives = AITAC_GetNumTeamHives(BotTeam, false);
|
||||
|
||||
if (NumFades == 0 || (NumOnos == 0 && NumTeamHives > 1)) { return false; }
|
||||
}
|
||||
|
||||
int NumTeamPlayers = AIMGR_GetNumPlayersOnTeam(BotTeam);
|
||||
int DesiredLerks = (int)ceilf((float)NumTeamPlayers * 0.1f);
|
||||
int NumLerks = imaxi(AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER3, nullptr), AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_HARASS, pBot));
|
||||
|
@ -3906,6 +3953,19 @@ bool AITAC_IsAlienCapperNeeded(AvHAIPlayer* pBot)
|
|||
if (ThisNode->OwningTeam == EnemyTeam) { NumEnemyNodes++; }
|
||||
}
|
||||
|
||||
// If we are currently an assault bot and we are almost able to go fade, only sacrifice that if we are truly desperate and have fades already on the team
|
||||
if (pBot->BotRole == BOT_ROLE_ASSAULT)
|
||||
{
|
||||
if (pBot->Player->GetResources() > BALANCE_VAR(kFadeCost) * 0.8f)
|
||||
{
|
||||
if (NumOwnedNodes >= 3) { return false; }
|
||||
|
||||
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
|
||||
|
||||
if (NumFades < 1) { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
int NumNodesLeft = NumEligibleNodes - NumOwnedNodes;
|
||||
|
||||
if (NumNodesLeft == 0) { return false; }
|
||||
|
@ -3997,9 +4057,17 @@ bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot)
|
|||
|
||||
float ResNodeOwnership = AITAC_GetTeamResNodeOwnership(BotTeam, true);
|
||||
|
||||
// Don't lose all those resources!
|
||||
if (IsPlayerLerk(pBot->Edict) || IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict))
|
||||
// Don't lose all those resources if we're a higher lifeform!
|
||||
if (pBot->Player->GetUser3() > AVH_USER3_ALIEN_PLAYER2)
|
||||
{
|
||||
if (IsPlayerLerk(pBot->Edict)) { return false; }
|
||||
|
||||
int NumFades = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER4, pBot->Edict);
|
||||
int NumOnos = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER5, pBot->Edict);
|
||||
|
||||
// Don't downgrade to gorge if we're the only fade or onos on the team
|
||||
if ((IsPlayerFade(pBot->Edict) && NumFades == 0) || (IsPlayerOnos(pBot->Edict) && NumOnos == 0)) { return false; }
|
||||
|
||||
// Only waste those resources if we have loads of res nodes and can easily replenish the lost resources
|
||||
if (pBot->Player->GetResources() < 75 || ResNodeOwnership < 0.6f)
|
||||
{
|
||||
|
|
|
@ -70,6 +70,7 @@ AvHAIDroppedItem* AITAC_GetDroppedItemRefFromEdict(edict_t* ItemEdict);
|
|||
Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive);
|
||||
|
||||
int AITAC_GetNumHives();
|
||||
int AITAC_GetNumTeamHives(AvHTeamNumber Team, bool bFullyCompletedOnly);
|
||||
|
||||
void AITAC_OnNavMeshModified();
|
||||
|
||||
|
@ -163,6 +164,7 @@ const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes();
|
|||
const vector<AvHAIResourceNode*> AITAC_GetAllReachableResourceNodes(AvHTeamNumber Team);
|
||||
const vector<AvHAIHiveDefinition*> AITAC_GetAllHives();
|
||||
const vector<AvHAIHiveDefinition*> AITAC_GetAllTeamHives(AvHTeamNumber Team, bool bFullyBuiltOnly);
|
||||
const AvHAIHiveDefinition* AITAC_GetNearestTeamHive(AvHTeamNumber Team, const Vector SearchLocation, bool bFullyBuiltOnly);
|
||||
|
||||
bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius);
|
||||
|
||||
|
|
|
@ -1842,11 +1842,7 @@ void AlienProgressGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
BotLookAt(pBot, Task->TaskTarget);
|
||||
|
||||
if (gpGlobals->time - pBot->LastRequestTime > min_request_spam_time)
|
||||
{
|
||||
pBot->Impulse = SAYING_4;
|
||||
pBot->LastRequestTime = gpGlobals->time;
|
||||
}
|
||||
AIPlayerRequestHealth(pBot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue