Continuing alien building and other non-combat logic

This commit is contained in:
RGreenlees 2024-01-16 17:10:53 +00:00 committed by pierow
parent d3e1d18e11
commit 9e4c531346
12 changed files with 1371 additions and 403 deletions

View File

@ -220,11 +220,13 @@ typedef struct _STRUCTURE_OBSTACLE
typedef struct _RESOURCE_NODE
{
AvHFuncResource* ResourceEntity = nullptr; // The func_resource edict reference
edict_t* ResourceEdict = nullptr;
Vector Location = g_vecZero; // origin of the func_resource edict (not the tower itself)
bool bIsOccupied = false; // True if there is any resource tower on it
AvHTeamNumber OwningTeam = TEAM_IND; // The team that has currently capped this node (TEAM_IND if none)
edict_t* ActiveTowerEntity = nullptr; // Reference to the resource tower edict (if capped)
bool bIsBaseNode = false; // Is this a node in the marine base or active alien hive?
edict_t* ParentHive = nullptr;
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Is this reachable by the bots? Checks for marine reachability only
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Is this reachable by the bots? Checks for marine reachability only
bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated

View File

@ -449,8 +449,6 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int
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:
@ -483,7 +481,7 @@ void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned
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_SHORT(5); // hold time in seconds * 256
WRITE_STRING(string);//string); // send the string
MESSAGE_END(); // end

View File

@ -10,6 +10,7 @@
#include "AvHAITask.h"
#include "AvHAICommander.h"
#include "AvHAIPlayerManager.h"
#include "AvHAIConfig.h"
#include "../AvHGamerules.h"
#include "../AvHMessage.h"
@ -1612,15 +1613,8 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
void CustomThink(AvHAIPlayer* pBot)
{
if (IsPlayerAlien(pBot->Edict))
{
if (!vIsZero(AIDEBUG_GetDebugVector1()))
{
const AvHAIHiveDefinition* Hive = AITAC_GetHiveNearestLocation(AIDEBUG_GetDebugVector1());
UpdateAIAlienPlayerNSRole(pBot);
BotAlienBuildHive(pBot, Hive);
}
}
}
void DroneThink(AvHAIPlayer* pBot)
@ -1730,20 +1724,25 @@ void UpdateAIAlienPlayerNSRole(AvHAIPlayer* pBot)
return;
}
// Don't switch roles if already fade/onos or those resources are potentially wasted
if (IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict))
if (AITAC_IsAlienCapperNeeded(pBot))
{
SetNewAIPlayerRole(pBot, BOT_ROLE_ASSAULT);
SetNewAIPlayerRole(pBot, BOT_ROLE_FIND_RESOURCES);
return;
}
// Likewise for lerks
if (IsPlayerLerk(pBot->Edict))
if (AITAC_IsAlienBuilderNeeded(pBot))
{
SetNewAIPlayerRole(pBot, BOT_ROLE_BUILDER);
return;
}
if (AITAC_IsAlienHarasserNeeded(pBot))
{
SetNewAIPlayerRole(pBot, BOT_ROLE_HARASS);
return;
}
SetNewAIPlayerRole(pBot, BOT_ROLE_ASSAULT);
}
@ -1782,7 +1781,7 @@ void UpdateAIMarinePlayerNSRole(AvHAIPlayer* pBot)
}
// 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);
float ResNodeOwnership = AITAC_GetTeamResNodeOwnership(BotTeamNumber, true);
int DesiredResCappers = (ResNodeOwnership < 0.5f) ? 2 : 1;
@ -2172,6 +2171,49 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
{
UpdateAIAlienPlayerNSRole(pBot);
AvHAIHiveDefinition* HiveToBuild = nullptr;
if (AITAC_ShouldBotBuildHive(pBot, &HiveToBuild))
{
BotAlienBuildHive(pBot, HiveToBuild);
return;
}
if (pBot->BotRole == BOT_ROLE_ASSAULT)
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_ONE);
}
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);
AIPlayerSetPrimaryAlienTask(pBot, &pBot->PrimaryBotTask);
AIPlayerSetSecondaryAlienTask(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 = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
}
}
void AIPlayerCOThink(AvHAIPlayer* pBot)
@ -2255,7 +2297,7 @@ void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot)
bool ShouldBotThink(AvHAIPlayer* pBot)
{
return (IsPlayerActiveInGame(pBot->Edict) || IsPlayerCommander(pBot->Edict)) && !IsPlayerGestating(pBot->Edict);
return GetGameRules()->GetGameStarted() && (IsPlayerActiveInGame(pBot->Edict) || IsPlayerCommander(pBot->Edict)) && !IsPlayerGestating(pBot->Edict);
}
void BotResumePlay(AvHAIPlayer* pBot)
@ -2345,3 +2387,402 @@ void BotStopCommanderMode(AvHAIPlayer* pBot)
pBot->Player->EffectivePlayerClassChanged();
}
}
void AIPlayerSetPrimaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
switch (pBot->BotRole)
{
case BOT_ROLE_BUILDER:
AIPlayerSetAlienBuilderPrimaryTask(pBot, Task);
return;
case BOT_ROLE_FIND_RESOURCES:
AIPlayerSetAlienCapperPrimaryTask(pBot, Task);
return;
case BOT_ROLE_ASSAULT:
AIPlayerSetAlienAssaultPrimaryTask(pBot, Task);
return;
case BOT_ROLE_HARASS:
AIPlayerSetAlienHarasserPrimaryTask(pBot, Task);
return;
default:
return;
}
}
void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
// Do we have any missing upgrade chambers (should have 3 of each if we can build them)
AvHAIDeployableStructureType MissingStructure = AITAC_GetNextMissingUpgradeChamberForTeam(BotTeam);
// If we do have a missing upgrade chamber, built it at the nearest hive or resource node that we own, whichever is nearest
if (MissingStructure != STRUCTURE_NONE)
{
if (Task->TaskType == TASK_BUILD && Task->StructureType == MissingStructure) { return; }
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
DeployableSearchFilter ResNodeFilter;
ResNodeFilter.DeployableTeam = BotTeam;
ResNodeFilter.ReachabilityTeam = BotTeam;
ResNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
AvHAIResourceNode* NearestNode = AITAC_FindNearestResourceNodeToLocation(pBot->Edict->v.origin, &ResNodeFilter);
Vector BuildOrigin = ZERO_VECTOR;
float MinDist = 0.0f;
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->OwningTeam != BotTeam) { continue; }
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisHive->FloorLocation);
if (vIsZero(BuildOrigin) || ThisDist < MinDist)
{
BuildOrigin = ThisHive->FloorLocation;
MinDist = ThisDist;
}
}
if (NearestNode)
{
float ThisDist = vDist2DSq(pBot->Edict->v.origin, NearestNode->Location);
if (vIsZero(BuildOrigin) || ThisDist < MinDist)
{
BuildOrigin = NearestNode->Location;
}
}
if (vIsZero(BuildOrigin))
{
BuildOrigin = pBot->CurrentFloorPosition;
}
if (Task->TaskType == TASK_BUILD && vDist2DSq(Task->TaskLocation, BuildOrigin) <= UTIL_MetresToGoldSrcUnits(5.0f))
{
Task->StructureType = MissingStructure;
return;
}
Vector ActualBuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), BuildOrigin, UTIL_MetresToGoldSrcUnits(3.0f));
if (vIsZero(ActualBuildLocation))
{
ActualBuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), BuildOrigin, UTIL_MetresToGoldSrcUnits(3.0f));
}
AITASK_SetBuildTask(pBot, Task, MissingStructure, ActualBuildLocation, false);
return;
}
// No missing upgrade chambers to drop, let's look for empty hives we can start staking a claim to, to deny to the enemy
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
AvHAIHiveDefinition* HiveToSecure = nullptr;
float MaxDist = 0.0f;
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->Status == HIVE_STATUS_UNBUILT)
{
unsigned int StructureTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN)
{
StructureTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
}
DeployableSearchFilter EnemyStructureFilter;
EnemyStructureFilter.DeployableTeam = EnemyTeam;
EnemyStructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStructureFilter.DeployableTypes = StructureTypes;
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
bool bEnemyHaveFoothold = AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStructureFilter);
if (bEnemyHaveFoothold) { continue; }
if (AITAC_GetNumPlayersOfTeamInArea(EnemyTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER) > 1) { continue; }
int OtherBuilders = AITAC_GetNumPlayersOfTeamAndClassInArea(EnemyTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_ALIEN_PLAYER2);
if (OtherBuilders >= 2) { continue; }
DeployableSearchFilter ExistingReinforcementFilter;
ExistingReinforcementFilter.DeployableTeam = BotTeam;
ExistingReinforcementFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
vector<AvHAIBuildableStructure*> AllReinforcingStructures = AITAC_FindAllDeployables(ThisHive->FloorLocation, &ExistingReinforcementFilter);
int NumOCs = 0;
int NumDCs = 0;
int NumMCs = 0;
int NumSCs = 0;
for (auto it = AllReinforcingStructures.begin(); it != AllReinforcingStructures.end(); it++)
{
switch ((*it)->StructureType)
{
case STRUCTURE_ALIEN_OFFENCECHAMBER:
NumOCs++;
break;
case STRUCTURE_ALIEN_DEFENCECHAMBER:
NumDCs++;
break;
case STRUCTURE_ALIEN_MOVEMENTCHAMBER:
NumMCs++;
break;
case STRUCTURE_ALIEN_SENSORYCHAMBER:
NumSCs++;
break;
default:
break;
}
}
if (NumOCs < 3
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER) && NumDCs < 2)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER) && NumMCs < 1)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER) && NumSCs < 1))
{
float ThisDist = vDist2DSq(AITAC_GetTeamStartingLocation(EnemyTeam), ThisHive->FloorLocation);
if (ThisDist > MaxDist)
{
HiveToSecure = ThisHive;
MaxDist = ThisDist;
}
}
}
}
if (HiveToSecure)
{
AITASK_SetReinforceStructureTask(pBot, Task, HiveToSecure->HiveEntity->edict(), false);
return;
}
DeployableSearchFilter ResNodeFilter;
ResNodeFilter.DeployableTeam = BotTeam;
ResNodeFilter.DeployableTypes = STRUCTURE_ALIEN_RESTOWER;
ResNodeFilter.ReachabilityTeam = BotTeam;
ResNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
vector<AvHAIBuildableStructure*> AllMatchingTowers = AITAC_FindAllDeployables(pBot->Edict->v.origin, &ResNodeFilter);
edict_t* TowerToReinforce = nullptr;
float MinDist = 0.0f;
for (auto it = AllMatchingTowers.begin(); it != AllMatchingTowers.end(); it++)
{
AvHAIBuildableStructure* ThisResTower = (*it);
DeployableSearchFilter ExistingReinforcementFilter;
ExistingReinforcementFilter.DeployableTeam = BotTeam;
ExistingReinforcementFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
vector<AvHAIBuildableStructure*> AllReinforcingStructures = AITAC_FindAllDeployables(ThisResTower->Location, &ExistingReinforcementFilter);
int NumOCs = 0;
int NumDCs = 0;
int NumMCs = 0;
int NumSCs = 0;
for (auto it = AllReinforcingStructures.begin(); it != AllReinforcingStructures.end(); it++)
{
switch ((*it)->StructureType)
{
case STRUCTURE_ALIEN_OFFENCECHAMBER:
NumOCs++;
break;
case STRUCTURE_ALIEN_DEFENCECHAMBER:
NumDCs++;
break;
case STRUCTURE_ALIEN_MOVEMENTCHAMBER:
NumMCs++;
break;
case STRUCTURE_ALIEN_SENSORYCHAMBER:
NumSCs++;
break;
default:
break;
}
}
if (NumOCs < 3
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER) && NumDCs < 2)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER) && NumMCs < 1)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER) && NumSCs < 1))
{
float ThisDist = vDist2DSq(AITAC_GetTeamStartingLocation(EnemyTeam), ThisResTower->Location);
if (!TowerToReinforce || ThisDist < MinDist)
{
TowerToReinforce = ThisResTower->edict;
MaxDist = ThisDist;
}
}
}
if (!FNullEnt(TowerToReinforce))
{
AITASK_SetReinforceStructureTask(pBot, Task, TowerToReinforce, false);
}
}
void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHAIResourceNode* NodeToCap = nullptr;
float ResourcesRequired = BALANCE_VAR(kResourceTowerCost);
if (!IsPlayerGorge(pBot->Edict))
{
ResourcesRequired += BALANCE_VAR(kGorgeCost);
}
bool bCanPlaceTower = pBot->Player->GetResources() >= (BALANCE_VAR(kResourceTowerCost) * 0.8f);
if (IsPlayerLerk(pBot->Edict) || IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict))
{
bCanPlaceTower = pBot->Player->GetResources() >= 75 && AITAC_GetTeamResNodeOwnership(BotTeam, true) >= 0.5f;
}
bool bCanAttackTowers = (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB));
// If we have enough resources to cap a node, then find an empty one we can slap one down in
if (bCanPlaceTower || !bCanAttackTowers)
{
DeployableSearchFilter EmptyNodeFilter;
EmptyNodeFilter.DeployableTeam = TEAM_IND;
EmptyNodeFilter.ReachabilityTeam = BotTeam;
EmptyNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
vector<AvHAIResourceNode*> EligibleNodes = AITAC_GetAllMatchingResourceNodes(pBot->Edict->v.origin, &EmptyNodeFilter);
float MinDist = 0.0f;
for (auto it = EligibleNodes.begin(); it != EligibleNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
edict_t* ExistingBuilder = AITAC_GetNearestPlayerOfClassInArea(BotTeam, ThisNode->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
if (!FNullEnt(ExistingBuilder) && vDist2DSq(ExistingBuilder->v.origin, ThisNode->Location) < vDist2DSq(pBot->Edict->v.origin, ThisNode->Location) && GetPlayerResources(ExistingBuilder) >= (BALANCE_VAR(kResourceTowerCost) * 0.8f)) { continue; }
vector<AvHAIPlayer*> OtherAITeam = AIMGR_GetAIPlayersOnTeam(BotTeam);
bool bNodeClaimed = false;
for (auto BotIt = OtherAITeam.begin(); BotIt != OtherAITeam.end(); BotIt++)
{
AvHAIPlayer* OtherBot = (*BotIt);
if (OtherBot != pBot && OtherBot->PrimaryBotTask.TaskType == TASK_CAP_RESNODE && OtherBot->PrimaryBotTask.TaskTarget == ThisNode->ResourceEdict)
{
bNodeClaimed = true;
break;
}
}
if (bNodeClaimed) { continue; }
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisNode->Location);
if (!NodeToCap || ThisDist < MinDist)
{
NodeToCap = ThisNode;
MinDist = ThisDist;
}
}
if (NodeToCap)
{
AITASK_SetCapResNodeTask(pBot, Task, NodeToCap, false);
return;
}
}
// Let's find an enemy tower to take out
DeployableSearchFilter EnemyNodeFilter;
EnemyNodeFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
EnemyNodeFilter.ReachabilityTeam = BotTeam;
EnemyNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
vector<AvHAIResourceNode*> EligibleNodes = AITAC_GetAllMatchingResourceNodes(pBot->Edict->v.origin, &EnemyNodeFilter);
float MinDist = 0.0f;
NodeToCap = nullptr;
for (auto it = EligibleNodes.begin(); it != EligibleNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
// Don't attack nodes which are firmly owned by the enemy (i.e. in marine base, or part of an enemy alien team's active hive)
if (ThisNode->bIsBaseNode)
{
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (AIMGR_GetEnemyTeamType(BotTeam) == AVH_CLASS_TYPE_MARINE)
{
// Too close to the marine comm chair, don't touch this one
if (vDist2DSq(ThisNode->Location, AITAC_GetCommChairLocation(EnemyTeam)) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { continue; }
}
else
{
// The enemy alien team has a hive here, don't attack this res node or we'll get smooshed
AvHAIHiveDefinition* ParentHive = AITAC_GetHiveFromEdict(ThisNode->ParentHive);
if (ParentHive && ParentHive->OwningTeam == EnemyTeam) { continue; }
}
}
float ThisDist = vDist2DSq(ThisNode->Location, AITAC_GetTeamStartingLocation(BotTeam));
if (!NodeToCap || ThisDist < MinDist)
{
NodeToCap = ThisNode;
MinDist = ThisDist;
}
}
if (NodeToCap)
{
AITASK_SetAttackTask(pBot, Task, NodeToCap->ActiveTowerEntity, false);
return;
}
AIPlayerSetAlienAssaultPrimaryTask(pBot, Task);
}
void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
}
void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
}
void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
}

