fix: low-skilled bots aiming overflows on ladders (ref #543)
nav: ignore first collision if we are in ladder node (by @commandcobra7). bot: backported csbot function to check is enemy behind smoke. bot: added ``yb_smoke_grenade_checks`` to control which method to use (2-csbot, 1-podbot, 0-disabled).
This commit is contained in:
parent
ccb1735511
commit
f9bae83466
8 changed files with 180 additions and 11 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2d7a426b69102e3c6c07f2503240ed21f69c1143
|
Subproject commit 3596ac42ccc21d93d7fe2a937870d76e944f7f07
|
||||||
|
|
@ -126,6 +126,7 @@ public:
|
||||||
bool isTeamStacked (int team);
|
bool isTeamStacked (int team);
|
||||||
bool kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
|
bool kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
|
||||||
bool hasCustomCSDMSpawnEntities ();
|
bool hasCustomCSDMSpawnEntities ();
|
||||||
|
bool isLineBlockedBySmoke (const Vector &from, const Vector &to, float grenadeBloat = 1.0f);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const Array <edict_t *> &getActiveGrenades () {
|
const Array <edict_t *> &getActiveGrenades () {
|
||||||
|
|
|
||||||
|
|
@ -427,6 +427,7 @@ private:
|
||||||
bool isEnemyInSight (Vector &endPos);
|
bool isEnemyInSight (Vector &endPos);
|
||||||
bool isEnemyNoticeable (float range);
|
bool isEnemyNoticeable (float range);
|
||||||
bool isCreature ();
|
bool isCreature ();
|
||||||
|
bool isOnLadderPath ();
|
||||||
|
|
||||||
void doPlayerAvoidance (const Vector &normal);
|
void doPlayerAvoidance (const Vector &normal);
|
||||||
void selectCampButtons (int index);
|
void selectCampButtons (int index);
|
||||||
|
|
@ -877,6 +878,7 @@ extern ConVar cv_grenadier_mode;
|
||||||
extern ConVar cv_ignore_enemies_after_spawn_time;
|
extern ConVar cv_ignore_enemies_after_spawn_time;
|
||||||
extern ConVar cv_camping_time_min;
|
extern ConVar cv_camping_time_min;
|
||||||
extern ConVar cv_camping_time_max;
|
extern ConVar cv_camping_time_max;
|
||||||
|
extern ConVar cv_smoke_grenade_checks;
|
||||||
|
|
||||||
extern ConVar mp_freezetime;
|
extern ConVar mp_freezetime;
|
||||||
extern ConVar mp_roundtime;
|
extern ConVar mp_roundtime;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ ConVar cv_pickup_custom_items ("pickup_custom_items", "0", "Allows or disallows
|
||||||
ConVar cv_pickup_ammo_and_kits ("pickup_ammo_and_kits", "0", "Allows bots pickup mod items like ammo, health kits and suits.");
|
ConVar cv_pickup_ammo_and_kits ("pickup_ammo_and_kits", "0", "Allows bots pickup mod items like ammo, health kits and suits.");
|
||||||
ConVar cv_pickup_best ("pickup_best", "1", "Allows or disallows bots to pickup best weapons.");
|
ConVar cv_pickup_best ("pickup_best", "1", "Allows or disallows bots to pickup best weapons.");
|
||||||
ConVar cv_ignore_objectives ("ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages.");
|
ConVar cv_ignore_objectives ("ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages.");
|
||||||
|
ConVar cv_smoke_grenade_checks ("smoke_grenade_checks", "2", "Affect bot's vision by smoke clouds.", true, 0.0f, 2.0f);
|
||||||
|
|
||||||
// game console variables
|
// game console variables
|
||||||
ConVar mp_c4timer ("mp_c4timer", nullptr, Var::GameRef);
|
ConVar mp_c4timer ("mp_c4timer", nullptr, Var::GameRef);
|
||||||
|
|
@ -131,7 +132,7 @@ void Bot::avoidGrenades () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((pent->v.flags & FL_ONGROUND) && model == kSmokeModelName) {
|
else if (cv_smoke_grenade_checks.int_ () == 1 && (pent->v.flags & FL_ONGROUND) && model == kSmokeModelName) {
|
||||||
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) {
|
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) {
|
||||||
const auto &entOrigin = game.getEntityOrigin (pent);
|
const auto &entOrigin = game.getEntityOrigin (pent);
|
||||||
const auto &betweenUs = (entOrigin - pev->origin).normalize_apx ();
|
const auto &betweenUs = (entOrigin - pev->origin).normalize_apx ();
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,13 @@ bool Bot::checkBodyParts (edict_t *target) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::seesEnemy (edict_t *player) {
|
bool Bot::seesEnemy (edict_t *player) {
|
||||||
|
auto isBehindSmokeClouds = [&] (const Vector &pos) {
|
||||||
|
if (cv_smoke_grenade_checks.int_ () == 2) {
|
||||||
|
return bots.isLineBlockedBySmoke (getEyesPos (), pos);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if (game.isNullEntity (player)) {
|
if (game.isNullEntity (player)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +231,11 @@ bool Bot::seesEnemy (edict_t *player) {
|
||||||
ignoreFieldOfView = true;
|
ignoreFieldOfView = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ignoreFieldOfView || isInViewCone (player->v.origin)) && frustum.check (m_viewFrustum, player) && checkBodyParts (player)) {
|
if ((ignoreFieldOfView || isInViewCone (player->v.origin))
|
||||||
|
&& frustum.check (m_viewFrustum, player)
|
||||||
|
&& !isBehindSmokeClouds (player->v.origin)
|
||||||
|
&& checkBodyParts (player)) {
|
||||||
|
|
||||||
m_seeEnemyTime = game.time ();
|
m_seeEnemyTime = game.time ();
|
||||||
m_lastEnemy = player;
|
m_lastEnemy = player;
|
||||||
m_lastEnemyOrigin = m_enemyOrigin;
|
m_lastEnemyOrigin = m_enemyOrigin;
|
||||||
|
|
@ -1379,7 +1390,7 @@ void Bot::attackMovement () {
|
||||||
const bool alreadyDucking = m_duckTime > game.time () || isDucking ();
|
const bool alreadyDucking = m_duckTime > game.time () || isDucking ();
|
||||||
|
|
||||||
if (alreadyDucking) {
|
if (alreadyDucking) {
|
||||||
m_duckTime = game.time () + m_frameInterval * 2.0f;
|
m_duckTime = game.time () + m_frameInterval * 3.0f;
|
||||||
}
|
}
|
||||||
else if ((distance > 768.0f && hasPrimaryWeapon ())
|
else if ((distance > 768.0f && hasPrimaryWeapon ())
|
||||||
&& (m_enemyParts & (Visibility::Head | Visibility::Body))
|
&& (m_enemyParts & (Visibility::Head | Visibility::Body))
|
||||||
|
|
@ -1390,7 +1401,7 @@ void Bot::attackMovement () {
|
||||||
|
|
||||||
if (vistab.visible (m_currentNodeIndex, enemyNearestIndex, VisIndex::Crouch)
|
if (vistab.visible (m_currentNodeIndex, enemyNearestIndex, VisIndex::Crouch)
|
||||||
&& vistab.visible (enemyNearestIndex, m_currentNodeIndex, VisIndex::Crouch)) {
|
&& vistab.visible (enemyNearestIndex, m_currentNodeIndex, VisIndex::Crouch)) {
|
||||||
m_duckTime = game.time () + m_frameInterval * 2.0f;
|
m_duckTime = game.time () + m_frameInterval * 3.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_moveSpeed = 0.0f;
|
m_moveSpeed = 0.0f;
|
||||||
|
|
|
||||||
115
src/manager.cpp
115
src/manager.cpp
|
|
@ -2149,3 +2149,118 @@ void BotThreadWorker::startup (int workers) {
|
||||||
// start up the worker
|
// start up the worker
|
||||||
m_botWorker.startup (static_cast <size_t> (requestedThreads));
|
m_botWorker.startup (static_cast <size_t> (requestedThreads));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BotManager::isLineBlockedBySmoke (const Vector &from, const Vector &to, float grenadeBloat) {
|
||||||
|
if (m_activeGrenades.empty ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
constexpr auto kSmokeGrenadeRadius = 115;
|
||||||
|
|
||||||
|
// distance along line of sight covered by smoke
|
||||||
|
float totalSmokedLength = 0.0f;
|
||||||
|
|
||||||
|
Vector sightDir = to - from;
|
||||||
|
const float sightLength = sightDir.normalizeInPlace ();
|
||||||
|
|
||||||
|
for (auto pent : m_activeGrenades) {
|
||||||
|
if (game.isNullEntity (pent)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need drawn models
|
||||||
|
if (pent->v.effects & EF_NODRAW) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// smoke must be on a ground
|
||||||
|
if (!(pent->v.flags & FL_ONGROUND)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be a smoke grenade
|
||||||
|
if (!util.isModel (pent, kSmokeModelName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float smokeRadiusSq = cr::sqrf (kSmokeGrenadeRadius) * cr::sqrf (grenadeBloat);
|
||||||
|
const Vector &smokeOrigin = game.getEntityOrigin (pent);
|
||||||
|
|
||||||
|
Vector toGrenade = smokeOrigin - from;
|
||||||
|
float alongDist = toGrenade | sightDir;
|
||||||
|
|
||||||
|
// compute closest point to grenade along line of sight ray
|
||||||
|
Vector close;
|
||||||
|
|
||||||
|
// constrain closest point to line segment
|
||||||
|
if (alongDist < 0.0f) {
|
||||||
|
close = from;
|
||||||
|
}
|
||||||
|
else if (alongDist >= sightLength) {
|
||||||
|
close = to;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
close = from + sightDir * alongDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if closest point is within smoke radius, the line overlaps the smoke cloud
|
||||||
|
Vector toClose = close - smokeOrigin;
|
||||||
|
float lengthSq = toClose.lengthSq ();
|
||||||
|
|
||||||
|
if (lengthSq < smokeRadiusSq) {
|
||||||
|
// some portion of the ray intersects the cloud
|
||||||
|
|
||||||
|
const float fromSq = toGrenade.lengthSq ();
|
||||||
|
const float toSq = (smokeOrigin - to).lengthSq ();
|
||||||
|
|
||||||
|
if (fromSq < smokeRadiusSq) {
|
||||||
|
if (toSq < smokeRadiusSq) {
|
||||||
|
// both 'from' and 'to' lie within the cloud
|
||||||
|
// entire length is smoked
|
||||||
|
totalSmokedLength += (to - from).length ();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 'from' is inside the cloud, 'to' is outside
|
||||||
|
// compute half of total smoked length as if ray crosses entire cloud chord
|
||||||
|
float halfSmokedLength = cr::sqrtf (smokeRadiusSq - lengthSq);
|
||||||
|
|
||||||
|
if (alongDist > 0.0f) {
|
||||||
|
// ray goes thru 'close'
|
||||||
|
totalSmokedLength += halfSmokedLength + (close - from).length ();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ray starts after 'close'
|
||||||
|
totalSmokedLength += halfSmokedLength - (close - from).length ();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (toSq < smokeRadiusSq) {
|
||||||
|
// 'from' is outside the cloud, 'to' is inside
|
||||||
|
// compute half of total smoked length as if ray crosses entire cloud chord
|
||||||
|
const float halfSmokedLength = cr::sqrtf (smokeRadiusSq - lengthSq);
|
||||||
|
Vector v = to - smokeOrigin;
|
||||||
|
|
||||||
|
if ((v | sightDir) > 0.0f) {
|
||||||
|
// ray goes thru 'close'
|
||||||
|
totalSmokedLength += halfSmokedLength + (close - to).length ();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ray ends before 'close'
|
||||||
|
totalSmokedLength += halfSmokedLength - (close - to).length ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 'from' and 'to' lie outside of the cloud - the line of sight completely crosses it
|
||||||
|
// determine the length of the chord that crosses the cloud
|
||||||
|
const float smokedLength = 2.0f * cr::sqrtf (smokeRadiusSq - lengthSq);
|
||||||
|
totalSmokedLength += smokedLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// define how much smoke a bot can see thru
|
||||||
|
const float maxSmokedLength = 0.7f * kSmokeGrenadeRadius;
|
||||||
|
|
||||||
|
// return true if the total length of smoke-covered line-of-sight is too much
|
||||||
|
return (totalSmokedLength > maxSmokedLength);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -555,7 +555,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
// not stuck yet
|
// not stuck yet
|
||||||
else {
|
else {
|
||||||
// test if there's something ahead blocking the way
|
// test if there's something ahead blocking the way
|
||||||
if (!isOnLadder () && isBlockedForward (dirNormal, &tr)) {
|
if (!isOnLadderPath () && !isOnLadder () && isBlockedForward (dirNormal, &tr)) {
|
||||||
if (cr::fzero (m_firstCollideTime)) {
|
if (cr::fzero (m_firstCollideTime)) {
|
||||||
m_firstCollideTime = game.time () + 0.2f;
|
m_firstCollideTime = game.time () + 0.2f;
|
||||||
}
|
}
|
||||||
|
|
@ -3151,6 +3151,15 @@ bool Bot::isReachableNode (int index) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bot::isOnLadderPath () {
|
||||||
|
const auto prevNodeIndex = m_previousNodes[0];
|
||||||
|
|
||||||
|
// bot entered ladder path
|
||||||
|
return (m_pathFlags & NodeFlag::Ladder)
|
||||||
|
&& graph.exists (prevNodeIndex)
|
||||||
|
&& (graph[prevNodeIndex].flags & NodeFlag::Ladder);
|
||||||
|
}
|
||||||
|
|
||||||
void Bot::findShortestPath (int srcIndex, int destIndex) {
|
void Bot::findShortestPath (int srcIndex, int destIndex) {
|
||||||
// this function finds the shortest path from source index to destination index
|
// this function finds the shortest path from source index to destination index
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,9 +226,12 @@ void Bot::setAimDirection () {
|
||||||
if (onLadder && m_pathWalk.hasNext ()) {
|
if (onLadder && m_pathWalk.hasNext ()) {
|
||||||
const auto &nextPath = graph[m_pathWalk.next ()];
|
const auto &nextPath = graph[m_pathWalk.next ()];
|
||||||
|
|
||||||
if ((nextPath.flags & NodeFlag::Ladder) && m_destOrigin.distanceSq (pev->origin) < cr::sqrf (64.0f) && nextPath.origin.z > m_pathOrigin.z + 30.0f) {
|
if ((nextPath.flags & NodeFlag::Ladder) && m_destOrigin.distanceSq (pev->origin) < cr::sqrf (128.0f) && nextPath.origin.z > m_pathOrigin.z + 26.0f) {
|
||||||
m_lookAt = nextPath.origin;
|
m_lookAt = nextPath.origin;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
m_lookAt = m_destOrigin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to look at last victim for a little, maybe there's some one else
|
// try to look at last victim for a little, maybe there's some one else
|
||||||
|
|
@ -399,13 +402,19 @@ void Bot::updateLookAngles () {
|
||||||
updateBodyAngles ();
|
updateBodyAngles ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const float aimSkill = cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 25.0f;
|
float aimSkill = cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 25.0f;
|
||||||
|
|
||||||
|
// do not slowdown while on ladder
|
||||||
|
if (isOnLadderPath () || isOnLadder ()) {
|
||||||
|
aimSkill = 100.0f;
|
||||||
|
}
|
||||||
|
const bool importantAimFlags = (m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade));
|
||||||
|
|
||||||
float accelerate = aimSkill * 30.0f;
|
float accelerate = aimSkill * 30.0f;
|
||||||
float stiffness = aimSkill * 2.0f;
|
float stiffness = aimSkill * 2.0f;
|
||||||
float damping = aimSkill * 0.25f;
|
float damping = aimSkill * 0.25f;
|
||||||
|
|
||||||
if (((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
|
if ((importantAimFlags || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
|
||||||
if (m_difficulty == Difficulty::Expert) {
|
if (m_difficulty == Difficulty::Expert) {
|
||||||
accelerate += 600.0f;
|
accelerate += 600.0f;
|
||||||
}
|
}
|
||||||
|
|
@ -414,8 +423,29 @@ void Bot::updateLookAngles () {
|
||||||
}
|
}
|
||||||
m_idealAngles = pev->v_angle;
|
m_idealAngles = pev->v_angle;
|
||||||
|
|
||||||
const float angleDiffPitch = cr::anglesDifference (direction.x, m_idealAngles.x);
|
float angleDiffPitch = cr::anglesDifference (direction.x, m_idealAngles.x);
|
||||||
const float angleDiffYaw = cr::anglesDifference (direction.y, m_idealAngles.y);
|
float angleDiffYaw = cr::anglesDifference (direction.y, m_idealAngles.y);
|
||||||
|
|
||||||
|
// prevent reverse facing angles when navigating normally
|
||||||
|
if (m_difficulty < Difficulty::Easy && m_moveToGoal && !importantAimFlags && !m_pathOrigin.empty ()) {
|
||||||
|
const float forward = (m_pathOrigin - pev->origin).yaw ();
|
||||||
|
|
||||||
|
if (!cr::fzero (forward)) {
|
||||||
|
const float current = cr::wrapAngle (pev->v_angle.y - forward);
|
||||||
|
const float target = cr::wrapAngle (direction.y - forward);
|
||||||
|
|
||||||
|
if (current * target < 0.0f) {
|
||||||
|
if (cr::abs (current - target) >= 180.0f) {
|
||||||
|
if (angleDiffYaw > 0.0f) {
|
||||||
|
angleDiffYaw -= 360.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
angleDiffYaw += 360.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cr::abs (angleDiffYaw) < 1.0f) {
|
if (cr::abs (angleDiffYaw) < 1.0f) {
|
||||||
m_lookYawVel = 0.0f;
|
m_lookYawVel = 0.0f;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue