From 3add6092d933c4a780cf961684107e3d893e0692 Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Wed, 21 Feb 2024 17:21:23 +0000 Subject: [PATCH] Bots attack turrets --- main/source/mod/AIPlayers/AvHAIConstants.h | 3 + main/source/mod/AIPlayers/AvHAIHelper.cpp | 2 +- main/source/mod/AIPlayers/AvHAIPlayer.cpp | 341 +++++++++++++++--- .../mod/AIPlayers/AvHAIPlayerManager.cpp | 48 ++- .../source/mod/AIPlayers/AvHAIPlayerManager.h | 3 + main/source/mod/AIPlayers/AvHAITactical.cpp | 11 + main/source/mod/AIPlayers/AvHAITask.cpp | 39 ++ .../mod/AIPlayers/AvHAIWeaponHelper.cpp | 2 +- main/source/mod/AvHConsoleCommands.cpp | 4 + main/source/mod/AvHGamerules.cpp | 21 ++ 10 files changed, 417 insertions(+), 57 deletions(-) diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 70060b50..0a2b7bb0 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -104,6 +104,7 @@ typedef enum STRUCTURE_STATUS_UNDERATTACK = 1 << 4, STRUCTURE_STATUS_RESEARCHING = 1 << 5, STRUCTURE_STATUS_DAMAGED = 1 << 6, + STRUCTURE_STATUS_DISABLED = 1 << 7, // For marine turrets when there's no TF STRUCTURE_STATUS_ALL = -1 } AvHAIStructureStatus; @@ -715,6 +716,8 @@ typedef struct AVH_AI_PLAYER AvHAICombatStrategy CurrentCombatStrategy = COMBAT_STRATEGY_ATTACK; edict_t* CurrentEnemyRef = nullptr; + vector DangerTurrets; + AvHAIPlayerTask* CurrentTask = nullptr; // Bot's current task they're performing AvHAIPlayerTask PrimaryBotTask; AvHAIPlayerTask SecondaryBotTask; diff --git a/main/source/mod/AIPlayers/AvHAIHelper.cpp b/main/source/mod/AIPlayers/AvHAIHelper.cpp index be549534..4adf3eab 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIHelper.cpp @@ -243,7 +243,7 @@ bool IsDamagingStructure(AvHAIDeployableStructureType StructureType) switch (StructureType) { case STRUCTURE_ALIEN_OFFENCECHAMBER: - case STRUCTURE_MARINE_TURRETFACTORY: + case STRUCTURE_MARINE_TURRET: return true; default: return false; diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index c7115d22..442f126e 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -14,6 +14,7 @@ #include "../AvHGamerules.h" #include "../AvHMessage.h" +#include "../AvHTurret.h" extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles @@ -1312,7 +1313,8 @@ void BotUpdateView(AvHAIPlayer* pBot) bool bHasLOSToAnyEnemy = false; - int EnemyTeam = 0; + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); pBot->ViewForwardVector = UTIL_GetForwardVector(pBot->Edict->v.v_angle); @@ -1475,7 +1477,41 @@ void BotUpdateView(AvHAIPlayer* pBot) } } - if (!bHasLOSToAnyEnemy) + pBot->DangerTurrets.clear(); + + bool bIsInRangeOfEnemyTurret = false; + + DeployableSearchFilter EnemyTurretFilter; + EnemyTurretFilter.DeployableTeam = EnemyTeam; + EnemyTurretFilter.ExcludeStatusFlags = (STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_DISABLED); + + if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN) + { + EnemyTurretFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER; + EnemyTurretFilter.MaxSearchRadius = 700.0f; // For some reason, offence chambers have a hard-coded range + } + else + { + EnemyTurretFilter.DeployableTypes = STRUCTURE_MARINE_TURRET; + EnemyTurretFilter.MaxSearchRadius = BALANCE_VAR(kTurretRange); + } + + vector EligibleTurrets = AITAC_FindAllDeployables(pBot->Edict->v.origin, &EnemyTurretFilter); + + for (auto it = EligibleTurrets.begin(); it != EligibleTurrets.end(); it++) + { + AvHAIBuildableStructure* ThisTurret = (*it); + AvHTurret* TurretRef = dynamic_cast(ThisTurret->EntityRef); + + if (TurretRef && TurretRef->GetIsValidTarget(pBot->Player)) + { + bIsInRangeOfEnemyTurret = true; + pBot->DangerTurrets.push_back(ThisTurret); + } + + } + + if (!bHasLOSToAnyEnemy && !bIsInRangeOfEnemyTurret) { pBot->LastSafeLocation = pBot->Edict->v.origin; } @@ -1792,13 +1828,6 @@ void DroneThink(AvHAIPlayer* pBot) BotProgressTask(pBot, &pBot->PrimaryBotTask); } - AIDEBUG_DrawBotPath(pBot); - - if (pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end()) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, 0, 255, 255); - } - } void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole) @@ -3347,6 +3376,53 @@ void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* } +void AIPlayerSetWantsAndNeedsCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) +{ + if (gpGlobals->time - pBot->LastCombatTime > 5.0f) + { + if (BotReloadWeapons(pBot)) { return; } + } + + if (Task->TaskType == TASK_RESUPPLY) { return; } + + bool bNeedsHealth = pBot->Edict->v.health < (pBot->Edict->v.max_health * 0.9f); + bool bNeedsAmmo = UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < (UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player) * 0.9f); + + if (bNeedsHealth || bNeedsAmmo) + { + bool bTaskIsUrgent = (pBot->Edict->v.health < (pBot->Edict->v.max_health * 0.7f)) || (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player)); + + float SearchRadius = (bTaskIsUrgent) ? UTIL_MetresToGoldSrcUnits(20.0f) : UTIL_MetresToGoldSrcUnits(5.0f); + + // If we're completely out of primary weapon ammo then always head back regardless of where we are in the map + if (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) == 0 && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) == 0) + { + SearchRadius = 0.0f; + } + + DeployableSearchFilter NearestArmouryFilter; + NearestArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY); + NearestArmouryFilter.DeployableTeam = pBot->Player->GetTeam(); + NearestArmouryFilter.ReachabilityTeam = pBot->Player->GetTeam(); + NearestArmouryFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + NearestArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + NearestArmouryFilter.MaxSearchRadius = SearchRadius; + + AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &NearestArmouryFilter); + + // We really need some health or ammo, hit the armoury + if (NearestArmoury) + { + Task->TaskType = TASK_RESUPPLY; + Task->bTaskIsUrgent = true; + Task->TaskLocation = NearestArmoury->Location; + Task->TaskTarget = NearestArmoury->edict; + return; + } + } + +} + void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { if (gpGlobals->time - pBot->LastCombatTime > 5.0f) @@ -3604,6 +3680,56 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) } AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + + if (pBot->DangerTurrets.size() > 0) + { + AvHAIBuildableStructure* NearestDangerTurret = nullptr; + float MinDist = 0.0f; + + for (auto it = pBot->DangerTurrets.begin(); it != pBot->DangerTurrets.end(); it++) + { + float ThisDist = vDist2DSq(pBot->Edict->v.origin, (*it)->Location); + + if (!NearestDangerTurret || ThisDist < MinDist) + { + NearestDangerTurret = (*it); + } + } + + if (NearestDangerTurret) + { + if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN) + { + AITASK_SetAttackTask(pBot, Task, NearestDangerTurret->edict, true); + return; + } + else + { + DeployableSearchFilter EnemyTFFilter; + EnemyTFFilter.DeployableTeam = EnemyTeam; + EnemyTFFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY); + EnemyTFFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; + EnemyTFFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + EnemyTFFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); + EnemyTFFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + EnemyTFFilter.ReachabilityTeam = BotTeam; + + AvHAIBuildableStructure* EnemyTF = AITAC_FindClosestDeployableToLocation(NearestDangerTurret->Location, &EnemyTFFilter); + + if (EnemyTF) + { + AITASK_SetAttackTask(pBot, Task, EnemyTF->edict, true); + return; + } + else + { + AITASK_SetAttackTask(pBot, Task, NearestDangerTurret->edict, true); + return; + } + } + } + } // Find any nearby unbuilt structures DeployableSearchFilter UnbuiltFilter; @@ -4243,6 +4369,7 @@ void AIPlayerCOMarineThink(AvHAIPlayer* pBot) AIPlayerSetPrimaryCOMarineTask(pBot, &pBot->PrimaryBotTask); AIPlayerSetSecondaryCOMarineTask(pBot, &pBot->SecondaryBotTask); + AIPlayerSetWantsAndNeedsCOMarineTask(pBot, &pBot->WantsAndNeedsTask); } pBot->CurrentTask = AIPlayerGetNextTask(pBot); @@ -4285,6 +4412,7 @@ void AIPlayerCOAlienThink(AvHAIPlayer* pBot) AIPlayerSetPrimaryCOAlienTask(pBot, &pBot->PrimaryBotTask); AIPlayerSetSecondaryCOAlienTask(pBot, &pBot->SecondaryBotTask); + AIPlayerSetWantsAndNeedsAlienTask(pBot, &pBot->WantsAndNeedsTask); } pBot->CurrentTask = AIPlayerGetNextTask(pBot); @@ -4621,7 +4749,7 @@ void AIPlayerSetSecondaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) float ThisDist = vDist2D(pBot->Edict->v.origin, TheHive->FloorLocation); - int NumExistingDefenders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, TheHive->FloorLocation, ThisDist - 10.0f, false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER); + int NumExistingDefenders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, TheHive->FloorLocation, ThisDist - 10.0f, false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2); if (NumExistingDefenders < MaxDefenders) { @@ -4630,30 +4758,54 @@ void AIPlayerSetSecondaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) } } - if (TheHive->HealthPercent < 1.0f && IsPlayerGorge(pBot->Edict)) + if (IsPlayerGorge(pBot->Edict)) { - Task->TaskType = TASK_HEAL; - Task->TaskTarget = TheHive->HiveEdict; - Task->bTaskIsUrgent = true; - return; + edict_t* TeamMateToHeal = nullptr; + + vector AllNearbyTeammates = AITAC_GetAllPlayersOfTeamInArea(BotTeam, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2); + + float MinDist = 0.0f; + + for (auto it = AllNearbyTeammates.begin(); it != AllNearbyTeammates.end(); it++) + { + edict_t* ThisPlayer = (*it)->edict(); + + if (!FNullEnt(ThisPlayer) && IsPlayerActiveInGame(ThisPlayer) && GetPlayerOverallHealthPercent(ThisPlayer) < 0.99f) + { + float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisPlayer->v.origin); + if (FNullEnt(TeamMateToHeal) || ThisDist < MinDist) + { + TeamMateToHeal = ThisPlayer; + } + } + } + + if (!FNullEnt(TeamMateToHeal)) + { + Task->TaskType = TASK_HEAL; + Task->TaskTarget = TeamMateToHeal; + Task->bTaskIsUrgent = true; + return; + } + + if (TheHive->HealthPercent < 1.0f) + { + Task->TaskType = TASK_HEAL; + Task->TaskTarget = TheHive->HiveEdict; + Task->bTaskIsUrgent = true; + return; + } } - if (TheHive->HealthPercent < 0.8f) + if (pBot->BotRole == BOT_ROLE_SWEEPER && !IsPlayerGorge(pBot->Edict) && TheHive->HealthPercent < 0.8f) { - if (IsPlayerGorge(pBot->Edict) || (pBot->BotRole == BOT_ROLE_SWEEPER && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_TWO))) + if (pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_TWO)) { if (!IsPlayerGorge(pBot->Edict)) { AITASK_SetEvolveTask(pBot, Task, TheHive->HiveEdict, ALIEN_LIFEFORM_TWO, true); return; } - else - { - Task->TaskType = TASK_HEAL; - Task->TaskTarget = TheHive->HiveEdict; - Task->bTaskIsUrgent = true; - return; - } } } @@ -4700,18 +4852,6 @@ void AIPlayerDMThink(AvHAIPlayer* pBot) void AIPlayerThink(AvHAIPlayer* pBot) { - if (pBot == AIMGR_GetDebugAIPlayer()) - { - bool bBreak = true; - - AIDEBUG_DrawBotPath(pBot); - - if (pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end()) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, 0, 255, 255); - } - } - switch (GetGameRules()->GetMapMode()) { case MAP_MODE_NS: @@ -4728,18 +4868,6 @@ void AIPlayerThink(AvHAIPlayer* pBot) void TestNavThink(AvHAIPlayer* pBot) { - if (pBot == AIMGR_GetDebugAIPlayer()) - { - bool bBreak = true; // Add a break point here if you want to debug a specific bot - - AIDEBUG_DrawBotPath(pBot); - - if (pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end()) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, 0, 255, 255); - } - } - AITASK_BotUpdateAndClearTasks(pBot); pBot->CurrentTask = &pBot->PrimaryBotTask; @@ -4807,13 +4935,22 @@ void TestNavThink(AvHAIPlayer* pBot) } else { - AvHAIResourceNode* RandomNode = AITAC_GetRandomResourceNode((AvHTeamNumber)pBot->Edict->v.team, pBot->BotNavInfo.NavProfile.ReachabilityFlag); + Vector RandomPoint = ZERO_VECTOR; - if (!RandomNode) { return; } + if (GetGameRules()->GetMapMode() == MAP_MODE_NS) + { + AvHAIResourceNode* RandomNode = AITAC_GetRandomResourceNode((AvHTeamNumber)pBot->Edict->v.team, pBot->BotNavInfo.NavProfile.ReachabilityFlag); - Vector RandomPoint = RandomNode->Location; + if (!RandomNode) { return; } - if (RandomPoint != ZERO_VECTOR && UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, RandomPoint, max_player_use_reach)) + Vector RandomPoint = RandomNode->Location; + } + else + { + RandomPoint = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(100.0f)); + } + + if (!vIsZero(RandomPoint) && UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, RandomPoint, max_player_use_reach)) { AITASK_SetMoveTask(pBot, &pBot->PrimaryBotTask, RandomPoint, true); } @@ -5970,6 +6107,60 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) AvHTeamNumber BotTeam = pBot->Player->GetTeam(); AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + if (pBot->DangerTurrets.size() > 0) + { + AvHAIBuildableStructure* NearestDangerTurret = nullptr; + float MinDist = 0.0f; + + for (auto it = pBot->DangerTurrets.begin(); it != pBot->DangerTurrets.end(); it++) + { + float ThisDist = vDist2DSq(pBot->Edict->v.origin, (*it)->Location); + + if (!NearestDangerTurret || ThisDist < MinDist) + { + NearestDangerTurret = (*it); + } + } + + if (NearestDangerTurret) + { + if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN) + { + AITASK_SetAttackTask(pBot, Task, NearestDangerTurret->edict, true); + return; + } + else + { + DeployableSearchFilter EnemyTFFilter; + EnemyTFFilter.DeployableTeam = EnemyTeam; + EnemyTFFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY); + EnemyTFFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; + EnemyTFFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + if (pBot->Player->GetUser3() == AVH_USER3_ALIEN_PLAYER1 || pBot->Player->GetUser3() == AVH_USER3_ALIEN_PLAYER3) + { + EnemyTFFilter.ExcludeStatusFlags |= STRUCTURE_STATUS_ELECTRIFIED; + } + + EnemyTFFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); + EnemyTFFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + EnemyTFFilter.ReachabilityTeam = BotTeam; + + AvHAIBuildableStructure* EnemyTF = AITAC_FindClosestDeployableToLocation(NearestDangerTurret->Location, &EnemyTFFilter); + + if (EnemyTF) + { + AITASK_SetAttackTask(pBot, Task, EnemyTF->edict, true); + return; + } + else + { + AITASK_SetAttackTask(pBot, Task, NearestDangerTurret->edict, true); + return; + } + } + } + } + if (IsPlayerGorge(pBot->Edict)) { edict_t* TeamMateToHeal = nullptr; @@ -6198,6 +6389,39 @@ void AIPlayerSetWantsAndNeedsAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { float CurrentHealth = GetPlayerOverallHealthPercent(pBot->Edict); + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + + if (PlayerHasWeapon(pBot->Player, WEAPON_LERK_PRIMALSCREAM) && !pBot->Player->GetIsScreaming()) + { + vector NearbyAllies = AITAC_GetAllPlayersOfTeamInArea(pBot->Player->GetTeam(), pBot->Edict->v.origin, BALANCE_VAR(kPrimalScreamRange), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2); + int NumBuffTargets = 0; + + for (auto it = NearbyAllies.begin(); it != NearbyAllies.end(); it++) + { + AvHPlayer* ThisPlayer = (*it); + edict_t* ThisPlayerEdict = ThisPlayer->edict(); + + if (AITAC_AnyPlayerOnTeamWithLOS(EnemyTeam, ThisPlayerEdict->v.origin, UTIL_MetresToGoldSrcUnits(10.0f))) + { + NumBuffTargets++; + } + } + + if (NumBuffTargets > 0) + { + pBot->DesiredCombatWeapon = WEAPON_LERK_PRIMALSCREAM; + + if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_LERK_PRIMALSCREAM) + { + pBot->Button |= IN_ATTACK; + } + + return; + } + + } + if (Task->TaskType == TASK_GET_HEALTH) { Task->bTaskIsUrgent = Task->bTaskIsUrgent || CurrentHealth < 0.4f; @@ -6814,6 +7038,7 @@ bool LerkCombatThink(AvHAIPlayer* pBot) enemy_status* TrackedEnemyRef = &pBot->TrackedEnemies[pBot->CurrentEnemy]; + if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_RETREAT) { edict_t* NearestHealingSource = AITAC_AlienFindNearestHealingSource(pBot->Player->GetTeam(), pBot->Edict->v.origin, pBot->Edict, true); @@ -6886,6 +7111,18 @@ bool LerkCombatThink(AvHAIPlayer* pBot) if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK) { + if (PlayerHasWeapon(pBot->Player, WEAPON_LERK_PRIMALSCREAM) && !pBot->Player->GetIsScreaming()) + { + pBot->DesiredCombatWeapon = WEAPON_LERK_SPORES; + + if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_LERK_PRIMALSCREAM) + { + pBot->Button |= IN_ATTACK; + } + + return true; + } + AvHAIWeapon DesiredWeapon = WEAPON_LERK_BITE; if (vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(5.0f))) diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index 17aaa3f2..633524ef 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -31,6 +31,7 @@ bool bHasRoundStarted = false; bool bMapDataInitialised = false; bool bTestNavigation = false; +bool bDroneMode = false; extern int m_spriteTexture; @@ -614,6 +615,18 @@ void AIMGR_UpdateAIPlayers() { BotDeltaTime = ThinkDelta; + if (bot == AIMGR_GetDebugAIPlayer()) + { + bool bBreak = true; // Add a break point here if you want to debug a specific bot + + AIDEBUG_DrawBotPath(bot); + + if (bot->BotNavInfo.CurrentPathPoint != bot->BotNavInfo.CurrentPath.end()) + { + UTIL_DrawLine(INDEXENT(1), bot->Edict->v.origin, bot->BotNavInfo.CurrentPathPoint->Location, 0, 255, 255); + } + } + if (bHasRoundStarted && ShouldBotThink(bot)) { if (bot->bIsInactive) @@ -625,15 +638,18 @@ void AIMGR_UpdateAIPlayers() UpdateBotChat(bot); - if (bTestNavigation) + if (bDroneMode) + { + DroneThink(bot); + } + else if (bTestNavigation) { TestNavThink(bot); } else { AIPlayerThink(bot); - } - + } EndBotFrame(bot); @@ -1053,6 +1069,12 @@ AvHAIPlayer* AIMGR_GetDebugAIPlayer() void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer) { + if (FNullEnt(AIPlayer)) + { + DebugAIPlayer = nullptr; + return; + } + for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++) { if (it->Edict == AIPlayer) @@ -1061,14 +1083,34 @@ void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer) return; } } + + DebugAIPlayer = nullptr; } void AIMGR_SetTestNavMode(bool bNewValue) { + if (bNewValue) + { + bDroneMode = false; + } bTestNavigation = bNewValue; } +void AIMGR_SetDroneMode(bool bNewValue) +{ + if (bNewValue) + { + bTestNavigation = false; + } + bDroneMode = bNewValue; +} + bool AIMGR_GetTestNavMode() +{ + return bTestNavigation; +} + +bool AIMGR_GetDroneMode() { return bTestNavigation; } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.h b/main/source/mod/AIPlayers/AvHAIPlayerManager.h index 51e4c88b..ea3d509c 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.h +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.h @@ -90,4 +90,7 @@ void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer); void AIMGR_SetTestNavMode(bool bNewValue); bool AIMGR_GetTestNavMode(); +void AIMGR_SetDroneMode(bool bNewValue); +bool AIMGR_GetDroneMode(); + #endif \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 1eac41c5..1a779772 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -23,6 +23,7 @@ #include "../AvHServerUtil.h" #include "../AvHSharedUtil.h" #include "../AvHMarineEquipment.h" +#include "../AvHTurret.h" #include @@ -2037,6 +2038,16 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) NewFlags |= STRUCTURE_STATUS_RESEARCHING; } + if (StructureType == STRUCTURE_MARINE_TURRET) + { + AvHTurret* TurretRef = dynamic_cast(BaseBuildable); + + if (TurretRef && !TurretRef->GetEnabledState()) + { + NewFlags |= STRUCTURE_STATUS_DISABLED; + } + } + float NewHealthPercent = (BuildingEdict->v.health / BuildingEdict->v.max_health); if (NewHealthPercent < BuildingMap[EntIndex].healthPercent) diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index 81605caf..6aae9c71 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -13,6 +13,7 @@ #include "../AvHAlienWeaponConstants.h" #include "../AvHGamerules.h" #include "../AvHWeldable.h" +#include "../AvHTurret.h" extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles @@ -1604,6 +1605,26 @@ void BotProgressAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) Weapon = BotAlienChooseBestWeaponForStructure(pBot, Task->TaskTarget); } + if (IsPlayerMarine(pBot->Player) && IsDamagingStructure(Task->TaskTarget) && !IsMeleeWeapon(Weapon)) + { + if (GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0 || IsPlayerReloading(pBot->Player)) + { + BotReloadWeapons(pBot); + + AvHTurret* TurretRef = dynamic_cast(CBaseEntity::Instance(Task->TaskTarget)); + + if (TurretRef && TurretRef->GetIsValidTarget(pBot->Player)) + { + if (vIsZero(pBot->LastSafeLocation)) + { + pBot->LastSafeLocation = AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()); + } + + MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL); + } + } + } + BotAttackResult AttackResult = PerformAttackLOSCheck(pBot, Weapon, Task->TaskTarget); if (AttackResult == ATTACK_SUCCESS) @@ -1616,6 +1637,24 @@ void BotProgressAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) BotShootTarget(pBot, Weapon, Task->TaskTarget); + if (IsDamagingStructure(Task->TaskTarget)) + { + Vector EnemyOrientation = UTIL_GetVectorNormal2D(Task->TaskTarget->v.origin - pBot->Edict->v.origin); + + Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR); + + pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(RightDir) : UTIL_GetVectorNormal2D(-RightDir); + + // Let's get ziggy with it + if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + { + pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; + pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); + } + + BotMovementInputs(pBot); + } + return; } diff --git a/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp b/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp index c5ffda94..bbc8e3fe 100644 --- a/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp @@ -742,7 +742,7 @@ AvHAIWeapon BotMarineChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* ta { AvHAIDeployableStructureType StructureType = GetStructureTypeFromEdict(target); - if (StructureType == STRUCTURE_NONE || StructureType == STRUCTURE_ALIEN_HIVE || IsDamagingStructure(StructureType)) + if (StructureType == STRUCTURE_NONE || StructureType == STRUCTURE_ALIEN_HIVE || IsDamagingStructure(StructureType) || UTIL_IsStructureElectrified(target)) { if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0) { diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index cc604f48..00ad3630 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -1505,6 +1505,10 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) { AIMGR_SetDebugAIPlayer(SpectatedPlayer->edict()); } + else + { + AIMGR_SetDebugAIPlayer(nullptr); + } theSuccess = true; } diff --git a/main/source/mod/AvHGamerules.cpp b/main/source/mod/AvHGamerules.cpp index 50fd0b79..73ff457d 100644 --- a/main/source/mod/AvHGamerules.cpp +++ b/main/source/mod/AvHGamerules.cpp @@ -403,6 +403,27 @@ AvHGamerules::AvHGamerules() : mTeamA(TEAM_ONE), mTeamB(TEAM_TWO) AIMGR_SetTestNavMode(bNewTestValue); }); + REGISTER_SERVER_FUNCTION("sv_aidronemode", []() + { + if (avh_botsenabled.value == 0) + { + return; + } + + bool bNewTestValue = !AIMGR_GetDroneMode(); + + AIMGR_SetDroneMode(bNewTestValue); + }); + REGISTER_SERVER_FUNCTION("sv_stopaidebug", []() + { + if (avh_botsenabled.value == 0) + { + return; + } + + AIMGR_SetTestNavMode(false); + AIMGR_SetDroneMode(false); + }); g_VoiceGameMgr.Init(&gVoiceHelper, gpGlobals->maxClients);