View File

@ -76,6 +76,14 @@ void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask*
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetPrimaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot);
bool ShouldBotThink(AvHAIPlayer* pBot);

View File

@ -593,7 +593,7 @@ void AIMGR_UpdateAIPlayers()
UpdateBotChat(bot);
CustomThink(bot);
AIPlayerThink(bot);
BotUpdateDesiredViewRotation(bot);
}
@ -616,6 +616,52 @@ void AIMGR_UpdateAIPlayers()
BotIt++;
}
vector<AvHAIPlayer*> AlienPlayers = AIMGR_GetAIPlayersOnTeam(TEAM_TWO);
int NumBuilders = 0;
int NumCappers = 0;
int NumHarassers = 0;
int NumAssault = 0;
for (auto it = AlienPlayers.begin(); it != AlienPlayers.end(); it++)
{
AvHAIPlayer* NewCapper = (*it);
if (NewCapper)
{
switch (NewCapper->BotRole)
{
case BOT_ROLE_BUILDER:
NumBuilders++;
break;
case BOT_ROLE_FIND_RESOURCES:
NumCappers++;
break;
case BOT_ROLE_HARASS:
NumHarassers++;
break;
case BOT_ROLE_ASSAULT:
NumAssault++;
break;
default:
break;
}
}
}
char buf[256];
char interbuf[32];
sprintf(buf, "Builders: %d\n", NumBuilders);
sprintf(interbuf, "Cappers: %d\n", NumCappers);
strcat(buf, interbuf);
sprintf(interbuf, "Harassers: %d\n", NumHarassers);
strcat(buf, interbuf);
sprintf(interbuf, "Assault: %d\n", NumAssault);
strcat(buf, interbuf);
UTIL_DrawHUDText(INDEXENT(1), 0, 0.1f, 0.1f, 255, 255, 255, buf);
PrevTime = CurrTime;
}
@ -708,6 +754,26 @@ int AIMGR_GetNumAIPlayersWithRoleOnTeam(AvHTeamNumber Team, AvHAIBotRole Role, A
return Result;
}
int AIMGR_GetNumHumansOfClassOnTeam(AvHTeamNumber Team, AvHUser3 PlayerType)
{
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_AIPlayerExistsOnTeam(AvHTeamNumber Team)
{
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
@ -847,6 +913,13 @@ AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam)
return (FriendlyTeam == TeamANumber) ? TeamBNumber : TeamANumber;
}
AvHClassType AIMGR_GetTeamType(const AvHTeamNumber Team)
{
AvHTeam* TeamRef = GetGameRules()->GetTeam(Team);
return (TeamRef) ? TeamRef->GetTeamType() : AVH_CLASS_TYPE_UNDEFINED;
}
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam)
{
AvHTeamNumber EnemyTeamNumber = AIMGR_GetEnemyTeam(FriendlyTeam);
@ -891,6 +964,27 @@ vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team)
return Result;
}
vector<AvHPlayer*> AIMGR_GetNonAIPlayersOnTeam(AvHTeamNumber Team)
{
vector<AvHPlayer*> TeamPlayers = AIMGR_GetAllPlayersOnTeam(Team);
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
{
AvHPlayer* ThisPlayer = it->Player;
if (!ThisPlayer) { continue; }
std::vector<AvHPlayer*>::iterator FoundPlayer = std::find(TeamPlayers.begin(), TeamPlayers.end(), ThisPlayer);
if (FoundPlayer != TeamPlayers.end())
{
TeamPlayers.erase(FoundPlayer);
}
}
return TeamPlayers;
}
void AIMGR_UpdateAIMapData()
{
AITAC_UpdateMapAIData();

View File

@ -60,16 +60,24 @@ int AIMGR_GetNumHumanPlayersOnTeam(AvHTeamNumber Team);
int AIMGR_GetNumAIPlayersWithRoleOnTeam(AvHTeamNumber Team, AvHAIBotRole Role, AvHAIPlayer* IgnoreAIPlayer);
int AIMGR_GetNumHumansOfClassOnTeam(AvHTeamNumber Team, AvHUser3 PlayerType);
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetTeamType(const AvHTeamNumber Team);
// Returns all NS AI players. Does not include third-party bots
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers();
// Returns all NS AI players on the requested team. Does not include third-party bots
vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team);
// Returns all players on a team which are not an internal NS bot. Will still include third party bots such as Whichbot and RCBot
vector<AvHPlayer*> AIMGR_GetNonAIPlayersOnTeam(AvHTeamNumber Team);
void AIMGR_ClearBotData();
AvHAIPlayer* AIMGR_GetDebugAIPlayer();

View File

@ -663,7 +663,16 @@ void AITAC_PopulateHiveData()
AvHAIHiveDefinition NewHive;
NewHive.HiveEntity = theEntity;
NewHive.Location = theEntity->pev->origin;
NewHive.HiveResNodeRef = AITAC_GetNearestResourceNodeToLocation(theEntity->pev->origin);
AvHAIResourceNode* NearestNode = AITAC_GetNearestResourceNodeToLocation(theEntity->pev->origin);
if (NearestNode)
{
NewHive.HiveResNodeRef = NearestNode;
NearestNode->bIsBaseNode = true;
NearestNode->ParentHive = NewHive.HiveEntity->edict();
}
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);
@ -1039,6 +1048,7 @@ void AITAC_PopulateResourceNodes()
AvHAIResourceNode NewResNode;
NewResNode.ResourceEntity = theEntity;
NewResNode.ResourceEdict = theEntity->edict();
NewResNode.Location = theEntity->pev->origin;
NewResNode.TeamAReachabilityFlags = AI_REACHABILITY_NONE;
NewResNode.TeamBReachabilityFlags = AI_REACHABILITY_NONE;
@ -2291,7 +2301,7 @@ AvHAIResourceNode* AITAC_GetResourceNodeFromEdict(const edict_t* Edict)
{
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
{
if (it->ResourceEntity->edict() == Edict)
if (it->ResourceEdict == Edict)
{
return &(*it);
}
@ -2380,11 +2390,19 @@ AvHAIResourceNode* AITAC_GetNearestResourceNodeToLocation(const Vector Location)
return Result;
}
float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team)
float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team, bool bIncludeBaseNodes)
{
int NumViableResNodes = 0;
int NumOwnedResNodes = 0;
AvHAIResourceNode* MarineBaseNode = nullptr;
bool bIsMarineType = AIMGR_GetTeamType(Team) == AVH_CLASS_TYPE_MARINE;
if (bIsMarineType)
{
MarineBaseNode = AITAC_GetNearestResourceNodeToLocation(AITAC_GetCommChairLocation(Team));
}
for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++)
{
unsigned int CheckReachabilityFlags = (it->TeamAReachabilityFlags | it->TeamBReachabilityFlags);
@ -2396,6 +2414,23 @@ float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team)
if (CheckReachabilityFlags == AI_REACHABILITY_UNREACHABLE) { continue; }
if (!bIncludeBaseNodes)
{
if (it->bIsBaseNode)
{
if (bIsMarineType)
{
if (it->ResourceEntity == MarineBaseNode->ResourceEntity) { continue; }
}
else
{
const AvHAIHiveDefinition* NearestHive = AITAC_GetHiveNearestLocation(it->Location);
if (NearestHive->Status != HIVE_STATUS_UNBUILT) { continue; }
}
}
}
NumViableResNodes++;
if (it->OwningTeam == Team)
@ -2547,6 +2582,33 @@ int AITAC_GetNumActivePlayersOnTeam(const AvHTeamNumber Team)
return Result;
}
int AITAC_GetNumPlayersOfTeamAndClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass)
{
int Result = 0;
float MaxRadiusSq = sqrf(SearchRadius);
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
edict_t* PlayerEdict = INDEXENT(i);
if (FNullEnt(PlayerEdict) || PlayerEdict->free || PlayerEdict == IgnorePlayer) { continue; }
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
if (PlayerRef != nullptr && GetPlayerActiveClass(PlayerRef) == SearchClass && (Team == TEAM_IND || PlayerRef->GetTeam() == Team) && IsPlayerActiveInGame(PlayerEdict))
{
float Dist = (bConsiderPhaseDist) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(PlayerEdict->v.origin, SearchLocation)) : vDist2DSq(PlayerEdict->v.origin, SearchLocation);
if (Dist <= MaxRadiusSq)
{
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;
@ -2627,6 +2689,35 @@ edict_t* AITAC_GetNearestPlayerOfClassInArea(const AvHTeamNumber Team, const Vec
}
vector<edict_t*> AITAC_GetAllPlayersOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass)
{
vector<edict_t*> Result;
float MaxRadiusSq = sqrf(SearchRadius);
float MinDistSq = 0.0f;
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
edict_t* PlayerEdict = INDEXENT(i);
if (FNullEnt(PlayerEdict) || PlayerEdict->free || PlayerEdict == IgnorePlayer) { continue; }
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
if (PlayerRef != nullptr && (SearchClass == AVH_USER3_NONE || GetPlayerActiveClass(PlayerRef) == SearchClass) && (Team == TEAM_IND || PlayerRef->GetTeam() == Team) && IsPlayerActiveInGame(PlayerEdict))
{
float Dist = (bConsiderPhaseDist) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(PlayerEdict->v.origin, SearchLocation)) : vDist2DSq(PlayerEdict->v.origin, SearchLocation);
if (Dist <= MaxRadiusSq)
{
Result.push_back(PlayerEdict);
MinDistSq = Dist;
}
}
}
return Result;
}
AvHAIHiveDefinition* AITAC_GetTeamHiveWithTech(const AvHTeamNumber Team, const AvHMessageID Tech)
{
AvHTeam* TeamRef = GetGameRules()->GetTeam(Team);
@ -3094,7 +3185,7 @@ AvHAIDeployableStructureType UTIL_GetChamberTypeForHiveTech(AvHMessageID HiveTec
case ALIEN_BUILD_MOVEMENT_CHAMBER:
return STRUCTURE_ALIEN_MOVEMENTCHAMBER;
case ALIEN_BUILD_SENSORY_CHAMBER:
return STRUCTURE_ALIEN_OFFENCECHAMBER;
return STRUCTURE_ALIEN_SENSORYCHAMBER;
default:
return STRUCTURE_NONE;
}
@ -3302,9 +3393,196 @@ bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, fl
return false;
}
bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot)
bool AITAC_IsAlienHarasserNeeded(AvHAIPlayer* pBot)
{
if (IsPlayerLerk(pBot->Edict)) { return true; }
if (pBot->Player->GetResources() < BALANCE_VAR(kLerkCost)) { return false; }
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
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));
return NumLerks < DesiredLerks;
}
bool AITAC_ShouldBotBuildHive(AvHAIPlayer* pBot, AvHAIHiveDefinition** EligibleHive)
{
*EligibleHive = nullptr;
float HiveCost = BALANCE_VAR(kHiveCost);
if (!IsPlayerGorge(pBot->Edict))
{
HiveCost += BALANCE_VAR(kGorgeCost);
}
if (pBot->Player->GetResources() < HiveCost) { return false; }
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
// If we're a higher lifeform, ensure we can't leave this to someone else before considering losing those resources
// We will ignore humans and third party bots, because we don't know if they will drop the hive or not. Not everyone can be as team-spirited as us...
if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict))
{
vector<AvHAIPlayer*> OtherAITeamMates = AIMGR_GetAIPlayersOnTeam(BotTeam);
for (auto it = OtherAITeamMates.begin(); it != OtherAITeamMates.end(); it++)
{
AvHAIPlayer* OtherBot = (*it);
// If the other bot has enough resources to drop a hive, and they're a less expensive life form than us, let them do it.
if (OtherBot->Player->GetResources() >= BALANCE_VAR(kHiveCost) * 0.8f && OtherBot->Player->GetUser3() < pBot->Player->GetUser3()) { return false; }
}
}
AvHAIHiveDefinition* SuitableHive = nullptr;
float MinDist = 0.0f;
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->Status == HIVE_STATUS_BUILT) { continue; }
if (ThisHive->Status == HIVE_STATUS_BUILDING)
{
// Aliens can only build one hive at a time, so if we have one already under construction then automatic no
if (ThisHive->OwningTeam == BotTeam)
{
return false;
}
else
{
// It's an enemy hive under construction, so we can't build this one but can keep searching
continue;
}
}
// Check to make sure someone else isn't planning to drop a hive, otherwise don't bother
bool bHasOtherBuilder = false;
vector<edict_t*> OtherBuilders = AITAC_GetAllPlayersOfClassInArea(BotTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
for (auto BuildIt = OtherBuilders.begin(); BuildIt != OtherBuilders.end(); BuildIt++)
{
edict_t* OtherBuilder = (*BuildIt);
if (vDist2DSq(OtherBuilder->v.origin, ThisHive->FloorLocation) && GetPlayerResources(OtherBuilder) >= BALANCE_VAR(kHiveCost) * 0.8f) { bHasOtherBuilder = true; }
}
if (bHasOtherBuilder) { return false; }
// Enemy are in here right now, wait until they're cleared out
if (AITAC_GetNumPlayersOfTeamInArea(EnemyTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER) > 2) { continue; }
// Must be an empty hive
DeployableSearchFilter EnemyFortificationsFilter;
EnemyFortificationsFilter.DeployableTeam = EnemyTeam;
EnemyFortificationsFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
EnemyFortificationsFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
EnemyFortificationsFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyFortificationsFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; // This is important to prevent exploiting the AI. Those structures have to be built first!
}
else
{
EnemyFortificationsFilter.DeployableTypes = (STRUCTURE_ALIEN_OFFENCECHAMBER);
}
// Enemy have built some stuff, wait until it's clear before building
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyFortificationsFilter)) { continue; }
// Should be clear to drop dat hive!
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisHive->FloorLocation);
if (!SuitableHive || ThisDist < MinDist)
{
SuitableHive = ThisHive;
MinDist = ThisDist;
}
}
*EligibleHive = SuitableHive;
return (SuitableHive != nullptr);
}
bool AITAC_IsAlienCapperNeeded(AvHAIPlayer* pBot)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
float ResNodeOwnership = AITAC_GetTeamResNodeOwnership(BotTeam, false);
float EnemyNodeOwnership = AITAC_GetTeamResNodeOwnership(EnemyTeam, false);
int NumTeamPlayers = AIMGR_GetNumPlayersOnTeam(BotTeam);
int MaxCappers = (int)ceilf((float)NumTeamPlayers * 0.5f);
int NumCurrentCappers = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_FIND_RESOURCES, pBot);
int DesiredCappers = 0;
if (ResNodeOwnership > 0.6f) { return false; }
if (ResNodeOwnership < 0.25f)
{
DesiredCappers = MaxCappers;
}
else if (ResNodeOwnership < 0.4f)
{
DesiredCappers = imaxi(1, MaxCappers - 1);
}
else
{
DesiredCappers = imaxi(1, MaxCappers - 2);
}
// If we're lerk/fade/onos, only go capper if we have lots of resources and can replace them easily, otherwise we can't afford to waste resources going back to gorge
if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict))
{
if (pBot->Player->GetResources() < 75 || ResNodeOwnership < 0.4f) { return false; }
int NumSkulks = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER1, nullptr);
int NumGorges = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER2, nullptr);
// Don't go capping if there are other players who could do it instead and aren't higher life forms
if ((NumSkulks + NumGorges) > DesiredCappers) { return false; }
}
return NumCurrentCappers < DesiredCappers;
}
bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot)
{
// The basic logic here is that we have a max number of builders based on team size.
// We add one extra required builder for every empty hive that needs fortifications placed
// One for any missing upgrade chambers
// and one if there are any resource towers that need reinforcing which aren't part of a hive
// This should mean that we could have up to 3 builders at the start if the team is big enough, then that should reduce down to just 1 eventually
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
float ResNodeOwnership = AITAC_GetTeamResNodeOwnership(BotTeam, true);
// Don't lose all those resources!
if (IsPlayerLerk(pBot->Edict) || IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict))
{
// 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)
{
return false;
}
}
AvHMessageID HiveTechOne = CONFIG_GetHiveTechAtIndex(0);
AvHMessageID HiveTechTwo = CONFIG_GetHiveTechAtIndex(1);
@ -3318,26 +3596,153 @@ bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot)
StructureFilter.DeployableTeam = BotTeam;
int NumTeamPlayers = AIMGR_GetNumPlayersOnTeam(BotTeam);
int MaxBuilders = imini(2, (int)floorf((float)NumTeamPlayers * 0.5f));
int NumCurrentBuilders = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_BUILDER, pBot);
int MaxBuilders = (int)ceilf((float)NumTeamPlayers * 0.3f); // Max 1/3 of team can be builder at any one time
if (MaxBuilders == 0) { return false; }
// We have a hive without any associated chambers yet
if (AITAC_TeamHiveWithTechExists(BotTeam, MESSAGE_NULL))
// If we're struggling for resources, then cut back on max number of builders to have more focused on capping nodes
if (ResNodeOwnership < 0.25f)
{
return NumCurrentBuilders < MaxBuilders;
MaxBuilders = (imaxi(1, MaxBuilders - 1));
}
StructureFilter.DeployableTypes = ChamberTypeOne;
if (AITAC_TeamHiveWithTechExists(BotTeam, HiveTechOne) && AITAC_GetNumDeployablesNearLocation(pBot->Edict->v.origin, &StructureFilter) < 3) { return NumBuilders < 1; }
StructureFilter.DeployableTypes = ChamberTypeTwo;
if (AITAC_TeamHiveWithTechExists(BotTeam, HiveTechTwo) && AITAC_GetNumDeployablesNearLocation(pBot->Edict->v.origin, &StructureFilter) < 3) { return NumBuilders < 1; }
StructureFilter.DeployableTypes = ChamberTypeThree;
if (AITAC_TeamHiveWithTechExists(BotTeam, HiveTechThree) && AITAC_GetNumDeployablesNearLocation(pBot->Edict->v.origin, &StructureFilter) < 3) { return NumBuilders < 1; }
// Don't build if we're a higher lifeform and there are others who could do the job instead
if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict))
{
int NumSkulks = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER1, nullptr);
int NumGorges = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER2, nullptr);
int NumDead = AITAC_GetNumDeadPlayersOnTeam(BotTeam);
if ((NumSkulks + NumGorges + NumDead) > MaxBuilders) { return false; }
}
int NumCurrentBuilders = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_BUILDER, pBot);
vector<AvHPlayer*> NonAIPlayers = AIMGR_GetNonAIPlayersOnTeam(BotTeam);
for (auto it = NonAIPlayers.begin(); it != NonAIPlayers.end(); it++)
{
AvHPlayer* ThisPlayer = (*it);
if (GetPlayerActiveClass(ThisPlayer) == AVH_USER3_ALIEN_PLAYER2)
{
NumCurrentBuilders++;
}
}
// We already have too many gorges running around
if (NumCurrentBuilders >= MaxBuilders) { return false; }
int DesiredBuilders = 0;
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
const AvHAIHiveDefinition* ThisHive = (*it);
// Can't build in an enemy hive (if playing AvA)
if (ThisHive->OwningTeam == EnemyTeam) { continue; }
if (ThisHive->OwningTeam == BotTeam)
{
if (ThisHive->Status == HIVE_STATUS_BUILT)
{
// Hive hasn't got a chamber assigned yet, we need at least one builder for that
if (ThisHive->TechStatus == MESSAGE_NULL)
{
DesiredBuilders++;
if (DesiredBuilders >= MaxBuilders)
{
return NumCurrentBuilders < DesiredBuilders;
}
}
continue;
}
}
bool bEnemyIsMarines = (AIMGR_GetEnemyTeamType(BotTeam) == AVH_CLASS_TYPE_MARINE);
DeployableSearchFilter EnemyStructures;
EnemyStructures.DeployableTeam = EnemyTeam;
EnemyStructures.ReachabilityTeam = EnemyTeam;
EnemyStructures.ReachabilityFlags = (bEnemyIsMarines) ? AI_REACHABILITY_MARINE : AI_REACHABILITY_SKULK;
EnemyStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (bEnemyIsMarines)
{
EnemyStructures.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyStructures.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStructures.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
}
else
{
EnemyStructures.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
}
// Enemy have a foothold here, don't get involved
if (AITAC_GetNumDeployablesNearLocation(ThisHive->Location, &EnemyStructures) > 0)
{
continue;
}
// The enemy don't have a proper foothold yet
DeployableSearchFilter ExistingReinforcementFilter;
ExistingReinforcementFilter.DeployableTeam = BotTeam;
ExistingReinforcementFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
vector<AvHAIBuildableStructure*> AllReinforcingStructures = AITAC_FindAllDeployables(ThisHive->FloorLocation, &ExistingReinforcementFilter);
int NumOCs = 0;
int NumDCs = 0;
int NumMCs = 0;
int NumSCs = 0;
for (auto it = AllReinforcingStructures.begin(); it != AllReinforcingStructures.end(); it++)
{
switch ((*it)->StructureType)
{
case STRUCTURE_ALIEN_OFFENCECHAMBER:
NumOCs++;
break;
case STRUCTURE_ALIEN_DEFENCECHAMBER:
NumDCs++;
break;
case STRUCTURE_ALIEN_MOVEMENTCHAMBER:
NumMCs++;
break;
case STRUCTURE_ALIEN_SENSORYCHAMBER:
NumSCs++;
break;
default:
break;
}
}
if (NumOCs < 3
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER) && NumDCs < 2)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER) && NumMCs < 1)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER) && NumSCs < 1))
{
DesiredBuilders++;
if (DesiredBuilders >= MaxBuilders)
{
return NumCurrentBuilders < DesiredBuilders;
}
}
}
// We have hives to fortify and upgrades to enable. Ignore resource nodes for now. We will get a bot assigned to those once we've done everything else
if (DesiredBuilders > 0)
{
return NumCurrentBuilders < DesiredBuilders;
}
DeployableSearchFilter ResNodeFilter;
ResNodeFilter.DeployableTeam = BotTeam;
ResNodeFilter.ReachabilityTeam = BotTeam;
@ -3349,7 +3754,121 @@ bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot)
{
AvHAIResourceNode* ThisNode = (*it);
if (ThisNode->bIsBaseNode && !FNullEnt(ThisNode->ParentHive))
{
AvHAIHiveDefinition* HiveRef = AITAC_GetHiveFromEdict(ThisNode->ParentHive);
if (HiveRef && HiveRef->Status != HIVE_STATUS_UNBUILT)
{
continue;
}
}
DeployableSearchFilter ExistingReinforcementFilter;
ExistingReinforcementFilter.DeployableTeam = BotTeam;
ExistingReinforcementFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
ExistingReinforcementFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
vector<AvHAIBuildableStructure*> AllReinforcingStructures = AITAC_FindAllDeployables(ThisNode->Location, &ExistingReinforcementFilter);
int NumOCs = 0;
int NumDCs = 0;
int NumMCs = 0;
int NumSCs = 0;
for (auto it = AllReinforcingStructures.begin(); it != AllReinforcingStructures.end(); it++)
{
switch ((*it)->StructureType)
{
case STRUCTURE_ALIEN_OFFENCECHAMBER:
NumOCs++;
break;
case STRUCTURE_ALIEN_DEFENCECHAMBER:
NumDCs++;
break;
case STRUCTURE_ALIEN_MOVEMENTCHAMBER:
NumMCs++;
break;
case STRUCTURE_ALIEN_SENSORYCHAMBER:
NumSCs++;
break;
default:
break;
}
}
if (NumOCs < 2
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER) && NumDCs < 2)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER) && NumDCs < 1)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER) && NumDCs < 1))
{
DesiredBuilders++;
if (DesiredBuilders >= MaxBuilders)
{
return NumCurrentBuilders < DesiredBuilders;
}
break;
}
}
return NumCurrentBuilders < DesiredBuilders;
}
AvHAIDeployableStructureType AITAC_GetNextMissingUpgradeChamberForTeam(AvHTeamNumber Team)
{
if (AIMGR_GetTeamType(Team) != AVH_CLASS_TYPE_ALIEN) { return STRUCTURE_NONE; }
AvHMessageID HiveTechOne = CONFIG_GetHiveTechAtIndex(0);
AvHMessageID HiveTechTwo = CONFIG_GetHiveTechAtIndex(1);
AvHMessageID HiveTechThree = CONFIG_GetHiveTechAtIndex(2);
AvHAIDeployableStructureType ChamberTypeOne = UTIL_GetChamberTypeForHiveTech(HiveTechOne);
AvHAIDeployableStructureType ChamberTypeTwo = UTIL_GetChamberTypeForHiveTech(HiveTechTwo);
AvHAIDeployableStructureType ChamberTypeThree = UTIL_GetChamberTypeForHiveTech(HiveTechThree);
DeployableSearchFilter SearchFilter;
SearchFilter.DeployableTeam = Team;
bool bHasFreeHive = AITAC_TeamHiveWithTechExists(Team, MESSAGE_NULL);
if (ChamberTypeOne != STRUCTURE_NONE && (bHasFreeHive || AITAC_TeamHiveWithTechExists(Team, HiveTechOne)))
{
SearchFilter.DeployableTypes = ChamberTypeOne;
int NumChambers = AITAC_GetNumDeployablesNearLocation(AITAC_GetTeamStartingLocation(Team), &SearchFilter);
if (NumChambers < 3)
{
return ChamberTypeOne;
}
}
if (ChamberTypeTwo != STRUCTURE_NONE && (bHasFreeHive || AITAC_TeamHiveWithTechExists(Team, HiveTechTwo)))
{
SearchFilter.DeployableTypes = ChamberTypeTwo;
int NumChambers = AITAC_GetNumDeployablesNearLocation(AITAC_GetTeamStartingLocation(Team), &SearchFilter);
if (NumChambers < 3)
{
return ChamberTypeTwo;
}
}
if (ChamberTypeThree != STRUCTURE_NONE && (bHasFreeHive || AITAC_TeamHiveWithTechExists(Team, HiveTechThree)))
{
SearchFilter.DeployableTypes = ChamberTypeThree;
int NumChambers = AITAC_GetNumDeployablesNearLocation(AITAC_GetTeamStartingLocation(Team), &SearchFilter);
if (NumChambers < 3)
{
return ChamberTypeThree;
}
}
return STRUCTURE_NONE;
}

View File

@ -107,7 +107,7 @@ 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);
float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team, bool bIncludeBaseNodes);
int AITAC_GetNumResourceNodesNearLocation(const Vector Location, const DeployableSearchFilter* Filter);
AvHAIResourceNode* AITAC_FindNearestResourceNodeToLocation(const Vector Location, const DeployableSearchFilter* Filter);
AvHAIResourceNode* AITAC_GetNearestResourceNodeToLocation(const Vector Location);
@ -119,8 +119,10 @@ 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_GetNumPlayersOfTeamAndClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
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);
vector<edict_t*> AITAC_GetAllPlayersOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
AvHAIHiveDefinition* AITAC_GetTeamHiveWithTech(const AvHTeamNumber Team, const AvHMessageID Tech);
bool AITAC_TeamHiveWithTechExists(const AvHTeamNumber Team, const AvHMessageID Tech);
@ -158,4 +160,12 @@ const vector<AvHAIHiveDefinition*> AITAC_GetAllHives();
bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius);
bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot);
bool AITAC_IsAlienCapperNeeded(AvHAIPlayer* pBot);
bool AITAC_IsAlienHarasserNeeded(AvHAIPlayer* pBot);
bool AITAC_ShouldBotBuildHive(AvHAIPlayer* pBot, AvHAIHiveDefinition** EligibleHive);
AvHAIDeployableStructureType AITAC_GetNextMissingUpgradeChamberForTeam(AvHTeamNumber Team);
#endif

View File

@ -581,71 +581,23 @@ bool AITASK_IsAlienBuildTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (!Task) { return false; }
if (!Task->TaskLocation) { return false; }
if (vIsZero(Task->TaskLocation)) { return false; }
if (Task->StructureType == STRUCTURE_NONE) { return false; }
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return true; }
if (pBot->ActiveBuildInfo.NumAttempts >= 3) { return false; }
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_FAILED && pBot->ActiveBuildInfo.NumAttempts >= 3) { return false; }
if (!FNullEnt(Task->TaskTarget) && !UTIL_IsBuildableStructureStillReachable(pBot, Task->TaskTarget)) { return false; }
if (pBot->ActiveBuildInfo.LinkedStructure && (UTIL_StructureIsFullyBuilt(pBot->ActiveBuildInfo.LinkedStructure->edict) || !UTIL_IsBuildableStructureStillReachable(pBot, pBot->ActiveBuildInfo.LinkedStructure->edict))) { return false; }
if (Task->StructureType == STRUCTURE_ALIEN_HIVE)
{
if (gpGlobals->time - Task->LastBuildAttemptTime < 1.0f) { return true; }
const AvHAIHiveDefinition* HiveIndex = AITAC_GetHiveNearestLocation(Task->TaskLocation);
if (!HiveIndex) { return false; }
if (HiveIndex->Status != HIVE_STATUS_UNBUILT) { return false; }
edict_t* OtherHiveBuilder = nullptr;
AvHAIPlayer* OtherHiveBuilderBot = GetFirstBotWithBuildTask(pBot->Player->GetTeam(), STRUCTURE_ALIEN_HIVE, pBot->Edict);
if (OtherHiveBuilderBot)
{
OtherHiveBuilder = OtherHiveBuilderBot->Edict;
}
if (!FNullEnt(OtherHiveBuilder) && GetPlayerResources(OtherHiveBuilder) > GetPlayerResources(pBot->Edict)) { return false; }
edict_t* OtherGorge = AITAC_GetNearestPlayerOfClassInArea(pBot->Player->GetTeam(), HiveIndex->Location, UTIL_MetresToGoldSrcUnits(10.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
if (!FNullEnt(OtherGorge) && GetPlayerResources(OtherGorge) > pBot->Player->GetResources())
{
char buf[512];
sprintf(buf, "I won't drop hive, %s can do it", STRING(OtherGorge->v.netname));
BotSay(pBot, true, 1.0f, buf);
return false;
}
DeployableSearchFilter EnemyFilter;
EnemyFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
EnemyFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
// Marines have built a phase gate and/or turret factory in the hive
if (AITAC_DeployableExistsAtLocation(HiveIndex->Location, &EnemyFilter))
{
string LocationName;
char buf[512];
if (GetNearestMapLocationAtPoint(HiveIndex->Location, LocationName))
{
sprintf(buf, "We need to clear %s before I can build the hive", LocationName.c_str());
}
else
{
sprintf(buf, "We need to clear the hive before I can build it");
}
BotSay(pBot, true, 1.0f, buf);
return false;
}
return true;
}
if (Task->StructureType == STRUCTURE_ALIEN_RESTOWER)
@ -658,54 +610,13 @@ bool AITASK_IsAlienBuildTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (ResNodeIndex->OwningTeam != pBot->Player->GetTeam()) { return false; } // Node has been capped by the enemy, no longer relevant
if (!IsPlayerGorge(pBot->Edict)) { return false; } // Don't evolve into a gorge just to finish building an already-placed structure
if (UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity)) { return false; } // Don't bother if it's already completed
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->Location) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) { return false; } // Only bother finishing the build if you're close enough
if (FNullEnt(Task->TaskTarget))
{
Task->TaskTarget = ResNodeIndex->ActiveTowerEntity;
}
}
edict_t* OtherGorge = AITAC_GetNearestPlayerOfClassInArea(pBot->Player->GetTeam(), Task->TaskLocation, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
// Check if another player is planning to cap the res node. If they are closer, and either the tower is already placed or they have enough res to place the tower, then move on and do something else
if (!FNullEnt(OtherGorge))
{
if (vDist2DSq(OtherGorge->v.origin, Task->TaskLocation) < vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) && (!FNullEnt(Task->TaskTarget) || (GetPlayerResources(OtherGorge) >= BALANCE_VAR(kResourceTowerCost))))
{
return false;
}
}
return true;
}
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = Task->StructureType;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
// Don't build more if we've already got quite a few in the immediate vicinity. Helps prevent structure spam
if (AITAC_GetNumDeployablesNearLocation(Task->TaskLocation, &StructureFilter) >= 3)
{
return false;
}
if (!FNullEnt(Task->TaskTarget))
{
if ((Task->TaskTarget->v.effects & EF_NODRAW) || (Task->TaskTarget->v.deadflag == DEAD_DEAD)) { return false; }
return !UTIL_StructureIsFullyBuilt(Task->TaskTarget);
}
else
{
if (Task->StructureType == STRUCTURE_ALIEN_RESTOWER) { return true; }
return UTIL_GetNavAreaAtLocation(Task->TaskLocation) == SAMPLE_POLYAREA_GROUND;
}
return UTIL_GetNavAreaAtLocation(Task->TaskLocation) == SAMPLE_POLYAREA_GROUND;
}
bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
@ -1261,269 +1172,172 @@ void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
}
void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (FNullEnt(Task->TaskTarget)) { return; }
if (!FNullEnt(Task->TaskSecondaryTarget))
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return; }
// We had a go, whether it succeeded or not we should try a new location
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_FAILED || pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_SUCCESS)
{
if (UTIL_StructureIsFullyBuilt(Task->TaskSecondaryTarget))
Task->TaskLocation = ZERO_VECTOR;
pBot->ActiveBuildInfo.BuildStatus = BUILD_ATTEMPT_NONE;
}
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
Vector ReinforceLocation = Task->TaskTarget->v.origin;
float SearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
if (Task->StructureType == STRUCTURE_ALIEN_HIVE)
{
AvHAIHiveDefinition* HiveToReinforce = AITAC_GetHiveFromEdict(Task->TaskTarget);
if (HiveToReinforce)
{
Task->TaskSecondaryTarget = nullptr;
Task->BuildAttempts = 0;
Task->bIsWaitingForBuildLink = false;
Task->TaskLocation = g_vecZero;
ReinforceLocation = HiveToReinforce->FloorLocation;
}
else
{
if (IsPlayerInUseRange(pBot->Edict, Task->TaskSecondaryTarget))
{
BotUseObject(pBot, Task->TaskSecondaryTarget, true);
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskSecondaryTarget->v.origin) > sqrf(60.0f))
{
MoveDirectlyTo(pBot, Task->TaskSecondaryTarget->v.origin);
}
return;
}
MoveTo(pBot, Task->TaskSecondaryTarget->v.origin, MOVESTYLE_NORMAL);
return;
}
SearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
}
if (gpGlobals->time - Task->LastBuildAttemptTime < 1.0f)
{
return;
}
if (Task->bIsWaitingForBuildLink)
{
Task->TaskLocation = g_vecZero;
Task->bIsWaitingForBuildLink = false;
}
AvHMessageID HiveTechOne = CONFIG_GetHiveTechAtIndex(0);
AvHMessageID HiveTechTwo = CONFIG_GetHiveTechAtIndex(1);
AvHMessageID HiveTechThree = CONFIG_GetHiveTechAtIndex(2);
AvHAIDeployableStructureType ChamberTypeOne = UTIL_GetChamberTypeForHiveTech(HiveTechOne);
AvHAIDeployableStructureType ChamberTypeTwo = UTIL_GetChamberTypeForHiveTech(HiveTechTwo);
AvHAIDeployableStructureType ChamberTypeThree = UTIL_GetChamberTypeForHiveTech(HiveTechThree);
bool bActiveHiveWithoutTechExists = AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), MESSAGE_NULL);
bool bActiveHiveWithTechOneExists = AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), HiveTechOne);
bool bActiveHiveWithTechTwoExists = AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), HiveTechTwo);
bool bActiveHiveWithTechThreeExists = AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), HiveTechThree);
AvHAIDeployableStructureType NextStructure = AITAC_GetNextMissingUpgradeChamberForTeam(BotTeam);
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = ChamberTypeOne;
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
StructureFilter.DeployableTeam = BotTeam;
StructureFilter.MaxSearchRadius = SearchRadius;
int NumHiveTechOne = AITAC_GetNumDeployablesNearLocation(ZERO_VECTOR, &StructureFilter);
StructureFilter.DeployableTypes = ChamberTypeTwo;
int NumHiveTechTwo = AITAC_GetNumDeployablesNearLocation(ZERO_VECTOR, &StructureFilter);
StructureFilter.DeployableTypes = ChamberTypeThree;
int NumHiveTechThree = AITAC_GetNumDeployablesNearLocation(ZERO_VECTOR, &StructureFilter);
if (Task->StructureType == STRUCTURE_NONE)
if (NextStructure == STRUCTURE_NONE)
{
if (bActiveHiveWithoutTechExists)
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
int NumOCs = AITAC_GetNumDeployablesNearLocation(ReinforceLocation, &StructureFilter);
if (NumOCs < 3)
{
if (!bActiveHiveWithTechOneExists)
{
Task->StructureType = ChamberTypeOne;
}
else if (!bActiveHiveWithTechTwoExists)
{
Task->StructureType = ChamberTypeTwo;
}
else if (!bActiveHiveWithTechThreeExists)
{
Task->StructureType = ChamberTypeThree;
}
}
else
{
if (bActiveHiveWithTechOneExists && NumHiveTechOne < 3)
{
Task->StructureType = ChamberTypeOne;
}
else if (bActiveHiveWithTechTwoExists && NumHiveTechTwo < 3)
{
Task->StructureType = ChamberTypeTwo;
}
else if (bActiveHiveWithTechThreeExists && NumHiveTechThree < 3)
{
Task->StructureType = ChamberTypeThree;
}
else
{
Task->StructureType = STRUCTURE_ALIEN_OFFENCECHAMBER;
}
}
}
bool bCanBuildChamberTypeOne = bActiveHiveWithoutTechExists || bActiveHiveWithTechOneExists;
bool bCanBuildChamberTypeTwo = bActiveHiveWithoutTechExists || bActiveHiveWithTechTwoExists;
bool bCanBuildChamberTypeThree = bActiveHiveWithoutTechExists || bActiveHiveWithTechThreeExists;
StructureFilter.DeployableTypes = Task->StructureType;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
int NumChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
int NumDesiredChambers = (Task->StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER || Task->StructureType == STRUCTURE_ALIEN_DEFENCECHAMBER) ? 2 : 1;
if (Task->StructureType != STRUCTURE_ALIEN_OFFENCECHAMBER)
{
// The idea here is the gorge builds however many chambers at this hive/RT are needed to meet the minimum
// reinforce requirements, or ensure 3 of each upgrade chamber are built, whichever is more.
// e.g. normally they only build 1 MC/SC per hive/RT, but will build 3 if needed to ensure a min 3 of each chamber
int Deficit = 0;
if (Task->StructureType == ChamberTypeOne)
{
Deficit = clampi((3 - NumHiveTechOne), 0, 3);
}
if (Task->StructureType == ChamberTypeTwo)
{
Deficit = clampi((3 - NumHiveTechTwo), 0, 3);
}
if (Task->StructureType == ChamberTypeThree)
{
Deficit = clampi((3 - NumHiveTechThree), 0, 3);
}
Deficit += NumChambers;
NumDesiredChambers = imaxi(Deficit, NumDesiredChambers);
}
if (NumChambers >= NumDesiredChambers)
{
bool bChosenNextChamber = false;
if (!bActiveHiveWithoutTechExists)
{
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
int NumOffenceChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
if (NumOffenceChambers < 2)
{
Task->StructureType = STRUCTURE_ALIEN_OFFENCECHAMBER;
bChosenNextChamber = true;
}
}
if (!bChosenNextChamber && bCanBuildChamberTypeOne)
{
StructureFilter.DeployableTypes = ChamberTypeOne;
int NumDefenceChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
if (NumDefenceChambers < 2)
{
Task->StructureType = ChamberTypeOne;
bChosenNextChamber = true;
}
}
if (!bChosenNextChamber && bCanBuildChamberTypeTwo)
{
StructureFilter.DeployableTypes = ChamberTypeTwo;
int NumMoveChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
if (NumMoveChambers < 1)
{
Task->StructureType = ChamberTypeTwo;
bChosenNextChamber = true;
}
}
if (!bChosenNextChamber && bCanBuildChamberTypeThree)
{
StructureFilter.DeployableTypes = ChamberTypeThree;
int NumSensoryChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
if (NumSensoryChambers < 1)
{
Task->StructureType = ChamberTypeThree;
bChosenNextChamber = true;
}
}
if (!bChosenNextChamber) { return; }
}
if (Task->TaskLocation != g_vecZero)
{
dtPolyRef Poly = UTIL_GetNavAreaAtLocation(BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE], Task->TaskLocation);
if (Poly != SAMPLE_POLYAREA_GROUND)
{
Task->TaskLocation = g_vecZero;
NextStructure = STRUCTURE_ALIEN_OFFENCECHAMBER;
}
}
if (vIsZero(Task->TaskLocation))
if (NextStructure == STRUCTURE_NONE)
{
AvHAIDeployableStructureType ReinforcedStructure = GetStructureTypeFromEdict(Task->TaskTarget);
Vector TargetLocation = (ReinforcedStructure == STRUCTURE_ALIEN_HIVE) ? UTIL_GetFloorUnderEntity(Task->TaskTarget) : Task->TaskTarget->v.origin;
Vector BuildLocation = FindClosestNavigablePointToDestination(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), TargetLocation, UTIL_MetresToGoldSrcUnits(50.0f));
if (BuildLocation != g_vecZero)
if (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER))
{
float currDist = vDist2D(BuildLocation, TargetLocation);
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_DEFENCECHAMBER;
float MaxDist = fmaxf(UTIL_MetresToGoldSrcUnits(1.0f), (UTIL_MetresToGoldSrcUnits(5.0f) - currDist));
int NumDCs = AITAC_GetNumDeployablesNearLocation(ReinforceLocation, &StructureFilter);
Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[GORGE_BASE_NAV_PROFILE], BuildLocation, MaxDist);
if (NumDCs < 2)
{
NextStructure = STRUCTURE_ALIEN_DEFENCECHAMBER;
}
}
}
if (NextStructure == STRUCTURE_NONE)
{
if (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER))
{
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_MOVEMENTCHAMBER;
int NumMCs = AITAC_GetNumDeployablesNearLocation(ReinforceLocation, &StructureFilter);
if (NumMCs < 1)
{
NextStructure = STRUCTURE_ALIEN_MOVEMENTCHAMBER;
}
}
}
if (NextStructure == STRUCTURE_NONE)
{
if (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER))
{
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_SENSORYCHAMBER;
int NumSCs = AITAC_GetNumDeployablesNearLocation(ReinforceLocation, &StructureFilter);
if (NumSCs < 1)
{
NextStructure = STRUCTURE_ALIEN_SENSORYCHAMBER;
}
}
}
if (NextStructure != STRUCTURE_NONE)
{
if (vIsZero(Task->TaskLocation))
{
Vector NewLoc = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ReinforceLocation, SearchRadius);
if (vIsZero(NewLoc))
{
NewLoc = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), ReinforceLocation, SearchRadius);
}
Task->TaskLocation = NewLoc;
}
if (vIsZero(Task->TaskLocation)) { return; }
}
int ResRequired = UTIL_GetCostOfStructureType(Task->StructureType);
if (!IsPlayerGorge(pBot->Edict))
{
ResRequired += BALANCE_VAR(kGorgeCost);
}
if (pBot->Player->GetResources() < ResRequired)
{
if (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB))
if (!vIsZero(Task->TaskLocation))
{
DeployableSearchFilter EnemyStuff;
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam());
EnemyStuff.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStuff.ReachabilityTeam = pBot->Player->GetTeam();
EnemyStuff.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
AvHAIBuildableStructure* NearestEnemyStructure = AITAC_FindClosestDeployableToLocation(Task->TaskTarget->v.origin, &EnemyStuff);
if (NearestEnemyStructure)
float ResourceCost = UTIL_GetCostOfStructureType(NextStructure);
if (!IsPlayerGorge(pBot->Edict))
{
BotAttackNonPlayerTarget(pBot, NearestEnemyStructure->edict);
ResourceCost += BALANCE_VAR(kGorgeCost);
}
if (pBot->Player->GetResources() >= ResourceCost)
{
BotAlienPlaceChamber(pBot, Task->TaskLocation, NextStructure);
return;
}
}
BotGuardLocation(pBot, Task->TaskLocation);
return;
}
// We have nothing to build, or we don't have enough resources yet, see if there's any unfinished structures we can finish off
if (IsPlayerGorge(pBot->Edict))
{
DeployableSearchFilter UnfinishedFilter;
UnfinishedFilter.DeployableTeam = BotTeam;
UnfinishedFilter.ExcludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
UnfinishedFilter.ReachabilityTeam = BotTeam;
UnfinishedFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
UnfinishedFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
UnfinishedFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
BotAlienPlaceChamber(pBot, Task->TaskLocation, Task->StructureType);
AvHAIBuildableStructure* UnfinishedStructure = AITAC_FindClosestDeployableToLocation(ReinforceLocation, &UnfinishedFilter);
if (UnfinishedStructure)
{
AIPlayerBuildStructure(pBot, UnfinishedStructure->edict);
return;
}
}
// We can't build anything, see if there's anything to attack nearby
if (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB))
{
DeployableSearchFilter EnemyStructureFilter;
EnemyStructureFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
EnemyStructureFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStructureFilter.ReachabilityTeam = BotTeam;
EnemyStructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
AvHAIBuildableStructure* EnemyStructure = AITAC_FindClosestDeployableToLocation(ReinforceLocation, &EnemyStructureFilter);
if (EnemyStructure)
{
BotAttackNonPlayerTarget(pBot, EnemyStructure->edict);
return;
}
}
BotGuardLocation(pBot, ReinforceLocation);
}
@ -1582,46 +1396,48 @@ void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget)
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 (IsPlayerMarine(pBot->Edict))
{
if (vDist2DSq(pBot->Edict->v.origin, BuildTarget->v.origin) > sqrf(60.0f))
// If we were ducking before then keep ducking
if (pBot->Edict->v.oldbuttons & IN_DUCK)
{
MoveDirectlyTo(pBot, BuildTarget->v.origin);
pBot->Button |= IN_DUCK;
}
else
{
Vector NewViewPoint = UTIL_GetRandomPointInBoundingBox(BuildTarget->v.absmin, BuildTarget->v.absmax);
BotLookAt(pBot, NewViewPoint);
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))
{
// 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)
{
if (BuildTarget->v.origin > pBot->Edict->v.origin)
{
BotJump(pBot);
}
else
{
pBot->Button |= IN_DUCK;
}
BotJump(pBot);
}
else
{
pBot->Button |= IN_DUCK;
}
}
MoveTo(pBot, BuildTarget->v.origin, MOVESTYLE_NORMAL);
@ -1777,6 +1593,12 @@ void BotProgressAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
return;
}
if (IsPlayerGorge(pBot->Edict) && !PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB))
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_ONE);
return;
}
AvHAIWeapon Weapon = WEAPON_INVALID;
if (IsPlayerMarine(pBot->Edict))
@ -2038,6 +1860,12 @@ void AlienProgressBuildHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
// We tried and failed to place the structure
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING)
{
return;
}
if (Task->StructureType == STRUCTURE_ALIEN_HIVE)
{
AlienProgressBuildHiveTask(pBot, Task);
@ -2046,19 +1874,21 @@ void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (pBot->ActiveBuildInfo.LinkedStructure)
{
if (UTIL_StructureIsFullyBuilt(pBot->ActiveBuildInfo.LinkedStructure->edict)) { return; }
edict_t* LinkedEdict = pBot->ActiveBuildInfo.LinkedStructure->edict;
if (IsPlayerInUseRange(pBot->Edict, Task->TaskTarget))
if (UTIL_StructureIsFullyBuilt(LinkedEdict)) { return; }
if (IsPlayerInUseRange(pBot->Edict, LinkedEdict))
{
BotUseObject(pBot, Task->TaskTarget, true);
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) > sqrf(60.0f))
BotUseObject(pBot, LinkedEdict, true);
if (vDist2DSq(pBot->Edict->v.origin, LinkedEdict->v.origin) > sqrf(60.0f))
{
MoveDirectlyTo(pBot, Task->TaskTarget->v.origin);
MoveDirectlyTo(pBot, LinkedEdict->v.origin);
}
return;
}
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
MoveTo(pBot, LinkedEdict->v.origin, MOVESTYLE_NORMAL);
return;
}
@ -3231,10 +3061,9 @@ void AITASK_SetCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const Av
AvHAIDeployableStructureType NodeStructureType = (IsPlayerMarine(pBot->Edict)) ? STRUCTURE_MARINE_RESTOWER : STRUCTURE_ALIEN_RESTOWER;
if (Task->TaskType == TASK_CAP_RESNODE && Task->TaskLocation == NodeRef->Location)
if (Task->TaskType == TASK_CAP_RESNODE && Task->TaskTarget == NodeRef->ResourceEdict)
{
Task->bTaskIsUrgent = bIsUrgent;
Task->TaskTarget = NodeRef->ActiveTowerEntity;
return;
}
@ -3245,11 +3074,7 @@ void AITASK_SetCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const Av
Task->TaskType = TASK_CAP_RESNODE;
Task->StructureType = NodeStructureType;
Task->TaskLocation = (!vIsZero(WaitLocation)) ? WaitLocation : NodeRef->Location;
if (!FNullEnt(NodeRef->ActiveTowerEntity))
{
Task->TaskTarget = NodeRef->ActiveTowerEntity;
}
Task->TaskTarget = NodeRef->ResourceEdict;
Task->bTaskIsUrgent = bIsUrgent;
}
@ -3389,6 +3214,7 @@ void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task,
Task->TaskType = TASK_REINFORCE_STRUCTURE;
Task->TaskTarget = Target;
Task->StructureType = GetStructureTypeFromEdict(Target);
Task->bTaskIsUrgent = bIsUrgent;
}

