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:
RGreenlees 2024-04-18 23:44:05 +01:00
parent 188b1dac32
commit 33b5fcbe82
9 changed files with 294 additions and 199 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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
}
}
}