Wave attacking implementation

This commit is contained in:
RGreenlees 2024-05-02 20:53:25 +01:00 committed by pierow
parent 8835eb0a60
commit 6021daabab
13 changed files with 695 additions and 198 deletions

View file

@ -323,7 +323,7 @@ typedef struct _BOT_MSG
bool bIsTeamSay = false; // Is this a team-only message?
} bot_msg;
typedef struct _BOT_GUARD_INFO
typedef struct _AVH_AI_GUARD_INFO
{
Vector GuardLocation = g_vecZero; // What position are we guarding?
Vector GuardStandPosition = g_vecZero; // Where the bot should stand to guard position (moves around a bit)
@ -414,7 +414,8 @@ typedef enum
TASK_TOUCH,
TASK_REINFORCE_STRUCTURE,
TASK_SECURE_HIVE,
TASK_PLACE_MINE
TASK_PLACE_MINE,
TASK_ATTACK_BASE
}
BotTaskType;
@ -809,7 +810,24 @@ typedef struct AVH_AI_PLAYER
float HearingThreshold = 0.0f; // How loud does a sound need to be before the bot detects it? This is set when hearing a sound so that louder sounds drown out quieter ones, and decrements quickly
int DebugValue = 0; // Used for debugging the bot
} AvHAIPlayer;
typedef struct _AVH_AI_SQUAD
{
AvHTeamNumber SquadTeam = TEAM_IND; // Which team this squad is for
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
BotTaskType SquadObjective = TASK_NONE; // What to do with the objective
bool bExecuteObjective = false; // Are we at the gather or execute phase?
bool IsValid()
{
return (SquadMembers.size() > 0 && !FNullEnt(SquadTarget));
}
} AvHAISquad;
#endif

View file

@ -302,12 +302,12 @@ bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation)
return theSuccess;
}
void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot, float DrawTime)
void AIDEBUG_DrawBotPath(edict_t* OutputPlayer, AvHAIPlayer* pBot, float DrawTime)
{
AIDEBUG_DrawPath(pBot->BotNavInfo.CurrentPath, DrawTime);
AIDEBUG_DrawPath(OutputPlayer, pBot->BotNavInfo.CurrentPath, DrawTime);
}
void AIDEBUG_DrawPath(vector<bot_path_node>& path, float DrawTime)
void AIDEBUG_DrawPath(edict_t* OutputPlayer, vector<bot_path_node>& path, float DrawTime)
{
if (path.size() == 0) { return; }
@ -320,28 +320,28 @@ void AIDEBUG_DrawPath(vector<bot_path_node>& path, float DrawTime)
{
case SAMPLE_POLYFLAGS_WELD:
case SAMPLE_POLYFLAGS_DOOR:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 255, 0, 0);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime, 255, 0, 0);
break;
case SAMPLE_POLYFLAGS_JUMP:
case SAMPLE_POLYFLAGS_DUCKJUMP:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 255, 255, 0);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime, 255, 255, 0);
break;
case SAMPLE_POLYFLAGS_LADDER:
case SAMPLE_POLYFLAGS_LIFT:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 0, 0, 255);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime, 0, 0, 255);
break;
case SAMPLE_POLYFLAGS_WALLCLIMB:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 0, 128, 0);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime, 0, 128, 0);
break;
case SAMPLE_POLYFLAGS_BLOCKED:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 128, 128, 128);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime, 128, 128, 128);
break;
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 255, 128, 128);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime, 255, 128, 128);
break;
default:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime);
UTIL_DrawLine(OutputPlayer, FromLoc, ToLoc, DrawTime);
break;
}
}
@ -525,32 +525,29 @@ void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned
{
if (FNullEnt(pEntity)) { return; }
float FrameDelta = AIMGR_GetFrameDelta();
FrameDelta *= 256.0f;
float delta = AIMGR_GetFrameDelta();
short Duration = (short)roundf(FrameDelta);
// TODO: Be able to turn these off as a preference
hudtextparms_t theTextParms;
MESSAGE_BEGIN(MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_TEXTMESSAGE);
WRITE_BYTE(channel); // channel
WRITE_SHORT((int)(x * 8192.0f)); // x coordinates * 8192
WRITE_SHORT((int)(y * 8192.0f)); // y coordinates * 8192
WRITE_BYTE(0); // effect (fade in/out)
WRITE_BYTE(r); // initial RED
WRITE_BYTE(g); // initial GREEN
WRITE_BYTE(b); // initial BLUE
WRITE_BYTE(1); // initial ALPHA
WRITE_BYTE(r); // effect RED
WRITE_BYTE(g); // effect GREEN
WRITE_BYTE(b); // effect BLUE
WRITE_BYTE(1); // effect ALPHA
WRITE_SHORT(1); // fade-in time in seconds * 256
WRITE_SHORT(Duration); // fade-out time in seconds * 256
WRITE_SHORT(Duration); // hold time in seconds * 256
WRITE_STRING(string);//string); // send the string
MESSAGE_END(); // end
// Init text parms
theTextParms.x = x;
theTextParms.y = y;
theTextParms.effect = 0;
theTextParms.r1 = 240;
theTextParms.g1 = 240;
theTextParms.b1 = 240;
theTextParms.a1 = 128;
theTextParms.r2 = 240;
theTextParms.g2 = 240;
theTextParms.b2 = 240;
theTextParms.a2 = 128;
theTextParms.fadeinTime = .0f;
theTextParms.fadeoutTime = .0f;
theTextParms.holdTime = 0.2f;
theTextParms.channel = channel;
return;
UTIL_HudMessage(CBaseEntity::Instance(pEntity), theTextParms, string);
}
void UTIL_ClearLocalizations()
@ -682,6 +679,8 @@ char* UTIL_TaskTypeToChar(const BotTaskType TaskType)
return "Touch Trigger";
case TASK_WELD:
return "Weld Target";
case TASK_ATTACK_BASE:
return "Attack Enemy Base";
default:
return "None";
}

View file

@ -39,8 +39,8 @@ bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation);
AvHAIDeployableStructureType GetDeployableObjectTypeFromEdict(const edict_t* StructureEdict);
void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot, float DrawTime = 0.0f);
void AIDEBUG_DrawPath(vector<bot_path_node>& path, float DrawTime = 0.0f);
void AIDEBUG_DrawBotPath(edict_t* OutputPlayer, AvHAIPlayer* pBot, float DrawTime = 0.0f);
void AIDEBUG_DrawPath(edict_t* OutputPlayer, vector<bot_path_node>& path, float DrawTime = 0.0f);
// Draws a white line between start and end for the given player (pEntity) for 0.1s
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end);

View file

@ -21,6 +21,10 @@ extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles
extern cvar_t avh_botdebugmode;
#ifdef BOTDEBUG
extern edict_t* DebugBots[MAX_PLAYERS];
#endif
void BotJump(AvHAIPlayer* pBot)
{
if (pBot->BotNavInfo.IsOnGround)
@ -1861,7 +1865,8 @@ void EndBotFrame(AvHAIPlayer* pBot)
void CustomThink(AvHAIPlayer* pBot)
{
DEBUG_PrintCombatInfo(pBot);
// Test Combat Stuff
//DEBUG_PrintCombatInfo(INDEXENT(1), pBot);
pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot);
@ -1876,6 +1881,66 @@ void CustomThink(AvHAIPlayer* pBot)
AlienCombatThink(pBot);
}
}
else
{
if (AITAC_ShouldBotBeCautious(pBot))
{
MoveTo(pBot, AITAC_GetTeamStartingLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())), MOVESTYLE_AMBUSH);
}
else
{
MoveTo(pBot, AITAC_GetTeamStartingLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())), MOVESTYLE_NORMAL);
}
}
/*AITASK_BotUpdateAndClearTasks(pBot);
if (pBot->PrimaryBotTask.TaskType != TASK_SECURE_HIVE)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
AvHAIHiveDefinition* HiveToClear = nullptr;
float MinDist = 0.0f;
vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->Status == HIVE_STATUS_UNBUILT)
{
DeployableSearchFilter EnemyStuffFilter;
EnemyStuffFilter.DeployableTeam = EnemyTeam;
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStuffFilter.ReachabilityTeam = BotTeam;
EnemyStuffFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuffFilter))
{
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisHive->FloorLocation);
if (!HiveToClear || ThisDist < MinDist)
{
HiveToClear = ThisHive;
MinDist = ThisDist;
}
}
}
}
if (HiveToClear)
{
AITASK_SetSecureHiveTask(pBot, &pBot->PrimaryBotTask, HiveToClear->HiveEdict, HiveToClear->FloorLocation, true);
}
}
else
{
BotProgressTask(pBot, &pBot->PrimaryBotTask);
}*/
}
void DroneThink(AvHAIPlayer* pBot)
@ -2482,6 +2547,23 @@ AvHAICombatStrategy GetLerkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_stat
float EnemyHealthPercent = GetPlayerOverallHealthPercent(EnemyEdict);
int NumAllies = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, EnemyEdict->v.origin, UTIL_MetresToGoldSrcUnits(20.0f), EnemyEdict);
DeployableSearchFilter TurretFilter;
TurretFilter.DeployableTeam = EnemyTeam;
TurretFilter.DeployableTypes = (STRUCTURE_MARINE_TURRET | STRUCTURE_ALIEN_OFFENCECHAMBER);
TurretFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
TurretFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
TurretFilter.MaxSearchRadius = BALANCE_VAR(kTurretRange);
vector<AvHAIBuildableStructure> Turrets = AITAC_FindAllDeployables(EnemyEdict->v.origin, &TurretFilter);
for (auto it = Turrets.begin(); it != Turrets.end(); it++)
{
if (UTIL_QuickTrace(pBot->Edict, GetPlayerTopOfCollisionHull(it->edict), EnemyEdict->v.origin))
{
NumAllies++;
}
}
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, EnemyEdict->v.origin);
float RetreatHealthPercent = (NumAllies > 1) ? 0.5f : 0.35f;
@ -2775,7 +2857,7 @@ AvHAICombatStrategy GetMarineCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_st
case TASK_CAP_RESNODE:
case TASK_GUARD:
case TASK_SECURE_HIVE:
ThreatThreshold = 1.0f;
ThreatThreshold = (IsPlayerMarine(pBot->Edict)) ? 1.0f : 2.0f;
break;
default:
break;
@ -4370,17 +4452,9 @@ bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return true; }
// If we're already capping a node, are at the node and there is an unfinished tower on there, then finish the job and don't move on yet
if (Task->TaskType == TASK_CAP_RESNODE)
if (Task->TaskType == TASK_CAP_RESNODE && vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
{
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
if (ResNodeIndex && ResNodeIndex->OwningTeam == BotTeam)
{
if (!FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
{
return true;
}
}
return true;
}
}
@ -5524,17 +5598,22 @@ void AIPlayerDMThink(AvHAIPlayer* pBot)
void AIPlayerThink(AvHAIPlayer* pBot)
{
//#ifdef DEBUG
if (pBot == AIMGR_GetDebugAIPlayer())
#ifdef BOTDEBUG
for (int i = 0; i < gpGlobals->maxClients; i++)
{
bool bBreak = true; // Add a break point here if you want to debug a specific bot
edict_t* PlayerEdict = INDEXENT(i + 1);
AIDEBUG_DrawBotPath(pBot);
if (DebugBots[i] == pBot->Edict && !FNullEnt(PlayerEdict) && IsEdictPlayer(PlayerEdict) && IsPlayerHuman(PlayerEdict))
{
AvHAIPlayer* BotRef = AIMGR_GetBotRefFromEdict(DebugBots[i]);
DEBUG_PrintTaskInfo(pBot);
DEBUG_PrintCombatInfo(pBot);
if (BotRef)
{
DEBUG_PrintBotDebugInfo(PlayerEdict, BotRef);
}
}
}
//#endif
#endif
pBot->ThinkDelta = fminf(gpGlobals->time - pBot->LastThinkTime, 0.1f);
pBot->LastThinkTime = gpGlobals->time;
@ -6327,6 +6406,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
if (Task->TaskType == TASK_EVOLVE) { return; }
// CHECK TO SEE IF WE NEED TO EVOLVE INTO FADE/ONOS
if (!IsPlayerFade(pBot->Edict) && !IsPlayerOnos(pBot->Edict))
{
@ -6366,6 +6447,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
// CHECK TO SEE IF WE NEED TO HELP BREAK A SIEGE THAT IS ACTIVE (I.E. HAS SIEGE TURRETS)
const AvHAIHiveDefinition* NearestSiegedHive = AITAC_GetNearestHiveUnderActiveSiege(EnemyTeam, pBot->Edict->v.origin);
if (NearestSiegedHive)
@ -6434,7 +6517,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
}
// If we're up against marines, look out for any siege stuff
// CHECK IF MARINES ARE TRYING TO BUILD A SIEGE BASE
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
// Check if we're already trying to break a siege attempt, so we don't get torn between multiple potentials
@ -6513,11 +6597,11 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
}
// CHECK IF WE NEED TO HOLD AN EMPTY HIVE FOR US TO BUILD IN
Vector EnemyBaseLocation = AITAC_GetTeamStartingLocation(EnemyTeam);
AvHAIHiveDefinition* HiveToGuard = nullptr;
AvHAIHiveDefinition* HiveToSecure = nullptr;
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
@ -6551,84 +6635,72 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
if (!bShouldGuardEmptyHive) { continue; }
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->OwningTeam != TEAM_IND) { continue; }
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; }
bool bEnemyIsSecuring = AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuffFilter);
if (bEnemyIsSecuring)
if (bEnemyIsSecuring) { continue; }
DeployableSearchFilter FriendlyStuffFilter;
FriendlyStuffFilter.DeployableTeam = BotTeam;
FriendlyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
FriendlyStuffFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
// Don't guard a hive if some defences are already present
if (AITAC_GetNumDeployablesNearLocation(ThisHive->FloorLocation, &FriendlyStuffFilter) >= 2) { continue; }
// Only guard empty hives if a gorge is in there
if (AITAC_GetNumPlayersOfTeamAndClassInArea(BotTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(20.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2) == 0) { continue; }
bool bNeedsExtraGuards = true;
int NumGuards = 0;
vector<AvHPlayer*> HumanPlayers = AIMGR_GetNonAIPlayersOnTeam(BotTeam);
vector<AvHAIPlayer*> AITeamPlayers = AIMGR_GetAIPlayersOnTeam(BotTeam);
for (auto AIIt = AITeamPlayers.begin(); AIIt != AITeamPlayers.end(); AIIt++)
{
if ((*AIIt) == pBot) { continue; }
if ((*AIIt)->PrimaryBotTask.TaskType == TASK_GUARD && (*AIIt)->PrimaryBotTask.TaskTarget == ThisHive->HiveEdict)
{
if ((*AIIt)->Player->GetUser3() >= AVH_USER3_ALIEN_PLAYER3) { bNeedsExtraGuards = false; }
NumGuards++;
}
}
for (auto GuardIt = HumanPlayers.begin(); GuardIt != HumanPlayers.end(); GuardIt++)
{
AvHPlayer* ThisGuard = (*GuardIt);
if (IsPlayerActiveInGame(ThisGuard->edict()) && vDist2DSq(ThisGuard->edict()->v.origin, ThisHive->FloorLocation) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{
if (ThisGuard->GetUser3() >= AVH_USER3_ALIEN_PLAYER3) { bNeedsExtraGuards = false; }
NumGuards++;
}
}
bNeedsExtraGuards = bNeedsExtraGuards && NumGuards < 2;
if (bNeedsExtraGuards)
{
float ThisDist = vDist2DSq(ThisHive->FloorLocation, EnemyBaseLocation);
if (ThisDist > MaxSecureDist)
{
HiveToSecure = ThisHive;
MaxSecureDist = ThisDist;
HiveToGuard = ThisHive;
MaxGuardDist = ThisDist;
}
}
else
{
if (!bShouldGuardEmptyHive) { continue; }
DeployableSearchFilter FriendlyStuffFilter;
FriendlyStuffFilter.DeployableTeam = BotTeam;
FriendlyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
FriendlyStuffFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
// Don't guard a hive if some defences are already present
if (AITAC_GetNumDeployablesNearLocation(ThisHive->FloorLocation, &FriendlyStuffFilter) >= 2) { continue; }
// Only guard empty hives if a gorge is in there
if (AITAC_GetNumPlayersOfTeamAndClassInArea(BotTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(20.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2) == 0) { continue; }
bool bNeedsExtraGuards = true;
int NumGuards = 0;
vector<AvHPlayer*> HumanPlayers = AIMGR_GetNonAIPlayersOnTeam(BotTeam);
vector<AvHAIPlayer*> AITeamPlayers = AIMGR_GetAIPlayersOnTeam(BotTeam);
for (auto AIIt = AITeamPlayers.begin(); AIIt != AITeamPlayers.end(); AIIt++)
{
if ((*AIIt) == pBot) { continue; }
if ((*AIIt)->PrimaryBotTask.TaskType == TASK_GUARD && (*AIIt)->PrimaryBotTask.TaskTarget == ThisHive->HiveEdict)
{
if ((*AIIt)->Player->GetUser3() >= AVH_USER3_ALIEN_PLAYER3) { bNeedsExtraGuards = false; }
NumGuards++;
}
}
for (auto GuardIt = HumanPlayers.begin(); GuardIt != HumanPlayers.end(); GuardIt++)
{
AvHPlayer* ThisGuard = (*GuardIt);
if (IsPlayerActiveInGame(ThisGuard->edict()) && vDist2DSq(ThisGuard->edict()->v.origin, ThisHive->FloorLocation) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{
if (ThisGuard->GetUser3() >= AVH_USER3_ALIEN_PLAYER3) { bNeedsExtraGuards = false; }
NumGuards++;
}
}
bNeedsExtraGuards = bNeedsExtraGuards && NumGuards < 2;
if (bNeedsExtraGuards)
{
float ThisDist = vDist2DSq(ThisHive->FloorLocation, EnemyBaseLocation);
if (ThisDist > MaxSecureDist)
{
HiveToGuard = ThisHive;
MaxGuardDist = ThisDist;
}
}
else
{
// The purpose of this is to ensure we only guard one empty hive at a time, otherwise all the assault bots will be sitting around in empty hives and not pressuring marines
// If we have an empty hive already being guarded, then this bool will ensure the bot doesn't go guard an empty hive even if there are 2
bShouldGuardEmptyHive = false;
}
// The purpose of this is to ensure we only guard one empty hive at a time, otherwise all the assault bots will be sitting around in empty hives and not pressuring marines
// If we have an empty hive already being guarded, then this bool will ensure the bot doesn't go guard an empty hive even if there are 2
bShouldGuardEmptyHive = false;
}
}
@ -6641,70 +6713,61 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
Task->TaskLength = 60.0f;
return;
}
else if (HiveToSecure)
// FIND A HIVE TO RETAKE. PICK THE WEAKEST ONE
int MaxHiveStrength = 0;
AvHAIHiveDefinition* HiveToSecure = nullptr;
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
// Check if we're already trying to clear out a hive
if (Task->TaskType == TASK_ATTACK)
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
if (ThisHive->OwningTeam != EnemyTeam && EnemyStructures.size() == 0) { continue; }
int ThisStrength = 0;
for (auto StructureIt = EnemyStructures.begin(); StructureIt != EnemyStructures.end(); StructureIt++)
{
const AvHAIHiveDefinition* HiveNearestAttackTarget = AITAC_GetNearestTeamHive(BotTeam, Task->TaskTarget->v.origin, false);
if (HiveNearestAttackTarget && vDist2DSq(HiveNearestAttackTarget->Location, Task->TaskTarget->v.origin) <= sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { return; }
}
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
// Don't attack electrified structures as skulk
if (pBot->Player->GetUser3() < AVH_USER3_ALIEN_PLAYER4)
{
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_ELECTRIFIED;
}
vector<AvHAIBuildableStructure> AllEnemyThings = AITAC_FindAllDeployables(HiveToSecure->FloorLocation, &EnemyStuffFilter);
AvHAIBuildableStructure StructureToAttack;
for (auto it = AllEnemyThings.begin(); it != AllEnemyThings.end(); it++)
{
AvHAIBuildableStructure ThisStructure = (*it);
// First prioritise phase gates or alien OCs
if (ThisStructure.StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure.StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
switch (StructureIt->StructureType)
{
if (FNullEnt(StructureToAttack.edict) || StructureToAttack.StructureType != ThisStructure.StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure.Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack.Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (!FNullEnt(StructureToAttack.edict) && (StructureToAttack.StructureType == STRUCTURE_MARINE_PHASEGATE || ThisStructure.StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)) { continue; }
// Then prioritise turret factories
if (ThisStructure.StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure.StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)
{
if (FNullEnt(StructureToAttack.edict) || StructureToAttack.StructureType != ThisStructure.StructureType || vDist2DSq(pBot->Edict->v.origin, ThisStructure.Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack.Location))
{
StructureToAttack = ThisStructure;
continue;
}
}
if (!FNullEnt(StructureToAttack.edict) && (StructureToAttack.StructureType == STRUCTURE_MARINE_TURRETFACTORY || ThisStructure.StructureType == STRUCTURE_MARINE_ADVTURRETFACTORY)) { continue; }
// Then target any other structures
if (FNullEnt(StructureToAttack.edict) || vDist2DSq(pBot->Edict->v.origin, ThisStructure.Location) < vDist2DSq(pBot->Edict->v.origin, StructureToAttack.Location))
{
StructureToAttack = ThisStructure;
case STRUCTURE_MARINE_PHASEGATE:
ThisStrength += 2;
break;
case STRUCTURE_MARINE_TURRETFACTORY:
ThisStrength += (UTIL_IsStructureElectrified(StructureIt->edict)) ? 2 : 1;
break;
case STRUCTURE_MARINE_TURRET:
ThisStrength += 1;
break;
case STRUCTURE_ALIEN_OFFENCECHAMBER:
ThisStrength += 1;
break;
default:
break;
}
}
if (StructureToAttack.IsValid())
if (!HiveToSecure || ThisStrength < MaxHiveStrength)
{
AITASK_SetAttackTask(pBot, Task, StructureToAttack.edict, false);
return;
HiveToSecure = ThisHive;
MaxHiveStrength = ThisStrength;
}
}
if (HiveToSecure)
{
AITASK_SetSecureHiveTask(pBot, Task, HiveToSecure->HiveEdict, HiveToSecure->FloorLocation, false);
return;
}
// ATTACK THE ENEMY BASE
DeployableSearchFilter EnemyInfPortalFilter;
EnemyInfPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
EnemyInfPortalFilter.DeployableTeam = EnemyTeam;
@ -6730,6 +6793,8 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
return;
}
// FIND ANY LAST ENEMIES TO KILL AND END GAME
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
edict_t* TargetPlayer = nullptr;
@ -8468,7 +8533,7 @@ bool OnosCombatThink(AvHAIPlayer* pBot)
return true;
}
void DEBUG_PrintTaskInfo(AvHAIPlayer* pBot)
void DEBUG_PrintTaskInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot)
{
char buf[511];
char interbuf[164];
@ -8516,19 +8581,19 @@ void DEBUG_PrintTaskInfo(AvHAIPlayer* pBot)
if (!FNullEnt(pBot->CurrentTask->TaskTarget))
{
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CurrentTask->TaskTarget->v.origin, 255, 0, 0);
UTIL_DrawLine(OutputPlayer, pBot->Edict->v.origin, pBot->CurrentTask->TaskTarget->v.origin, 255, 0, 0);
}
if (!vIsZero(pBot->CurrentTask->TaskLocation))
{
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CurrentTask->TaskLocation, 255, 255, 0);
UTIL_DrawLine(OutputPlayer, pBot->Edict->v.origin, pBot->CurrentTask->TaskLocation, 255, 255, 0);
}
}
UTIL_DrawHUDText(INDEXENT(1), 0, 0.1f, 0.1f, 255, 255, 255, buf);
UTIL_DrawHUDText(OutputPlayer, 0, 0.1, 0.1f, 255, 255, 255, buf);
}
void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot)
void DEBUG_PrintCombatInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot)
{
char buf[511];
char interbuf[164];
@ -8550,7 +8615,7 @@ void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot)
if (TrackedEnemy < 0)
{
UTIL_DrawHUDText(INDEXENT(1), 1, 0.6f, 0.1f, 255, 255, 255, buf);
UTIL_DrawHUDText(OutputPlayer, 1, 0.6f, 0.1f, 255, 255, 255, buf);
return;
}
@ -8606,11 +8671,23 @@ void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot)
strcat(buf, interbuf);
UTIL_DrawHUDText(INDEXENT(1), 1, 0.6f, 0.1f, 255, 255, 255, buf);
UTIL_DrawHUDText(OutputPlayer, 1, 0.6f, 0.1f, 255, 255, 255, buf);
if (!vIsZero(TrackedInfo->LastDetectedLocation))
{
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, TrackedInfo->LastDetectedLocation, 255, 0, 0);
UTIL_DrawLine(OutputPlayer, pBot->Edict->v.origin, TrackedInfo->LastDetectedLocation, 255, 0, 0);
}
}
void DEBUG_PrintBotDebugInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot)
{
if (FNullEnt(OutputPlayer) || OutputPlayer->free) { return; }
bool bBreak = true; // Add a break point here if you want to debug a specific bot
AIDEBUG_DrawBotPath(OutputPlayer, pBot);
DEBUG_PrintTaskInfo(OutputPlayer, pBot);
DEBUG_PrintCombatInfo(OutputPlayer, pBot);
}

View file

@ -184,7 +184,8 @@ bool OnosCombatThink(AvHAIPlayer* pBot);
bool BombardierCombatThink(AvHAIPlayer* pBot);
bool RegularMarineCombatThink(AvHAIPlayer* pBot);
void DEBUG_PrintTaskInfo(AvHAIPlayer* pBot);
void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot);
void DEBUG_PrintBotDebugInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot);
void DEBUG_PrintTaskInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot);
void DEBUG_PrintCombatInfo(edict_t* OutputPlayer, AvHAIPlayer* pBot);
#endif

View file

@ -54,6 +54,10 @@ bool bBotsEnabled = false;
float CurrentFrameDelta = 0.01f;
#ifdef BOTDEBUG
edict_t* DebugBots[MAX_PLAYERS];
#endif
AvHAICommanderMode AIMGR_GetCommanderMode()
{
if (avh_botcommandermode.value == 1)
@ -599,6 +603,7 @@ void AIMGR_UpdateAIPlayers()
}
AIMGR_ProcessPendingSounds();
AITAC_UpdateSquads();
}
int NumCommanders = AIMGR_GetNumAICommanders();
@ -889,7 +894,11 @@ void AIMGR_ResetRound()
{
if (!AIMGR_IsBotEnabled()) { return; } // Do nothing if we're not using bots, as the data will be cleared out via AIMGR_OnBotDisabled()
AITAC_ClearMapAIData(false);
AITAC_ClearMapAIData(false);
#ifdef BOTDEBUG
memset(DebugBots, 0, sizeof(DebugBots));
#endif
// AI Players would be 0 if the round is being reset because a new game is starting. If the round is reset
// from a console command, or tournament mode readying up etc, then bot logic is unaffected
@ -1084,6 +1093,16 @@ AvHAIPlayer* AIMGR_GetBotRefFromPlayer(AvHPlayer* PlayerRef)
return nullptr;
}
AvHAIPlayer* AIMGR_GetBotRefFromEdict(edict_t* PlayerEdict)
{
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end(); BotIt++)
{
if (BotIt->Edict == PlayerEdict) { return &(*BotIt); }
}
return nullptr;
}
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam)
{
AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber();
@ -1259,11 +1278,14 @@ AvHAIPlayer* AIMGR_GetDebugAIPlayer()
return DebugAIPlayer;
}
void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer)
void AIMGR_SetDebugAIPlayer(edict_t* SpectatingPlayer, edict_t* AIPlayer)
{
#ifdef BOTDEBUG
int PlayerIndex = ENTINDEX(SpectatingPlayer) - 1;
if (FNullEnt(AIPlayer))
{
DebugAIPlayer = nullptr;
DebugBots[PlayerIndex] = nullptr;
return;
}
@ -1271,12 +1293,13 @@ void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer)
{
if (it->Edict == AIPlayer)
{
DebugAIPlayer = &(*it);
DebugBots[PlayerIndex] = it->Edict;
return;
}
}
DebugAIPlayer = nullptr;
DebugBots[PlayerIndex] = nullptr;
#endif
}
void AIMGR_ReceiveCommanderRequest(AvHTeamNumber Team, edict_t* Requestor, const char* Request)

View file

@ -86,6 +86,7 @@ void AIMGR_ReloadNavigationData();
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);
AvHAIPlayer* AIMGR_GetBotRefFromPlayer(AvHPlayer* PlayerRef);
AvHAIPlayer* AIMGR_GetBotRefFromEdict(edict_t* PlayerEdict);
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam);
@ -108,7 +109,7 @@ vector<AvHPlayer*> AIMGR_GetNonAIPlayersOnTeam(AvHTeamNumber Team);
void AIMGR_ClearBotData();
AvHAIPlayer* AIMGR_GetDebugAIPlayer();
void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer);
void AIMGR_SetDebugAIPlayer(edict_t* SpectatingPlayer, edict_t* AIPlayer);
void AIMGR_ReceiveCommanderRequest(AvHTeamNumber Team, edict_t* Requestor, const char* Request);

View file

@ -78,6 +78,8 @@ bool IsPlayerInReadyRoom(const edict_t* Player)
bool IsPlayerActiveInGame(const edict_t* Player)
{
if (FNullEnt(Player)) { return false; }
return !IsPlayerInReadyRoom(Player) && Player->v.team != 0 && !IsPlayerSpectator(Player) && !IsPlayerDead(Player) && !IsPlayerBeingDigested(Player) && !IsPlayerCommander(Player);
}

View file

@ -66,6 +66,8 @@ edict_t* LastSeenLerkTeamB = nullptr; // Track who went lerk on team B last time
float LastSeenLerkTeamATime = 0.0f;
float LastSeenLerkTeamBTime = 0.0f;
vector<AvHAISquad> ActiveSquads;
std::vector<AvHAIBuildableStructure> AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter)
{
std::vector<AvHAIBuildableStructure> Result;
@ -2562,7 +2564,7 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure)
{
NavHint* ThisHint = (*it);
if (vDist2DSq(NewStructure->edict->v.origin, ThisHint->Position) < sqrf(32.0f) && fabsf(NewStructure->Location.z - ThisHint->Position.z) < 50.0f)
if (vDist2DSq(NewStructure->edict->v.origin, ThisHint->Position) < sqrf(64.0f) && fabsf(NewStructure->Location.z - ThisHint->Position.z) < 50.0f)
{
ThisHint->OccupyingBuilding = NewStructure->edict;
}
@ -2802,6 +2804,8 @@ void AITAC_ClearMapAIData(bool bInitialMapLoad)
Hives.clear();
}
AITAC_ClearSquads();
MarineDroppedItemMap.clear();
TeamAStructureMap.clear();
TeamBStructureMap.clear();
@ -4266,6 +4270,27 @@ bool AITAC_ShouldBotBeCautious(AvHAIPlayer* pBot)
int NumEnemiesAtDestination = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, CurrentPathNode.Location, UTIL_MetresToGoldSrcUnits(50.0f), pBot->Edict);
if (NumEnemiesAtDestination <= 1)
{
DeployableSearchFilter TurretFilter;
TurretFilter.DeployableTeam = EnemyTeam;
TurretFilter.DeployableTypes = (STRUCTURE_MARINE_TURRET | STRUCTURE_ALIEN_OFFENCECHAMBER);
TurretFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
TurretFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
TurretFilter.MaxSearchRadius = BALANCE_VAR(kTurretRange);
vector<AvHAIBuildableStructure> Turrets = AITAC_FindAllDeployables(CurrentPathNode.Location, &TurretFilter);
for (auto it = Turrets.begin(); it != Turrets.end(); it++)
{
if (UTIL_QuickTrace(pBot->Edict, GetPlayerTopOfCollisionHull(it->edict), CurrentPathNode.Location))
{
NumEnemiesAtDestination++;
}
}
}
if (NumEnemiesAtDestination > 1)
{
return (vDist2DSq(pBot->Edict->v.origin, CurrentPathNode.Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)));
@ -5403,4 +5428,200 @@ Vector AITAC_GetRandomBuildHintInLocation(const unsigned int StructureType, cons
}
return Result;
}
bool AITAC_IsBotPursuingSquadObjective(AvHAIPlayer* pBot, AvHAISquad* Squad)
{
// Bot is dead, no longer playing, or otherwise incapacitated
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; }
// Bot is focused on the job at hand
if (!pBot->CurrentTask || pBot->CurrentTask == &pBot->PrimaryBotTask) { return true; }
// Bot isn't currently pursuing squad objective, so check if it's doing something in the vicinity
Vector TaskLocation = (!FNullEnt(pBot->CurrentTask->TaskTarget)) ? pBot->CurrentTask->TaskTarget->v.origin : pBot->CurrentTask->TaskLocation;
return vDist2DSq(TaskLocation, Squad->SquadTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f));
}
void AITAC_ManageSquads()
{
for (auto it = ActiveSquads.begin(); it != ActiveSquads.end();)
{
for (auto pIt = it->SquadMembers.begin(); pIt != it->SquadMembers.end();)
{
AvHAIPlayer* ThisPlayer = (*pIt);
if (!AITAC_IsBotPursuingSquadObjective(ThisPlayer, &(*it)))
{
pIt = it->SquadMembers.erase(pIt);
}
else
{
pIt++;
}
}
if (it->SquadMembers.size() == 0)
{
it = ActiveSquads.erase(it);
}
else
{
it++;
}
}
}
void AITAC_UpdateSquads()
{
AITAC_ManageSquads();
for (auto it = ActiveSquads.begin(); it != ActiveSquads.end(); it++)
{
if (it->SquadMembers.size() > 1)
{
if (vIsZero(it->SquadGatherLocation))
{
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));
if (dtStatusSucceed(PathFindResult))
{
for (auto pIt = TravelPath.rbegin(); pIt != TravelPath.rend(); pIt++)
{
if (pIt->area != SAMPLE_POLYAREA_GROUND || pIt->flag != SAMPLE_POLYFLAGS_WALK) { continue; }
if (UTIL_QuickTrace(nullptr, pIt->Location, it->SquadTarget->v.origin)) { continue; }
if (vDist2DSq(pIt->Location, it->SquadTarget->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f)))
{
DeployableSearchFilter EnemyStuff;
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(it->SquadTeam);
EnemyStuff.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
if (AITAC_DeployableExistsAtLocation(pIt->Location, &EnemyStuff))
{
continue;
}
it->SquadGatherLocation = pIt->Location;
break;
}
}
}
}
}
else
{
it->SquadGatherLocation = ZERO_VECTOR;
}
if (!it->bExecuteObjective && !vIsZero(it->SquadGatherLocation))
{
bool bAllHaveGathered = true;
for (auto playerIt = it->SquadMembers.begin(); playerIt != it->SquadMembers.end(); playerIt++)
{
AvHAIPlayer* ThisBot = (*playerIt);
if (vDist2DSq(ThisBot->Edict->v.origin, it->SquadGatherLocation) > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
{
bAllHaveGathered = false;
}
}
if (bAllHaveGathered)
{
it->bExecuteObjective = true;
}
}
}
}
AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, edict_t* TaskTarget, BotTaskType ObjectiveType)
{
AvHAISquad* JoinSquad = nullptr;
for (auto it = ActiveSquads.begin(); it != ActiveSquads.end(); it++)
{
if (it->SquadTeam == pBot->Player->GetTeam() && it->SquadTarget == TaskTarget && 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.SquadTarget = TaskTarget;
NewSquad.SquadObjective = ObjectiveType;
NewSquad.bExecuteObjective = false;
NewSquad.SquadGatherLocation = ZERO_VECTOR;
ActiveSquads.push_back(NewSquad);
return nullptr;
}
void AITAC_ClearSquads()
{
ActiveSquads.clear();
}
Vector AITAC_GetGatherLocationForSquad(AvHAISquad* Squad)
{
if (!Squad || FNullEnt(Squad->SquadTarget)) { return ZERO_VECTOR; }
vector<bot_path_node> TravelPath;
dtStatus PathFindResult = FindPathClosestToPoint(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), AITAC_GetTeamStartingLocation(Squad->SquadTeam), UTIL_GetEntityGroundLocation(Squad->SquadTarget), TravelPath, UTIL_MetresToGoldSrcUnits(20.0f));
if (dtStatusSucceed(PathFindResult))
{
for (auto pIt = TravelPath.rend(); pIt != TravelPath.rbegin(); pIt++)
{
if (pIt->area != SAMPLE_POLYAREA_GROUND || pIt->flag != SAMPLE_POLYFLAGS_WALK) { continue; }
if (UTIL_QuickTrace(nullptr, pIt->Location, Squad->SquadTarget->v.origin)) { continue; }
if (vDist2DSq(pIt->Location, Squad->SquadTarget->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f)))
{
DeployableSearchFilter EnemyStuff;
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(Squad->SquadTeam);
EnemyStuff.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
if (AITAC_DeployableExistsAtLocation(pIt->Location, &EnemyStuff))
{
continue;
}
return pIt->Location;
}
}
}
return ZERO_VECTOR;
}

View file

@ -204,4 +204,10 @@ edict_t* AITAC_GetLastSeenLerkForTeam(AvHTeamNumber Team, float& LastSeenTime);
bool AITAC_IsCompletedStructureOfTypeNearLocation(AvHTeamNumber Team, unsigned int StructureType, Vector SearchLocation, float SearchRadius);
bool AITAC_IsStructureOfTypeNearLocation(AvHTeamNumber Team, unsigned int StructureType, Vector SearchLocation, float SearchRadius);
void AITAC_UpdateSquads();
void AITAC_ManageSquads();
void AITAC_ClearSquads();
AvHAISquad* AITAC_GetSquadForObjective(AvHAIPlayer* pBot, edict_t* TaskTarget, BotTaskType ObjectiveType);
Vector AITAC_GetGatherLocationForSquad(AvHAISquad* Squad);
#endif

View file

@ -392,9 +392,11 @@ bool AITASK_IsTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
else
{
return false;
return AITASK_IsAlienSecureHiveTaskStillValid(pBot, Task);
}
}
case TASK_ATTACK_BASE:
return true;
case TASK_DEFEND:
return AITASK_IsDefendTaskStillValid(pBot, Task);
case TASK_WELD:
@ -937,6 +939,28 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
return AITAC_DeployableExistsAtLocation(Task->TaskTarget->v.origin, &EnemyStuff);
}
bool AITASK_IsAlienSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (!Task || FNullEnt(Task->TaskTarget) || !IsPlayerAlien(pBot->Edict)) { return false; }
AvHAIHiveDefinition* HiveToSecure = AITAC_GetHiveFromEdict(Task->TaskTarget);
if (!HiveToSecure) { return false; }
if (HiveToSecure->Status != HIVE_STATUS_UNBUILT && HiveToSecure->OwningTeam != pBot->Player->GetTeam()) { return true; }
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
DeployableSearchFilter EnemyStuff;
EnemyStuff.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
EnemyStuff.ReachabilityTeam = BotTeam;
EnemyStuff.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
return AITAC_DeployableExistsAtLocation(HiveToSecure->FloorLocation, &EnemyStuff);
}
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (!Task || FNullEnt(Task->TaskTarget) || IsPlayerAlien(pBot->Edict)) { return false; }
@ -2530,6 +2554,10 @@ void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
MarineProgressSecureHiveTask(pBot, Task);
}
else
{
AlienProgressSecureHiveTask(pBot, Task);
}
}
break;
default:
@ -2617,6 +2645,121 @@ void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
return;
}
void AlienProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
const AvHAIHiveDefinition* Hive = AITAC_GetHiveFromEdict(Task->TaskTarget);
if (!Hive) { return; }
AvHAISquad* ActiveSquad = AITAC_GetSquadForObjective(pBot, Task->TaskTarget, Task->TaskType);
if (ActiveSquad && !ActiveSquad->bExecuteObjective && !vIsZero(ActiveSquad->SquadGatherLocation))
{
BotGuardLocation(pBot, ActiveSquad->SquadGatherLocation);
return;
}
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
bool bEnemyIsMarines = AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE;
// Don't prioritise electrified structures if we're skulk or lerk
bool bAvoidElectrified = (pBot->Player->GetUser3() == AVH_USER3_ALIEN_PLAYER1 || pBot->Player->GetUser3() == AVH_USER3_ALIEN_PLAYER3);
DeployableSearchFilter EnemyStuffFilter;
EnemyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
EnemyStuffFilter.DeployableTeam = EnemyTeam;
EnemyStuffFilter.ReachabilityTeam = BotTeam;
EnemyStuffFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
if (bEnemyIsMarines)
{
EnemyStuffFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
AvHAIBuildableStructure PhaseGate = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &EnemyStuffFilter);
if (PhaseGate.IsValid())
{
if (bAvoidElectrified)
{
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStuffFilter.IncludeStatusFlags = STRUCTURE_STATUS_ELECTRIFIED;
EnemyStuffFilter.MaxSearchRadius = BALANCE_VAR(kElectricalRange);
AvHAIBuildableStructure ElectrifiedStructure = AITAC_FindClosestDeployableToLocation(PhaseGate.Location, &EnemyStuffFilter);
if (ElectrifiedStructure.IsValid())
{
BotAlienAttackNonPlayerTarget(pBot, ElectrifiedStructure.edict);
return;
}
}
BotAlienAttackNonPlayerTarget(pBot, PhaseGate.edict);
return;
}
EnemyStuffFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
EnemyStuffFilter.IncludeStatusFlags = STRUCTURE_STATUS_NONE;
EnemyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
AvHAIBuildableStructure TF = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &EnemyStuffFilter);
if (TF.IsValid())
{
BotAlienAttackNonPlayerTarget(pBot, TF.edict);
return;
}
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
AvHAIBuildableStructure EnemyStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &EnemyStuffFilter);
if (EnemyStructure.IsValid())
{
BotAlienAttackNonPlayerTarget(pBot, EnemyStructure.edict);
return;
}
}
else
{
EnemyStuffFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
AvHAIBuildableStructure EnemyOC = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &EnemyStuffFilter);
if (EnemyOC.IsValid())
{
BotAlienAttackNonPlayerTarget(pBot, EnemyOC.edict);
return;
}
if (Hive->Status == HIVE_STATUS_BUILT && Hive->OwningTeam != BotTeam)
{
BotAlienAttackNonPlayerTarget(pBot, Hive->HiveEdict);
return;
}
EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
AvHAIBuildableStructure AnyEnemyStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &EnemyStuffFilter);
if (AnyEnemyStructure.IsValid())
{
BotAlienAttackNonPlayerTarget(pBot, AnyEnemyStructure.edict);
return;
}
if (Hive->Status != HIVE_STATUS_UNBUILT && Hive->OwningTeam != BotTeam)
{
BotAlienAttackNonPlayerTarget(pBot, Hive->HiveEdict);
return;
}
}
BotGuardLocation(pBot, Hive->FloorLocation);
}
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
const AvHAIHiveDefinition* Hive = AITAC_GetHiveFromEdict(Task->TaskTarget);
@ -2624,6 +2767,7 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (!Hive) { return; }
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = SEARCH_ALL_MARINE_STRUCTURES;
@ -2703,7 +2847,7 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
DeployableSearchFilter EnemyStructures;
EnemyStructures.DeployableTypes = SEARCH_ALL_STRUCTURES;
EnemyStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
EnemyStructures.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
EnemyStructures.DeployableTeam = EnemyTeam;
EnemyStructures.ReachabilityTeam = BotTeam;
EnemyStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
@ -2927,7 +3071,6 @@ void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocat
vector<bot_path_node> path;
path.clear();
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();

View file

@ -45,6 +45,7 @@ bool AITASK_IsEvolveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienGetHealthTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienHealTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
@ -93,6 +94,9 @@ void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget);
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
// Clear all enemy structures
void AlienProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressHealTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);

View file

@ -1416,21 +1416,23 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
}
#ifdef BOTDEBUG
else if (FStrEq(pcmd, "bot_setdebugaiplayer"))
{
CBaseEntity* SpectatedPlayer = theAvHPlayer->GetSpectatingEntity();
if (SpectatedPlayer)
{
AIMGR_SetDebugAIPlayer(SpectatedPlayer->edict());
AIMGR_SetDebugAIPlayer(theAvHPlayer->edict(), SpectatedPlayer->edict());
}
else
{
AIMGR_SetDebugAIPlayer(nullptr);
AIMGR_SetDebugAIPlayer(theAvHPlayer->edict(), nullptr);
}
theSuccess = true;
}
#endif
else if (FStrEq(pcmd, "bot_cometome"))
{
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAllAIPlayers();