Bot movement improvements

* Commander no longer sends reminders once an order is issued
* Fixed an issue where marines would loiter and refuse to move on once securing a hive
* Improved wall climbing: bots handle varied wall angles better
This commit is contained in:
RGreenlees 2024-05-17 21:46:35 +01:00 committed by pierow
parent 5beb313546
commit c2e41c2011
7 changed files with 98 additions and 33 deletions

View File

@ -380,7 +380,7 @@ bool AICOMM_IsOrderStillValid(AvHAIPlayer* pBot, ai_commander_order* Order)
bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order) bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order)
{ {
// For now, disable reminders as it is annoying for humans and pointless for bots who should obey it always anyway // For now, disable reminders as it is annoying for humans and pointless for bots who should obey it always anyway
return false; return (Order->LastReminderTime < 0.1f);
float NewDist = vDist2DSq(Order->Assignee->v.origin, Order->OrderLocation); float NewDist = vDist2DSq(Order->Assignee->v.origin, Order->OrderLocation);
float OldDist = Order->LastPlayerDistance; float OldDist = Order->LastPlayerDistance;
@ -2154,7 +2154,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
{ {
DeployableSearchFilter StructureFilter; DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE; StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
StructureFilter.DeployableTeam = pBot->Player->GetTeam(); StructureFilter.DeployableTeam = pBot->Player->GetTeam();
StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
StructureFilter.ReachabilityTeam = pBot->Player->GetTeam(); StructureFilter.ReachabilityTeam = pBot->Player->GetTeam();
@ -2164,7 +2164,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
AvHAIBuildableStructure ExistingPG; AvHAIBuildableStructure ExistingPG;
AvHAIBuildableStructure ExistingTF; AvHAIBuildableStructure ExistingTF;
Vector OutpostLocation = (ExistingStructure.IsValid()) ? ExistingStructure.Location : HiveToSecure->FloorLocation; Vector OutpostLocation = (ExistingStructure.IsValid() && ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY) ? ExistingStructure.Location : HiveToSecure->FloorLocation;
if (HiveToSecure->HiveResNodeRef && HiveToSecure->HiveResNodeRef->OwningTeam == TEAM_IND) if (HiveToSecure->HiveResNodeRef && HiveToSecure->HiveResNodeRef->OwningTeam == TEAM_IND)
{ {
@ -3360,11 +3360,11 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY; StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
AvHAIBuildableStructure ExistingStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter); AvHAIBuildableStructure ExistingStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter);
if (ExistingStructure.IsValid() && UTIL_QuickTrace(nullptr, UTIL_GetCentreOfEntity(ExistingStructure.edict), Hive->Location)) if (ExistingStructure.IsValid() && ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY)
{ {
SecureLocation = ExistingStructure.Location; SecureLocation = ExistingStructure.Location;
} }
@ -3444,7 +3444,7 @@ bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinit
bTurretFactoryElectrified = (Structure.StructureStatusFlags & STRUCTURE_STATUS_ELECTRIFIED); bTurretFactoryElectrified = (Structure.StructureStatusFlags & STRUCTURE_STATUS_ELECTRIFIED);
SearchFilter.DeployableTypes = STRUCTURE_MARINE_TURRET; SearchFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
SearchFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(8.0f); SearchFilter.MaxSearchRadius = BALANCE_VAR(kTurretFactoryBuildDistance);
NumTurrets = AITAC_GetNumDeployablesNearLocation(Structure.Location, &SearchFilter); NumTurrets = AITAC_GetNumDeployablesNearLocation(Structure.Location, &SearchFilter);

View File

@ -788,6 +788,7 @@ typedef struct AVH_AI_PLAYER
edict_t* LookTarget = nullptr; // Used to work out what view angle is needed to look at the desired entity edict_t* LookTarget = nullptr; // Used to work out what view angle is needed to look at the desired entity
Vector LookTargetLocation = g_vecZero; // This is the bot's current desired look target. Could be an enemy (see LookTarget), or point of interest Vector LookTargetLocation = g_vecZero; // This is the bot's current desired look target. Could be an enemy (see LookTarget), or point of interest
Vector MoveLookLocation = g_vecZero; // If the bot has to look somewhere specific for movement (e.g. up for a ladder or wall-climb), this will override LookTargetLocation so the bot doesn't get distracted and mess the move up Vector MoveLookLocation = g_vecZero; // If the bot has to look somewhere specific for movement (e.g. up for a ladder or wall-climb), this will override LookTargetLocation so the bot doesn't get distracted and mess the move up
bool bSnapView = false; // Use for rapid, precise snapping of the bot's view to the target. Useful if the bot requires more precise view angles for movement or other reasons
float LastTargetTrackUpdate = 0.0f; // Add a delay to how frequently a bot can track a target's movements float LastTargetTrackUpdate = 0.0f; // Add a delay to how frequently a bot can track a target's movements
float ViewInterpolationSpeed = 0.0f; // How fast should the bot turn its view? Depends on distance to turn float ViewInterpolationSpeed = 0.0f; // How fast should the bot turn its view? Depends on distance to turn
float ViewInterpStartedTime = 0.0f; // Used for interpolation float ViewInterpStartedTime = 0.0f; // Used for interpolation

View File

@ -3702,7 +3702,10 @@ void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
pBot->desiredMovementDir = vBotOrientation; pBot->desiredMovementDir = vBotOrientation;
} }
if (vIsZero(pBot->LookTargetLocation))
{
BotLookAt(pBot, EndPoint + (vBotOrientation * 100.0f));
}
} }
@ -4890,6 +4893,30 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
} }
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint); Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
Vector ClimbAngle = UTIL_GetVectorNormal(Vector(EndPoint.x, EndPoint.y, RequiredClimbHeight) - pBot->Edict->v.origin);
TraceResult SurfaceCheck;
UTIL_TraceHull(pBot->Edict->v.origin - Vector(0.0f, 0.0f, 1.0f), pBot->Edict->v.origin + Vector(0.0f, 0.0f, 5.0f), ignore_monsters, head_hull, pBot->Edict->v.pContainingEntity, &SurfaceCheck);
Vector CeilNormal = SurfaceCheck.vecPlaneNormal;
bool bIsUnderClimbing = false;
bool bClimbingUnderway = ((pBot->CollisionHullBottomLocation.z - StartPoint.z) >= 32.0f) && IsPlayerClimbingWall(pBot->Edict);
if (pEdict->v.origin.z < (RequiredClimbHeight - 10.0f) && !(pEdict->v.flags & FL_ONGROUND) && bClimbingUnderway)
{
bIsUnderClimbing = (SurfaceCheck.flFraction < 1.0f && UTIL_GetDotProduct(ClimbAngle, CeilNormal) < 0.0f);
}
if (bIsUnderClimbing)
{
pBot->Button |= IN_WALK;
vForward = (UTIL_GetDotProduct2D(vForward, CeilNormal) > 0.0f) ? vForward : -vForward;
}
Vector vRight = UTIL_GetVectorNormal(UTIL_GetCrossProduct(vForward, UP_VECTOR)); Vector vRight = UTIL_GetVectorNormal(UTIL_GetCrossProduct(vForward, UP_VECTOR));
pBot->desiredMovementDir = vForward; pBot->desiredMovementDir = vForward;
@ -4912,14 +4939,10 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
pBot->Button &= ~IN_DUCK; pBot->Button &= ~IN_DUCK;
} }
float ZDiff = fabs(pEdict->v.origin.z - RequiredClimbHeight); float ZDiff = fabs(pEdict->v.origin.z - RequiredClimbHeight);
Vector AdjustedTargetLocation = EndPoint + (UTIL_GetVectorNormal2D(EndPoint - StartPoint) * 1000.0f); Vector AdjustedTargetLocation = EndPoint + (UTIL_GetVectorNormal2D(EndPoint - StartPoint) * 1000.0f);
Vector DirectAheadView = pBot->CurrentEyePosition + (UTIL_GetVectorNormal2D(AdjustedTargetLocation - pBot->CurrentEyePosition) * 100.0f); Vector DirectAheadView = pBot->CurrentEyePosition + (UTIL_GetVectorNormal2D(AdjustedTargetLocation - pBot->CurrentEyePosition) * 100.0f);
Vector ClimbSurfaceNormal = UTIL_GetVectorNormal(EndPoint - StartPoint);
Vector LookLocation = g_vecZero; Vector LookLocation = g_vecZero;
if (ZDiff < 1.0f) if (ZDiff < 1.0f)
@ -4931,10 +4954,10 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
// Don't look up/down quite so much as we reach the desired height so we slow down a bit, reduces the chance of over-shooting and climbing right over a vent // Don't look up/down quite so much as we reach the desired height so we slow down a bit, reduces the chance of over-shooting and climbing right over a vent
if (pEdict->v.origin.z > RequiredClimbHeight) if (pEdict->v.origin.z > RequiredClimbHeight)
{ {
if (ZDiff > 32.0f) if (ZDiff > 16.0f)
{ {
ClimbSurfaceNormal = ClimbSurfaceNormal - (2.0f * (UTIL_GetDotProduct(ClimbSurfaceNormal, UP_VECTOR) * ClimbSurfaceNormal)); ClimbAngle = ClimbAngle - (2.0f * (UTIL_GetDotProduct(ClimbAngle, UP_VECTOR) * ClimbAngle));
LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); LookLocation = pBot->CurrentEyePosition + (ClimbAngle * 100.0f);
} }
else else
{ {
@ -4943,12 +4966,27 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
} }
else else
{ {
ClimbSurfaceNormal = ClimbSurfaceNormal; if (bIsUnderClimbing)
LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); {
LookLocation = pBot->CurrentEyePosition + vForward;
LookLocation.z = EndPoint.z + 100.0f;
}
else
{
if (bClimbingUnderway)
{
LookLocation = pBot->CurrentEyePosition + (ClimbAngle * 100.0f);
}
else
{
LookLocation = pBot->CurrentEyePosition + vForward;
LookLocation.z = RequiredClimbHeight;
}
}
} }
} }
if (IsPlayerClimbingWall(pBot->Edict)) if (IsPlayerClimbingWall(pBot->Edict) && !bIsUnderClimbing)
{ {
Vector RightDir = UTIL_GetCrossProduct(vForward, UP_VECTOR); Vector RightDir = UTIL_GetCrossProduct(vForward, UP_VECTOR);
@ -4971,7 +5009,8 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
} }
} }
BotMoveLookAt(pBot, LookLocation); BotMoveLookAt(pBot, LookLocation, true);
//BotDirectLookAt(pBot, LookLocation);
} }
@ -7937,6 +7976,11 @@ void BotMovementInputs(AvHAIPlayer* pBot)
float botSpeed = (pBot->BotNavInfo.bShouldWalk) ? (pBot->Edict->v.maxspeed * 0.4f) : pBot->Edict->v.maxspeed; float botSpeed = (pBot->BotNavInfo.bShouldWalk) ? (pBot->Edict->v.maxspeed * 0.4f) : pBot->Edict->v.maxspeed;
if (pBot->BotNavInfo.bShouldWalk)
{
pBot->Button |= IN_WALK;
}
if (angleDelta < -180.0f) if (angleDelta < -180.0f)
{ {
angleDelta += 360.0f; angleDelta += 360.0f;
@ -9690,6 +9734,11 @@ bool NAV_IsMovementTaskStillValid(AvHAIPlayer* pBot)
if (MoveTask->TaskType == MOVE_TASK_TOUCH) if (MoveTask->TaskType == MOVE_TASK_TOUCH)
{ {
if (pBot->BotNavInfo.IsOnGround)
{
if (vDist2DSq(pBot->Edict->v.origin, MoveTask->TaskLocation) < sqrf(8.0f) && fabs(pBot->Edict->v.origin.z - MoveTask->TaskLocation.z) < 32.0f) { return false; }
}
return (!FNullEnt(MoveTask->TaskTarget) && !IsPlayerTouchingEntity(pBot->Edict, MoveTask->TaskTarget)); return (!FNullEnt(MoveTask->TaskTarget) && !IsPlayerTouchingEntity(pBot->Edict, MoveTask->TaskTarget));
} }

View File

@ -59,17 +59,18 @@ void BotSuicide(AvHAIPlayer* pBot)
} }
/* Makes the bot look at the specified position */ /* Makes the bot look at the specified position */
void BotLookAt(AvHAIPlayer* pBot, const Vector target) void BotLookAt(AvHAIPlayer* pBot, const Vector target, bool bSnap)
{ {
pBot->bSnapView = bSnap;
pBot->LookTargetLocation.x = target.x; pBot->LookTargetLocation.x = target.x;
pBot->LookTargetLocation.y = target.y; pBot->LookTargetLocation.y = target.y;
pBot->LookTargetLocation.z = target.z; pBot->LookTargetLocation.z = target.z;
} }
void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target) void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target, bool bSnap)
{ {
pBot->bSnapView = bSnap;
pBot->MoveLookLocation.x = target.x; pBot->MoveLookLocation.x = target.x;
pBot->MoveLookLocation.y = target.y; pBot->MoveLookLocation.y = target.y;
pBot->MoveLookLocation.z = target.z; pBot->MoveLookLocation.z = target.z;
@ -126,10 +127,12 @@ enemy_status* GetTrackedEnemyRefForTarget(AvHAIPlayer* pBot, edict_t* Target)
return nullptr; return nullptr;
} }
void BotLookAt(AvHAIPlayer* pBot, edict_t* target) void BotLookAt(AvHAIPlayer* pBot, edict_t* target, bool bSnap)
{ {
if (FNullEnt(target)) { return; } if (FNullEnt(target)) { return; }
pBot->bSnapView = bSnap;
pBot->LookTarget = target; pBot->LookTarget = target;
// For team mates we don't track enemy refs, so just look at the friendly player // For team mates we don't track enemy refs, so just look at the friendly player
@ -1152,6 +1155,14 @@ void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot)
if (pBot->DesiredLookDirection.x > 180) if (pBot->DesiredLookDirection.x > 180)
pBot->DesiredLookDirection.x -= 360; pBot->DesiredLookDirection.x -= 360;
if (pBot->bSnapView)
{
pBot->ViewInterpolationSpeed = 1000.0f;
pBot->ViewInterpStartedTime = gpGlobals->time;
return;
}
// Now figure out how far we have to turn to reach our desired target // Now figure out how far we have to turn to reach our desired target
float yDelta = pBot->DesiredLookDirection.y - pBot->InterpolatedLookDirection.y; float yDelta = pBot->DesiredLookDirection.y - pBot->InterpolatedLookDirection.y;
float xDelta = pBot->DesiredLookDirection.x - pBot->InterpolatedLookDirection.x; float xDelta = pBot->DesiredLookDirection.x - pBot->InterpolatedLookDirection.x;
@ -1194,8 +1205,6 @@ void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot)
yOffset *= -1.0f; yOffset *= -1.0f;
} }
pBot->DesiredLookDirection.x += xOffset; pBot->DesiredLookDirection.x += xOffset;
pBot->DesiredLookDirection.y += yOffset; pBot->DesiredLookDirection.y += yOffset;
} }
@ -5741,7 +5750,7 @@ void TestNavThink(AvHAIPlayer* pBot)
pBot->CurrentTask = &pBot->PrimaryBotTask; pBot->CurrentTask = &pBot->PrimaryBotTask;
if (IsPlayerAlien(pBot->Edict) && IsPlayerSkulk(pBot->Edict)) if (IsPlayerAlien(pBot->Edict) && IsPlayerSkulk(pBot->Edict) && AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER1, pBot->Edict) > 0)
{ {
if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER2, pBot->Edict) == 0) if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER2, pBot->Edict) == 0)
{ {

View File

@ -26,9 +26,9 @@ static const float f_ffwidth = f_ffheight * BOT_ASPECT_RATIO;
void BotJump(AvHAIPlayer* pBot); void BotJump(AvHAIPlayer* pBot);
void BotSuicide(AvHAIPlayer* pBot); void BotSuicide(AvHAIPlayer* pBot);
void BotLookAt(AvHAIPlayer* pBot, Vector NewLocation); void BotLookAt(AvHAIPlayer* pBot, Vector NewLocation, bool bSnap = false);
void BotLookAt(AvHAIPlayer* pBot, edict_t* target); void BotLookAt(AvHAIPlayer* pBot, edict_t* target, bool bSnap = false);
void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target); void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target, bool bSnap = false);
void BotDirectLookAt(AvHAIPlayer* pBot, Vector target); void BotDirectLookAt(AvHAIPlayer* pBot, Vector target);
bool BotUseObject(AvHAIPlayer* pBot, edict_t* Target, bool bContinuous); bool BotUseObject(AvHAIPlayer* pBot, edict_t* Target, bool bContinuous);

View File

@ -2585,7 +2585,7 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
if (NearestHive) if (NearestHive)
{ {
if (NearestHive->Status == HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, StructureRef->Location) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) if (NearestHive->Status == HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, StructureRef->Location) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{ {
StructureRef->Purpose = STRUCTURE_PURPOSE_FORTIFY; StructureRef->Purpose = STRUCTURE_PURPOSE_FORTIFY;
} }
@ -5520,9 +5520,10 @@ bool AITAC_IsBotPursuingSquadObjective(AvHAIPlayer* pBot, AvHAISquad* Squad)
if (!pBot->CurrentTask || pBot->CurrentTask == &pBot->PrimaryBotTask) { return true; } if (!pBot->CurrentTask || pBot->CurrentTask == &pBot->PrimaryBotTask) { return true; }
// Bot isn't currently pursuing squad objective, so check if it's doing something in the vicinity // Bot isn't currently pursuing squad objective, so check if it's doing something in the vicinity
Vector TaskLocation = (!FNullEnt(pBot->CurrentTask->TaskTarget)) ? pBot->CurrentTask->TaskTarget->v.origin : pBot->CurrentTask->TaskLocation; Vector BotTaskLocation = (!FNullEnt(pBot->CurrentTask->TaskTarget)) ? pBot->CurrentTask->TaskTarget->v.origin : pBot->CurrentTask->TaskLocation;
Vector SquadTaskLocation = (!FNullEnt(Squad->SquadTarget)) ? Squad->SquadTarget->v.origin : Squad->ObjectiveLocation;
return vDist2DSq(TaskLocation, Squad->SquadTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)); return vDist2DSq(BotTaskLocation, SquadTaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f));
} }
void AITAC_ManageSquads() void AITAC_ManageSquads()

View File

@ -2814,11 +2814,14 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
StructureFilter.ReachabilityTeam = BotTeam; StructureFilter.ReachabilityTeam = BotTeam;
StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_FORTIFY;
vector<AvHAIBuildableStructure> BuildableStructures = AITAC_FindAllDeployables(Hive->FloorLocation, &StructureFilter); vector<AvHAIBuildableStructure> BuildableStructures = AITAC_FindAllDeployables(Hive->FloorLocation, &StructureFilter);
bool bKeyStructureBuilt = false; bool bKeyStructureBuilt = false;
AvHAIBuildableStructure KeyOutpostStructure;
AvHAIBuildableStructure StructureToBuild; AvHAIBuildableStructure StructureToBuild;
float MinDist = 0.0f; float MinDist = 0.0f;
@ -2828,6 +2831,7 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if ((ThisStructure.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED) && (ThisStructure.StructureType & (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE))) if ((ThisStructure.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED) && (ThisStructure.StructureType & (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE)))
{ {
KeyOutpostStructure = ThisStructure;
bKeyStructureBuilt = true; bKeyStructureBuilt = true;
} }
@ -2904,8 +2908,9 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
} }
} }
Vector OutpostLocation = (KeyOutpostStructure.IsValid()) ? KeyOutpostStructure.Location : Task->TaskLocation;
BotGuardLocation(pBot, Task->TaskLocation); BotGuardLocation(pBot, OutpostLocation);
} }