mirror of
https://github.com/ENSL/NS.git
synced 2025-02-21 19:31:19 +00:00
Improve bot combat
* Reworked how bot hearing and sight combine * Reworked target prioritisation * Reworked when a bot should ignore a nearby enemy and engage * Overall result is bots should have a better balance of engaging enemies and getting on with their tasks
This commit is contained in:
parent
cfc65e1f5a
commit
f6796ad025
7 changed files with 744 additions and 553 deletions
|
@ -478,26 +478,24 @@ typedef struct _BOT_PATH_NODE
|
|||
// Represents a bot's current understanding of an enemy player's status
|
||||
typedef struct _ENEMY_STATUS
|
||||
{
|
||||
AvHPlayer* EnemyPlayer = nullptr;
|
||||
edict_t* EnemyEdict = nullptr; // Reference to the enemy player edict
|
||||
Vector LastVisibleLocation = g_vecZero; // The last point the bot saw the target
|
||||
Vector LastSeenLocation = g_vecZero; // The last visibly-confirmed location of the player or tracked location (if parasited / motion tracked)
|
||||
Vector LastFloorPosition = g_vecZero; // Nearest point on the floor where the enemy was (for moving towards it)
|
||||
Vector LastSeenVelocity = g_vecZero; // Last visibly-confirmed movement direction of the player
|
||||
Vector PendingSeenLocation = g_vecZero; // The last visibly-confirmed location of the player
|
||||
Vector PendingSeenVelocity = g_vecZero; // Last visibly-confirmed movement direction of the player
|
||||
Vector LastLOSPosition = g_vecZero; // The last position where the bot has LOS to the enemy
|
||||
Vector LastHiddenPosition = g_vecZero; // The last position where the bot did NOT have LOS to the enemy
|
||||
float LastSeenTime = 0.0f; // Last time the bot saw the player (not tracked)
|
||||
float LastTrackedTime = 0.0f; // Last time the bot saw the player (tracked position)
|
||||
//bool bInFOV = false; // Is the player in the bot's FOV
|
||||
bool bHasLOS = false; // Does the bot have LOS to the target
|
||||
bool bIsVisible = false; // Enemy is in FOV and has LOS
|
||||
bool bIsAwareOfPlayer = false; // Is the bot aware of this player's presence?
|
||||
float NextUpdateTime = 0.0f; // When the bot can next react to a change in target's state
|
||||
float NextVelocityUpdateTime = 0.0f; // When the bot can next react to a change in target's state
|
||||
float EndTrackingTime = 0.0f; // When to stop "sensing" enemy movement after losing LOS
|
||||
float CertaintyOfLocation = 0.0f; // How sure the bot is where the enemy is if they're cloaked
|
||||
AvHPlayer* PlayerRef = nullptr; // Reference to the enemy AvHPlayer
|
||||
edict_t* PlayerEdict = nullptr; // Reference to the enemy player edict
|
||||
|
||||
Vector LastDetectedLocation = g_vecZero; // Where the bot last detected the enemy, either through sight, motion tracking or sound
|
||||
Vector LastVisibleLocation = g_vecZero; // Last point the bot had visible confirmation of the enemy
|
||||
Vector LastKnownVelocity = g_vecZero;
|
||||
Vector VisiblePointOnPlayer = g_vecZero;
|
||||
float AwarenessOfPlayer = 0.0f; // How aware of this enemy the bot is
|
||||
float LastDetectedTime = 0.0f; // When the bot last saw the enemy or they pinged on motion tracking
|
||||
float InitialAwarenessTime = 0.0f; // When the bot first became aware of the enemy
|
||||
float LastVisibleTime = 0.0f; // Last time the bot actually saw the enemy
|
||||
float EnemyThreatLevel = 0.0f;
|
||||
|
||||
bool bHasLOS = false; // Does the bot has LOS of the enemy?
|
||||
bool bEnemyHasLOS = false;
|
||||
|
||||
Vector LastLOSPosition = g_vecZero;
|
||||
Vector LastCoverPosition = g_vecZero;
|
||||
|
||||
} enemy_status;
|
||||
|
||||
|
|
|
@ -7093,25 +7093,71 @@ void BotFollowPath(AvHAIPlayer* pBot)
|
|||
|
||||
if (IsPlayerStandingOnPlayer(pBot->Edict) && CurrentNode.flag != SAMPLE_POLYFLAGS_WALLCLIMB && CurrentNode.flag != SAMPLE_POLYFLAGS_LADDER)
|
||||
{
|
||||
if (pBot->Edict->v.groundentity->v.velocity.Length2D() > 10.0f)
|
||||
Vector ForwardDir = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
|
||||
bool bCanMoveBackwards = UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, pBot->Edict->v.origin - (ForwardDir * 50.0f));
|
||||
|
||||
if (bCanMoveBackwards)
|
||||
{
|
||||
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(-pBot->Edict->v.groundentity->v.velocity);
|
||||
pBot->desiredMovementDir = -ForwardDir;
|
||||
return;
|
||||
}
|
||||
pBot->desiredMovementDir = -UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
return;
|
||||
|
||||
bool bCanMoveForward = UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, pBot->Edict->v.origin + (ForwardDir * 50.0f));
|
||||
|
||||
if (bCanMoveForward)
|
||||
{
|
||||
pBot->desiredMovementDir = ForwardDir;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a point we can go back to, and we can reach it, then go for it. Otherwise, keep pushing on and hope the other guy moves
|
||||
if (!vIsZero(pBot->BotNavInfo.LastOpenLocation))
|
||||
{
|
||||
if (UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, pBot->BotNavInfo.LastOpenLocation, GetPlayerRadius(pBot->Edict)))
|
||||
{
|
||||
NAV_SetMoveMovementTask(pBot, pBot->BotNavInfo.LastOpenLocation, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<AvHPlayer*> PotentialRiders = AITAC_GetAllPlayersOfTeamInArea(pBot->Player->GetTeam(), pBot->Edict->v.origin, pBot->Edict->v.size.Length(), false, pBot->Edict, AVH_USER3_NONE);
|
||||
/*vector<AvHPlayer*> PotentialRiders = AITAC_GetAllPlayersOfTeamInArea(pBot->Player->GetTeam(), pBot->Edict->v.origin, pBot->Edict->v.size.Length(), false, pBot->Edict, AVH_USER3_NONE);
|
||||
|
||||
for (auto it = PotentialRiders.begin(); it != PotentialRiders.end(); it++)
|
||||
{
|
||||
if ((*it)->pev->groundentity == pBot->Edict)
|
||||
{
|
||||
pBot->desiredMovementDir = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
return;
|
||||
Vector ForwardDir = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
bool bCanMoveForward = UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, pBot->Edict->v.origin + (ForwardDir * 50.0f));
|
||||
|
||||
if (bCanMoveForward)
|
||||
{
|
||||
pBot->desiredMovementDir = ForwardDir;
|
||||
return;
|
||||
}
|
||||
|
||||
bool bCanMoveBackwards = UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, pBot->Edict->v.origin - (ForwardDir * 50.0f));
|
||||
|
||||
if (bCanMoveBackwards)
|
||||
{
|
||||
pBot->desiredMovementDir = -ForwardDir;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a point we can go back to, and we can reach it, then go for it. Otherwise, keep pushing on and hope the other guy moves
|
||||
if (!vIsZero(pBot->BotNavInfo.LastOpenLocation))
|
||||
{
|
||||
if (UTIL_PointIsReachable(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, pBot->BotNavInfo.LastOpenLocation, GetPlayerRadius(pBot->Edict)))
|
||||
{
|
||||
NAV_SetMoveMovementTask(pBot, pBot->BotNavInfo.LastOpenLocation, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict))
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -66,6 +66,8 @@ void BotClearEnemyTrackingInfo(enemy_status* TrackingInfo);
|
|||
bool IsPlayerInBotFOV(AvHAIPlayer* Observer, edict_t* TargetPlayer);
|
||||
void UpdateAIPlayerViewFrustum(AvHAIPlayer* pBot);
|
||||
|
||||
float BotRateEnemyThreat(AvHAIPlayer* pBot, enemy_status* TrackingInfo);
|
||||
|
||||
bool UTIL_IsCloakedPlayerInvisible(edict_t* Observer, AvHPlayer* Player);
|
||||
|
||||
|
||||
|
@ -182,4 +184,6 @@ bool OnosCombatThink(AvHAIPlayer* pBot);
|
|||
bool BombardierCombatThink(AvHAIPlayer* pBot);
|
||||
bool RegularMarineCombatThink(AvHAIPlayer* pBot);
|
||||
|
||||
void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot);
|
||||
|
||||
#endif
|
|
@ -1456,8 +1456,6 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
}
|
||||
|
||||
BotGuardLocation(pBot, (!vIsZero(Task->TaskLocation)) ? Task->TaskLocation : ReinforceLocation);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void BotProgressResupplyTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -1471,10 +1469,13 @@ void BotProgressResupplyTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
pBot->DesiredCombatWeapon = GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
|
||||
if (UTIL_PlayerHasLOSToEntity(pBot->Edict, Task->TaskTarget, max_player_use_reach, false))
|
||||
bool bHasLOS = UTIL_PlayerHasLOSToEntity(pBot->Edict, Task->TaskTarget, UTIL_MetresToGoldSrcUnits(5.0f), false);
|
||||
float DistToArmoury = vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin);
|
||||
|
||||
if (bHasLOS && DistToArmoury <= sqrf(max_player_use_reach))
|
||||
{
|
||||
BotUseObject(pBot, Task->TaskTarget, true);
|
||||
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) > sqrf(50.0f))
|
||||
if (DistToArmoury > sqrf(50.0f))
|
||||
{
|
||||
MoveDirectlyTo(pBot, Task->TaskTarget->v.origin);
|
||||
}
|
||||
|
@ -1483,7 +1484,7 @@ void BotProgressResupplyTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
if (bHasLOS)
|
||||
{
|
||||
BotLookAt(pBot, UTIL_GetCentreOfEntity(Task->TaskTarget));
|
||||
}
|
||||
|
|
|
@ -1039,6 +1039,59 @@ BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapo
|
|||
return ATTACK_SUCCESS;
|
||||
}
|
||||
|
||||
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const Vector TargetLocation)
|
||||
{
|
||||
if (!TargetLocation) { return ATTACK_INVALIDTARGET; }
|
||||
|
||||
if (Weapon == WEAPON_NONE) { return ATTACK_NOWEAPON; }
|
||||
|
||||
// Don't need aiming or special LOS checks for primal scream as it's AoE buff
|
||||
if (Weapon == WEAPON_LERK_PRIMALSCREAM)
|
||||
{
|
||||
return ATTACK_SUCCESS;
|
||||
}
|
||||
|
||||
// Add a LITTLE bit of give to avoid edge cases where the bot is a smidge out of range
|
||||
float MaxWeaponRange = GetMaxIdealWeaponRange(Weapon) - 5.0f;
|
||||
|
||||
// Don't need aiming or special LOS checks for Xenocide as it's an AOE attack, just make sure we're close enough and don't have a wall in the way
|
||||
if (Weapon == WEAPON_SKULK_XENOCIDE)
|
||||
{
|
||||
if (vDist3DSq(pBot->Edict->v.origin, TargetLocation) <= sqrf(MaxWeaponRange) && UTIL_QuickTrace(pBot->Edict, pBot->Edict->v.origin, TargetLocation))
|
||||
{
|
||||
return ATTACK_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATTACK_OUTOFRANGE;
|
||||
}
|
||||
}
|
||||
|
||||
// For charge and stomp, we can go through stuff so don't need to check for being blocked
|
||||
if (Weapon == WEAPON_ONOS_CHARGE || Weapon == WEAPON_ONOS_STOMP)
|
||||
{
|
||||
if (vDist3DSq(pBot->Edict->v.origin, TargetLocation) > sqrf(MaxWeaponRange)) { return ATTACK_OUTOFRANGE; }
|
||||
|
||||
if (!UTIL_QuickTrace(pBot->Edict, pBot->Edict->v.origin, TargetLocation) || fabsf(TargetLocation.z - TargetLocation.z) > 50.0f) { return ATTACK_OUTOFRANGE; }
|
||||
|
||||
return ATTACK_SUCCESS;
|
||||
}
|
||||
|
||||
TraceResult hit;
|
||||
|
||||
Vector StartTrace = pBot->CurrentEyePosition;
|
||||
|
||||
Vector AttackDir = UTIL_GetVectorNormal(TargetLocation - StartTrace);
|
||||
|
||||
Vector EndTrace = pBot->CurrentEyePosition + (AttackDir * MaxWeaponRange);
|
||||
|
||||
if (vDist3DSq(StartTrace, EndTrace) < vDist3DSq(StartTrace, TargetLocation)) { return ATTACK_OUTOFRANGE; }
|
||||
|
||||
UTIL_TraceLine(StartTrace, EndTrace, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &hit);
|
||||
|
||||
return (hit.flFraction >= 1.0f) ? ATTACK_SUCCESS : ATTACK_BLOCKED;
|
||||
}
|
||||
|
||||
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const Vector TargetLocation, const edict_t* Target)
|
||||
{
|
||||
if (!TargetLocation) { return ATTACK_INVALIDTARGET; }
|
||||
|
@ -1085,23 +1138,12 @@ BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapo
|
|||
|
||||
Vector EndTrace = pBot->CurrentEyePosition + (AttackDir * MaxWeaponRange);
|
||||
|
||||
if (vDist3DSq(StartTrace, EndTrace) < vDist3DSq(StartTrace, TargetLocation)) { return ATTACK_OUTOFRANGE; }
|
||||
|
||||
UTIL_TraceLine(StartTrace, EndTrace, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &hit);
|
||||
|
||||
if (FNullEnt(hit.pHit)) { return ATTACK_OUTOFRANGE; }
|
||||
return (hit.flFraction >= 1.0f || hit.pHit == Target) ? ATTACK_SUCCESS : ATTACK_BLOCKED;
|
||||
|
||||
if (hit.pHit != Target)
|
||||
{
|
||||
if (vDist3DSq(pBot->CurrentEyePosition, TargetLocation) > sqrf(MaxWeaponRange))
|
||||
{
|
||||
return ATTACK_OUTOFRANGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATTACK_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
return ATTACK_SUCCESS;
|
||||
}
|
||||
|
||||
BotAttackResult PerformAttackLOSCheck(const Vector Location, const AvHAIWeapon Weapon, const edict_t* Target)
|
||||
|
|
|
@ -70,6 +70,7 @@ bool IsHitscanWeapon(AvHAIWeapon Weapon);
|
|||
float GetTimeUntilPlayerNextRefire(const AvHPlayer* Player);
|
||||
|
||||
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const edict_t* Target);
|
||||
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const Vector TargetLocation);
|
||||
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const Vector TargetLocation, const edict_t* Target);
|
||||
BotAttackResult PerformAttackLOSCheck(const Vector Location, const AvHAIWeapon Weapon, const edict_t* Target);
|
||||
|
||||
|
|
Loading…
Reference in a new issue