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 kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
|
||||
bool hasCustomCSDMSpawnEntities ();
|
||||
bool isLineBlockedBySmoke (const Vector &from, const Vector &to, float grenadeBloat = 1.0f);
|
||||
|
||||
public:
|
||||
const Array <edict_t *> &getActiveGrenades () {
|
||||
|
|
|
|||
|
|
@ -427,6 +427,7 @@ private:
|
|||
bool isEnemyInSight (Vector &endPos);
|
||||
bool isEnemyNoticeable (float range);
|
||||
bool isCreature ();
|
||||
bool isOnLadderPath ();
|
||||
|
||||
void doPlayerAvoidance (const Vector &normal);
|
||||
void selectCampButtons (int index);
|
||||
|
|
@ -877,6 +878,7 @@ extern ConVar cv_grenadier_mode;
|
|||
extern ConVar cv_ignore_enemies_after_spawn_time;
|
||||
extern ConVar cv_camping_time_min;
|
||||
extern ConVar cv_camping_time_max;
|
||||
extern ConVar cv_smoke_grenade_checks;
|
||||
|
||||
extern ConVar mp_freezetime;
|
||||
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_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_smoke_grenade_checks ("smoke_grenade_checks", "2", "Affect bot's vision by smoke clouds.", true, 0.0f, 2.0f);
|
||||
|
||||
// game console variables
|
||||
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) {
|
||||
const auto &entOrigin = game.getEntityOrigin (pent);
|
||||
const auto &betweenUs = (entOrigin - pev->origin).normalize_apx ();
|
||||
|
|
|
|||
|
|
@ -215,6 +215,13 @@ bool Bot::checkBodyParts (edict_t *target) {
|
|||
}
|
||||
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -224,7 +231,11 @@ bool Bot::seesEnemy (edict_t *player) {
|
|||
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_lastEnemy = player;
|
||||
m_lastEnemyOrigin = m_enemyOrigin;
|
||||
|
|
@ -1379,7 +1390,7 @@ void Bot::attackMovement () {
|
|||
const bool alreadyDucking = m_duckTime > game.time () || isDucking ();
|
||||
|
||||
if (alreadyDucking) {
|
||||
m_duckTime = game.time () + m_frameInterval * 2.0f;
|
||||
m_duckTime = game.time () + m_frameInterval * 3.0f;
|
||||
}
|
||||
else if ((distance > 768.0f && hasPrimaryWeapon ())
|
||||
&& (m_enemyParts & (Visibility::Head | Visibility::Body))
|
||||
|
|
@ -1390,7 +1401,7 @@ void Bot::attackMovement () {
|
|||
|
||||
if (vistab.visible (m_currentNodeIndex, enemyNearestIndex, 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;
|
||||
|
|
|
|||
115
src/manager.cpp
115
src/manager.cpp
|
|
@ -2149,3 +2149,118 @@ void BotThreadWorker::startup (int workers) {
|
|||
// start up the worker
|
||||
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
|
||||
else {
|
||||
// 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)) {
|
||||
m_firstCollideTime = game.time () + 0.2f;
|
||||
}
|
||||
|
|
@ -3151,6 +3151,15 @@ bool Bot::isReachableNode (int index) {
|
|||
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) {
|
||||
// this function finds the shortest path from source index to destination index
|
||||
|
||||
|
|
|
|||
|
|
@ -226,9 +226,12 @@ void Bot::setAimDirection () {
|
|||
if (onLadder && m_pathWalk.hasNext ()) {
|
||||
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;
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
// try to look at last victim for a little, maybe there's some one else
|
||||
|
|
@ -399,13 +402,19 @@ void Bot::updateLookAngles () {
|
|||
updateBodyAngles ();
|
||||
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 stiffness = aimSkill * 2.0f;
|
||||
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) {
|
||||
accelerate += 600.0f;
|
||||
}
|
||||
|
|
@ -414,8 +423,29 @@ void Bot::updateLookAngles () {
|
|||
}
|
||||
m_idealAngles = pev->v_angle;
|
||||
|
||||
const float angleDiffPitch = cr::anglesDifference (direction.x, m_idealAngles.x);
|
||||
const float angleDiffYaw = cr::anglesDifference (direction.y, m_idealAngles.y);
|
||||
float angleDiffPitch = cr::anglesDifference (direction.x, m_idealAngles.x);
|
||||
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) {
|
||||
m_lookYawVel = 0.0f;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue