Alien coordinated base attacks

* Aliens will now gang up to attack the marine base
* Aliens will spread out more when attacking in a group, rather than all going for the same buildings
This commit is contained in:
RGreenlees 2024-05-15 15:21:17 +01:00 committed by pierow
parent c0bef7cb05
commit 5beb313546
7 changed files with 247 additions and 55 deletions

View file

@ -415,7 +415,7 @@ typedef enum
TASK_REINFORCE_STRUCTURE,
TASK_SECURE_HIVE,
TASK_PLACE_MINE,
TASK_ATTACK_BASE
TASK_ASSAULT_MARINE_BASE
}
BotTaskType;
@ -821,6 +821,7 @@ typedef struct _AVH_AI_SQUAD
vector<AvHAIPlayer*> SquadMembers; // Which bots are assigned to this
Vector SquadGatherLocation = g_vecZero; // Where should the squad gather before attempting the objective?
edict_t* SquadTarget = nullptr; // The target of the objective
Vector ObjectiveLocation = g_vecZero;
BotTaskType SquadObjective = TASK_NONE; // What to do with the objective
bool bExecuteObjective = false; // Are we at the gather or execute phase?

View file

@ -680,8 +680,8 @@ char* UTIL_TaskTypeToChar(const BotTaskType TaskType)
return "Touch Trigger";
case TASK_WELD:
return "Weld Target";
case TASK_ATTACK_BASE:
return "Attack Enemy Base";
case TASK_ASSAULT_MARINE_BASE:
return "Assault Marine Base";
default:
return "None";
}

View file

@ -1878,19 +1878,17 @@ void EndBotFrame(AvHAIPlayer* pBot)
void CustomThink(AvHAIPlayer* pBot)
{
pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot);
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (pBot->CurrentEnemy > -1)
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
if (IsPlayerMarine(pBot->Player))
{
if (MarineCombatThink(pBot)) { return; }
}
else
{
if (AlienCombatThink(pBot)) { return; }
}
AITASK_SetAssaultMarineBaseTask(pBot, &pBot->PrimaryBotTask, AITAC_GetTeamStartingLocation(EnemyTeam), true);
}
pBot->CurrentTask = &pBot->PrimaryBotTask;
BotProgressTask(pBot, pBot->CurrentTask);
}
void DroneThink(AvHAIPlayer* pBot)
@ -2240,18 +2238,18 @@ void AIPlayerNSThink(AvHAIPlayer* pBot)
{
AvHTeam* BotTeam = GetGameRules()->GetTeam(pBot->Player->GetTeam());
if (!BotTeam) { return; }
if (!BotTeam) { return; }
pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot);
pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot);
if (BotTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)
{
AIPlayerNSMarineThink(pBot);
}
else
{
AIPlayerNSAlienThink(pBot);
}
if (BotTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)
{
AIPlayerNSMarineThink(pBot);
}
else
{
AIPlayerNSAlienThink(pBot);
}
}
int BotGetNextEnemyTarget(AvHAIPlayer* pBot)
@ -5935,13 +5933,11 @@ void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination)
{
if (UTIL_QuickTrace(pBot->Edict, Destination + Vector(0.0f, 0.0f, 32.0f), HiveRef->Location))
{
AITASK_SetAttackTask(pBot, &pBot->CommanderTask, HiveRef->HiveEdict, false);
AITASK_SetSecureHiveTask(pBot, &pBot->CommanderTask, HiveRef->HiveEdict, ActualMoveLocation, false);
pBot->CommanderTask.bIssuedByCommander = true;
return;
}
}
}
}
// Otherwise, treat as a normal move order. Go there and wait a bit to see what the commander wants to do next
@ -6778,12 +6774,14 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
int MaxHiveStrength = 0;
AvHAIHiveDefinition* HiveToSecure = nullptr;
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->OwningTeam == BotTeam) { continue; }
vector<AvHAIBuildableStructure> EnemyStructures = AITAC_FindAllDeployables(ThisHive->FloorLocation, &EnemyStuffFilter);
// Enemy hasn't built anything here, so doesn't need clearing
@ -6825,32 +6823,29 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
return;
}
// ATTACK THE ENEMY BASE
// ATTACK THE ENEMY BASE IF AGAINST MARINES
DeployableSearchFilter EnemyInfPortalFilter;
EnemyInfPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
EnemyInfPortalFilter.DeployableTeam = EnemyTeam;
EnemyInfPortalFilter.ReachabilityTeam = BotTeam;
EnemyInfPortalFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyInfPortalFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyInfPortalFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure EnemyInfPortal = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyInfPortalFilter);
if (EnemyInfPortal.IsValid())
// AvA is handled above in securing hives: this will cause aliens to attack and eliminate all enemy hives
// So this is only needed for marines where their base could be anywhere outside of hives
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
AITASK_SetAttackTask(pBot, Task, EnemyInfPortal.edict, false);
return;
}
if (Task->TaskType == TASK_ASSAULT_MARINE_BASE) { return; }
// TODO: Attack enemy hive/base
edict_t* EnemyChair = AITAC_GetCommChair(EnemyTeam);
Vector EnemyBaseLocation = AITAC_GetTeamStartingLocation(EnemyTeam);
if (!FNullEnt(EnemyChair))
{
AITASK_SetAttackTask(pBot, Task, EnemyChair, false);
return;
if (!vIsZero(EnemyBaseLocation))
{
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (AITAC_DeployableExistsAtLocation(EnemyBaseLocation, &EnemyStuffFilter))
{
AITASK_SetAssaultMarineBaseTask(pBot, Task, EnemyBaseLocation, false);
return;
}
}
}
// FIND ANY LAST ENEMIES TO KILL AND END GAME

View file

@ -5514,7 +5514,7 @@ bool AITAC_IsBotPursuingSquadObjective(AvHAIPlayer* pBot, AvHAISquad* Squad)
if (!IsPlayerActiveInGame(pBot->Edict) || pBot->Player->GetTeam() != Squad->SquadTeam) { return false; }
// Bot no longer has this squad's objective as its primary task
if (pBot->PrimaryBotTask.TaskType != Squad->SquadObjective || pBot->PrimaryBotTask.TaskTarget != Squad->SquadTarget) { return false; }
if (pBot->PrimaryBotTask.TaskType != Squad->SquadObjective || (!FNullEnt(Squad->SquadTarget) && pBot->PrimaryBotTask.TaskTarget != Squad->SquadTarget) || (FNullEnt(Squad->SquadTarget) && !vEquals(pBot->PrimaryBotTask.TaskLocation, Squad->ObjectiveLocation))) { return false; }
// Bot is focused on the job at hand
if (!pBot->CurrentTask || pBot->CurrentTask == &pBot->PrimaryBotTask) { return true; }
@ -5565,7 +5565,9 @@ void AITAC_UpdateSquads()
{
vector<bot_path_node> TravelPath;
dtStatus PathFindResult = FindPathClosestToPoint(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), AITAC_GetTeamStartingLocation(it->SquadTeam), UTIL_GetEntityGroundLocation(it->SquadTarget), TravelPath, UTIL_MetresToGoldSrcUnits(20.0f));
Vector TargetLocation = (!FNullEnt(it->SquadTarget)) ? UTIL_GetEntityGroundLocation(it->SquadTarget) : it->ObjectiveLocation;
dtStatus PathFindResult = FindPathClosestToPoint(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), AITAC_GetTeamStartingLocation(it->SquadTeam), TargetLocation, TravelPath, UTIL_MetresToGoldSrcUnits(20.0f));
if (dtStatusSucceed(PathFindResult))
{
@ -5573,9 +5575,12 @@ void AITAC_UpdateSquads()
{
if (pIt->area != SAMPLE_POLYAREA_GROUND || pIt->flag != SAMPLE_POLYFLAGS_WALK) { continue; }
if (UTIL_QuickTrace(nullptr, pIt->Location, it->SquadTarget->v.origin)) { continue; }
if (!FNullEnt(it->SquadTarget))
{
if (UTIL_QuickTrace(nullptr, pIt->Location, it->SquadTarget->v.origin)) { continue; }
}
if (vDist2DSq(pIt->Location, it->SquadTarget->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f)))
if (vDist2DSq(pIt->Location, TargetLocation) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f)))
{
DeployableSearchFilter EnemyStuff;
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(it->SquadTeam);
@ -5663,6 +5668,48 @@ AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, edict_t* TaskTarget, B
return nullptr;
}
AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, Vector TaskLocation, BotTaskType ObjectiveType)
{
AvHAISquad* JoinSquad = nullptr;
for (auto it = ActiveSquads.begin(); it != ActiveSquads.end(); it++)
{
if (it->SquadTeam == pBot->Player->GetTeam() && vEquals(it->ObjectiveLocation, TaskLocation) && it->SquadObjective == ObjectiveType)
{
auto element = std::find(it->SquadMembers.begin(), it->SquadMembers.end(), pBot);
if (element != it->SquadMembers.end())
{
return &(*it);
}
else
{
if (!JoinSquad && !it->bExecuteObjective)
{
JoinSquad = &(*it);
}
}
}
}
if (JoinSquad)
{
JoinSquad->SquadMembers.push_back(pBot);
return JoinSquad;
}
AvHAISquad NewSquad;
NewSquad.SquadTeam = pBot->Player->GetTeam();
NewSquad.ObjectiveLocation = TaskLocation;
NewSquad.SquadObjective = ObjectiveType;
NewSquad.bExecuteObjective = false;
NewSquad.SquadGatherLocation = ZERO_VECTOR;
ActiveSquads.push_back(NewSquad);
return nullptr;
}
void AITAC_ClearSquads()
{
ActiveSquads.clear();

View file

@ -208,6 +208,7 @@ void AITAC_UpdateSquads();
void AITAC_ManageSquads();
void AITAC_ClearSquads();
AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, edict_t* TaskTarget, BotTaskType ObjectiveType);
AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, Vector TaskLocation, BotTaskType ObjectiveType);
Vector AITAC_GetGatherLocationForSquad(AvHAISquad* Squad);
#endif

View file

@ -395,8 +395,8 @@ bool AITASK_IsTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
return AITASK_IsAlienSecureHiveTaskStillValid(pBot, Task);
}
}
case TASK_ATTACK_BASE:
return true;
case TASK_ASSAULT_MARINE_BASE:
return AITASK_IsAssaultMarineBaseTaskStillValid(pBot, Task);
case TASK_DEFEND:
return AITASK_IsDefendTaskStillValid(pBot, Task);
case TASK_WELD:
@ -961,6 +961,24 @@ bool AITASK_IsAlienSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
return AITAC_DeployableExistsAtLocation(HiveToSecure->FloorLocation, &EnemyStuff);
}
bool AITASK_IsAssaultMarineBaseTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (AIMGR_GetTeamType(EnemyTeam) != AVH_CLASS_TYPE_MARINE) { return false; }
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTeam = EnemyTeam;
StructureFilter.DeployableTypes = (STRUCTURE_MARINE_OBSERVATORY | STRUCTURE_MARINE_ARMSLAB | STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY | STRUCTURE_MARINE_INFANTRYPORTAL | STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_PROTOTYPELAB);
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
StructureFilter.ReachabilityTeam = BotTeam;
StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
return AITAC_DeployableExistsAtLocation(Task->TaskLocation, &StructureFilter);
}
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (!Task || FNullEnt(Task->TaskTarget) || IsPlayerAlien(pBot->Edict)) { return false; }
@ -2577,6 +2595,9 @@ void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
}
break;
case TASK_ASSAULT_MARINE_BASE:
BotProgressAssaultMarineBaseTask(pBot, Task);
break;
default:
break;
@ -2997,6 +3018,115 @@ void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
void BotProgressAssaultMarineBaseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (AIMGR_GetTeamType(BotTeam) == AVH_CLASS_TYPE_ALIEN)
{
AvHAISquad* ActiveSquad = AITAC_GetSquadForObjective(pBot, Task->TaskLocation, Task->TaskType);
if (ActiveSquad && !ActiveSquad->bExecuteObjective && !vIsZero(ActiveSquad->SquadGatherLocation))
{
BotGuardLocation(pBot, ActiveSquad->SquadGatherLocation);
return;
}
}
DeployableSearchFilter EnemyStructureFilter;
EnemyStructureFilter.DeployableTeam = EnemyTeam;
EnemyStructureFilter.ReachabilityTeam = BotTeam;
EnemyStructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
EnemyStructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
// First go for any TFs, to eliminate defences
EnemyStructureFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
vector<AvHAIBuildableStructure> Structures = AITAC_FindAllDeployables(Task->TaskLocation, &EnemyStructureFilter);
for (auto it = Structures.begin(); it != Structures.end(); it++)
{
float DistToStructure = vDist2D(pBot->Edict->v.origin, it->Location) - 5.0f;
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, it->Location, DistToStructure, false, pBot->Edict, AVH_USER3_NONE) < 2)
{
BotAttackNonPlayerTarget(pBot, it->edict);
return;
}
}
// First go for any observatory, to prevent beacon
EnemyStructureFilter.DeployableTypes = STRUCTURE_MARINE_OBSERVATORY;
Structures = AITAC_FindAllDeployables(Task->TaskLocation, &EnemyStructureFilter);
for (auto it = Structures.begin(); it != Structures.end(); it++)
{
float DistToStructure = vDist2D(pBot->Edict->v.origin, it->Location) - 5.0f;
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, it->Location, DistToStructure, false, pBot->Edict, AVH_USER3_NONE) < 2)
{
BotAttackNonPlayerTarget(pBot, it->edict);
return;
}
}
// Next go for any arms lab, to weaken the marines
EnemyStructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMSLAB;
Structures = AITAC_FindAllDeployables(Task->TaskLocation, &EnemyStructureFilter);
for (auto it = Structures.begin(); it != Structures.end(); it++)
{
float DistToStructure = vDist2D(pBot->Edict->v.origin, it->Location) - 5.0f;
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, it->Location, DistToStructure, false, pBot->Edict, AVH_USER3_NONE) < 2)
{
BotAttackNonPlayerTarget(pBot, it->edict);
return;
}
}
// Next go for any infantry portals, to prevent reinforcements
EnemyStructureFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
Structures = AITAC_FindAllDeployables(Task->TaskLocation, &EnemyStructureFilter);
for (auto it = Structures.begin(); it != Structures.end(); it++)
{
float DistToStructure = vDist2D(pBot->Edict->v.origin, it->Location) - 5.0f;
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, it->Location, DistToStructure, false, pBot->Edict, AVH_USER3_NONE) < 2)
{
BotAttackNonPlayerTarget(pBot, it->edict);
return;
}
}
// Finally, any other structures
EnemyStructureFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
Structures = AITAC_FindAllDeployables(Task->TaskLocation, &EnemyStructureFilter);
for (auto it = Structures.begin(); it != Structures.end(); it++)
{
float DistToStructure = vDist2D(pBot->Edict->v.origin, it->Location) - 5.0f;
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, it->Location, DistToStructure, false, pBot->Edict, AVH_USER3_NONE) < 2)
{
BotAttackNonPlayerTarget(pBot, it->edict);
return;
}
}
// nothing to attack, just hang around
BotGuardLocation(pBot, Task->TaskLocation);
}
void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation)
{
float DistFromGuardLocation = vDist2DSq(pBot->Edict->v.origin, GuardLocation);
@ -3858,6 +3988,19 @@ void AITASK_SetMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict
Task->bTaskIsUrgent = bIsUrgent;
Task->TaskLocation = UTIL_GetNextMinePosition2(Target);
Task->StructureType = STRUCTURE_MARINE_DEPLOYEDMINE;
}
void AITASK_SetAssaultMarineBaseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, Vector BaseLocation, bool bIsUrgent)
{
if (Task->TaskType == TASK_ASSAULT_MARINE_BASE && vEquals(BaseLocation, Task->TaskLocation))
{
Task->bTaskIsUrgent = bIsUrgent;
return;
}
AITASK_ClearBotTask(pBot, Task);
Task->TaskType = TASK_ASSAULT_MARINE_BASE;
Task->TaskLocation = BaseLocation;
Task->bTaskIsUrgent = bIsUrgent;
}

View file

@ -47,6 +47,8 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAssaultMarineBaseTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienGetHealthTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienHealTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
@ -67,6 +69,7 @@ void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task,
void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const AvHAIDeployableStructureType FirstStructureType, bool bIsUrgent);
void AITASK_SetSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const Vector WaitLocation, bool bIsUrgent);
void AITASK_SetMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, bool bIsUrgent);
void AITASK_SetAssaultMarineBaseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, Vector BaseLocation, bool bIsUrgent);
void AITASK_SetWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* HealingSource, const bool bIsUrgent);
@ -90,6 +93,8 @@ void MarineProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressAssaultMarineBaseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget);
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);