diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index 70eb41f5..df140597 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -1001,7 +1001,31 @@ void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessa return; } - pBot->Impulse = TargetLifeform; + float EvolveCost = 0.0f; + + switch (TargetLifeform) + { + case ALIEN_LIFEFORM_TWO: + EvolveCost = BALANCE_VAR(kGorgeCost); + break; + case ALIEN_LIFEFORM_THREE: + EvolveCost = BALANCE_VAR(kLerkCost); + break; + case ALIEN_LIFEFORM_FOUR: + EvolveCost = BALANCE_VAR(kFadeCost); + break; + case ALIEN_LIFEFORM_FIVE: + EvolveCost = BALANCE_VAR(kOnosCost); + break; + default: + EvolveCost = 0.0f; + break; + } + + if (pBot->Player->GetResources() >= EvolveCost) + { + pBot->Impulse = TargetLifeform; + } } void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot) @@ -1646,11 +1670,6 @@ void UpdateAIPlayerDMRole(AvHAIPlayer* pBot) } -void UpdateAIAlienPlayerNSRole(AvHAIPlayer* pBot) -{ - -} - bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot) { AvHAICommanderMode CurrentCommanderMode = AIMGR_GetCommanderMode(); @@ -1700,6 +1719,34 @@ bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot) return true; } +void UpdateAIAlienPlayerNSRole(AvHAIPlayer* pBot) +{ + AvHTeamNumber BotTeamNumber = pBot->Player->GetTeam(); + + if (BotTeamNumber == TEAM_IND) + { + SetNewAIPlayerRole(pBot, BOT_ROLE_NONE); + + return; + } + + // Don't switch roles if already fade/onos or those resources are potentially wasted + if (IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict)) + { + SetNewAIPlayerRole(pBot, BOT_ROLE_ASSAULT); + return; + } + + // Likewise for lerks + if (IsPlayerLerk(pBot->Edict)) + { + SetNewAIPlayerRole(pBot, BOT_ROLE_HARASS); + return; + } + + +} + void UpdateAIMarinePlayerNSRole(AvHAIPlayer* pBot) { AvHTeamNumber BotTeamNumber = pBot->Player->GetTeam(); @@ -1857,11 +1904,6 @@ void AIPlayerNSMarineThink(AvHAIPlayer* pBot) { pBot->DesiredCombatWeapon = BotMarineChooseBestWeapon(pBot, nullptr); } - - if (pBot->CommanderTask.TaskType != TASK_NONE) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CommanderTask.TaskLocation); - } } void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index f0b85182..018c76ac 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -142,6 +142,16 @@ void AIMGR_UpdateAIPlayerCounts() return; } +int AIMGR_GetNumPlayersOnTeam(AvHTeamNumber Team) +{ + AvHTeamNumber teamA = GetGameRules()->GetTeamANumber(); + AvHTeamNumber teamB = GetGameRules()->GetTeamBNumber(); + + if (Team != teamA && Team != teamB) { return 0; } + + return (Team == teamA) ? GetGameRules()->GetTeamAPlayerCount() : GetGameRules()->GetTeamBPlayerCount(); +} + void AIMGR_UpdateTeamBalance() { AvHTeamNumber teamA = GetGameRules()->GetTeamANumber(); diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.h b/main/source/mod/AIPlayers/AvHAIPlayerManager.h index ada8e6c4..4e8af8ee 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.h +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.h @@ -38,7 +38,9 @@ void AIMGR_UpdateFillTeams(); vector AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team); -// How many AI players are in the game (does not include third-party bots like RCBot/Whichbot) +// Convenient helper function to get total number of players (human and AI) on a team +int AIMGR_GetNumPlayersOnTeam(AvHTeamNumber Team); +// How many AI players are in the game (does NOT include third-party bots like RCBot/Whichbot) int AIMGR_GetNumAIPlayers(); // Returns true if an AI player is on the requested team (does NOT include third-party bots like RCBot/Whichbot) int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team); diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 157e6d8c..2bd2cd22 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -14,6 +14,7 @@ #include "AvHAIHelper.h" #include "AvHAIConstants.h" #include "AvHAIPlayerManager.h" +#include "AvHAIConfig.h" #include "../AvHGamerules.h" #include "../AvHServerUtil.h" @@ -3299,4 +3300,56 @@ bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, fl } return false; +} + +bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot) +{ + AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + + AvHMessageID HiveTechOne = CONFIG_GetHiveTechAtIndex(0); + AvHMessageID HiveTechTwo = CONFIG_GetHiveTechAtIndex(1); + AvHMessageID HiveTechThree = CONFIG_GetHiveTechAtIndex(2); + + AvHAIDeployableStructureType ChamberTypeOne = UTIL_GetChamberTypeForHiveTech(HiveTechOne); + AvHAIDeployableStructureType ChamberTypeTwo = UTIL_GetChamberTypeForHiveTech(HiveTechTwo); + AvHAIDeployableStructureType ChamberTypeThree = UTIL_GetChamberTypeForHiveTech(HiveTechThree); + + DeployableSearchFilter StructureFilter; + StructureFilter.DeployableTeam = BotTeam; + + int NumTeamPlayers = AIMGR_GetNumPlayersOnTeam(BotTeam); + int MaxBuilders = imini(2, (int)floorf((float)NumTeamPlayers * 0.5f)); + int NumCurrentBuilders = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeam, BOT_ROLE_BUILDER, pBot); + + if (MaxBuilders == 0) { return false; } + + // We have a hive without any associated chambers yet + if (AITAC_TeamHiveWithTechExists(BotTeam, MESSAGE_NULL)) + { + return NumCurrentBuilders < MaxBuilders; + } + + StructureFilter.DeployableTypes = ChamberTypeOne; + if (AITAC_TeamHiveWithTechExists(BotTeam, HiveTechOne) && AITAC_GetNumDeployablesNearLocation(pBot->Edict->v.origin, &StructureFilter) < 3) { return NumBuilders < 1; } + + StructureFilter.DeployableTypes = ChamberTypeTwo; + if (AITAC_TeamHiveWithTechExists(BotTeam, HiveTechTwo) && AITAC_GetNumDeployablesNearLocation(pBot->Edict->v.origin, &StructureFilter) < 3) { return NumBuilders < 1; } + + StructureFilter.DeployableTypes = ChamberTypeThree; + if (AITAC_TeamHiveWithTechExists(BotTeam, HiveTechThree) && AITAC_GetNumDeployablesNearLocation(pBot->Edict->v.origin, &StructureFilter) < 3) { return NumBuilders < 1; } + + DeployableSearchFilter ResNodeFilter; + ResNodeFilter.DeployableTeam = BotTeam; + ResNodeFilter.ReachabilityTeam = BotTeam; + ResNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + + vector OwnedNodes = AITAC_GetAllMatchingResourceNodes(pBot->Edict->v.origin, &ResNodeFilter); + + for (auto it = OwnedNodes.begin(); it != OwnedNodes.end(); it++) + { + AvHAIResourceNode* ThisNode = (*it); + + + } + } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index 7ded669d..607e5f98 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -1999,27 +1999,9 @@ void AlienProgressGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) void AlienProgressHealTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { - if (!IsPlayerGorge(pBot->Edict) || FNullEnt(Task->TaskTarget) || IsPlayerDead(Task->TaskTarget)) { return; } + if (FNullEnt(Task->TaskTarget) || IsPlayerDead(Task->TaskTarget)) { return; } - float DesiredDist = (IsEdictStructure(Task->TaskTarget)) ? kHealingSprayRange : (kHealingSprayRange * 0.5f); - - BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, WEAPON_GORGE_HEALINGSPRAY, Task->TaskTarget); - - if (LOSCheck == ATTACK_SUCCESS) - { - pBot->DesiredCombatWeapon = WEAPON_GORGE_HEALINGSPRAY; - BotLookAt(pBot, UTIL_GetCentreOfEntity(Task->TaskTarget)); - if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_GORGE_HEALINGSPRAY) - { - pBot->Button |= IN_ATTACK; - } - - return; - } - else - { - MoveTo(pBot, UTIL_GetEntityGroundLocation(Task->TaskTarget), MOVESTYLE_NORMAL, kHealingSprayRange); - } + BotAlienHealTarget(pBot, Task->TaskTarget); } void AlienProgressBuildHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) @@ -2214,9 +2196,6 @@ void BotAlienBuildResTower(AvHAIPlayer* pBot, const AvHAIResourceNode* NodeToCap RegisterBotAlienBuildAttempt(pBot, NodeToCap->Location, STRUCTURE_ALIEN_RESTOWER); } - - - } void BotAlienBuildHive(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToBuild) @@ -2267,6 +2246,33 @@ void BotAlienBuildHive(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToBuild } } +void BotAlienHealTarget(AvHAIPlayer* pBot, edict_t* HealTarget) +{ + float MaxHealRange = GetMaxIdealWeaponRange(WEAPON_GORGE_HEALINGSPRAY); + float TargetHealRange = MaxHealRange * 0.5f; + + BotAttackResult HitCheck = PerformAttackLOSCheck(pBot, WEAPON_GORGE_HEALINGSPRAY, HealTarget); + + if (HitCheck == ATTACK_SUCCESS) + { + if (IsPlayerGorge(pBot->Edict)) + { + BotShootTarget(pBot, WEAPON_GORGE_HEALINGSPRAY, HealTarget); + return; + } + else + { + BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_TWO); + return; + } + } + else + { + MoveTo(pBot, UTIL_GetEntityGroundLocation(HealTarget), MOVESTYLE_NORMAL, MaxHealRange); + } + +} + void RegisterBotAlienBuildAttempt(AvHAIPlayer* pBot, Vector PlacementLocation, AvHAIDeployableStructureType DesiredStructure) { pBot->ActiveBuildInfo.AttemptedLocation = PlacementLocation; diff --git a/main/source/mod/AIPlayers/AvHAITask.h b/main/source/mod/AIPlayers/AvHAITask.h index 2506117b..c49f5027 100644 --- a/main/source/mod/AIPlayers/AvHAITask.h +++ b/main/source/mod/AIPlayers/AvHAITask.h @@ -112,6 +112,7 @@ void UTIL_ClearGuardInfo(AvHAIPlayer* pBot); void BotAlienPlaceChamber(AvHAIPlayer* pBot, Vector Location, AvHAIDeployableStructureType DesiredStructure); void BotAlienBuildHive(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToBuild); void BotAlienBuildResTower(AvHAIPlayer* pBot, const AvHAIResourceNode* NodeToCap); +void BotAlienHealTarget(AvHAIPlayer* pBot, edict_t* HealTarget); void RegisterBotAlienBuildAttempt(AvHAIPlayer* pBot, Vector PlacementLocation, AvHAIDeployableStructureType DesiredStructure); diff --git a/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp b/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp index 55e1c995..03c612d5 100644 --- a/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp @@ -399,7 +399,7 @@ float GetMaxIdealWeaponRange(const AvHAIWeapon Weapon) case WEAPON_LERK_BITE: return BALANCE_VAR(kBite2Range); case WEAPON_GORGE_HEALINGSPRAY: - return BALANCE_VAR(kHealingSprayRange); + return BALANCE_VAR(kHealingSprayRange) * 0.5f; case WEAPON_MARINE_WELDER: return BALANCE_VAR(kWelderRange); default: