From 2789e7a57c8fd59cde7627e7e11d223388b8d3ab Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Wed, 21 Feb 2024 11:10:35 +0000 Subject: [PATCH] First pass at combat mode --- main/source/mod/AIPlayers/AvHAICommander.cpp | 111 ++- main/source/mod/AIPlayers/AvHAIConstants.h | 7 +- main/source/mod/AIPlayers/AvHAIHelper.cpp | 91 +++ main/source/mod/AIPlayers/AvHAIHelper.h | 3 + main/source/mod/AIPlayers/AvHAINavigation.cpp | 20 +- main/source/mod/AIPlayers/AvHAIPlayer.cpp | 721 +++++++++++++++++- main/source/mod/AIPlayers/AvHAIPlayer.h | 10 + main/source/mod/AIPlayers/AvHAITactical.cpp | 34 +- main/source/mod/AIPlayers/AvHAITask.cpp | 11 +- 9 files changed, 909 insertions(+), 99 deletions(-) diff --git a/main/source/mod/AIPlayers/AvHAICommander.cpp b/main/source/mod/AIPlayers/AvHAICommander.cpp index ed750468..70bc7029 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.cpp +++ b/main/source/mod/AIPlayers/AvHAICommander.cpp @@ -20,7 +20,10 @@ bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureTyp WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD); // Don't allow the commander to place a structure somewhere unreachable to marines - if (!UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), Location, max_player_use_reach)) { return false; } + if (!UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), Location, max_player_use_reach)) + { + return false; + } AvHMessageID StructureID = UTIL_StructureTypeToImpulseCommand(StructureToDeploy); @@ -28,7 +31,10 @@ bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureTyp BuildLocation.z += 4.0f; // This would be rejected if a human was trying to build here, so don't let the bot do it - if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) { return false; } + if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) + { + return false; + } string theErrorMessage; int theCost = 0; @@ -477,7 +483,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot) if (SiegedHive) { - int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE); + int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, SiegedHive->HiveEdict, ORDERPURPOSE_SIEGE_HIVE); if (NumAssignedPlayers < DesiredPlayers) { @@ -487,7 +493,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot) if (!FNullEnt(NewAssignee)) { - AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE); + AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, SiegedHive->HiveEdict, ORDERPURPOSE_SIEGE_HIVE); } } } @@ -560,7 +566,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot) if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; } if (AICOMM_IsHiveFullySecured(pBot, ThisHive, false)) { continue; } - int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ThisHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE); + int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ThisHive->HiveEdict, ORDERPURPOSE_SECURE_HIVE); if (NumAssignedPlayers < DesiredPlayers) { @@ -583,7 +589,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot) if (!FNullEnt(NewAssignee)) { - AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, EmptyHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE); + AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, EmptyHive->HiveEdict, ORDERPURPOSE_SECURE_HIVE); } } } @@ -913,19 +919,33 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) Vector BuildLocation = ZERO_VECTOR; + bool bSuccess = false; + bool bFoundLocation = false; + if (NearestInfantryPortal) { BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestInfantryPortal->Location, UTIL_MetresToGoldSrcUnits(5.0f)); + + if (!vIsZero(BuildLocation)) + { + bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation); + bFoundLocation = true; + } } - if (vIsZero(BuildLocation)) + if (!bSuccess) { BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); + + if (!vIsZero(BuildLocation)) + { + bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation); + bFoundLocation = true; + } } - if (!vIsZero(BuildLocation)) + if (bFoundLocation) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation); return (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kArmoryCost) + 5); } } @@ -1977,42 +1997,51 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair) if (ExistingInfantryPortal) { BuildLocation = UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingInfantryPortal->edict->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), UTIL_MetresToGoldSrcUnits(3.0f)); + + if (!vIsZero(BuildLocation)) + { + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation); + + if (bSuccess) { return true; } + } } - if (vIsZero(BuildLocation)) + Vector SearchPoint = ZERO_VECTOR; + + DeployableSearchFilter ResNodeFilter; + ResNodeFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; + ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam(); + + const AvHAIResourceNode* ResNode = AITAC_FindNearestResourceNodeToLocation(CommChair->v.origin, &ResNodeFilter); + + if (ResNode) { - Vector SearchPoint = ZERO_VECTOR; - - DeployableSearchFilter ResNodeFilter; - ResNodeFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; - ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam(); - - const AvHAIResourceNode* ResNode = AITAC_FindNearestResourceNodeToLocation(CommChair->v.origin, &ResNodeFilter); - - if (ResNode) - { - SearchPoint = ResNode->Location; - } - else - { - return false; - } - - Vector NearestPointToChair = FindClosestNavigablePointToDestination(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SearchPoint, CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f)); - - if (NearestPointToChair != ZERO_VECTOR) - { - float Distance = vDist2D(NearestPointToChair, CommChair->v.origin); - float RandomDist = UTIL_MetresToGoldSrcUnits(5.0f) - Distance; - - BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestPointToChair, RandomDist); - - } - else - { - BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f)); - } + SearchPoint = ResNode->Location; } + else + { + return false; + } + + Vector NearestPointToChair = FindClosestNavigablePointToDestination(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SearchPoint, CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f)); + + if (!vIsZero(NearestPointToChair)) + { + float Distance = vDist2D(NearestPointToChair, CommChair->v.origin); + float RandomDist = UTIL_MetresToGoldSrcUnits(5.0f) - Distance; + + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestPointToChair, RandomDist); + + if (!vIsZero(BuildLocation)) + { + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation); + + if (bSuccess) { return true; } + } + + } + + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f)); if (vIsZero(BuildLocation)) { return false; } diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index c98bc4d9..70060b50 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -189,12 +189,12 @@ typedef enum _AVHAIBOTROLE BOT_ROLE_FIND_RESOURCES, // Will hunt for uncapped resource nodes and cap them. Will attack enemy resource towers BOT_ROLE_SWEEPER, // Defensive role to protect infrastructure and build at base. Will patrol to keep outposts secure - BOT_ROLE_ASSAULT, // Will go to attack the hive and other alien structures + BOT_ROLE_ASSAULT, // Will go to attack the enemy base. In combat mode, used for Fade-focus aliens // Marine-only Roles BOT_ROLE_COMMAND, // Will attempt to take command - BOT_ROLE_BOMBARDIER, // Bot is armed with a GL and wants to wreck your shit + BOT_ROLE_BOMBARDIER, // Bot is armed with a GL and wants to wreck your shit. In combat mode, used for Onos-focus aliens // Alien-only roles @@ -248,11 +248,13 @@ typedef struct _RESOURCE_NODE typedef struct _HIVE_DEFINITION_T { AvHHive* HiveEntity = nullptr; // Hive entity reference + edict_t* HiveEdict = nullptr; // Hive edict reference Vector Location = g_vecZero; // Origin of the hive Vector FloorLocation = g_vecZero; // Some hives are suspended in the air, this is the floor location directly beneath it HiveStatusType Status = HIVE_STATUS_UNBUILT; // Can be unbuilt, in progress, or fully built AvHMessageID TechStatus = MESSAGE_NULL; // What tech (if any) is assigned to this hive right now bool bIsUnderAttack = false; // Is the hive currently under attack? Becomes false if not taken damage for more than 10 seconds + float HealthPercent = 0.0f; // If the hive is built and active, what its health currently is AvHAIResourceNode* HiveResNodeRef = nullptr; // Which resource node (indexes into ResourceNodes array) belongs to this hive? unsigned int ObstacleRefs[MAX_NAV_MESHES]; // When in progress or built, will place an obstacle so bots don't try to walk through it float NextFloorLocationCheck = 0.0f; // When should the closest navigable point to the hive be calculated? Used to delay the check after a hive is built @@ -759,6 +761,7 @@ typedef struct AVH_AI_PLAYER AvHAIBotRole BotRole = BOT_ROLE_NONE; int ExperiencePointsAvailable = 0; // How much experience the bot has to spend + AvHMessageID NextCombatModeUpgrade = MESSAGE_NULL; } AvHAIPlayer; diff --git a/main/source/mod/AIPlayers/AvHAIHelper.cpp b/main/source/mod/AIPlayers/AvHAIHelper.cpp index 5e43b63a..be549534 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIHelper.cpp @@ -6,8 +6,12 @@ #include "../AvHGamerules.h" +#include + int m_spriteTexture; +std::unordered_map LocalizedLocationsMap; + bool UTIL_CommanderTrace(const edict_t* pEdict, const Vector& start, const Vector& end) { TraceResult hit; @@ -492,4 +496,91 @@ void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned MESSAGE_END(); // end return; +} + +void UTIL_ClearLocalizations() +{ + LocalizedLocationsMap.clear(); +} + +void UTIL_LocalizeText(const char* InputText, string& OutputText) +{ + // Don't localize empty strings + if (!strcmp(InputText, "")) + { + OutputText = ""; + } + + char theInputString[1024]; + + sprintf(theInputString, "%s", InputText); + + std::unordered_map::const_iterator FoundLocalization = LocalizedLocationsMap.find(theInputString); + + if (FoundLocalization != LocalizedLocationsMap.end()) + { + OutputText = FoundLocalization->second; + return; + } + + char filename[256]; + + std::string localizedString(theInputString); + + string titlesPath = string(getModDirectory()) + "/titles.txt"; + strcpy(filename, titlesPath.c_str()); + + std::ifstream cFile(filename); + if (cFile.is_open()) + { + std::string line; + while (getline(cFile, line)) + { + line.erase(std::remove_if(line.begin(), line.end(), isspace), + line.end()); + if (line[0] == '/' || line.empty()) + continue; + + if (line.compare(theInputString) == 0) + { + getline(cFile, line); + getline(cFile, localizedString); + break; + + } + } + } + + char theOutputString[1024]; + + sprintf(theOutputString, "%s", localizedString.c_str()); + + string Delimiter = "Hive -"; + auto delimiterPos = localizedString.find(Delimiter); + + if (delimiterPos == std::string::npos) + { + Delimiter = "Hive Location -"; + delimiterPos = localizedString.find(Delimiter); + } + + if (delimiterPos == std::string::npos) + { + Delimiter = "Hive Location -"; + delimiterPos = localizedString.find("Hive Location -"); + } + + if (delimiterPos != std::string::npos) + { + auto AreaName = localizedString.substr(delimiterPos + Delimiter.length()); + + AreaName.erase(0, AreaName.find_first_not_of(" \r\n\t\v\f")); + + sprintf(theOutputString, "%s", AreaName.c_str()); + } + + OutputText = theOutputString; + + LocalizedLocationsMap[InputText] = OutputText; + } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAIHelper.h b/main/source/mod/AIPlayers/AvHAIHelper.h index 0c5b11e1..999f89af 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.h +++ b/main/source/mod/AIPlayers/AvHAIHelper.h @@ -52,4 +52,7 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSec void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string); +void UTIL_ClearLocalizations(); +void UTIL_LocalizeText(const char* InputText, string& OutputText); + #endif \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index 7c10077b..b05118f4 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -5161,11 +5161,19 @@ void UpdateBotStuck(AvHAIPlayer* pBot) } ClearBotPath(pBot); - } if (!vIsZero(pBot->desiredMovementDir)) { + edict_t* BlockingEntity = UTIL_TraceEntity(pBot->Edict, pBot->Edict->v.origin, (pBot->Edict->v.origin + pBot->desiredMovementDir * 50.0f)); + + if (IsEdictStructure(BlockingEntity)) + { + pBot->desiredMovementDir = UTIL_GetVectorNormal2D(pBot->desiredMovementDir + UTIL_GetCrossProduct(pBot->desiredMovementDir, UP_VECTOR)); + + BotMovementInputs(pBot); + } + BotJump(pBot); if (!IsPlayerSkulk(pBot->Edict)) @@ -5235,11 +5243,15 @@ void UpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle) { pBot->BotNavInfo.NavProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE); pBot->BotNavInfo.NavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE); + pBot->BotNavInfo.NavProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE); + pBot->BotNavInfo.NavProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE); } else { pBot->BotNavInfo.NavProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE); pBot->BotNavInfo.NavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE); + pBot->BotNavInfo.NavProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE); + pBot->BotNavInfo.NavProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE); } } @@ -8245,12 +8257,12 @@ void NAV_ProgressMovementTask(AvHAIPlayer* pBot) { if (IsPlayerInUseRange(pBot->Edict, MoveTask->TaskTarget)) { - Vector BBMin = MoveTask->TaskTarget->v.absmin; - Vector BBMax = MoveTask->TaskTarget->v.absmax; + Vector BBMin = MoveTask->TaskTarget->v.absmin + Vector(5.0f, 5.0f, 5.0f); + Vector BBMax = MoveTask->TaskTarget->v.absmax - Vector(5.0f, 5.0f, 5.0f); vScaleBB(BBMin, BBMax, 0.75f); - BotLookAt(pBot, vClosestPointOnBB(pBot->CurrentEyePosition, BBMin, BBMax)); + BotLookAt(pBot, vClosestPointOnBB(pBot->Edict->v.origin, BBMin, BBMax)); pBot->DesiredCombatWeapon = WEAPON_MARINE_WELDER; if (GetPlayerCurrentWeapon(pBot->Player) != WEAPON_MARINE_WELDER) diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index 278de535..c7115d22 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -1814,9 +1814,9 @@ void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole) void UpdateAIPlayerCORole(AvHAIPlayer* pBot) { + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); - - if (AIMGR_GetNumAIPlayersWithRoleOnTeam(pBot->Player->GetTeam(), BOT_ROLE_SWEEPER, pBot) == 0) + if (AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_SWEEPER, pBot) == 0) { SetNewAIPlayerRole(pBot, BOT_ROLE_SWEEPER); return; @@ -1826,21 +1826,30 @@ void UpdateAIPlayerCORole(AvHAIPlayer* pBot) { if (IsPlayerLerk(pBot->Edict)) { - SetNewAIPlayerRole(pBot, BOT_ROLE_SWEEPER); + SetNewAIPlayerRole(pBot, BOT_ROLE_HARASS); return; } - int NumLerks = AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER3, pBot->Edict); - int NumHarassers = AIMGR_GetNumAIPlayersWithRoleOnTeam(pBot->Player->GetTeam(), BOT_ROLE_HARASS, pBot); + int NumLerks = AITAC_GetNumPlayersOnTeamOfClass(BotTeam, AVH_USER3_ALIEN_PLAYER3, pBot->Edict); + int NumHarassers = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_HARASS, pBot); if (NumLerks + NumHarassers == 0) { - SetNewAIPlayerRole(pBot, BOT_ROLE_SWEEPER); + SetNewAIPlayerRole(pBot, BOT_ROLE_HARASS); + return; + } + + int MaxOnos = (int)(ceilf((float)(AIMGR_GetNumPlayersOnTeam(BotTeam) - 2)) * 0.3f); + + if (AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_BOMBARDIER, pBot) < MaxOnos) + { + SetNewAIPlayerRole(pBot, BOT_ROLE_BOMBARDIER); return; } } SetNewAIPlayerRole(pBot, BOT_ROLE_ASSAULT); + } void UpdateAIPlayerDMRole(AvHAIPlayer* pBot) @@ -3268,7 +3277,7 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas if (ActiveSiegeHive) { - AITASK_SetAttackTask(pBot, Task, ActiveSiegeHive->HiveEntity->edict(), false); + AITASK_SetAttackTask(pBot, Task, ActiveSiegeHive->HiveEdict, false); return; } @@ -3304,7 +3313,7 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas if (!vIsZero(ActualMoveLocation)) { - AITASK_SetSecureHiveTask(pBot, Task, NearestEmptyHive->HiveEntity->edict(), NearestEmptyHive->FloorLocation, false); + AITASK_SetSecureHiveTask(pBot, Task, NearestEmptyHive->HiveEdict, NearestEmptyHive->FloorLocation, false); return; } } @@ -3834,7 +3843,7 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot) { pBot->PrimaryBotTask.TaskType = TASK_BUILD; pBot->PrimaryBotTask.StructureType = STRUCTURE_ALIEN_HIVE; - char msg[64]; + char msg[128]; sprintf(msg, "I'm going to drop the hive at %s", HiveToBuild->HiveName); BotSay(pBot, true, 1.0f, msg); } @@ -3946,48 +3955,233 @@ AvHMessageID GetNextAIPlayerCOMarineUpgrade(AvHAIPlayer* pBot) return RESEARCH_WEAPONS_ONE; } + if (!pBot->Player->GetHasCombatModeUpgrade(BUILD_SHOTGUN)) + { + return BUILD_SHOTGUN; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(BUILD_SHOTGUN)) + { + return BUILD_SHOTGUN; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(BUILD_HMG)) + { + return BUILD_HMG; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(RESEARCH_ARMOR_TWO)) + { + return RESEARCH_ARMOR_TWO; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(RESEARCH_WEAPONS_TWO)) + { + return RESEARCH_WEAPONS_TWO; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(BUILD_HEAVY)) + { + return BUILD_HEAVY; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(RESEARCH_ARMOR_THREE)) + { + return RESEARCH_ARMOR_TWO; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(RESEARCH_WEAPONS_THREE)) + { + return RESEARCH_WEAPONS_TWO; + } + + return MESSAGE_NULL; } AvHMessageID GetNextAIPlayerCOAlienUpgrade(AvHAIPlayer* pBot) { + int NumPointsAvailable = pBot->ExperiencePointsAvailable; + + if (IsPlayerGorge(pBot->Edict)) + { + NumPointsAvailable += GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_TWO); + } + + if (IsPlayerLerk(pBot->Edict)) + { + NumPointsAvailable += GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_THREE); + } + + if (IsPlayerFade(pBot->Edict)) + { + NumPointsAvailable += GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR); + } + + if (IsPlayerOnos(pBot->Edict)) + { + NumPointsAvailable += GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE); + } + // Always start off getting carapace, to improve viability early game if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_ONE)) { return ALIEN_EVOLUTION_ONE; } - // Unlock leap for further viability early game + // If we are a harasser, always ensure we have enough resources to go lerk + if (pBot->BotRole == BOT_ROLE_HARASS) + { + if (NumPointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_THREE)) + { + return MESSAGE_NULL; + } + + // Lerks need adrenaline + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_EIGHT)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Unlock umbra + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_HIVE_TWO_UNLOCK)) + { + return ALIEN_HIVE_TWO_UNLOCK; + } + + // Get that sweet primal scream + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_HIVE_THREE_UNLOCK)) + { + return ALIEN_HIVE_THREE_UNLOCK; + } + + // Unlock focus + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_ELEVEN)) + { + return ALIEN_EVOLUTION_ELEVEN; + } + + // Unlock regeneration + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_TWO)) + { + return ALIEN_EVOLUTION_TWO; + } + + // Unlock celerity + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_SEVEN)) + { + return ALIEN_EVOLUTION_SEVEN; + } + + + return MESSAGE_NULL; + } + + + if (pBot->BotRole == BOT_ROLE_SWEEPER) + { + // If we are a sweeper, always ensure we have enough resources to go gorge in case we want to heal the hive + if (NumPointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_TWO)) + { + return MESSAGE_NULL; + } + + // Gorges need adrenaline + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_EIGHT)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Regen + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_TWO)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Celerity + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_SEVEN)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Redemption + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_THREE)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Focus + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_ELEVEN)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Silence + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_NINE)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Cloak + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_TEN)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + return MESSAGE_NULL; + } + + // ASSAULT and BOMBARDIER STUFF BELOW + // ASSAULT = jacked-up fade + // BOMBARDIER = Onos + + // Unlock leap if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_HIVE_TWO_UNLOCK)) { return ALIEN_HIVE_TWO_UNLOCK; } - // If we are a sweeper, always ensure we have enough resources to go gorge in case we want to heal the hive - if (pBot->BotRole == BOT_ROLE_SWEEPER) + // If we're going assault then make sure we've saved up enough for fade + if (pBot->BotRole == BOT_ROLE_ASSAULT) { - if (!IsPlayerGorge(pBot->Edict)) + if (NumPointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR)) { - if (pBot->ExperiencePointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_TWO)) - { - return MESSAGE_NULL; - } + return MESSAGE_NULL; + } + + // Unlock adrenaline + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_EIGHT)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_HIVE_THREE_UNLOCK)) + { + return ALIEN_HIVE_THREE_UNLOCK; } } - // If we are a harasser, always ensure we have enough resources to go lerk - if (pBot->BotRole == BOT_ROLE_HARASS) + // As a bombardier, we can still go fade if we can't afford Onos yet, so calculate our points savings accordingly + if (pBot->BotRole == BOT_ROLE_BOMBARDIER) { - if (!IsPlayerLerk(pBot->Edict)) + if (NumPointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE)) { - if (pBot->ExperiencePointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_THREE)) - { - return MESSAGE_NULL; - } + return MESSAGE_NULL; + } + + // Unlock adrenaline + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_EIGHT)) + { + return ALIEN_EVOLUTION_EIGHT; + } + + // Unlock regeneration + if (!pBot->Player->GetHasCombatModeUpgrade(ALIEN_EVOLUTION_TWO)) + { + return ALIEN_EVOLUTION_TWO; } } - - + return MESSAGE_NULL; } void AIPlayerCOThink(AvHAIPlayer* pBot) @@ -4012,9 +4206,462 @@ void AIPlayerCOThink(AvHAIPlayer* pBot) UpdateAIPlayerCORole(pBot); - AvHMessageID NextCombatUpgrade = GetNextAIPlayerCOUpgrade(pBot); + if (IsPlayerMarine(pBot->Edict)) + { + AIPlayerCOMarineThink(pBot); + } + else + { + AIPlayerCOAlienThink(pBot); + } } +void AIPlayerCOMarineThink(AvHAIPlayer* pBot) +{ + AvHMessageID NextCombatUpgrade = GetNextAIPlayerCOMarineUpgrade(pBot); + + if (NextCombatUpgrade != MESSAGE_NULL) + { + int Cost = GetGameRules()->GetCostForMessageID(NextCombatUpgrade); + + if (pBot->ExperiencePointsAvailable >= Cost) + { + if (gpGlobals->time - pBot->LastRequestTime > 1.0f) + { + pBot->Impulse = (int)NextCombatUpgrade; + pBot->LastRequestTime = gpGlobals->time; + return; + } + } + } + + if (gpGlobals->time >= pBot->BotNextTaskEvaluationTime) + { + pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f); + + AITASK_BotUpdateAndClearTasks(pBot); + + AIPlayerSetPrimaryCOMarineTask(pBot, &pBot->PrimaryBotTask); + AIPlayerSetSecondaryCOMarineTask(pBot, &pBot->SecondaryBotTask); + } + + pBot->CurrentTask = AIPlayerGetNextTask(pBot); + + if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE) + { + BotProgressTask(pBot, pBot->CurrentTask); + } +} + +void AIPlayerCOAlienThink(AvHAIPlayer* pBot) +{ + if (!pBot->CurrentTask) { pBot->CurrentTask = &pBot->PrimaryBotTask; } + + AvHMessageID NextCombatUpgrade = GetNextAIPlayerCOAlienUpgrade(pBot); + + if (NextCombatUpgrade != MESSAGE_NULL) + { + int Cost = GetGameRules()->GetCostForMessageID(NextCombatUpgrade); + + if (pBot->ExperiencePointsAvailable >= Cost) + { + if (gpGlobals->time - pBot->LastCombatTime > 5.0f) + { + if (gpGlobals->time - pBot->LastRequestTime > 1.0f) + { + pBot->Impulse = (int)NextCombatUpgrade; + pBot->LastRequestTime = gpGlobals->time; + return; + } + } + } + } + + if (gpGlobals->time >= pBot->BotNextTaskEvaluationTime) + { + pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f); + + AITASK_BotUpdateAndClearTasks(pBot); + + AIPlayerSetPrimaryCOAlienTask(pBot, &pBot->PrimaryBotTask); + AIPlayerSetSecondaryCOAlienTask(pBot, &pBot->SecondaryBotTask); + } + + pBot->CurrentTask = AIPlayerGetNextTask(pBot); + + if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE) + { + BotProgressTask(pBot, pBot->CurrentTask); + } + +} + +void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) +{ + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + + DeployableSearchFilter EnemyStuffFilter; + EnemyStuffFilter.DeployableTeam = EnemyTeam; + EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES; + EnemyStuffFilter.ReachabilityTeam = BotTeam; + EnemyStuffFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + + AvHAIBuildableStructure* EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyStuffFilter); + + if (EnemyStructure) + { + AITASK_SetAttackTask(pBot, Task, EnemyStructure->edict, false); + return; + } + else + { + if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN) + { + const AvHAIHiveDefinition* EnemyHive = AITAC_GetActiveHiveNearestLocation(EnemyTeam, pBot->Edict->v.origin); + + if (EnemyHive) + { + AITASK_SetAttackTask(pBot, Task, EnemyHive->HiveEdict, false); + return; + } + } + } + + vector AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam); + edict_t* TargetPlayer = nullptr; + + float MinDist = 0.0f; + + for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++) + { + AvHPlayer* ThisPlayer = (*it); + + if (!ThisPlayer) { continue; } + + edict_t* PlayerEdict = ThisPlayer->edict(); + + if (!IsPlayerActiveInGame(PlayerEdict)) { continue; } + + float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin); + + if (FNullEnt(TargetPlayer) || ThisDist < MinDist) + { + TargetPlayer = PlayerEdict; + MinDist = ThisDist; + } + } + + if (!FNullEnt(TargetPlayer)) + { + MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL); + } +} + +void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) +{ + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + + edict_t* CommChair = AITAC_GetCommChair(BotTeam); + + DeployableSearchFilter AttackedStructuresFilter; + AttackedStructuresFilter.DeployableTypes = SEARCH_ALL_STRUCTURES; + AttackedStructuresFilter.DeployableTeam = BotTeam; + AttackedStructuresFilter.ReachabilityTeam = BotTeam; + AttackedStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + AttackedStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_UNDERATTACK; + AttackedStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(30.0f); + + vector AllAttackedStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &AttackedStructuresFilter); + + AvHAIBuildableStructure* StructureToDefend = nullptr; + float MinDist = 0.0f; + + for (auto it = AllAttackedStructures.begin(); it != AllAttackedStructures.end(); it++) + { + AvHAIBuildableStructure* ThisStructure = (*it); + + float ThisDist = vDist2D(pBot->Edict->v.origin, ThisStructure->edict->v.origin); + + int NumAttackers = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, ThisStructure->Location, UTIL_MetresToGoldSrcUnits(15.0f), nullptr); + + if (NumAttackers == 0) { continue; } + + int NumExistingDefenders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, ThisStructure->Location, ThisDist - 10.0f, false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER); + + if (NumExistingDefenders < 2) + { + if (!StructureToDefend || ThisDist < MinDist) + { + StructureToDefend = ThisStructure; + MinDist = ThisDist; + } + } + } + + if (StructureToDefend) + { + AITASK_SetDefendTask(pBot, Task, StructureToDefend->edict, true); + return; + } + + if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER)) + { + DeployableSearchFilter DamagedStructuresFilter; + DamagedStructuresFilter.DeployableTypes = SEARCH_ALL_STRUCTURES; + DamagedStructuresFilter.DeployableTeam = BotTeam; + DamagedStructuresFilter.ReachabilityTeam = BotTeam; + DamagedStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + DamagedStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_DAMAGED; + DamagedStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); + + AvHAIBuildableStructure* StructureToRepair = nullptr; + vector AllDamagedStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &DamagedStructuresFilter); + + MinDist = 0.0f; + + for (auto it = AllDamagedStructures.begin(); it != AllDamagedStructures.end(); it++) + { + AvHAIBuildableStructure* ThisStructure = (*it); + + if (ThisStructure->StructureType == STRUCTURE_MARINE_COMMCHAIR && ThisStructure->healthPercent < 0.7f) + { + StructureToRepair = ThisStructure; + break; + } + + float ThisDist = vDist2DSq(ThisStructure->Location, pBot->Edict->v.origin); + + if (!StructureToRepair || ThisDist < MinDist) + { + StructureToRepair = ThisStructure; + MinDist = ThisDist; + } + } + + if (StructureToRepair) + { + AITASK_SetWeldTask(pBot, Task, StructureToRepair->edict, true); + return; + } + + DeployableSearchFilter NearbyArmouryFilter; + NearbyArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY); + NearbyArmouryFilter.DeployableTeam = BotTeam; + NearbyArmouryFilter.ReachabilityTeam = BotTeam; + NearbyArmouryFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; + + AvHAIBuildableStructure* NearestEasyAccessArmoury = AITAC_FindClosestDeployableToLocation(ZERO_VECTOR, &NearbyArmouryFilter); + + if (!NearestEasyAccessArmoury) + { + NearbyArmouryFilter.ReachabilityFlags = AI_REACHABILITY_WELDER; + + AvHAIBuildableStructure* NearestWeldableAccessArmoury = AITAC_FindClosestDeployableToLocation(ZERO_VECTOR, &NearbyArmouryFilter); + + if (NearestWeldableAccessArmoury) + { + AITASK_SetMoveTask(pBot, Task, NearestWeldableAccessArmoury->Location, true); + return; + } + } + } + + AITASK_ClearBotTask(pBot, Task); +} + +void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) +{ + + if (IsPlayerGorge(pBot->Edict)) + { + const AvHAIHiveDefinition* TheHive = AITAC_GetNearestTeamHive(pBot->Player->GetTeam(), pBot->Edict->v.origin, true); + + if (TheHive) + { + AITASK_ClearBotTask(pBot, Task); + BotGuardLocation(pBot, TheHive->FloorLocation); + return; + } + } + + if (Task->TaskType == TASK_ATTACK && vDist2DSq(Task->TaskTarget->v.origin, pBot->Edict->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) + { + return; + } + + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + + if (pBot->BotRole == BOT_ROLE_HARASS) + { + if (!IsPlayerLerk(pBot->Edict) && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_THREE)) + { + if (Task->TaskType != TASK_EVOLVE) + { + const AvHAIHiveDefinition* NearestHive = AITAC_GetActiveHiveNearestLocation(BotTeam, pBot->Edict->v.origin); + + if (NearestHive) + { + AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEdict, ALIEN_LIFEFORM_THREE, true); + + } + } + + return; + } + } + + if (pBot->BotRole == BOT_ROLE_ASSAULT) + { + if (!IsPlayerFade(pBot->Edict) && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR)) + { + if (Task->TaskType != TASK_EVOLVE) + { + const AvHAIHiveDefinition* NearestHive = AITAC_GetActiveHiveNearestLocation(BotTeam, pBot->Edict->v.origin); + + if (NearestHive) + { + AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEdict, ALIEN_LIFEFORM_FOUR, true); + } + } + return; + } + } + + if (pBot->BotRole == BOT_ROLE_BOMBARDIER) + { + if (!IsPlayerOnos(pBot->Edict) && !IsPlayerFade(pBot->Edict)) + { + AvHMessageID DesiredEvolution = MESSAGE_NULL; + + if (pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE)) + { + DesiredEvolution = ALIEN_LIFEFORM_FIVE; + } + else if (pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR)) + { + DesiredEvolution = ALIEN_LIFEFORM_FOUR; + } + + if (DesiredEvolution != MESSAGE_NULL) + { + if (Task->TaskType != TASK_EVOLVE) + { + const AvHAIHiveDefinition* NearestHive = AITAC_GetActiveHiveNearestLocation(BotTeam, pBot->Edict->v.origin); + + if (NearestHive) + { + AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEdict, DesiredEvolution, true); + } + } + return; + } + } + } + + DeployableSearchFilter EnemyStuffFilter; + EnemyStuffFilter.DeployableTeam = EnemyTeam; + EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES; + EnemyStuffFilter.ReachabilityTeam = BotTeam; + EnemyStuffFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + + AvHAIBuildableStructure* EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyStuffFilter); + + if (EnemyStructure) + { + AITASK_SetAttackTask(pBot, Task, EnemyStructure->edict, false); + return; + } + + vector AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam); + edict_t* TargetPlayer = nullptr; + + float MinDist = 0.0f; + + for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++) + { + AvHPlayer* ThisPlayer = (*it); + + if (!ThisPlayer) { continue; } + + edict_t* PlayerEdict = ThisPlayer->edict(); + + if (!IsPlayerActiveInGame(PlayerEdict)) { continue; } + + float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin); + + if (FNullEnt(TargetPlayer) || ThisDist < MinDist) + { + TargetPlayer = PlayerEdict; + MinDist = ThisDist; + } + } + + if (!FNullEnt(TargetPlayer)) + { + MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL); + } + +} + +void AIPlayerSetSecondaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) +{ + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); + + const AvHAIHiveDefinition* TheHive = AITAC_GetNearestTeamHive(BotTeam, pBot->Edict->v.origin, true); + + if (TheHive->bIsUnderAttack) + { + int NumAttackers = AITAC_GetNumPlayersOfTeamInArea(EnemyTeam, TheHive->FloorLocation, UTIL_MetresToGoldSrcUnits(15.0f), false, nullptr, AVH_USER3_NONE); + + int MaxDefenders = imini(NumAttackers + 1, (int)floorf((float)AIMGR_GetNumPlayersOnTeam(BotTeam) * 0.5f)); + + 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); + + if (NumExistingDefenders < MaxDefenders) + { + AITASK_SetDefendTask(pBot, Task, TheHive->HiveEdict, true); + return; + } + } + + if (TheHive->HealthPercent < 1.0f && IsPlayerGorge(pBot->Edict)) + { + Task->TaskType = TASK_HEAL; + Task->TaskTarget = TheHive->HiveEdict; + Task->bTaskIsUrgent = true; + return; + } + + if (TheHive->HealthPercent < 0.8f) + { + if (IsPlayerGorge(pBot->Edict) || (pBot->BotRole == BOT_ROLE_SWEEPER && 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; + } + } + } + + + AITASK_ClearBotTask(pBot, Task); +} + + void AIPlayerDMThink(AvHAIPlayer* pBot) { pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot); @@ -4259,7 +4906,7 @@ void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination) { if (!AICOMM_IsHiveFullySecured(pBot, HiveRef, false)) { - AITASK_SetSecureHiveTask(pBot, &pBot->CommanderTask, HiveRef->HiveEntity->edict(), ActualMoveLocation, false); + AITASK_SetSecureHiveTask(pBot, &pBot->CommanderTask, HiveRef->HiveEdict, ActualMoveLocation, false); pBot->CommanderTask.bIssuedByCommander = true; return; } @@ -4480,7 +5127,7 @@ void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task if (HiveToSecure) { - AITASK_SetReinforceStructureTask(pBot, Task, HiveToSecure->HiveEntity->edict(), false); + AITASK_SetReinforceStructureTask(pBot, Task, HiveToSecure->HiveEdict, false); return; } @@ -4771,7 +5418,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task if (NearestHive) { - AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_FIVE, true); + AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEdict, ALIEN_LIFEFORM_FIVE, true); return; } } @@ -4788,7 +5435,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task if (NearestHive) { - AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_FOUR, true); + AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEdict, ALIEN_LIFEFORM_FOUR, true); return; } @@ -5022,7 +5669,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task { if ((*AIIt) == pBot) { continue; } - if ((*AIIt)->PrimaryBotTask.TaskType == TASK_GUARD && (*AIIt)->PrimaryBotTask.TaskTarget == ThisHive->HiveEntity->edict()) + if ((*AIIt)->PrimaryBotTask.TaskType == TASK_GUARD && (*AIIt)->PrimaryBotTask.TaskTarget == ThisHive->HiveEdict) { if ((*AIIt)->Player->GetUser3() >= AVH_USER3_ALIEN_PLAYER3) { bNeedsExtraGuards = false; } NumGuards++; @@ -5065,7 +5712,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task { Task->TaskType = TASK_GUARD; Task->TaskLocation = HiveToGuard->FloorLocation; - Task->TaskTarget = HiveToGuard->HiveEntity->edict(); + Task->TaskTarget = HiveToGuard->HiveEdict; return; } else if (HiveToSecure) @@ -5215,7 +5862,7 @@ void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas if (NearestHive) { - AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEntity->edict(), ALIEN_LIFEFORM_THREE, true); + AITASK_SetEvolveTask(pBot, Task, NearestHive->HiveEdict, ALIEN_LIFEFORM_THREE, true); return; } else @@ -5282,7 +5929,7 @@ void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas { const AvHAIHiveDefinition* EnemyHive = AITAC_GetActiveHiveNearestLocation(EnemyTeam, pBot->Edict->v.origin); - AITASK_SetAttackTask(pBot, Task, EnemyHive->HiveEntity->edict(), false); + AITASK_SetAttackTask(pBot, Task, EnemyHive->HiveEdict, false); return; } @@ -5466,7 +6113,7 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) if (ThisBot != pBot && IsPlayerActiveInGame(ThisBot->Edict) && !IsPlayerGorge(ThisBot->Edict) && vDist2DSq(ThisBot->Edict->v.origin, ThisHive->FloorLocation) > sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { - if (ThisBot->SecondaryBotTask.TaskType == TASK_DEFEND && ThisBot->SecondaryBotTask.TaskTarget == ThisHive->HiveEntity->edict()) + if (ThisBot->SecondaryBotTask.TaskType == TASK_DEFEND && ThisBot->SecondaryBotTask.TaskTarget == ThisHive->HiveEdict) { int ThisDefenderStrength = 1; @@ -5500,7 +6147,7 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) if (HiveToDefend) { - AITASK_SetDefendTask(pBot, Task, HiveToDefend->HiveEntity->edict(), true); + AITASK_SetDefendTask(pBot, Task, HiveToDefend->HiveEdict, true); return; } diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.h b/main/source/mod/AIPlayers/AvHAIPlayer.h index bc6873dd..e0b1e1da 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.h +++ b/main/source/mod/AIPlayers/AvHAIPlayer.h @@ -63,6 +63,11 @@ void AIPlayerNSMarineThink(AvHAIPlayer* pBot); void AIPlayerNSAlienThink(AvHAIPlayer* pBot); // Think routine for the combat game mode void AIPlayerCOThink(AvHAIPlayer* pBot); +void AIPlayerCOMarineThink(AvHAIPlayer* pBot); +void AIPlayerCOAlienThink(AvHAIPlayer* pBot); + + + // Think routine for the deathmatch game mode (e.g. when playing CS maps) void AIPlayerDMThink(AvHAIPlayer* pBot); @@ -94,6 +99,11 @@ void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); void AIPlayerSetWantsAndNeedsAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); +void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); +void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); +void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); +void AIPlayerSetSecondaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); + void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot); bool ShouldBotThink(AvHAIPlayer* pBot); diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index e180c070..1eac41c5 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -6,6 +6,9 @@ // Contains gorge-related functions. Needs refactoring into helper function file // + + + #include "AvHAITactical.h" #include "AvHAINavigation.h" #include "AvHAITask.h" @@ -28,7 +31,6 @@ #include - vector ResourceNodes; vector Hives; @@ -636,7 +638,7 @@ Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive) { if (!Hive) { return ZERO_VECTOR; } - Vector HiveFloorLoc = UTIL_GetFloorUnderEntity(Hive->HiveEntity->edict()); + Vector HiveFloorLoc = UTIL_GetFloorUnderEntity(Hive->HiveEdict); Vector NearestNavigableLoc = ZERO_VECTOR; @@ -672,23 +674,26 @@ void AITAC_PopulateHiveData() AvHAIHiveDefinition NewHive; NewHive.HiveEntity = theEntity; + NewHive.HiveEdict = theEntity->edict(); NewHive.Location = theEntity->pev->origin; + memset(&NewHive.ObstacleRefs, 0, sizeof(NewHive.ObstacleRefs)); AvHAIResourceNode* NearestNode = AITAC_GetNearestResourceNodeToLocation(theEntity->pev->origin); if (NearestNode) { NewHive.HiveResNodeRef = NearestNode; - NearestNode->ParentHive = NewHive.HiveEntity->edict(); + NearestNode->ParentHive = NewHive.HiveEdict; } - NewHive.FloorLocation = UTIL_GetFloorUnderEntity(theEntity->edict()); // Some hives are suspended in the air, this is the floor location directly beneath it + NewHive.FloorLocation = UTIL_GetFloorUnderEntity(NewHive.HiveEdict); // Some hives are suspended in the air, this is the floor location directly beneath it string HiveName; string theLocationName; if (AvHSHUGetNameOfLocation(GetGameRules()->GetInfoLocations(), NewHive.Location, theLocationName)) { + UTIL_LocalizeText(theLocationName.c_str(), theLocationName); HiveName = theLocationName; } @@ -719,6 +724,15 @@ void AITAC_RefreshHiveData() AvHTeamNumber CurrentOwningTeam = theEntity->GetTeamNumber(); HiveStatusType CurrentStatus = (theEntity->GetIsActive() ? HIVE_STATUS_BUILT : (theEntity->GetIsSpawning() ? HIVE_STATUS_BUILDING : HIVE_STATUS_UNBUILT)); + if (CurrentStatus == HIVE_STATUS_BUILT) + { + it->HealthPercent = (it->HiveEdict->v.health / it->HiveEdict->v.max_health); + } + else + { + it->HealthPercent = 1.0f; + } + bool bHiveDestroyed = (CurrentOwningTeam != it->OwningTeam) || (it->Status == HIVE_STATUS_BUILT && CurrentStatus != it->Status); if (bHiveDestroyed) @@ -745,7 +759,7 @@ void AITAC_RefreshHiveData() if (it->Status != HIVE_STATUS_UNBUILT && it->ObstacleRefs[REGULAR_NAV_MESH] == 0) { - UTIL_AddTemporaryObstacles(UTIL_GetCentreOfEntity(it->HiveEntity->edict()) - Vector(0.0f, 0.0f, 25.0f), 125.0f, 300.0f, DT_AREA_NULL, it->ObstacleRefs); + UTIL_AddTemporaryObstacles(UTIL_GetCentreOfEntity(it->HiveEdict) - Vector(0.0f, 0.0f, 25.0f), 125.0f, 300.0f, DT_AREA_NULL, it->ObstacleRefs); it->NextFloorLocationCheck = gpGlobals->time + 1.0f; } else if (it->Status == HIVE_STATUS_UNBUILT && it->ObstacleRefs[REGULAR_NAV_MESH] != 0) @@ -2061,8 +2075,6 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure) { - if (!GetGameRules()->GetGameStarted()) { return; } - UTIL_AddStructureTemporaryObstacles(NewStructure); AvHTeamNumber StructureTeam = (AvHTeamNumber)NewStructure->edict->v.team; @@ -2250,6 +2262,8 @@ void AITAC_LinkDeployedItemToAction(AvHAIPlayer* CommanderBot, const AvHAIDroppe void AITAC_ClearMapAIData() { + UTIL_ClearLocalizations(); + ResourceNodes.clear(); AITAC_ClearHiveInfo(); @@ -2632,7 +2646,7 @@ AvHAIHiveDefinition* AITAC_GetHiveFromEdict(const edict_t* Edict) for (auto it = Hives.begin(); it != Hives.end(); it++) { - if (it->HiveEntity->edict() == Edict) + if (it->HiveEdict == Edict) { return &(*it); } @@ -4503,11 +4517,11 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo ThisDist -= BALANCE_VAR(kHiveHealRadius) * 0.75f; // We're already in healing distance of a hive, that's our healing source - if (ThisDist <= 0.0f) { return (*it)->HiveEntity->edict(); } + if (ThisDist <= 0.0f) { return (*it)->HiveEdict; } if (FNullEnt(Result) || ThisDist < MinDist) { - Result = (*it)->HiveEntity->edict(); + Result = (*it)->HiveEdict; MinDist = ThisDist; } } diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index feee55b2..81605caf 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -2384,12 +2384,13 @@ void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) // so instead aim at the closest point on the func_weldable to us. if (!IsEdictPlayer(Task->TaskTarget) && !IsEdictStructure(Task->TaskTarget)) { - Vector BBMin = Task->TaskTarget->v.absmin; - Vector BBMax = Task->TaskTarget->v.absmax; + Vector BBMin = Task->TaskTarget->v.absmin + Vector(5.0f, 5.0f, 5.0f); + Vector BBMax = Task->TaskTarget->v.absmax - Vector(5.0f, 5.0f, 5.0f); vScaleBB(BBMin, BBMax, 0.75f); - AimLocation = vClosestPointOnBB(pBot->CurrentEyePosition, BBMin, BBMax); + AimLocation = vClosestPointOnBB(pBot->Edict->v.origin, BBMin, BBMax); + } BotLookAt(pBot, AimLocation); @@ -2625,7 +2626,7 @@ void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation) { float DistFromGuardLocation = vDist2DSq(pBot->Edict->v.origin, GuardLocation); - if (DistFromGuardLocation > sqrf(UTIL_MetresToGoldSrcUnits(5.0f))) + if (DistFromGuardLocation > sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) { pBot->GuardInfo.GuardLocation = g_vecZero; pBot->GuardInfo.GuardStartLookTime = 0.0f; @@ -2662,7 +2663,7 @@ void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation) if (gpGlobals->time > pBot->GuardInfo.ThisGuardStandTime) { - pBot->GuardInfo.GuardStandPosition = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, GuardLocation, UTIL_MetresToGoldSrcUnits(4.0f)); + pBot->GuardInfo.GuardStandPosition = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, GuardLocation, UTIL_MetresToGoldSrcUnits(5.0f)); pBot->GuardInfo.ThisGuardStandTime = gpGlobals->time + frandrange(5.0f, 10.0f); }