View File

@ -526,6 +526,16 @@ Vector UTIL_GetGrenadeThrowTarget(edict_t* Player, const Vector TargetLocation,
}
}
AvHAIWeapon BotAlienChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target)
{
if (FNullEnt(target))
{
return UTIL_GetPlayerPrimaryWeapon(pBot->Player);
}
return BotAlienChooseBestWeaponForStructure(pBot, target);
}
AvHAIWeapon BotMarineChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target)
{

View File

@ -47,6 +47,7 @@ AvHAIWeapon BotAlienChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* tar
// Helper function to pick the best weapon for any given situation and target type.
AvHAIWeapon BotMarineChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target);
AvHAIWeapon BotAlienChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target);
AvHAIWeapon FadeGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);
AvHAIWeapon OnosGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);

View File

@ -1535,6 +1535,57 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
else if (FStrEq(pcmd, "getaliencomp"))
{
vector<AvHAIPlayer*> AlienPlayers = AIMGR_GetAIPlayersOnTeam(TEAM_TWO);
int NumBuilders = 0;
int NumCappers = 0;
int NumHarassers = 0;
int NumAssault = 0;
for (auto it = AlienPlayers.begin(); it != AlienPlayers.end(); it++)
{
AvHAIPlayer* NewCapper = (*it);
if (NewCapper)
{
switch (NewCapper->BotRole)
{
case BOT_ROLE_BUILDER:
NumBuilders++;
break;
case BOT_ROLE_FIND_RESOURCES:
NumCappers++;
break;
case BOT_ROLE_HARASS:
NumHarassers++;
break;
case BOT_ROLE_ASSAULT:
NumAssault++;
break;
default:
break;
}
}
}
char buf[32];
sprintf(buf, "Builders: %d\n", NumBuilders);
UTIL_SayText(buf, theAvHPlayer);
sprintf(buf, "Cappers: %d\n", NumCappers);
UTIL_SayText(buf, theAvHPlayer);
sprintf(buf, "Harassers: %d\n", NumHarassers);
UTIL_SayText(buf, theAvHPlayer);
sprintf(buf, "Assault: %d\n", NumAssault);
UTIL_SayText(buf, theAvHPlayer);
theSuccess = true;
}
else if (FStrEq(pcmd, "testresearchavailable"))
{
AvHTeam* PlayerTeam = GetGameRules()->GetTeam(theAvHPlayer->GetTeam());