mirror of
https://github.com/ENSL/NS.git
synced 2025-03-14 06:34:33 +00:00
Commander improvements
This commit is contained in:
parent
940e4b8074
commit
7dcd3ca1d8
13 changed files with 1159 additions and 542 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue