Door and movement improvements

* Fixed buttons having overly long wait times with bots
* Fixed issue with climbing walls in certain situations
This commit is contained in:
RGreenlees 2024-03-23 23:10:52 +00:00 committed by pierow
parent 3daa2f32e4
commit f391176841
4 changed files with 269 additions and 67 deletions

View file

@ -276,7 +276,7 @@ void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Orde
}
else
{
Vector MoveLoc = (bIsSiegeHiveOrder) ? UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Hive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(25.0f)) : Hive->FloorLocation;
Vector MoveLoc = (bIsSiegeHiveOrder) ? UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Hive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(20.0f)) : Hive->FloorLocation;
AICOMM_IssueMovementOrder(pBot, Order->Assignee, MoveLoc);
Order->LastReminderTime = gpGlobals->time;
@ -1972,7 +1972,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
{
Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
if (!vIsZero(NextBuildPosition))
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
{
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
@ -1981,7 +1981,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition))
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
{
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
@ -1990,7 +1990,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(NextBuildPosition))
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
{
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);

View file

@ -159,7 +159,7 @@ void CONFIG_ParseConfigFile()
while (getline(cFile, line))
{
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
line.erase(std::remove_if(line.begin(), line.end(), isspace),
line.end());
if (line[0] == '#' || line.empty())
continue;
@ -468,8 +468,8 @@ void CONFIG_RegenerateIniFile()
fprintf(NewConfigFile, "### Skill Settings ###\n\n");
fprintf(NewConfigFile, "# Bot skill settings. You can define as many settings as you like and reference them by name\n");
fprintf(NewConfigFile, "# Format is BotSkillName = name, followed by one of the following:\n");
fprintf(NewConfigFile, "# Bot skill settings. There are 4 settings from 0 - 3 for easiest - hardest\n");
fprintf(NewConfigFile, "# Use BotSkillName=<index> to start defining a skill level, then the following:\n");
fprintf(NewConfigFile, "# ReactionTime = How quickly in seconds the bot will react to sighting enemies\n");
fprintf(NewConfigFile, "# AimSkill = How accurately the bot can lock sights on you after seeing you (0.0 - 1.0)\n");
fprintf(NewConfigFile, "# MovementTracking = How accurately the bot can follow a moving target (0.0 - 1.0)\n");
@ -514,7 +514,8 @@ void CONFIG_RegenerateIniFile()
fprintf(NewConfigFile, "AlienReactionTime=0.1\n");
fprintf(NewConfigFile, "AlienAimSkill=1.0\n");
fprintf(NewConfigFile, "AlienMovementTracking=1.0\n");
fprintf(NewConfigFile, "AlienViewSpeed=2.0\n\n");
fprintf(NewConfigFile, "AlienViewSpeed=2.0\n\n\n");
fprintf(NewConfigFile, "# Desired team sizes. Only used if bot fill mode is 'fillteams'\n");
fprintf(NewConfigFile, "# Format is TeamSize=mapname:nummarines/numaliens\n");

View file

@ -2326,7 +2326,9 @@ bool HasBotCompletedClimbMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector
{
Vector PositionInMove = vClosestPointOnLine2D(MoveStart, MoveEnd, pBot->Edict->v.origin);
if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED)
if (!vEquals2D(PositionInMove, MoveEnd, 4.0f)) { return false; }
/*if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED)
{
Vector ThisMoveDir = UTIL_GetVectorNormal2D(MoveEnd - MoveStart);
Vector NextMoveDir = UTIL_GetVectorNormal2D(NextMoveDestination - MoveEnd);
@ -2344,11 +2346,40 @@ bool HasBotCompletedClimbMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector
}
}
}
}*/
if (pBot->BotNavInfo.IsOnGround)
{
return UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination);
}
else
{
if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED)
{
Vector ThisMoveDir = UTIL_GetVectorNormal2D(MoveEnd - MoveStart);
Vector NextMoveDir = UTIL_GetVectorNormal2D(NextMoveDestination - MoveEnd);
float MoveDot = UTIL_GetDotProduct2D(ThisMoveDir, NextMoveDir);
if (MoveDot > 0.0f)
{
if (pBot->Edict->v.origin.z >= RequiredClimbHeight && !pBot->BotNavInfo.IsOnGround)
{
if (UTIL_QuickTrace(pBot->Edict, pBot->Edict->v.origin, NextMoveDestination)
&& fabsf(pBot->CollisionHullBottomLocation.z - MoveEnd.z) < 100.0f)
{
return true;
}
}
}
}
else
{
return false;
}
}
if (!vEquals2D(PositionInMove, MoveEnd, 2.0f)) { return false; }
return vPointOverlaps3D(MoveEnd, pBot->Edict->v.absmin, pBot->Edict->v.absmax) && UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination);
}
bool HasBotCompletedJumpMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag)
@ -4081,7 +4112,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
if (NearestLiftTrigger)
{
// If the trigger is on cooldown, or the door/train is designed to automatically return without being summoned, then just wait for it to come back
if (gpGlobals->time < NearestLiftTrigger->NextActivationTime || (NearestLift->DoorType == DOORTYPE_TRAIN && !(NearestLift->DoorEdict->v.spawnflags & SF_TRAIN_WAIT_RETRIGGER)) || (NearestLift->DoorType == DOORTYPE_DOOR && NearestLift->DoorEntity && NearestLift->DoorEntity->GetToggleState() == TS_AT_TOP && NearestLift->DoorEntity->m_flWait > 0.0f && !(NearestLift->DoorEdict->v.spawnflags & SF_DOOR_NO_AUTO_RETURN)))
if (gpGlobals->time < NearestLiftTrigger->NextActivationTime || (NearestLift->DoorType == DOORTYPE_TRAIN && !(NearestLift->DoorEdict->v.spawnflags & SF_TRAIN_WAIT_RETRIGGER)) || (NearestLift->DoorType == DOORTYPE_DOOR && NearestLift->DoorEntity && NearestLift->DoorEntity->GetToggleState() == TS_AT_TOP && NearestLift->DoorEntity->m_flWait > 0.0f && !FBitSet(NearestLift->DoorEdict->v.spawnflags, SF_DOOR_NO_AUTO_RETURN)))
{
if (!bIsOnLift && !bIsLiftAtOrNearStart)
{
@ -4462,6 +4493,25 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
{
edict_t* pEdict = pBot->Edict;
if (UTIL_PointIsDirectlyReachable(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, EndPoint))
{
Vector PointOnMoveLine = vClosestPointOnLine2D(StartPoint, EndPoint, pBot->Edict->v.origin);
if (vEquals2D(PointOnMoveLine, EndPoint, 4.0f))
{
// Stop holding crouch if we're a skulk so we can actually climb
if (IsPlayerSkulk(pBot->Edict))
{
pBot->Button &= ~IN_DUCK;
}
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(EndPoint - pBot->CurrentFloorPosition);
return;
}
}
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
Vector vRight = UTIL_GetVectorNormal(UTIL_GetCrossProduct(vForward, UP_VECTOR));
@ -4499,6 +4549,8 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
pBot->Button &= ~IN_DUCK;
}
float ZDiff = fabs(pEdict->v.origin.z - RequiredClimbHeight);
Vector AdjustedTargetLocation = EndPoint + (UTIL_GetVectorNormal2D(EndPoint - StartPoint) * 1000.0f);
Vector DirectAheadView = pBot->CurrentEyePosition + (UTIL_GetVectorNormal2D(AdjustedTargetLocation - pBot->CurrentEyePosition) * 100.0f);
@ -8014,18 +8066,30 @@ void UTIL_UpdateDoorTriggers(nav_door* Door)
}
}
float BaseTriggerDelay = (it->ToggleEnt) ? it->ToggleEnt->m_flDelay : 0.0f;
float DoorDelay = Door->DoorEntity->GetDelay();
it->ActivationDelay = BaseTriggerDelay + DoorDelay + 1.0f;
float BaseTriggerDelay = 0.0f;
float BaseTriggerResetTime = 0.0f;
bool bButtonIsToggle = FBitSet(it->Edict->v.spawnflags, SF_DOOR_NO_AUTO_RETURN);
if (it->ToggleEnt)
{
BaseTriggerDelay = it->ToggleEnt->m_flDelay;
BaseTriggerResetTime = (bButtonIsToggle) ? 1.0f : it->ToggleEnt->GetDelay();
}
float DoorDelay = (FBitSet(Door->DoorEdict->v.spawnflags, SF_DOOR_NO_AUTO_RETURN)) ? 0.0f : Door->DoorEntity->GetDelay();
it->ActivationDelay = fmaxf(BaseTriggerDelay, BaseTriggerResetTime) + DoorDelay + 1.0f;
if (it->ToggleEnt && it->ToggleEnt->GetToggleState() != it->LastToggleState)
{
if (it->LastToggleState != TS_GOING_UP && it->LastToggleState != TS_GOING_DOWN)
TOGGLE_STATE NewState = (TOGGLE_STATE)it->ToggleEnt->GetToggleState();
if (it->LastToggleState == TS_AT_BOTTOM || (bButtonIsToggle && it->LastToggleState == TS_AT_TOP))
{
it->NextActivationTime = gpGlobals->time + fmaxf(it->ActivationDelay + 1.0f, 1.0f);
it->NextActivationTime = gpGlobals->time + fmaxf(it->ActivationDelay, 1.0f);
}
it->LastToggleState = (TOGGLE_STATE)it->ToggleEnt->GetToggleState();
it->LastToggleState = NewState;
}
it++;

View file

@ -4655,6 +4655,7 @@ void AIPlayerCOAlienThink(AvHAIPlayer* pBot)
void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
@ -4666,10 +4667,11 @@ void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
AvHAIBuildableStructure EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyStuffFilter);
edict_t* StructureToAttack = nullptr;
if (EnemyStructure.IsValid())
{
AITASK_SetAttackTask(pBot, Task, EnemyStructure.edict, false);
return;
StructureToAttack = EnemyStructure.edict;
}
else
{
@ -4679,40 +4681,73 @@ void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (EnemyHive)
{
AITASK_SetAttackTask(pBot, Task, EnemyHive->HiveEdict, false);
return;
StructureToAttack = EnemyHive->HiveEdict;
}
}
}
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
edict_t* TargetPlayer = nullptr;
float MinDist = 0.0f;
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
// Nothing to attack, just hunt down remaining enemy players. Shouldn't happen in vanilla combat mode, but a plugin might change behaviour
if (FNullEnt(StructureToAttack))
{
AvHPlayer* ThisPlayer = (*it);
if (!ThisPlayer) { continue; }
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
edict_t* TargetPlayer = nullptr;
edict_t* PlayerEdict = ThisPlayer->edict();
float MinDist = 0.0f;
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
{
TargetPlayer = PlayerEdict;
MinDist = ThisDist;
AvHPlayer* ThisPlayer = (*it);
if (!ThisPlayer) { continue; }
edict_t* PlayerEdict = ThisPlayer->edict();
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
{
TargetPlayer = PlayerEdict;
MinDist = ThisDist;
}
}
if (!FNullEnt(TargetPlayer))
{
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
}
return;
}
// If we're close to the enemy base then just attack. We don't want bots marching through the enemy base and ignoring the hive/comm chair
if (vDist2DSq(pBot->Edict->v.origin, StructureToAttack->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
return;
}
// At this point we already know what we want to do, just crack on
if (Task->TaskType != TASK_NONE) { return; }
// Decide if we're going to attack right away, or take a little detour first. Helps mix things up and prevents all bots just gang-rushing the base endlessly
if (randbool())
{
Vector RandomVisitPoint = UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, StructureToAttack->v.origin, UTIL_MetresToGoldSrcUnits(20.0f), UTIL_MetresToGoldSrcUnits(40.0f));
if (!vIsZero(RandomVisitPoint))
{
AITASK_SetMoveTask(pBot, Task, RandomVisitPoint, false);
return;
}
}
if (!FNullEnt(TargetPlayer))
{
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
}
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
}
void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
@ -4765,6 +4800,51 @@ void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
{
vector<AvHPlayer*> NearbyPlayers = AITAC_GetAllPlayersOfTeamInArea(BotTeam, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
AvHPlayer* NearestWeldablePlayer = nullptr;
AvHPlayer* NearestBadlyDamagedPlayer = nullptr;
float MinDist = 0.0f;
float MinBadDist = 0.0f;
for (auto it = NearbyPlayers.begin(); it != NearbyPlayers.end(); it++)
{
AvHPlayer* ThisPlayer = (*it);
edict_t* PlayerEdict = ThisPlayer->edict();
float ArmourPercent = PlayerEdict->v.armorvalue / (float)GetPlayerMaxArmour(PlayerEdict);
if (ArmourPercent < 1.0f)
{
float ThisDist = vDist2DSq(pBot->Edict->v.origin, PlayerEdict->v.origin);
if (ArmourPercent < 0.75f)
{
if (!NearestBadlyDamagedPlayer || ThisDist < MinBadDist)
{
NearestBadlyDamagedPlayer = ThisPlayer;
MinBadDist = ThisDist;
}
}
else
{
if (!NearestWeldablePlayer || ThisDist < MinDist)
{
NearestWeldablePlayer = ThisPlayer;
MinDist = ThisDist;
}
}
}
}
// Basically, we won't prioritise welding players over structures unless they're low on armour, otherwise we prefer structures. This avoids
// situations where the bot constantly keeps topping up nearby players when there are more important weld targets to worry about
if (NearestBadlyDamagedPlayer)
{
AITASK_SetWeldTask(pBot, Task, NearestBadlyDamagedPlayer->edict(), false);
return;
}
DeployableSearchFilter DamagedStructuresFilter;
DamagedStructuresFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
DamagedStructuresFilter.DeployableTeam = BotTeam;
@ -4803,6 +4883,13 @@ void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
return;
}
if (NearestWeldablePlayer)
{
AITASK_SetWeldTask(pBot, Task, NearestWeldablePlayer->edict(), false);
return;
}
DeployableSearchFilter NearbyArmouryFilter;
NearbyArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
NearbyArmouryFilter.DeployableTeam = BotTeam;
@ -4926,40 +5013,84 @@ void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
AvHAIBuildableStructure EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyStuffFilter);
edict_t* StructureToAttack = nullptr;
if (EnemyStructure.IsValid())
{
AITASK_SetAttackTask(pBot, Task, EnemyStructure.edict, false);
return;
StructureToAttack = EnemyStructure.edict;
}
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
edict_t* TargetPlayer = nullptr;
float MinDist = 0.0f;
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
else
{
AvHPlayer* ThisPlayer = (*it);
if (!ThisPlayer) { continue; }
edict_t* PlayerEdict = ThisPlayer->edict();
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN)
{
TargetPlayer = PlayerEdict;
MinDist = ThisDist;
const AvHAIHiveDefinition* EnemyHive = AITAC_GetActiveHiveNearestLocation(EnemyTeam, pBot->Edict->v.origin);
if (EnemyHive)
{
StructureToAttack = EnemyHive->HiveEdict;
}
}
}
if (!FNullEnt(TargetPlayer))
// Nothing to attack, just hunt down remaining enemy players. Shouldn't happen in vanilla combat mode, but a plugin might change behaviour
if (FNullEnt(StructureToAttack))
{
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
edict_t* TargetPlayer = nullptr;
float MinDist = 0.0f;
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
{
AvHPlayer* ThisPlayer = (*it);
if (!ThisPlayer) { continue; }
edict_t* PlayerEdict = ThisPlayer->edict();
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
{
TargetPlayer = PlayerEdict;
MinDist = ThisDist;
}
}
if (!FNullEnt(TargetPlayer))
{
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
}
return;
}
// If we're close to the enemy base then just attack. We don't want bots marching through the enemy base and ignoring the hive/comm chair
if (vDist2DSq(pBot->Edict->v.origin, StructureToAttack->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
return;
}
// At this point we already know what we want to do, just crack on
if (Task->TaskType != TASK_NONE) { return; }
// Decide if we're going to attack right away, or take a little detour first. Helps mix things up and prevents all bots just gang-rushing the base endlessly
if (randbool())
{
Vector RandomVisitPoint = UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, StructureToAttack->v.origin, UTIL_MetresToGoldSrcUnits(20.0f), UTIL_MetresToGoldSrcUnits(40.0f));
if (!vIsZero(RandomVisitPoint))
{
AITASK_SetMoveTask(pBot, Task, RandomVisitPoint, false);
return;
}
}
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
}
@ -5277,6 +5408,12 @@ void BotResumePlay(AvHAIPlayer* pBot)
SetBaseNavProfile(pBot);
pBot->bIsInactive = false;
// Keep things nicely randomized in Combat mode
if (GetGameRules()->GetMapMode() == MAP_MODE_CO)
{
AITASK_ClearBotTask(pBot, &pBot->PrimaryBotTask);
}
}
void UpdateCommanderOrders(AvHAIPlayer* pBot)