Bot Relocation

* Marine bots can now relocate their base to a nearby hive
* If the chance to relocate is above 0 (configurable in nsbots.ini) then the bots will relocate at the start of a match based on the percentage set, or if the current base is overrun and about to be lost
* The bot will recycle the old comm chair
* The bot will abandon the relocation attempt if unsuccessful 90 seconds into a match
This commit is contained in:
RGreenlees 2024-05-23 09:42:33 +01:00 committed by pierow
parent 91231ac069
commit e17fba76ea
9 changed files with 65 additions and 12 deletions

View file

@ -14,6 +14,9 @@ MaxAIMatchTime=90
# 2 = When the round has started (after countdown)
BotFillTiming=1
# Chance the AI commander will try to relocate to an empty hive at match start
# Value is a decimal between 0.0 and 1.0, with 0 being never and 1 being always
RelocationChance=0.1
### Skill Settings ###

View file

@ -8,6 +8,7 @@
#include "AvHAITask.h"
#include "AvHAIHelper.h"
#include "AvHAIPlayerManager.h"
#include "AvHAIConfig.h"
#include "AvHSharedUtil.h"
#include "AvHServerUtil.h"
@ -3701,6 +3702,8 @@ void AICOMM_ReceiveChatRequest(AvHAIPlayer* Commander, edict_t* Requestor, const
bool AICOMM_ShouldCommanderRelocate(AvHAIPlayer* pBot)
{
if (!CONFIG_IsRelocationAllowed()) { return false; }
AvHTeamNumber Team = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(Team);
@ -3818,13 +3821,13 @@ bool AICOMM_ShouldCommanderRelocate(AvHAIPlayer* pBot)
}
// If the match has been going on for a minute and we haven't made any progress in relocation then give up, or we risk losing everything
if (AIMGR_GetMatchLength() > 60.0f)
if (AIMGR_GetMatchLength() > 90.0f)
{
return AITAC_GetNumPlayersOfTeamInArea(Team, pBot->RelocationSpot, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER) > 0;
}
}
return true;
return AITAC_IsRelocationEnabled();
}
bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot)
@ -3877,7 +3880,12 @@ bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot)
if (!RelocationCommChair.IsValid())
{
Vector BuildPoint = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), RelocationPoint, UTIL_MetresToGoldSrcUnits(2.0f));
Vector BuildPoint = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_COMMCHAIR, pBot->RelocationSpot, UTIL_MetresToGoldSrcUnits(10.0f));
if (vIsZero(BuildPoint))
{
BuildPoint = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), RelocationPoint, UTIL_MetresToGoldSrcUnits(2.0f));
}
if (vIsZero(BuildPoint))
{

View file

@ -12,7 +12,7 @@ BotFillTiming CurrentBotFillTiming = FILLTIMING_ALLHUMANS;
float MaxAIMatchTimeMinutes = 90.0f;
bool bRelocationAllowed = false;
float RelocationChance = 0.1f;
std::unordered_map<std::string, TeamSizeDefinitions> TeamSizeMap;
@ -94,7 +94,12 @@ bool CONFIG_IsOnosAllowed()
bool CONFIG_IsRelocationAllowed()
{
return bRelocationAllowed;
return RelocationChance > 0.0f;
}
float CONFIG_GetRelocationChance()
{
return RelocationChance;
}
float CONFIG_GetMaxStuckTime()
@ -335,10 +340,10 @@ void CONFIG_ParseConfigFile()
continue;
}
if (!stricmp(keyChar, "AllowRelocation"))
if (!stricmp(keyChar, "RelocationChance"))
{
const char* ValueChar = value.c_str();
bRelocationAllowed = (!stricmp(ValueChar, "True") || !stricmp(ValueChar, "T"));
float RelocationValue = std::stof(value.c_str());
RelocationChance = RelocationValue;
continue;
}
@ -656,7 +661,11 @@ void CONFIG_RegenerateIniFile()
fprintf(NewConfigFile, "# 0 = On map load (after 5 second grace period)\n");
fprintf(NewConfigFile, "# 1 = When all humans have joined a team (i.e. no more humans left in ready room)\n");
fprintf(NewConfigFile, "# 2 = When the round has started (after countdown)\n");
fprintf(NewConfigFile, "BotFillTiming=1\n\n\n");
fprintf(NewConfigFile, "BotFillTiming=1\n\n");
fprintf(NewConfigFile, "# Chance the AI Commander will try to relocate at the beginning of the game\n");
fprintf(NewConfigFile, "# Value is a decimal between 0.0 and 1.0, with 0 being never and 1 being always\n");
fprintf(NewConfigFile, "RelocationChance=0.1\n\n\n");
fprintf(NewConfigFile, "### Skill Settings ###\n\n");

View file

@ -44,6 +44,7 @@ bool CONFIG_IsFadeAllowed();
bool CONFIG_IsOnosAllowed();
bool CONFIG_IsRelocationAllowed();
float CONFIG_GetRelocationChance();
// Returns the max time a bot is allowed to be stuck before suiciding (0 means forever)
float CONFIG_GetMaxStuckTime();

View file

@ -930,6 +930,8 @@ void AIMGR_ResetRound()
bMapDataInitialised = true;
CountdownStartedTime = 0.0f;
AITAC_DetermineRelocationEnabled();
}
void AIMGR_ReloadNavigationData()

View file

@ -73,6 +73,7 @@ bool IsPlayerClimbingWall(const edict_t* Player)
bool IsPlayerInReadyRoom(const edict_t* Player)
{
if (FNullEnt(Player)) { return false; }
return Player->v.playerclass == PLAYMODE_READYROOM;
}

View file

@ -58,6 +58,8 @@ Vector TeamBStartingLocation = ZERO_VECTOR;
Vector TeamARelocationPoint = ZERO_VECTOR;
Vector TeamBRelocationPoint = ZERO_VECTOR;
bool bEnableRelocation = false; // For this round, should the AI commander try relocating (if appropriate)?
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
@ -5559,7 +5561,7 @@ void AITAC_ManageSquads()
for (auto pIt = it->SquadMembers.begin(); pIt != it->SquadMembers.end();)
{
AvHAIPlayer* ThisPlayer = (*pIt);
if (!AITAC_IsBotPursuingSquadObjective(ThisPlayer, &(*it)))
if (!ThisPlayer || FNullEnt(ThisPlayer->Edict) || !AITAC_IsBotPursuingSquadObjective(ThisPlayer, &(*it)))
{
pIt = it->SquadMembers.erase(pIt);
}
@ -5780,8 +5782,6 @@ Vector AITAC_GetGatherLocationForSquad(AvHAISquad* Squad)
Vector AITAC_FindNewTeamRelocationPoint(AvHTeamNumber Team)
{
if (!CONFIG_IsRelocationAllowed()) { return ZERO_VECTOR; }
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(Team);
// Only relocate if:
@ -5929,4 +5929,21 @@ bool AITAC_IsRelocationCompleted(AvHTeamNumber RelocationTeam, Vector Relocation
}
return true;
}
bool AITAC_IsRelocationEnabled()
{
return bEnableRelocation;
}
void AITAC_DetermineRelocationEnabled()
{
bEnableRelocation = false;
if (CONFIG_IsRelocationAllowed())
{
float RandomRoll = frandrange(0.0f, 1.0f);
bEnableRelocation = (RandomRoll <= CONFIG_GetRelocationChance());
}
}

View file

@ -216,4 +216,8 @@ Vector AITAC_FindNewTeamRelocationPoint(AvHTeamNumber Team);
bool AITAC_IsRelocationPointStillValid(AvHTeamNumber RelocationTeam, Vector RelocationPoint);
bool AITAC_IsRelocationCompleted(AvHTeamNumber RelocationTeam, Vector RelocationPoint);
bool AITAC_IsRelocationEnabled();
void AITAC_DetermineRelocationEnabled();
#endif

View file

@ -987,6 +987,8 @@ bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
if (!HiveToSecure || HiveToSecure->Status != HIVE_STATUS_UNBUILT) { return false; }
// A marine bot will consider their "secure hive" task completed if the following structures have been fully built:
// Phase gate (only if tech available)
// Turret factory (regular or advanced)
@ -995,6 +997,12 @@ bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
// We've relocated to this hive, no need to secure now
if (vDist2DSq(AITAC_GetTeamStartingLocation(BotTeam), HiveToSecure->FloorLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
{
return false;
}
bool bPhaseGatesAvailable = AITAC_PhaseGatesAvailable(BotTeam);
bool bHasPhaseGate = false;