Bug fixes + Pierow's dynamic load spread

This commit is contained in:
RGreenlees 2024-03-20 13:36:38 +00:00 committed by pierow
parent 50e5a0702e
commit c75d7ec80b
12 changed files with 470 additions and 160 deletions

View File

@ -479,6 +479,11 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
int NumPlayersOnTeam = AITAC_GetNumActivePlayersOnTeam(pBot->Player->GetTeam()); int NumPlayersOnTeam = AITAC_GetNumActivePlayersOnTeam(pBot->Player->GetTeam());
int DesiredPlayers = imini(2, (int)ceilf((float)NumPlayersOnTeam *0.5f)); int DesiredPlayers = imini(2, (int)ceilf((float)NumPlayersOnTeam *0.5f));
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
Vector TeamStartingLocation = AITAC_GetTeamStartingLocation(BotTeam);
const AvHAIHiveDefinition* SiegedHive = AITAC_GetNearestHiveUnderActiveSiege(pBot->Player->GetTeam(), AITAC_GetCommChairLocation(pBot->Player->GetTeam())); const AvHAIHiveDefinition* SiegedHive = AITAC_GetNearestHiveUnderActiveSiege(pBot->Player->GetTeam(), AITAC_GetCommChairLocation(pBot->Player->GetTeam()));
if (SiegedHive) if (SiegedHive)
@ -501,10 +506,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot)) if (AICOMM_ShouldCommanderPrioritiseNodes(pBot))
{ {
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
Vector TeamStartingLocation = AITAC_GetTeamStartingLocation(BotTeam);
DeployableSearchFilter ResNodeFilter; DeployableSearchFilter ResNodeFilter;
ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam(); ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam();
@ -570,7 +572,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
if (NumAssignedPlayers < DesiredPlayers) if (NumAssignedPlayers < DesiredPlayers)
{ {
float ThisDist = vDist2DSq(AITAC_GetCommChairLocation(pBot->Player->GetTeam()), ThisHive->Location); float ThisDist = vDist2DSq(TeamStartingLocation, ThisHive->Location);
if (!EmptyHive || ThisDist < MinDist) if (!EmptyHive || ThisDist < MinDist)
{ {
@ -594,6 +596,61 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
} }
} }
if (!AITAC_ResearchIsComplete(BotTeam, TECH_RESEARCH_PHASETECH)) { return; }
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN)
{
AvHAIHiveDefinition* HiveSiegeOpportunity = nullptr;
float MinDist = 0.0f;
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->Status == HIVE_STATUS_UNBUILT) { continue; }
DeployableSearchFilter ExistingSiegeFilter;
ExistingSiegeFilter.DeployableTeam = BotTeam;
ExistingSiegeFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
ExistingSiegeFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
ExistingSiegeFilter.ReachabilityTeam = BotTeam;
ExistingSiegeFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
ExistingSiegeFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f);
AvHAIBuildableStructure SiegeStructure = AITAC_FindClosestDeployableToLocation(ThisHive->Location, &ExistingSiegeFilter);
if (SiegeStructure.IsValid())
{
HiveSiegeOpportunity = ThisHive;
break;
}
else
{
float ThisDist = vDist2DSq(ThisHive->FloorLocation, TeamStartingLocation);
if (!HiveSiegeOpportunity || ThisDist < MinDist)
{
HiveSiegeOpportunity = ThisHive;
MinDist = ThisDist;
}
}
}
if (HiveSiegeOpportunity)
{
for(int i = 0; i < (DesiredPlayers - MinNumAssignedPlayers); i++)
{
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, HiveSiegeOpportunity->FloorLocation);
if (!FNullEnt(NewAssignee))
{
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, HiveSiegeOpportunity->HiveEdict, ORDERPURPOSE_SIEGE_HIVE);
}
}
}
}
} }
edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation) edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation)
@ -1843,119 +1900,160 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f); StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f);
Vector SiegeLocation = ZERO_VECTOR; Vector SiegeLocation = ZERO_VECTOR;
AvHAIBuildableStructure ExistingPG;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
AvHAIBuildableStructure ExistingPG = AITAC_FindClosestDeployableToLocation(HiveToSiege->Location, &StructureFilter);
StructureFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
AvHAIBuildableStructure ExistingTF = AITAC_FindClosestDeployableToLocation(HiveToSiege->Location, &StructureFilter);
StructureFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
AvHAIBuildableStructure ExistingArmoury = AITAC_FindClosestDeployableToLocation(HiveToSiege->Location, &StructureFilter);
edict_t* NearestBuilder = nullptr; edict_t* NearestBuilder = nullptr;
if (AITAC_PhaseGatesAvailable(CommanderTeam)) // We only build one of these at a time, so we don't drop a bunch of structures and then our intrepid sieger gets killed and the aliens nom them all
if (ExistingPG.IsValid())
{ {
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE; if (ExistingPG.IsCompleted())
ExistingPG = AITAC_FindClosestDeployableToLocation(HiveToSiege->Location, &StructureFilter);
if (ExistingPG.IsValid())
{ {
SiegeLocation = ExistingPG.Location; SiegeLocation = ExistingPG.Location;
} NearestBuilder = AITAC_GetClosestPlayerOnTeamWithLOS(CommanderTeam, ExistingPG.Location, UTIL_MetresToGoldSrcUnits(5.0f), pBot->Edict);
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
AvHAIBuildableStructure ExistingTF = AITAC_FindClosestDeployableToLocation(HiveToSiege->Location, &StructureFilter);
if (vIsZero(SiegeLocation))
{
if (ExistingTF.IsValid())
{
SiegeLocation = ExistingTF.Location;
} }
else else
{ {
NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, HiveToSiege->Location, UTIL_MetresToGoldSrcUnits(20.0f)); // Don't do anything else until we've finished building the phase gate
return false;
if (FNullEnt(NearestBuilder)) { return false; }
SiegeLocation = NearestBuilder->v.origin;
} }
} }
if (FNullEnt(NearestBuilder)) if (ExistingTF.IsValid())
{ {
NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, SiegeLocation, UTIL_MetresToGoldSrcUnits(20.0f)); if (ExistingTF.IsCompleted())
{
if (vIsZero(SiegeLocation))
{
SiegeLocation = ExistingTF.Location;
}
if (FNullEnt(NearestBuilder))
{
NearestBuilder = AITAC_GetClosestPlayerOnTeamWithLOS(CommanderTeam, ExistingTF.Location, UTIL_MetresToGoldSrcUnits(5.0f), pBot->Edict);
}
}
else
{
// Don't do anything else until we've finished building the turret factory
return false;
}
}
else
{
if (FNullEnt(NearestBuilder))
{
NearestBuilder = AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, HiveToSiege->Location, UTIL_MetresToGoldSrcUnits(20.0f));
}
} }
if (FNullEnt(NearestBuilder)) { return false; } if (FNullEnt(NearestBuilder)) { return false; }
Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); bool bPhaseGatesAvailable = AITAC_PhaseGatesAvailable(CommanderTeam);
if (vIsZero(NextBuildPosition)) if (vIsZero(SiegeLocation))
{ {
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); SiegeLocation = NearestBuilder->v.origin;
}
if (vIsZero(NextBuildPosition)) AvHAIDeployableStructureType NextStructure = STRUCTURE_NONE;
if (!ExistingPG.IsValid() && bPhaseGatesAvailable)
{
NextStructure = STRUCTURE_MARINE_PHASEGATE;
}
else if (!ExistingTF.IsValid())
{
NextStructure = STRUCTURE_MARINE_TURRETFACTORY;
}
else if (!ExistingArmoury.IsValid())
{
NextStructure = STRUCTURE_MARINE_ARMOURY;
}
if (NextStructure != STRUCTURE_NONE)
{
Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
if (!vIsZero(NextBuildPosition))
{ {
// Fall-back, this could end up putting the structure in dodgy spots but better than not placing it at all bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
if (bSuccess) { return true; }
} }
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition))
{
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (bSuccess) { return true; }
}
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition))
{
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (bSuccess) { return true; }
}
return false;
} }
if (!ExistingPG.IsValid()) if (!ExistingTF.IsValid()) { return false; }
{
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
}
if (ExistingPG.IsValid() && !(ExistingPG.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { return false; } if ((ExistingTF.StructureStatusFlags & STRUCTURE_STATUS_RESEARCHING)) { return false; }
if (!ExistingTF.IsValid())
{
if (vDist2DSq(NextBuildPosition, HiveToSiege->Location) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f))) { return true; }
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
}
if (ExistingTF.IsValid() && !(ExistingTF.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { return false; }
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
AvHAIBuildableStructure ExistingArmoury = AITAC_FindClosestDeployableToLocation(SiegeLocation, &StructureFilter);
if (!ExistingArmoury.IsValid())
{
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
}
if (ExistingTF.StructureType != STRUCTURE_MARINE_ADVTURRETFACTORY) if (ExistingTF.StructureType != STRUCTURE_MARINE_ADVTURRETFACTORY)
{ {
return AICOMM_UpgradeStructure(pBot, &ExistingTF); return AICOMM_UpgradeStructure(pBot, &ExistingTF);
} }
StructureFilter.DeployableTypes = STRUCTURE_MARINE_SIEGETURRET; StructureFilter.DeployableTypes = STRUCTURE_MARINE_SIEGETURRET;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f); StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
int NumSiegeTurrets = AITAC_GetNumDeployablesNearLocation(ExistingTF.Location, &StructureFilter); int NumSiegeTurrets = AITAC_GetNumDeployablesNearLocation(ExistingTF.Location, &StructureFilter);
if (NumSiegeTurrets == 0 || (NumSiegeTurrets < 5 && UTIL_IsStructureElectrified(ExistingTF.edict))) if (NumSiegeTurrets == 0 || (NumSiegeTurrets < 3 && UTIL_IsStructureElectrified(ExistingTF.edict)))
{ {
SiegeLocation = ExistingTF.Location; Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(5.0f));
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(5.0f)); if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) <= sqrf(BALANCE_VAR(kSiegeTurretRange)))
if (vIsZero(NextBuildPosition))
{ {
// Reduce radius to avoid putting it on the other side of a wall or something bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(3.0f));
if (vIsZero(NextBuildPosition)) if (bSuccess) { return true; }
{ }
// Fall-back, this could end up putting the structure in dodgy spots but better than not placing it at all
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(5.0f)); NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(5.0f));
}
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) <= sqrf(BALANCE_VAR(kSiegeTurretRange)))
{
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (bSuccess) { return true; }
}
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) <= sqrf(BALANCE_VAR(kSiegeTurretRange)))
{
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (bSuccess) { return true; }
} }
// Don't put the turret out of siege range
if (vDist2DSq(NextBuildPosition, HiveToSiege->Location) > sqrf(kSiegeTurretRange)) { return true; }
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
} }
if (!UTIL_IsStructureElectrified(ExistingTF.edict)) if (!UTIL_IsStructureElectrified(ExistingTF.edict))
@ -2013,14 +2111,29 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
{ {
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f)); Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (vIsZero(BuildLocation)) if (!vIsZero(BuildLocation))
{ {
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f)); bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
} }
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation)) if (!vIsZero(BuildLocation))
{ {
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY); bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), HiveToSecure->FloorLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
} }
return false; return false;
@ -2036,16 +2149,34 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
if (!ExistingTF.IsValid()) if (!ExistingTF.IsValid())
{ {
// First, try and put the TF near any existing phasegate (if it exists)
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(3.0f)); Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(3.0f));
if (vIsZero(BuildLocation))
{
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
}
if (!vIsZero(BuildLocation)) if (!vIsZero(BuildLocation))
{ {
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY); bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
}
// That failed, now try expanding the radius a bit and ignoring reachability
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
}
// That failed too, try putting it anywhere near the hive location
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), HiveToSecure->FloorLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
} }
return false; return false;
@ -2059,11 +2190,22 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
if (NumTurrets < 5) if (NumTurrets < 5)
{ {
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, UTIL_MetresToGoldSrcUnits(3.0f)); Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, (BALANCE_VAR(kCommandStationBuildDistance) * 0.8f));
if (!vIsZero(BuildLocation)) if (!vIsZero(BuildLocation))
{ {
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_FORTIFY); bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingTF.Location, (BALANCE_VAR(kCommandStationBuildDistance) * 0.8f));
if (!vIsZero(BuildLocation))
{
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (bSuccess) { return true; }
} }
return false; return false;

View File

@ -147,7 +147,7 @@ void CONFIG_ParseConfigFile()
BotSkillLevels[3].alien_bot_view_speed = 2.0f; BotSkillLevels[3].alien_bot_view_speed = 2.0f;
string BotConfigFile = string(getModDirectory()) + "/nsbots.cfg"; string BotConfigFile = string(getModDirectory()) + "/nsbots.ini";
const char* filename = BotConfigFile.c_str(); const char* filename = BotConfigFile.c_str();
@ -429,9 +429,117 @@ void CONFIG_ParseConfigFile()
} }
} }
} }
else
{
ALERT(at_console, "nsbots.ini was not found in the NS mod folder. You can regenerate it with the console command 'sv_regenbotini'");
}
} }
BotFillTiming CONFIG_GetBotFillTiming() BotFillTiming CONFIG_GetBotFillTiming()
{ {
return CurrentBotFillTiming; return CurrentBotFillTiming;
}
void CONFIG_RegenerateIniFile()
{
string BotConfigFile = string(getModDirectory()) + "/nsbots.ini";
const char* filename = BotConfigFile.c_str();
FILE* NewConfigFile = fopen(filename, "w+");
if (!NewConfigFile)
{
ALERT(at_console, "Unable to write to %s, please ensure the user has privileges\n", filename);
return;
}
fprintf(NewConfigFile, "### General bot settings ###\n\n");
fprintf(NewConfigFile, "# What prefix to put in front of a bot's name (can leave blank)\n");
fprintf(NewConfigFile, "prefix=[BOT]\n\n");
fprintf(NewConfigFile, "# When should the server start adding bots? Note: bots will always be added after round start regardless\n");
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, "### Skill Settings ###\n\n");
fprintf(NewConfigFile, "# Bot skill settings. You can define as many settings as you like and reference them by name\n");
fprintf(NewConfigFile, "# Format is BotSkillName = name, followed by one of the following:\n");
fprintf(NewConfigFile, "# ReactionTime = How quickly in seconds the bot will react to sighting enemies\n");
fprintf(NewConfigFile, "# AimSkill = How accurately the bot can lock sights on you after seeing you (0.0 - 1.0)\n");
fprintf(NewConfigFile, "# MovementTracking = How accurately the bot can follow a moving target (0.0 - 1.0)\n");
fprintf(NewConfigFile, "# ViewSpeed = How fast the bot can swivel its view (0.1 - 2.0)\n");
fprintf(NewConfigFile, "# Set the difficulty using the 'mp_botskill' cvar (0 - 3)\n\n");
fprintf(NewConfigFile, "BotSkillLevel=0\n");
fprintf(NewConfigFile, "MarineReactionTime=0.5\n");
fprintf(NewConfigFile, "MarineAimSkill=0.1\n");
fprintf(NewConfigFile, "MarineMovementTracking=0.1\n");
fprintf(NewConfigFile, "MarineViewSpeed=0.5\n");
fprintf(NewConfigFile, "AlienReactionTime=0.5\n");
fprintf(NewConfigFile, "AlienAimSkill=0.2\n");
fprintf(NewConfigFile, "AlienMovementTracking=0.2\n");
fprintf(NewConfigFile, "AlienViewSpeed=0.75\n\n");
fprintf(NewConfigFile, "BotSkillLevel=1\n");
fprintf(NewConfigFile, "MarineReactionTime=0.2\n");
fprintf(NewConfigFile, "MarineAimSkill=0.5\n");
fprintf(NewConfigFile, "MarineMovementTracking=0.4\n");
fprintf(NewConfigFile, "MarineViewSpeed=1.0\n");
fprintf(NewConfigFile, "AlienReactionTime=0.2\n");
fprintf(NewConfigFile, "AlienAimSkill=0.5\n");
fprintf(NewConfigFile, "AlienMovementTracking=0.5\n");
fprintf(NewConfigFile, "AlienViewSpeed=1.3\n\n");
fprintf(NewConfigFile, "BotSkillLevel=2\n");
fprintf(NewConfigFile, "MarineReactionTime=0.2\n");
fprintf(NewConfigFile, "MarineAimSkill=0.6\n");
fprintf(NewConfigFile, "MarineMovementTracking=0.6\n");
fprintf(NewConfigFile, "MarineViewSpeed=1.5\n");
fprintf(NewConfigFile, "AlienReactionTime=0.2\n");
fprintf(NewConfigFile, "AlienAimSkill=0.8\n");
fprintf(NewConfigFile, "AlienMovementTracking=0.8\n");
fprintf(NewConfigFile, "AlienViewSpeed=1.5\n\n");
fprintf(NewConfigFile, "BotSkillLevel=3\n");
fprintf(NewConfigFile, "MarineReactionTime=0.1\n");
fprintf(NewConfigFile, "MarineAimSkill=1.0\n");
fprintf(NewConfigFile, "MarineMovementTracking=1.0\n");
fprintf(NewConfigFile, "MarineViewSpeed=2.0\n");
fprintf(NewConfigFile, "AlienReactionTime=0.1\n");
fprintf(NewConfigFile, "AlienAimSkill=1.0\n");
fprintf(NewConfigFile, "AlienMovementTracking=1.0\n");
fprintf(NewConfigFile, "AlienViewSpeed=2.0\n\n");
fprintf(NewConfigFile, "# Desired team sizes. Only used if bot fill mode is 'fillteams'\n");
fprintf(NewConfigFile, "# Format is TeamSize=mapname:nummarines/numaliens\n");
fprintf(NewConfigFile, "# 'default' will be used if playing a map not listed below\n");
fprintf(NewConfigFile, "TeamSize=default:7/7\n");
fprintf(NewConfigFile, "TeamSize=ns_machina:8/8\n");
fprintf(NewConfigFile, "TeamSize=ns_ragnarok:8/8\n");
fprintf(NewConfigFile, "TeamSize=co_faceoff:4/4\n");
fprintf(NewConfigFile, "TeamSize=co_core:4/4\n");
fprintf(NewConfigFile, "TeamSize=co_pulse:6/6\n");
fprintf(NewConfigFile, "TeamSize=co_ulysses:6/6\n");
fprintf(NewConfigFile, "TeamSize=co_niveus:5/5\n");
fprintf(NewConfigFile, "TeamSize=co_kestrel:5/5\n\n\n");
fprintf(NewConfigFile, "### Alien Settings ###\n\n");
fprintf(NewConfigFile, "# Preferred chamber sequence. Valid entries are 'defense', 'movement' and 'sensory'. Separate sequence with forward slash\n");
fprintf(NewConfigFile, "# You can also use ? for random, so if you want movement always first but then defense and sensory at random, use\n");
fprintf(NewConfigFile, "# ChamberSequence:movement/?/?\n");
fprintf(NewConfigFile, "# Or if you want sensory always last, but movement and defence random, use\n");
fprintf(NewConfigFile, "# ChamberSequence=?/?/sensory\n");
fprintf(NewConfigFile, "ChamberSequence=defense/movement/sensory\n");
fflush(NewConfigFile);
fclose(NewConfigFile);
} }

View File

@ -58,6 +58,6 @@ bot_skill CONFIG_GetBotSkillLevel();
BotFillTiming CONFIG_GetBotFillTiming(); BotFillTiming CONFIG_GetBotFillTiming();
void CONFIG_RegenerateIniFile();
#endif #endif

View File

@ -352,6 +352,7 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE
bool bReachabilityMarkedDirty = false; // If true, reachability flags will be recalculated for this structure bool bReachabilityMarkedDirty = false; // If true, reachability flags will be recalculated for this structure
bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; } bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; }
bool IsCompleted() { return (StructureStatusFlags & STRUCTURE_STATUS_COMPLETED); }
} AvHAIBuildableStructure; } AvHAIBuildableStructure;

View File

@ -1706,7 +1706,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
if (CurrFlags == SAMPLE_POLYFLAGS_JUMP || CurrFlags == SAMPLE_POLYFLAGS_WALLCLIMB || CurrFlags == SAMPLE_POLYFLAGS_FLY) if (CurrFlags == SAMPLE_POLYFLAGS_JUMP || CurrFlags == SAMPLE_POLYFLAGS_WALLCLIMB || CurrFlags == SAMPLE_POLYFLAGS_FLY)
{ {
float MaxHeight = (CurrFlags == SAMPLE_POLYFLAGS_JUMP) ? fmaxf(PrevPoint.z, NextPathPoint.z) + 60.0f : UTIL_FindZHeightForWallClimb(path.back().Location, NextPathPoint, head_hull); float MaxHeight = (CurrFlags == SAMPLE_POLYFLAGS_JUMP) ? fmaxf(PrevPoint.z, NextPathPoint.z) + 60.0f : UTIL_FindZHeightForWallClimb(PrevPoint, NextPathPoint, head_hull);
NextPathNode.requiredZ = MaxHeight; NextPathNode.requiredZ = MaxHeight;
NextPathNode.Location = PrevPoint; NextPathNode.Location = PrevPoint;
@ -1752,7 +1752,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
} }
bot_path_node FinalInitialPathNode; bot_path_node FinalInitialPathNode;
FinalInitialPathNode.FromLocation = path.back().Location; FinalInitialPathNode.FromLocation = (path.size() > 0) ? path.back().Location : FromLocation;
FinalInitialPathNode.Location = ToLocation; FinalInitialPathNode.Location = ToLocation;
FinalInitialPathNode.area = SAMPLE_POLYAREA_GROUND; FinalInitialPathNode.area = SAMPLE_POLYAREA_GROUND;
FinalInitialPathNode.flag = SAMPLE_POLYFLAGS_WALLCLIMB; FinalInitialPathNode.flag = SAMPLE_POLYFLAGS_WALLCLIMB;
@ -5920,7 +5920,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
pBot->BotNavInfo.NextForceRecalc = 0.0f; pBot->BotNavInfo.NextForceRecalc = 0.0f;
pBot->BotNavInfo.bNavProfileChanged = false; pBot->BotNavInfo.bNavProfileChanged = false;
if (dtStatusSucceed(PathFindingStatus)) if (dtStatusSucceed(PathFindingStatus) && BotNavInfo->CurrentPath.size() > 0)
{ {
pBot->BotNavInfo.StuckInfo.bPathFollowFailed = false; pBot->BotNavInfo.StuckInfo.bPathFollowFailed = false;
ClearBotStuckMovement(pBot); ClearBotStuckMovement(pBot);
@ -6107,7 +6107,7 @@ Vector FindClosestNavigablePointToDestination(const nav_profile& NavProfile, con
dtStatus PathFindingResult = FindPathClosestToPoint(NavProfile, FromLocation, ToLocation, Path, MaxAcceptableDistance); dtStatus PathFindingResult = FindPathClosestToPoint(NavProfile, FromLocation, ToLocation, Path, MaxAcceptableDistance);
if (dtStatusSucceed(PathFindingResult)) if (dtStatusSucceed(PathFindingResult) && Path.size() > 0)
{ {
return Path.back().Location; return Path.back().Location;
} }
@ -6897,7 +6897,7 @@ bool BotRecalcPath(AvHAIPlayer* pBot, const Vector Destination)
dtStatus FoundPath = FindPathClosestToPoint(pBot, pBot->BotNavInfo.MoveStyle, pBot->CurrentFloorPosition, ValidNavmeshPoint, pBot->BotNavInfo.CurrentPath, max_ai_use_reach); dtStatus FoundPath = FindPathClosestToPoint(pBot, pBot->BotNavInfo.MoveStyle, pBot->CurrentFloorPosition, ValidNavmeshPoint, pBot->BotNavInfo.CurrentPath, max_ai_use_reach);
if (dtStatusSucceed(FoundPath)) if (dtStatusSucceed(FoundPath) && pBot->BotNavInfo.CurrentPath.size() > 0)
{ {
pBot->BotNavInfo.TargetDestination = Destination; pBot->BotNavInfo.TargetDestination = Destination;
pBot->BotNavInfo.ActualMoveDestination = pBot->BotNavInfo.CurrentPath.back().Location; pBot->BotNavInfo.ActualMoveDestination = pBot->BotNavInfo.CurrentPath.back().Location;
@ -8433,7 +8433,7 @@ void NAV_SetMoveMovementTask(AvHAIPlayer* pBot, Vector MoveLocation, DoorTrigger
vector<bot_path_node> Path; vector<bot_path_node> Path;
dtStatus PathStatus = FindPathClosestToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, MoveLocation, Path, 200.0f); dtStatus PathStatus = FindPathClosestToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, MoveLocation, Path, 200.0f);
if (dtStatusSucceed(PathStatus)) if (dtStatusSucceed(PathStatus) && Path.size() > 0)
{ {
MoveTask->TaskLocation = Path.back().Location; MoveTask->TaskLocation = Path.back().Location;
} }
@ -8452,7 +8452,7 @@ void NAV_SetTouchMovementTask(AvHAIPlayer* pBot, edict_t* EntityToTouch, DoorTri
vector<bot_path_node> Path; vector<bot_path_node> Path;
dtStatus PathStatus = FindPathClosestToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, UTIL_GetCentreOfEntity(EntityToTouch), Path, 200.0f); dtStatus PathStatus = FindPathClosestToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, UTIL_GetCentreOfEntity(EntityToTouch), Path, 200.0f);
if (dtStatusSucceed(PathStatus)) if (dtStatusSucceed(PathStatus) && Path.size() > 0)
{ {
MoveTask->TaskLocation = Path.back().Location; MoveTask->TaskLocation = Path.back().Location;
} }

View File

@ -3413,8 +3413,11 @@ void AIPlayerSetMarineCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{ {
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
// Go attack sieged hive // Go attack sieged hive
const AvHAIHiveDefinition* ActiveSiegeHive = AITAC_GetNearestHiveUnderActiveSiege(pBot->Player->GetTeam(), pBot->Edict->v.origin); const AvHAIHiveDefinition* ActiveSiegeHive = AITAC_GetNearestHiveUnderActiveSiege(BotTeam, pBot->Edict->v.origin);
if (ActiveSiegeHive) if (ActiveSiegeHive)
{ {
@ -3434,7 +3437,9 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
AvHAIHiveDefinition* ThisHive = (*it); AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; } if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; }
int NumMarinesSecuring = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ThisHive->Location, UTIL_MetresToGoldSrcUnits(15.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER); if (AICOMM_IsHiveFullySecured(pBot, ThisHive, false)) { continue; }
int NumMarinesSecuring = AITAC_GetNumPlayersOfTeamInArea(BotTeam, ThisHive->Location, UTIL_MetresToGoldSrcUnits(15.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
if (NumMarinesSecuring < 2) if (NumMarinesSecuring < 2)
{ {
@ -3461,15 +3466,39 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
// Go to a good siege location if phase gates available // Go to a good siege location if phase gates available
if (AITAC_PhaseGatesAvailable(pBot->Player->GetTeam())) if (AITAC_PhaseGatesAvailable(BotTeam))
{ {
const AvHAIHiveDefinition* ActiveHive = AITAC_GetActiveHiveNearestLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam()), pBot->Edict->v.origin); const AvHAIHiveDefinition* ActiveHive = AITAC_GetActiveHiveNearestLocation(AIMGR_GetEnemyTeam(BotTeam), pBot->Edict->v.origin);
if (ActiveHive) if (ActiveHive)
{ {
if (Task->TaskType != TASK_MOVE) DeployableSearchFilter EnemyDefences;
EnemyDefences.DeployableTeam = EnemyTeam;
EnemyDefences.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
EnemyDefences.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyDefences.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (!AITAC_DeployableExistsAtLocation(ActiveHive->FloorLocation, &EnemyDefences) && AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, ActiveHive->Location, UTIL_MetresToGoldSrcUnits(10.0f), nullptr) < 3)
{ {
AITASK_SetMoveTask(pBot, Task, UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, ActiveHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(20.0f)), false); AITASK_SetAttackTask(pBot, Task, ActiveHive->HiveEdict, false);
return;
}
if (Task->TaskType != TASK_GUARD || vDist2DSq(Task->TaskLocation, ActiveHive->Location) > sqrf(UTIL_MetresToGoldSrcUnits(20.0f)))
{
Vector GuardLocation = UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, ActiveHive->FloorLocation, UTIL_MetresToGoldSrcUnits(15.0f), UTIL_MetresToGoldSrcUnits(25.0f));
if (!vIsZero(GuardLocation))
{
Task->TaskType = TASK_GUARD;
Task->TaskLength = 60.0f;
Task->TaskLocation = GuardLocation;
Task->bTaskIsUrgent = false;
Task->TaskStartedTime = 0.0f;
return;
}
} }
return; return;

View File

@ -413,24 +413,12 @@ void AIMGR_AddAIPlayerToTeam(int Team)
int NewBotIndex = -1; int NewBotIndex = -1;
edict_t* BotEnt = nullptr; edict_t* BotEnt = nullptr;
// If game has ended, don't allow new bots to be added // If bots aren't enabled or the game has ended, don't allow new bots to be added
if (GetGameRules()->GetVictoryTeam() != TEAM_IND) if (!AIMGR_IsBotEnabled() || GetGameRules()->GetVictoryTeam() != TEAM_IND)
{ {
return; return;
} }
if (!NavmeshLoaded())
{
CONFIG_ParseConfigFile();
const char* theCStrLevelName = STRING(gpGlobals->mapname);
if (!loadNavigationData(theCStrLevelName))
{
return;
}
}
if (ActiveAIPlayers.size() >= gpGlobals->maxClients) if (ActiveAIPlayers.size() >= gpGlobals->maxClients)
{ {
ALERT(at_console, "Bot limit reached, cannot add more\n"); ALERT(at_console, "Bot limit reached, cannot add more\n");
@ -594,7 +582,6 @@ void AIMGR_UpdateAIPlayers()
static int CurrentBotSkill = 1; static int CurrentBotSkill = 1;
static int UpdateIndex = 0; static int UpdateIndex = 0;
static int FrameSpread = 3;
CurrTime = gpGlobals->time; CurrTime = gpGlobals->time;
@ -614,8 +601,6 @@ void AIMGR_UpdateAIPlayers()
CurrentBotSkill = cvarBotSkill; CurrentBotSkill = cvarBotSkill;
} }
bool bHasCommander = false;
if (bHasRoundStarted) if (bHasRoundStarted)
{ {
AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber(); AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber();
@ -640,6 +625,15 @@ void AIMGR_UpdateAIPlayers()
} }
} }
} }
int NumCommanders = AIMGR_GetNumAICommanders();
int NumRegularBots = AIMGR_GetNumAIPlayers() - NumCommanders;
int NumBotsThinkThisFrame = 0;
int BotsPerFrame = ceil(BOT_THINK_RATE_HZ * NumRegularBots * FrameDelta);
int BotIndex = 0;
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end();) for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end();)
{ {
@ -652,45 +646,35 @@ void AIMGR_UpdateAIPlayers()
AvHAIPlayer* bot = &(*BotIt); AvHAIPlayer* bot = &(*BotIt);
if (IsPlayerCommander(bot->Edict))
{
bHasCommander = true;
}
if (bSkillChanged) if (bSkillChanged)
{ {
const bot_skill NewSkillSettings = CONFIG_GetBotSkillLevel(); const bot_skill NewSkillSettings = CONFIG_GetBotSkillLevel();
memcpy(&bot->BotSkillSettings, &NewSkillSettings, sizeof(bot_skill)); memcpy(&bot->BotSkillSettings, &NewSkillSettings, sizeof(bot_skill));
} }
int BotIndex = distance(ActiveAIPlayers.begin(), BotIt);
BotUpdateViewRotation(bot, FrameDelta); BotUpdateViewRotation(bot, FrameDelta);
if (bHasRoundStarted) if (bHasRoundStarted)
{ {
if (IsPlayerCommander(bot->Edict)) if (IsPlayerCommander(bot->Edict))
{ {
if (UpdateIndex == FrameSpread) if (UpdateIndex == -1)
{ {
AIPlayerThink(bot); AIPlayerThink(bot);
} }
} }
else else
{ {
if (UpdateIndex != FrameSpread) if (UpdateIndex > -1 && BotIndex >= UpdateIndex && NumBotsThinkThisFrame < BotsPerFrame)
{ {
int BotModulo = BotIndex % FrameSpread; AIPlayerThink(bot);
NumBotsThinkThisFrame++;
if (BotModulo == UpdateIndex)
{
AIPlayerThink(bot);
}
} }
BotIndex++;
} }
} }
if (IS_DEDICATED_SERVER() || (CurrTime - bot->LastServerUpdateTime) >= BOT_MIN_FRAME_TIME) if (IS_DEDICATED_SERVER() || (CurrTime - bot->LastServerUpdateTime) >= BOT_SERVER_UPDATE_RATE)
{ {
UpdateBotChat(bot); UpdateBotChat(bot);
@ -707,11 +691,25 @@ void AIMGR_UpdateAIPlayers()
BotIt++; BotIt++;
} }
UpdateIndex++; if (UpdateIndex < 0)
{
if (UpdateIndex > FrameSpread || (!bHasCommander && UpdateIndex == FrameSpread)) UpdateIndex = 0;
}
else
{ {
UpdateIndex = 0; UpdateIndex += NumBotsThinkThisFrame;
}
if (UpdateIndex >= NumRegularBots)
{
if (NumCommanders > 0)
{
UpdateIndex = -1;
}
else
{
UpdateIndex = 0;
}
} }
PrevTime = CurrTime; PrevTime = CurrTime;
@ -723,6 +721,21 @@ int AIMGR_GetNumAIPlayers()
return ActiveAIPlayers.size(); return ActiveAIPlayers.size();
} }
int AIMGR_GetNumAICommanders()
{
int Result = 0;
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
{
if (it->Player->GetUser3() == AVH_USER3_COMMANDER_PLAYER)
{
Result++;
}
}
return Result;
}
AvHTeamNumber AIMGR_GetTeamANumber() AvHTeamNumber AIMGR_GetTeamANumber()
{ {
return GetGameRules()->GetTeamANumber(); return GetGameRules()->GetTeamANumber();
@ -1001,8 +1014,6 @@ void AIMGR_NewMap()
AITAC_ClearMapAIData(true); AITAC_ClearMapAIData(true);
CONFIG_ParseConfigFile();
AIMGR_BotPrecache(); AIMGR_BotPrecache();
bHasRoundStarted = false; bHasRoundStarted = false;
@ -1030,6 +1041,8 @@ void AIMGR_LoadNavigationData()
// Don't reload the nav mesh if it's already loaded // Don't reload the nav mesh if it's already loaded
if (NavmeshLoaded()) { return; } if (NavmeshLoaded()) { return; }
CONFIG_ParseConfigFile();
const char* theCStrLevelName = STRING(gpGlobals->mapname); const char* theCStrLevelName = STRING(gpGlobals->mapname);
if (!loadNavigationData(theCStrLevelName)) if (!loadNavigationData(theCStrLevelName))
@ -1189,6 +1202,11 @@ void AIMGR_UpdateAIMapData()
} }
} }
void AIMGR_RegenBotIni()
{
CONFIG_RegenerateIniFile();
}
void AIMGR_BotPrecache() void AIMGR_BotPrecache()
{ {
m_spriteTexture = PRECACHE_MODEL("sprites/zbeam6.spr"); m_spriteTexture = PRECACHE_MODEL("sprites/zbeam6.spr");

View File

@ -4,8 +4,10 @@
#include "../AvHConstants.h" #include "../AvHConstants.h"
#include "AvHAIPlayer.h" #include "AvHAIPlayer.h"
// Max rate bot can run its logic, default is 1/60th second. WARNING: Increasing the rate past 100hz causes bots to move and turn slowly due to GoldSrc limits! // The rate at which the bot will call RunPlayerMove in, default is 100hz. WARNING: Increasing the rate past 100hz causes bots to move and turn slowly due to GoldSrc limits!
static const double BOT_MIN_FRAME_TIME = (1.0 / 100.0); static const double BOT_SERVER_UPDATE_RATE = (1.0 / 100.0);
// The rate in hz (times per second) at which the bot will call AIPlayerThink, default is 10 times per second.
static const int BOT_THINK_RATE_HZ = 10;
// Once the first human player has joined the game, how long to wait before adding bots // Once the first human player has joined the game, how long to wait before adding bots
static const float AI_GRACE_PERIOD = 5.0f; static const float AI_GRACE_PERIOD = 5.0f;
// Max time to wait before spawning players if none connect (e.g. empty dedicated server) // Max time to wait before spawning players if none connect (e.g. empty dedicated server)
@ -43,9 +45,13 @@ vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team);
int AIMGR_GetNumPlayersOnTeam(AvHTeamNumber Team); int AIMGR_GetNumPlayersOnTeam(AvHTeamNumber Team);
// How many AI players are in the game (does NOT include third-party bots like RCBot/Whichbot) // How many AI players are in the game (does NOT include third-party bots like RCBot/Whichbot)
int AIMGR_GetNumAIPlayers(); int AIMGR_GetNumAIPlayers();
// How many bot commanders we have (across both teams)
int AIMGR_GetNumAICommanders();
// Returns true if an AI player is on the requested team (does NOT include third-party bots like RCBot/Whichbot) // 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); int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team);
void AIMGR_RegenBotIni();
void AIMGR_UpdateAIMapData(); void AIMGR_UpdateAIMapData();
bool AIMGR_ShouldStartPlayerBalancing(); bool AIMGR_ShouldStartPlayerBalancing();

View File

@ -4188,7 +4188,7 @@ vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamWithLOS(AvHTeamNumber Team, const Ve
return Results; return Results;
} }
bool AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer) int AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer)
{ {
int Result = 0; int Result = 0;

View File

@ -86,7 +86,7 @@ AvHMessageID UTIL_ItemTypeToImpulseCommand(const AvHAIDeployableItemType ItemTyp
edict_t* AITAC_GetClosestPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer); edict_t* AITAC_GetClosestPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
bool AITAC_AnyPlayerOnTeamHasLOSToLocation(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer); bool AITAC_AnyPlayerOnTeamHasLOSToLocation(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
bool AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer); int AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer); vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
bool AITAC_ShouldBotBeCautious(AvHAIPlayer* pBot); bool AITAC_ShouldBotBeCautious(AvHAIPlayer* pBot);

View File

@ -915,7 +915,7 @@ bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
{ {
AvHAIBuildableStructure Structure = (*it); AvHAIBuildableStructure Structure = (*it);
if (Structure.StructureType == STRUCTURE_MARINE_TURRETFACTORY) if (Structure.StructureType == STRUCTURE_MARINE_PHASEGATE)
{ {
bHasPhaseGate = true; bHasPhaseGate = true;
} }
@ -2806,7 +2806,7 @@ void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocat
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, ThisHive->FloorLocation, GuardLocation, path, 500.0f); dtStatus SearchResult = FindPathClosestToPoint(NavProfile, ThisHive->FloorLocation, GuardLocation, path, 500.0f);
if (dtStatusSucceed(SearchResult)) if (dtStatusSucceed(SearchResult) && path.size() > 0)
{ {
Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location); Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location);
Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f); Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f);
@ -2822,7 +2822,7 @@ void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocat
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, AITAC_GetTeamStartingLocation(EnemyTeam), GuardLocation, path, 500.0f); dtStatus SearchResult = FindPathClosestToPoint(NavProfile, AITAC_GetTeamStartingLocation(EnemyTeam), GuardLocation, path, 500.0f);
if (dtStatusSucceed(SearchResult)) if (dtStatusSucceed(SearchResult) && path.size() > 0)
{ {
Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location); Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location);
Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f); Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f);
@ -2839,7 +2839,7 @@ void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocat
{ {
dtStatus SearchResult = FindPathClosestToPoint(NavProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), GuardLocation, path, 500.0f); dtStatus SearchResult = FindPathClosestToPoint(NavProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), GuardLocation, path, 500.0f);
if (dtStatusSucceed(SearchResult)) if (dtStatusSucceed(SearchResult) && path.size() > 0)
{ {
Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location); Vector FinalApproachDir = UTIL_GetVectorNormal2D(path.back().Location - prev(prev(path.end()))->Location);
Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f); Vector ProspectiveNewGuardLoc = GuardLocation - (FinalApproachDir * 300.0f);
@ -3264,19 +3264,20 @@ void AITASK_SetBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Stru
if (FNullEnt(StructureToBuild) || UTIL_StructureIsFullyBuilt(StructureToBuild)) { return; } if (FNullEnt(StructureToBuild) || UTIL_StructureIsFullyBuilt(StructureToBuild)) { return; }
if (Task->TaskType == TASK_BUILD && Task->TaskTarget == StructureToBuild) { return; } if (Task->TaskType == TASK_BUILD && Task->TaskTarget == StructureToBuild)
{
Task->bTaskIsUrgent = bIsUrgent;
return;
}
// Get as close as possible to desired location // Get as close as possible to desired location
Vector BuildLocation = FindClosestNavigablePointToDestination(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, UTIL_GetEntityGroundLocation(StructureToBuild), 80.0f); Vector BuildLocation = UTIL_ProjectPointToNavmesh(StructureToBuild->v.origin);
if (BuildLocation != g_vecZero) Task->TaskType = TASK_BUILD;
{ Task->TaskTarget = StructureToBuild;
Task->TaskType = TASK_BUILD; Task->TaskLocation = (!vIsZero(BuildLocation)) ? BuildLocation : UTIL_GetFloorUnderEntity(StructureToBuild);
Task->TaskTarget = StructureToBuild; Task->bTaskIsUrgent = bIsUrgent;
Task->TaskLocation = BuildLocation; Task->StructureType = GetStructureTypeFromEdict(StructureToBuild);
Task->bTaskIsUrgent = bIsUrgent;
Task->StructureType = GetStructureTypeFromEdict(StructureToBuild);
}
} }
void AITASK_SetCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const AvHAIResourceNode* NodeRef, const bool bIsUrgent) void AITASK_SetCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const AvHAIResourceNode* NodeRef, const bool bIsUrgent)

View File

@ -416,6 +416,11 @@ AvHGamerules::AvHGamerules() : mTeamA(TEAM_ONE), mTeamB(TEAM_TWO)
} }
}); });
REGISTER_SERVER_FUNCTION("sv_regenbotini", []()
{
AIMGR_RegenBotIni();
});
g_VoiceGameMgr.Init(&gVoiceHelper, gpGlobals->maxClients); g_VoiceGameMgr.Init(&gVoiceHelper, gpGlobals->maxClients);
#ifdef DEBUG #ifdef DEBUG