diff --git a/cfg/addons/yapb/conf/lang/ru_lang.cfg b/cfg/addons/yapb/conf/lang/ru_lang.cfg index ecc2d04..a4da326 100644 --- a/cfg/addons/yapb/conf/lang/ru_lang.cfg +++ b/cfg/addons/yapb/conf/lang/ru_lang.cfg @@ -2205,6 +2205,12 @@ The name of setinfo key used to store password to bot commands and menus. [TRANSLATED] Имя ключа setinfo используемого для хранения пароля доступа к командам и меню бота. +[ORIGINAL] +Allows to use classic bot kill on issuing end-round command in menus, instead of gamedll endround. + +[TRANSLATED] +Позволяет использовать классическое убийство ботов при выполнении команды завершения раунда в меню, вместо использования функции завершения раунда от игровой (dll) библиотеки. + [ORIGINAL] Enables or disables CSDM / FFA mode for bots. Allowed values: '0', '1', '2', '3'. diff --git a/ext/crlib b/ext/crlib index ea7a91b..c722157 160000 --- a/ext/crlib +++ b/ext/crlib @@ -1 +1 @@ -Subproject commit ea7a91bd042599132e60482f51087d9669dbe1c4 +Subproject commit c72215707a9be44eb2582b82a90a4a7f27a9f2e1 diff --git a/inc/yapb.h b/inc/yapb.h index 0bb58f4..7742ad1 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -132,7 +132,7 @@ public: ~PathWalk () = default; public: - int32_t &doubleNext () { + int32_t &nextX2 () { return at (2); } @@ -307,6 +307,7 @@ private: bool m_isCreature {}; // bot is not a player, but something else ? zombie ? bool m_defuseNotified {}; // bot is notified about bomb defusion bool m_jumpSequence {}; // next path link will be jump link + bool m_checkFall {}; // check bot fall PathWalk m_pathWalk {}; // pointer to current node from path Dodge m_dodgeStrafeDir {}; // direction to strafe @@ -325,6 +326,7 @@ private: edict_t *m_targetEntity {}; // the entity that the bot is trying to reach edict_t *m_avoidGrenade {}; // pointer to grenade entity to avoid edict_t *m_hindrance {}; // the hindrance + edict_t *m_hearedEnemy {}; // the heared enemy Vector m_liftTravelPos {}; // lift travel position Vector m_moveAngles {}; // bot move angles @@ -344,6 +346,7 @@ private: Vector m_desiredVelocity {}; // desired velocity for jump nodes Vector m_breakableOrigin {}; // origin of breakable Vector m_rightRef {}; // right referential vector + Vector m_checkFallPoint[2] {}; // check fall point Array m_ignoredBreakable {}; // list of ignored breakables Array m_hostages {}; // pointer to used hostage entities @@ -416,6 +419,7 @@ private: bool isOutOfBombTimer (); bool isWeaponBadAtDistance (int weaponIndex, float distance); bool needToPauseFiring (float distance); + bool checkZoom (float distance); bool lookupEnemies (); bool isEnemyHidden (edict_t *enemy); bool isEnemyInvincible (edict_t *enemy); @@ -464,6 +468,7 @@ private: void updatePickups (); void ensureEntitiesClear (); void checkTerrain (float movedDistance, const Vector &dirNormal); + void checkFall (); void checkDarkness (); void checkParachute (); void updatePracticeValue (int damage); diff --git a/src/botlib.cpp b/src/botlib.cpp index 96f31b0..b18e895 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -91,7 +91,7 @@ void Bot::avoidGrenades () { } // check if visible to the bot - if (!seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) > pev->fov * 0.5f) { + if (isInFOV (pent->v.origin - getEyesPos ()) > pev->fov * 0.5f && !seesEntity (pent->v.origin)) { continue; } auto model = pent->v.model.str (9); @@ -133,11 +133,11 @@ void Bot::avoidGrenades () { } } else if (cv_smoke_grenade_checks.as () == 1 && (pent->v.flags & FL_ONGROUND) && model == kSmokeModelName) { - if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) { + if (seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) { const auto &entOrigin = game.getEntityOrigin (pent); const auto &betweenUs = (entOrigin - pev->origin).normalize_apx (); const auto &betweenNade = (entOrigin - pev->origin).normalize_apx (); - const auto &betweenResult = ((Vector (betweenNade.y, betweenNade.x, 0.0f) * 150.0f + entOrigin) - pev->origin).normalize_apx (); + const auto &betweenResult = ((betweenNade.get2d () * 150.0f + entOrigin) - pev->origin).normalize_apx (); if ((betweenNade | betweenUs) > (betweenNade | betweenResult) && util.isVisible (pent->v.origin, ent ())) { const float distance = entOrigin.distance (pev->origin); @@ -751,9 +751,6 @@ void Bot::updatePickups () { } void Bot::ensureEntitiesClear () { - if ((!m_isStuck || m_navTimeset + getEstimatedNodeReachTime () + m_frameInterval * 2.0f > game.time ()) && !(m_states & Sense::SeeingEnemy)) { - return; - } const auto tid = getCurrentTaskId (); if (tid == Task::PickupItem || (m_states & Sense::PickupItem)) { @@ -1685,8 +1682,10 @@ void Bot::overrideConditions () { // special handling for sniping if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) - && m_sniperStopTime > game.time () - && tid != Task::SeekCover) { + && m_shootTime - 0.4f <= game.time () + && m_sniperStopTime > game.time ()) { + + ignoreCollision (); m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; @@ -1897,8 +1896,7 @@ void Bot::setConditions () { } // don't listen if seeing enemy, just checked for sounds or being blinded (because its inhuman) - if (!cv_ignore_enemies - && m_soundUpdateTime < game.time () + if (m_soundUpdateTime < game.time () && m_blindTime < game.time () && m_seeEnemyTime + 1.0f < game.time ()) { @@ -3214,8 +3212,11 @@ void Bot::logic () { checkTerrain (movedDistance, dirNormal); } + // if we have fallen from the place of move, the nearest point is allowed + checkFall (); + // check the darkness - if (cv_check_darkness) { + if (!m_isCreature && cv_check_darkness) { checkDarkness (); } @@ -3227,7 +3228,11 @@ void Bot::logic () { m_moveSpeed = -pev->maxspeed; m_strafeSpeed = pev->maxspeed * static_cast (m_needAvoidGrenade); } - ensureEntitiesClear (); + + // ensure we're not stuck destroying/picking something + if (m_navTimeset + getEstimatedNodeReachTime () + 1.0f < game.time () && !(m_states & Sense::SeeingEnemy) && m_moveToGoal) { + ensureEntitiesClear (); + } // check if need to use parachute checkParachute (); @@ -3850,10 +3855,10 @@ bool Bot::isOutOfBombTimer () { } void Bot::updateHearing () { - if (game.is (GameFlags::FreeForAll) || m_enemyIgnoreTimer > game.time ()) { + if (game.is (GameFlags::FreeForAll) || m_enemyIgnoreTimer > game.time () || cv_ignore_enemies) { return; } - edict_t *hearedEnemy = nullptr; + m_hearedEnemy = nullptr; float nearestDistanceSq = kInfiniteDistance; // setup potential visibility set from engine @@ -3881,13 +3886,13 @@ void Bot::updateHearing () { } if (distanceSq < nearestDistanceSq) { - hearedEnemy = client.ent; + m_hearedEnemy = client.ent; nearestDistanceSq = distanceSq; } } // did the bot hear someone ? - if (util.isPlayer (hearedEnemy)) { + if (util.isPlayer (m_hearedEnemy)) { // change to best weapon if heard something if (m_shootTime < game.time () - 5.0f && isOnFloor () @@ -3909,7 +3914,7 @@ void Bot::updateHearing () { auto getHeardOriginWithError = [&] () -> Vector { auto error = kSprayDistance * cr::powf (nearestDistanceSq, 0.5f) / 2048.0f; - auto origin = hearedEnemy->v.origin; + auto origin = m_hearedEnemy->v.origin; origin.x = origin.x + rg (-error, error); origin.y = origin.y + rg (-error, error); @@ -3919,13 +3924,13 @@ void Bot::updateHearing () { // didn't bot already have an enemy ? take this one... if (m_lastEnemyOrigin.empty () || game.isNullEntity (m_lastEnemy)) { - m_lastEnemy = hearedEnemy; + m_lastEnemy = m_hearedEnemy; m_lastEnemyOrigin = getHeardOriginWithError (); } // bot had an enemy, check if it's the heard one else { - if (hearedEnemy == m_lastEnemy) { + if (m_hearedEnemy == m_lastEnemy) { // bot sees enemy ? then bail out ! if (m_states & Sense::SeeingEnemy) { return; @@ -3936,8 +3941,8 @@ void Bot::updateHearing () { // if bot had an enemy but the heard one is nearer, take it instead const float distanceSq = m_lastEnemyOrigin.distanceSq (pev->origin); - if (distanceSq > hearedEnemy->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) { - m_lastEnemy = hearedEnemy; + if (distanceSq > m_hearedEnemy->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) { + m_lastEnemy = m_hearedEnemy; m_lastEnemyOrigin = getHeardOriginWithError (); } else { @@ -3947,9 +3952,9 @@ void Bot::updateHearing () { } // check if heard enemy can be seen - if (checkBodyParts (hearedEnemy)) { - m_enemy = hearedEnemy; - m_lastEnemy = hearedEnemy; + if (checkBodyParts (m_hearedEnemy)) { + m_enemy = m_hearedEnemy; + m_lastEnemy = m_hearedEnemy; m_lastEnemyOrigin = m_enemyOrigin; m_states |= Sense::SeeingEnemy; @@ -3959,15 +3964,15 @@ void Bot::updateHearing () { // check if heard enemy can be shoot through some obstacle else { if (cv_shoots_thru_walls - && m_lastEnemy == hearedEnemy + && m_lastEnemy == m_hearedEnemy && rg.chance (conf.getDifficultyTweaks (m_difficulty)->hearThruPct) && m_seeEnemyTime + 3.0f > game.time () - && isPenetrableObstacle (hearedEnemy->v.origin)) { + && isPenetrableObstacle (m_hearedEnemy->v.origin)) { - m_enemy = hearedEnemy; - m_lastEnemy = hearedEnemy; - m_enemyOrigin = hearedEnemy->v.origin; - m_lastEnemyOrigin = hearedEnemy->v.origin; + m_enemy = m_hearedEnemy; + m_lastEnemy = m_hearedEnemy; + m_enemyOrigin = m_hearedEnemy->v.origin; + m_lastEnemyOrigin = m_hearedEnemy->v.origin; m_states |= (Sense::SeeingEnemy | Sense::SuspectEnemy); m_seeEnemyTime = game.time (); diff --git a/src/combat.cpp b/src/combat.cpp index 7686570..d6d8d90 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -876,6 +876,70 @@ bool Bot::needToPauseFiring (float distance) { return false; } +bool Bot::checkZoom (float distance) { + int zoomMagnification = 0; + bool zoomChange = false; + + // is the bot holding a sniper rifle? + if (usesSniper ()) { + // should the bot switch to the long-range zoom? + if (distance > 1500.0f) { + zoomMagnification = 2; + } + + // else should the bot switch to the close-range zoom ? + else if (distance > 150.0f) { + zoomMagnification = 1; + } + + // else should the bot restore the normal view ? + else if (distance <= 150.0f) { + zoomMagnification = 0; + } + } + + // else is the bot holding a zoomable rifle? + else if (m_difficulty < Difficulty::Hard && usesZoomableRifle ()) { + // should the bot switch to zoomed mode? + if (distance > 800.0f) { + zoomMagnification = 1; + } + + // else should the bot restore the normal view? + else if (distance <= 800.0f) { + zoomMagnification = 0; + } + } + + switch (zoomMagnification) { + case 0: + if (pev->fov < 90.0f) { + zoomChange = true; + } + break; + + case 1: + if (pev->fov >= 90.0f) { + zoomChange = true; + } + break; + + case 2: + if (pev->fov >= 40.0f) { + zoomChange = true; + } + break; + } + + if (zoomChange && m_zoomCheckTime < game.time ()) { + pev->button |= IN_ATTACK2; + m_shootTime = game.time () + 0.15f; + + m_zoomCheckTime = game.time () + 0.5f; + } + return zoomChange; +} + void Bot::selectWeapons (float distance, int index, int id, int choosen) { const auto tab = conf.getRawWeapons (); @@ -922,51 +986,23 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) { m_shieldCheckTime = game.time () + 1.0f; } - // is the bot holding a sniper rifle? - if (usesSniper () && m_zoomCheckTime < game.time ()) { - // should the bot switch to the long-range zoom? - if (distance > 1500.0f && pev->fov >= 40.0f) { - pev->button |= IN_ATTACK2; - } - - // else should the bot switch to the close-range zoom ? - else if (distance > 150.0f && pev->fov >= 90.0f) { - pev->button |= IN_ATTACK2; - } - - // else should the bot restore the normal view ? - else if (distance <= 150.0f && pev->fov < 90.0f) { - pev->button |= IN_ATTACK2; - } - m_zoomCheckTime = game.time () + 0.25f; - } - - // else is the bot holding a zoomable rifle? - else if (m_difficulty < Difficulty::Hard && usesZoomableRifle () && m_zoomCheckTime < game.time ()) { - // should the bot switch to zoomed mode? - if (distance > 800.0f && pev->fov >= 90.0f) { - pev->button |= IN_ATTACK2; - } - - // else should the bot restore the normal view? - else if (distance <= 800.0f && pev->fov < 90.0f) { - pev->button |= IN_ATTACK2; - } - m_zoomCheckTime = game.time () + 0.5f; + if (checkZoom (distance)) { + return; } // we're should stand still before firing sniper weapons, else sniping is useless.. if (usesSniper () && (m_aimFlags & (AimFlags::Enemy | AimFlags::LastEnemy)) - && !m_isReloading && pev->velocity.lengthSq () > 0.0f - && getCurrentTaskId () != Task::SeekCover) { + && !m_isReloading && pev->velocity.lengthSq () > 0.0f) { - m_moveSpeed = 0.0f; - m_strafeSpeed = 0.0f; - m_navTimeset = game.time (); + if (!cr::fzero (pev->velocity.x) || !cr::fzero (pev->velocity.y) || !cr::fzero (pev->velocity.z)) { + m_moveSpeed = 0.0f; + m_strafeSpeed = 0.0f; + m_navTimeset = game.time (); - if (cr::abs (pev->velocity.x) > 5.0f || cr::abs (pev->velocity.y) > 5.0f || cr::abs (pev->velocity.z) > 5.0f) { - m_sniperStopTime = game.time () + 2.0f; - return; + if (cr::abs (pev->velocity.x) > 5.0f || cr::abs (pev->velocity.y) > 5.0f || cr::abs (pev->velocity.z) > 5.0f) { + m_sniperStopTime = game.time () + 2.0f; + return; + } } } @@ -1331,7 +1367,7 @@ void Bot::attackMovement () { }; auto strafeUpdateTime = [] () { - return game.time () + rg (0.3f, 1.0f); + return game.time () + rg (0.3f, 0.8f); }; // to start strafing, we have to first figure out if the target is on the left side or right side @@ -1401,7 +1437,7 @@ void Bot::attackMovement () { } } else if (m_fightStyle == Fight::Stay) { - const bool alreadyDucking = m_duckTime > game.time () || isDucking (); + const bool alreadyDucking = m_duckTime >= game.time () || isDucking () || ((pev->button | pev->oldbuttons) & IN_DUCK); if (alreadyDucking) { m_duckTime = game.time () + m_frameInterval * 3.0f; diff --git a/src/manager.cpp b/src/manager.cpp index 68736ee..8efd9e2 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -1467,6 +1467,11 @@ void Bot::newRound () { m_timeDoorOpen = 0.0f; + for (auto &fall : m_checkFallPoint) { + fall = nullptr; + } + m_checkFall = false; + resetCollision (); resetDoubleJump (); diff --git a/src/navigate.cpp b/src/navigate.cpp index e79c9de..8bb7ea8 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -459,7 +459,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) { } m_hindrance = nullptr; - float distanceSq = cr::sqrf (384.0f); + float distanceSq = cr::sqrf (512.0f); if (isOnLadder () || isInNarrowPlace ()) { return; @@ -489,6 +489,16 @@ void Bot::doPlayerAvoidance (const Vector &normal) { if (ownPrio > otherPrio) { continue; } + + // they are higher priority - make way, unless we're already making way for someone more important + if (!game.isNullEntity (m_hindrance) && m_hindrance != client.ent) { + const auto avoidPrio = bots.getPlayerPriority (m_hindrance); + + // ignore because we're already avoiding someone better + if (avoidPrio > otherPrio) { + continue; + } + } const auto nearestDistanceSq = client.ent->v.origin.distanceSq (pev->origin); if (nearestDistanceSq < cr::sqrf (pev->maxspeed) && nearestDistanceSq < distanceSq) { @@ -815,6 +825,59 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { } } +void Bot::checkFall () { + + if (!m_checkFall) { + if (isOnFloor ()) { + m_checkFallPoint[0] = pev->origin; + + if (!game.isNullEntity (m_enemy)) { + m_checkFallPoint[1] = game.getEntityOrigin (m_enemy); + } + else if (m_currentNodeIndex != kInvalidNodeIndex) { + m_checkFallPoint[1] = m_pathOrigin; + } + else { + m_checkFallPoint[1] = nullptr; + } + } + else if (!isOnLadder () && !isInWater ()) { + if (!m_checkFallPoint[0].empty () && !m_checkFallPoint[1].empty ()) { + m_checkFall = true; + } + } + } + + if (!m_checkFall || !isOnFloor ()) { + return; + } + m_checkFall = false; + bool fixFall = false; + + const float baseDistanceSq = m_checkFallPoint[0].distanceSq (m_checkFallPoint[1]); + const float nowDistanceSq = pev->origin.distanceSq (m_checkFallPoint[1]); + + if (nowDistanceSq > baseDistanceSq + && (nowDistanceSq > baseDistanceSq * 1.2f || nowDistanceSq > baseDistanceSq + 200.0f) + && baseDistanceSq >= cr::sqrf (80.0f) && nowDistanceSq >= cr::sqrf (100.0f)) { + fixFall = true; + } + else if (pev->origin.z + 128.0f < m_checkFallPoint[1].z && pev->origin.z + 128.0f < m_checkFallPoint[0].z) { + fixFall = true; + } + + if (m_currentNodeIndex != kInvalidNodeIndex) { + if (pev->origin.distanceSq (m_checkFallPoint[1]) <= cr::sqrf (32.0f) && pev->origin.z + 16.0f < m_checkFallPoint[1].z) { + fixFall = true; + } + } + + if (fixFall) { + m_currentNodeIndex = kInvalidNodeIndex; + findValidNode (); + } +} + void Bot::moveToGoal () { findValidNode (); @@ -1641,7 +1704,7 @@ bool Bot::findNextBestNode () { int busyIndex = kInvalidNodeIndex; float lessDist[3] = { kInfiniteDistance, kInfiniteDistance, kInfiniteDistance }; - int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex , kInvalidNodeIndex }; + int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex, kInvalidNodeIndex }; const auto &origin = pev->origin + pev->velocity * m_frameInterval; const auto &bucket = graph.getNodesInBucket (origin); @@ -3093,9 +3156,9 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) { if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) { continue; } - const auto length = client.origin.distanceSq (graph[index].origin); + const auto distanceSq = client.origin.distanceSq (graph[index].origin); - if (length < cr::clamp (cr::sqrf (graph[index].radius) * 2.0f, cr::sqrf (90.0f), cr::sqrf (120.0f))) { + if (distanceSq < cr::clamp (cr::sqrf (graph[index].radius) * 2.0f, cr::sqrf (90.0f), cr::sqrf (120.0f))) { return true; } auto bot = bots[client.ent]; @@ -3103,7 +3166,7 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) { if (bot == nullptr || bot == this || !bot->m_isAlive) { continue; } - return bot->m_currentNodeIndex == index; + return bot->m_currentNodeIndex == index || bot->m_previousNodes[0] == index; } return false; } diff --git a/src/tasks.cpp b/src/tasks.cpp index 8c45994..7a42642 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -211,7 +211,7 @@ void Bot::normal_ () { else if (m_team == Team::CT) { if (!bots.isBombPlanted () && numFriendsNear (pev->origin, 210.0f) < 4) { const int index = findDefendNode (m_path->origin); - float campTime = rg (25.0f, 40.f); + float campTime = rg (25.0f, 40.0f); // rusher bots don't like to camp too much if (m_personality == Personality::Rusher) { @@ -270,7 +270,7 @@ void Bot::normal_ () { if ((!cr::fzero (m_moveSpeed) && m_moveSpeed > shiftSpeed) && (cv_walking_allowed && mp_footsteps) && m_difficulty >= Difficulty::Normal && (m_heardSoundTime + 6.0f >= game.time () || (m_states & Sense::HearingEnemy)) - && pev->origin.distanceSq (m_lastEnemyOrigin) < cr::sqrf (768.0f) + && numEnemiesNear (pev->origin, 768.0f) >= 1 && !isKnifeMode () && !bots.isBombPlanted ()) { @@ -623,12 +623,20 @@ void Bot::camp_ () { // random camp dir, or prediction auto useRandomCampDirOrPredictEnemy = [&] () { - if (game.isNullEntity (m_lastEnemy) || !m_lastEnemyOrigin.empty ()) { - auto pathLength = 0; - auto lastEnemyNearestIndex = findAimingNode (m_lastEnemyOrigin, pathLength); + if (!m_lastEnemyOrigin.empty ()) { + auto pathLength = m_lastPredictLength; + auto predictNode = m_lastPredictIndex; - if (pathLength > 1 && graph.exists (lastEnemyNearestIndex)) { - m_lookAtSafe = graph[lastEnemyNearestIndex].origin; + if (pathLength > 1 && graph.exists (predictNode)) { + m_lookAtSafe = graph[predictNode].origin + pev->view_ofs; + } + else { + pathLength = 0; + predictNode = findAimingNode (m_lastEnemyOrigin, pathLength); + + if (pathLength > 1 && graph.exists (predictNode)) { + m_lookAtSafe = graph[predictNode].origin + pev->view_ofs; + } } } else { @@ -637,10 +645,8 @@ void Bot::camp_ () { }; if (m_nextCampDirTime < game.time ()) { - m_nextCampDirTime = game.time () + rg (2.0f, 5.0f); - if (m_pathFlags & NodeFlag::Camp) { - Vector dest; + Vector dest {}; // switch from 1 direction to the other if (m_campDirection < 1) { @@ -673,6 +679,7 @@ void Bot::camp_ () { else { useRandomCampDirOrPredictEnemy (); } + m_nextCampDirTime = game.time () + rg (1.0f, 4.0f); } // press remembered crouch button pev->button |= m_campButtons; diff --git a/src/vision.cpp b/src/vision.cpp index 0acc34b..c63b3f1 100644 --- a/src/vision.cpp +++ b/src/vision.cpp @@ -550,10 +550,10 @@ void Bot::setAimDirection () { && pev->origin.distanceSq (destOrigin) < cr::sqrf (512.0f)) { const auto nextPathIndex = m_pathWalk.next (); - const auto doubleNextPath = m_pathWalk.doubleNext (); + const auto nextPathX2 = m_pathWalk.nextX2 (); - if (vistab.visible (m_currentNodeIndex, doubleNextPath)) { - const auto &gn = graph[doubleNextPath]; + if (vistab.visible (m_currentNodeIndex, nextPathX2)) { + const auto &gn = graph[nextPathX2]; m_lookAt = gn.origin + pev->view_ofs; } else if (vistab.visible (m_currentNodeIndex, nextPathIndex)) {