mirror of
https://github.com/ENSL/NS.git
synced 2024-11-13 00:24:38 +00:00
Improved marine combat
* Improved hearing logic * Improved marine combat decision making (they don't all cluster around the CC for no reason anymore) * Bots can now hear regen sounds
This commit is contained in:
parent
188b1dac32
commit
33b5fcbe82
9 changed files with 294 additions and 199 deletions
|
@ -497,6 +497,7 @@ typedef struct _ENEMY_STATUS
|
|||
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
|
||||
|
||||
} enemy_status;
|
||||
|
||||
|
|
|
@ -3691,11 +3691,16 @@ void StructureBlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vect
|
|||
|
||||
void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
||||
{
|
||||
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
|
||||
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - pBot->Edict->v.origin);
|
||||
|
||||
if (vIsZero(vForward))
|
||||
{
|
||||
vForward = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
|
||||
|
||||
if (vIsZero(vForward))
|
||||
{
|
||||
vForward = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
}
|
||||
}
|
||||
|
||||
pBot->desiredMovementDir = vForward;
|
||||
|
@ -3729,6 +3734,11 @@ void JumpMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
|||
if (vIsZero(vForward))
|
||||
{
|
||||
vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
|
||||
|
||||
if (vIsZero(vForward))
|
||||
{
|
||||
vForward = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
}
|
||||
}
|
||||
|
||||
pBot->desiredMovementDir = vForward;
|
||||
|
|
|
@ -1353,6 +1353,9 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
|
||||
UpdateAIPlayerViewFrustum(pBot);
|
||||
|
||||
float ViewUpdateDelta = gpGlobals->time - pBot->LastViewUpdateTime;
|
||||
ViewUpdateDelta = clampf(ViewUpdateDelta, 0.0f, 0.2f);
|
||||
|
||||
// Update list of currently visible players
|
||||
for (int i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
|
@ -1378,6 +1381,14 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
|
||||
enemy_status* TrackingInfo = &pBot->TrackedEnemies[EnemyIndex];
|
||||
|
||||
|
||||
TrackingInfo->CertaintyOfLocation -= (ViewUpdateDelta * 0.15f);
|
||||
TrackingInfo->CertaintyOfLocation = clampf(TrackingInfo->CertaintyOfLocation, 0.0f, 1.0f);
|
||||
|
||||
char msg[32];
|
||||
sprintf(msg, "%.2f\n", TrackingInfo->CertaintyOfLocation);
|
||||
UTIL_SayText(msg, CBaseEntity::Instance(INDEXENT(1)));
|
||||
|
||||
if (gpGlobals->time < TrackingInfo->NextUpdateTime)
|
||||
{
|
||||
continue;
|
||||
|
@ -1434,6 +1445,7 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
|
||||
if (bInFOV && (bCanSeeEnemy || bIsTracked))
|
||||
{
|
||||
TrackingInfo->CertaintyOfLocation = 1.0f;
|
||||
Vector FloorLocation = UTIL_GetEntityGroundLocation(Enemy);
|
||||
Vector BotVelocity = Enemy->v.velocity;
|
||||
|
||||
|
@ -1486,7 +1498,7 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
if (bHasLOS)
|
||||
if (bHasLOS && bCanSeeEnemy)
|
||||
{
|
||||
TrackingInfo->LastLOSPosition = pBot->CurrentFloorPosition + Vector(0.0f, 0.0f, 5.0f);
|
||||
|
||||
|
@ -1554,6 +1566,8 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
{
|
||||
pBot->LastSafeLocation = pBot->Edict->v.origin;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool UTIL_IsCloakedPlayerInvisible(edict_t* Observer, AvHPlayer* Player)
|
||||
|
@ -1595,6 +1609,7 @@ void BotClearEnemyTrackingInfo(enemy_status* TrackingInfo)
|
|||
TrackingInfo->LastSeenTime = 0.0f;
|
||||
TrackingInfo->LastLOSPosition = ZERO_VECTOR;
|
||||
TrackingInfo->LastHiddenPosition = ZERO_VECTOR;
|
||||
TrackingInfo->CertaintyOfLocation = 0.0f;
|
||||
}
|
||||
|
||||
void UpdateAIPlayerViewFrustum(AvHAIPlayer* pBot)
|
||||
|
@ -2001,26 +2016,32 @@ void UpdateAIPlayerDMRole(AvHAIPlayer* pBot)
|
|||
|
||||
}
|
||||
|
||||
void AIPlayerHearEnemy(AvHAIPlayer* pBot, edict_t* HeardEnemy)
|
||||
void AIPlayerHearEnemy(AvHAIPlayer* pBot, edict_t* HeardEnemy, float SoundVolume)
|
||||
{
|
||||
int heardIndex = ENTINDEX(HeardEnemy) - 1;
|
||||
|
||||
if (heardIndex < 0 || heardIndex >= 32 || HeardEnemy->v.team == pBot->Edict->v.team) { return; }
|
||||
|
||||
enemy_status* HeardEnemyStatus = &pBot->TrackedEnemies[heardIndex];
|
||||
|
||||
pBot->TrackedEnemies[heardIndex].LastSeenTime = gpGlobals->time;
|
||||
HeardEnemyStatus->LastSeenTime = gpGlobals->time;
|
||||
HeardEnemyStatus->CertaintyOfLocation += SoundVolume;
|
||||
HeardEnemyStatus->CertaintyOfLocation = clampf(HeardEnemyStatus->CertaintyOfLocation, 0.0f, 1.0f);
|
||||
|
||||
if (HeardEnemyStatus->CertaintyOfLocation < 0.15f) { return; }
|
||||
|
||||
// If the bot can't see the enemy (bCurrentlyVisible is false) then set the last seen location to a random point in the vicinity so the bot doesn't immediately know where they are
|
||||
if (pBot->TrackedEnemies[heardIndex].bIsVisible || vDist2DSq(pBot->TrackedEnemies[heardIndex].EnemyEdict->v.origin, pBot->Edict->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
|
||||
if (HeardEnemyStatus->bIsVisible || HeardEnemyStatus->CertaintyOfLocation > 0.75f || vDist2DSq(HeardEnemyStatus->EnemyEdict->v.origin, pBot->Edict->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
|
||||
{
|
||||
pBot->TrackedEnemies[heardIndex].LastSeenLocation = HeardEnemy->v.origin;
|
||||
HeardEnemyStatus->LastSeenLocation = HeardEnemy->v.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The further the enemy is, the more inaccurate the bot's guess will be where they are
|
||||
pBot->TrackedEnemies[heardIndex].LastSeenLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), HeardEnemy->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
HeardEnemyStatus->LastSeenLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), HeardEnemy->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
}
|
||||
|
||||
pBot->TrackedEnemies[heardIndex].bIsAwareOfPlayer = true;
|
||||
HeardEnemyStatus->bIsAwareOfPlayer = true;
|
||||
}
|
||||
|
||||
void AIPlayerTakeDamage(AvHAIPlayer* pBot, int damageTaken, edict_t* aggressor)
|
||||
|
@ -2703,7 +2724,7 @@ AvHAICombatStrategy GetMarineCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_st
|
|||
|
||||
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->LastSeenLocation);
|
||||
|
||||
bool bCanRetreat = AITAC_IsCompletedStructureOfTypeNearLocation(BotTeam, (STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY), ZERO_VECTOR, 0.0f);
|
||||
bool bCanRetreat = AITAC_IsCompletedStructureOfTypeNearLocation(BotTeam, (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY), ZERO_VECTOR, 0.0f);
|
||||
|
||||
// If we are doing something important, don't get distracted by enemies that aren't an immediate threat
|
||||
if (pBot->CurrentTask && (pBot->CurrentTask->TaskType == TASK_DEFEND || pBot->CommanderTask.TaskType != TASK_NONE))
|
||||
|
@ -2732,6 +2753,11 @@ AvHAICombatStrategy GetMarineCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_st
|
|||
return COMBAT_STRATEGY_RETREAT;
|
||||
}
|
||||
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) == 0 && UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) == 0 && UTIL_GetPlayerSecondaryWeaponClipAmmo(pBot->Player) == 0 && UTIL_GetPlayerSecondaryAmmoReserve(pBot->Player) == 0)
|
||||
{
|
||||
return COMBAT_STRATEGY_ATTACK;
|
||||
}
|
||||
|
||||
// Shotty users should attack, can't really skirmish with a shotgun
|
||||
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_SHOTGUN) && (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0 || UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0))
|
||||
{
|
||||
|
@ -2919,6 +2945,7 @@ void BotThrowGrenadeAtTarget(AvHAIPlayer* pBot, const Vector TargetPoint)
|
|||
|
||||
BotShootLocation(pBot, GetPlayerCurrentWeapon(pBot->Player), ThrowTargetLocation);
|
||||
}
|
||||
|
||||
bool BombardierCombatThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
return false;
|
||||
|
@ -2941,11 +2968,12 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
|
||||
bool bBotIsGrenadier = (DesiredCombatWeapon == WEAPON_MARINE_GL);
|
||||
|
||||
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, CurrentEnemy->v.origin);
|
||||
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, TrackedEnemyRef->LastSeenLocation);
|
||||
|
||||
bool bEnemyIsRanged = IsPlayerMarine(TrackedEnemyRef->EnemyPlayer) || ((GetPlayerCurrentWeapon(TrackedEnemyRef->EnemyPlayer) == WEAPON_FADE_ACIDROCKET) && DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)));
|
||||
|
||||
float LastEnemySeenTime = (TrackedEnemyRef->LastTrackedTime > 0.0f) ? TrackedEnemyRef->LastTrackedTime : TrackedEnemyRef->LastSeenTime;
|
||||
float TimeSinceLastEnemySighting = gpGlobals->time - LastEnemySeenTime;
|
||||
Vector LastEnemySeenLocation = TrackedEnemyRef->LastSeenLocation;
|
||||
|
||||
// Run away and restock
|
||||
|
@ -3037,15 +3065,29 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
// Maintain distance, pop and shoot
|
||||
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_SKIRMISH || pBot->CurrentCombatStrategy == COMBAT_STRATEGY_AMBUSH)
|
||||
{
|
||||
pBot->DesiredCombatWeapon = DesiredCombatWeapon;
|
||||
|
||||
if (GetPlayerCurrentWeapon(pBot->Player) != DesiredCombatWeapon)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vIsZero(pBot->LastSafeLocation))
|
||||
{
|
||||
pBot->LastSafeLocation = AITAC_GetTeamStartingLocation(BotTeam);
|
||||
}
|
||||
|
||||
if (TrackedEnemyRef->bHasLOS)
|
||||
float DesiredDistance = GetMinIdealWeaponRange(DesiredCombatWeapon) + ((GetMaxIdealWeaponRange(DesiredCombatWeapon) - GetMinIdealWeaponRange(DesiredCombatWeapon)) * 0.5f);
|
||||
|
||||
bool bCanReloadCurrentWeapon = (WeaponCanBeReloaded(DesiredCombatWeapon) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) < GetPlayerCurrentWeaponMaxClipAmmo(pBot->Player) && GetPlayerCurrentWeaponReserveAmmo(pBot->Player) > 0);
|
||||
bool bMustReloadCurrentWeapon = bCanReloadCurrentWeapon && GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0;
|
||||
bool bCanReloadAnyWeapon = BotAnyWeaponNeedsReloading(pBot);
|
||||
|
||||
if (TrackedEnemyRef->bHasLOS && (TrackedEnemyRef->bIsVisible || TrackedEnemyRef->CertaintyOfLocation >= 0.8f))
|
||||
{
|
||||
if (GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0)
|
||||
if (bMustReloadCurrentWeapon)
|
||||
{
|
||||
BotLookAt(pBot, TrackedEnemyRef->LastSeenLocation);
|
||||
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
|
||||
BotReloadWeapons(pBot);
|
||||
return true;
|
||||
|
@ -3053,7 +3095,9 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, pBot->LastSafeLocation) > sqrf(UTIL_MetresToGoldSrcUnits(3.0f)))
|
||||
{
|
||||
BotLookAt(pBot, TrackedEnemyRef->LastSeenLocation);
|
||||
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3119,6 +3163,7 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
|
||||
if (BotReloadWeapons(pBot)) { return true; }
|
||||
|
||||
BotLookAt(pBot, LastEnemySeenLocation);
|
||||
MoveTo(pBot, LastEnemySeenLocation, MOVESTYLE_NORMAL);
|
||||
}
|
||||
|
||||
|
@ -3128,19 +3173,43 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
// Go for the kill. Maintain desired distance and pursue when needed
|
||||
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK)
|
||||
{
|
||||
AvHAIWeapon IdealAttackWeapon = (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0 || UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0) ? UTIL_GetPlayerPrimaryWeapon(pBot->Player) : DesiredCombatWeapon;
|
||||
pBot->DesiredCombatWeapon = DesiredCombatWeapon;
|
||||
|
||||
float DesiredDistance = GetMinIdealWeaponRange(IdealAttackWeapon) + ((GetMaxIdealWeaponRange(IdealAttackWeapon) - GetMinIdealWeaponRange(IdealAttackWeapon)) * 0.5f);
|
||||
|
||||
bool bCanReloadCurrentWeapon = (WeaponCanBeReloaded(DesiredCombatWeapon) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) < GetPlayerCurrentWeaponMaxClipAmmo(pBot->Player) && GetPlayerCurrentWeaponReserveAmmo(pBot->Player) > 0);
|
||||
bool bMustReloadCurrentWeapon = bCanReloadCurrentWeapon && GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0;
|
||||
if (GetPlayerCurrentWeapon(pBot->Player) != DesiredCombatWeapon)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vIsZero(pBot->LastSafeLocation))
|
||||
{
|
||||
pBot->LastSafeLocation = AITAC_GetTeamStartingLocation(BotTeam);
|
||||
}
|
||||
|
||||
if (!TrackedEnemyRef->bHasLOS)
|
||||
float DesiredDistance = GetMinIdealWeaponRange(DesiredCombatWeapon) + ((GetMaxIdealWeaponRange(DesiredCombatWeapon) - GetMinIdealWeaponRange(DesiredCombatWeapon)) * 0.5f);
|
||||
|
||||
bool bCanReloadCurrentWeapon = (WeaponCanBeReloaded(DesiredCombatWeapon) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) < GetPlayerCurrentWeaponMaxClipAmmo(pBot->Player) && GetPlayerCurrentWeaponReserveAmmo(pBot->Player) > 0);
|
||||
bool bMustReloadCurrentWeapon = bCanReloadCurrentWeapon && GetPlayerCurrentWeaponClipAmmo(pBot->Player) == 0;
|
||||
bool bCanReloadAnyWeapon = BotAnyWeaponNeedsReloading(pBot);
|
||||
|
||||
if (bMustReloadCurrentWeapon)
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
BotLookAt(pBot, TrackedEnemyRef->LastSeenLocation);
|
||||
|
||||
if (TrackedEnemyRef->bHasLOS || DistToEnemy < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
BotGuardLocation(pBot, pBot->Edict->v.origin);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!TrackedEnemyRef->bHasLOS || (!TrackedEnemyRef->bIsVisible && TrackedEnemyRef->CertaintyOfLocation < 0.8f))
|
||||
{
|
||||
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GRENADE) || (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GL) && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0))
|
||||
{
|
||||
|
@ -3153,103 +3222,100 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
if ((IdealAttackWeapon != DesiredCombatWeapon || bCanReloadCurrentWeapon) && gpGlobals->time - TrackedEnemyRef->LastSeenTime > 3.0f)
|
||||
if (bCanReloadAnyWeapon && gpGlobals->time - TrackedEnemyRef->LastSeenTime > 3.0f)
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
if (vDist2DSq(pBot->Edict->v.origin, TrackedEnemyRef->LastVisibleLocation) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
BotLookAt(pBot, TrackedEnemyRef->LastSeenLocation);
|
||||
|
||||
if (DistToEnemy < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
MoveTo(pBot, AITAC_GetTeamStartingLocation(BotTeam), MOVESTYLE_NORMAL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MoveTo(pBot, TrackedEnemyRef->LastSeenLocation, MOVESTYLE_NORMAL);
|
||||
BotGuardLocation(pBot, LastEnemySeenLocation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bCanReloadCurrentWeapon && DistToEnemy > sqrf(DesiredDistance))
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
}
|
||||
|
||||
BotAttackResult LOSCheck = PerformAttackLOSCheck(pBot, DesiredCombatWeapon, CurrentEnemy);
|
||||
|
||||
if (bMustReloadCurrentWeapon)
|
||||
if (LOSCheck != ATTACK_SUCCESS)
|
||||
{
|
||||
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
|
||||
BotReloadWeapons(pBot);
|
||||
MoveTo(pBot, TrackedEnemyRef->LastSeenLocation, MOVESTYLE_NORMAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DistToEnemy > sqrf(DesiredDistance))
|
||||
if (bEnemyIsRanged)
|
||||
{
|
||||
if (IdealAttackWeapon != DesiredCombatWeapon)
|
||||
Vector EnemyOrientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
|
||||
|
||||
Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR);
|
||||
|
||||
pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(RightDir) : UTIL_GetVectorNormal2D(-RightDir);
|
||||
|
||||
// Let's get ziggy with it
|
||||
if (gpGlobals->time > pBot->BotNavInfo.NextZigTime)
|
||||
{
|
||||
BotReloadWeapons(pBot);
|
||||
MoveTo(pBot, pBot->LastSafeLocation, MOVESTYLE_NORMAL);
|
||||
return true;
|
||||
pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig;
|
||||
pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f);
|
||||
}
|
||||
|
||||
MoveTo(pBot, LastEnemySeenLocation, MOVESTYLE_NORMAL);
|
||||
|
||||
BotMovementInputs(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (bEnemyIsRanged)
|
||||
float MinDesiredDist = GetMinIdealWeaponRange(DesiredCombatWeapon);
|
||||
Vector Orientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
|
||||
|
||||
float EnemyMoveDot = UTIL_GetDotProduct2D(UTIL_GetVectorNormal2D(CurrentEnemy->v.velocity), -Orientation);
|
||||
|
||||
// Enemy is too close for comfort, or is moving towards us. Back up
|
||||
if (DistToEnemy < MinDesiredDist || EnemyMoveDot > 0.7f)
|
||||
{
|
||||
Vector EnemyOrientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
|
||||
Vector RetreatLocation = pBot->CurrentFloorPosition - (Orientation * 50.0f);
|
||||
|
||||
Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR);
|
||||
|
||||
pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(RightDir) : UTIL_GetVectorNormal2D(-RightDir);
|
||||
|
||||
// Let's get ziggy with it
|
||||
if (gpGlobals->time > pBot->BotNavInfo.NextZigTime)
|
||||
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, RetreatLocation))
|
||||
{
|
||||
pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig;
|
||||
pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f);
|
||||
MoveDirectlyTo(pBot, RetreatLocation);
|
||||
}
|
||||
|
||||
if (DesiredCombatWeapon != WEAPON_MARINE_KNIFE)
|
||||
{
|
||||
if (DistToEnemy < sqrf(100.0f))
|
||||
{
|
||||
if (IsPlayerReloading(pBot->Player) && CanInterruptWeaponReload(GetPlayerCurrentWeapon(pBot->Player)) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) > 0)
|
||||
{
|
||||
InterruptReload(pBot);
|
||||
return true;
|
||||
}
|
||||
BotJump(pBot);
|
||||
}
|
||||
}
|
||||
|
||||
BotMovementInputs(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
float MinDesiredDist = GetMinIdealWeaponRange(DesiredCombatWeapon);
|
||||
Vector Orientation = UTIL_GetVectorNormal2D(CurrentEnemy->v.origin - pBot->Edict->v.origin);
|
||||
|
||||
float EnemyMoveDot = UTIL_GetDotProduct2D(UTIL_GetVectorNormal2D(CurrentEnemy->v.velocity), -Orientation);
|
||||
|
||||
// Enemy is too close for comfort, or is moving towards us. Back up
|
||||
if (DistToEnemy < MinDesiredDist || EnemyMoveDot > 0.7f)
|
||||
{
|
||||
Vector RetreatLocation = pBot->CurrentFloorPosition - (Orientation * 50.0f);
|
||||
|
||||
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, RetreatLocation))
|
||||
{
|
||||
MoveDirectlyTo(pBot, RetreatLocation);
|
||||
}
|
||||
|
||||
if (DesiredCombatWeapon != WEAPON_MARINE_KNIFE)
|
||||
{
|
||||
if (DistToEnemy < sqrf(100.0f))
|
||||
{
|
||||
if (IsPlayerReloading(pBot->Player) && CanInterruptWeaponReload(GetPlayerCurrentWeapon(pBot->Player)) && GetPlayerCurrentWeaponClipAmmo(pBot->Player) > 0)
|
||||
{
|
||||
InterruptReload(pBot);
|
||||
return true;
|
||||
}
|
||||
BotJump(pBot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (!UTIL_PlayerHasLOSToLocation(pBot->Edict, TrackedEnemyRef->LastSeenLocation, UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
MoveTo(pBot, TrackedEnemyRef->LastSeenLocation, MOVESTYLE_NORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
BotGuardLocation(pBot, TrackedEnemyRef->LastSeenLocation);
|
||||
}
|
||||
}
|
||||
|
||||
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
|
||||
}
|
||||
|
||||
BotShootTarget(pBot, DesiredCombatWeapon, CurrentEnemy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ void UpdateAIPlayerDMRole(AvHAIPlayer* pBot);
|
|||
bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot);
|
||||
|
||||
void AIPlayerTakeDamage(AvHAIPlayer* pBot, int damageTaken, edict_t* aggressor);
|
||||
void AIPlayerHearEnemy(AvHAIPlayer* pBot, edict_t* HeardEnemy);
|
||||
void AIPlayerHearEnemy(AvHAIPlayer* pBot, edict_t* HeardEnemy, float SoundVolume);
|
||||
|
||||
int BotGetNextEnemyTarget(AvHAIPlayer* pBot);
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ float CountdownStartedTime = 0.0f;
|
|||
|
||||
bool bBotsEnabled = false;
|
||||
|
||||
float CurrentFrameDelta = 0.01f;
|
||||
|
||||
AvHAICommanderMode AIMGR_GetCommanderMode()
|
||||
{
|
||||
if (avh_botcommandermode.value == 1)
|
||||
|
@ -561,6 +563,8 @@ void AIMGR_UpdateAIPlayers()
|
|||
|
||||
float FrameDelta = CurrTime - PrevTime;
|
||||
|
||||
AIMGR_SetFrameDelta(FrameDelta);
|
||||
|
||||
int cvarBotSkill = clampi((int)avh_botskill.value, 0, 3);
|
||||
|
||||
bool bSkillChanged = (cvarBotSkill != CurrentBotSkill);
|
||||
|
@ -594,7 +598,7 @@ void AIMGR_UpdateAIPlayers()
|
|||
}
|
||||
}
|
||||
|
||||
AIMGR_ProcessPendingSounds(FrameDelta);
|
||||
AIMGR_ProcessPendingSounds();
|
||||
}
|
||||
|
||||
int NumCommanders = AIMGR_GetNumAICommanders();
|
||||
|
@ -1455,8 +1459,10 @@ bool AIMGR_IsMatchPracticallyOver()
|
|||
return false;
|
||||
}
|
||||
|
||||
void AIMGR_ProcessPendingSounds(float FrameDelta)
|
||||
void AIMGR_ProcessPendingSounds()
|
||||
{
|
||||
float FrameDelta = AIMGR_GetFrameDelta();
|
||||
|
||||
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
|
||||
{
|
||||
it->HearingThreshold -= FrameDelta;
|
||||
|
@ -1529,10 +1535,7 @@ void AIMGR_ProcessPendingSounds(float FrameDelta)
|
|||
|
||||
if (EmitterTeam != ThisTeam)
|
||||
{
|
||||
//char msg[64];
|
||||
//sprintf(msg, "%s Sound: %f\n", SoundType.c_str(), Volume);
|
||||
//UTIL_SayText(msg, CBaseEntity::Instance(INDEXENT(1)));
|
||||
AIPlayerHearEnemy(&(*it), EmittingEntity);
|
||||
AIPlayerHearEnemy(&(*it), EmittingEntity, Volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1542,3 +1545,12 @@ void AIMGR_ProcessPendingSounds(float FrameDelta)
|
|||
}
|
||||
}
|
||||
|
||||
void AIMGR_SetFrameDelta(float NewValue)
|
||||
{
|
||||
CurrentFrameDelta = NewValue;
|
||||
}
|
||||
|
||||
float AIMGR_GetFrameDelta()
|
||||
{
|
||||
return CurrentFrameDelta;
|
||||
}
|
|
@ -126,6 +126,9 @@ bool AIMGR_HasMatchEnded();
|
|||
|
||||
bool AIMGR_IsMatchPracticallyOver();
|
||||
|
||||
void AIMGR_ProcessPendingSounds(float FrameDelta);
|
||||
void AIMGR_ProcessPendingSounds();
|
||||
|
||||
void AIMGR_SetFrameDelta(float NewValue);
|
||||
float AIMGR_GetFrameDelta();
|
||||
|
||||
#endif
|
|
@ -570,120 +570,7 @@ AvHAIWeapon BotMarineChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target)
|
|||
|
||||
if (IsEdictPlayer(target))
|
||||
{
|
||||
float DistFromEnemy = vDist2DSq(pBot->Edict->v.origin, target->v.origin);
|
||||
|
||||
if (UTIL_GetPlayerPrimaryWeapon(pBot->Player) == WEAPON_MARINE_GL)
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 && DistFromEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
return WEAPON_MARINE_GL;
|
||||
}
|
||||
|
||||
if (BotGetSecondaryWeaponClipAmmo(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
|
||||
if (DistFromEnemy <= sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) == 0)
|
||||
{
|
||||
if (BotGetSecondaryWeaponClipAmmo(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
|
||||
if (PrimaryWeapon == WEAPON_MARINE_SHOTGUN)
|
||||
{
|
||||
if (DistFromEnemy > sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
|
||||
{
|
||||
if (BotGetSecondaryWeaponClipAmmo(pBot) > 0 || BotGetSecondaryWeaponAmmoReserve(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BotGetSecondaryWeaponClipAmmo(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DistFromEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
|
||||
if (BotGetSecondaryWeaponClipAmmo(pBot) > 0 || BotGetSecondaryWeaponAmmoReserve(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || (DistFromEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)) && UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0))
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BotGetSecondaryWeaponClipAmmo(pBot) > 0)
|
||||
{
|
||||
return GetBotMarineSecondaryWeapon(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return BotMarineChooseBestWeaponForStructure(pBot, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -691,6 +578,14 @@ AvHAIWeapon BotMarineChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target)
|
|||
}
|
||||
}
|
||||
|
||||
bool BotAnyWeaponNeedsReloading(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) < UTIL_GetPlayerPrimaryWeaponMaxClipSize(pBot->Player) && UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0) { return true; }
|
||||
if (UTIL_GetPlayerSecondaryWeaponClipAmmo(pBot->Player) < UTIL_GetPlayerSecondaryWeaponMaxClipSize(pBot->Player) && UTIL_GetPlayerSecondaryAmmoReserve(pBot->Player) > 0) { return true; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AvHAIWeapon BotAlienChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* target)
|
||||
{
|
||||
AvHAIDeployableStructureType StructureType = GetStructureTypeFromEdict(target);
|
||||
|
@ -772,6 +667,107 @@ AvHAIWeapon BotMarineChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* ta
|
|||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
|
||||
AvHAIWeapon MarineGetBestWeaponForPlayerTarget(AvHAIPlayer* pBot, AvHPlayer* Target)
|
||||
{
|
||||
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
AvHAIWeapon SecondaryWeapon = UTIL_GetPlayerSecondaryWeapon(pBot->Player);
|
||||
|
||||
float DistToEnemy = vDist2DSq(pBot->Edict->v.origin, Target->pev->origin);
|
||||
|
||||
bool bHasAmmoForPrimary = (PrimaryWeapon != WEAPON_INVALID && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) > 0);
|
||||
bool bHasAmmoForSecondary = (SecondaryWeapon != WEAPON_INVALID && UTIL_GetPlayerSecondaryWeaponClipAmmo(pBot->Player) > 0 || UTIL_GetPlayerSecondaryAmmoReserve(pBot->Player) > 0);
|
||||
|
||||
if (PrimaryWeapon != WEAPON_INVALID && UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0)
|
||||
{
|
||||
if (PrimaryWeapon == WEAPON_MARINE_GL)
|
||||
{
|
||||
if (DistToEnemy > sqrf(BALANCE_VAR(kGrenadeRadius)) || !bHasAmmoForSecondary)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bHasAmmoForSecondary)
|
||||
{
|
||||
return SecondaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PrimaryWeapon == WEAPON_MARINE_SHOTGUN)
|
||||
{
|
||||
float MaxDist = (IsPlayerMarine(Target) || Target->GetUser3() > AVH_USER3_ALIEN_PLAYER3) ? UTIL_MetresToGoldSrcUnits(15.0f) : UTIL_MetresToGoldSrcUnits(8.0f);
|
||||
|
||||
if (DistToEnemy < sqrf(MaxDist) || !bHasAmmoForSecondary)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bHasAmmoForSecondary)
|
||||
{
|
||||
return SecondaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
}
|
||||
|
||||
bool bEnemyIsRanged = IsPlayerMarine(Target) || ((GetPlayerCurrentWeapon(Target) == WEAPON_FADE_ACIDROCKET || GetPlayerCurrentWeapon(Target) == WEAPON_LERK_SPORES) && DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)));
|
||||
|
||||
if (bEnemyIsRanged)
|
||||
{
|
||||
if (bHasAmmoForSecondary)
|
||||
{
|
||||
return SecondaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
|
||||
if (DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
if (bHasAmmoForPrimary)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else if (bHasAmmoForSecondary)
|
||||
{
|
||||
return SecondaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
}
|
||||
|
||||
if (UTIL_GetPlayerPrimaryWeaponClipAmmo(pBot->Player) > 0)
|
||||
{
|
||||
return PrimaryWeapon;
|
||||
}
|
||||
else if (UTIL_GetPlayerSecondaryWeaponClipAmmo(pBot->Player) > 0)
|
||||
{
|
||||
return SecondaryWeapon;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WEAPON_MARINE_KNIFE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AvHAIWeapon GorgeGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target)
|
||||
{
|
||||
// Apparently I only imagined bile bomb doing damage to marine armour. Leaving it commented out in case we want to enable it again in future
|
||||
|
|
|
@ -43,8 +43,11 @@ bool IsMeleeWeapon(const AvHAIWeapon Weapon);
|
|||
Vector UTIL_GetGrenadeThrowTarget(edict_t* Player, const Vector TargetLocation, const float ExplosionRadius, bool bPrecise);
|
||||
|
||||
AvHAIWeapon BotMarineChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* target);
|
||||
AvHAIWeapon MarineGetBestWeaponForPlayerTarget(AvHAIPlayer* pBot, AvHPlayer* Target);
|
||||
AvHAIWeapon BotAlienChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* target);
|
||||
|
||||
bool BotAnyWeaponNeedsReloading(AvHAIPlayer* pBot);
|
||||
|
||||
// Helper function to pick the best weapon for any given situation and target type.
|
||||
AvHAIWeapon BotMarineChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target);
|
||||
AvHAIWeapon BotAlienChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target);
|
||||
|
|
|
@ -8803,6 +8803,10 @@ bool AvHPlayer::Heal(float inAmount, bool inPlaySound, bool dcHealing)
|
|||
{
|
||||
// Play regeneration event
|
||||
PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
|
||||
#ifdef AVH_SERVER
|
||||
// Heartbeat sound is a little loud, so reduce it for the AI so bots don't instantly spot your location with a single regen tic
|
||||
AISND_RegisterNewSound(this->entindex(), this->pev->origin, AI_SOUND_OTHER, this->GetAlienAdjustedEventVolume() * 0.25f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue