Commander improvements

This commit is contained in:
RGreenlees 2024-01-30 23:00:41 +00:00 committed by pierow
parent 940e4b8074
commit 7dcd3ca1d8
13 changed files with 1159 additions and 542 deletions

View file

@ -381,6 +381,37 @@ bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* O
return NewDist >= OldDist;
}
bool AICOMM_ShouldCommanderPrioritiseNodes(AvHAIPlayer* pBot)
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
int NumOwnedNodes = 0;
int NumEligibleNodes = 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);
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
// 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; }
NumEligibleNodes++;
if (ThisNode->OwningTeam == BotTeam) { NumOwnedNodes++; }
}
int NumNodesLeft = NumEligibleNodes - NumOwnedNodes;
if (NumNodesLeft == 0) { return false; }
return NumOwnedNodes < 3 || NumNodesLeft > 3;
}
void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
{
// Clear out any orders which aren't relevant any more
@ -425,6 +456,61 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
}
}
if (AICOMM_ShouldCommanderPrioritiseNodes(pBot))
{
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
Vector TeamStartingLocation = AITAC_GetTeamStartingLocation(BotTeam);
DeployableSearchFilter ResNodeFilter;
ResNodeFilter.ReachabilityTeam = pBot->Player->GetTeam();
ResNodeFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
vector<AvHAIResourceNode*> EligibleResNodes = AITAC_GetAllMatchingResourceNodes(ZERO_VECTOR, &ResNodeFilter);
AvHAIResourceNode* NearestNode = nullptr;
float MinDist = 0.0f;
for (auto it = EligibleResNodes.begin(); it != EligibleResNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
if (!ThisNode || ThisNode->OwningTeam == BotTeam) { continue; }
int NumDesiredPlayers = (ThisNode->OwningTeam == EnemyTeam) ? 2 : 1;
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ThisNode->ResourceEntity->edict(), ORDERPURPOSE_SECURE_RESNODE);
if (NumAssignedPlayers >= NumDesiredPlayers) { continue; }
float ThisDist = vDist2DSq(TeamStartingLocation, ThisNode->Location);
if (ThisNode->OwningTeam == EnemyTeam)
{
ThisDist *= 2.0f;
}
if (!NearestNode || ThisDist < MinDist)
{
NearestNode = ThisNode;
MinDist = ThisDist;
}
}
if (NearestNode)
{
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, NearestNode->Location);
if (!FNullEnt(NewAssignee))
{
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, NearestNode->ResourceEntity->edict(), ORDERPURPOSE_SECURE_RESNODE);
}
}
return;
}
vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
AvHAIHiveDefinition* EmptyHive = nullptr;
@ -1456,6 +1542,12 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
Vector OutpostLocation = (ExistingStructure) ? 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)
{
if (ExistingStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
@ -1653,6 +1745,8 @@ bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot)
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
{
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
AICOMM_CheckNewRequests(pBot);
ai_commander_request* NextRequest = nullptr;
@ -1682,8 +1776,66 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
}
// We didn't find any requests outstanding
if (!NextRequest) { return false; }
// We didn't find any requests outstanding, see if we want to pro-actively drop stuff for our team
if (!NextRequest)
{
int NumDesiredWelders = 1;
int NumTeamWelders = AITAC_GetNumWeaponsInPlay(CommanderTeam, WEAPON_MARINE_WELDER);
vector<AvHAIResourceNode*> AllNodes = AITAC_GetAllResourceNodes();
for (auto it = AllNodes.begin(); it != AllNodes.end(); it++)
{
AvHAIResourceNode* ThisNode = (*it);
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisNode->TeamAReachabilityFlags : ThisNode->TeamBReachabilityFlags;
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
{
NumDesiredWelders++;
break;
}
}
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllHives();
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
unsigned int TeamReachabilityFlags = (CommanderTeam == AIMGR_GetTeamANumber()) ? ThisHive->TeamAReachabilityFlags : ThisHive->TeamBReachabilityFlags;
if ((TeamReachabilityFlags & AI_REACHABILITY_WELDER) && !(TeamReachabilityFlags & AI_REACHABILITY_MARINE))
{
NumDesiredWelders++;
break;
}
}
NumDesiredWelders = imini(NumDesiredWelders, (AIMGR_GetNumPlayersOnTeam(CommanderTeam) / 2));
if (NumTeamWelders < NumDesiredWelders)
{
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;
}
}
return false;
}
edict_t* Requestor = NextRequest->Requestor;
@ -1740,12 +1892,14 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
AvHAIWeapon WeaponType = UTIL_GetPlayerPrimaryWeapon(thePlayer);
// Requesting player doesn't have a primary weapon, check if they have a pistol
if (WeaponType == WEAPON_INVALID)
{
bFillPrimaryWeapon = false;
WeaponType = UTIL_GetPlayerSecondaryWeapon(thePlayer);
}
// They've only got a knife, don't bother dropping ammo
if (WeaponType == WEAPON_INVALID)
{
NextRequest->bResponded = true;
@ -1755,6 +1909,7 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
int AmmoDeficit = (bFillPrimaryWeapon) ? (UTIL_GetPlayerPrimaryMaxAmmoReserve(thePlayer) - UTIL_GetPlayerPrimaryAmmoReserve(thePlayer)) : (UTIL_GetPlayerSecondaryMaxAmmoReserve(thePlayer) - UTIL_GetPlayerSecondaryAmmoReserve(thePlayer));
int WeaponClipSize = (bFillPrimaryWeapon) ? UTIL_GetPlayerPrimaryWeaponMaxClipSize(thePlayer) : UTIL_GetPlayerSecondaryWeaponMaxClipSize(thePlayer);
// Player already has full ammo, they're yanking our chain
if (AmmoDeficit == 0)
{
NextRequest->bResponded = true;
@ -1762,11 +1917,13 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
}
int DesiredNumAmmoPacks = (int)(ceilf((float)AmmoDeficit / (float)WeaponClipSize));
// Don't drop more than 5 at any one time
DesiredNumAmmoPacks = clampi(DesiredNumAmmoPacks, 0, 5);
int NumAmmoPacksPresent = AITAC_GetNumItemsInLocation(Requestor->v.origin, DEPLOYABLE_ITEM_AMMO, (AvHTeamNumber)Requestor->v.team, AI_REACHABILITY_MARINE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false);
DesiredNumAmmoPacks -= NumAmmoPacksPresent;
// Do we need to drop any ammo, or has the player got enough surrounding them already?
if (DesiredNumAmmoPacks > 0)
{
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(2.0f));
@ -1774,6 +1931,7 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
if (bSuccess)
{
// We've dropped enough that the player has enough to fill their boots. Mission accomplished
if (DesiredNumAmmoPacks <= 1)
{
NextRequest->bResponded = true;
@ -1784,6 +1942,7 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
}
else
{
// Player already has enough ammo packs deployed by them to satisfy. Don't drop any more
NextRequest->bResponded = true;
}
@ -1792,10 +1951,10 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
if (NextRequest->RequestType == COMMANDER_NEXTIDLE)
{
// TODO: Have the commander prioritise this player when looking for people to give orders to
NextRequest->bResponded = true;
}
return false;
}

View file

@ -63,5 +63,7 @@ bool AICOMM_ShouldCommanderLeaveChair(AvHAIPlayer* pBot);
const AvHAIResourceNode* AICOMM_GetNearestResourceNodeCapOpportunity(const AvHTeamNumber Team, const Vector SearchLocation);
const AvHAIHiveDefinition* AICOMM_GetHiveSiegeOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation);
bool AICOMM_ShouldCommanderPrioritiseNodes(AvHAIPlayer* pBot);
#endif // AVH_AI_COMMANDER_H

View file

@ -236,8 +236,8 @@ typedef struct _RESOURCE_NODE
edict_t* ActiveTowerEntity = nullptr; // Reference to the resource tower edict (if capped)
bool bIsBaseNode = false; // Is this a node in the marine base or active alien hive?
edict_t* ParentHive = nullptr;
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Is this reachable by the bots? Checks for marine reachability only
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Is this reachable by the bots? Checks for marine reachability only
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Who on team A can reach this node?
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Who on team B can reach this node?
bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated
float NextReachabilityRefreshTime = 0.0f;
} AvHAIResourceNode;
@ -255,6 +255,8 @@ typedef struct _HIVE_DEFINITION_T
unsigned int ObstacleRefs[MAX_NAV_MESHES]; // When in progress or built, will place an obstacle so bots don't try to walk through it
float NextFloorLocationCheck = 0.0f; // When should the closest navigable point to the hive be calculated? Used to delay the check after a hive is built
AvHTeamNumber OwningTeam = TEAM_IND; // Which team owns this hive currently (TEAM_IND if empty)
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Who on team A can reach this node?
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Who on team B can reach this node?
} AvHAIHiveDefinition;
@ -400,6 +402,35 @@ typedef enum
BUILD_ATTEMPT_FAILED
} BotBuildAttemptStatus;
typedef enum
{
MOVE_TASK_NONE = 0,
MOVE_TASK_USE,
MOVE_TASK_BREAK,
MOVE_TASK_TOUCH,
MOVE_TASK_PICKUP,
MOVE_TASK_WELD
} BotMovementTaskType;
// Door type. Not currently used, future feature so bots know how to open a door
enum DoorActivationType
{
DOOR_NONE, // No type, cannot be activated (permanently open/shut)
DOOR_USE, // Door activated by using it directly
DOOR_TRIGGER,// Door activated by touching a trigger_once or trigger_multiple
DOOR_BUTTON, // Door activated by pressing a button
DOOR_WELD, // Door activated by welding something
DOOR_SHOOT, // Door activated by being shot
DOOR_BREAK // Door activated by breaking something
};
// Door type. Not currently used, future feature so bots know how to open a door
enum NavDoorType
{
DOORTYPE_DOOR, // No type, cannot be activated (permanently open/shut)
DOORTYPE_PLAT, // Door activated by using it directly
DOORTYPE_TRAIN // Door activated by touching a trigger_once or trigger_multiple
};
// Bot path node. A path will be several of these strung together to lead the bot to its destination
typedef struct _BOT_PATH_NODE
@ -417,7 +448,8 @@ typedef struct _ENEMY_STATUS
{
AvHPlayer* EnemyPlayer = nullptr;
edict_t* EnemyEdict = nullptr; // Reference to the enemy player edict
Vector LastSeenLocation = g_vecZero; // The last visibly-confirmed location of the player
Vector LastVisibleLocation = g_vecZero; // The last point the bot saw the target
Vector LastSeenLocation = g_vecZero; // The last visibly-confirmed location of the player or tracked location (if parasited / motion tracked)
Vector LastFloorPosition = g_vecZero; // Nearest point on the floor where the enemy was (for moving towards it)
Vector LastSeenVelocity = g_vecZero; // Last visibly-confirmed movement direction of the player
Vector PendingSeenLocation = g_vecZero; // The last visibly-confirmed location of the player
@ -469,6 +501,7 @@ typedef struct _AVH_AI_PLAYER_TASK
float TaskLength = 0.0f; // If a task has gone on longer than this time, it will be considered completed
} AvHAIPlayerTask;
typedef struct _AVH_AI_BUILD_ATTEMPT
{
AvHAIDeployableStructureType AttemptedStructureType = STRUCTURE_NONE;
@ -479,6 +512,30 @@ typedef struct _AVH_AI_BUILD_ATTEMPT
AvHAIBuildableStructure* LinkedStructure = nullptr;
} AvHAIBuildAttempt;
typedef struct _DOOR_TRIGGER
{
CBaseEntity* Entity = nullptr;
CBaseToggle* ToggleEnt = nullptr;
edict_t* Edict = nullptr;
DoorActivationType TriggerType = DOOR_NONE;
bool bIsActivated = false;
CBaseEntity* TriggerChangeTargetRef = nullptr;
float ActivationDelay = 0.0f;
float LastActivatedTime = 0.0f;
TOGGLE_STATE LastToggleState = TS_AT_BOTTOM;
float LastNextThink = 0.0f;
float NextActivationTime = 0.0f;
} DoorTrigger;
typedef struct _AVH_AI_PLAYER_MOVE_TASK
{
BotMovementTaskType TaskType = MOVE_TASK_NONE;
Vector TaskLocation = g_vecZero;
edict_t* TaskTarget = nullptr;
DoorTrigger* TriggerToActivate = nullptr;
bool bPathGenerated = false;
} AvHAIPlayerMoveTask;
// Contains the bot's current navigation info, such as current path
typedef struct _NAV_STATUS
{
@ -523,14 +580,12 @@ typedef struct _NAV_STATUS
bool bZig; // Is the bot zigging, or zagging?
float NextZigTime; // Controls how frequently they zig or zag
AvHAIPlayerTask MovementTask;
nav_profile NavProfile;
bool bNavProfileChanged = false;
unsigned int SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.)
AvHAIPlayerMoveTask MovementTask;
} nav_status;
// Type of goal the commander wants to achieve

View file

@ -511,7 +511,6 @@ void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure)
void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Height, int area, unsigned int* ObstacleRefArray)
{
unsigned int ObstacleNum = 0;
float Pos[3] = { Location.x, Location.z - (Height * 0.5f), -Location.y };
@ -526,7 +525,7 @@ void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Heigh
ObstacleRefArray[i] = (unsigned int)ObsRef;
if (ObstacleNum > 0)
if ((unsigned int)ObsRef > 0)
{
bNavMeshModified = true;
}
@ -1822,6 +1821,11 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
pBot->BotNavInfo.SpecialMovementFlags |= CurrFlags;
if (pBot->BotNavInfo.SpecialMovementFlags & SAMPLE_POLYFLAGS_WELD)
{
bool bPing = true;
}
// End alignment to floor
// For ladders and wall climbing, calculate the climb height needed to complete the move.
@ -2063,7 +2067,7 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
return;
}
AITASK_SetWeldTask(pBot, &pBot->BotNavInfo.MovementTask, BlockingDoorEdict, true);
NAV_SetWeldMovementTask(pBot, BlockingDoorEdict, nullptr);
return;
}
@ -2126,7 +2130,7 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
// Door must be shot to open
if (Door->ActivationType == DOOR_SHOOT)
{
BotAttackNonPlayerTarget(pBot, Door->DoorEdict);
BotShootTarget(pBot, GetPlayerCurrentWeapon(pBot->Player), Door->DoorEdict);
return;
}
@ -2139,19 +2143,19 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
{
Vector UseLocation = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Trigger->Edict);
AITASK_SetUseTask(pBot, &pBot->BotNavInfo.MovementTask, Trigger->Edict, UseLocation, true);
NAV_SetUseMovementTask(pBot, Trigger->Edict, Trigger);
}
else if (Trigger->TriggerType == DOOR_TRIGGER)
{
AITASK_SetTouchTask(pBot, &pBot->BotNavInfo.MovementTask, Trigger->Edict, true);
NAV_SetTouchMovementTask(pBot, Trigger->Edict, Trigger);
}
else if (Trigger->TriggerType == DOOR_WELD)
{
AITASK_SetWeldTask(pBot, &pBot->BotNavInfo.MovementTask, Trigger->Edict, true);
NAV_SetWeldMovementTask(pBot, Trigger->Edict, Trigger);
}
else if (Trigger->TriggerType == DOOR_BREAK)
{
AITASK_SetAttackTask(pBot, &pBot->BotNavInfo.MovementTask, Trigger->Edict, true);
NAV_SetBreakMovementTask(pBot, Trigger->Edict, Trigger);
}
return;
@ -3623,7 +3627,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
if (vDist2DSq(pBot->Edict->v.origin, StartPoint) > sqrf(50.0f))
{
AITASK_SetMoveTask(pBot, &pBot->BotNavInfo.MovementTask, StartPoint, true);
NAV_SetMoveMovementTask(pBot, StartPoint, nullptr);
}
return;
}
@ -3634,7 +3638,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
if (vDist2DSq(pBot->Edict->v.origin, StartPoint) > sqrf(50.0f))
{
AITASK_SetMoveTask(pBot, &pBot->BotNavInfo.MovementTask, StartPoint, true);
NAV_SetMoveMovementTask(pBot, StartPoint, nullptr);
return;
}
@ -3700,7 +3704,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
if (vDist2DSq(pBot->Edict->v.origin, StartPoint) > sqrf(32.0f))
{
AITASK_SetMoveTask(pBot, &pBot->BotNavInfo.MovementTask, StartPoint, true);
NAV_SetMoveMovementTask(pBot, StartPoint, nullptr);
}
}
@ -3764,19 +3768,19 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
Vector UseLocation = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, NearestLiftTrigger->Edict);
AITASK_SetUseTask(pBot, &pBot->BotNavInfo.MovementTask, NearestLiftTrigger->Edict, UseLocation, true);
NAV_SetUseMovementTask(pBot, NearestLiftTrigger->Edict, NearestLiftTrigger);
}
else if (NearestLiftTrigger->TriggerType == DOOR_TRIGGER)
{
AITASK_SetTouchTask(pBot, &pBot->BotNavInfo.MovementTask, NearestLiftTrigger->Edict, true);
NAV_SetTouchMovementTask(pBot, NearestLiftTrigger->Edict, NearestLiftTrigger);
}
else if (NearestLiftTrigger->TriggerType == DOOR_WELD)
{
AITASK_SetWeldTask(pBot, &pBot->BotNavInfo.MovementTask, NearestLiftTrigger->Edict, true);
NAV_SetWeldMovementTask(pBot, NearestLiftTrigger->Edict, NearestLiftTrigger);
}
else if (NearestLiftTrigger->TriggerType == DOOR_BREAK)
{
AITASK_SetAttackTask(pBot, &pBot->BotNavInfo.MovementTask, NearestLiftTrigger->Edict, true);
NAV_SetBreakMovementTask(pBot, NearestLiftTrigger->Edict, NearestLiftTrigger);
}
return;
@ -3785,7 +3789,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
if (!bIsOnLift && vDist2DSq(pBot->Edict->v.origin, StartPoint) > sqrf(50.0f) && !bIsLiftAtOrNearStart)
{
AITASK_SetMoveTask(pBot, &pBot->BotNavInfo.MovementTask, StartPoint, true);
NAV_SetMoveMovementTask(pBot, StartPoint, nullptr);
}
else
{
@ -4958,6 +4962,23 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
bool bReverseCourse = (vDist3DSq(DestinationPointOnLine, MoveFrom) < vDist3DSq(DestinationPointOnLine, MoveTo));
if (flag == SAMPLE_POLYFLAGS_LIFT)
{
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveFrom) || UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveTo))
{
return true;
}
if (bReverseCourse)
{
LiftMove(pBot, MoveTo, MoveFrom);
}
else
{
LiftMove(pBot, MoveFrom, MoveTo);
}
}
if (flag == SAMPLE_POLYFLAGS_WALK)
{
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveFrom) || UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveTo))
@ -5337,9 +5358,13 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
bool bIsFlyingProfile = pBot->BotNavInfo.NavProfile.bFlyingProfile;
bool bNavProfileChanged = pBot->BotNavInfo.bNavProfileChanged;
bool bForceRecalculation = (pBot->BotNavInfo.NextForceRecalc > 0.0f && gpGlobals->time >= pBot->BotNavInfo.NextForceRecalc);
bool bIsPerformingMoveTask = (BotNavInfo->MovementTask.TaskType != MOVE_TASK_NONE && vEquals(Destination, BotNavInfo->MovementTask.TaskLocation, GetPlayerRadius(pBot->Player)));
bool bEndGoalChanged = (!vEquals(Destination, BotNavInfo->TargetDestination, GetPlayerRadius(pBot->Player)) && !bIsPerformingMoveTask);
bool bMoveTaskGenerated = (BotNavInfo->MovementTask.TaskType == MOVE_TASK_NONE || (vEquals(BotNavInfo->PathDestination, BotNavInfo->MovementTask.TaskLocation, GetPlayerRadius(pBot->Player))));
// Only recalculate the path if there isn't a path, or something has changed and enough time has elapsed since the last path calculation
bool bShouldCalculatePath = (bNavProfileChanged || bForceRecalculation || BotNavInfo->CurrentPath.size() == 0 || !vEquals(Destination, BotNavInfo->TargetDestination, GetPlayerRadius(pBot->Player)));
bool bShouldCalculatePath = (bNavProfileChanged || bForceRecalculation || BotNavInfo->CurrentPath.size() == 0 || bEndGoalChanged || !bMoveTaskGenerated);
if (bShouldCalculatePath)
{
@ -5348,6 +5373,12 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
if (!bIsFlyingProfile && !pBot->BotNavInfo.IsOnGround) { return true; }
dtStatus PathFindingStatus = DT_FAILURE;
if (bEndGoalChanged)
{
ClearBotPath(pBot);
NAV_ClearMovementTask(pBot);
}
if (bIsFlyingProfile)
{
@ -5365,11 +5396,17 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
pBot->BotNavInfo.bNavProfileChanged = false;
if (dtStatusSucceed(PathFindingStatus))
{
BotNavInfo->PathDestination = BotNavInfo->CurrentPath.back().Location;
{
ClearBotStuckMovement(pBot);
pBot->BotNavInfo.TotalStuckTime = 0.0f;
BotNavInfo->TargetDestination = Destination;
BotNavInfo->PathDestination = Destination;
if (!bIsPerformingMoveTask)
{
BotNavInfo->ActualMoveDestination = BotNavInfo->CurrentPath.back().Location;
BotNavInfo->TargetDestination = Destination;
}
BotNavInfo->CurrentPathPoint = BotNavInfo->CurrentPath.begin();
}
else
@ -5410,252 +5447,40 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
}
}
if (BotNavInfo->CurrentPath.size() > 0)
if (!bIsPerformingMoveTask && BotNavInfo->MovementTask.TaskType != MOVE_TASK_NONE)
{
if (IsBotPermaStuck(pBot))
if (NAV_IsMovementTaskStillValid(pBot))
{
BotSuicide(pBot);
return false;
}
if (pBot->Edict->v.flags & FL_INWATER)
{
BotFollowSwimPath(pBot);
}
else
{
if (bIsFlyingProfile)
{
BotFollowFlightPath(pBot);
}
else
{
BotFollowPath(pBot);
}
}
// Check to ensure BotFollowFlightPath or BotFollowPath haven't cleared the path (will happen if reached end of path)
if (BotNavInfo->CurrentPathPoint != BotNavInfo->CurrentPath.end())
{
HandlePlayerAvoidance(pBot, BotNavInfo->CurrentPathPoint->Location);
BotMovementInputs(pBot);
}
return true;
}
return false;
}
bool MoveTo_OLD(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle MoveStyle, const float MaxAcceptableDist)
{
nav_status* BotNavInfo = &pBot->BotNavInfo;
// If we are currently in the process of getting back on the navmesh, don't interrupt
if (BotNavInfo->UnstuckMoveLocation != g_vecZero)
{
if (IsBotPermaStuck(pBot))
{
BotSuicide(pBot);
return false;
}
Vector MoveTarget = BotNavInfo->UnstuckMoveStartLocation + (UTIL_GetVectorNormal2D(BotNavInfo->UnstuckMoveLocation - BotNavInfo->UnstuckMoveStartLocation) * 100.0f);
MoveDirectlyTo(pBot, MoveTarget);
Vector ClosestPoint = vClosestPointOnLine2D(BotNavInfo->UnstuckMoveStartLocation, BotNavInfo->UnstuckMoveLocation, pBot->Edict->v.origin);
bool bAtOrPastMoveLocation = vEquals2D(ClosestPoint, BotNavInfo->UnstuckMoveLocation, 0.1f);
if (bAtOrPastMoveLocation)
{
ClearBotStuckMovement(pBot);
NAV_ProgressMovementTask(pBot);
return true;
}
else
{
// This should only be a short movement, if we don't get there in a few seconds then give up
if ((gpGlobals->time - BotNavInfo->UnstuckMoveLocationStartTime) > 5.0f)
{
ClearBotStuckMovement(pBot);
return true;
}
BotJump(pBot);
if (IsPlayerSkulk(pBot->Edict))
{
pBot->Button &= ~IN_DUCK;
}
else
{
pBot->Button |= IN_DUCK;
}
return true;
}
}
pBot->BotNavInfo.MoveStyle = MoveStyle;
UTIL_UpdateBotMovementStatus(pBot);
bool bIsFlyingProfile = pBot->BotNavInfo.NavProfile.bFlyingProfile;
bool bForceRecalculation = (pBot->BotNavInfo.NextForceRecalc > 0.0f && gpGlobals->time >= pBot->BotNavInfo.NextForceRecalc);
if (BotNavInfo->CurrentPath.size() > 0)
{
if (pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end())
{
NAV_ClearMovementTask(pBot);
ClearBotPath(pBot);
return true;
}
bool bNavProfileChanged = pBot->BotNavInfo.bNavProfileChanged;
bool bHasMovementTask = (BotNavInfo->MovementTask.TaskType != TASK_NONE);
Vector MoveTaskDestination = g_vecZero;
Vector MoveTaskOrigin = g_vecZero;
Vector MoveSecondaryOrigin = g_vecZero;
if (bHasMovementTask)
{
MoveTaskDestination = BotNavInfo->MovementTask.TaskLocation;
MoveTaskOrigin = (!FNullEnt(BotNavInfo->MovementTask.TaskTarget)) ? BotNavInfo->MovementTask.TaskTarget->v.origin : g_vecZero;
MoveSecondaryOrigin = (!FNullEnt(BotNavInfo->MovementTask.TaskSecondaryTarget)) ? BotNavInfo->MovementTask.TaskSecondaryTarget->v.origin : g_vecZero;
}
bool bUltimateDestinationChanged = !vEquals(Destination, BotNavInfo->TargetDestination, GetPlayerRadius(pBot->Player)) && !vEquals(Destination, MoveTaskDestination) && !vEquals(Destination, MoveTaskOrigin) && !vEquals(Destination, MoveSecondaryOrigin);
bool bHasReachedDestination = BotIsAtLocation(pBot, BotNavInfo->TargetDestination);
if (bUltimateDestinationChanged || bNavProfileChanged || bHasReachedDestination || bForceRecalculation)
{
// First abort our current move so we don't try to recalculate half-way up a wall or ladder
if (bIsFlyingProfile || AbortCurrentMove(pBot, Destination))
{
// Don't clear the path if we're in the middle of a movement task
if (!vEquals(Destination, MoveTaskDestination) && !vEquals(Destination, MoveTaskOrigin) && !vEquals(Destination, MoveSecondaryOrigin))
{
ClearBotPath(pBot);
}
return true;
}
}
else
{
if (bHasMovementTask && !vEquals(Destination, MoveTaskDestination) && !vEquals(Destination, MoveTaskOrigin) && !vEquals(Destination, MoveSecondaryOrigin))
{
if (AITASK_IsTaskStillValid(pBot, &BotNavInfo->MovementTask))
{
BotProgressTask(pBot, &BotNavInfo->MovementTask);
return true;
}
else
{
AITASK_ClearBotTask(pBot, &BotNavInfo->MovementTask);
}
}
}
}
bool bCanRecalculatePath = (gpGlobals->time - pBot->BotNavInfo.LastPathCalcTime > MIN_PATH_RECALC_TIME);
// Only recalculate the path if there isn't a path, or something has changed and enough time has elapsed since the last path calculation
bool bShouldCalculatePath = bCanRecalculatePath && (bForceRecalculation || BotNavInfo->CurrentPath.size() == 0 || !vEquals(Destination, BotNavInfo->PathDestination));
if (bShouldCalculatePath)
{
if (pBot->Player->IsOnLadder())
{
BotJump(pBot);
return true;
}
pBot->BotNavInfo.LastPathCalcTime = gpGlobals->time;
BotNavInfo->bNavProfileChanged = false;
BotNavInfo->NextForceRecalc = 0.0f;
if (vIsZero(BotNavInfo->TargetDestination))
{
BotNavInfo->TargetDestination = Destination;
}
BotNavInfo->PathDestination = Destination;
dtStatus PathFindingStatus = DT_FAILURE;
if (bIsFlyingProfile)
{
PathFindingStatus = FindFlightPathToPoint(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, Destination, BotNavInfo->CurrentPath, MaxAcceptableDist);
}
else
{
Vector ValidNavmeshPoint = UTIL_ProjectPointToNavmesh(Destination, Vector(max_ai_use_reach, max_ai_use_reach, max_ai_use_reach), pBot->BotNavInfo.NavProfile);
// Destination is not on the nav mesh, so we can't get close enough
if (vIsZero(ValidNavmeshPoint))
{
sprintf(pBot->PathStatus, "Could not project destination to navmesh");
return false;
}
PathFindingStatus = FindPathClosestToPoint(pBot, pBot->BotNavInfo.MoveStyle, pBot->CollisionHullBottomLocation, ValidNavmeshPoint, BotNavInfo->CurrentPath, MaxAcceptableDist);
}
if (dtStatusSucceed(PathFindingStatus))
{
BotNavInfo->ActualMoveDestination = BotNavInfo->CurrentPath.back().Location;
ClearBotStuckMovement(pBot);
pBot->BotNavInfo.TotalStuckTime = 0.0f;
BotNavInfo->CurrentPathPoint = BotNavInfo->CurrentPath.begin();
sprintf(pBot->PathStatus, "Path finding successful");
}
else
{
Vector PointBackOnPath = FindClosestPointBackOnPath(pBot);
if (PointBackOnPath != g_vecZero)
{
ClearBotStuckMovement(pBot);
ClearBotPath(pBot);
BotNavInfo->UnstuckMoveLocation = PointBackOnPath;
BotNavInfo->UnstuckMoveStartLocation = pBot->Edict->v.origin;
BotNavInfo->UnstuckMoveLocationStartTime = gpGlobals->time;
sprintf(pBot->PathStatus, "Backwards Path Find Successful");
}
else
{
if (BotNavInfo->LastNavMeshPosition != g_vecZero && vDist2DSq(BotNavInfo->LastNavMeshPosition, pBot->Edict->v.origin) > GetPlayerRadius(pBot->Player))
{
ClearBotStuckMovement(pBot);
ClearBotPath(pBot);
BotNavInfo->UnstuckMoveLocation = BotNavInfo->LastNavMeshPosition;
BotNavInfo->UnstuckMoveStartLocation = pBot->Edict->v.origin;
BotNavInfo->UnstuckMoveLocationStartTime = gpGlobals->time;
}
else
{
BotSuicide(pBot);
}
return false;
}
}
}
if (BotNavInfo->CurrentPath.size() > 0)
{
// If this path requires use of a welder and we don't have one, then find one
if ((pBot->BotNavInfo.SpecialMovementFlags & SAMPLE_POLYFLAGS_WELD) && !PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
{
if (pBot->BotNavInfo.MovementTask.TaskType != MOVE_TASK_PICKUP)
{
nav_profile BaseProfile = GetBaseNavProfile(MARINE_BASE_NAV_PROFILE);
AvHAIDroppedItem* NearestWelder = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_WELDER, pBot->Player->GetTeam(), BaseProfile.ReachabilityFlag, 0.0f, 0.0f, true);
if (NearestWelder)
{
NAV_SetPickupMovementTask(pBot, NearestWelder->edict, nullptr);
return true;
}
}
}
if (IsBotPermaStuck(pBot))
{
BotSuicide(pBot);
@ -5689,6 +5514,7 @@ bool MoveTo_OLD(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle
}
return false;
}
Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot)
@ -6024,23 +5850,6 @@ void BotFollowPath(AvHAIPlayer* pBot)
return;
}
// If this path requires use of a welder and we don't have one, then find one
if ((pBot->BotNavInfo.SpecialMovementFlags & SAMPLE_POLYFLAGS_WELD) && !PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
{
if (pBot->BotNavInfo.MovementTask.TaskType != TASK_GET_WEAPON)
{
AITASK_ClearBotTask(pBot, &pBot->BotNavInfo.MovementTask);
AvHAIDroppedItem* NearestWelder = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_WELDER, pBot->Player->GetTeam(), pBot->BotNavInfo.NavProfile.ReachabilityFlag, 0.0f, 0.0f, true);
if (NearestWelder)
{
AITASK_SetPickupTask(pBot, &pBot->BotNavInfo.MovementTask, NearestWelder->edict, true);
return;
}
}
}
Vector MoveTo = BotNavInfo->CurrentPathPoint->Location;
unsigned int CurrentFlag = BotNavInfo->CurrentPathPoint->flag;
@ -6834,8 +6643,6 @@ void ClearBotPath(AvHAIPlayer* pBot)
pBot->BotNavInfo.SpecialMovementFlags = 0;
AITASK_ClearBotTask(pBot, &pBot->BotNavInfo.MovementTask);
pBot->BotNavInfo.bNavProfileChanged = false;
pBot->BotNavInfo.TargetDestination = g_vecZero;
@ -8259,4 +8066,228 @@ dtStatus DEBUG_TestFindPath(const nav_profile& NavProfile, const Vector FromLoca
}
return DT_SUCCESS;
}
void NAV_SetMoveMovementTask(AvHAIPlayer* pBot, Vector MoveLocation, DoorTrigger* TriggerToActivate)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_TOUCH && vEquals(MoveTask->TaskLocation, MoveLocation)) { return; }
MoveTask->TaskType = MOVE_TASK_TOUCH;
MoveTask->TaskLocation = MoveLocation;
vector<bot_path_node> Path;
dtStatus PathStatus = FindPathClosestToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, MoveLocation, Path, 200.0f);
if (dtStatusSucceed(PathStatus))
{
MoveTask->TaskLocation = Path.back().Location;
}
}
void NAV_SetTouchMovementTask(AvHAIPlayer* pBot, edict_t* EntityToTouch, DoorTrigger* TriggerToActivate)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_TOUCH && MoveTask->TaskTarget == EntityToTouch) { return; }
MoveTask->TaskType = MOVE_TASK_TOUCH;
MoveTask->TaskTarget = EntityToTouch;
MoveTask->TriggerToActivate = TriggerToActivate;
vector<bot_path_node> Path;
dtStatus PathStatus = FindPathClosestToPoint(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, UTIL_GetCentreOfEntity(EntityToTouch), Path, 200.0f);
if (dtStatusSucceed(PathStatus))
{
MoveTask->TaskLocation = Path.back().Location;
}
}
void NAV_SetUseMovementTask(AvHAIPlayer* pBot, edict_t* EntityToUse, DoorTrigger* TriggerToActivate)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_USE && MoveTask->TaskTarget == EntityToUse) { return; }
NAV_ClearMovementTask(pBot);
MoveTask->TaskType = MOVE_TASK_USE;
MoveTask->TaskTarget = EntityToUse;
MoveTask->TriggerToActivate = TriggerToActivate;
MoveTask->TaskLocation = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, EntityToUse);
}
void NAV_SetBreakMovementTask(AvHAIPlayer* pBot, edict_t* EntityToBreak, DoorTrigger* TriggerToActivate)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_BREAK && MoveTask->TaskTarget == EntityToBreak) { return; }
NAV_ClearMovementTask(pBot);
MoveTask->TaskType = MOVE_TASK_BREAK;
MoveTask->TaskTarget = EntityToBreak;
MoveTask->TriggerToActivate = TriggerToActivate;
MoveTask->TaskLocation = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, EntityToBreak);
}
void NAV_SetWeldMovementTask(AvHAIPlayer* pBot, edict_t* EntityToWeld, DoorTrigger* TriggerToActivate)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_WELD && MoveTask->TaskTarget == EntityToWeld) { return; }
NAV_ClearMovementTask(pBot);
MoveTask->TaskType = MOVE_TASK_WELD;
MoveTask->TaskTarget = EntityToWeld;
MoveTask->TriggerToActivate = TriggerToActivate;
MoveTask->TaskLocation = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, EntityToWeld);
}
void NAV_ClearMovementTask(AvHAIPlayer* pBot)
{
pBot->BotNavInfo.MovementTask.TaskType = MOVE_TASK_NONE;
pBot->BotNavInfo.MovementTask.TaskLocation = ZERO_VECTOR;
pBot->BotNavInfo.MovementTask.TaskTarget = nullptr;
pBot->BotNavInfo.MovementTask.TriggerToActivate = nullptr;
}
void NAV_ProgressMovementTask(AvHAIPlayer* pBot)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_NONE) { return; }
if (MoveTask->TaskType == MOVE_TASK_USE)
{
if (IsPlayerInUseRange(pBot->Edict, MoveTask->TaskTarget))
{
BotUseObject(pBot, MoveTask->TaskTarget, false);
ClearBotStuck(pBot);
return;
}
}
if (MoveTask->TaskType == MOVE_TASK_BREAK)
{
AvHAIWeapon Weapon = WEAPON_INVALID;
if (IsPlayerMarine(pBot->Edict))
{
Weapon = BotMarineChooseBestWeaponForStructure(pBot, MoveTask->TaskTarget);
}
else
{
Weapon = BotAlienChooseBestWeaponForStructure(pBot, MoveTask->TaskTarget);
}
BotAttackResult AttackResult = PerformAttackLOSCheck(pBot, Weapon, MoveTask->TaskTarget);
if (AttackResult == ATTACK_SUCCESS)
{
// If we were ducking before then keep ducking
if (pBot->Edict->v.oldbuttons & IN_DUCK)
{
pBot->Button |= IN_DUCK;
}
BotShootTarget(pBot, Weapon, MoveTask->TaskTarget);
ClearBotStuck(pBot);
return;
}
}
if (MoveTask->TaskType == MOVE_TASK_WELD)
{
if (IsPlayerInUseRange(pBot->Edict, MoveTask->TaskTarget))
{
Vector BBMin = MoveTask->TaskTarget->v.absmin;
Vector BBMax = MoveTask->TaskTarget->v.absmax;
vScaleBB(BBMin, BBMax, 0.75f);
BotLookAt(pBot, vClosestPointOnBB(pBot->CurrentEyePosition, BBMin, BBMax));
pBot->DesiredCombatWeapon = WEAPON_MARINE_WELDER;
if (GetPlayerCurrentWeapon(pBot->Player) != WEAPON_MARINE_WELDER)
{
return;
}
pBot->Button |= IN_ATTACK;
ClearBotStuck(pBot);
return;
}
}
MoveTo(pBot, MoveTask->TaskLocation, MOVESTYLE_NORMAL);
}
bool NAV_IsMovementTaskStillValid(AvHAIPlayer* pBot)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_NONE) { return false; }
if (MoveTask->TriggerToActivate)
{
if (!MoveTask->TriggerToActivate->bIsActivated) { return false; }
if (MoveTask->TriggerToActivate->NextActivationTime > gpGlobals->time) { return false; }
}
if (MoveTask->TaskType == MOVE_TASK_USE)
{
return (vDist2DSq(pBot->Edict->v.origin, MoveTask->TaskLocation) > sqrf(GetPlayerRadius(pBot->Player)) || fabsf(pBot->Edict->v.origin.z - MoveTask->TaskLocation.z) > 50.0f);
}
if (MoveTask->TaskType == MOVE_TASK_PICKUP)
{
return (!FNullEnt(MoveTask->TaskTarget) && !(MoveTask->TaskTarget->v.effects & EF_NODRAW));
}
if (MoveTask->TaskType == MOVE_TASK_TOUCH)
{
return (!FNullEnt(MoveTask->TaskTarget) && !IsPlayerTouchingEntity(pBot->Edict, MoveTask->TaskTarget));
}
if (MoveTask->TaskType == MOVE_TASK_BREAK)
{
return (!FNullEnt(MoveTask->TaskTarget) && MoveTask->TaskTarget->v.deadflag == DEAD_NO && MoveTask->TaskTarget->v.health > 0.0f);
}
if (MoveTask->TaskType == MOVE_TASK_WELD)
{
AvHWeldable* WeldableRef = dynamic_cast<AvHWeldable*>(CBaseEntity::Instance(MoveTask->TaskTarget));
if (WeldableRef)
{
return !WeldableRef->GetIsWelded();
}
}
return false;
}
void NAV_SetPickupMovementTask(AvHAIPlayer* pBot, edict_t* ThingToPickup, DoorTrigger* TriggerToActivate)
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_PICKUP && MoveTask->TaskTarget == ThingToPickup) { return; }
NAV_ClearMovementTask(pBot);
MoveTask->TaskType = MOVE_TASK_PICKUP;
MoveTask->TaskTarget = ThingToPickup;
MoveTask->TriggerToActivate = TriggerToActivate;
MoveTask->TaskLocation = ThingToPickup->v.origin;
}

View file

@ -71,40 +71,6 @@ enum SamplePolyFlags
SAMPLE_POLYFLAGS_ALL = -1 // All abilities.
};
// Door type. Not currently used, future feature so bots know how to open a door
enum DoorActivationType
{
DOOR_NONE, // No type, cannot be activated (permanently open/shut)
DOOR_USE, // Door activated by using it directly
DOOR_TRIGGER,// Door activated by touching a trigger_once or trigger_multiple
DOOR_BUTTON, // Door activated by pressing a button
DOOR_WELD, // Door activated by welding something
DOOR_SHOOT, // Door activated by being shot
DOOR_BREAK // Door activated by breaking something
};
// Door type. Not currently used, future feature so bots know how to open a door
enum NavDoorType
{
DOORTYPE_DOOR, // No type, cannot be activated (permanently open/shut)
DOORTYPE_PLAT, // Door activated by using it directly
DOORTYPE_TRAIN // Door activated by touching a trigger_once or trigger_multiple
};
typedef struct _DOOR_TRIGGER
{
CBaseEntity* Entity = nullptr;
CBaseToggle* ToggleEnt = nullptr;
edict_t* Edict = nullptr;
DoorActivationType TriggerType = DOOR_NONE;
bool bIsActivated = false;
CBaseEntity* TriggerChangeTargetRef = nullptr;
float ActivationDelay = 0.0f;
float LastActivatedTime = 0.0f;
TOGGLE_STATE LastToggleState = TS_AT_BOTTOM;
float LastNextThink = 0.0f;
float NextActivationTime = 0.0f;
} DoorTrigger;
// Door reference. Not used, but is a future feature to allow bots to track if a door is open or not, and how to open it etc.
typedef struct _NAV_DOOR
@ -505,5 +471,17 @@ void OnOffMeshConnectionAdded(dtOffMeshConnection* NewConnection);
const dtOffMeshConnection* DEBUG_FindNearestOffMeshConnectionToPoint(const Vector Point, unsigned int FilterFlags);
void NAV_SetPickupMovementTask(AvHAIPlayer* pBot, edict_t* ThingToPickup, DoorTrigger* TriggerToActivate);
void NAV_SetMoveMovementTask(AvHAIPlayer* pBot, Vector MoveLocation, DoorTrigger* TriggerToActivate);
void NAV_SetTouchMovementTask(AvHAIPlayer* pBot, edict_t* EntityToTouch, DoorTrigger* TriggerToActivate);
void NAV_SetUseMovementTask(AvHAIPlayer* pBot, edict_t* EntityToUse, DoorTrigger* TriggerToActivate);
void NAV_SetBreakMovementTask(AvHAIPlayer* pBot, edict_t* EntityToBreak, DoorTrigger* TriggerToActivate);
void NAV_SetWeldMovementTask(AvHAIPlayer* pBot, edict_t* EntityToWeld, DoorTrigger* TriggerToActivate);
void NAV_ClearMovementTask(AvHAIPlayer* pBot);
void NAV_ProgressMovementTask(AvHAIPlayer* pBot);
bool NAV_IsMovementTaskStillValid(AvHAIPlayer* pBot);
#endif // BOT_NAVIGATION_H

View file

@ -365,40 +365,39 @@ void BotSay(AvHAIPlayer* pBot, bool bTeamSay, float Delay, char* textToSay)
}
}
void BotReloadWeapons(AvHAIPlayer* pBot)
bool BotReloadWeapons(AvHAIPlayer* pBot)
{
// Aliens and commander don't reload
if (!IsPlayerMarine(pBot->Edict) || !IsPlayerActiveInGame(pBot->Edict)) { return; }
if (!IsPlayerMarine(pBot->Edict) || !IsPlayerActiveInGame(pBot->Edict)) { return false; }
if (gpGlobals->time - pBot->LastCombatTime > 5.0f)
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
AvHAIWeapon SecondaryWeapon = GetBotMarineSecondaryWeapon(pBot);
AvHAIWeapon CurrentWeapon = GetPlayerCurrentWeapon(pBot->Player);
if (WeaponCanBeReloaded(PrimaryWeapon) && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player) && UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
{
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
AvHAIWeapon SecondaryWeapon = GetBotMarineSecondaryWeapon(pBot);
AvHAIWeapon CurrentWeapon = GetPlayerCurrentWeapon(pBot->Player);
pBot->DesiredCombatWeapon = PrimaryWeapon;
if (WeaponCanBeReloaded(PrimaryWeapon) && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player) && UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
if (CurrentWeapon == PrimaryWeapon)
{
pBot->DesiredCombatWeapon = PrimaryWeapon;
if (CurrentWeapon == PrimaryWeapon)
{
BotReloadCurrentWeapon(pBot);
}
return;
BotReloadCurrentWeapon(pBot);
}
if (WeaponCanBeReloaded(SecondaryWeapon) && BotGetSecondaryWeaponClipAmmo(pBot) < BotGetSecondaryWeaponMaxClipSize(pBot) && BotGetSecondaryWeaponAmmoReserve(pBot) > 0)
{
pBot->DesiredCombatWeapon = SecondaryWeapon;
if (CurrentWeapon == SecondaryWeapon)
{
BotReloadCurrentWeapon(pBot);
}
return;
}
return true;
}
if (WeaponCanBeReloaded(SecondaryWeapon) && BotGetSecondaryWeaponClipAmmo(pBot) < BotGetSecondaryWeaponMaxClipSize(pBot) && BotGetSecondaryWeaponAmmoReserve(pBot) > 0)
{
pBot->DesiredCombatWeapon = SecondaryWeapon;
if (CurrentWeapon == SecondaryWeapon)
{
BotReloadCurrentWeapon(pBot);
}
return true;
}
return false;
}
void BotDropWeapon(AvHAIPlayer* pBot)
@ -661,7 +660,7 @@ void BotShootTarget(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, edict_t* Target
return;
}
if (CurrentWeapon == WEAPON_NONE) { return; }
if (CurrentWeapon == WEAPON_INVALID) { return; }
if (CurrentWeapon == WEAPON_SKULK_XENOCIDE || CurrentWeapon == WEAPON_LERK_PRIMALSCREAM)
@ -1410,6 +1409,12 @@ void BotUpdateView(AvHAIPlayer* pBot)
TrackingInfo->bIsAwareOfPlayer = true;
TrackingInfo->LastSeenLocation = (bHasLOS) ? VisiblePoint : Enemy->v.origin;
if (bHasLOS)
{
TrackingInfo->LastVisibleLocation = Enemy->v.origin;
}
TrackingInfo->LastFloorPosition = FloorLocation;
if (bHasLOS)
@ -1666,21 +1671,20 @@ void CustomThink(AvHAIPlayer* pBot)
{
if (IsPlayerMarine(pBot->Player))
{
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GRENADE))
pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot);
if (pBot->CurrentEnemy < 0)
{
if (!vIsZero(AIDEBUG_GetDebugVector1()))
{
BotThrowGrenadeAtTarget(pBot, AIDEBUG_GetDebugVector1());
}
MoveTo(pBot, AITAC_GetTeamStartingLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())), MOVESTYLE_NORMAL);
}
else
{
pBot->DesiredCombatWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
MarineCombatThink(pBot);
}
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_NONE) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
if (DesiredWeapon != WEAPON_INVALID && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
@ -1688,14 +1692,14 @@ void CustomThink(AvHAIPlayer* pBot)
return;
}
if (!IsPlayerFade(pBot->Edict))
if (!IsPlayerLerk(pBot->Edict))
{
if (pBot->Player->GetResources() < BALANCE_VAR(kFadeCost))
if (pBot->Player->GetResources() < BALANCE_VAR(kLerkCost))
{
pBot->Player->GiveResources(50.0f);
pBot->Player->GiveResources(30.0f);
}
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_FOUR);
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_THREE);
return;
}
@ -1769,6 +1773,31 @@ void UpdateAIPlayerDMRole(AvHAIPlayer* pBot)
}
void AIPlayerTakeDamage(AvHAIPlayer* pBot, int damageTaken, edict_t* aggressor)
{
int aggressorIndex = ENTINDEX(aggressor) - 1;
if (aggressorIndex > -1 && aggressor->v.team != pBot->Edict->v.team && IsPlayerActiveInGame(aggressor))
{
pBot->TrackedEnemies[aggressorIndex].LastSeenTime = gpGlobals->time;
// If the bot can't see the enemy (bCurrentlyVisible is false) then set the last seen location to a random point in the vicinity so the bot doesn't immediately know where they are
if (pBot->TrackedEnemies[aggressorIndex].bIsVisible || vDist2DSq(pBot->TrackedEnemies[aggressorIndex].EnemyEdict->v.origin, pBot->Edict->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
{
pBot->TrackedEnemies[aggressorIndex].LastSeenLocation = aggressor->v.origin;
}
else
{
// The further the enemy is, the more inaccurate the bot's guess will be where they are
pBot->TrackedEnemies[aggressorIndex].LastSeenLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), aggressor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
}
pBot->TrackedEnemies[aggressorIndex].LastSeenVelocity = aggressor->v.velocity;
pBot->TrackedEnemies[aggressorIndex].bIsAwareOfPlayer = true;
pBot->TrackedEnemies[aggressorIndex].bHasLOS = true;
}
}
bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot)
{
AvHAICommanderMode CurrentCommanderMode = AIMGR_GetCommanderMode();
@ -2160,7 +2189,7 @@ AvHAICombatStrategy GetLerkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_stat
{
return COMBAT_STRATEGY_SKIRMISH;
}
}
}
AvHAICombatStrategy GetFadeCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy)
{
@ -2347,6 +2376,17 @@ AvHAICombatStrategy GetMarineCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_st
float CurrentHealthPercent = (pBot->Edict->v.health / pBot->Edict->v.max_health);
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->LastSeenLocation);
// If we are doing something important, don't get distracted by enemies that aren't an immediate threat
if (pBot->CurrentTask && pBot->CurrentTask->TaskType == TASK_DEFEND || pBot->CommanderTask.TaskType != TASK_NONE)
{
if ((!CurrentEnemy->bHasLOS || DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) && (!vIsZero(pBot->CurrentTask->TaskLocation) && !UTIL_PlayerHasLOSToLocation(CurrentEnemy->EnemyEdict, pBot->CurrentTask->TaskLocation, UTIL_MetresToGoldSrcUnits(30.0f))))
{
return COMBAT_STRATEGY_IGNORE;
}
}
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_RETREAT)
{
int MinDesiredAmmo = imini(UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player), UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player) * 2);
@ -2358,18 +2398,28 @@ AvHAICombatStrategy GetMarineCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_st
}
int NumEnemyAllies = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, EnemyEdict->v.origin, UTIL_MetresToGoldSrcUnits(10.0f), EnemyEdict);
int NumFriendlies = AITAC_GetNumPlayersOnTeamWithLOS(BotTeam, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(10.0f), pBot->Edict);
if (CurrentHealthPercent < 0.3f || (CurrentHealthPercent < 0.5f && NumEnemyAllies > 0) || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player))
{
return COMBAT_STRATEGY_RETREAT;
}
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL))
// Shotty users should attack, can't really skirmish with a shotgun
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_SHOTGUN) && (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0 || UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0))
{
return COMBAT_STRATEGY_ATTACK;
}
bool bIsEnemyRanged = IsPlayerMarine(CurrentEnemy->EnemyPlayer);
if (bIsEnemyRanged || PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL))
{
return COMBAT_STRATEGY_SKIRMISH;
}
if (!PlayerHasHeavyArmour(pBot->Edict) && (CurrentEnemy->EnemyPlayer->GetUser3() > AVH_USER3_ALIEN_PLAYER3 || NumEnemyAllies > 0))
// If we're up against a stronger enemy than us, skirmish instead to avoid getting wiped out
if (!PlayerHasHeavyArmour(pBot->Edict) && (CurrentEnemy->EnemyPlayer->GetUser3() > AVH_USER3_ALIEN_PLAYER3 || NumEnemyAllies > 0 || PlayerHasHeavyArmour(EnemyEdict)))
{
return COMBAT_STRATEGY_SKIRMISH;
}
@ -2556,6 +2606,16 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
AvHAIWeapon DesiredCombatWeapon = BotMarineChooseBestWeapon(pBot, CurrentEnemy);
bool bBotIsGrenadier = (DesiredCombatWeapon == WEAPON_MARINE_GL);
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->v.origin);
bool bEnemyIsRanged = IsPlayerMarine(TrackedEnemyRef->EnemyPlayer) || ((GetPlayerCurrentWeapon(TrackedEnemyRef->EnemyPlayer) == WEAPON_FADE_ACIDROCKET) && DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)));
float LastEnemySeenTime = (TrackedEnemyRef->LastTrackedTime > 0.0f) ? TrackedEnemyRef->LastTrackedTime : TrackedEnemyRef->LastSeenTime;
Vector LastEnemySeenLocation = TrackedEnemyRef->LastSeenLocation;
// Run away and restock
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_RETREAT)
{
if (NearestHealthPack && (pBot->Edict->v.health < pBot->Edict->v.max_health * 0.7f))
@ -2585,7 +2645,7 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
if (IsPlayerInUseRange(pBot->Edict, NearestArmouryRef->edict))
{
BotUseObject(pBot, NearestArmouryRef->edict, true);
return;
return true;
}
}
@ -2602,20 +2662,263 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
{
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, DesiredCombatWeapon, CurrentEnemy);
if (DesiredCombatWeapon != WEAPON_MARINE_KNIFE)
{
if (DistToEnemy < sqrf(100.0f))
{
if (IsPlayerReloading(pBot->Player) && CanInterruptWeaponReload(GetPlayerCurrentWeapon(pBot->Player)) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) > 0)
{
InterruptReload(pBot);
}
BotJump(pBot);
}
}
if (LOSCheck == ATTACK_SUCCESS)
{
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
return true;
if (!bBotIsGrenadier || DistToEnemy > sqrf(BALANCE_VAR(kGrenadeRadius)))
{
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
return true;
}
}
}
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) == 0 && vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) == 0 && DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
{
BotReloadWeapons(pBot);
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, DesiredCombatWeapon, CurrentEnemy);
if (LOSCheck == ATTACK_SUCCESS)
{
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
}
else
{
BotReloadWeapons(pBot);
}
}
return true;
}
// Maintain distance, pop and shoot
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_SKIRMISH || pBot->CurrentCombatStrategy == COMBAT_STRATEGY_AMBUSH)
{
if (vIsZero(pBot->LastSafeLocation))
{
pBot->LastSafeLocation = AITAC_GetTeamStartingLocation(BotTeam);
}
if (TrackedEnemyRef->bHasLOS)
{
if (GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0)
{
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
BotReloadWeapons(pBot);
return true;
}
if (vDist2DSq(pBot->Edict->v.origin, pBot->LastSafeLocation) > sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
{
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
}
else
{
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GRENADE) && DesiredCombatWeapon != WEAPON_MARINE_GL)
{
// Plus 1 to include the target themselves
int NumTargets = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, CurrentEnemy->v.origin, BALANCE_VAR(kGrenadeRadius), nullptr);
if (NumTargets > 1)
{
BotThrowGrenadeAtTarget(pBot, CurrentEnemy->v.origin);
return true;
}
}
if (bEnemyIsRanged)
{
Vector EnemyOrientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR);
pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(RightDir) : UTIL_GetVectorNormal2D(-RightDir);
// Let's get ziggy with it
if (gpGlobals->time > pBot->BotNavInfo.NextZigTime)
{
pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig;
pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f);
}
BotMovementInputs(pBot);
}
else
{
if (DesiredCombatWeapon != WEAPON_MARINE_KNIFE)
{
if (DistToEnemy < sqrf(100.0f))
{
if (IsPlayerReloading(pBot->Player) && CanInterruptWeaponReload(GetPlayerCurrentWeapon(pBot->Player)) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) > 0)
{
InterruptReload(pBot);
}
BotJump(pBot);
}
}
}
}
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
}
else
{
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GRENADE) || (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL) && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0))
{
Vector GrenadeTarget = UTIL_GetGrenadeThrowTarget(pBot->Edict, LastEnemySeenLocation, BALANCE_VAR(kGrenadeRadius), true);
if (!vIsZero(GrenadeTarget))
{
BotThrowGrenadeAtTarget(pBot, GrenadeTarget);
return true;
}
}
if (BotReloadWeapons(pBot)) { return true; }
MoveTo(pBot, LastEnemySeenLocation, MOVESTYLE_NORMAL);
}
return true;
}
// Go for the kill. Maintain desired distance and pursue when needed
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK)
{
AvHAIWeapon IdealAttackWeapon = (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0 || UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0) ? UTIL_GetPlayerPrimaryWeapon(pBot->Player) : DesiredCombatWeapon;
float DesiredDistance = GetMinIdealWeaponRange(IdealAttackWeapon) + ((GetMaxIdealWeaponRange(IdealAttackWeapon) - GetMinIdealWeaponRange(IdealAttackWeapon)) * 0.5f);
bool bCanReloadCurrentWeapon = (WeaponCanBeReloaded(DesiredCombatWeapon) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) < GetPlayerCurrentWeaponMaxClipAmmo(pBot->Player) && GetPlayerCurrentWeaponReserveAmmo(pBot->Player) > 0);
bool bMustReloadCurrentWeapon = bCanReloadCurrentWeapon && GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0;
if (vIsZero(pBot->LastSafeLocation))
{
pBot->LastSafeLocation = AITAC_GetTeamStartingLocation(BotTeam);
}
if (!TrackedEnemyRef->bHasLOS)
{
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GRENADE) || (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL) && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0))
{
Vector GrenadeTarget = UTIL_GetGrenadeThrowTarget(pBot->Edict, LastEnemySeenLocation, BALANCE_VAR(kGrenadeRadius), true);
if (!vIsZero(GrenadeTarget))
{
BotThrowGrenadeAtTarget(pBot, GrenadeTarget);
return true;
}
}
if ((IdealAttackWeapon != DesiredCombatWeapon || bCanReloadCurrentWeapon) && gpGlobals->time - TrackedEnemyRef->LastSeenTime > 3.0f)
{
BotReloadWeapons(pBot);
if (vDist2DSq(pBot->Edict->v.origin, TrackedEnemyRef->LastVisibleLocation) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
{
MoveTo(pBot, AITAC_GetTeamStartingLocation(BotTeam), MOVESTYLE_NORMAL);
}
return true;
}
MoveTo(pBot, TrackedEnemyRef->LastSeenLocation, MOVESTYLE_NORMAL);
return true;
}
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, DesiredCombatWeapon, CurrentEnemy);
if (bMustReloadCurrentWeapon)
{
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
BotReloadWeapons(pBot);
return true;
}
if (DistToEnemy > sqrf(DesiredDistance))
{
if (IdealAttackWeapon != DesiredCombatWeapon)
{
BotReloadWeapons(pBot);
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
return true;
}
MoveTo(pBot, LastEnemySeenLocation, MOVESTYLE_NORMAL);
}
else
{
if (bEnemyIsRanged)
{
Vector EnemyOrientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR);
pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(RightDir) : UTIL_GetVectorNormal2D(-RightDir);
// Let's get ziggy with it
if (gpGlobals->time > pBot->BotNavInfo.NextZigTime)
{
pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig;
pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f);
}
BotMovementInputs(pBot);
}
else
{
float MinDesiredDist = GetMinIdealWeaponRange(DesiredCombatWeapon);
Vector Orientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
float EnemyMoveDot = UTIL_GetDotProduct2D(UTIL_GetVectorNormal2D(CurrentEnemy->v.velocity), -Orientation);
// Enemy is too close for comfort, or is moving towards us. Back up
if (DistToEnemy < MinDesiredDist || EnemyMoveDot > 0.7f)
{
Vector RetreatLocation = pBot->CurrentFloorPosition - (Orientation * 50.0f);
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, RetreatLocation))
{
MoveDirectlyTo(pBot, RetreatLocation);
}
if (DesiredCombatWeapon != WEAPON_MARINE_KNIFE)
{
if (DistToEnemy < sqrf(100.0f))
{
if (IsPlayerReloading(pBot->Player) && CanInterruptWeaponReload(GetPlayerCurrentWeapon(pBot->Player)) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) > 0)
{
InterruptReload(pBot);
return true;
}
BotJump(pBot);
}
}
}
else
{
MoveTo(pBot, TrackedEnemyRef->LastSeenLocation, MOVESTYLE_NORMAL);
}
}
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
}
}
return false;
}
@ -2631,136 +2934,10 @@ bool MarineCombatThink(AvHAIPlayer* pBot)
pBot->LastCombatTime = gpGlobals->time;
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL))
{
return BombardierCombatThink(pBot);
}
else
{
return RegularMarineCombatThink(pBot);
}
return RegularMarineCombatThink(pBot);
}
return false;
edict_t* pEdict = pBot->Edict;
edict_t* CurrentEnemy = pBot->TrackedEnemies[pBot->CurrentEnemy].EnemyEdict;
enemy_status* TrackedEnemyRef = &pBot->TrackedEnemies[pBot->CurrentEnemy];
pBot->LastCombatTime = gpGlobals->time;
// ENEMY IS OUT OF SIGHT
if (!TrackedEnemyRef->bHasLOS)
{
MarineHuntEnemy(pBot, TrackedEnemyRef);
return true;
}
// ENEMY IS VISIBLE
AvHAIWeapon DesiredCombatWeapon = BotMarineChooseBestWeapon(pBot, CurrentEnemy);
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, DesiredCombatWeapon, TrackedEnemyRef->LastSeenLocation, CurrentEnemy);
if (LOSCheck == ATTACK_SUCCESS)
{
BotShootLocation(pBot, DesiredCombatWeapon, TrackedEnemyRef->LastSeenLocation);
}
float DistFromEnemy = vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->v.origin);
if (DesiredCombatWeapon != WEAPON_MARINE_KNIFE)
{
if (DistFromEnemy < sqrf(100.0f))
{
if (IsPlayerReloading(pBot->Player) && CanInterruptWeaponReload(GetPlayerCurrentWeapon(pBot->Player)) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) > 0)
{
InterruptReload(pBot);
}
BotJump(pBot);
}
}
// We're going to have the marine always try and use their primary weapon, which means
// that they will try and put enough distance between themselves and the enemy to use it effectively,
// and retreat if they need to reload or are out of ammo
// We are using our primary weapon right now (has ammo left in the clip)
if (DesiredCombatWeapon == PrimaryWeapon)
{
BotLookAt(pBot, CurrentEnemy);
if (LOSCheck == ATTACK_OUTOFRANGE)
{
MoveTo(pBot, TrackedEnemyRef->LastFloorPosition, MOVESTYLE_NORMAL);
if (gpGlobals->time - TrackedEnemyRef->LastSeenTime > 5.0f)
{
BotReloadWeapons(pBot);
}
return true;
}
// Note that we already do visibility checks above, so blocked here means there is another player or structure in the way
if (LOSCheck == ATTACK_BLOCKED)
{
edict_t* TracedEntity = UTIL_TraceEntity(pEdict, pBot->CurrentEyePosition, UTIL_GetCentreOfEntity(CurrentEnemy));
// Just blast through an alien structure if it's in the way
if (!FNullEnt(TracedEntity) && TracedEntity != CurrentEnemy)
{
if (TracedEntity->v.team != 0 && TracedEntity->v.team != pEdict->v.team)
{
BotShootTarget(pBot, DesiredCombatWeapon, TracedEntity);
}
}
float MinDesiredDist = GetMinIdealWeaponRange(DesiredCombatWeapon);
Vector EngagementLocation = pBot->BotNavInfo.TargetDestination;
float EngagementLocationDist = vDist2DSq(EngagementLocation, CurrentEnemy->v.origin);
if (!EngagementLocation || EngagementLocationDist < sqrf(MinDesiredDist) || PerformAttackLOSCheck(EngagementLocation + Vector(0.0f, 0.0f, 50.0f), DesiredCombatWeapon, CurrentEnemy) != ATTACK_SUCCESS)
{
EngagementLocation = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, CurrentEnemy->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
if (EngagementLocation != ZERO_VECTOR && PerformAttackLOSCheck(EngagementLocation + Vector(0.0f, 0.0f, 50.0f), DesiredCombatWeapon, CurrentEnemy) != ATTACK_SUCCESS)
{
EngagementLocation = ZERO_VECTOR;
}
}
MoveTo(pBot, EngagementLocation, MOVESTYLE_NORMAL);
return true;
}
if (LOSCheck == ATTACK_SUCCESS)
{
float MinDesiredDist = GetMinIdealWeaponRange(DesiredCombatWeapon);
Vector Orientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
float EnemyMoveDot = UTIL_GetDotProduct2D(UTIL_GetVectorNormal2D(CurrentEnemy->v.velocity), -Orientation);
// Enemy is too close for comfort, or is moving towards us. Back up
if (DistFromEnemy < MinDesiredDist || EnemyMoveDot > 0.7f)
{
Vector RetreatLocation = pBot->CurrentFloorPosition - (Orientation * 50.0f);
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, RetreatLocation))
{
MoveDirectlyTo(pBot, RetreatLocation);
}
}
}
return true;
}
return true;
}
void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
@ -4703,23 +4880,23 @@ bool LerkCombatThink(AvHAIPlayer* pBot)
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK)
{
pBot->DesiredCombatWeapon = WEAPON_LERK_BITE;
AvHAIWeapon DesiredWeapon = WEAPON_LERK_BITE;
if (vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
{
if (!IsAreaAffectedBySpores(CurrentEnemy->v.origin))
{
pBot->DesiredCombatWeapon = WEAPON_LERK_SPORES;
DesiredWeapon = WEAPON_LERK_SPORES;
}
}
MoveTo(pBot, CurrentEnemy->v.origin, MOVESTYLE_NORMAL);
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, pBot->DesiredCombatWeapon, CurrentEnemy);
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, DesiredWeapon, CurrentEnemy);
if (LOSCheck == ATTACK_SUCCESS)
{
BotShootTarget(pBot, pBot->DesiredCombatWeapon, CurrentEnemy);
BotShootTarget(pBot, DesiredWeapon, CurrentEnemy);
}
return true;

View file

@ -20,7 +20,8 @@ bool CanBotLeap(AvHAIPlayer* pBot);
void BotLeap(AvHAIPlayer* pBot, const Vector TargetLocation);
float GetLeapCost(AvHAIPlayer* pBot);
void BotReloadWeapons(AvHAIPlayer* pBot);
// Returns true if the bot needs to reload
bool BotReloadWeapons(AvHAIPlayer* pBot);
// Make the bot type something in either global or team chat
void BotSay(AvHAIPlayer* pBot, bool bTeamSay, float Delay, char* textToSay);
@ -105,6 +106,8 @@ void UpdateAIPlayerDMRole(AvHAIPlayer* pBot);
bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot);
void AIPlayerTakeDamage(AvHAIPlayer* pBot, int damageTaken, edict_t* aggressor);
int BotGetNextEnemyTarget(AvHAIPlayer* pBot);
AvHMessageID AlienGetDesiredUpgrade(AvHAIPlayer* pBot, HiveTechStatus DesiredTech);

View file

@ -234,7 +234,7 @@ void AIMGR_UpdateFillTeams()
}
}
if (TeamSizeA < NumDesiredTeamA)
if (TeamSizeA < NumDesiredTeamA && TeamSizeA <= TeamSizeB)
{
AIMGR_AddAIPlayerToTeam(1);
return;
@ -249,7 +249,7 @@ void AIMGR_UpdateFillTeams()
}
}
if (TeamSizeB < NumDesiredTeamB)
if (TeamSizeB < NumDesiredTeamB && TeamSizeB <= TeamSizeA)
{
AIMGR_AddAIPlayerToTeam(2);
return;
@ -616,52 +616,21 @@ void AIMGR_UpdateAIPlayers()
BotIt++;
}
vector<AvHAIPlayer*> AlienPlayers = AIMGR_GetAIPlayersOnTeam(TEAM_TWO);
int NumBuilders = 0;
int NumCappers = 0;
int NumHarassers = 0;
int NumAssault = 0;
for (auto it = AlienPlayers.begin(); it != AlienPlayers.end(); it++)
if (!vIsZero(DebugVector1) && !vIsZero(DebugVector2))
{
AvHAIPlayer* NewCapper = (*it);
vector<bot_path_node> path;
if (NewCapper)
nav_profile NavProfile = GetBaseNavProfile(MARINE_BASE_NAV_PROFILE);
NavProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
dtStatus PathStatus = FindPathClosestToPoint(NavProfile, DebugVector1, DebugVector2, path, 100.0f);
if (dtStatusSucceed(PathStatus))
{
switch (NewCapper->BotRole)
{
case BOT_ROLE_BUILDER:
NumBuilders++;
break;
case BOT_ROLE_FIND_RESOURCES:
NumCappers++;
break;
case BOT_ROLE_HARASS:
NumHarassers++;
break;
case BOT_ROLE_ASSAULT:
NumAssault++;
break;
default:
break;
}
AIDEBUG_DrawPath(path, 0.1f);
}
}
char buf[256];
char interbuf[32];
sprintf(buf, "Builders: %d\n", NumBuilders);
sprintf(interbuf, "Cappers: %d\n", NumCappers);
strcat(buf, interbuf);
sprintf(interbuf, "Harassers: %d\n", NumHarassers);
strcat(buf, interbuf);
sprintf(interbuf, "Assault: %d\n", NumAssault);
strcat(buf, interbuf);
UTIL_DrawHUDText(INDEXENT(1), 0, 0.1f, 0.1f, 255, 255, 255, buf);
PrevTime = CurrTime;
}
@ -676,6 +645,16 @@ int AIMGR_GetNumAIPlayers()
return ActiveAIPlayers.size();
}
AvHTeamNumber AIMGR_GetTeamANumber()
{
return GetGameRules()->GetTeamANumber();
}
AvHTeamNumber AIMGR_GetTeamBNumber()
{
return GetGameRules()->GetTeamANumber();
}
vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team)
{
vector<AvHPlayer*> Result;
@ -684,7 +663,7 @@ vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team)
{
edict_t* PlayerEdict = INDEXENT(i);
if (!FNullEnt(PlayerEdict) && PlayerEdict->v.team == Team)
if (!FNullEnt(PlayerEdict) && (Team == TEAM_IND || PlayerEdict->v.team == Team))
{
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
@ -832,6 +811,8 @@ void AIMGR_RoundStarted()
AITAC_RefreshHiveData();
UTIL_UpdateDoors(true);
UTIL_UpdateTileCache();
}
@ -905,6 +886,16 @@ AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team)
return nullptr;
}
AvHAIPlayer* AIMGR_GetBotRefFromPlayer(AvHPlayer* PlayerRef)
{
for (auto BotIt = ActiveAIPlayers.begin(); BotIt != ActiveAIPlayers.end(); BotIt++)
{
if (BotIt->Player == PlayerRef) { return &(*BotIt); }
}
return nullptr;
}
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam)
{
AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber();

View file

@ -64,11 +64,13 @@ int AIMGR_GetNumHumansOfClassOnTeam(AvHTeamNumber Team, AvHUser3 PlayerType);
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);
AvHAIPlayer* AIMGR_GetBotRefFromPlayer(AvHPlayer* PlayerRef);
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetTeamType(const AvHTeamNumber Team);
AvHTeamNumber AIMGR_GetTeamANumber();
AvHTeamNumber AIMGR_GetTeamBNumber();
// Returns all NS AI players. Does not include third-party bots
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers();

View file

@ -739,6 +739,8 @@ void AITAC_RefreshHiveData()
it->FloorLocation = AITAC_GetFloorLocationForHive(&(*it));
it->NextFloorLocationCheck = gpGlobals->time + (5.0f + (0.1f * NextRefresh));
AITAC_RefreshReachabilityForHive(&(*it));
}
NextRefresh++;
@ -991,6 +993,139 @@ void AITAC_RefreshAllResNodeReachability()
}
}
void AITAC_RefreshReachabilityForHive(AvHAIHiveDefinition* Hive)
{
if (!bTileCacheUpToDate) { return; }
Hive->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
Hive->TeamBReachabilityFlags = AI_REACHABILITY_NONE;
Vector HiveLocation = Hive->FloorLocation;
bool bOnNavMesh = UTIL_PointIsOnNavmesh(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), HiveLocation, Vector(max_player_use_reach, max_player_use_reach, max_player_use_reach));
if (!bOnNavMesh)
{
Hive->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
Hive->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
return;
}
Vector TeamAStart = AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber());
Vector TeamBStart = AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber());
if (GetGameRules()->GetTeamA()->GetTeamType() == AVH_CLASS_TYPE_MARINE)
{
bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamAStart, HiveLocation, max_player_use_reach);
if (bIsReachableMarine)
{
Hive->TeamAReachabilityFlags |= AI_REACHABILITY_MARINE;
Hive->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamAStart, HiveLocation, max_player_use_reach);
if (bIsReachableWelder)
{
Hive->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
Hive->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
else
{
bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamAStart, HiveLocation, max_player_use_reach);
bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamAStart, HiveLocation, max_player_use_reach);
bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamAStart, HiveLocation, max_player_use_reach);
if (bIsReachableSkulk)
{
Hive->TeamAReachabilityFlags |= AI_REACHABILITY_SKULK;
}
if (bIsReachableGorge)
{
Hive->TeamAReachabilityFlags |= AI_REACHABILITY_GORGE;
}
if (bIsReachableOnos)
{
Hive->TeamAReachabilityFlags |= AI_REACHABILITY_ONOS;
}
if (Hive->TeamAReachabilityFlags == AI_REACHABILITY_NONE)
{
Hive->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE)
{
bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamBStart, HiveLocation, max_player_use_reach);
if (bIsReachableMarine)
{
Hive->TeamBReachabilityFlags |= AI_REACHABILITY_MARINE;
Hive->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamBStart, HiveLocation, max_player_use_reach);
if (bIsReachableWelder)
{
Hive->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER;
}
else
{
Hive->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
else
{
bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamBStart, HiveLocation, max_player_use_reach);
bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamBStart, HiveLocation, max_player_use_reach);
bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamBStart, HiveLocation, max_player_use_reach);
if (bIsReachableSkulk)
{
Hive->TeamBReachabilityFlags |= AI_REACHABILITY_SKULK;
}
if (bIsReachableGorge)
{
Hive->TeamBReachabilityFlags |= AI_REACHABILITY_GORGE;
}
if (bIsReachableOnos)
{
Hive->TeamBReachabilityFlags |= AI_REACHABILITY_ONOS;
}
if (Hive->TeamBReachabilityFlags == AI_REACHABILITY_NONE)
{
Hive->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
{
if (Hives.size() == 0)
@ -1129,7 +1264,6 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE;
}
}
}
void AITAC_PopulateResourceNodes()
@ -2951,6 +3085,27 @@ bool UTIL_DroppedItemIsPrimaryWeapon(const AvHAIDeployableItemType ItemType)
return false;
}
AvHAIWeapon UTIL_GetWeaponTypeFromDroppedItem(const AvHAIDeployableItemType ItemType)
{
switch (ItemType)
{
case DEPLOYABLE_ITEM_GRENADELAUNCHER:
return WEAPON_MARINE_GL;
case DEPLOYABLE_ITEM_HMG:
return WEAPON_MARINE_HMG;
case DEPLOYABLE_ITEM_SHOTGUN:
return WEAPON_MARINE_SHOTGUN;
case DEPLOYABLE_ITEM_WELDER:
return WEAPON_MARINE_WELDER;
case DEPLOYABLE_ITEM_MINES:
return WEAPON_MARINE_MINES;
default:
return WEAPON_INVALID;
}
return WEAPON_INVALID;
}
Vector UTIL_GetNextMinePosition(edict_t* StructureToMine)
{
if (FNullEnt(StructureToMine)) { return ZERO_VECTOR; }
@ -4226,4 +4381,50 @@ bool AITAC_IsAlienUpgradeAvailableForTeam(AvHTeamNumber Team, HiveTechStatus Des
ChamberFilter.DeployableTypes = SearchType;
return (AITAC_DeployableExistsAtLocation(ZERO_VECTOR, &ChamberFilter));
}
int AITAC_GetNumWeaponsInPlay(AvHTeamNumber Team, AvHAIWeapon WeaponType)
{
int Result = 0;
vector<AvHPlayer*> PlayerList = AIMGR_GetAllPlayersOnTeam(Team);
for (auto it = PlayerList.begin(); it != PlayerList.end(); it++)
{
AvHPlayer* PlayerRef = (*it);
if (!PlayerRef) { continue; }
edict_t* PlayerEdict = PlayerRef->edict();
if (PlayerRef && !FNullEnt(PlayerEdict) && IsPlayerActiveInGame(PlayerEdict) && PlayerHasWeapon(PlayerRef, WeaponType))
{
Result++;
}
}
for (auto it = MarineDroppedItemMap.begin(); it != MarineDroppedItemMap.end(); it++)
{
AvHAIWeapon ThisWeaponType = UTIL_GetWeaponTypeFromDroppedItem(it->second.ItemType);
if (ThisWeaponType != WeaponType) { continue; }
unsigned int ReachabilityFlags = (Team == TEAM_IND) ? (it->second.TeamAReachabilityFlags | it->second.TeamBReachabilityFlags) : ((Team == GetGameRules()->GetTeamANumber()) ? it->second.TeamAReachabilityFlags : it->second.TeamBReachabilityFlags);
if (ReachabilityFlags != AI_REACHABILITY_UNREACHABLE)
{
DeployableSearchFilter ArmouryFilter;
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
ArmouryFilter.DeployableTeam = Team;
ArmouryFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
if (AITAC_DeployableExistsAtLocation(it->second.Location, &ArmouryFilter))
{
Result++;
}
}
}
return Result;
}

View file

@ -37,6 +37,7 @@ void AITAC_RefreshBuildableStructures();
AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure);
void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure);
void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode);
void AITAC_RefreshReachabilityForHive(AvHAIHiveDefinition* Hive);
void AITAC_RefreshAllResNodeReachability();
void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item);
void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure);
@ -131,6 +132,8 @@ bool AITAC_TeamHiveWithTechExists(const AvHTeamNumber Team, const AvHMessageID T
AvHAIDeployableItemType UTIL_GetItemTypeFromEdict(const edict_t* ItemEdict);
bool UTIL_DroppedItemIsPrimaryWeapon(const AvHAIDeployableItemType ItemType);
AvHAIWeapon UTIL_GetWeaponTypeFromDroppedItem(const AvHAIDeployableItemType ItemType);
bool UTIL_StructureIsResearching(edict_t* Structure);
bool UTIL_StructureIsResearching(edict_t* Structure, const AvHMessageID Research);
bool UTIL_StructureIsUpgrading(edict_t* Structure);
@ -177,4 +180,6 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo
bool AITAC_IsAlienUpgradeAvailableForTeam(AvHTeamNumber Team, HiveTechStatus DesiredTech);
int AITAC_GetNumWeaponsInPlay(AvHTeamNumber Team, AvHAIWeapon WeaponType);
#endif

View file

@ -397,19 +397,19 @@ float GetMaxIdealWeaponRange(const AvHAIWeapon Weapon)
case WEAPON_SKULK_XENOCIDE:
return (float)BALANCE_VAR(kDivineWindRadius) * 0.8f;
case WEAPON_ONOS_GORE:
return (float)BALANCE_VAR(kClawsRange);
return (float)BALANCE_VAR(kClawsRange) + 20.0f;
case WEAPON_ONOS_DEVOUR:
return (float)BALANCE_VAR(kDevourRange);
case WEAPON_FADE_SWIPE:
return (float)BALANCE_VAR(kSwipeRange);
return (float)BALANCE_VAR(kSwipeRange) + 30.0f;
case WEAPON_SKULK_BITE:
return (float)BALANCE_VAR(kBiteRange);
return (float)BALANCE_VAR(kBiteRange) + 20.0f;
case WEAPON_LERK_BITE:
return (float)BALANCE_VAR(kBite2Range);
return (float)BALANCE_VAR(kBite2Range) + 20.0f;
case WEAPON_GORGE_HEALINGSPRAY:
return (float)BALANCE_VAR(kHealingSprayRange) * 0.5f;
case WEAPON_MARINE_WELDER:
return (float)BALANCE_VAR(kWelderRange);
return (float)BALANCE_VAR(kWelderRange) + 10.0f;
default:
return max_player_use_reach;
}

View file

@ -255,6 +255,8 @@
#include "AvHNetworkMessages.h"
#include "AvHNexusServer.h"
#include "AIPlayers/AvHAIPlayerManager.h"
std::string GetLogStringForPlayer( edict_t *pEntity );
extern int gJetpackEventID;
@ -9162,6 +9164,17 @@ int AvHPlayer::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, floa
if(pevAttacker)
{
CBasePlayer* inAttackingPlayer = dynamic_cast<CBasePlayer*>(CBaseEntity::Instance(ENT(pevAttacker)));
if (inAttackingPlayer)
{
AvHAIPlayer* VictimBot = AIMGR_GetBotRefFromPlayer(this);
if (VictimBot)
{
AIPlayerTakeDamage(VictimBot, flDamage, inAttackingPlayer->edict());
}
}
const char* inWeaponName = STRING(pevInflictor->classname);
if(inAttackingPlayer && inWeaponName)
{