From 5beb313546b70778a106ed5520621c12b3bf309a Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Wed, 15 May 2024 15:21:17 +0100 Subject: [PATCH] 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 --- main/source/mod/AvHAIConstants.h | 3 +- main/source/mod/AvHAIHelper.cpp | 4 +- main/source/mod/AvHAIPlayer.cpp | 87 +++++++++--------- main/source/mod/AvHAITactical.cpp | 55 ++++++++++- main/source/mod/AvHAITactical.h | 1 + main/source/mod/AvHAITask.cpp | 147 +++++++++++++++++++++++++++++- main/source/mod/AvHAITask.h | 5 + 7 files changed, 247 insertions(+), 55 deletions(-) diff --git a/main/source/mod/AvHAIConstants.h b/main/source/mod/AvHAIConstants.h index 73896975..6ecd84e7 100644 --- a/main/source/mod/AvHAIConstants.h +++ b/main/source/mod/AvHAIConstants.h @@ -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 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? diff --git a/main/source/mod/AvHAIHelper.cpp b/main/source/mod/AvHAIHelper.cpp index 00ff8823..ca2becd7 100644 --- a/main/source/mod/AvHAIHelper.cpp +++ b/main/source/mod/AvHAIHelper.cpp @@ -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"; } diff --git a/main/source/mod/AvHAIPlayer.cpp b/main/source/mod/AvHAIPlayer.cpp index 49a1efde..bea5861a 100644 --- a/main/source/mod/AvHAIPlayer.cpp +++ b/main/source/mod/AvHAIPlayer.cpp @@ -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 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 diff --git a/main/source/mod/AvHAITactical.cpp b/main/source/mod/AvHAITactical.cpp index f51b22f8..5744488a 100644 --- a/main/source/mod/AvHAITactical.cpp +++ b/main/source/mod/AvHAITactical.cpp @@ -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 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(); diff --git a/main/source/mod/AvHAITactical.h b/main/source/mod/AvHAITactical.h index 81c0f542..2157a8df 100644 --- a/main/source/mod/AvHAITactical.h +++ b/main/source/mod/AvHAITactical.h @@ -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 \ No newline at end of file diff --git a/main/source/mod/AvHAITask.cpp b/main/source/mod/AvHAITask.cpp index 9eb6d717..3c186222 100644 --- a/main/source/mod/AvHAITask.cpp +++ b/main/source/mod/AvHAITask.cpp @@ -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 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; } \ No newline at end of file diff --git a/main/source/mod/AvHAITask.h b/main/source/mod/AvHAITask.h index 7d8613d7..e3cca028 100644 --- a/main/source/mod/AvHAITask.h +++ b/main/source/mod/AvHAITask.h @@ -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);