mirror of
https://github.com/ENSL/NS.git
synced 2025-04-20 16:30:56 +00:00
Start reimplementing alien AI
This commit is contained in:
parent
933ed063f0
commit
083f1ad3ac
17 changed files with 931 additions and 502 deletions
|
@ -126,11 +126,11 @@ cvar_t avh_version = {kvVersion, "330", FCVAR_SERVER};
|
|||
|
||||
// AI Player Settings
|
||||
cvar_t avh_botsenabled = { kvBotsEnabled,"0", FCVAR_SERVER }; // Bots can be added to the server Y/N
|
||||
cvar_t avh_botautomode = { kvBotAutoMode,"0", FCVAR_SERVER }; // Defines automated behaviour for adding/removing bots
|
||||
cvar_t avh_botminplayers = { kvBotMinPlayers,"0", FCVAR_SERVER }; // If bots are enabled and auto mode == 2 then it will maintain this player count by adding/removing as needed
|
||||
cvar_t avh_botautomode = { kvBotAutoMode,"0", FCVAR_SERVER }; // Defines automated behaviour for adding/removing bots. 0 = manual (must add via console), 1 = automatic (auto-fills teams), 2 = balance only (only keeps teams even)
|
||||
cvar_t avh_botminplayers = { kvBotMinPlayers,"0", FCVAR_SERVER }; // If bots are enabled and auto mode == 1 then it will maintain this player count by adding/removing as needed
|
||||
cvar_t avh_botskill = { kvBotSkill,"0", FCVAR_SERVER }; // Sets the skill for the bots (0 = easiest, 3 = hardest)
|
||||
cvar_t avh_botusemapdefaults = { kvBotUseMapDefaults,"0", FCVAR_SERVER }; // Defines automated behaviour for adding/removing bots
|
||||
cvar_t avh_botcommandermode = { kvBotCommanderMode,"0", FCVAR_SERVER }; // 0 = Bots never command, 1 = Only if no humans on team, 2 = If nobody takes charge
|
||||
cvar_t avh_botusemapdefaults = { kvBotUseMapDefaults,"0", FCVAR_SERVER }; // If bot auto mode == 1 then the min players will be taken from the config
|
||||
cvar_t avh_botcommandermode = { kvBotCommanderMode,"0", FCVAR_SERVER }; // 0 = Bots never command, 1 = If nobody takes charge, 2 = Only if no humans on team
|
||||
|
||||
|
||||
//playtest cvars
|
||||
|
|
|
@ -74,7 +74,7 @@ bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDe
|
|||
|
||||
bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research)
|
||||
{
|
||||
if (FNullEnt(StructureToResearch->edict)) { return false; }
|
||||
if (!StructureToResearch || FNullEnt(StructureToResearch->edict)) { return false; }
|
||||
|
||||
// Don't do anything if the structure is being recycled, or we DON'T want to recycle but the structure is already busy
|
||||
if (StructureToResearch->EntityRef->GetIsRecycling() || (Research != BUILD_RECYCLE && StructureToResearch->EntityRef->GetIsResearching())) { return false; }
|
||||
|
@ -192,10 +192,15 @@ void AICOMM_AssignNewPlayerOrder(AvHAIPlayer* pBot, edict_t* Assignee, edict_t*
|
|||
NewOrder.Assignee = Assignee;
|
||||
NewOrder.OrderTarget = TargetEntity;
|
||||
NewOrder.OrderPurpose = OrderPurpose;
|
||||
|
||||
AICOMM_IssueOrderForAssignedJob(pBot, &NewOrder);
|
||||
NewOrder.LastReminderTime = 0.0f;
|
||||
NewOrder.LastPlayerDistance = 0.0f;
|
||||
|
||||
pBot->ActiveOrders.push_back(NewOrder);
|
||||
|
||||
if (AICOMM_DoesPlayerOrderNeedReminder(pBot, &NewOrder))
|
||||
{
|
||||
AICOMM_IssueOrderForAssignedJob(pBot, &NewOrder);
|
||||
}
|
||||
}
|
||||
|
||||
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order)
|
||||
|
@ -405,11 +410,10 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
if (SiegedHive)
|
||||
{
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE);
|
||||
int NumSiegingPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, SiegedHive->HiveEntity->edict(), ORDERPURPOSE_SIEGE_HIVE);
|
||||
|
||||
if ((NumAssignedPlayers + NumSiegingPlayers) < DesiredPlayers)
|
||||
if (NumAssignedPlayers < DesiredPlayers)
|
||||
{
|
||||
for (int i = 0; i < DesiredPlayers - (NumAssignedPlayers + NumSiegingPlayers); i++)
|
||||
for (int i = 0; i < (DesiredPlayers - NumAssignedPlayers); i++)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, SiegedHive->FloorLocation);
|
||||
|
||||
|
@ -425,6 +429,7 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
|
||||
AvHAIHiveDefinition* EmptyHive = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
int MinNumAssignedPlayers = 0;
|
||||
|
||||
for (auto it = Hives.begin(); it != Hives.end(); it++)
|
||||
{
|
||||
|
@ -432,15 +437,15 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
if (ThisHive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
if (AICOMM_IsHiveFullySecured(pBot, ThisHive, false)) { continue; }
|
||||
|
||||
int NumPlayersSecuring = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ThisHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE);
|
||||
|
||||
if ((NumPlayersSecuring + NumAssignedPlayers) < DesiredPlayers)
|
||||
if (NumAssignedPlayers < DesiredPlayers)
|
||||
{
|
||||
float ThisDist = vDist2DSq(AITAC_GetCommChairLocation(pBot->Player->GetTeam()), ThisHive->Location);
|
||||
|
||||
if (!EmptyHive || ThisDist < MinDist)
|
||||
{
|
||||
MinNumAssignedPlayers = NumAssignedPlayers;
|
||||
EmptyHive = ThisHive;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
|
@ -449,11 +454,14 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
|
||||
if (EmptyHive)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, EmptyHive->FloorLocation);
|
||||
|
||||
if (!FNullEnt(NewAssignee))
|
||||
for (int i = 0; i < (DesiredPlayers - MinNumAssignedPlayers); i++)
|
||||
{
|
||||
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, EmptyHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE);
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, EmptyHive->FloorLocation);
|
||||
|
||||
if (!FNullEnt(NewAssignee))
|
||||
{
|
||||
AICOMM_AssignNewPlayerOrder(pBot, NewAssignee, EmptyHive->HiveEntity->edict(), ORDERPURPOSE_SECURE_HIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,10 +474,9 @@ void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot)
|
|||
|
||||
if (ResNode)
|
||||
{
|
||||
int NumPlayersSecuring = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), ResNode->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
int NumAssignedPlayers = AICOMM_GetNumPlayersAssignedToOrder(pBot, ResNode->ResourceEntity->edict(), ORDERPURPOSE_SECURE_RESNODE);
|
||||
|
||||
if ((NumPlayersSecuring + NumAssignedPlayers) < 1)
|
||||
if (NumAssignedPlayers < 1)
|
||||
{
|
||||
edict_t* NewAssignee = AICOMM_GetPlayerWithNoOrderNearestLocation(pBot, ResNode->Location);
|
||||
|
||||
|
@ -763,7 +770,7 @@ bool AICOMM_IsRequestValid(ai_commander_request* Request)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
|
||||
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
|
||||
{
|
||||
|
||||
AvHTeamNumber TeamNumber = pBot->Player->GetTeam();
|
||||
|
@ -784,7 +791,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
|
|||
|
||||
if (NumInfantryPortals < 2)
|
||||
{
|
||||
if (AICOMM_BuildInfantryPortal(pBot, CommChair, Action))
|
||||
if (AICOMM_BuildInfantryPortal(pBot, CommChair))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -872,7 +879,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
|
|||
|
||||
if (HiveToSecure)
|
||||
{
|
||||
if (AICOMM_PerformNextSecureHiveAction(pBot, HiveToSecure, Action))
|
||||
if (AICOMM_PerformNextSecureHiveAction(pBot, HiveToSecure))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -924,7 +931,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
|
|||
|
||||
if (HiveToSiege)
|
||||
{
|
||||
if (AICOMM_PerformNextSiegeHiveAction(pBot, HiveToSiege, Action))
|
||||
if (AICOMM_PerformNextSiegeHiveAction(pBot, HiveToSiege))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1001,10 +1008,63 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot, commander_action* Action)
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot)
|
||||
{
|
||||
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
|
||||
|
||||
vector<AvHAIHiveDefinition*> Hives = AITAC_GetAllHives();
|
||||
|
||||
for (auto it = Hives.begin(); it != Hives.end(); it++)
|
||||
{
|
||||
AvHAIHiveDefinition* Hive = (*it);
|
||||
|
||||
if (Hive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
|
||||
DeployableSearchFilter TFFilter;
|
||||
TFFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
TFFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY;
|
||||
TFFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
TFFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_ELECTRIFIED;
|
||||
TFFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
AvHAIBuildableStructure* NearestTF = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &TFFilter);
|
||||
|
||||
if (NearestTF)
|
||||
{
|
||||
TFFilter.DeployableTypes = STRUCTURE_MARINE_TURRET;
|
||||
TFFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
int NumTurrets = AITAC_GetNumDeployablesNearLocation(NearestTF->Location, &TFFilter);
|
||||
|
||||
if (NumTurrets > 0)
|
||||
{
|
||||
if (AICOMM_ResearchTech(pBot, NearestTF, RESEARCH_ELECTRICAL))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pBot->Player->GetResources() > 60)
|
||||
{
|
||||
if (Hive->HiveResNodeRef && Hive->HiveResNodeRef->OwningTeam == pBot->Player->GetTeam())
|
||||
{
|
||||
edict_t* Tower = Hive->HiveResNodeRef->ActiveTowerEntity;
|
||||
if (!FNullEnt(Tower) && UTIL_StructureIsFullyBuilt(Tower) && !UTIL_IsStructureElectrified(Tower))
|
||||
{
|
||||
AvHAIBuildableStructure* ResTower = AITAC_GetDeployableRefFromEdict(Tower);
|
||||
|
||||
if (ResTower && AICOMM_ResearchTech(pBot, ResTower, RESEARCH_ELECTRICAL))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTeam = CommanderTeam;
|
||||
StructureFilter.ReachabilityTeam = CommanderTeam;
|
||||
|
@ -1252,7 +1312,7 @@ const AvHAIResourceNode* AICOMM_GetNearestResourceNodeCapOpportunity(const AvHTe
|
|||
return Result;
|
||||
}
|
||||
|
||||
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action)
|
||||
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege)
|
||||
{
|
||||
AvHTeamNumber CommanderTeam = pBot->Player->GetTeam();
|
||||
|
||||
|
@ -1385,7 +1445,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
|
||||
}
|
||||
|
||||
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure, commander_action* Action)
|
||||
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure)
|
||||
{
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE;
|
||||
|
@ -1481,16 +1541,10 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!UTIL_IsStructureElectrified(ExistingTF->edict))
|
||||
{
|
||||
return AICOMM_ResearchTech(pBot, ExistingTF, RESEARCH_ELECTRICAL);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander_action* Action)
|
||||
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair)
|
||||
{
|
||||
if (FNullEnt(CommChair) || !UTIL_StructureIsFullyBuilt(CommChair)) { return false; }
|
||||
|
||||
|
@ -1840,8 +1894,8 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot)
|
|||
|
||||
if (AICOMM_CheckForNextRecycleAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextSupportAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextBuildAction(pBot, &pBot->BuildAction)) { return; }
|
||||
if (AICOMM_CheckForNextResearchAction(pBot, &pBot->ResearchAction)) { return; }
|
||||
if (AICOMM_CheckForNextBuildAction(pBot)) { return; }
|
||||
if (AICOMM_CheckForNextResearchAction(pBot)) { return; }
|
||||
}
|
||||
|
||||
bool AICOMM_IsCommanderActionValid(AvHAIPlayer* pBot, commander_action* Action)
|
||||
|
@ -1931,7 +1985,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
|
|||
|
||||
if (Hive->Status != HIVE_STATUS_UNBUILT) { continue; }
|
||||
|
||||
if (AICOMM_IsHiveFullySecured(CommanderBot, Hive, true)) { continue; }
|
||||
if (AICOMM_IsHiveFullySecured(CommanderBot, Hive, false)) { continue; }
|
||||
|
||||
Vector SecureLocation = Hive->FloorLocation;
|
||||
|
||||
|
|
|
@ -35,10 +35,10 @@ bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* O
|
|||
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order);
|
||||
|
||||
void AICOMM_ClearAction(commander_action* Action);
|
||||
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action);
|
||||
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot, commander_action* Action);
|
||||
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
|
||||
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
|
||||
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
|
||||
void AICOMM_SetDeployStructureAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableStructureType StructureToBuild, const Vector Location, bool bIsUrgent);
|
||||
|
@ -48,9 +48,9 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot);
|
|||
|
||||
const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation);
|
||||
|
||||
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander_action* Action);
|
||||
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action);
|
||||
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure, commander_action* Action);
|
||||
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair);
|
||||
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege);
|
||||
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure);
|
||||
|
||||
ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edict_t* Requestor);
|
||||
void AICOMM_CheckNewRequests(AvHAIPlayer* pBot);
|
||||
|
|
|
@ -204,7 +204,7 @@ typedef enum _AVHAIBOTROLE
|
|||
typedef struct _OFF_MESH_CONN
|
||||
{
|
||||
unsigned int ConnectionRefs[2];
|
||||
unsigned short ConnectionFlags = 0;
|
||||
unsigned int ConnectionFlags = 0;
|
||||
Vector FromLocation = g_vecZero;
|
||||
Vector ToLocation = g_vecZero;
|
||||
edict_t* TargetObject = nullptr;
|
||||
|
@ -379,6 +379,14 @@ typedef enum
|
|||
}
|
||||
BotAttackResult;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BUILD_ATTEMPT_NONE = 0,
|
||||
BUILD_ATTEMPT_PENDING,
|
||||
BUILD_ATTEMPT_SUCCESS,
|
||||
BUILD_ATTEMPT_FAILED
|
||||
} BotBuildAttemptStatus;
|
||||
|
||||
|
||||
// Bot path node. A path will be several of these strung together to lead the bot to its destination
|
||||
typedef struct _BOT_PATH_NODE
|
||||
|
@ -386,7 +394,7 @@ typedef struct _BOT_PATH_NODE
|
|||
Vector FromLocation = g_vecZero; // Location to move from
|
||||
Vector Location = g_vecZero; // Location to move to
|
||||
float requiredZ = 0.0f; // If climbing a up ladder or wall, how high should they aim to get before dismounting.
|
||||
unsigned short flag = 0; // Is this a ladder movement, wall climb, walk etc
|
||||
unsigned int flag = 0; // Is this a ladder movement, wall climb, walk etc
|
||||
unsigned char area = 0; // Is this a crouch area, normal walking area etc
|
||||
unsigned int poly = 0; // The nav mesh poly this point resides on
|
||||
} bot_path_node;
|
||||
|
@ -447,6 +455,16 @@ 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;
|
||||
Vector AttemptedLocation = g_vecZero;
|
||||
int NumAttempts = 0;
|
||||
BotBuildAttemptStatus BuildStatus = BUILD_ATTEMPT_NONE;
|
||||
float BuildAttemptTime = 0.0f;
|
||||
AvHAIBuildableStructure* LinkedStructure = nullptr;
|
||||
} AvHAIBuildAttempt;
|
||||
|
||||
// Contains the bot's current navigation info, such as current path
|
||||
typedef struct _NAV_STATUS
|
||||
{
|
||||
|
@ -486,7 +504,7 @@ typedef struct _NAV_STATUS
|
|||
BotMoveStyle MoveStyle = MOVESTYLE_NORMAL; // Current desired move style (e.g. normal, ambush, hide). Will trigger new path calculations if this changes
|
||||
float LastPathCalcTime = 0.0f; // When the bot last calculated a path, to limit how frequently it can recalculate
|
||||
|
||||
bool bPendingRecalculation = false; // This bot should recalculate its path as soon as it can
|
||||
float NextForceRecalc = 0.0f; // If set, then the bot will force-recalc its current path
|
||||
|
||||
bool bZig; // Is the bot zigging, or zagging?
|
||||
float NextZigTime; // Controls how frequently they zig or zag
|
||||
|
@ -496,7 +514,7 @@ typedef struct _NAV_STATUS
|
|||
nav_profile NavProfile;
|
||||
bool bNavProfileChanged = false;
|
||||
|
||||
unsigned short SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.)
|
||||
unsigned int SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.)
|
||||
|
||||
|
||||
} nav_status;
|
||||
|
@ -629,11 +647,7 @@ typedef struct AVH_AI_PLAYER
|
|||
|
||||
nav_status BotNavInfo; // Bot's movement information, their current path, where in the path they are etc.
|
||||
|
||||
commander_action BuildAction;
|
||||
commander_action ResearchAction;
|
||||
commander_action SupportAction;
|
||||
commander_action RecycleAction;
|
||||
commander_action* CurrentAction;
|
||||
AvHAIBuildAttempt BuildAttempts;
|
||||
|
||||
vector<ai_commander_request> ActiveRequests;
|
||||
vector<ai_commander_order> ActiveOrders;
|
||||
|
|
|
@ -223,6 +223,25 @@ bool IsEdictStructure(const edict_t* edict)
|
|||
return (GetDeployableObjectTypeFromEdict(edict) != STRUCTURE_NONE);
|
||||
}
|
||||
|
||||
bool IsDamagingStructure(const edict_t* StructureEdict)
|
||||
{
|
||||
return IsDamagingStructure(GetStructureTypeFromEdict(StructureEdict));
|
||||
}
|
||||
|
||||
bool IsDamagingStructure(AvHAIDeployableStructureType StructureType)
|
||||
{
|
||||
switch (StructureType)
|
||||
{
|
||||
case STRUCTURE_ALIEN_OFFENCECHAMBER:
|
||||
case STRUCTURE_MARINE_TURRETFACTORY:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AvHAIDeployableStructureType GetStructureTypeFromEdict(const edict_t* StructureEdict)
|
||||
{
|
||||
if (FNullEnt(StructureEdict)) { return STRUCTURE_NONE; }
|
||||
|
|
|
@ -28,6 +28,11 @@ bool IsEdictStructure(const edict_t* edict);
|
|||
|
||||
AvHAIDeployableStructureType GetStructureTypeFromEdict(const edict_t* StructureEdict);
|
||||
|
||||
// Returns true if this structure shoots back (turret or offence chamber)
|
||||
bool IsDamagingStructure(const edict_t* StructureEdict);
|
||||
// Returns true if this structure shoots back (turret or offence chamber)
|
||||
bool IsDamagingStructure(AvHAIDeployableStructureType StructureType);
|
||||
|
||||
bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation);
|
||||
|
||||
AvHAIDeployableStructureType GetDeployableObjectTypeFromEdict(const edict_t* StructureEdict);
|
||||
|
|
|
@ -126,7 +126,7 @@ struct OffMeshConnectionDef
|
|||
bool bBiDir = false;
|
||||
float Rad = 0.0f;
|
||||
unsigned char Area = 0;
|
||||
unsigned short Flag = 0;
|
||||
unsigned int Flag = 0;
|
||||
bool bPendingDelete = false;
|
||||
bool bDirty = false;
|
||||
};
|
||||
|
@ -244,13 +244,13 @@ struct MeshProcess : public dtTileCacheMeshProcess
|
|||
}
|
||||
else if (polyAreas[i] == DT_TILECACHE_TEAM1STRUCTURE_AREA)
|
||||
{
|
||||
polyAreas[i] = SAMPLE_POLYAREA_BLOCKED;
|
||||
polyFlags[i] = SAMPLE_POLYFLAGS_BLOCKED | SAMPLE_POLYFLAGS_TEAM1STRUCTURE;
|
||||
polyAreas[i] = SAMPLE_POLYAREA_STRUCTUREBLOCK;
|
||||
polyFlags[i] = SAMPLE_POLYFLAGS_TEAM1STRUCTURE;
|
||||
}
|
||||
else if (polyAreas[i] == DT_TILECACHE_TEAM2STRUCTURE_AREA)
|
||||
{
|
||||
polyAreas[i] = SAMPLE_POLYAREA_BLOCKED;
|
||||
polyFlags[i] = SAMPLE_POLYFLAGS_BLOCKED | SAMPLE_POLYFLAGS_TEAM2STRUCTURE;
|
||||
polyAreas[i] = SAMPLE_POLYAREA_STRUCTUREBLOCK;
|
||||
polyFlags[i] = SAMPLE_POLYFLAGS_TEAM2STRUCTURE;
|
||||
}
|
||||
else if (polyAreas[i] == DT_TILECACHE_WELD_AREA)
|
||||
{
|
||||
|
@ -431,7 +431,7 @@ unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector L
|
|||
|
||||
ObstacleNum = (unsigned int)ObsRef;
|
||||
|
||||
if (ObstacleNum > 0)
|
||||
if (ObstacleNum > 0 && NavMeshIndex != BUILDING_NAV_MESH)
|
||||
{
|
||||
bNavMeshModified = true;
|
||||
}
|
||||
|
@ -894,6 +894,7 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
|
||||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 2.0f);
|
||||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
|
||||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
|
||||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
|
||||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
|
||||
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_FLY | SAMPLE_POLYFLAGS_WALLCLIMB | SAMPLE_POLYFLAGS_WELD);
|
||||
|
@ -906,6 +907,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE | SAMPLE_POLYFLAGS_TEAM2PHASEGATE | SAMPLE_POLYFLAGS_DUCKJUMP | SAMPLE_POLYFLAGS_WELD | SAMPLE_POLYFLAGS_FLY);
|
||||
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
|
@ -919,6 +922,7 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
|
||||
|
@ -934,6 +938,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
|
@ -947,6 +953,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.5f);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
|
||||
|
@ -961,6 +969,8 @@ void UTIL_PopulateBaseNavProfiles()
|
|||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 2.0f);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 5.0f); // Onos is a wrecking machine, structures shouldn't be such an obstacle for them!
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
|
||||
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
|
||||
|
@ -1360,7 +1370,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
|
|||
m_navMesh->getPolyArea(StraightPolyPath[0], &CurrArea);
|
||||
m_navMesh->getPolyFlags(StraightPolyPath[0], &CurrFlags);
|
||||
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE | SAMPLE_POLYFLAGS_NOONOS);
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
// At this point we have our path. Copy it to the path store
|
||||
int nIndex = 0;
|
||||
|
@ -1380,7 +1390,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
|
|||
m_navMesh->getPolyArea(StraightPolyPath[nVert], &ThisArea);
|
||||
m_navMesh->getPolyFlags(StraightPolyPath[nVert], &ThisFlags);
|
||||
|
||||
ThisFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE);
|
||||
ThisFlags &= ~(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
if (ThisArea == SAMPLE_POLYAREA_GROUND || ThisArea == SAMPLE_POLYAREA_CROUCH)
|
||||
{
|
||||
|
@ -1605,7 +1615,7 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
|
|||
m_navMesh->getPolyFlags(StraightPolyPath[0], &CurrFlags);
|
||||
m_navMesh->getPolyArea(StraightPolyPath[0], &CurrArea);
|
||||
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE | SAMPLE_POLYFLAGS_NOONOS);
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
// At this point we have our path. Copy it to the path store
|
||||
int nIndex = 0;
|
||||
|
@ -1627,7 +1637,7 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
|
|||
m_navMesh->getPolyArea(StraightPolyPath[nVert], &ThisArea);
|
||||
m_navMesh->getPolyFlags(StraightPolyPath[nVert], &ThisFlags);
|
||||
|
||||
ThisFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE | SAMPLE_POLYFLAGS_NOONOS);
|
||||
ThisFlags &= ~(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
if (ThisArea == SAMPLE_POLYAREA_GROUND || ThisArea == SAMPLE_POLYAREA_CROUCH)
|
||||
{
|
||||
|
@ -1774,7 +1784,7 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
|
|||
m_navMesh->getPolyFlags(StraightPolyPath[0], &CurrFlags);
|
||||
m_navMesh->getPolyArea(StraightPolyPath[0], &CurrArea);
|
||||
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE | SAMPLE_POLYFLAGS_NOONOS);
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
// At this point we have our path. Copy it to the path store
|
||||
int nIndex = 0;
|
||||
|
@ -1861,7 +1871,7 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
|
|||
m_navMesh->getPolyFlags(StraightPolyPath[nVert], &CurrFlags);
|
||||
m_navMesh->getPolyArea(StraightPolyPath[nVert], &CurrArea);
|
||||
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE | SAMPLE_POLYFLAGS_NOONOS);
|
||||
CurrFlags &= ~(SAMPLE_POLYFLAGS_NOONOS);
|
||||
|
||||
NodeFromLocation = NextPathNode.Location;
|
||||
|
||||
|
@ -1971,6 +1981,8 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
return ((vDist2D(pEdict->v.origin, MoveTo) < playerRadius && bDestIsDirectlyReachable) || bAtOrPastDestination);
|
||||
}
|
||||
case SAMPLE_POLYFLAGS_BLOCKED:
|
||||
case SAMPLE_POLYFLAGS_TEAM1STRUCTURE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2STRUCTURE:
|
||||
return bAtOrPastDestination;
|
||||
case SAMPLE_POLYFLAGS_FALL:
|
||||
case SAMPLE_POLYFLAGS_JUMP:
|
||||
|
@ -2019,7 +2031,7 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
|
|||
|
||||
void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
|
||||
{
|
||||
edict_t* BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, SAMPLE_POLYAREA_GROUND, nullptr);
|
||||
edict_t* BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, pBot->BotNavInfo.CurrentPathPoint->flag, nullptr);
|
||||
|
||||
if (FNullEnt(BlockingDoorEdict))
|
||||
{
|
||||
|
@ -2114,7 +2126,7 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
|
|||
// Door must be shot to open
|
||||
if (Door->ActivationType == DOOR_SHOOT)
|
||||
{
|
||||
BotAttackTarget(pBot, Door->DoorEdict);
|
||||
BotAttackNonPlayerTarget(pBot, Door->DoorEdict);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2386,7 +2398,7 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* Pa
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector FromLocation, const Vector ToLocation, const unsigned short MovementFlag, edict_t* SearchBreakable)
|
||||
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector FromLocation, const Vector ToLocation, const unsigned int MovementFlag, edict_t* SearchBreakable)
|
||||
{
|
||||
Vector FromLoc = FromLocation;
|
||||
Vector ToLoc = ToLocation;
|
||||
|
@ -2495,7 +2507,7 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector From
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector ToLocation, const unsigned short MovementFlag, edict_t* SearchDoor)
|
||||
edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector ToLocation, const unsigned int MovementFlag, edict_t* SearchDoor)
|
||||
{
|
||||
|
||||
Vector FromLoc = FromLocation;
|
||||
|
@ -2839,6 +2851,10 @@ void NewMove(AvHAIPlayer* pBot)
|
|||
case SAMPLE_POLYFLAGS_BLOCKED:
|
||||
BlockedMove(pBot, MoveFrom, MoveTo);
|
||||
break;
|
||||
case SAMPLE_POLYFLAGS_TEAM1STRUCTURE:
|
||||
case SAMPLE_POLYFLAGS_TEAM2STRUCTURE:
|
||||
StructureBlockedMove(pBot, MoveFrom, MoveTo);
|
||||
break;
|
||||
case SAMPLE_POLYFLAGS_WALLCLIMB:
|
||||
{
|
||||
if (IsPlayerSkulk(pBot->Edict))
|
||||
|
@ -3024,6 +3040,50 @@ void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
|||
}
|
||||
}
|
||||
|
||||
void StructureBlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
||||
{
|
||||
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
|
||||
|
||||
pBot->desiredMovementDir = vForward;
|
||||
|
||||
DeployableSearchFilter BlockingFilter;
|
||||
BlockingFilter.DeployableTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam());
|
||||
BlockingFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(3.0f);
|
||||
|
||||
vector<AvHAIBuildableStructure*> BlockingStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &BlockingFilter);
|
||||
|
||||
AvHAIBuildableStructure* CulpritStructure = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = BlockingStructures.begin(); it != BlockingStructures.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
float ThisDist = vDistanceFromLine2DSq(StartPoint, EndPoint, ThisStructure->Location);
|
||||
|
||||
if (!CulpritStructure || ThisDist < MinDist)
|
||||
{
|
||||
CulpritStructure = ThisStructure;
|
||||
}
|
||||
}
|
||||
|
||||
if (CulpritStructure)
|
||||
{
|
||||
BotMoveLookAt(pBot, CulpritStructure->Location);
|
||||
|
||||
AvHAIWeapon AttackWeapon = (IsPlayerAlien(pBot->Edict)) ? BotAlienChooseBestWeaponForStructure(pBot, CulpritStructure->edict) : BotMarineChooseBestWeaponForStructure(pBot, CulpritStructure->edict);
|
||||
|
||||
if (GetPlayerCurrentWeapon(pBot->Player) != AttackWeapon)
|
||||
{
|
||||
pBot->DesiredMoveWeapon = AttackWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
BotShootTarget(pBot, AttackWeapon, CulpritStructure->edict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
||||
{
|
||||
|
||||
|
@ -4876,7 +4936,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
|
|||
|
||||
Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation;
|
||||
Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location;
|
||||
unsigned short flag = pBot->BotNavInfo.CurrentPathPoint->flag;
|
||||
unsigned int flag = pBot->BotNavInfo.CurrentPathPoint->flag;
|
||||
|
||||
Vector ClosestPointOnLine = vClosestPointOnLine2D(MoveFrom, MoveTo, pBot->Edict->v.origin);
|
||||
|
||||
|
@ -4893,7 +4953,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
|
|||
|
||||
if (flag == SAMPLE_POLYFLAGS_WALK)
|
||||
{
|
||||
if (UTIL_PointIsDirectlyReachable(pBot->Edict->v.origin, MoveFrom) || UTIL_PointIsDirectlyReachable(pBot->Edict->v.origin, MoveTo))
|
||||
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveFrom) || UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveTo))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -5040,31 +5100,37 @@ void SetBaseNavProfile(AvHAIPlayer* pBot)
|
|||
|
||||
void UpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle)
|
||||
{
|
||||
|
||||
if (IsPlayerMarine(pBot->Player))
|
||||
{
|
||||
MarineUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pBot->Edict->v.iuser3)
|
||||
{
|
||||
case AVH_USER3_MARINE_PLAYER:
|
||||
MarineUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
break;
|
||||
case AVH_USER3_ALIEN_PLAYER1:
|
||||
SkulkUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
return;
|
||||
break;
|
||||
case AVH_USER3_ALIEN_PLAYER2:
|
||||
GorgeUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
return;
|
||||
break;
|
||||
case AVH_USER3_ALIEN_PLAYER3:
|
||||
LerkUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
return;
|
||||
break;
|
||||
case AVH_USER3_ALIEN_PLAYER4:
|
||||
FadeUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
return;
|
||||
break;
|
||||
case AVH_USER3_ALIEN_PLAYER5:
|
||||
OnosUpdateBotMoveProfile(pBot, MoveStyle);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pBot->Player->GetTeam() == GetGameRules()->GetTeamANumber())
|
||||
{
|
||||
pBot->BotNavInfo.NavProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE);
|
||||
pBot->BotNavInfo.NavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pBot->BotNavInfo.NavProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_TEAM1STRUCTURE);
|
||||
pBot->BotNavInfo.NavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_TEAM2STRUCTURE);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5303,6 +5369,8 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
|
|||
|
||||
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())
|
||||
|
@ -5327,10 +5395,10 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
|
|||
}
|
||||
|
||||
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)
|
||||
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))
|
||||
|
@ -5363,7 +5431,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
|
|||
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 && (BotNavInfo->CurrentPath.size() == 0 || !vEquals(Destination, BotNavInfo->PathDestination));
|
||||
bool bShouldCalculatePath = bCanRecalculatePath && (bForceRecalculation || BotNavInfo->CurrentPath.size() == 0 || !vEquals(Destination, BotNavInfo->PathDestination));
|
||||
|
||||
if (bShouldCalculatePath)
|
||||
{
|
||||
|
@ -5374,8 +5442,8 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
|
|||
}
|
||||
|
||||
pBot->BotNavInfo.LastPathCalcTime = gpGlobals->time;
|
||||
BotNavInfo->bPendingRecalculation = false;
|
||||
BotNavInfo->bNavProfileChanged = false;
|
||||
BotNavInfo->NextForceRecalc = 0.0f;
|
||||
|
||||
if (vIsZero(BotNavInfo->TargetDestination))
|
||||
{
|
||||
|
@ -5831,7 +5899,7 @@ void BotFollowPath(AvHAIPlayer* pBot)
|
|||
|
||||
Vector MoveTo = BotNavInfo->CurrentPathPoint->Location;
|
||||
|
||||
unsigned short CurrentFlag = BotNavInfo->CurrentPathPoint->flag;
|
||||
unsigned int CurrentFlag = BotNavInfo->CurrentPathPoint->flag;
|
||||
|
||||
bool bIsUsingPhaseGate = (CurrentFlag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || CurrentFlag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
|
||||
|
||||
|
@ -7882,7 +7950,7 @@ nav_door* UTIL_GetClosestLiftToPoints(const Vector StartPoint, const Vector EndP
|
|||
return Result;
|
||||
}
|
||||
|
||||
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef)
|
||||
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned int flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef)
|
||||
{
|
||||
Vector ConnStart, ConnEnd;
|
||||
|
||||
|
|
|
@ -37,12 +37,14 @@ constexpr auto MAX_PATH_POLY = 512; // Max nav mesh polys that can be traversed
|
|||
// Possible area types. Water, Road, Door and Grass are not used (left-over from Detour library)
|
||||
enum SamplePolyAreas
|
||||
{
|
||||
SAMPLE_POLYAREA_GROUND = 0, // Regular ground movement
|
||||
SAMPLE_POLYAREA_CROUCH = 1, // Requires crouched movement
|
||||
SAMPLE_POLYAREA_BLOCKED = 2, // Requires a jump to get over
|
||||
SAMPLE_POLYAREA_FALLDAMAGE = 3, // Requires taking fall damage (if not immune to it)
|
||||
SAMPLE_POLYAREA_WALLCLIMB = 4, // Requires the ability to wall-stick, fly or blink
|
||||
SAMPLE_POLYAREA_OBSTRUCTION = 5 // There is a door or weldable object in the way
|
||||
SAMPLE_POLYAREA_GROUND = 0, // Regular ground movement
|
||||
SAMPLE_POLYAREA_CROUCH = 1, // Requires crouched movement
|
||||
SAMPLE_POLYAREA_BLOCKED = 2, // Requires a jump to get over
|
||||
SAMPLE_POLYAREA_FALLDAMAGE = 3, // Requires taking fall damage (if not immune to it)
|
||||
SAMPLE_POLYAREA_WALLCLIMB = 4, // Requires the ability to wall-stick, fly or blink
|
||||
SAMPLE_POLYAREA_OBSTRUCTION = 5, // There is a door or weldable object in the way
|
||||
SAMPLE_POLYAREA_STRUCTUREBLOCK = 6, // An enemy structure is blocking the way that must be destroyed
|
||||
SAMPLE_POLYAREA_PHASEGATE = 7 // Phase gate area, for area cost calculation
|
||||
};
|
||||
|
||||
// Possible movement types. Swim and door are not used
|
||||
|
@ -247,6 +249,8 @@ void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
|
|||
void JumpMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
|
||||
// Called by NewMove, determines movement direction and jump inputs to hop over obstructions (structures)
|
||||
void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
|
||||
// Called by NewMove, determines which structure is in the way and attacks it
|
||||
void StructureBlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
|
||||
// Called by NewMove, determines the movement direction and inputs required to drop down from start to end points
|
||||
void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
|
||||
// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint
|
||||
|
@ -275,9 +279,9 @@ DoorTrigger* UTIL_GetNearestDoorTriggerFromLift(edict_t* LiftEdict, nav_door* Do
|
|||
bool UTIL_IsPathBlockedByDoor(const Vector StartLoc, const Vector EndLoc, edict_t* SearchDoor);
|
||||
|
||||
edict_t* UTIL_GetDoorBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchDoor);
|
||||
edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector ToLocation, const unsigned short MovementFlag, edict_t* SearchDoor);
|
||||
edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector ToLocation, const unsigned int MovementFlag, edict_t* SearchDoor);
|
||||
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchBreakable);
|
||||
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector FromLocation, const Vector ToLocation, const unsigned short MovementFlag, edict_t* SearchBreakable);
|
||||
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector FromLocation, const Vector ToLocation, const unsigned int MovementFlag, edict_t* SearchBreakable);
|
||||
|
||||
|
||||
Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdict);
|
||||
|
@ -316,7 +320,7 @@ void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef);
|
|||
|
||||
void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs);
|
||||
|
||||
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef);
|
||||
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned int flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef);
|
||||
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef);
|
||||
|
||||
|
||||
|
|
|
@ -341,53 +341,6 @@ void BotLeap(AvHAIPlayer* pBot, const Vector TargetLocation)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void LinkDeployedObjectToCommanderAction(AvHAIPlayer* Commander, AvHAIBuildableStructure* NewStructure)
|
||||
{
|
||||
if (!Commander || !NewStructure || FNullEnt(Commander->Edict)) { return; }
|
||||
|
||||
commander_action* Action = nullptr;
|
||||
|
||||
if (Commander->BuildAction.bIsAwaitingBuildLink && Commander->BuildAction.StructureToBuild == NewStructure->StructureType)
|
||||
{
|
||||
if (vDist2DSq(Commander->BuildAction.BuildLocation, NewStructure->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
Action = &Commander->BuildAction;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Action)
|
||||
{
|
||||
if (Commander->SupportAction.bIsAwaitingBuildLink && Commander->SupportAction.StructureToBuild == NewStructure->StructureType)
|
||||
{
|
||||
if (vDist2DSq(Commander->SupportAction.BuildLocation, NewStructure->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
Action = &Commander->SupportAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Action) { return; }
|
||||
|
||||
NewStructure->LastSuccessfulCommanderLocation = Action->LastAttemptedCommanderLocation;
|
||||
NewStructure->LastSuccessfulCommanderAngle = Action->LastAttemptedCommanderAngle;
|
||||
NewStructure->Purpose = Action->ActionPurpose;
|
||||
|
||||
float CoolDown = (Action->NumDesiredInstances > 1) ? 0.33f : commander_action_cooldown;
|
||||
|
||||
Commander->next_commander_action_time = gpGlobals->time + CoolDown;
|
||||
|
||||
Action->NumInstances++;
|
||||
|
||||
if (Action->NumDesiredInstances > 1)
|
||||
{
|
||||
Action->BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], Action->BuildLocation, UTIL_MetresToGoldSrcUnits(1.0f));
|
||||
}
|
||||
|
||||
Action->bIsAwaitingBuildLink = false;
|
||||
|
||||
}
|
||||
|
||||
bot_msg* GetAvailableBotMsgSlot(AvHAIPlayer* pBot)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
|
@ -466,28 +419,9 @@ void BotDropWeapon(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
void BotAttackTarget(AvHAIPlayer* pBot, edict_t* Target)
|
||||
void BotAlienAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target)
|
||||
{
|
||||
if (FNullEnt(Target) || (Target->v.deadflag != DEAD_NO)) { return; }
|
||||
|
||||
AvHAIWeapon Weapon = WEAPON_INVALID;
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
{
|
||||
Weapon = BotMarineChooseBestWeaponForStructure(pBot, Target);
|
||||
}
|
||||
else
|
||||
{
|
||||
Weapon = BotAlienChooseBestWeaponForStructure(pBot, Target);
|
||||
}
|
||||
|
||||
// Add special logic for grenade launchers since they aren't used like regular marine hitscan weapons
|
||||
// This will handle things like firing from around corners, making sure they have cover from allies etc.
|
||||
if (Weapon == WEAPON_MARINE_GL)
|
||||
{
|
||||
BombardierAttackTarget(pBot, Target);
|
||||
return;
|
||||
}
|
||||
AvHAIWeapon Weapon = BotAlienChooseBestWeaponForStructure(pBot, Target);
|
||||
|
||||
BotAttackResult AttackResult = PerformAttackLOSCheck(pBot, Weapon, Target);
|
||||
|
||||
|
@ -502,21 +436,7 @@ void BotAttackTarget(AvHAIPlayer* pBot, edict_t* Target)
|
|||
pBot->Button |= IN_DUCK;
|
||||
}
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict))
|
||||
{
|
||||
if (AITAC_ShouldBotBeCautious(pBot))
|
||||
{
|
||||
MoveTo(pBot, Target->v.origin, MOVESTYLE_HIDE, 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveTo(pBot, Target->v.origin, MOVESTYLE_NORMAL, 100.0f);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Vector AttackPoint = (IsEdictPlayer(Target) || IsEdictStructure(Target)) ? Target->v.origin : UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Target);
|
||||
Vector AttackPoint = (IsEdictStructure(Target)) ? Target->v.origin : UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Target);
|
||||
|
||||
if (StructureType == STRUCTURE_ALIEN_HIVE)
|
||||
{
|
||||
|
@ -528,28 +448,42 @@ void BotAttackTarget(AvHAIPlayer* pBot, edict_t* Target)
|
|||
}
|
||||
}
|
||||
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_NORMAL, WeaponRange);
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
if (IsPlayerLerk(pBot->Edict))
|
||||
{
|
||||
if (gpGlobals->time - pBot->LastCombatTime > 5.0f)
|
||||
if (AITAC_ShouldBotBeCautious(pBot))
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_HIDE, 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_NORMAL, 100.0f);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_NORMAL, WeaponRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttackResult == ATTACK_BLOCKED)
|
||||
{
|
||||
if (!(IsEdictPlayer(Target) && !IsEdictStructure(Target)))
|
||||
|
||||
// We're attacking a shootable trigger
|
||||
if (!IsEdictStructure(Target))
|
||||
{
|
||||
Vector AttackPoint = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Target);
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_NORMAL, WeaponRange);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have regen and are hurt and are attacking a damaging structure, let us heal up a bit
|
||||
if ((StructureType == STRUCTURE_MARINE_TURRET || StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER) && GetPlayerOverallHealthPercent(pBot->Edict) < 0.75f && AvHGetAlienUpgradeLevel(pBot->Edict->v.iuser4, MASK_UPGRADE_2) > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (vIsZero(pBot->BotNavInfo.ActualMoveDestination) || UTIL_TraceEntity(pBot->Edict, pBot->BotNavInfo.ActualMoveDestination + Vector(0.0f, 0.0f, 32.0f), UTIL_GetCentreOfEntity(Target)) != Target)
|
||||
{
|
||||
Vector NewAttackLocation = ZERO_VECTOR;
|
||||
|
@ -589,6 +523,130 @@ void BotAttackTarget(AvHAIPlayer* pBot, edict_t* Target)
|
|||
}
|
||||
}
|
||||
|
||||
void BotMarineAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target)
|
||||
{
|
||||
AvHAIWeapon Weapon = BotMarineChooseBestWeaponForStructure(pBot, Target);
|
||||
|
||||
// Add special logic for grenade launchers since they aren't used like regular marine hitscan weapons
|
||||
// This will handle things like firing from around corners, making sure they have cover from allies etc.
|
||||
if (Weapon == WEAPON_MARINE_GL)
|
||||
{
|
||||
BombardierAttackTarget(pBot, Target);
|
||||
return;
|
||||
}
|
||||
|
||||
BotAttackResult AttackResult = PerformAttackLOSCheck(pBot, Weapon, Target);
|
||||
|
||||
float WeaponRange = GetMaxIdealWeaponRange(Weapon);
|
||||
|
||||
AvHAIDeployableStructureType StructureType = GetStructureTypeFromEdict(Target);
|
||||
|
||||
if (AttackResult == ATTACK_OUTOFRANGE)
|
||||
{
|
||||
if (vDist2DSq(pBot->Edict->v.origin, Target->v.origin) < sqrf(max_player_use_reach))
|
||||
{
|
||||
pBot->Button |= IN_DUCK;
|
||||
}
|
||||
|
||||
Vector AttackPoint = (IsEdictStructure(Target)) ? Target->v.origin : UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Target);
|
||||
|
||||
if (StructureType == STRUCTURE_ALIEN_HIVE)
|
||||
{
|
||||
const AvHAIHiveDefinition* HiveDefinition = AITAC_GetHiveFromEdict(Target);
|
||||
|
||||
if (HiveDefinition)
|
||||
{
|
||||
AttackPoint = HiveDefinition->FloorLocation;
|
||||
}
|
||||
}
|
||||
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_NORMAL, WeaponRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttackResult == ATTACK_BLOCKED)
|
||||
{
|
||||
// Finish reloading, we are probably behind cover
|
||||
if (IsPlayerReloading(pBot->Player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We're attacking a shootable trigger
|
||||
if (!IsEdictStructure(Target))
|
||||
{
|
||||
Vector AttackPoint = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Target);
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_NORMAL, WeaponRange);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vIsZero(pBot->BotNavInfo.ActualMoveDestination) || UTIL_TraceEntity(pBot->Edict, pBot->BotNavInfo.ActualMoveDestination + Vector(0.0f, 0.0f, 32.0f), UTIL_GetCentreOfEntity(Target)) != Target)
|
||||
{
|
||||
Vector NewAttackLocation = ZERO_VECTOR;
|
||||
|
||||
if (vIsZero(pBot->BotNavInfo.ActualMoveDestination))
|
||||
{
|
||||
NewAttackLocation = FindClosestNavigablePointToDestination(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, UTIL_GetEntityGroundLocation(Target), WeaponRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewAttackLocation = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, 2.0f);
|
||||
|
||||
// Did we find a clear spot we could attack from? If so, make that our new move destination
|
||||
if (NewAttackLocation != ZERO_VECTOR && UTIL_TraceEntity(pBot->Edict, NewAttackLocation + Vector(0.0f, 0.0f, 32.0f), UTIL_GetCentreOfEntity(Target)) == Target)
|
||||
{
|
||||
MoveTo(pBot, NewAttackLocation, MOVESTYLE_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveTo(pBot, pBot->BotNavInfo.TargetDestination, MOVESTYLE_NORMAL);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttackResult == ATTACK_SUCCESS)
|
||||
{
|
||||
if (IsPlayerReloading(pBot->Player))
|
||||
{
|
||||
if (StructureType == STRUCTURE_MARINE_TURRET || StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
|
||||
{
|
||||
MoveTo(pBot, AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()), MOVESTYLE_NORMAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we were ducking before then keep ducking
|
||||
if (pBot->Edict->v.oldbuttons & IN_DUCK)
|
||||
{
|
||||
pBot->Button |= IN_DUCK;
|
||||
}
|
||||
|
||||
BotShootTarget(pBot, Weapon, Target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BotAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target)
|
||||
{
|
||||
if (FNullEnt(Target) || (Target->v.deadflag != DEAD_NO)) { return; }
|
||||
|
||||
AvHAIWeapon Weapon = WEAPON_INVALID;
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
{
|
||||
BotMarineAttackNonPlayerTarget(pBot, Target);
|
||||
}
|
||||
else
|
||||
{
|
||||
BotAlienAttackNonPlayerTarget(pBot, Target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BotShootTarget(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, edict_t* Target)
|
||||
{
|
||||
if (FNullEnt(Target) || (Target->v.deadflag != DEAD_NO)) { return; }
|
||||
|
@ -926,8 +984,23 @@ void BotShootLocation(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, const Vector
|
|||
}
|
||||
}
|
||||
|
||||
void BotEvolveLifeform(AvHAIPlayer* pBot, AvHMessageID TargetLifeform)
|
||||
void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetLifeform)
|
||||
{
|
||||
if (!IsPlayerAlien(pBot->Edict)) { return; }
|
||||
|
||||
Vector EvolvePoint = UTIL_ProjectPointToNavmesh(DesiredEvolveLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
if (vIsZero(EvolvePoint))
|
||||
{
|
||||
EvolvePoint = DesiredEvolveLocation;
|
||||
}
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, EvolvePoint) > sqrf(32.0f))
|
||||
{
|
||||
MoveTo(pBot, EvolvePoint, MOVESTYLE_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
pBot->Impulse = TargetLifeform;
|
||||
}
|
||||
|
||||
|
@ -1487,6 +1560,15 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
|
|||
UpdateCommanderOrders(pBot);
|
||||
}
|
||||
|
||||
// If we tried placing a building as gorge, and nothing has appeared within 0.5s, the placement failed.
|
||||
if (pBot->BuildAttempts.BuildStatus == BUILD_ATTEMPT_PENDING)
|
||||
{
|
||||
if ((gpGlobals->time - pBot->BuildAttempts.BuildAttemptTime) > 0.5f)
|
||||
{
|
||||
pBot->BuildAttempts.BuildStatus = BUILD_ATTEMPT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CustomThink(AvHAIPlayer* pBot)
|
||||
|
@ -1510,7 +1592,14 @@ void DroneThink(AvHAIPlayer* pBot)
|
|||
BotProgressTask(pBot, &pBot->PrimaryBotTask);
|
||||
}
|
||||
|
||||
//AIDEBUG_DrawBotPath(pBot);
|
||||
AIDEBUG_DrawBotPath(pBot);
|
||||
|
||||
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_NONE) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
|
||||
|
||||
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
|
||||
{
|
||||
BotSwitchToWeapon(pBot, DesiredWeapon);
|
||||
}
|
||||
}
|
||||
|
||||
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole)
|
||||
|
@ -2032,6 +2121,12 @@ void AIPlayerDMThink(AvHAIPlayer* pBot)
|
|||
|
||||
void AIPlayerThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (pBot == AIMGR_GetDebugAIPlayer())
|
||||
{
|
||||
bool bBreak = true;
|
||||
|
||||
}
|
||||
|
||||
switch (GetGameRules()->GetMapMode())
|
||||
{
|
||||
case MAP_MODE_NS:
|
||||
|
|
|
@ -22,21 +22,21 @@ float GetLeapCost(AvHAIPlayer* pBot);
|
|||
|
||||
void BotReloadWeapons(AvHAIPlayer* pBot);
|
||||
|
||||
void LinkDeployedObjectToCommanderAction(AvHAIPlayer* Commander, AvHAIBuildableStructure* NewStructure);
|
||||
|
||||
// Make the bot type something in either global or team chat
|
||||
void BotSay(AvHAIPlayer* pBot, bool bTeamSay, float Delay, char* textToSay);
|
||||
bot_msg* GetAvailableBotMsgSlot(AvHAIPlayer* pBot);
|
||||
|
||||
void BotDropWeapon(AvHAIPlayer* pBot);
|
||||
|
||||
void BotAttackTarget(AvHAIPlayer* pBot, edict_t* Target);
|
||||
void BotAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target);
|
||||
void BotMarineAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target);
|
||||
void BotAlienAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target);
|
||||
|
||||
void BotShootTarget(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, edict_t* Target);
|
||||
void BotShootLocation(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, const Vector TargetLocation);
|
||||
void BombardierAttackTarget(AvHAIPlayer* pBot, edict_t* Target);
|
||||
|
||||
void BotEvolveLifeform(AvHAIPlayer* pBot, AvHMessageID Lifeform);
|
||||
void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetLifeform);
|
||||
|
||||
enemy_status* GetTrackedEnemyRefForTarget(AvHAIPlayer* pBot, edict_t* Target);
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ extern int m_spriteTexture;
|
|||
Vector DebugVector1 = ZERO_VECTOR;
|
||||
Vector DebugVector2 = ZERO_VECTOR;
|
||||
|
||||
AvHAIPlayer* DebugAIPlayer = nullptr;
|
||||
|
||||
vector<bot_path_node> DebugPath;
|
||||
|
||||
string BotNames[MAX_PLAYERS] = { "MrRobot",
|
||||
|
@ -70,17 +72,17 @@ string BotNames[MAX_PLAYERS] = { "MrRobot",
|
|||
|
||||
AvHAICommanderMode AIMGR_GetCommanderMode()
|
||||
{
|
||||
if (avh_botcommandermode.value == 0)
|
||||
if (avh_botcommandermode.value == 1)
|
||||
{
|
||||
return COMMANDERMODE_DISABLED;
|
||||
return COMMANDERMODE_ENABLED;
|
||||
}
|
||||
|
||||
if (avh_botcommandermode.value == 1)
|
||||
if (avh_botcommandermode.value == 2)
|
||||
{
|
||||
return COMMANDERMODE_IFNOHUMAN;
|
||||
}
|
||||
|
||||
return COMMANDERMODE_ENABLED;
|
||||
return COMMANDERMODE_DISABLED;
|
||||
|
||||
}
|
||||
|
||||
|
@ -124,15 +126,15 @@ void AIMGR_UpdateAIPlayerCounts()
|
|||
return;
|
||||
}
|
||||
|
||||
if (avh_botautomode.value == 1) // Balance only: bots will only be added and removed to ensure teams remain balanced
|
||||
if (avh_botautomode.value == 1) // Fill teams: bots will be added and removed to maintain a minimum player count
|
||||
{
|
||||
AIMGR_UpdateTeamBalance();
|
||||
AIMGR_UpdateFillTeams();
|
||||
return;
|
||||
}
|
||||
|
||||
if (avh_botautomode.value == 2) // Fill teams: bots will be added and removed to maintain a minimum player count
|
||||
if (avh_botautomode.value == 2) // Balance only: bots will only be added and removed to ensure teams remain balanced
|
||||
{
|
||||
AIMGR_UpdateFillTeams();
|
||||
AIMGR_UpdateTeamBalance();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -455,6 +457,12 @@ void AIMGR_AddAIPlayerToTeam(int Team)
|
|||
NewAIPlayer.Edict = BotEnt;
|
||||
NewAIPlayer.Team = theNewAIPlayer->GetTeam();
|
||||
|
||||
NewAIPlayer.CurrentTask = nullptr;
|
||||
NewAIPlayer.PrimaryBotTask.TaskType = TASK_NONE;
|
||||
NewAIPlayer.SecondaryBotTask.TaskType = TASK_NONE;
|
||||
NewAIPlayer.WantsAndNeedsTask.TaskType = TASK_NONE;
|
||||
NewAIPlayer.CommanderTask.TaskType = TASK_NONE;
|
||||
|
||||
const bot_skill BotSkillSettings = CONFIG_GetGlobalBotSkillLevel();
|
||||
|
||||
memcpy(&NewAIPlayer.BotSkillSettings, &BotSkillSettings, sizeof(bot_skill));
|
||||
|
@ -565,9 +573,7 @@ void AIMGR_UpdateAIPlayers()
|
|||
|
||||
UpdateBotChat(bot);
|
||||
|
||||
AIPlayerThink(bot);
|
||||
|
||||
AIDEBUG_DrawPath(DebugPath, 0.0f);
|
||||
DroneThink(bot);
|
||||
|
||||
BotUpdateDesiredViewRotation(bot);
|
||||
}
|
||||
|
@ -813,43 +819,6 @@ AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
AvHAIPlayer* AIMGR_FindPlayerOnTeamWaitingBuildLink(const AvHTeamNumber Team, const AvHAIDeployableStructureType NewStructure, const Vector BuildLocation)
|
||||
{
|
||||
vector<AvHAIPlayer*> TeamPlayers = AIMGR_GetAIPlayersOnTeam(Team);
|
||||
|
||||
for (auto it = TeamPlayers.begin(); it != TeamPlayers.end(); it++)
|
||||
{
|
||||
AvHAIPlayer* AIPlayer = (*it);
|
||||
|
||||
if (AIPlayer->PrimaryBotTask.bIsWaitingForBuildLink && AIPlayer->PrimaryBotTask.StructureType == NewStructure)
|
||||
{
|
||||
if (vDist2DSq(BuildLocation, AIPlayer->PrimaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
|
||||
{
|
||||
return AIPlayer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (AIPlayer->SecondaryBotTask.bIsWaitingForBuildLink && AIPlayer->SecondaryBotTask.StructureType == NewStructure)
|
||||
{
|
||||
if (vDist2DSq(BuildLocation, AIPlayer->SecondaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
|
||||
{
|
||||
return AIPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
if (AIPlayer->WantsAndNeedsTask.bIsWaitingForBuildLink && AIPlayer->WantsAndNeedsTask.StructureType == NewStructure)
|
||||
{
|
||||
if (vDist2DSq(BuildLocation, AIPlayer->WantsAndNeedsTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
|
||||
{
|
||||
return AIPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam)
|
||||
{
|
||||
AvHTeamNumber TeamANumber = GetGameRules()->GetTeamANumber();
|
||||
|
@ -858,6 +827,15 @@ AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam)
|
|||
return (FriendlyTeam == TeamANumber) ? TeamBNumber : TeamANumber;
|
||||
}
|
||||
|
||||
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam)
|
||||
{
|
||||
AvHTeamNumber EnemyTeamNumber = AIMGR_GetEnemyTeam(FriendlyTeam);
|
||||
|
||||
AvHTeam* TeamRef = GetGameRules()->GetTeam(EnemyTeamNumber);
|
||||
|
||||
return (TeamRef) ? TeamRef->GetTeamType() : AVH_CLASS_TYPE_UNDEFINED;
|
||||
}
|
||||
|
||||
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers()
|
||||
{
|
||||
vector<AvHAIPlayer*> Result;
|
||||
|
@ -904,3 +882,20 @@ void AIMGR_BotPrecache()
|
|||
{
|
||||
m_spriteTexture = PRECACHE_MODEL("sprites/zbeam6.spr");
|
||||
}
|
||||
|
||||
AvHAIPlayer* AIMGR_GetDebugAIPlayer()
|
||||
{
|
||||
return DebugAIPlayer;
|
||||
}
|
||||
|
||||
void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer)
|
||||
{
|
||||
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
|
||||
{
|
||||
if (it->Edict == AIPlayer)
|
||||
{
|
||||
DebugAIPlayer = &(*it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,13 +58,17 @@ int AIMGR_GetNumAIPlayersWithRoleOnTeam(AvHTeamNumber Team, AvHAIBotRole Role, A
|
|||
|
||||
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);
|
||||
|
||||
AvHAIPlayer* AIMGR_FindPlayerOnTeamWaitingBuildLink(const AvHTeamNumber Team, const AvHAIDeployableStructureType NewStructure, const Vector BuildLocation);
|
||||
|
||||
|
||||
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam);
|
||||
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam);
|
||||
|
||||
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers();
|
||||
vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team);
|
||||
|
||||
void AIMGR_ClearBotData();
|
||||
|
||||
AvHAIPlayer* AIMGR_GetDebugAIPlayer();
|
||||
void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer);
|
||||
|
||||
#endif
|
|
@ -221,9 +221,9 @@ AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocation(const Vector& Loc
|
|||
{
|
||||
for (auto& it : TeamAStructureMap)
|
||||
{
|
||||
if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; }
|
||||
if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; }
|
||||
if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; }
|
||||
if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; }
|
||||
if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; }
|
||||
|
||||
unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags);
|
||||
|
||||
|
@ -248,10 +248,10 @@ AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocation(const Vector& Loc
|
|||
{
|
||||
for (auto& it : TeamBStructureMap)
|
||||
{
|
||||
if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; }
|
||||
if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; }
|
||||
if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; }
|
||||
if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; }
|
||||
|
||||
|
||||
unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags);
|
||||
|
||||
if (Filter->ReachabilityTeam != TEAM_IND)
|
||||
|
@ -1182,6 +1182,18 @@ void AITAC_OnNavMeshModified()
|
|||
{
|
||||
it->bReachabilityMarkedDirty = true;
|
||||
}
|
||||
|
||||
vector<AvHAIPlayer*> AllAIPlayers = AIMGR_GetAllAIPlayers();
|
||||
|
||||
for (auto it = AllAIPlayers.begin(); it != AllAIPlayers.end(); it++)
|
||||
{
|
||||
AvHAIPlayer* ThisPlayer = (*it);
|
||||
|
||||
if (IsPlayerActiveInGame(ThisPlayer->Edict) && ThisPlayer->BotNavInfo.CurrentPath.size() > 0)
|
||||
{
|
||||
ThisPlayer->BotNavInfo.NextForceRecalc = gpGlobals->time + frandrange(0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AITAC_RefreshBuildableStructures()
|
||||
|
@ -1660,6 +1672,8 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
|
|||
|
||||
std::unordered_map<int, AvHAIBuildableStructure>& BuildingMap = (BaseBuildable->GetTeamNumber() == TeamANumber) ? TeamAStructureMap : TeamBStructureMap;
|
||||
|
||||
BuildingMap[EntIndex].StructureType = StructureType;
|
||||
|
||||
// This is the first time we've seen this structure, so it must be new
|
||||
if (BuildingMap[EntIndex].LastSeen == 0)
|
||||
{
|
||||
|
@ -1674,8 +1688,6 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
|
|||
AITAC_OnStructureCreated(&BuildingMap[EntIndex]);
|
||||
}
|
||||
|
||||
BuildingMap[EntIndex].StructureType = StructureType;
|
||||
|
||||
if (vIsZero(BuildingMap[EntIndex].Location) || !vEquals(BaseBuildable->pev->origin, BuildingMap[EntIndex].Location, 5.0f))
|
||||
{
|
||||
AITAC_RefreshReachabilityForStructure(&BuildingMap[EntIndex]);
|
||||
|
@ -1754,24 +1766,9 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure)
|
|||
|
||||
if (!Team) { return; }
|
||||
|
||||
if (Team->GetTeamType() == AVH_CLASS_TYPE_MARINE)
|
||||
if (Team->GetTeamType() == AVH_CLASS_TYPE_ALIEN)
|
||||
{
|
||||
AvHAIPlayer* ActiveAICommander = AIMGR_GetAICommander(StructureTeam);
|
||||
|
||||
if (ActiveAICommander)
|
||||
{
|
||||
LinkDeployedObjectToCommanderAction(ActiveAICommander, NewStructure);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AvHAIPlayer* BuildingPlayer = AIMGR_FindPlayerOnTeamWaitingBuildLink(StructureTeam, NewStructure->StructureType, NewStructure->Location);
|
||||
|
||||
if (BuildingPlayer)
|
||||
{
|
||||
AITAC_LinkAlienStructureToTask(BuildingPlayer, NewStructure);
|
||||
}
|
||||
|
||||
AITAC_LinkAlienStructureToPlayer(NewStructure);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1805,7 +1802,7 @@ void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure)
|
|||
NewConnection.TargetObject = OtherPhaseGate->edict;
|
||||
memset(&NewConnection.ConnectionRefs[0], 0, sizeof(NewConnection.ConnectionRefs));
|
||||
|
||||
UTIL_AddOffMeshConnection(NewStructure->Location, OtherPhaseGate->Location, SAMPLE_POLYAREA_GROUND, NewFlag, true, &NewConnection);
|
||||
UTIL_AddOffMeshConnection(NewStructure->Location, OtherPhaseGate->Location, SAMPLE_POLYAREA_PHASEGATE, NewFlag, true, &NewConnection);
|
||||
|
||||
NewStructure->OffMeshConnections.push_back(NewConnection);
|
||||
|
||||
|
@ -1875,9 +1872,24 @@ void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure)
|
|||
}
|
||||
}
|
||||
|
||||
void AITAC_LinkAlienStructureToTask(AvHAIPlayer* pBot, AvHAIBuildableStructure* NewStructure)
|
||||
void AITAC_LinkAlienStructureToPlayer(AvHAIBuildableStructure* NewStructure)
|
||||
{
|
||||
vector<AvHAIPlayer*> AllTeamPlayers = AIMGR_GetAIPlayersOnTeam((AvHTeamNumber)NewStructure->edict->v.team);
|
||||
|
||||
for (auto it = AllTeamPlayers.begin(); it != AllTeamPlayers.end(); it++)
|
||||
{
|
||||
AvHAIPlayer* Player = (*it);
|
||||
|
||||
if (Player->BuildAttempts.BuildStatus == BUILD_ATTEMPT_PENDING && Player->BuildAttempts.AttemptedStructureType == NewStructure->StructureType)
|
||||
{
|
||||
if (vDist2DSq(NewStructure->Location, Player->BuildAttempts.AttemptedLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
|
||||
{
|
||||
Player->BuildAttempts.BuildStatus = BUILD_ATTEMPT_SUCCESS;
|
||||
Player->BuildAttempts.LinkedStructure = NewStructure;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AITAC_LinkDeployedItemToAction(AvHAIPlayer* CommanderBot, const AvHAIDroppedItem* NewItem)
|
||||
|
@ -2044,18 +2056,18 @@ unsigned char UTIL_GetAreaForObstruction(AvHAIDeployableStructureType StructureT
|
|||
AvHTeamNumber TeamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber TeamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
unsigned char StructureArea = (BuildingEdict->v.team == TeamA) ? DT_TILECACHE_TEAM1STRUCTURE_AREA : DT_TILECACHE_TEAM2STRUCTURE_AREA;
|
||||
unsigned char TeamStructureArea = (BuildingEdict->v.team == TeamA) ? DT_TILECACHE_TEAM1STRUCTURE_AREA : DT_TILECACHE_TEAM2STRUCTURE_AREA;
|
||||
|
||||
switch (StructureType)
|
||||
{
|
||||
case STRUCTURE_MARINE_RESTOWER:
|
||||
case STRUCTURE_MARINE_COMMCHAIR:
|
||||
case STRUCTURE_MARINE_ARMOURY:
|
||||
case STRUCTURE_MARINE_ADVARMOURY:
|
||||
case STRUCTURE_MARINE_OBSERVATORY:
|
||||
case STRUCTURE_ALIEN_RESTOWER:
|
||||
case STRUCTURE_MARINE_RESTOWER:
|
||||
case STRUCTURE_ALIEN_HIVE:
|
||||
return StructureArea;
|
||||
return TeamStructureArea;
|
||||
default:
|
||||
return DT_TILECACHE_BLOCKED_AREA;
|
||||
}
|
||||
|
@ -2072,6 +2084,8 @@ float UTIL_GetStructureRadiusForObstruction(AvHAIDeployableStructureType Structu
|
|||
case STRUCTURE_MARINE_TURRETFACTORY:
|
||||
case STRUCTURE_MARINE_COMMCHAIR:
|
||||
return 60.0f;
|
||||
case STRUCTURE_MARINE_TURRET:
|
||||
return 30.0f;
|
||||
default:
|
||||
return 40.0f;
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure);
|
|||
void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure);
|
||||
void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure);
|
||||
void AITAC_LinkDeployedItemToAction(AvHAIPlayer* CommanderBot, const AvHAIDroppedItem* NewItem);
|
||||
void AITAC_LinkAlienStructureToTask(AvHAIPlayer* pBot, AvHAIBuildableStructure* NewStructure);
|
||||
void AITAC_LinkAlienStructureToPlayer(AvHAIBuildableStructure* NewStructure);
|
||||
|
||||
float AITAC_GetPhaseDistanceBetweenPoints(const Vector StartPoint, const Vector EndPoint);
|
||||
|
||||
|
|
|
@ -137,6 +137,8 @@ void AITASK_ClearBotTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
Task->LastBuildAttemptTime = 0.0f;
|
||||
Task->BuildAttempts = 0;
|
||||
Task->StructureType = STRUCTURE_NONE;
|
||||
|
||||
memset(&pBot->BuildAttempts, 0, sizeof(AvHAIBuildAttempt));
|
||||
}
|
||||
|
||||
bool AITASK_IsTaskUrgent(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -714,8 +716,6 @@ bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict)) { return false; }
|
||||
|
||||
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
|
||||
|
||||
if (!ResNodeIndex)
|
||||
|
@ -723,8 +723,53 @@ bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
return false;
|
||||
}
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
// Don't waste resources switching down to gorge if we're a lerk, fade or onos
|
||||
// but we can still clear the area of enemy structures
|
||||
if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
DeployableSearchFilter EnemyStructuresFilter;
|
||||
EnemyStructuresFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructuresFilter.ReachabilityTeam = BotTeam;
|
||||
EnemyStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
return AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructuresFilter);
|
||||
}
|
||||
|
||||
// We can attack structures basically if we aren't stuck with Gorge's spit attack
|
||||
bool bCanAttackStructures = (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB));
|
||||
|
||||
if (ResNodeIndex->bIsOccupied)
|
||||
{
|
||||
// If we have a tower on the node, we can still help build it if it's not finished yet,
|
||||
// or we can clear the area if we're able to
|
||||
if (ResNodeIndex->OwningTeam == BotTeam)
|
||||
{
|
||||
if (IsPlayerGorge(pBot->Edict) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity)) { return true; }
|
||||
|
||||
// If the tower is fully built and we don't have bile bomb then our work here is done
|
||||
if (!bCanAttackStructures) { return false; }
|
||||
|
||||
// If we can clear the area of enemy junk then do so, otherwise we're finished
|
||||
DeployableSearchFilter EnemyStructuresFilter;
|
||||
EnemyStructuresFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructuresFilter.ReachabilityTeam = BotTeam;
|
||||
EnemyStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
return AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructuresFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enemy owns the res node, but we can cap it if we're able to attack structures, or there's a friend in the area who can.
|
||||
return (bCanAttackStructures || AITAC_GetNumPlayersOfTeamInArea(BotTeam, ResNodeIndex->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If another gorge is claiming this spot, then move on
|
||||
if (!IsPlayerGorge(pBot->Edict) && !ResNodeIndex->bIsOccupied)
|
||||
if (!IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
edict_t* OtherBuilder = AITAC_GetNearestPlayerOfClassInArea(pBot->Player->GetTeam(), ResNodeIndex->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
|
||||
|
||||
|
@ -732,36 +777,15 @@ bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
{
|
||||
if (GetPlayerResources(OtherBuilder) >= (int)(kResourceTowerCost * 0.7f))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we can clear the area of enemy junk then do so, otherwise we will let the other guy place the tower
|
||||
DeployableSearchFilter EnemyStructuresFilter;
|
||||
EnemyStructuresFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructuresFilter.ReachabilityTeam = BotTeam;
|
||||
EnemyStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
|
||||
if (ResNodeIndex->bIsOccupied)
|
||||
{
|
||||
if (IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
if (ResNodeIndex->OwningTeam == pBot->Player->GetTeam())
|
||||
{
|
||||
return !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity);
|
||||
return AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructuresFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return (ResNodeIndex->OwningTeam != pBot->Player->GetTeam());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Task->BuildAttempts > 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -776,7 +800,9 @@ bool AITASK_IsMarineCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
|
||||
if (!ResNodeIndex) { return false; }
|
||||
|
||||
// Always obey commander orders even if there's a bunch of other marines already there
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
// Always obey commander orders even if there's a bunch of other marines already there, otherwise don't bother if someone else is securing it
|
||||
if (!Task->bIssuedByCommander)
|
||||
{
|
||||
int DesiredNumCappers = (ResNodeIndex->OwningTeam == AIMGR_GetEnemyTeam(pBot->Player->GetTeam())) ? 2 : 1;
|
||||
|
@ -785,15 +811,24 @@ bool AITASK_IsMarineCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
if (NumMarinesNearby >= DesiredNumCappers && vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) > sqrf(UTIL_MetresToGoldSrcUnits(4.0f))) { return false; }
|
||||
}
|
||||
|
||||
if (ResNodeIndex->bIsOccupied)
|
||||
{
|
||||
if (ResNodeIndex->OwningTeam == pBot->Player->GetTeam())
|
||||
{
|
||||
return (FNullEnt(ResNodeIndex->ActiveTowerEntity) || !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity));
|
||||
}
|
||||
}
|
||||
// Obviously still valid task if the node is empty
|
||||
if (!ResNodeIndex->bIsOccupied) { return true; }
|
||||
|
||||
return true;
|
||||
// Also obviously still valid if it's owned by the enemy
|
||||
if (ResNodeIndex->OwningTeam != BotTeam) { return true; }
|
||||
|
||||
// Likewise, still valid if there isn't a tower or it's not fully built
|
||||
if (FNullEnt(ResNodeIndex->ActiveTowerEntity) || !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity)) { return true; }
|
||||
|
||||
// At this point, the node is capped fully. However, don't consider a res node secured if the enemy still has their junk lying around. Clear it all out.
|
||||
DeployableSearchFilter EnemyStructures;
|
||||
EnemyStructures.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStructures.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructures.ReachabilityTeam = BotTeam;
|
||||
EnemyStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
return AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructures);
|
||||
}
|
||||
|
||||
bool AITASK_IsDefendTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -823,69 +858,77 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
|
|||
|
||||
if (!FNullEnt(Task->TaskSecondaryTarget) && !UTIL_StructureIsFullyBuilt(Task->TaskSecondaryTarget)) { return true; }
|
||||
|
||||
if (Task->TaskTarget->v.team != pBot->Player->GetTeam()) { return false; }
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
if (Task->TaskTarget->v.team != BotTeam) { return false; }
|
||||
|
||||
// The reinforce structure task is true if we have an undecided hive available that we could build a new chamber with
|
||||
bool bActiveHiveWithoutTechExists = AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), MESSAGE_NULL);
|
||||
|
||||
if (bActiveHiveWithoutTechExists) { return true; }
|
||||
|
||||
DeployableSearchFilter StructureFilter;
|
||||
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
|
||||
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER | ALIEN_BUILD_DEFENSE_CHAMBER | ALIEN_BUILD_MOVEMENT_CHAMBER | ALIEN_BUILD_SENSORY_CHAMBER;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
StructureFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
|
||||
// At least 2 offence chambers
|
||||
int NumOffenceChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
vector<AvHAIBuildableStructure*> AllNearbyStructures = AITAC_FindAllDeployables(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
|
||||
if (NumOffenceChambers < 2) { return true; }
|
||||
bool bUnfinishedStructureExists = false;
|
||||
int NumOffenceChambers = 0;
|
||||
int NumDefenceChambers = 0;
|
||||
int NumMovementChambers = 0;
|
||||
int NumSensoryChambers = 0;
|
||||
|
||||
// At least 2 defence chambers, if the hive exists for it
|
||||
if (AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), ALIEN_BUILD_DEFENSE_CHAMBER))
|
||||
for (auto it = AllNearbyStructures.begin(); it != AllNearbyStructures.end(); it++)
|
||||
{
|
||||
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_DEFENCECHAMBER;
|
||||
int NumDefenceChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
if (NumDefenceChambers < 2) { return true; }
|
||||
if (!(ThisStructure->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { bUnfinishedStructureExists = true; }
|
||||
|
||||
StructureFilter.MaxSearchRadius = 0.0f;
|
||||
int NumTotalDefenceChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
switch (ThisStructure->StructureType)
|
||||
{
|
||||
case STRUCTURE_ALIEN_OFFENCECHAMBER:
|
||||
NumOffenceChambers++;
|
||||
break;
|
||||
case STRUCTURE_ALIEN_DEFENCECHAMBER:
|
||||
NumDefenceChambers++;
|
||||
break;
|
||||
case STRUCTURE_ALIEN_MOVEMENTCHAMBER:
|
||||
NumMovementChambers++;
|
||||
break;
|
||||
case STRUCTURE_ALIEN_SENSORYCHAMBER:
|
||||
NumSensoryChambers++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
if (NumTotalDefenceChambers < 3) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
// At least 1 movement and sensory chamber, if the hive exists for them
|
||||
if (AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), ALIEN_BUILD_MOVEMENT_CHAMBER))
|
||||
{
|
||||
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_MOVEMENTCHAMBER;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
// Task is still valid if we have any missing structures, or we're a gorge at the target site and there is an incomplete structure that we can finish off
|
||||
|
||||
bool bHasMoveChamber = AITAC_DeployableExistsAtLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
if (NumOffenceChambers < 2
|
||||
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER) && NumDefenceChambers < 2)
|
||||
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER) && NumMovementChambers < 1)
|
||||
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER) && NumSensoryChambers < 1)
|
||||
|| (IsPlayerGorge(pBot->Edict) && bUnfinishedStructureExists && vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) <= sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
|
||||
) { return true; }
|
||||
|
||||
if (!bHasMoveChamber) { return true; }
|
||||
// Otherwise, are there any enemy structures lying around we could clear out?
|
||||
|
||||
StructureFilter.MaxSearchRadius = 0.0f;
|
||||
int NumTotalMoveChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
bool bCanAttackStructures = (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB));
|
||||
|
||||
if (NumTotalMoveChambers < 3) { return true; }
|
||||
}
|
||||
if (!bCanAttackStructures) { return false; }
|
||||
|
||||
if (AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), ALIEN_BUILD_SENSORY_CHAMBER))
|
||||
{
|
||||
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_SENSORYCHAMBER;
|
||||
StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
DeployableSearchFilter EnemyStuff;
|
||||
EnemyStuff.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStuff.ReachabilityTeam = BotTeam;
|
||||
EnemyStuff.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
bool bHasSensoryChamber = AITAC_DeployableExistsAtLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
|
||||
if (!bHasSensoryChamber) { return true; }
|
||||
|
||||
StructureFilter.MaxSearchRadius = 0.0f;
|
||||
int NumTotalSensoryChambers = AITAC_GetNumDeployablesNearLocation(Task->TaskTarget->v.origin, &StructureFilter);
|
||||
|
||||
if (NumTotalSensoryChambers < 3) { return true; }
|
||||
}
|
||||
|
||||
// We have all available chambers set up
|
||||
return false;
|
||||
return AITAC_DeployableExistsAtLocation(Task->TaskTarget->v.origin, &EnemyStuff);
|
||||
}
|
||||
|
||||
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -946,7 +989,18 @@ bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
|
|||
|
||||
bool bSecuredResNode = (!ResNode || (ResNode->OwningTeam == BotTeam && !FNullEnt(ResNode->ActiveTowerEntity) && UTIL_StructureIsFullyBuilt(ResNode->ActiveTowerEntity)));
|
||||
|
||||
return !((!bPhaseGatesAvailable || bHasPhaseGate) && bHasTurretFactory && NumTurrets >= 5 && bSecuredResNode);
|
||||
if ((bPhaseGatesAvailable && !bHasPhaseGate) || !bHasTurretFactory || NumTurrets < 5) { return true; }
|
||||
|
||||
// Don't consider a hive secured if the enemy still has their junk in there. Clear it all out.
|
||||
|
||||
DeployableSearchFilter EnemyStructures;
|
||||
EnemyStructures.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStructures.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructures.ReachabilityTeam = BotTeam;
|
||||
EnemyStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
|
||||
return AITAC_DeployableExistsAtLocation(HiveToSecure->FloorLocation, &EnemyStructures);
|
||||
}
|
||||
|
||||
bool AITASK_IsEvolveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -1214,7 +1268,6 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (!FNullEnt(Task->TaskSecondaryTarget))
|
||||
{
|
||||
|
||||
if (UTIL_StructureIsFullyBuilt(Task->TaskSecondaryTarget))
|
||||
{
|
||||
Task->TaskSecondaryTarget = nullptr;
|
||||
|
@ -1448,6 +1501,24 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (pBot->Player->GetResources() < ResRequired)
|
||||
{
|
||||
if (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB))
|
||||
{
|
||||
DeployableSearchFilter EnemyStuff;
|
||||
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam());
|
||||
EnemyStuff.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStuff.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
EnemyStuff.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
AvHAIBuildableStructure* NearestEnemyStructure = AITAC_FindClosestDeployableToLocation(Task->TaskTarget->v.origin, &EnemyStuff);
|
||||
|
||||
if (NearestEnemyStructure)
|
||||
{
|
||||
BotAttackNonPlayerTarget(pBot, NearestEnemyStructure->edict);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BotGuardLocation(pBot, Task->TaskLocation);
|
||||
return;
|
||||
}
|
||||
|
@ -1460,7 +1531,7 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (!IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
AITASK_SetEvolveTask(pBot, &pBot->WantsAndNeedsTask, pBot->Edict->v.origin, ALIEN_LIFEFORM_TWO, true);
|
||||
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_TWO);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1579,14 +1650,6 @@ void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget)
|
|||
|
||||
MoveTo(pBot, BuildTarget->v.origin, MOVESTYLE_NORMAL);
|
||||
|
||||
if (IsPlayerMarine(pBot->Edict))
|
||||
{
|
||||
if (gpGlobals->time - pBot->LastCombatTime > 5.0f)
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
}
|
||||
}
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, BuildTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
BotLookAt(pBot, UTIL_GetCentreOfEntity(BuildTarget));
|
||||
|
@ -2204,107 +2267,140 @@ void AlienProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (!ResNodeIndex) { return; }
|
||||
|
||||
int NumResourcesRequired = (IsPlayerGorge(pBot->Edict) ? BALANCE_VAR(kResourceTowerCost) : (BALANCE_VAR(kResourceTowerCost) + BALANCE_VAR(kGorgeCost)));
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
|
||||
float DistFromNode = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation);
|
||||
// We can attack structures if we're not a gorge stuck with spit as our only offensive weapon
|
||||
bool bBotCanAttackStructures = !IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB);
|
||||
|
||||
if (DistFromNode > sqrf(UTIL_MetresToGoldSrcUnits(2.0f)) || !UTIL_QuickTrace(pBot->Edict, pBot->CurrentEyePosition, (Task->TaskLocation + Vector(0.0f, 0.0f, 50.0f))))
|
||||
// First, clear out any marine phase gates nearby so they can't send backup, if we can actually do meaningful damage to them
|
||||
if (bBotCanAttackStructures)
|
||||
{
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL);
|
||||
return;
|
||||
AvHClassType EnemyClassType = AIMGR_GetEnemyTeamType(BotTeam);
|
||||
|
||||
if (EnemyClassType == AVH_CLASS_TYPE_MARINE)
|
||||
{
|
||||
DeployableSearchFilter PGFilter;
|
||||
PGFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
PGFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
PGFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
PGFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
AvHAIBuildableStructure* PG = AITAC_FindClosestDeployableToLocation(ResNodeIndex->Location, &PGFilter);
|
||||
|
||||
if (PG)
|
||||
{
|
||||
BotAttackNonPlayerTarget(pBot, PG->edict);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ResNodeIndex->bIsOccupied)
|
||||
{
|
||||
Task->TaskTarget = ResNodeIndex->ActiveTowerEntity;
|
||||
|
||||
if (ResNodeIndex->OwningTeam != pBot->Player->GetTeam())
|
||||
{
|
||||
|
||||
if (IsPlayerGorge(pBot->Edict) && !PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB))
|
||||
{
|
||||
int NumAllies = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), Task->TaskLocation, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
|
||||
if (NumAllies > 0)
|
||||
{
|
||||
BotGuardLocation(pBot, Task->TaskLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
BotEvolveLifeform(pBot, ALIEN_LIFEFORM_ONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AvHAIWeapon AttackWeapon = BotAlienChooseBestWeaponForStructure(pBot, Task->TaskTarget);
|
||||
|
||||
float MaxRange = GetMaxIdealWeaponRange(AttackWeapon);
|
||||
bool bHullSweep = IsMeleeWeapon(AttackWeapon);
|
||||
|
||||
if (UTIL_PlayerHasLOSToEntity(pBot->Edict, Task->TaskTarget, MaxRange, bHullSweep))
|
||||
{
|
||||
pBot->DesiredCombatWeapon = AttackWeapon;
|
||||
|
||||
if (GetPlayerCurrentWeapon(pBot->Player) == AttackWeapon)
|
||||
{
|
||||
BotShootTarget(pBot, pBot->DesiredCombatWeapon, Task->TaskTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
// If we have a tower on there already then help build it if we're gorge
|
||||
if (ResNodeIndex->OwningTeam == BotTeam)
|
||||
{
|
||||
if (!UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
|
||||
{
|
||||
if (UTIL_PlayerHasLOSToEntity(pBot->Edict, ResNodeIndex->ActiveTowerEntity, max_player_use_reach, true))
|
||||
if (IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
BotUseObject(pBot, ResNodeIndex->ActiveTowerEntity, true);
|
||||
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->ActiveTowerEntity->v.origin) > sqrf(60.0f))
|
||||
{
|
||||
MoveDirectlyTo(pBot, ResNodeIndex->ActiveTowerEntity->v.origin);
|
||||
}
|
||||
AIPlayerBuildStructure(pBot, ResNodeIndex->ActiveTowerEntity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the enemy owns it then destroy it if we can, or go Skulk to do so.
|
||||
if (bBotCanAttackStructures)
|
||||
{
|
||||
BotAttackNonPlayerTarget(pBot, ResNodeIndex->ActiveTowerEntity);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_ONE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Node is empty and not capped by either side
|
||||
|
||||
MoveTo(pBot, ResNodeIndex->ActiveTowerEntity->v.origin, MOVESTYLE_NORMAL);
|
||||
int NumResourcesRequired = (IsPlayerGorge(pBot->Edict) ? BALANCE_VAR(kResourceTowerCost) : (BALANCE_VAR(kResourceTowerCost) + BALANCE_VAR(kGorgeCost)));
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->ActiveTowerEntity->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
// We have enough resources to place the tower (includes cost of evolving to gorge if necessary)
|
||||
if (pBot->Player->GetResources() >= NumResourcesRequired)
|
||||
{
|
||||
float CurrDist = vDist2DSq(pBot->CurrentFloorPosition, ResNodeIndex->Location);
|
||||
|
||||
// Get close enough to place the tower if we aren't
|
||||
if (CurrDist > sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
|
||||
{
|
||||
MoveTo(pBot, ResNodeIndex->Location, MOVESTYLE_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Back up a bit if we're too close
|
||||
if (CurrDist < sqrf(UTIL_MetresToGoldSrcUnits(1.0f)))
|
||||
{
|
||||
BotLookAt(pBot, ResNodeIndex->Location);
|
||||
Vector Orientation = UTIL_GetVectorNormal2D(pBot->Edict->v.origin - ResNodeIndex->Location);
|
||||
Vector NewMoveLoc = ResNodeIndex->Location + (Orientation * UTIL_MetresToGoldSrcUnits(2.0f));
|
||||
MoveToWithoutNav(pBot, NewMoveLoc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsPlayerGorge(pBot->Edict))
|
||||
{
|
||||
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_TWO);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
BotLookAt(pBot, Task->TaskLocation);
|
||||
|
||||
if (gpGlobals->time - Task->LastBuildAttemptTime < 1.0f) { return; }
|
||||
|
||||
float LookDot = UTIL_GetDotProduct2D(UTIL_GetForwardVector2D(pBot->Edict->v.v_angle), UTIL_GetVectorNormal2D(Task->TaskLocation - pBot->Edict->v.origin));
|
||||
|
||||
if (LookDot > 0.9f)
|
||||
{
|
||||
BotLookAt(pBot, UTIL_GetCentreOfEntity(ResNodeIndex->ActiveTowerEntity));
|
||||
|
||||
pBot->Impulse = ALIEN_BUILD_RESOURCES;
|
||||
Task->LastBuildAttemptTime = gpGlobals->time + 1.0f;
|
||||
Task->bIsWaitingForBuildLink = true;
|
||||
Task->BuildAttempts++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsPlayerGorge(pBot->Edict))
|
||||
// We don't have enough resources to cap the node yet, so take out any enemy structures in the area while we wait if we can
|
||||
if (bBotCanAttackStructures)
|
||||
{
|
||||
BotEvolveLifeform(pBot, ALIEN_LIFEFORM_TWO);
|
||||
return;
|
||||
DeployableSearchFilter EnemyStructureFilter;
|
||||
EnemyStructureFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructureFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
AvHAIBuildableStructure* AttackTarget = AITAC_FindClosestDeployableToLocation(ResNodeIndex->Location, &EnemyStructureFilter);
|
||||
|
||||
if (AttackTarget)
|
||||
{
|
||||
BotAttackNonPlayerTarget(pBot, AttackTarget->edict);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BotLookAt(pBot, Task->TaskLocation);
|
||||
// No structures to take out, just wait for resources
|
||||
BotGuardLocation(pBot, ResNodeIndex->Location);
|
||||
|
||||
if (gpGlobals->time - Task->LastBuildAttemptTime < 1.0f) { return; }
|
||||
|
||||
float LookDot = UTIL_GetDotProduct2D(UTIL_GetForwardVector2D(pBot->Edict->v.v_angle), UTIL_GetVectorNormal2D(Task->TaskLocation - pBot->Edict->v.origin));
|
||||
|
||||
if (LookDot > 0.9f)
|
||||
{
|
||||
|
||||
pBot->Impulse = ALIEN_BUILD_RESOURCES;
|
||||
Task->LastBuildAttemptTime = gpGlobals->time + 1.0f;
|
||||
Task->bIsWaitingForBuildLink = true;
|
||||
Task->BuildAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -2477,17 +2573,27 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
StructureFilter.DeployableTeam = BotTeam;
|
||||
StructureFilter.ReachabilityTeam = BotTeam;
|
||||
StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_COMPLETED;
|
||||
StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
vector<AvHAIBuildableStructure*> BuildableStructures = AITAC_FindAllDeployables(Hive->FloorLocation, &StructureFilter);
|
||||
|
||||
bool bKeyStructureBuilt = false;
|
||||
|
||||
AvHAIBuildableStructure* StructureToBuild = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = BuildableStructures.begin(); it != BuildableStructures.end(); it++)
|
||||
{
|
||||
AvHAIBuildableStructure* ThisStructure = (*it);
|
||||
|
||||
if ((ThisStructure->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED) && (ThisStructure->StructureType & (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE)))
|
||||
{
|
||||
bKeyStructureBuilt = true;
|
||||
}
|
||||
|
||||
if (ThisStructure->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED) { continue; }
|
||||
|
||||
// Phase gates always take priority, so just go and build it if there is one
|
||||
if (ThisStructure->StructureType == STRUCTURE_MARINE_PHASEGATE)
|
||||
{
|
||||
AIPlayerBuildStructure(pBot, ThisStructure->edict);
|
||||
|
@ -2515,8 +2621,13 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
if (ResNode->OwningTeam != BotTeam)
|
||||
{
|
||||
BotAttackTarget(pBot, ResNode->ActiveTowerEntity);
|
||||
return;
|
||||
// Don't attack the RT until we have build a TF or PG. Avoids giving the game away too quickly
|
||||
if (bKeyStructureBuilt)
|
||||
{
|
||||
BotAttackNonPlayerTarget(pBot, ResNode->ActiveTowerEntity);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2528,6 +2639,25 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
}
|
||||
}
|
||||
|
||||
// We won't start attacking enemy structures until we have built a turret factory or phase gate so we don't reveal our evil plans until we're ready
|
||||
if (bKeyStructureBuilt)
|
||||
{
|
||||
DeployableSearchFilter EnemyStructures;
|
||||
EnemyStructures.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
|
||||
EnemyStructures.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
EnemyStructures.ReachabilityTeam = BotTeam;
|
||||
EnemyStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
|
||||
AvHAIBuildableStructure* EnemyStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &EnemyStructures);
|
||||
|
||||
if (EnemyStructure)
|
||||
{
|
||||
BotAttackNonPlayerTarget(pBot, EnemyStructure->edict);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BotGuardLocation(pBot, Task->TaskLocation);
|
||||
|
||||
}
|
||||
|
@ -2536,34 +2666,26 @@ void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
if (!Task) { return; }
|
||||
|
||||
float DistFromNode = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation);
|
||||
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
|
||||
|
||||
if (DistFromNode > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)) || !UTIL_QuickTrace(pBot->Edict, pBot->CurrentEyePosition, (Task->TaskLocation + Vector(0.0f, 0.0f, 50.0f))))
|
||||
// This shouldn't happen, but if somehow it does then at least do SOMETHING
|
||||
if (!ResNodeIndex)
|
||||
{
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL);
|
||||
|
||||
if (gpGlobals->time - pBot->LastCombatTime > 5.0f)
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
|
||||
|
||||
if (!ResNodeIndex) { return; }
|
||||
|
||||
// There is a res tower, ours or the enemies
|
||||
if (ResNodeIndex->bIsOccupied)
|
||||
{
|
||||
Task->TaskTarget = ResNodeIndex->ActiveTowerEntity;
|
||||
// Cancel the waiting timeout since a tower has been placed for us
|
||||
Task->TaskLength = 0.0f;
|
||||
|
||||
|
||||
if (ResNodeIndex->OwningTeam == pBot->Player->GetTeam())
|
||||
{
|
||||
if (!UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
|
||||
{
|
||||
// Cancel the waiting timeout since there is something for us to do
|
||||
Task->TaskLength = 0.0f;
|
||||
AIPlayerBuildStructure(pBot, ResNodeIndex->ActiveTowerEntity);
|
||||
|
||||
return;
|
||||
|
@ -2571,29 +2693,63 @@ void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
}
|
||||
else
|
||||
{
|
||||
AvHAIWeapon AttackWeapon = BotMarineChooseBestWeaponForStructure(pBot, Task->TaskTarget);
|
||||
// Cancel the waiting timeout since there is something for us to do
|
||||
Task->TaskLength = 0.0f;
|
||||
|
||||
float MaxRange = GetMaxIdealWeaponRange(AttackWeapon);
|
||||
bool bHullSweep = IsMeleeWeapon(AttackWeapon);
|
||||
// If we're playing MvM, then check the enemy hasn't got a phase gate nearby which could bring in defenders. If so, take that out first.
|
||||
AvHClassType EnemyType = AIMGR_GetEnemyTeamType(pBot->Player->GetTeam());
|
||||
|
||||
if (UTIL_PlayerHasLOSToEntity(pBot->Edict, Task->TaskTarget, MaxRange, bHullSweep))
|
||||
if (EnemyType == AVH_CLASS_TYPE_MARINE)
|
||||
{
|
||||
pBot->DesiredCombatWeapon = AttackWeapon;
|
||||
DeployableSearchFilter EnemyStructureFilter;
|
||||
EnemyStructureFilter.DeployableTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam());
|
||||
EnemyStructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
|
||||
EnemyStructureFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
EnemyStructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
if (GetPlayerCurrentWeapon(pBot->Player) == AttackWeapon)
|
||||
AvHAIBuildableStructure* EnemyStructure = AITAC_FindClosestDeployableToLocation(Task->TaskLocation, &EnemyStructureFilter);
|
||||
|
||||
if (EnemyStructure)
|
||||
{
|
||||
//BotShootTarget(pBot, pBot->DesiredCombatWeapon, Task->TaskTarget);
|
||||
BotAttackNonPlayerTarget(pBot, EnemyStructure->edict);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
|
||||
}
|
||||
|
||||
BotAttackNonPlayerTarget(pBot, ResNodeIndex->ActiveTowerEntity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out any enemy structures around the node
|
||||
DeployableSearchFilter EnemyStructureFilter;
|
||||
|
||||
EnemyStructureFilter.DeployableTeam = AIMGR_GetEnemyTeam(pBot->Player->GetTeam());
|
||||
EnemyStructureFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStructureFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
EnemyStructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
|
||||
|
||||
AvHAIBuildableStructure* EnemyStructure = AITAC_FindClosestDeployableToLocation(Task->TaskLocation, &EnemyStructureFilter);
|
||||
|
||||
if (EnemyStructure)
|
||||
{
|
||||
// Cancel the waiting timeout since we have something useful to do
|
||||
Task->TaskLength = 0.0f;
|
||||
BotAttackNonPlayerTarget(pBot, EnemyStructure->edict);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Give the commander 30 seconds to drop a tower for us, or give up and move on
|
||||
// If we're not at our destination yet, go there
|
||||
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) > UTIL_MetresToGoldSrcUnits(5.0f))
|
||||
{
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Empty res node with nothing to do but wait, stick around for 30 seconds and then move on if the commander doesn't drop an RT to build
|
||||
if (Task->TaskLength == 0.0f)
|
||||
{
|
||||
Task->TaskStartedTime = gpGlobals->time;
|
||||
|
@ -3104,9 +3260,11 @@ void AITASK_SetCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const Av
|
|||
|
||||
AITASK_ClearBotTask(pBot, Task);
|
||||
|
||||
Vector WaitLocation = UTIL_GetRandomPointOnNavmeshInRadius(pBot->BotNavInfo.NavProfile, NodeRef->Location, UTIL_MetresToGoldSrcUnits(1.0f));
|
||||
|
||||
Task->TaskType = TASK_CAP_RESNODE;
|
||||
Task->StructureType = NodeStructureType;
|
||||
Task->TaskLocation = NodeRef->Location;
|
||||
Task->TaskLocation = (!vIsZero(WaitLocation)) ? WaitLocation : NodeRef->Location;
|
||||
|
||||
if (!FNullEnt(NodeRef->ActiveTowerEntity))
|
||||
{
|
||||
|
@ -3247,6 +3405,8 @@ void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task,
|
|||
|
||||
AITASK_ClearBotTask(pBot, Task);
|
||||
|
||||
if (FNullEnt(Target) || Target->v.deadflag != DEAD_NO) { return; }
|
||||
|
||||
Task->TaskType = TASK_REINFORCE_STRUCTURE;
|
||||
Task->TaskTarget = Target;
|
||||
Task->bTaskIsUrgent = bIsUrgent;
|
||||
|
|
|
@ -682,6 +682,11 @@ AvHAIWeapon BotAlienChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* tar
|
|||
return WEAPON_GORGE_BILEBOMB;
|
||||
}
|
||||
|
||||
if (PlayerHasWeapon(pBot->Player, WEAPON_FADE_ACIDROCKET) && StructureType == STRUCTURE_ALIEN_HIVE || IsDamagingStructure(StructureType))
|
||||
{
|
||||
return WEAPON_FADE_ACIDROCKET;
|
||||
}
|
||||
|
||||
// If we have xenocide, then choose it if we have lots of good targets in blast radius
|
||||
if (PlayerHasWeapon(pBot->Player, WEAPON_SKULK_XENOCIDE))
|
||||
{
|
||||
|
@ -718,7 +723,7 @@ AvHAIWeapon BotMarineChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* ta
|
|||
{
|
||||
AvHAIDeployableStructureType StructureType = GetStructureTypeFromEdict(target);
|
||||
|
||||
if (StructureType == STRUCTURE_NONE)
|
||||
if (StructureType == STRUCTURE_NONE || StructureType == STRUCTURE_ALIEN_HIVE || IsDamagingStructure(StructureType))
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
|
||||
{
|
||||
|
@ -734,34 +739,14 @@ AvHAIWeapon BotMarineChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* ta
|
|||
}
|
||||
}
|
||||
|
||||
if (StructureType == STRUCTURE_ALIEN_HIVE || StructureType == STRUCTURE_ALIEN_OFFENCECHAMBER)
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
|
||||
{
|
||||
return UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
}
|
||||
else if (BotGetSecondaryWeaponClipAmmo(pBot) > 0 || BotGetSecondaryWeaponAmmoReserve(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
|
||||
if ((PrimaryWeapon == WEAPON_MARINE_GL || PrimaryWeapon == WEAPON_MARINE_SHOTGUN) && (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0))
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
if ((PrimaryWeapon == WEAPON_MARINE_GL || PrimaryWeapon == WEAPON_MARINE_SHOTGUN) && (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0))
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
|
||||
return UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
|
||||
AvHAIWeapon GorgeGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target)
|
||||
|
|
|
@ -1498,27 +1498,39 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
|
|||
|
||||
theSuccess = true;
|
||||
}
|
||||
else if (FStrEq(pcmd, "testcommanderbuild"))
|
||||
else if (FStrEq(pcmd, "setdebugaiplayer"))
|
||||
{
|
||||
AvHAIPlayer* AIComm = AIMGR_GetAICommander(theAvHPlayer->GetTeam());
|
||||
CBaseEntity* SpectatedPlayer = theAvHPlayer->GetSpectatingEntity();
|
||||
|
||||
if (AIComm)
|
||||
if (SpectatedPlayer)
|
||||
{
|
||||
AIMGR_SetDebugAIPlayer(SpectatedPlayer->edict());
|
||||
}
|
||||
|
||||
Vector TraceStart = GetPlayerEyePosition(theAvHPlayer->edict()); // origin + pev->view_ofs
|
||||
Vector LookDir = UTIL_GetForwardVector(theAvHPlayer->edict()->v.v_angle); // Converts view angles to normalized unit vector
|
||||
theSuccess = true;
|
||||
}
|
||||
else if (FStrEq(pcmd, "testalienreinforce"))
|
||||
{
|
||||
vector<AvHAIPlayer*> AlienPlayers = AIMGR_GetAIPlayersOnTeam(TEAM_TWO);
|
||||
|
||||
Vector TraceEnd = TraceStart + (LookDir * 1000.0f);
|
||||
if (AlienPlayers.size() > 0)
|
||||
{
|
||||
AvHAIPlayer* NewCapper = AlienPlayers[0];
|
||||
|
||||
TraceResult Hit;
|
||||
|
||||
UTIL_TraceLine(TraceStart, TraceEnd, ignore_monsters, theAvHPlayer->edict(), &Hit);
|
||||
|
||||
if (Hit.flFraction < 1.0f)
|
||||
if (NewCapper)
|
||||
{
|
||||
AICOMM_DeployStructure(AIComm, STRUCTURE_MARINE_ARMOURY, Hit.vecEndPos);
|
||||
}
|
||||
DeployableSearchFilter ResNodeFilter;
|
||||
ResNodeFilter.DeployableTeam = TEAM_TWO;
|
||||
ResNodeFilter.ReachabilityTeam = TEAM_TWO;
|
||||
ResNodeFilter.ReachabilityFlags = NewCapper->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
|
||||
AvHAIResourceNode* ResNode = AITAC_FindNearestResourceNodeToLocation(NewCapper->Edict->v.origin, &ResNodeFilter);
|
||||
|
||||
if (ResNode)
|
||||
{
|
||||
AITASK_SetReinforceStructureTask(NewCapper, &NewCapper->PrimaryBotTask, ResNode->ActiveTowerEntity, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
theSuccess = true;
|
||||
|
|
Loading…
Reference in a new issue