Potential crash fix

* Hopefully fixed rare crash in the Detour library
* Improved bot retreat thought process
* Fixed potential bot stuck when trying to evolve
* Aliens will now also see and track enemies marked by OC / scent of fear
* Updated some nav meshes
This commit is contained in:
RGreenlees 2024-07-03 16:01:17 +01:00 committed by pierow
parent cea65c1fdc
commit be1aead879
12 changed files with 45 additions and 542 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -2025,410 +2025,6 @@ const AvHAIResourceNode* AICOMM_GetNearestResourceNodeCapOpportunity(const AvHTe
return Result; return Result;
} }
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege)
{
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTeam = CommanderTeam;
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
StructureFilter.ReachabilityTeam = CommanderTeam;
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(25.0f);
Vector SiegeLocation = ZERO_VECTOR;
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;
// 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())
{
if (ExistingPG.IsCompleted())
{
SiegeLocation = ExistingPG.Location;
NearestBuilder = AITAC_GetClosestPlayerOnTeamWithLOS(CommanderTeam, ExistingPG.Location, UTIL_MetresToGoldSrcUnits(5.0f), pBot->Edict);
}
else
{
// Don't do anything else until we've finished building the phase gate
return false;
}
}
if (ExistingTF.IsValid())
{
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; }
bool bPhaseGatesAvailable = AITAC_PhaseGatesAvailable(CommanderTeam);
if (vIsZero(SiegeLocation))
{
SiegeLocation = NearestBuilder->v.origin;
}
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) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (DeployedStructure) { return true; }
}
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (DeployedStructure) { return true; }
}
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (DeployedStructure) { return true; }
}
return false;
}
if (!ExistingTF.IsValid()) { return false; }
if ((ExistingTF.StructureStatusFlags & STRUCTURE_STATUS_RESEARCHING)) { return false; }
if (ExistingTF.StructureType != STRUCTURE_MARINE_ADVTURRETFACTORY)
{
return AICOMM_UpgradeStructure(pBot, &ExistingTF);
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_SIEGETURRET;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
int NumSiegeTurrets = AITAC_GetNumDeployablesNearLocation(ExistingTF.Location, &StructureFilter);
if (NumSiegeTurrets == 0 || (NumSiegeTurrets < 3 && UTIL_IsStructureElectrified(ExistingTF.edict)))
{
Vector 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)))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (DeployedStructure) { return true; }
}
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)))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (DeployedStructure) { 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)))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
if (DeployedStructure) { return true; }
}
}
if (!UTIL_IsStructureElectrified(ExistingTF.edict))
{
return AICOMM_ResearchTech(pBot, &ExistingTF, RESEARCH_ELECTRICAL);
}
return false;
}
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure)
{
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
StructureFilter.ReachabilityTeam = pBot->Player->GetTeam();
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure ExistingStructure = AITAC_FindClosestDeployableToLocation(HiveToSecure->FloorLocation, &StructureFilter);
AvHAIBuildableStructure ExistingPG;
AvHAIBuildableStructure ExistingTF;
Vector OutpostLocation = (ExistingStructure.IsValid() && (ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY || ExistingStructure.Purpose == STRUCTURE_PURPOSE_BASE)) ? ExistingStructure.Location : HiveToSecure->FloorLocation;
if (HiveToSecure->HiveResNodeRef && HiveToSecure->HiveResNodeRef->OwningTeam == TEAM_IND)
{
AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, HiveToSecure->HiveResNodeRef->Location);
return true;
}
if (ExistingStructure.IsValid())
{
if (ExistingStructure.StructureType == STRUCTURE_MARINE_PHASEGATE)
{
ExistingPG = ExistingStructure;
}
else
{
ExistingTF = ExistingStructure;
}
}
if (AITAC_PhaseGatesAvailable(pBot->Player->GetTeam()))
{
if (!ExistingPG.IsValid())
{
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
ExistingPG = AITAC_FindClosestDeployableToLocation(OutpostLocation, &StructureFilter);
if (!ExistingPG.IsValid())
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { return true; }
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { return true; }
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), HiveToSecure->FloorLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { return true; }
}
return false;
}
}
}
if (!ExistingTF.IsValid())
{
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY;
ExistingTF = AITAC_FindClosestDeployableToLocation(OutpostLocation, &StructureFilter);
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));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { 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))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { 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))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { return true; }
}
return false;
}
}
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
int NumTurrets = AITAC_GetNumDeployablesNearLocation(ExistingTF.Location, &StructureFilter);
if (NumTurrets < 5)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF.Location, (BALANCE_VAR(kCommandStationBuildDistance) * 0.8f));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { return true; }
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingTF.Location, (BALANCE_VAR(kCommandStationBuildDistance) * 0.8f));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_FORTIFY);
if (DeployedStructure) { return true; }
}
return false;
}
return false;
}
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair)
{
if (FNullEnt(CommChair) || !UTIL_StructureIsFullyBuilt(CommChair)) { return false; }
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_INFANTRYPORTAL, CommChair->v.origin, BALANCE_VAR(kCommandStationBuildDistance));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE);
if (DeployedStructure) { return true; }
}
DeployableSearchFilter ExistingPortalFilter;
ExistingPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
ExistingPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
ExistingPortalFilter.DeployableTeam = pBot->Player->GetTeam();
ExistingPortalFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
ExistingPortalFilter.ReachabilityTeam = pBot->Player->GetTeam();
AvHAIBuildableStructure ExistingInfantryPortal = AITAC_FindClosestDeployableToLocation(CommChair->v.origin, &ExistingPortalFilter);
// First see if we can place the next infantry portal next to the first one
if (ExistingInfantryPortal.IsValid())
{
BuildLocation = UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingInfantryPortal.edict->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), UTIL_MetresToGoldSrcUnits(3.0f));
if (!vIsZero(BuildLocation))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE);
if (DeployedStructure) { return true; }
}
}
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, BALANCE_VAR(kCommandStationBuildDistance));
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))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE);
if (DeployedStructure) { return true; }
}
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, BALANCE_VAR(kCommandStationBuildDistance));
if (vIsZero(BuildLocation)) { return false; }
return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE) != nullptr;
}
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot) bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot)
{ {
for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++) for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++)
@ -4188,7 +3784,7 @@ void AICOMM_DeployBases(AvHAIPlayer* pBot)
{ {
AvHAIMarineBase* ThisBase = &(*baseIt); AvHAIMarineBase* ThisBase = &(*baseIt);
if (ThisBase->BaseType == MARINE_BASE_SIEGE && !ThisBase->bRecycleBase) if (ThisBase->BaseType == MARINE_BASE_SIEGE)
{ {
if (vDist2DSq(ThisHive->Location, ThisBase->BaseLocation) < sqrf(BALANCE_VAR(kSiegeTurretRange))) if (vDist2DSq(ThisHive->Location, ThisBase->BaseLocation) < sqrf(BALANCE_VAR(kSiegeTurretRange)))
{ {
@ -4838,119 +4434,6 @@ bool AICOMM_ShouldCommanderRelocate(AvHAIPlayer* pBot)
return false; return false;
} }
bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
Vector RelocationPoint = pBot->RelocationSpot;
if (vIsZero(RelocationPoint)) { return false; }
edict_t* CurrentCommChair = AITAC_GetCommChair(BotTeam);
if (FNullEnt(CurrentCommChair)) { return false; }
DeployableSearchFilter OrigInfPortalFilter;
OrigInfPortalFilter.DeployableTeam = BotTeam;
OrigInfPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
OrigInfPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
// First ensure we have one infantry portal in our starting location, in case it goes horribly wrong
if (!AITAC_DeployableExistsAtLocation(CurrentCommChair->v.origin, &OrigInfPortalFilter))
{
return AICOMM_BuildInfantryPortal(pBot, CurrentCommChair);
}
// Don't do anything more if we don't have anyone at the relocation point yet, but we can drop RTs if needed
if (AITAC_GetNumPlayersOfTeamInArea(BotTeam, RelocationPoint, UTIL_MetresToGoldSrcUnits(10.0f), false, nullptr, AVH_USER3_COMMANDER_PLAYER) == 0)
{
const AvHAIResourceNode* CappableNode = AICOMM_GetNearestResourceNodeCapOpportunity(BotTeam, CurrentCommChair->v.origin);
if (CappableNode)
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, CappableNode->Location);
if (DeployedStructure || pBot->Player->GetResources() <= BALANCE_VAR(kResourceTowerCost) + 10) { return true; }
}
return false;
}
DeployableSearchFilter NewBaseFilter;
NewBaseFilter.DeployableTeam = BotTeam;
NewBaseFilter.DeployableTypes = STRUCTURE_MARINE_COMMCHAIR;
NewBaseFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
NewBaseFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
AvHAIBuildableStructure RelocationCommChair = AITAC_FindClosestDeployableToLocation(RelocationPoint, &NewBaseFilter);
if (!RelocationCommChair.IsValid())
{
Vector BuildPoint = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_COMMCHAIR, pBot->RelocationSpot, UTIL_MetresToGoldSrcUnits(10.0f));
if (!vIsZero(BuildPoint))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_COMMCHAIR, BuildPoint, STRUCTURE_PURPOSE_BASE);
if (DeployedStructure) { return true; }
}
BuildPoint = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), pBot->RelocationSpot, UTIL_MetresToGoldSrcUnits(2.0f));
if (!vIsZero(BuildPoint))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_COMMCHAIR, BuildPoint, STRUCTURE_PURPOSE_BASE);
if (DeployedStructure) { return true; }
}
BuildPoint = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), pBot->RelocationSpot, UTIL_MetresToGoldSrcUnits(10.0f));
if (!vIsZero(BuildPoint))
{
AvHAIBuildableStructure* DeployedStructure = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_COMMCHAIR, BuildPoint, STRUCTURE_PURPOSE_BASE);
if (DeployedStructure) { return true; }
}
return false;
}
if (!(RelocationCommChair.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { return false; }
NewBaseFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
NewBaseFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(8.0f);
NewBaseFilter.IncludeStatusFlags = STRUCTURE_STATUS_NONE;
int NumInfPortals = AITAC_GetNumDeployablesNearLocation(RelocationCommChair.Location, &NewBaseFilter);
if (NumInfPortals < 2)
{
return AICOMM_BuildInfantryPortal(pBot, RelocationCommChair.edict);
}
DeployableSearchFilter OldStuffFilter;
OldStuffFilter.DeployableTeam = BotTeam;
OldStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
OldStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
OldStuffFilter.MinSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f);
OldStuffFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE;
vector<AvHAIBuildableStructure> OldBaseStructures = AITAC_FindAllDeployables(pBot->RelocationSpot, &OldStuffFilter);
for (auto it = OldBaseStructures.begin(); it != OldBaseStructures.end(); it++)
{
if (it->edict != CurrentCommChair)
{
return AICOMM_RecycleStructure(pBot, &(*it));
}
}
return false;
}
bool AICOMM_BuildOutBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut) bool AICOMM_BuildOutBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{ {
if (!pBot || !BaseToBuildOut) { return false; } if (!pBot || !BaseToBuildOut) { return false; }

View file

@ -43,7 +43,6 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot); bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot); bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot); bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot);
Vector AICOMM_GetNextScanLocation(AvHAIPlayer* pBot); Vector AICOMM_GetNextScanLocation(AvHAIPlayer* pBot);
@ -51,10 +50,6 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot);
const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation); const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation);
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair);
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege);
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure);
ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edict_t* Requestor); ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edict_t* Requestor);
void AICOMM_CheckNewRequests(AvHAIPlayer* pBot); void AICOMM_CheckNewRequests(AvHAIPlayer* pBot);
bool AICOMM_IsRequestValid(ai_commander_request* Request); bool AICOMM_IsRequestValid(ai_commander_request* Request);

View file

@ -510,6 +510,11 @@ bool UTIL_UpdateTileCache()
return bTileCacheUpToDate; return bTileCacheUpToDate;
} }
bool UTIL_IsTileCacheUpToDate()
{
return bTileCacheUpToDate;
}
Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall) Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall)
{ {
@ -6865,6 +6870,8 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
return true; return true;
} }
if (!UTIL_IsTileCacheUpToDate()) { return true; }
nav_status* BotNavInfo = &pBot->BotNavInfo; nav_status* BotNavInfo = &pBot->BotNavInfo;
pBot->BotNavInfo.MoveStyle = MoveStyle; pBot->BotNavInfo.MoveStyle = MoveStyle;
@ -7060,10 +7067,8 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot, Vector Destination) Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot, Vector Destination)
{ {
Vector GeneralMoveDir = UTIL_GetVectorNormal2D(Destination - pBot->Edict->v.origin);
Vector CheckDir = pBot->Edict->v.origin + (GeneralMoveDir * 16.0f);
Vector ValidNavmeshPoint = AdjustPointForPathfinding(CheckDir); Vector ValidNavmeshPoint = AdjustPointForPathfinding(pBot->CollisionHullBottomLocation, pBot->BotNavInfo.NavProfile);
if (vIsZero(ValidNavmeshPoint)) if (vIsZero(ValidNavmeshPoint))
{ {

View file

@ -495,6 +495,8 @@ nav_door* UTIL_GetNavDoorByEdict(const edict_t* DoorEdict);
nav_door* UTIL_GetClosestLiftToPoints(const Vector StartPoint, const Vector EndPoint); nav_door* UTIL_GetClosestLiftToPoints(const Vector StartPoint, const Vector EndPoint);
AvHAIOffMeshConnection* UTIL_GetOffMeshConnectionForLift(nav_door* LiftRef); AvHAIOffMeshConnection* UTIL_GetOffMeshConnectionForLift(nav_door* LiftRef);
bool UTIL_IsTileCacheUpToDate();
Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall); Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall);
void UTIL_PopulateBaseNavProfiles(); void UTIL_PopulateBaseNavProfiles();

View file

@ -1460,7 +1460,7 @@ void BotUpdateView(AvHAIPlayer* pBot)
bool bHasLOS = !vIsZero(VisiblePoint); bool bHasLOS = !vIsZero(VisiblePoint);
bool bIsPlayerInvisible = UTIL_IsCloakedPlayerInvisible(pBot->Edict, PlayerRef); bool bIsPlayerInvisible = UTIL_IsCloakedPlayerInvisible(pBot->Edict, PlayerRef);
bool bIsTracked = PlayerRef->GetOpacity() > 0.1f && (!bHasLOS && (IsPlayerParasited(PlayerEdict) || (GetHasUpgrade(pBot->Edict->v.iuser4, MASK_UPGRADE_8) && IsPlayerMotionTracked(PlayerEdict)))); bool bIsTracked = PlayerRef->GetOpacity() > 0.1f && (!bHasLOS && (IsPlayerParasited(PlayerEdict) || IsPlayerSOF(PlayerEdict) || (GetHasUpgrade(pBot->Edict->v.iuser4, MASK_UPGRADE_8) && IsPlayerMotionTracked(PlayerEdict))));
TrackingInfo->bHasLOS = bHasLOS; TrackingInfo->bHasLOS = bHasLOS;
TrackingInfo->bEnemyHasLOS = bEnemyCanSee; TrackingInfo->bEnemyHasLOS = bEnemyCanSee;
@ -1739,7 +1739,7 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
Vector ProjectPoint = (IsPlayerLerk(pBot->Edict)) ? pBot->CurrentFloorPosition : pBot->CollisionHullBottomLocation; Vector ProjectPoint = (IsPlayerLerk(pBot->Edict)) ? pBot->CurrentFloorPosition : pBot->CollisionHullBottomLocation;
if (vDist3DSq(pBot->BotNavInfo.LastNavMeshCheckPosition, ProjectPoint) > sqrf(16.0f)) if (UTIL_IsTileCacheUpToDate() && vDist3DSq(pBot->BotNavInfo.LastNavMeshCheckPosition, ProjectPoint) > sqrf(16.0f))
{ {
if (UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), ProjectPoint, 16.0f)) if (UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), ProjectPoint, 16.0f))
{ {
@ -1748,7 +1748,6 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
pBot->BotNavInfo.LastNavMeshPosition = NavPoint; pBot->BotNavInfo.LastNavMeshPosition = NavPoint;
if (pBot->BotNavInfo.IsOnGround || IsPlayerLerk(pBot->Edict)) if (pBot->BotNavInfo.IsOnGround || IsPlayerLerk(pBot->Edict))
{ {
Vector ForwardVector = UTIL_GetForwardVector2D(pBot->Edict->v.angles); Vector ForwardVector = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
@ -3027,7 +3026,7 @@ void AIPlayerNSMarineThink(AvHAIPlayer* pBot)
if (MarineCombatThink(pBot)) { return; } if (MarineCombatThink(pBot)) { return; }
} }
if (gpGlobals->time >= pBot->BotNextTaskEvaluationTime) if (UTIL_IsTileCacheUpToDate() && gpGlobals->time >= pBot->BotNextTaskEvaluationTime)
{ {
pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f); pBot->BotNextTaskEvaluationTime = gpGlobals->time + frandrange(0.2f, 0.5f);
@ -7734,6 +7733,8 @@ bool SkulkCombatThink(AvHAIPlayer* pBot)
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, TrackedEnemyRef->LastDetectedLocation); float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, TrackedEnemyRef->LastDetectedLocation);
bool bShouldBreakRetreat = false;
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_RETREAT) if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_RETREAT)
{ {
edict_t* NearestHealingSource = AITAC_AlienFindNearestHealingSource(pBot->Player->GetTeam(), pBot->Edict->v.origin, pBot->Edict, true); edict_t* NearestHealingSource = AITAC_AlienFindNearestHealingSource(pBot->Player->GetTeam(), pBot->Edict->v.origin, pBot->Edict, true);
@ -7777,7 +7778,7 @@ bool SkulkCombatThink(AvHAIPlayer* pBot)
} }
return false; bShouldBreakRetreat = true;
} }
bool bShouldBreakAmbush = false; bool bShouldBreakAmbush = false;
@ -7787,7 +7788,7 @@ bool SkulkCombatThink(AvHAIPlayer* pBot)
bShouldBreakAmbush = DistToEnemy < ((TrackedEnemyRef->bHasLOS) ? sqrf(UTIL_MetresToGoldSrcUnits(5.0f)) : sqrf(UTIL_MetresToGoldSrcUnits(3.0f))); bShouldBreakAmbush = DistToEnemy < ((TrackedEnemyRef->bHasLOS) ? sqrf(UTIL_MetresToGoldSrcUnits(5.0f)) : sqrf(UTIL_MetresToGoldSrcUnits(3.0f)));
} }
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK || (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_AMBUSH && bShouldBreakAmbush)) if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK || bShouldBreakRetreat || (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_AMBUSH && bShouldBreakAmbush))
{ {
bool bIsCloaked = (UTIL_IsCloakedPlayerInvisible(CurrentEnemy, pBot->Player) || pBot->Player->GetOpacity() < 0.5f); bool bIsCloaked = (UTIL_IsCloakedPlayerInvisible(CurrentEnemy, pBot->Player) || pBot->Player->GetOpacity() < 0.5f);
@ -8551,10 +8552,9 @@ bool FadeCombatThink(AvHAIPlayer* pBot)
return true; return true;
} }
// If the enemy can see the healing source, then we must go on the attack as we're cornered
bShouldBreakRetreat = true;
} }
bShouldBreakRetreat = true;
} }
bool bShouldBreakAmbush = false; bool bShouldBreakAmbush = false;
@ -8769,11 +8769,11 @@ bool OnosCombatThink(AvHAIPlayer* pBot)
MoveTo(pBot, UTIL_GetEntityGroundLocation(NearestHealingSource), MOVESTYLE_NORMAL, DesiredDistFromHealingSource); MoveTo(pBot, UTIL_GetEntityGroundLocation(NearestHealingSource), MOVESTYLE_NORMAL, DesiredDistFromHealingSource);
return true; return true;
} }
}
// If the enemy can see the healing source, then we must go on the attack // If the enemy can see the healing source, then we must go on the attack
bShouldBreakRetreat = true; bShouldBreakRetreat = true;
} }
}
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK || bShouldBreakRetreat) if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK || bShouldBreakRetreat)
{ {

View file

@ -170,6 +170,11 @@ bool IsPlayerMotionTracked(const edict_t* Player)
return (Player->v.iuser4 & MASK_VIS_DETECTED); return (Player->v.iuser4 & MASK_VIS_DETECTED);
} }
bool IsPlayerSOF(const edict_t* Player)
{
return (Player->v.iuser4 & MASK_SENSORY_NEARBY);
}
float GetPlayerEnergy(const edict_t* Player) float GetPlayerEnergy(const edict_t* Player)
{ {
return (Player->v.fuser3 * 0.001f); return (Player->v.fuser3 * 0.001f);

View file

@ -67,6 +67,8 @@ bool IsPlayerGestating(const edict_t* Player);
bool IsPlayerParasited(const edict_t* Player); bool IsPlayerParasited(const edict_t* Player);
// Is the player being marked through walls to enemies through being sighted by an ally or affected by motion tracking? // Is the player being marked through walls to enemies through being sighted by an ally or affected by motion tracking?
bool IsPlayerMotionTracked(const edict_t* Player); bool IsPlayerMotionTracked(const edict_t* Player);
// Is the player being marked through walls by a nearby sensory chamber or scent of fear?
bool IsPlayerSOF(const edict_t* Player);
// Is the player currently on a ladder? Always false for Skulks and Lerks as they can't climb ladders // Is the player currently on a ladder? Always false for Skulks and Lerks as they can't climb ladders
bool IsPlayerOnLadder(const edict_t* Player); bool IsPlayerOnLadder(const edict_t* Player);
// Is the player an onos under the effect of charge? // Is the player an onos under the effect of charge?

View file

@ -5396,6 +5396,8 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo
edict_t* Result = nullptr; edict_t* Result = nullptr;
float MinDist = 0.0f; float MinDist = 0.0f;
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(Team);
vector<AvHAIHiveDefinition*> AllTeamHives = AITAC_GetAllTeamHives(Team, true); vector<AvHAIHiveDefinition*> AllTeamHives = AITAC_GetAllTeamHives(Team, true);
for (auto it = AllTeamHives.begin(); it != AllTeamHives.end(); it++) for (auto it = AllTeamHives.begin(); it != AllTeamHives.end(); it++)
@ -5404,6 +5406,8 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo
// Factor healing radius into the distance checks, we don't have to be right at the hive to heal // Factor healing radius into the distance checks, we don't have to be right at the hive to heal
ThisDist -= BALANCE_VAR(kHiveHealRadius) * 0.75f; ThisDist -= BALANCE_VAR(kHiveHealRadius) * 0.75f;
if (AITAC_AnyPlayerOnTeamHasLOSToLocation(EnemyTeam, (*it)->Location, UTIL_MetresToGoldSrcUnits(30.0f), nullptr)) { continue; }
// We're already in healing distance of a hive, that's our healing source // We're already in healing distance of a hive, that's our healing source
if (ThisDist <= 0.0f) { return (*it)->HiveEdict; } if (ThisDist <= 0.0f) { return (*it)->HiveEdict; }
@ -5429,6 +5433,8 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo
// Factor healing radius into the distance checks, we don't have to be sat on top of the DC to heal // Factor healing radius into the distance checks, we don't have to be sat on top of the DC to heal
ThisDist -= BALANCE_VAR(kHiveHealRadius) * 0.75f; ThisDist -= BALANCE_VAR(kHiveHealRadius) * 0.75f;
if (AITAC_AnyPlayerOnTeamHasLOSToLocation(EnemyTeam, ThisDC.Location, UTIL_MetresToGoldSrcUnits(30.0f), nullptr)) { continue; }
// We're already in healing distance of a DC, that's our healing source // We're already in healing distance of a DC, that's our healing source
if (ThisDist <= 0.0f) { return ThisDC.edict; } if (ThisDist <= 0.0f) { return ThisDC.edict; }
@ -5445,6 +5451,11 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo
{ {
float PlayerSearchDist = (!FNullEnt(Result)) ? MinDist : 0.0f; // As before, we only want players closer than our current "winner" float PlayerSearchDist = (!FNullEnt(Result)) ? MinDist : 0.0f; // As before, we only want players closer than our current "winner"
FriendlyGorge = AITAC_GetNearestPlayerOfClassInArea(Team, SearchLocation, PlayerSearchDist, false, SearchingPlayer, AVH_USER3_ALIEN_PLAYER2); FriendlyGorge = AITAC_GetNearestPlayerOfClassInArea(Team, SearchLocation, PlayerSearchDist, false, SearchingPlayer, AVH_USER3_ALIEN_PLAYER2);
if (!FNullEnt(FriendlyGorge))
{
if (AITAC_AnyPlayerOnTeamHasLOSToLocation(EnemyTeam, FriendlyGorge->v.origin, UTIL_MetresToGoldSrcUnits(30.0f), nullptr)) { FriendlyGorge = nullptr; }
}
} }
return (!FNullEnt(FriendlyGorge) ? FriendlyGorge : Result); return (!FNullEnt(FriendlyGorge) ? FriendlyGorge : Result);

View file

@ -2108,7 +2108,7 @@ void BotProgressEvolveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{ {
if (FNullEnt(Task->TaskTarget)) if (FNullEnt(Task->TaskTarget))
{ {
Task->TaskLocation = FindClosestNavigablePointToDestination(BaseNavProfiles[ONOS_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), UTIL_GetEntityGroundLocation(Task->TaskTarget), UTIL_MetresToGoldSrcUnits(10.0f)); Task->TaskLocation = FindClosestNavigablePointToDestination(BaseNavProfiles[ONOS_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), pBot->CurrentFloorPosition, UTIL_MetresToGoldSrcUnits(10.0f));
if (vIsZero(Task->TaskLocation)) if (vIsZero(Task->TaskLocation))
{ {
@ -2126,11 +2126,11 @@ void BotProgressEvolveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
} }
else else
{ {
Task->TaskLocation = FindClosestNavigablePointToDestination(BaseNavProfiles[ONOS_BASE_NAV_PROFILE], pBot->CurrentFloorPosition, UTIL_GetEntityGroundLocation(Task->TaskTarget), UTIL_MetresToGoldSrcUnits(10.0f)); Task->TaskLocation = FindClosestNavigablePointToDestination(BaseNavProfiles[ONOS_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), UTIL_GetEntityGroundLocation(Task->TaskTarget), UTIL_MetresToGoldSrcUnits(10.0f));
if (vIsZero(Task->TaskLocation)) if (vIsZero(Task->TaskLocation))
{ {
Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE], Task->TaskLocation, UTIL_MetresToGoldSrcUnits(5.0f)); Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE], UTIL_GetEntityGroundLocation(Task->TaskTarget), UTIL_MetresToGoldSrcUnits(10.0f));
} }
} }
} }