Improved bot stuck detection

This commit is contained in:
RGreenlees 2024-02-05 23:18:07 +00:00 committed by pierow
parent 16f34dc987
commit a747ed0187
12 changed files with 1177 additions and 249 deletions

View file

@ -61,9 +61,11 @@ bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDe
if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) { return false; }
bool theSuccess = (AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player) != NULL);
CBaseEntity* NewItem = AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player);
if (!theSuccess) { return false; }
if (!NewItem) { return false; }
AITAC_UpdateMarineItem(NewItem, ItemToDeploy);
pBot->Player->PayPurchaseCost(theCost);
@ -85,6 +87,20 @@ bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureTo
if (!StructureToResearch->EntityRef->GetIsTechnologyAvailable(Research)) { return false; }
AvHTeam* CommanderTeamRef = AIMGR_GetTeamRef(pBot->Player->GetTeam());
if (!CommanderTeamRef) { return false; }
AvHResearchManager& theResearchManager = CommanderTeamRef->GetResearchManager();
bool theIsResearchable = false;
int theResearchCost = 0.0f;
float theResearchTime = 0.0f;
theResearchManager.GetResearchInfo(Research, theIsResearchable, theResearchCost, theResearchTime);
if (pBot->Player->GetResources() < theResearchCost) { return false; }
pBot->Player->SetSelection(StructureIndex, true);
pBot->Button |= IN_ATTACK2;
@ -388,6 +404,7 @@ bool AICOMM_ShouldCommanderPrioritiseNodes(AvHAIPlayer* pBot)
int NumOwnedNodes = 0;
int NumEligibleNodes = 0;
int NumFreeNodes = 0;
// First get ours and the enemy's ownership of all eligible nodes (we can reach them, and they're in the enemy base)
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllReachableResourceNodes(BotTeam);
@ -399,6 +416,11 @@ bool AICOMM_ShouldCommanderPrioritiseNodes(AvHAIPlayer* pBot)
// We don't care about the node at marine spawn or enemy hives, ignore then in our calculations
if (ThisNode->OwningTeam == EnemyTeam && ThisNode->bIsBaseNode) { continue; }
if (ThisNode->OwningTeam == TEAM_IND)
{
NumFreeNodes++;
}
NumEligibleNodes++;
if (ThisNode->OwningTeam == BotTeam) { NumOwnedNodes++; }
@ -408,7 +430,7 @@ bool AICOMM_ShouldCommanderPrioritiseNodes(AvHAIPlayer* pBot)
if (NumNodesLeft == 0) { return false; }
return NumOwnedNodes < 3 || NumNodesLeft > 3;
return NumOwnedNodes < 3 || NumFreeNodes > 3;
}
@ -587,8 +609,11 @@ edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector Se
for (auto it = PlayerList.begin(); it != PlayerList.end();)
{
AvHPlayer* PlayerRef = (*it);
AvHAIPlayer* AIPlayerRef = AIMGR_GetBotRefFromPlayer(PlayerRef);
if (!IsPlayerActiveInGame(PlayerRef->edict()))
// Don't give orders to incapacitated players, or if the bot is currently playing a defensive role. Stops the commander sending everyone out
// and leaving nobody at base
if (!IsPlayerActiveInGame(PlayerRef->edict()) || (AIPlayerRef && AIPlayerRef->BotRole == BOT_ROLE_SWEEPER))
{
it = PlayerList.erase(it);
}
@ -1093,9 +1118,76 @@ bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
{
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
// First thing: if our base is damaged and there's nobody able to weld, drop a welder so we don't let the base die
bool bBaseIsDamaged = false;
DeployableSearchFilter DamagedBaseStructures;
DamagedBaseStructures.DeployableTypes = (STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_INFANTRYPORTAL);
DamagedBaseStructures.DeployableTeam = CommanderTeam;
DamagedBaseStructures.ReachabilityTeam = CommanderTeam;
DamagedBaseStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
DamagedBaseStructures.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
DamagedBaseStructures.IncludeStatusFlags = STRUCTURE_STATUS_DAMAGED;
DamagedBaseStructures.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
DamagedBaseStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
bBaseIsDamaged = AITAC_DeployableExistsAtLocation(AITAC_GetCommChairLocation(CommanderTeam), &DamagedBaseStructures);
if (bBaseIsDamaged)
{
AvHAIDroppedItem* NearestWelder = AITAC_FindClosestItemToLocation(AITAC_GetCommChairLocation(CommanderTeam), DEPLOYABLE_ITEM_WELDER, CommanderTeam, AI_REACHABILITY_MARINE, 0.0f, UTIL_MetresToGoldSrcUnits(15.0f), false);
bool bPlayerHasWelder = false;
if (!NearestWelder)
{
vector<AvHPlayer*> PlayersAtBase = AITAC_GetAllPlayersOfTeamInArea(CommanderTeam, AITAC_GetCommChairLocation(CommanderTeam), UTIL_MetresToGoldSrcUnits(15.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
for (auto it = PlayersAtBase.begin(); it != PlayersAtBase.end(); it++)
{
AvHPlayer* ThisPlayer = (*it);
if (PlayerHasWeapon(ThisPlayer, WEAPON_MARINE_WELDER))
{
bPlayerHasWelder = true;
}
}
}
if (!NearestWelder && !bPlayerHasWelder)
{
DeployableSearchFilter ArmouryFilter;
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
ArmouryFilter.DeployableTeam = CommanderTeam;
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* NearestArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &ArmouryFilter);
if (NearestArmoury)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_WELDER, DeployLocation);
return bSuccess;
}
}
}
// Now work out how many welders we want on the team generally
int NumDesiredWelders = 1;
if (!AICOMM_ShouldCommanderPrioritiseNodes(pBot))
{
NumDesiredWelders = (int)ceilf((float)AIMGR_GetNumPlayersOnTeam(CommanderTeam) * 0.3f);
return false;
}
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
// Add additional welders to the team if we have hives or resource nodes which can only be reached with a welder
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
@ -1127,7 +1219,7 @@ bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
}
}
NumDesiredWelders = imini(NumDesiredWelders, (AIMGR_GetNumPlayersOnTeam(CommanderTeam) / 2));
NumDesiredWelders = imini(NumDesiredWelders, (int)(ceilf((float)AIMGR_GetNumPlayersOnTeam(CommanderTeam) * 0.5f)));
if (NumTeamWelders < NumDesiredWelders)
{
@ -1149,9 +1241,7 @@ bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
}
// Don't drop stuff if we badly need resource nodes
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot)) { return false; }
if (pBot->Player->GetResources() < 30) { return false; }
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot) && pBot->Player->GetResources() < 20) { return false; }
int NumDesiredShotguns = (int)ceilf(AIMGR_GetNumPlayersOnTeam(CommanderTeam) * 0.33f);
@ -1232,9 +1322,94 @@ bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot)
}
}
if (AITAC_ResearchIsComplete(CommanderTeam, TECH_RESEARCH_HEAVYARMOR))
{
if (!AITAC_ResearchIsComplete(CommanderTeam, TECH_RESEARCH_HEAVYARMOR)) { return false; }
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ADVARMOURY;
StructureFilter.DeployableTeam = CommanderTeam;
StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
AvHAIBuildableStructure* NearestAdvArmoury = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &StructureFilter);
StructureFilter.DeployableTypes = STRUCTURE_MARINE_PROTOTYPELAB;
AvHAIBuildableStructure* NearestPrototypeLab = AITAC_FindClosestDeployableToLocation(AITAC_GetTeamStartingLocation(CommanderTeam), &StructureFilter);
if (!NearestAdvArmoury || !NearestPrototypeLab) { return false; }
AvHAIDroppedItem* ExistingHA = AITAC_FindClosestItemToLocation(NearestPrototypeLab->Location, DEPLOYABLE_ITEM_HEAVYARMOUR, CommanderTeam, AI_REACHABILITY_MARINE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false);
AvHAIDroppedItem* ExistingHMG = AITAC_FindClosestItemToLocation(NearestAdvArmoury->Location, DEPLOYABLE_ITEM_HMG, CommanderTeam, AI_REACHABILITY_MARINE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false);
AvHAIDroppedItem* ExistingWelder = AITAC_FindClosestItemToLocation(NearestAdvArmoury->Location, DEPLOYABLE_ITEM_WELDER, CommanderTeam, AI_REACHABILITY_MARINE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false);
if (ExistingHA && ExistingHMG && ExistingWelder) { return false; }
vector<edict_t*> NearbyPlayers = AITAC_GetAllPlayersOfClassInArea(CommanderTeam, NearestAdvArmoury->Location, UTIL_MetresToGoldSrcUnits(10.0f), false, pBot->Edict, AVH_USER3_MARINE_PLAYER);
bool bDropWeapon = false;
bool bDropWelder = false;
for (auto it = NearbyPlayers.begin(); it != NearbyPlayers.end(); it++)
{
edict_t* PlayerEdict = (*it);
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
if (!PlayerEdict) { continue; }
if (PlayerHasHeavyArmour(PlayerEdict) || PlayerHasJetpack(PlayerEdict))
{
if (PlayerHasWeapon(PlayerRef, WEAPON_MARINE_MG) || UTIL_GetPlayerPrimaryWeapon(PlayerRef) == WEAPON_INVALID)
{
bDropWeapon = true;
}
else
{
if (!PlayerHasWeapon(PlayerRef, WEAPON_MARINE_WELDER))
{
bDropWelder = true;
}
}
}
}
if (!ExistingHA && !bDropWelder && !bDropWeapon)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestPrototypeLab->Location, UTIL_MetresToGoldSrcUnits(3.0f));
if (vIsZero(DeployLocation))
{
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestPrototypeLab->Location, UTIL_MetresToGoldSrcUnits(3.0f));
}
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_HEAVYARMOUR, DeployLocation);
return bSuccess;
}
if (bDropWeapon && !ExistingHMG)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestAdvArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
if (vIsZero(DeployLocation))
{
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestAdvArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
}
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_HMG, DeployLocation);
return bSuccess;
}
if (bDropWelder && !ExistingWelder)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestAdvArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
if (vIsZero(DeployLocation))
{
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestAdvArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f));
}
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_WELDER, DeployLocation);
return bSuccess;
}
return false;
@ -1649,17 +1824,17 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
{
SiegeLocation = ExistingTF->Location;
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF->Location, UTIL_MetresToGoldSrcUnits(5.0f));
if (vIsZero(NextBuildPosition))
{
// Reduce radius to avoid putting it on the other side of a wall or something
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF->Location, 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
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingTF->Location, UTIL_MetresToGoldSrcUnits(5.0f));
}
}

View file

@ -103,6 +103,7 @@ typedef enum
STRUCTURE_STATUS_PARASITED = 1 << 3,
STRUCTURE_STATUS_UNDERATTACK = 1 << 4,
STRUCTURE_STATUS_RESEARCHING = 1 << 5,
STRUCTURE_STATUS_DAMAGED = 1 << 6,
STRUCTURE_STATUS_ALL = -1
} AvHAIStructureStatus;
@ -536,6 +537,14 @@ typedef struct _AVH_AI_PLAYER_MOVE_TASK
bool bPathGenerated = false;
} AvHAIPlayerMoveTask;
typedef struct _AVH_AI_STUCK_TRACKER
{
Vector LastBotPosition = g_vecZero;
Vector MoveDestination = g_vecZero;
float TotalStuckTime = 0.0f; // Total time the bot has spent stuck
} AvHAIPlayerStuckTracker;
// Contains the bot's current navigation info, such as current path
typedef struct _NAV_STATUS
{
@ -559,8 +568,6 @@ typedef struct _NAV_STATUS
Vector StuckCheckMoveLocation = g_vecZero; // Where is the bot trying to go that we're checking if they're stuck?
Vector UnstuckMoveLocation = g_vecZero; // If the bot is unable to find a path, blindly move here to try and fix the problem
Vector UnstuckMoveStartLocation = g_vecZero; // So the bot can track where it was when it started the unstuck movement
float UnstuckMoveLocationStartTime = 0.0f; // When did the bot start trying to move to UnstuckMoveLocation? Give up after certain amount of time
float LandedTime = 0.0f; // When the bot last landed after a fall/jump.
float LeapAttemptedTime = 0.0f; // When the bot last attempted to leap/blink. Avoid spam that sends it flying around too fast
@ -583,6 +590,8 @@ typedef struct _NAV_STATUS
nav_profile NavProfile;
bool bNavProfileChanged = false;
AvHAIPlayerStuckTracker StuckInfo;
unsigned int SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.)
AvHAIPlayerMoveTask MovementTask;

View file

@ -158,9 +158,9 @@ Vector UTIL_GetFloorUnderEntity(const edict_t* Edict)
TraceResult hit;
Vector EntityCentre = UTIL_GetCentreOfEntity(Edict);
Vector EntityCentre = UTIL_GetCentreOfEntity(Edict) + Vector(0.0f, 0.0f, 1.0f);
UTIL_TraceHull(EntityCentre, (EntityCentre - Vector(0.0f, 0.0f, 1000.0f)), ignore_monsters, GetPlayerHullIndex(Edict), Edict->v.pContainingEntity, &hit);
UTIL_TraceHull(EntityCentre, (EntityCentre - Vector(0.0f, 0.0f, 1000.0f)), ignore_monsters, head_hull, Edict->v.pContainingEntity, &hit);
if (hit.flFraction < 1.0f)
{

View file

@ -442,6 +442,8 @@ unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector L
void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure)
{
if (Structure->StructureType == STRUCTURE_MARINE_DEPLOYEDMINE) { return; }
bool bCollideWithPlayers = UTIL_ShouldStructureCollide(Structure->StructureType);
float Radius = UTIL_GetStructureRadiusForObstruction(Structure->StructureType);
@ -3821,6 +3823,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
void PhaseGateMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
DeployableSearchFilter PGFilter;
PGFilter.DeployableTeam = pBot->Player->GetTeam();
PGFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
PGFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(2.0f);
PGFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
@ -3945,7 +3948,7 @@ bool IsBotOffPath(const AvHAIPlayer* pBot)
{
Vector ExactJumpTarget = UTIL_GetGroundLocation(MoveTo);
if (pBot->BotNavInfo.IsOnGround && (MoveTo.z - pBot->CurrentFloorPosition.z) > max_player_jump_height)
if (pBot->BotNavInfo.IsOnGround && (ExactJumpTarget.z - pBot->CurrentFloorPosition.z) > max_player_jump_height)
{
return true;
}
@ -4256,6 +4259,7 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination)
HandlePlayerAvoidance(pBot, Destination);
BotMovementInputs(pBot);
}
@ -4952,12 +4956,6 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
{
if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.NavProfile.bFlyingProfile) { return true; }
if (IsBotPermaStuck(pBot))
{
BotSuicide(pBot);
return false;
}
Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation;
Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location;
unsigned int flag = pBot->BotNavInfo.CurrentPathPoint->flag;
@ -5092,20 +5090,37 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
return false;
}
bool IsBotPermaStuck(AvHAIPlayer* pBot)
void UpdateBotStuck(AvHAIPlayer* pBot)
{
if (MAX_BOT_STUCK_TIME <= 0.1f) { return false; }
if (vIsZero(pBot->LastPosition) || vDist3DSq(pBot->Edict->v.origin, pBot->LastPosition) > sqrf(32.0f))
if (vIsZero(pBot->desiredMovementDir))
{
pBot->TimeSinceLastMovement = 0.0f;
pBot->LastPosition = pBot->Edict->v.origin;
return false;
return;
}
pBot->TimeSinceLastMovement += AIMGR_GetBotDeltaTime();
bool bIsFollowingPath = (pBot->BotNavInfo.CurrentPath.size() > 0 && pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end());
return (pBot->TimeSinceLastMovement >= 30.0f);
bool bDist3D = pBot->BotNavInfo.NavProfile.bFlyingProfile || (bIsFollowingPath && (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LADDER || pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_WALLCLIMB));
float DistFromLastPoint = (bDist3D) ? vDist3DSq(pBot->Edict->v.origin, pBot->BotNavInfo.StuckInfo.LastBotPosition) : vDist2DSq(pBot->Edict->v.origin, pBot->BotNavInfo.StuckInfo.LastBotPosition);
if (DistFromLastPoint >= sqrf(8.0f))
{
pBot->BotNavInfo.StuckInfo.TotalStuckTime = 0.0f;
pBot->BotNavInfo.StuckInfo.LastBotPosition = pBot->Edict->v.origin;
}
else
{
pBot->BotNavInfo.StuckInfo.TotalStuckTime += AIMGR_GetBotDeltaTime();
}
if (pBot->BotNavInfo.StuckInfo.TotalStuckTime > 0.25f)
{
BotJump(pBot);
if (!IsPlayerSkulk(pBot->Edict))
{
pBot->Button |= IN_DUCK;
}
}
}
void SetBaseNavProfile(AvHAIPlayer* pBot)
@ -5355,7 +5370,7 @@ void OnosUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle)
bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle MoveStyle, const float MaxAcceptableDist)
{
if (vIsZero(Destination) || (vDist2D(pBot->Edict->v.origin, Destination) <= 8.0f && (fabs(pBot->CollisionHullBottomLocation.z - Destination.z) < 50.0f)))
if (vIsZero(Destination) || (vDist2D(pBot->Edict->v.origin, Destination) <= 6.0f && (fabs(pBot->CollisionHullBottomLocation.z - Destination.z) < 50.0f)))
{
ClearBotMovement(pBot);
return true;
@ -5449,12 +5464,6 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
}
}
if (IsBotPermaStuck(pBot))
{
BotSuicide(pBot);
return false;
}
ClearBotPath(pBot);
return false;
}
@ -5494,12 +5503,6 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
}
}
if (IsBotPermaStuck(pBot))
{
BotSuicide(pBot);
return false;
}
if (pBot->Edict->v.flags & FL_INWATER)
{
BotFollowSwimPath(pBot);
@ -5633,7 +5636,11 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot)
void BotFollowFlightPath(AvHAIPlayer* pBot)
{
if (pBot->BotNavInfo.CurrentPath.size() == 0) { return; }
if (pBot->BotNavInfo.CurrentPath.size() == 0)
{
ClearBotStuck(pBot);
return;
}
nav_status* BotNavInfo = &pBot->BotNavInfo;
edict_t* pEdict = pBot->Edict;
@ -5680,15 +5687,6 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
Vector MoveDir = UTIL_GetVectorNormal(CurrentMoveDest - MoveFrom);
if (IsBotStuck(pBot, CurrentMoveDest))
{
if (BotNavInfo->TotalStuckTime > 3.0f)
{
ClearBotPath(pBot);
return;
}
}
float CurrentSpeed = vSize3D(pEdict->v.velocity);
if (vDist2DSq(pEdict->v.origin, MoveFrom) > sqrf(100.0f) && vDist2DSq(pEdict->v.origin, CurrentMoveDest) > sqrf(100.0f))
@ -5854,6 +5852,7 @@ void BotFollowPath(AvHAIPlayer* pBot)
nav_status* BotNavInfo = &pBot->BotNavInfo;
edict_t* pEdict = pBot->Edict;
// If we've reached our current path point
if (HasBotReachedPathPoint(pBot))
{
@ -5877,33 +5876,8 @@ void BotFollowPath(AvHAIPlayer* pBot)
Vector MoveTo = BotNavInfo->CurrentPathPoint->Location;
unsigned int CurrentFlag = BotNavInfo->CurrentPathPoint->flag;
bool bIsUsingPhaseGate = (CurrentFlag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || CurrentFlag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
NewMove(pBot);
if (!bIsUsingPhaseGate && IsBotStuck(pBot, MoveTo))
{
if (BotNavInfo->TotalStuckTime > 3.0f)
{
ClearBotPath(pBot);
return;
}
// If onos, ducking is usually a good way to get unstuck...
if (IsPlayerOnos(pBot->Edict))
{
pBot->Button |= IN_DUCK;
}
if (!IsPlayerClimbingWall(pBot->Edict) && !IsPlayerOnLadder(pBot->Edict))
{
PerformUnstuckMove(pBot, MoveTo);
return;
}
}
}
void PerformUnstuckMove(AvHAIPlayer* pBot, const Vector MoveDestination)
@ -5971,9 +5945,17 @@ void PerformUnstuckMove(AvHAIPlayer* pBot, const Vector MoveDestination)
bool IsBotStuck(AvHAIPlayer* pBot, const Vector MoveDestination)
{
// If invalid move destination then bail out
if (vIsZero(MoveDestination) || vIsZero(pBot->desiredMovementDir)) { return false; }
if (vIsZero(MoveDestination) || vDist2DSq(pBot->Edict->v.origin, MoveDestination) < sqrf(32.0f))
{
pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time;
return false;
}
if (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LIFT) { return false; }
if (pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_LIFT || pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || pBot->BotNavInfo.CurrentPathPoint->flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE)
{
pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time;
return false;
}
// If moving to a new destination set a new distance baseline. We do not reset the stuck timer
if (MoveDestination != pBot->BotNavInfo.StuckCheckMoveLocation)
@ -5985,7 +5967,7 @@ bool IsBotStuck(AvHAIPlayer* pBot, const Vector MoveDestination)
}
// If first time performing a stuck check, set a baseline time and return false
if (pBot->BotNavInfo.LastStuckCheckTime == 0.0f)
if (pBot->BotNavInfo.LastStuckCheckTime == 0.0f || gpGlobals->time - pBot->BotNavInfo.LastStuckCheckTime > 3.0f)
{
pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time;
return false;
@ -6381,8 +6363,6 @@ void ClearBotStuck(AvHAIPlayer* pBot)
pBot->BotNavInfo.LastStuckCheckTime = gpGlobals->time;
pBot->BotNavInfo.TotalStuckTime = 0.0f;
pBot->BotNavInfo.UnstuckMoveLocation = g_vecZero;
pBot->BotNavInfo.UnstuckMoveLocationStartTime = 0.0f;
pBot->BotNavInfo.UnstuckMoveStartLocation = g_vecZero;
pBot->BotNavInfo.StuckCheckMoveLocation = g_vecZero;
}
@ -6677,8 +6657,6 @@ void ClearBotPath(AvHAIPlayer* pBot)
void ClearBotStuckMovement(AvHAIPlayer* pBot)
{
pBot->BotNavInfo.UnstuckMoveLocation = g_vecZero;
pBot->BotNavInfo.UnstuckMoveLocationStartTime = 0.0f;
pBot->BotNavInfo.UnstuckMoveStartLocation = g_vecZero;
}
void BotMovementInputs(AvHAIPlayer* pBot)

View file

@ -309,6 +309,8 @@ bool BotRecalcPath(AvHAIPlayer* pBot, const Vector Destination);
*/
bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle MoveStyle, const float MaxAcceptableDist = max_ai_use_reach);
void UpdateBotStuck(AvHAIPlayer* pBot);
// Used by the MoveTo command, handles the bot's movement and inputs to follow a path it has calculated for itself
void BotFollowPath(AvHAIPlayer* pBot);
void BotFollowFlightPath(AvHAIPlayer* pBot);
@ -316,8 +318,6 @@ void BotFollowSwimPath(AvHAIPlayer* pBot);
void SkipAheadInFlightPath(AvHAIPlayer* pBot);
// If the bot has been unable to move more than 32 units in the last MaxStuckTime seconds (must be trying to move somewhere) then returns true
bool IsBotPermaStuck(AvHAIPlayer* pBot);
// Walks directly towards the destination. No path finding, just raw movement input. Will detect obstacles and try to jump/duck under them.
void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination);

File diff suppressed because it is too large Load diff

View file

@ -597,7 +597,7 @@ void AIMGR_UpdateAIPlayers()
UpdateBotChat(bot);
AIPlayerThink(bot);
DroneThink(bot);
BotUpdateDesiredViewRotation(bot);
}
@ -659,6 +659,11 @@ AvHTeamNumber AIMGR_GetTeamBNumber()
return GetGameRules()->GetTeamANumber();
}
AvHTeam* AIMGR_GetTeamRef(const AvHTeamNumber Team)
{
return GetGameRules()->GetTeam(Team);
}
vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team)
{
vector<AvHPlayer*> Result;

View file

@ -72,6 +72,8 @@ AvHClassType AIMGR_GetTeamType(const AvHTeamNumber Team);
AvHTeamNumber AIMGR_GetTeamANumber();
AvHTeamNumber AIMGR_GetTeamBNumber();
AvHTeam* AIMGR_GetTeamRef(const AvHTeamNumber Team);
// Returns all NS AI players. Does not include third-party bots
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers();
// Returns all NS AI players on the requested team. Does not include third-party bots

View file

@ -1760,6 +1760,13 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
AITAC_RefreshHiveData();
}
if (Structure->StructureType == STRUCTURE_MARINE_DEPLOYEDMINE)
{
Structure->TeamAReachabilityFlags = AI_REACHABILITY_ALL;
Structure->TeamBReachabilityFlags = AI_REACHABILITY_ALL;
return;
}
Structure->bReachabilityMarkedDirty = false;
Structure->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
@ -1891,23 +1898,47 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
{
if (!Structure || (Structure->pev->effects & EF_NODRAW) || (Structure->pev->deadflag != DEAD_NO)) { return nullptr; }
AvHBaseBuildable* BaseBuildable = dynamic_cast<AvHBaseBuildable*>(Structure);
edict_t* BuildingEdict = Structure->edict();
if (!BaseBuildable) { return nullptr; }
edict_t* BuildingEdict = BaseBuildable->edict();
AvHAIDeployableStructureType StructureType = UTIL_IUSER3ToStructureType(BaseBuildable->pev->iuser3);
AvHAIDeployableStructureType StructureType = UTIL_IUSER3ToStructureType(BuildingEdict->v.iuser3);
if (StructureType == STRUCTURE_NONE) { return nullptr; }
int EntIndex = BaseBuildable->entindex();
int EntIndex = ENTINDEX(BuildingEdict);
if (EntIndex < 0) { return nullptr; }
AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber();
AvHTeamNumber TeamBNumber = GetGameRules()->GetTeamBNumber();
std::unordered_map<int, AvHAIBuildableStructure>& BuildingMap = (BaseBuildable->GetTeamNumber() == TeamANumber) ? TeamAStructureMap : TeamBStructureMap;
std::unordered_map<int, AvHAIBuildableStructure>& BuildingMap = ((AvHTeamNumber)BuildingEdict->v.team == TeamANumber) ? TeamAStructureMap : TeamBStructureMap;
if (StructureType == STRUCTURE_MARINE_DEPLOYEDMINE)
{
BuildingMap[EntIndex].StructureType = StructureType;
if (BuildingMap[EntIndex].LastSeen == 0)
{
BuildingMap[EntIndex].Location = BuildingEdict->v.origin;
BuildingMap[EntIndex].edict = BuildingEdict;
BuildingMap[EntIndex].healthPercent = 1.0f;
BuildingMap[EntIndex].EntityRef = nullptr;
BuildingMap[EntIndex].StructureStatusFlags = STRUCTURE_STATUS_COMPLETED;
BuildingMap[EntIndex].TeamAReachabilityFlags = AI_REACHABILITY_ALL;
BuildingMap[EntIndex].TeamBReachabilityFlags = AI_REACHABILITY_ALL;
AITAC_OnStructureCreated(&BuildingMap[EntIndex]);
}
BuildingMap[EntIndex].LastSeen = StructureRefreshFrame;
return &BuildingMap[EntIndex];
}
AvHBaseBuildable* BaseBuildable = dynamic_cast<AvHBaseBuildable*>(Structure);
if (!BaseBuildable)
{
return nullptr;
}
BuildingMap[EntIndex].StructureType = StructureType;
@ -1980,6 +2011,11 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
BuildingMap[EntIndex].healthPercent = NewHealthPercent;
if (BuildingMap[EntIndex].healthPercent < 0.99f && BaseBuildable->GetIsBuilt())
{
NewFlags |= STRUCTURE_STATUS_DAMAGED;
}
if (gpGlobals->time - BuildingMap[EntIndex].lastDamagedTime < 10.0f)
{
NewFlags |= STRUCTURE_STATUS_UNDERATTACK;
@ -2008,7 +2044,7 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure)
UTIL_AddStructureTemporaryObstacles(NewStructure);
AvHTeamNumber StructureTeam = NewStructure->EntityRef->GetTeamNumber();
AvHTeamNumber StructureTeam = (AvHTeamNumber)NewStructure->edict->v.team;
AITAC_RefreshReachabilityForStructure(NewStructure);
@ -2018,9 +2054,9 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure)
if (!Team) { return; }
if (Team->GetTeamType() == AVH_CLASS_TYPE_ALIEN)
if (Team->GetTeamType() == AVH_CLASS_TYPE_ALIEN || NewStructure->StructureType == STRUCTURE_MARINE_DEPLOYEDMINE)
{
AITAC_LinkAlienStructureToPlayer(NewStructure);
AITAC_LinkStructureToPlayer(NewStructure);
}
}
@ -2156,7 +2192,7 @@ void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure)
}
}
void AITAC_LinkAlienStructureToPlayer(AvHAIBuildableStructure* NewStructure)
void AITAC_LinkStructureToPlayer(AvHAIBuildableStructure* NewStructure)
{
vector<AvHAIPlayer*> AllTeamPlayers = AIMGR_GetAIPlayersOnTeam((AvHTeamNumber)NewStructure->edict->v.team);
@ -2370,6 +2406,8 @@ float UTIL_GetStructureRadiusForObstruction(AvHAIDeployableStructureType Structu
return 60.0f;
case STRUCTURE_MARINE_TURRET:
return 30.0f;
case STRUCTURE_MARINE_DEPLOYEDMINE:
return 12.0f;
default:
return 40.0f;
@ -3120,24 +3158,18 @@ Vector UTIL_GetNextMinePosition(edict_t* StructureToMine)
bool bBack = false;
bool bLeft = false;
int NumMines = 0;
DeployableSearchFilter MineFilter;
MineFilter.DeployableTeam = StructureTeam;
MineFilter.DeployableTypes = STRUCTURE_MARINE_DEPLOYEDMINE;
MineFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(3.0f);
AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber();
AvHTeamNumber TeamBNumber = GetGameRules()->GetTeamBNumber();
vector<AvHAIBuildableStructure*> SurroundingMines = AITAC_FindAllDeployables(StructureToMine->v.origin, &MineFilter);
std::unordered_map<int, AvHAIBuildableStructure>& BuildingMap = (StructureTeam == TeamANumber) ? TeamAStructureMap : TeamBStructureMap;
for (auto& it : BuildingMap)
for (auto it = SurroundingMines.begin(); it != SurroundingMines.end(); it++)
{
unsigned int TeamReachabilityFlag = (StructureTeam == TeamANumber) ? it.second.TeamAReachabilityFlags : it.second.TeamBReachabilityFlags;
AvHAIBuildableStructure* ThisMine = (*it);
if (it.second.StructureType != STRUCTURE_MARINE_DEPLOYEDMINE || !(TeamReachabilityFlag & AI_REACHABILITY_MARINE)) { continue; }
if (vDist2DSq(StructureToMine->v.origin, it.second.Location) > sqrf(UTIL_MetresToGoldSrcUnits(2.0f))) { continue; }
NumMines++;
Vector Dir = UTIL_GetVectorNormal2D(it.second.Location - StructureToMine->v.origin);
Vector Dir = UTIL_GetVectorNormal2D(ThisMine->Location - StructureToMine->v.origin);
if (UTIL_GetDotProduct2D(FwdVector, Dir) > 0.7f)
{
@ -3158,7 +3190,6 @@ Vector UTIL_GetNextMinePosition(edict_t* StructureToMine)
{
bLeft = true;
}
}
float Size = fmaxf(StructureToMine->v.size.x, StructureToMine->v.size.y);
@ -3212,7 +3243,7 @@ Vector UTIL_GetNextMinePosition(edict_t* StructureToMine)
}
}
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInDonut(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], StructureToMine->v.origin, Size, Size + 16.0f);
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), StructureToMine->v.origin, Size);
return BuildLocation;
}
@ -3415,6 +3446,35 @@ bool AITAC_AnyPlayerOnTeamHasLOSToLocation(AvHTeamNumber Team, const Vector& Loc
return false;
}
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer)
{
vector<AvHPlayer*> Results;
float distSq = sqrf(SearchRadius);
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
edict_t* PlayerEdict = INDEXENT(i);
if (!FNullEnt(PlayerEdict) && PlayerEdict != IgnorePlayer && PlayerEdict->v.team == Team && IsPlayerActiveInGame(PlayerEdict))
{
float ThisDist = vDist2DSq(PlayerEdict->v.origin, Location);
if (ThisDist <= distSq && UTIL_QuickTrace(PlayerEdict, GetPlayerEyePosition(PlayerEdict), Location))
{
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
if (PlayerRef)
{
Results.push_back(PlayerRef);
}
}
}
}
return Results;
}
bool AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer)
{
int Result = 0;
@ -3591,6 +3651,9 @@ const AvHAIHiveDefinition* AITAC_GetNearestHiveUnderActiveSiege(AvHTeamNumber Si
AvHAIHiveDefinition* Result = nullptr;
float MinDist = 0.0f;
// Only marines can siege, so return nothing if the enemy are not marines
if (AIMGR_GetTeamType(SiegingTeam) != AVH_CLASS_TYPE_MARINE) { return nullptr; }
for (auto it = Hives.begin(); it != Hives.end(); it++)
{
if (it->Status == HIVE_STATUS_UNBUILT) { continue; }

View file

@ -45,7 +45,7 @@ void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure);
void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure);
void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure);
void AITAC_LinkDeployedItemToAction(AvHAIPlayer* CommanderBot, const AvHAIDroppedItem* NewItem);
void AITAC_LinkAlienStructureToPlayer(AvHAIBuildableStructure* NewStructure);
void AITAC_LinkStructureToPlayer(AvHAIBuildableStructure* NewStructure);
float AITAC_GetPhaseDistanceBetweenPoints(const Vector StartPoint, const Vector EndPoint);
@ -80,6 +80,7 @@ AvHMessageID UTIL_ItemTypeToImpulseCommand(const AvHAIDeployableItemType ItemTyp
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_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);
bool AITAC_ShouldBotBeCautious(AvHAIPlayer* pBot);
// Clears out the marine and alien buildable structure maps, resource node and hive lists, and the marine item list

View file

@ -1129,39 +1129,25 @@ void BotProgressPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
float DistToPlaceLocation = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation);
if (DistToPlaceLocation < sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING)
{
pBot->DesiredCombatWeapon = WEAPON_MARINE_MINES;
}
if (!FNullEnt(Task->TaskSecondaryTarget))
{
Task->TaskLocation = g_vecZero;
Task->TaskSecondaryTarget = nullptr;
Task->BuildAttempts = 0;
return;
}
if (Task->bIsWaitingForBuildLink)
if (pBot->ActiveBuildInfo.BuildStatus != BUILD_ATTEMPT_NONE)
{
if (gpGlobals->time - Task->LastBuildAttemptTime > 1.0f)
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_FAILED)
{
Task->bIsWaitingForBuildLink = false;
if (Task->BuildAttempts > 3)
{
float Size = fmaxf(Task->TaskTarget->v.size.x, Task->TaskTarget->v.size.y);
Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE], Task->TaskTarget->v.origin, Size + 8.0f);
}
else
{
Vector Dir = UTIL_GetVectorNormal2D(Task->TaskLocation - Task->TaskTarget->v.origin);
Task->TaskLocation = Task->TaskLocation + (Dir * 8.0f);
}
float Size = fmaxf(Task->TaskTarget->v.size.x, Task->TaskTarget->v.size.y);
Size += 8.0f;
Task->TaskLocation = UTIL_GetRandomPointOnNavmeshInDonut(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), Task->TaskTarget->v.origin, Size, Size + 8.0f);
}
return;
else
{
Task->TaskLocation = ZERO_VECTOR;
}
pBot->ActiveBuildInfo.BuildStatus = BUILD_ATTEMPT_NONE;
}
if (vIsZero(Task->TaskLocation))
@ -1173,22 +1159,23 @@ void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
AITASK_ClearBotTask(pBot, Task);
return;
}
}
if (DistToPlaceLocation < sqrf(16.0f))
{
Vector MoveDir = UTIL_GetVectorNormal2D(Task->TaskLocation - pBot->Edict->v.origin);
MoveDirectlyTo(pBot, Task->TaskLocation - (MoveDir * 28.0f));
return;
}
if (DistToPlaceLocation > sqrf(32.0f))
float DistToPlaceLocation = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation);
if (DistToPlaceLocation < sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
{
pBot->DesiredCombatWeapon = WEAPON_MARINE_MINES;
}
if (DistToPlaceLocation > sqrf(8.0f))
{
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL);
return;
}
BotLookAt(pBot, Task->TaskLocation);
BotDirectLookAt(pBot, Task->TaskLocation);
if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_MARINE_MINES)
{
@ -1197,9 +1184,11 @@ void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (LookDot > 0.95f)
{
pBot->Button |= IN_ATTACK;
Task->LastBuildAttemptTime = gpGlobals->time;
Task->BuildAttempts++;
Task->bIsWaitingForBuildLink = true;
pBot->ActiveBuildInfo.AttemptedLocation = Task->TaskLocation;
pBot->ActiveBuildInfo.BuildStatus = BUILD_ATTEMPT_PENDING;
pBot->ActiveBuildInfo.BuildAttemptTime = gpGlobals->time;
pBot->ActiveBuildInfo.AttemptedStructureType = STRUCTURE_MARINE_DEPLOYEDMINE;
pBot->ActiveBuildInfo.NumAttempts++;
}
}
}
@ -1921,11 +1910,6 @@ void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
return;
}
if (!vIsZero(Task->TaskLocation))
{
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, Task->TaskLocation);
}
// We tried and failed to place the structure
if (pBot->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_FAILED)
{
@ -2393,12 +2377,21 @@ void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (IsPlayerInUseRange(pBot->Edict, Task->TaskTarget))
{
Vector BBMin = Task->TaskTarget->v.absmin;
Vector BBMax = Task->TaskTarget->v.absmax;
Vector AimLocation = UTIL_GetCentreOfEntity(Task->TaskTarget);
vScaleBB(BBMin, BBMax, 0.75f);
// If we're targeting a func_weldable, then the centre of the entity might be in a wall or out of reach
// so instead aim at the closest point on the func_weldable to us.
if (!IsEdictPlayer(Task->TaskTarget) && !IsEdictStructure(Task->TaskTarget))
{
Vector BBMin = Task->TaskTarget->v.absmin;
Vector BBMax = Task->TaskTarget->v.absmax;
BotLookAt(pBot, vClosestPointOnBB(pBot->CurrentEyePosition, BBMin, BBMax));
vScaleBB(BBMin, BBMax, 0.75f);
AimLocation = vClosestPointOnBB(pBot->CurrentEyePosition, BBMin, BBMax);
}
BotLookAt(pBot, AimLocation);
pBot->DesiredCombatWeapon = WEAPON_MARINE_WELDER;
if (GetPlayerCurrentWeapon(pBot->Player) != WEAPON_MARINE_WELDER)

View file

@ -386,6 +386,7 @@ float GetMaxIdealWeaponRange(const AvHAIWeapon Weapon)
case WEAPON_SKULK_PARASITE:
case WEAPON_SKULK_LEAP:
case WEAPON_ONOS_CHARGE:
case WEAPON_GORGE_SPIT:
return UTIL_MetresToGoldSrcUnits(20.0f);
case WEAPON_MARINE_HMG:
case WEAPON_MARINE_GRENADE: