From 6f912eb0562d84e765e72f322ab04d61a9add0c4 Mon Sep 17 00:00:00 2001 From: jeefo Date: Mon, 20 Sep 2021 13:50:00 +0300 Subject: [PATCH] Refactoring (#246) add: yb_chat_percent, yb_camping_time_[min/max], yb_danger_factor[min/max], yb_chat_percent cvars fix: possible crash with difficulty 0 fix: debug_goal should not be used as last history fix: startup on old hlds, because of missing pfnGetFileSize fix: crash with out-bounds read change: again tweaked some aiming code change: player avoidance code so bots will jump less when stuck change: max followers is just /4 of max players refactor: redone distance between vectors refactor: remove magic numbers in graph.add function --- ext/crlib | 2 +- inc/graph.h | 20 ++++++-- src/botlib.cpp | 99 ++++++++++++++++++++-------------------- src/chatlib.cpp | 3 +- src/combat.cpp | 34 +++++++------- src/control.cpp | 2 +- src/graph.cpp | 116 +++++++++++++++++++++++++---------------------- src/manager.cpp | 2 +- src/navigate.cpp | 109 +++++++++++++++++++++++++------------------- src/support.cpp | 4 +- 10 files changed, 214 insertions(+), 177 deletions(-) diff --git a/ext/crlib b/ext/crlib index 26c617d..b5392f3 160000 --- a/ext/crlib +++ b/ext/crlib @@ -1 +1 @@ -Subproject commit 26c617d0a0cd18238a62f6e829da9161b7d56bd0 +Subproject commit b5392f30bd5c0f2d0de0c6b3f39104c9aa1b1ee0 diff --git a/inc/graph.h b/inc/graph.h index 1b6d064..d83bdc2 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -43,11 +43,6 @@ CR_DECLARE_SCOPED_ENUM (PathConnection, Bidirectional ) -// defines node add commands -CR_DECLARE_SCOPED_ENUM (GraphAdd, - Normal = 0, -) - // a* route state CR_DECLARE_SCOPED_ENUM (RouteState, Open = 0, @@ -94,6 +89,20 @@ CR_DECLARE_SCOPED_ENUM (LiftState, Leaving ) +// node add flags +CR_DECLARE_SCOPED_ENUM (NodeAddFlag, + Normal = 0, + TOnly = 1, + CTOnly = 2, + NoHostage = 3, + Rescue = 4, + Camp = 5, + CampEnd = 6, + JumpStart = 9, + JumpEnd = 10, + Goal = 100 +) + // a* route struct Route { float g, f; @@ -312,6 +321,7 @@ public: int getDangerDamage (int team, int start, int goal); int getPathDist (int srcIndex, int destIndex); int clearConnections (int index); + int getBspSize (); float calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin); diff --git a/src/botlib.cpp b/src/botlib.cpp index a1bcf2f..98737ca 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -10,14 +10,17 @@ ConVar cv_debug ("yb_debug", "0", "Enables or disables useful messages about bot states. Not required for end users.", true, 0.0f, 4.0f); ConVar cv_debug_goal ("yb_debug_goal", "-1", "Forces all alive bots to build path and go to the specified here graph node.", true, -1.0f, kMaxNodes); ConVar cv_user_follow_percent ("yb_user_follow_percent", "20", "Specifies the percent of bots, than can follow leader on each round start.", true, 0.0f, 100.0f); -ConVar cv_user_max_followers ("yb_user_max_followers", "1", "Specifies how many bots can follow a single user.", true, 0.0f, static_cast (kGameMaxPlayers / 2)); +ConVar cv_user_max_followers ("yb_user_max_followers", "1", "Specifies how many bots can follow a single user.", true, 0.0f, static_cast (kGameMaxPlayers / 4)); ConVar cv_jasonmode ("yb_jasonmode", "0", "If enabled, all bots will be forced only the knife, skipping weapon buying routines."); ConVar cv_radio_mode ("yb_radio_mode", "2", "Allows bots to use radio or chattter.\nAllowed values: '0', '1', '2'.\nIf '0', radio and chatter is disabled.\nIf '1', only radio allowed.\nIf '2' chatter and radio allowed.", true, 0.0f, 2.0f); ConVar cv_economics_rounds ("yb_economics_rounds", "1", "Specifies whether bots able to use team economics, like do not buy any weapons for whole team to keep money for better guns."); ConVar cv_walking_allowed ("yb_walking_allowed", "1", "Specifies whether bots able to use 'shift' if they thinks that enemy is near."); -ConVar cv_camping_allowed ("yb_camping_allowed", "1", "Allows or disallows bots to camp. Doesn't affects bomb/hostage defending tasks"); +ConVar cv_camping_allowed ("yb_camping_allowed", "1", "Allows or disallows bots to camp. Doesn't affects bomb/hostage defending tasks."); + +ConVar cv_camping_time_min ("yb_camping_time_min", "15.0", "Lower bound of time from which time for camping is calculated", true, 5.0f, 90.0f); +ConVar cv_camping_time_max ("yb_camping_time_max", "45.0", "Upper bound of time from which time for camping is calculated", true, 15.0f, 120.0f); ConVar cv_tkpunish ("yb_tkpunish", "1", "Allows or disallows bots to take revenge of teamkillers / team attacks."); ConVar cv_freeze_bots ("yb_freeze_bots", "0", "If enabled the bots think function is disabled, so bots will not move anywhere from their spawn spots."); @@ -30,7 +33,7 @@ ConVar cv_restricted_weapons ("yb_restricted_weapons", "", "Specifies semicolon ConVar cv_attack_monsters ("yb_attack_monsters", "0", "Allows or disallows bots to attack monsters."); ConVar cv_pickup_custom_items ("yb_pickup_custom_items", "0", "Allows or disallows bots to pickup custom items."); -ConVar cv_ignore_objectives ("yb_ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages"); +ConVar cv_ignore_objectives ("yb_ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages."); // game console variables ConVar mp_c4timer ("mp_c4timer", nullptr, Var::GameRef); @@ -154,7 +157,7 @@ void Bot::checkGrenadesThrow () { return; } } - float distance = (m_lastEnemyOrigin - pev->origin).length2d (); + float distance = m_lastEnemyOrigin.distance2d (pev->origin); // don't throw grenades at anything that isn't on the ground! if (!(m_lastEnemy->v.flags & FL_ONGROUND) && !m_lastEnemy->v.waterlevel && m_lastEnemyOrigin.z > pev->absmax.z) { @@ -346,8 +349,8 @@ void Bot::avoidGrenades () { } if (!(pent->v.flags & FL_ONGROUND)) { - float distance = (pent->v.origin - pev->origin).lengthSq (); - float distanceMoved = ((pent->v.origin + pent->v.velocity * getFrameInterval ()) - pev->origin).lengthSq (); + float distance = pent->v.origin.distanceSq (pev->origin); + float distanceMoved = pev->origin.distance (pent->v.origin + pent->v.velocity * getFrameInterval ()); if (distanceMoved < distance && distance < cr::square (500.0f)) { const auto &dirToPoint = (pev->origin - pent->v.origin).normalize2d (); @@ -365,7 +368,7 @@ void Bot::avoidGrenades () { } else if ((pent->v.flags & FL_ONGROUND) && strcmp (model, "smokegrenade.mdl") == 0) { if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) { - float distance = (pent->v.origin - pev->origin).length (); + float distance = pent->v.origin.distance (pev->origin); // shrink bot's viewing distance to smoke grenade's distance if (m_viewDistance > distance) { @@ -404,7 +407,7 @@ void Bot::checkBreakablesAround () { continue; } const auto &origin = game.getEntityOrigin (breakable); - const auto lengthToObstacle = (origin - pev->origin).lengthSq (); + const auto lengthToObstacle = origin.distanceSq (pev->origin); // too far, skip it if (lengthToObstacle > cr::square (400.0f)) { @@ -505,7 +508,7 @@ void Bot::updatePickups () { const Vector &origin = game.getEntityOrigin (ent); // too far from us ? - if ((pev->origin - origin).lengthSq () > radius) { + if (pev->origin.distanceSq (origin) > radius) { continue; } @@ -544,7 +547,7 @@ void Bot::updatePickups () { } // too far from us ? - if ((pev->origin - origin).lengthSq () > radius) { + if (pev->origin.distanceSq (origin) > radius) { continue; } @@ -776,7 +779,7 @@ void Bot::updatePickups () { } } - if ((pev->origin - origin).lengthSq () > cr::square (60.0f)) { + if (pev->origin.distanceSq (origin) > cr::square (60.0f)) { if (!graph.isNodeReacheable (pev->origin, origin)) { allowPickup = false; @@ -855,7 +858,7 @@ void Bot::getCampDirection (Vector *dest) { // check if the trace hit something... if (tr.flFraction < 1.0f) { - float length = (tr.vecEndPos - src).lengthSq (); + float length = tr.vecEndPos.distanceSq (src); if (length > 10000.0f) { return; @@ -1697,7 +1700,7 @@ void Bot::overrideConditions () { // special handling, if we have a knife in our hands if ((bots.getRoundStartTime () + 6.0f > game.time () || !hasAnyWeapons ()) && usesKnife () && (util.isPlayer (m_enemy) || (cv_attack_monsters.bool_ () && util.isMonster (m_enemy)))) { - float length = (pev->origin - m_enemy->v.origin).length2d (); + float length = pev->origin.distance2d (m_enemy->v.origin); // do waypoint movement if enemy is not reachable with a knife if (length > 100.0f && (m_states & Sense::SeeingEnemy)) { @@ -1891,7 +1894,7 @@ void Bot::filterTasks () { filter[Task::PickupItem].desire = 50.0f; // always pickup button } else { - float distance = (500.0f - (game.getEntityOrigin (m_pickupItem) - pev->origin).length ()) * 0.2f; + float distance = (500.0f - pev->origin.distance (game.getEntityOrigin (m_pickupItem))) * 0.2f; if (distance > 50.0f) { distance = 50.0f; @@ -1955,7 +1958,7 @@ void Bot::filterTasks () { // if half of the round is over, allow hunting if (getCurrentTaskId () != Task::EscapeFromBomb && game.isNullEntity (m_enemy) && bots.getRoundMidTime () < game.time () && !m_isUsingGrenade && m_currentNodeIndex != graph.getNearest (m_lastEnemyOrigin) && m_personality != Personality::Careful && !cv_ignore_enemies.bool_ ()) { - float desireLevel = 4096.0f - ((1.0f - tempAgression) * (m_lastEnemyOrigin - pev->origin).length ()); + float desireLevel = 4096.0f - ((1.0f - tempAgression) * m_lastEnemyOrigin.distance (pev->origin)); desireLevel = (100.0f * desireLevel) / 4096.0f; desireLevel -= retreatLevel; @@ -2167,7 +2170,7 @@ bool Bot::isEnemyThreat () { } // if enemy is near or facing us directly - if ((m_enemy->v.origin - pev->origin).lengthSq () < cr::square (256.0f) || isInViewCone (m_enemy->v.origin)) { + if (m_enemy->v.origin.distanceSq (pev->origin) < cr::square (256.0f) || isInViewCone (m_enemy->v.origin)) { return true; } return false; @@ -2188,7 +2191,7 @@ bool Bot::reactOnEnemy () { } int enemyIndex = graph.getNearest (m_enemy->v.origin); - auto lineDist = (m_enemy->v.origin - pev->origin).length (); + auto lineDist = m_enemy->v.origin.distance (pev->origin); auto pathDist = static_cast (graph.getPathDist (ownIndex, enemyIndex)); if (pathDist - lineDist > 112.0f || isOnLadder ()) { @@ -2224,7 +2227,7 @@ void Bot::checkRadioQueue () { m_radioOrder = 0; return; } - float distance = (m_radioEntity->v.origin - pev->origin).length (); + float distance = m_radioEntity->v.origin.distance (pev->origin); switch (m_radioOrder) { case Radio::CoverMe: @@ -2497,7 +2500,7 @@ void Bot::checkRadioQueue () { } auto enemy = client.ent; - float curDist = (m_radioEntity->v.origin - enemy->v.origin).lengthSq (); + float curDist = m_radioEntity->v.origin.distanceSq (enemy->v.origin); if (curDist < nearestDistance) { nearestDistance = curDist; @@ -2599,7 +2602,7 @@ void Bot::checkRadioQueue () { // find nearest bomb waypoint to player for (auto &point : graph.m_goalPoints) { - distance = (graph[point].origin - m_radioEntity->v.origin).lengthSq (); + distance = graph[point].origin.distanceSq (m_radioEntity->v.origin); if (distance < minDistance) { minDistance = distance; @@ -2652,7 +2655,7 @@ void Bot::checkRadioQueue () { } auto enemy = client.ent; - float dist = (m_radioEntity->v.origin - enemy->v.origin).lengthSq (); + float dist = m_radioEntity->v.origin.distanceSq (enemy->v.origin); if (dist < nearestDistance) { nearestDistance = dist; @@ -2723,7 +2726,7 @@ void Bot::updateAimDir () { else if (flags & AimFlags::Grenade) { m_lookAt = m_throw; - float throwDistance = (m_throw - pev->origin).length (); + float throwDistance = m_throw.distance (pev->origin); float coordCorrection = 0.0f; if (throwDistance > 100.0f && throwDistance < 800.0f) { @@ -2799,7 +2802,7 @@ void Bot::updateAimDir () { return nullptr; }; - if (m_moveToGoal && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && (pev->origin - m_destOrigin).lengthSq () < cr::square (160.0f)) { + if (m_moveToGoal && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && pev->origin.distanceSq (m_destOrigin) < cr::square (160.0f)) { auto nextPathIndex = m_pathWalk.next (); if (graph.isVisible (m_currentNodeIndex, nextPathIndex)) { @@ -2817,7 +2820,7 @@ void Bot::updateAimDir () { auto dangerIndex = graph.getDangerIndex (m_team, m_currentNodeIndex, m_currentNodeIndex); if (graph.exists (dangerIndex) && graph.isVisible (m_currentNodeIndex, dangerIndex) && !(graph[dangerIndex].flags & NodeFlag::Crouch)) { - if ((graph[dangerIndex].origin - pev->origin).lengthSq () < cr::square (160.0f)) { + if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::square (160.0f)) { m_lookAt = m_destOrigin; } else { @@ -2910,7 +2913,7 @@ void Bot::frame () { if (bots.isBombPlanted () && m_team == Team::CT && m_notKilled) { const Vector &bombPosition = graph.getBombOrigin (); - if (!m_hasProgressBar && getCurrentTaskId () != Task::EscapeFromBomb && (pev->origin - bombPosition).lengthSq () < cr::square (1540.0f) && !isBombDefusing (bombPosition)) { + if (!m_hasProgressBar && getCurrentTaskId () != Task::EscapeFromBomb && pev->origin.distanceSq (bombPosition) < cr::square (1540.0f) && !isBombDefusing (bombPosition)) { m_itemIgnore = nullptr; m_itemCheckTime = game.time (); @@ -2927,7 +2930,7 @@ void Bot::frame () { } // clear enemy far away - if (!m_lastEnemyOrigin.empty () && !game.isNullEntity (m_lastEnemy) && (pev->origin - m_lastEnemyOrigin).lengthSq () >= cr::square (1600.0f)) { + if (!m_lastEnemyOrigin.empty () && !game.isNullEntity (m_lastEnemy) && pev->origin.distanceSq (m_lastEnemyOrigin) >= cr::square (1600.0f)) { m_lastEnemy = nullptr; m_lastEnemyOrigin = nullptr; } @@ -3134,7 +3137,7 @@ void Bot::normal_ () { if (!(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && !m_reloadState) { m_reloadState = Reload::Primary; } - m_timeCamping = game.time () + rg.get (10.0f, 25.0f); + m_timeCamping = game.time () + rg.get (cv_camping_time_min.float_ (), cv_camping_time_max.float_ ()); startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, m_timeCamping, true); m_camp = m_path->origin + m_path->start.forward () * 500.0f; @@ -3239,7 +3242,7 @@ void Bot::normal_ () { destIndex = graph.getFarest (pev->origin, 512.0f); } - if (m_prevGoalIndex == currIndex && !(graph[currIndex].flags & NodeFlag::Goal)) { + if (!graph.exists (cv_debug_goal.int_ ()) && graph.exists (currIndex) && m_prevGoalIndex == currIndex && !(graph[currIndex].flags & NodeFlag::Goal)) { m_goalHistory.push (currIndex); } m_prevGoalIndex = destIndex; @@ -3388,7 +3391,7 @@ void Bot::huntEnemy_ () { } } - if ((m_lastEnemyOrigin - pev->origin).lengthSq () < cr::square (512.0f)) { + if (m_lastEnemyOrigin.distanceSq (pev->origin) < cr::square (512.0f)) { m_moveSpeed = getShiftSpeed (); } } @@ -3616,7 +3619,7 @@ void Bot::camp_ () { const Vector &dotB = (graph[i].origin - pev->origin).normalize2d (); if ((dotA | dotB) > 0.9f) { - int distance = static_cast ((pev->origin - graph[i].origin).length ()); + int distance = static_cast (graph[i].origin.distance (pev->origin)); if (numFoundPoints >= 3) { for (int j = 0; j < 3; ++j) { @@ -3900,7 +3903,7 @@ void Bot::defuseBomb_ () { m_strafeSpeed = 0.0f; // bot is reloading and we close enough to start defusing - if (m_isReloading && (bombPos - pev->origin).length2d () < 80.0f) { + if (m_isReloading && bombPos.distance2d (pev->origin) < 80.0f) { if (m_numEnemiesLeft == 0 || timeToBlowUp < fullDefuseTime + 7.0f || ((getAmmoInClip () > 8 && m_reloadState == Reload::Primary) || (getAmmoInClip () > 5 && m_reloadState == Reload::Secondary))) { int weaponIndex = bestWeaponCarried (); @@ -3942,8 +3945,8 @@ void Bot::defuseBomb_ () { botStandOrigin = pev->origin; } - float duckLength = (m_entity - botDuckOrigin).lengthSq (); - float standLength = (m_entity - botStandOrigin).lengthSq (); + float duckLength = m_entity.distanceSq (botDuckOrigin); + float standLength = m_entity.distanceSq (botStandOrigin); if (duckLength > 5625.0f || standLength > 5625.0f) { if (standLength < duckLength) { @@ -4026,7 +4029,7 @@ void Bot::followUser_ () { m_reloadState = Reload::Primary; } - if ((m_targetEntity->v.origin - pev->origin).lengthSq () > cr::square (130.0f)) { + if (m_targetEntity->v.origin.distanceSq (pev->origin) > cr::square (130.0f)) { m_followWaitTime = 0.0f; } else { @@ -4106,7 +4109,7 @@ void Bot::throwExplosive_ () { ignoreCollision (); - if ((pev->origin - dest).lengthSq () < cr::square (400.0f)) { + if (pev->origin.distanceSq (dest) < cr::square (400.0f)) { // heck, I don't wanna blow up myself m_grenadeCheckTime = game.time () + kGrenadeCheckTime; @@ -4173,7 +4176,7 @@ void Bot::throwFlashbang_ () { ignoreCollision (); - if ((pev->origin - dest).lengthSq () < cr::square (400.0f)) { + if (pev->origin.distanceSq (dest) < cr::square (400.0f)) { m_grenadeCheckTime = game.time () + kGrenadeCheckTime; // heck, I don't wanna blow up myself selectBestWeapon (); @@ -4380,7 +4383,7 @@ void Bot::escapeFromBomb_ () { float safeRadius = rg.get (1513.0f, 2048.0f); for (int i = 0; i < graph.length (); ++i) { - if ((graph[i].origin - graph.getBombOrigin ()).length () < safeRadius || isOccupiedNode (i)) { + if (graph[i].origin.distance (graph.getBombOrigin ()) < safeRadius || isOccupiedNode (i)) { continue; } int pathDistance = graph.getPathDist (m_currentNodeIndex, i); @@ -4457,7 +4460,7 @@ void Bot::pickupItem_ () { m_entity = dest; // find the distance to the item - float itemDistance = (dest - pev->origin).length (); + float itemDistance = dest.distance (pev->origin); switch (m_pickupType) { case Pickup::DroppedC4: @@ -4730,7 +4733,7 @@ void Bot::logic () { // see how far bot has moved since the previous position... if (m_checkTerrain) { - movedDistance = (m_prevOrigin - pev->origin).length (); + movedDistance = m_prevOrigin.distance (pev->origin); } // save current position as previous @@ -5074,7 +5077,7 @@ bool Bot::hasHostage () { if (!game.isNullEntity (hostage)) { // don't care about dead hostages - if (hostage->v.health <= 0.0f || (pev->origin - hostage->v.origin).lengthSq () > cr::square (600.0f)) { + if (hostage->v.health <= 0.0f || pev->origin.distanceSq (hostage->v.origin) > cr::square (600.0f)) { hostage = nullptr; continue; } @@ -5273,7 +5276,7 @@ void Bot::dropWeaponForUser (edict_t *user, bool discardC4) { // this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop* // command, very useful, when i'm don't have money to buy anything... ) - if (util.isAlive (user) && m_moneyAmount >= 2000 && hasPrimaryWeapon () && (user->v.origin - pev->origin).length () <= 450.0f) { + if (util.isAlive (user) && m_moneyAmount >= 2000 && hasPrimaryWeapon () && user->v.origin.distance (pev->origin) <= 450.0f) { m_aimFlags |= AimFlags::Entity; m_lookAt = user->v.origin; @@ -5506,7 +5509,7 @@ Vector Bot::isBombAudible () { } // we hear bomb if length greater than radius - if (desiredRadius < (pev->origin - bombOrigin).length2d ()) { + if (desiredRadius < pev->origin.distance2d (bombOrigin)) { return bombOrigin; } return nullptr; @@ -5645,7 +5648,7 @@ bool Bot::isOutOfBombTimer () { const Vector &bombOrigin = graph.getBombOrigin (); // for terrorist, if timer is lower than 13 seconds, return true - if (timeLeft < 13.0f && m_team == Team::Terrorist && (bombOrigin - pev->origin).lengthSq () < cr::square (964.0f)) { + if (timeLeft < 13.0f && m_team == Team::Terrorist && bombOrigin.distanceSq (pev->origin) < cr::square (964.0f)) { return true; } bool hasTeammatesWithDefuserKit = false; @@ -5653,7 +5656,7 @@ bool Bot::isOutOfBombTimer () { // check if our teammates has defusal kit for (const auto &bot : bots) { // search players with defuse kit - if (bot.get () != this && bot->m_team == Team::CT && bot->m_hasDefuser && (bombOrigin - bot->pev->origin).lengthSq () < cr::square (512.0f)) { + if (bot.get () != this && bot->m_team == Team::CT && bot->m_hasDefuser && bombOrigin.distanceSq (bot->pev->origin) < cr::square (512.0f)) { hasTeammatesWithDefuserKit = true; break; } @@ -5691,7 +5694,7 @@ void Bot::updateHearing () { if (!game.checkVisibility (client.ent, set)) { continue; } - float distance = (client.noise.pos - pev->origin).length (); + float distance = client.noise.pos.distance (pev->origin); if (distance > client.noise.dist) { continue; @@ -5739,9 +5742,9 @@ void Bot::updateHearing () { } else { // if bot had an enemy but the heard one is nearer, take it instead - float distance = (m_lastEnemyOrigin - pev->origin).lengthSq (); + float distance = m_lastEnemyOrigin.distanceSq (pev->origin); - if (distance > (player->v.origin - pev->origin).lengthSq () && m_seeEnemyTime + 2.0f < game.time ()) { + if (distance > player->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) { m_lastEnemy = player; m_lastEnemyOrigin = player->v.origin; } @@ -5810,7 +5813,7 @@ bool Bot::isBombDefusing (const Vector &bombOrigin) { } auto bot = bots[client.ent]; - auto bombDistance = (client.origin - bombOrigin).lengthSq (); + auto bombDistance = client.origin.distanceSq (bombOrigin); if (bot && !bot->m_notKilled) { if (m_team != bot->m_team || bot->getCurrentTaskId () == Task::EscapeFromBomb) { diff --git a/src/chatlib.cpp b/src/chatlib.cpp index 9989c8f..319c7cd 100644 --- a/src/chatlib.cpp +++ b/src/chatlib.cpp @@ -8,6 +8,7 @@ #include ConVar cv_chat ("yb_chat", "1", "Enables or disables bots chat functionality."); +ConVar cv_chat_percent ("yb_chat_percent", "30", "Chances bot will send random dead chat when killed.", true, 0.0f, 100.0f); void BotSupport::stripTags (String &line) { if (line.empty ()) { @@ -316,7 +317,7 @@ bool Bot::isReplyingToChat () { void Bot::checkForChat () { // say a text every now and then - if (rg.chance (30) || m_notKilled || !cv_chat.bool_ ()) { + if (rg.chance (cv_chat_percent.int_ ()) || m_notKilled || !cv_chat.bool_ ()) { return; } diff --git a/src/combat.cpp b/src/combat.cpp index 106fc4b..512ac11 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -23,7 +23,7 @@ int Bot::numFriendsNear (const Vector &origin, float radius) { continue; } - if ((client.origin - origin).lengthSq () < cr::square (radius)) { + if (client.origin.distanceSq (origin) < cr::square (radius)) { count++; } } @@ -38,7 +38,7 @@ int Bot::numEnemiesNear (const Vector &origin, float radius) { continue; } - if ((client.origin - origin).lengthSq () < cr::square (radius)) { + if (client.origin.distanceSq (origin) < cr::square (radius)) { count++; } } @@ -246,7 +246,7 @@ bool Bot::lookupEnemies () { player = m_enemy; // is player is alive - if (m_enemyUpdateTime > game.time () && (m_enemy->v.origin - pev->origin).lengthSq () < nearestDistance && util.isAlive (player) && seesEnemy (player)) { + if (m_enemyUpdateTime > game.time () && m_enemy->v.origin.distanceSq (pev->origin) < nearestDistance && util.isAlive (player) && seesEnemy (player)) { newEnemy = player; } } @@ -274,7 +274,7 @@ bool Bot::lookupEnemies () { if (seesEnemy (intresting)) { // higher priority for big monsters float scaleFactor = (1.0f / calculateScaleFactor (intresting)); - float distance = (intresting->v.origin - pev->origin).lengthSq () * scaleFactor; + float distance = intresting->v.origin.distanceSq (pev->origin) * scaleFactor; if (distance * 0.7f < nearestDistance) { nearestDistance = distance; @@ -307,7 +307,7 @@ bool Bot::lookupEnemies () { shieldEnemy = player; continue; } - float distance = (player->v.origin - pev->origin).lengthSq (); + float distance = player->v.origin.distanceSq (pev->origin); if (distance * 0.7f < nearestDistance) { nearestDistance = distance; @@ -482,7 +482,7 @@ const Vector &Bot::getEnemyBodyOffset () { if (!m_enemyParts) { return m_enemyOrigin; } - float distance = (m_enemy->v.origin - pev->origin).length (); + float distance = m_enemy->v.origin.distance (pev->origin); // do not aim at head, at long distance (only if not using sniper weapon) if ((m_enemyParts & Visibility::Body) && !usesSniper () && distance > (m_difficulty > Difficulty::Normal ? 2000.0f : 1000.0f)) { @@ -547,9 +547,9 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) { { 0.0f, 0.0f, 0.0f }, // none { 0.0f, 0.0f, 0.0f }, // melee { 2.5f, 1.5f, 0.2f }, // pistol - { 6.5f, 2.0f, -9.9f }, // shotgun - { 0.5f, -3.5f, -9.0f }, // zoomrifle - { 0.5f, -3.5f, -9.5f }, // rifle + { 6.5f, 0.0f, -9.9f }, // shotgun + { 0.5f, -6.5f, -9.0f }, // zoomrifle + { 0.5f, -6.5f, -9.5f }, // rifle { 2.5f, 0.5f, -4.5f }, // smg { 0.5f, 0.5f, 1.5f }, // sniper { 1.5f, -2.0f, -9.0f } // heavy @@ -597,7 +597,7 @@ bool Bot::isFriendInLineOfFire (float distance) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team || client.ent == ent ()) { continue; } - auto friendDistance = (client.ent->v.origin - pev->origin).lengthSq (); + auto friendDistance = client.ent->v.origin.distanceSq (pev->origin); if (friendDistance <= distance && util.getShootingCone (ent (), client.ent->v.origin) > friendDistance / (friendDistance + 1089.0f)) { return true; @@ -632,14 +632,14 @@ bool Bot::isPenetrableObstacle (const Vector &dest) { game.testLine (dest, source, TraceIgnore::Monsters, ent (), &tr); if (!cr::fequal (tr.flFraction, 1.0f)) { - if ((tr.vecEndPos - dest).lengthSq () > cr::square (800.0f)) { + if (tr.vecEndPos.distanceSq (dest) > cr::square (800.0f)) { return false; } if (tr.vecEndPos.z >= dest.z + 200.0f) { return false; } - obstacleDistance = (tr.vecEndPos - source).lengthSq (); + obstacleDistance = tr.vecEndPos.distanceSq (source); } } const float distance = cr::square (75.0f); @@ -690,7 +690,7 @@ bool Bot::isPenetrableObstacle2 (const Vector &dest) { } if (numHits < 3 && thikness < 98) { - if ((dest - point).lengthSq () < 13143.0f) { + if (dest.distanceSq (point) < 13143.0f) { return true; } } @@ -900,7 +900,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) { void Bot::fireWeapons () { // this function will return true if weapon was fired, false otherwise - float distance = (m_lookAt - getEyesPos ()).length (); // how far away is the enemy? + float distance = m_lookAt.distance (getEyesPos ()); // how far away is the enemy? // or if friend in line of fire, stop this too but do not update shoot time if (!game.isNullEntity (m_enemy)) { @@ -1020,7 +1020,7 @@ void Bot::focusEnemy () { if (m_enemySurpriseTime > game.time ()) { return; } - float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum? + float distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum? if (distance < 128.0f && !usesSniper ()) { if (usesKnife ()) { @@ -1065,7 +1065,7 @@ void Bot::attackMovement () { if (game.isNullEntity (m_enemy)) { return; } - float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum? + float distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum? if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.time ()) { int approach; @@ -1570,7 +1570,7 @@ bool Bot::isGroupOfEnemies (const Vector &location, int numEnemies, float radius continue; } - if ((client.ent->v.origin - location).lengthSq () < cr::square (radius)) { + if (client.ent->v.origin.distanceSq (location) < cr::square (radius)) { // don't target our teammates... if (client.team == m_team) { return false; diff --git a/src/control.cpp b/src/control.cpp index 4ccd2aa..0a45510 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -1351,7 +1351,7 @@ int BotControl::menuGraphType (int item) { break; case 8: - graph.add (100); + graph.add (NodeAddFlag::Goal); showMenu (Menu::NodeType); break; diff --git a/src/graph.cpp b/src/graph.cpp index e6e9aae..a55a890 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -386,6 +386,15 @@ int BotGraph::clearConnections (int index) { return numFixedLinks; } +int BotGraph::getBspSize () { + MemFile file (strings.format ("maps/%s.bsp", game.getMapName ())); + + if (file) { + return static_cast (file.length ()); + } + return 0; +} + void BotGraph::addPath (int addIndex, int pathIndex, float distance) { if (!exists (addIndex) || !exists (pathIndex)) { return; @@ -437,7 +446,7 @@ int BotGraph::getFarest (const Vector &origin, float maxDistance) { maxDistance = cr::square (maxDistance); for (const auto &path : m_paths) { - float distance = (path.origin - origin).lengthSq (); + float distance = path.origin.distanceSq (origin); if (distance > maxDistance) { index = path.number; @@ -458,7 +467,7 @@ int BotGraph::getNearestNoBuckets (const Vector &origin, float minDistance, int if (flags != -1 && !(path.flags & flags)) { continue; // if flag not -1 and node has no this flag, skip node } - float distance = (path.origin - origin).lengthSq (); + float distance = path.origin.distanceSq (origin); if (distance < minDistance) { index = path.number; @@ -491,7 +500,7 @@ int BotGraph::getNearest (const Vector &origin, float minDistance, int flags) { if (flags != -1 && !(m_paths[at].flags & flags)) { continue; // if flag not -1 and node has no this flag, skip node } - float distance = (m_paths[at].origin - origin).lengthSq (); + float distance = origin.distanceSq (m_paths[at].origin); if (distance < minDistanceSq) { index = at; @@ -523,7 +532,7 @@ IntArray BotGraph::searchRadius (float radius, const Vector &origin, int maxCoun break; } - if ((m_paths[at].origin - origin).lengthSq () < radius) { + if (origin.distanceSq (m_paths[at].origin) < radius) { result.push (at); } } @@ -548,7 +557,7 @@ void BotGraph::add (int type, const Vector &pos) { m_hasChanged = true; switch (type) { - case 5: + case NodeAddFlag::Camp: index = getEditorNeareset (); if (index != kInvalidNodeIndex) { @@ -564,7 +573,7 @@ void BotGraph::add (int type, const Vector &pos) { } break; - case 6: + case NodeAddFlag::CampEnd: index = getEditorNeareset (); if (index != kInvalidNodeIndex) { @@ -581,11 +590,11 @@ void BotGraph::add (int type, const Vector &pos) { } return; - case 9: + case NodeAddFlag::JumpStart: index = getEditorNeareset (); if (index != kInvalidNodeIndex && m_paths[index].number >= 0) { - float distance = (m_paths[index].origin - m_editor->v.origin).length (); + float distance = m_editor->v.origin.distance (m_paths[index].origin); if (distance < 50.0f) { addNewNode = false; @@ -599,11 +608,11 @@ void BotGraph::add (int type, const Vector &pos) { } break; - case 10: + case NodeAddFlag::JumpEnd: index = getEditorNeareset (); if (index != kInvalidNodeIndex && m_paths[index].number >= 0) { - float distance = (m_paths[index].origin - m_editor->v.origin).length (); + float distance = m_editor->v.origin.distance (m_paths[index].origin); if (distance < 50.0f) { addNewNode = false; @@ -627,7 +636,7 @@ void BotGraph::add (int type, const Vector &pos) { auto nearest = getEditorNeareset (); // do not allow to place waypoints "inside" waypoints, make at leat 10 units range - if (exists (nearest) && (m_paths[nearest].origin - newOrigin).lengthSq () < cr::square (10.0f)) { + if (exists (nearest) && newOrigin.distanceSq (m_paths[nearest].origin) < cr::square (10.0f)) { ctrl.msg ("Can't add node. It's way to near to %d node. Please move some units anywhere.", nearest); return; } @@ -666,11 +675,11 @@ void BotGraph::add (int type, const Vector &pos) { m_lastNode = m_editor->v.origin; } - if (type == 9) { + if (type == NodeAddFlag::JumpStart) { m_lastJumpNode = index; } - else if (type == 10) { - float distance = (m_paths[m_lastJumpNode].origin - m_editor->v.origin).length (); + else if (type == NodeAddFlag::JumpEnd) { + float distance = m_paths[m_lastJumpNode].origin.distance (m_editor->v.origin); addPath (m_lastJumpNode, index, distance); for (auto &link : m_paths[m_lastJumpNode].links) { @@ -701,32 +710,32 @@ void BotGraph::add (int type, const Vector &pos) { } switch (type) { - case 1: + case NodeAddFlag::TOnly: path->flags |= NodeFlag::Crossing; path->flags |= NodeFlag::TerroristOnly; break; - case 2: + case NodeAddFlag::CTOnly: path->flags |= NodeFlag::Crossing; path->flags |= NodeFlag::CTOnly; break; - case 3: + case NodeAddFlag::NoHostage: path->flags |= NodeFlag::NoHostage; break; - case 4: + case NodeAddFlag::Rescue: path->flags |= NodeFlag::Rescue; break; - case 5: + case NodeAddFlag::Camp: path->flags |= NodeFlag::Crossing; path->flags |= NodeFlag::Camp; path->start = m_editor->v.v_angle; break; - case 100: + case NodeAddFlag::Goal: path->flags |= NodeFlag::Goal; break; } @@ -750,7 +759,7 @@ void BotGraph::add (int type, const Vector &pos) { game.testLine (newOrigin, calc.origin, TraceIgnore::Monsters, m_editor, &tr); if (cr::fequal (tr.flFraction, 1.0f) && cr::abs (newOrigin.x - calc.origin.x) < 64.0f && cr::abs (newOrigin.y - calc.origin.y) < 64.0f && cr::abs (newOrigin.z - calc.origin.z) < m_autoPathDistance) { - float distance = (calc.origin - newOrigin).length (); + float distance = newOrigin.distance (calc.origin); addPath (index, calc.number, distance); addPath (calc.number, index, distance); @@ -759,7 +768,7 @@ void BotGraph::add (int type, const Vector &pos) { else { // check if the node is reachable from the new one if (isNodeReacheable (newOrigin, calc.origin) || isNodeReacheable (calc.origin, newOrigin)) { - float distance = (calc.origin - newOrigin).length (); + float distance = newOrigin.distance (calc.origin); if (distance < minDistance) { destIndex = calc.number; @@ -772,12 +781,12 @@ void BotGraph::add (int type, const Vector &pos) { if (exists (destIndex)) { // check if the node is reachable from the new one (one-way) if (isNodeReacheable (newOrigin, m_paths[destIndex].origin)) { - addPath (index, destIndex, (m_paths[destIndex].origin - newOrigin).length ()); + addPath (index, destIndex, newOrigin.distance (m_paths[destIndex].origin)); } // check if the new one is reachable from the node (other way) if (isNodeReacheable (m_paths[destIndex].origin, newOrigin)) { - addPath (destIndex, index, (m_paths[destIndex].origin - newOrigin).length ()); + addPath (destIndex, index, newOrigin.distance (m_paths[destIndex].origin)); } } } @@ -790,12 +799,12 @@ void BotGraph::add (int type, const Vector &pos) { // check if the node is reachable from the new one (one-way) if (isNodeReacheable (newOrigin, calc.origin)) { - addPath (index, calc.number, (calc.origin - newOrigin).length ()); + addPath (index, calc.number, calc.origin.distance (newOrigin)); } // check if the new one is reachable from the node (other way) if (isNodeReacheable (calc.origin, newOrigin)) { - addPath (calc.number, index, (calc.origin - newOrigin).length ()); + addPath (calc.number, index, calc.origin.distance (newOrigin)); } } clearConnections (index); @@ -975,7 +984,7 @@ void BotGraph::pathCreate (char dir) { return; } - float distance = (m_paths[nodeTo].origin - m_paths[nodeFrom].origin).length (); + float distance = m_paths[nodeFrom].origin.distance (m_paths[nodeTo].origin); if (dir == PathConnection::Outgoing) { addPath (nodeFrom, nodeTo, distance); @@ -1787,7 +1796,7 @@ bool BotGraph::loadGraphData () { loadPractice (); if (exten.mapSize > 0) { - int mapSize = engfuncs.pfnGetFileSize (strings.format ("maps/%s.bsp", game.getMapName ())); + int mapSize = getBspSize (); if (mapSize != exten.mapSize) { ctrl.msg ("Warning: Graph data is probably not for this map. Please check bots behaviour."); @@ -1829,7 +1838,7 @@ bool BotGraph::saveGraphData () { ExtenHeader exten {}; strings.copy (exten.author, author.chars (), cr::bufsize (exten.author)); - exten.mapSize = engfuncs.pfnGetFileSize (strings.format ("maps/%s.bsp", game.getMapName ())); + exten.mapSize = getBspSize (); // ensure narrow places saved into file m_narrowChecked = false; @@ -1883,13 +1892,13 @@ const char *BotGraph::getOldFormatGraphName (bool isMemoryFile) { float BotGraph::calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin) { // this function returns 2D traveltime to a position - return (origin - src).length2d () / maxSpeed; + return origin.distance2d (src) / maxSpeed; } bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) { TraceResult tr {}; - float distance = (destination - src).length (); + float distance = destination.distance (src); // is the destination not close enough? if (distance > m_autoPathDistance) { @@ -1945,7 +1954,7 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) { game.testLine (check, down, TraceIgnore::Monsters, m_editor, &tr); float lastHeight = tr.flFraction * 1000.0f; // height from ground - distance = (destination - check).length (); // distance from goal + distance = destination.distance (check); // distance from goal while (distance > 10.0f) { // move 10 units closer to the goal... @@ -1963,7 +1972,7 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) { return false; // can't get there without jumping... } lastHeight = height; - distance = (destination - check).length (); // distance from goal + distance = destination.distance (check); // distance from goal } return true; } @@ -2115,7 +2124,7 @@ void BotGraph::frame () { if (m_jumpLearnNode) { if (!m_endJumpPoint) { if (m_editor->v.button & IN_JUMP) { - add (9); + add (NodeAddFlag::JumpStart); m_timeJumpStarted = game.time (); m_endJumpPoint = true; @@ -2126,7 +2135,7 @@ void BotGraph::frame () { } } else if (((m_editor->v.flags & FL_ONGROUND) || m_editor->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1f < game.time () && m_endJumpPoint) { - add (10); + add (NodeAddFlag::JumpEnd); m_jumpLearnNode = false; m_endJumpPoint = false; @@ -2136,13 +2145,13 @@ void BotGraph::frame () { // check if it's a auto-add-node mode enabled if (hasEditFlag (GraphEdit::Auto) && (m_editor->v.flags & (FL_ONGROUND | FL_PARTIALGROUND))) { // find the distance from the last used node - float distance = (m_lastNode - m_editor->v.origin).lengthSq (); + float distance = m_lastNode.distanceSq (m_editor->v.origin); if (distance > cr::square (128.0f)) { // check that no other reachable nodes are nearby... for (const auto &path : m_paths) { if (isNodeReacheable (m_editor->v.origin, path.origin)) { - distance = (path.origin - m_editor->v.origin).lengthSq (); + distance = path.origin.distanceSq (m_editor->v.origin); if (distance < nearestDistance) { nearestDistance = distance; @@ -2152,7 +2161,7 @@ void BotGraph::frame () { // make sure nearest node is far enough away... if (nearestDistance >= cr::square (128.0f)) { - add (GraphAdd::Normal); // place a node here + add (NodeAddFlag::Normal); // place a node here } } } @@ -2163,7 +2172,7 @@ void BotGraph::frame () { // now iterate through all nodes in a map, and draw required ones for (auto &path : m_paths) { - float distance = (path.origin - m_editor->v.origin).length (); + float distance = path.origin.distance (m_editor->v.origin); // check if node is whitin a distance, and is visible if (distance < 512.0f && ((util.isVisible (path.origin, m_editor) && util.isInViewCone (path.origin, m_editor)) || !util.isAlive (m_editor) || distance < 128.0f)) { @@ -2705,7 +2714,7 @@ void BotGraph::addBasic () { do { if (getNearestNoBuckets (point, 50.0f) == kInvalidNodeIndex) { - add (3, point); + add (NodeAddFlag::NoHostage, point); } point.z += 160; } while (point.z < down.z - 40.0f); @@ -2713,7 +2722,7 @@ void BotGraph::addBasic () { point = down + Vector (0.0f, 0.0f, 38.0f); if (getNearestNoBuckets (point, 50.0f) == kInvalidNodeIndex) { - add (3, point); + add (NodeAddFlag::NoHostage, point); } m_isOnLadder = false; @@ -2731,31 +2740,30 @@ void BotGraph::addBasic () { }); }; - autoCreateForEntity (0, "info_player_deathmatch"); // then terrortist spawnpoints - autoCreateForEntity (0, "info_player_start"); // then add ct spawnpoints - autoCreateForEntity (0, "info_vip_start"); // then vip spawnpoint - autoCreateForEntity (0, "armoury_entity"); // weapons on the map ? + autoCreateForEntity (NodeAddFlag::Normal, "info_player_deathmatch"); // then terrortist spawnpoints + autoCreateForEntity (NodeAddFlag::Normal, "info_player_start"); // then add ct spawnpoints + autoCreateForEntity (NodeAddFlag::Normal, "info_vip_start"); // then vip spawnpoint + autoCreateForEntity (NodeAddFlag::Normal, "armoury_entity"); // weapons on the map ? - autoCreateForEntity (4, "func_hostage_rescue"); // hostage rescue zone - autoCreateForEntity (4, "info_hostage_rescue"); // hostage rescue zone (same as above) + autoCreateForEntity (NodeAddFlag::Rescue, "func_hostage_rescue"); // hostage rescue zone + autoCreateForEntity (NodeAddFlag::Rescue, "info_hostage_rescue"); // hostage rescue zone (same as above) - autoCreateForEntity (100, "func_bomb_target"); // bombspot zone - autoCreateForEntity (100, "info_bomb_target"); // bombspot zone (same as above) - autoCreateForEntity (100, "hostage_entity"); // hostage entities - autoCreateForEntity (100, "func_vip_safetyzone"); // vip rescue (safety) zone - autoCreateForEntity (100, "func_escapezone"); // terrorist escape zone + autoCreateForEntity (NodeAddFlag::Goal, "func_bomb_target"); // bombspot zone + autoCreateForEntity (NodeAddFlag::Goal, "info_bomb_target"); // bombspot zone (same as above) + autoCreateForEntity (NodeAddFlag::Goal, "hostage_entity"); // hostage entities + autoCreateForEntity (NodeAddFlag::Goal, "func_vip_safetyzone"); // vip rescue (safety) zone + autoCreateForEntity (NodeAddFlag::Goal, "func_escapezone"); // terrorist escape zone } void BotGraph::eraseFromDisk () { // this function removes graph file from the hard disk StringArray forErase; + bots.kickEveryone (true); auto map = game.getMapName (); auto data = getDataDirectory (); - bots.kickEveryone (true); - // if we're delete graph, delete all corresponding to it files forErase.push (strings.format ("%spwf/%s.pwf", data, map)); // graph itself forErase.push (strings.format ("%strain/%s.prc", data, map)); // corresponding to practice diff --git a/src/manager.cpp b/src/manager.cpp index 569f5d5..c5aeaec 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -12,7 +12,7 @@ ConVar cv_autovacate ("yb_autovacate", "1", "Kick bots to automatically make roo ConVar cv_quota ("yb_quota", "9", "Specifies the number bots to be added to the game.", true, 0.0f, static_cast (kGameMaxPlayers)); ConVar cv_quota_mode ("yb_quota_mode", "normal", "Specifies the type of quota.\nAllowed values: 'normal', 'fill', and 'match'.\nIf 'fill', the server will adjust bots to keep N players in the game, where N is yb_quota.\nIf 'match', the server will maintain a 1:N ratio of humans to bots, where N is yb_quota_match.", false); ConVar cv_quota_match ("yb_quota_match", "0", "Number of players to match if yb_quota_mode set to 'match'", true, 0.0f, static_cast (kGameMaxPlayers)); -ConVar cv_think_fps ("yb_think_fps", "30.0", "Specifies how many times per second bot code will run.", true, 30.0f, 90.0f); +ConVar cv_think_fps ("yb_think_fps", "26.0", "Specifies how many times per second bot code will run.", true, 24.0f, 90.0f); ConVar cv_autokill_delay ("yb_autokill_delay", "0.0", "Specifies amount of time in seconds when bots will be killed if no humans left alive.", true, 0.0f, 90.0f); ConVar cv_join_after_player ("yb_join_after_player", "0", "Specifies whether bots should join server, only when at least one human player in game."); diff --git a/src/navigate.cpp b/src/navigate.cpp index 5bc8457..80b46ee 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -8,7 +8,10 @@ #include ConVar cv_whose_your_daddy ("yb_whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots."); -ConVar cv_debug_heuristic_type ("yb_debug_heuristic_type", "0", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f); +ConVar cv_path_heuristic_mode ("yb_path_heuristic_mode", "4", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f); + +ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bound of danger factor that used to add additional danger to path based on practice.", true, 100.0f, 2400.0f); +ConVar cv_path_danger_factor_max ("yb_path_danger_factor_max", "400", "Upper bound of danger factor that used to add additional danger to path based on practice.", true, 200.0f, 4800.0f); int Bot::findBestGoal () { @@ -180,7 +183,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) { int count = 0; for (auto &point : graph.m_goalPoints) { - float distance = (graph[point].origin - pev->origin).lengthSq (); + float distance = graph[point].origin.distanceSq (pev->origin); if (distance > cr::square (1024.0f)) { continue; @@ -295,6 +298,10 @@ bool Bot::doPlayerAvoidance (const Vector &normal) { edict_t *hindrance = nullptr; float distance = cr::square (300.0f); + if (getCurrentTaskId () == Task::Attack || getCurrentTaskId () == Task::SeekCover || isOnLadder ()) { + return false; + } + // find nearest player to bot for (const auto &client : util.getClients ()) { @@ -312,7 +319,7 @@ bool Bot::doPlayerAvoidance (const Vector &normal) { if (client.team != m_team || client.ent == ent ()) { continue; } - auto nearest = (client.ent->v.origin - pev->origin).lengthSq (); + auto nearest = client.ent->v.origin.distanceSq (pev->origin); if (nearest < cr::square (pev->maxspeed) && nearest < distance) { hindrance = client.ent; @@ -324,7 +331,7 @@ bool Bot::doPlayerAvoidance (const Vector &normal) { if (!hindrance) { return false; } - const float interval = getFrameInterval (); + const float interval = getFrameInterval () * 4.0f; // use our movement angles, try to predict where we should be next frame Vector right, forward; @@ -335,8 +342,8 @@ bool Bot::doPlayerAvoidance (const Vector &normal) { predict += right * m_strafeSpeed * interval; predict += pev->velocity * interval; - auto movedDistance = (hindrance->v.origin - predict).lengthSq (); - auto nextFrameDistance = ((hindrance->v.origin + hindrance->v.velocity * interval) - pev->origin).lengthSq (); + auto movedDistance = hindrance->v.origin.distanceSq (predict); + auto nextFrameDistance = pev->origin.distanceSq (hindrance->v.origin + hindrance->v.velocity * interval); // is player that near now or in future that we need to steer away? if (movedDistance <= cr::square (48.0f) || (distance <= cr::square (56.0f) && nextFrameDistance < distance)) { @@ -349,13 +356,11 @@ bool Bot::doPlayerAvoidance (const Vector &normal) { else { setStrafeSpeed (normal, -pev->maxspeed); } - -#if 0 if (distance < cr::square (56.0f)) { - if ((dir | forward.get2d ()) < 0.0f) + if ((dir | forward.get2d ()) < 0.0f) { m_moveSpeed = -pev->maxspeed; + } } -#endif return true; } return false; @@ -365,9 +370,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { // if avoiding someone do not consider stuck TraceResult tr {}; - doPlayerAvoidance (dirNormal); + m_isStuck = doPlayerAvoidance (dirNormal); - m_isStuck = false; + if (m_isStuck) { + resetCollision (); + return; + } // Standing still, no need to check? // FIXME: doesn't care for ladder movement (handled separately) should be included in some way @@ -655,7 +663,7 @@ bool Bot::updateNavigation () { } m_destOrigin = m_pathOrigin + pev->view_ofs; - float nodeDistance = (pev->origin - m_pathOrigin).length (); + float nodeDistance = pev->origin.distance (m_pathOrigin); // this node has additional travel flags - care about them if (m_currentTravelFlags & PathFlag::Jump) { @@ -714,7 +722,7 @@ bool Bot::updateNavigation () { if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) { // if the door is near enough... - if ((game.getEntityOrigin (tr.pHit) - pev->origin).lengthSq () < 2500.0f) { + if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) { ignoreCollision (); // don't consider being stuck if (rg.chance (50)) { @@ -795,7 +803,7 @@ bool Bot::updateNavigation () { } // needs precise placement - check if we get past the point - if (desiredDistance < 22.0f && nodeDistance < 30.0f && (pev->origin + (pev->velocity * getFrameInterval ()) - m_pathOrigin).lengthSq () >= cr::square (nodeDistance)) { + if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * getFrameInterval ()) >= cr::square (nodeDistance)) { desiredDistance = nodeDistance + 1.0f; } @@ -837,7 +845,7 @@ bool Bot::updateNavigation () { // bot within 'hearable' bomb tick noises? if (!bombOrigin.empty ()) { - float distance = (bombOrigin - graph[taskTarget].origin).length (); + float distance = bombOrigin.distance (graph[taskTarget].origin); if (distance > 512.0f) { if (rg.chance (50) && !graph.isVisited (taskTarget)) { @@ -930,7 +938,7 @@ bool Bot::updateLiftHandling () { m_destOrigin = m_liftTravelPos; // check if we enough to destination - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); // need to wait our following teammate ? @@ -984,7 +992,7 @@ bool Bot::updateLiftHandling () { if (needWaitForTeammate) { m_destOrigin = m_liftTravelPos; - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); } } @@ -1015,7 +1023,7 @@ bool Bot::updateLiftHandling () { m_liftState = LiftState::TravelingBy; m_liftUsageTime = game.time () + 14.0f; - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); } } @@ -1025,7 +1033,7 @@ bool Bot::updateLiftHandling () { if (m_liftState == LiftState::TravelingBy) { m_destOrigin = Vector (m_liftTravelPos.x, m_liftTravelPos.y, pev->origin.z); - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); } } @@ -1042,7 +1050,7 @@ bool Bot::updateLiftHandling () { m_destOrigin = pev->origin; } - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); } } @@ -1075,7 +1083,7 @@ bool Bot::updateLiftHandling () { m_destOrigin = button->v.origin; } - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); } } @@ -1106,7 +1114,7 @@ bool Bot::updateLiftHandling () { } } - if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { + if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) { wait (); } } @@ -1204,8 +1212,12 @@ void Bot::findShortestPath (int srcIndex, int destIndex) { void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath::Fast */) { // this function finds a path from srcIndex to destIndex + auto dangerFactor = [&] () -> float { + return rg.get (cv_path_danger_factor_min.float_ (), cv_path_danger_factor_max.float_ ()) * 2.0f / cr::clamp (m_difficulty, 1, 3); + }; + // least kills and number of nodes to goal for a team - auto gfunctionKillsDist = [] (int team, int currentIndex, int parentIndex) -> float { + auto gfunctionKillsDist = [&dangerFactor] (int team, int currentIndex, int parentIndex) -> float { if (parentIndex == kInvalidNodeIndex) { return 0.0f; } @@ -1221,7 +1233,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: if (current.flags & NodeFlag::Crouch) { cost *= 1.5f; } - return cost; + return cost + dangerFactor (); }; // least kills and number of nodes to goal for a team @@ -1238,7 +1250,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: }; // least kills to goal for a team - auto gfunctionKills = [] (int team, int currentIndex, int) -> float { + auto gfunctionKills = [&dangerFactor] (int team, int currentIndex, int) -> float { auto cost = static_cast (graph.getDangerDamage (team, currentIndex, currentIndex)); const auto ¤t = graph[currentIndex]; @@ -1251,7 +1263,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: if (current.flags & NodeFlag::Crouch) { cost *= 1.5f; } - return cost + 0.5f; + return cost + dangerFactor () + 1.0f; }; // least kills to goal for a team @@ -1310,7 +1322,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: float y = cr::abs (start.origin.y - goal.origin.y); float z = cr::abs (start.origin.z - goal.origin.z); - switch (cv_debug_heuristic_type.int_ ()) { + switch (cv_path_heuristic_mode.int_ ()) { case 0: default: return cr::max (cr::max (x, y), z); // chebyshev distance @@ -1326,7 +1338,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: // euclidean based distance float euclidean = cr::powf (cr::powf (x, 2.0f) + cr::powf (y, 2.0f) + cr::powf (z, 2.0f), 0.5f); - if (cv_debug_heuristic_type.int_ () == 4) { + if (cv_path_heuristic_mode.int_ () == 4) { return 1000.0f * (cr::ceilf (euclidean) - euclidean); } return euclidean; @@ -1343,7 +1355,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: // none heuristic auto hfunctionNone = [&hfunctionPathDist] (int index, int startIndex, int goalIndex) -> float { - return hfunctionPathDist (index, startIndex, goalIndex) / 128.0f * 10.0f; + return hfunctionPathDist (index, startIndex, goalIndex) / (128.0f * 10.0f); }; if (!graph.exists (srcIndex)) { @@ -1578,7 +1590,7 @@ bool Bot::findBestNearestNode () { } // if we're still here, find some close nodes - float distance = (pev->origin - graph[at].origin).lengthSq (); + float distance = pev->origin.distanceSq (graph[at].origin); if (distance < lessDist[0]) { lessDist[2] = lessDist[1]; @@ -1650,7 +1662,7 @@ float Bot::getReachTime () { // calculate 'real' time that we need to get from one node to another if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) { - float distance = (graph[m_previousNodes[0]].origin - graph[m_currentNodeIndex].origin).length (); + float distance = graph[m_previousNodes[0]].origin.distance (graph[m_currentNodeIndex].origin); // caclulate estimated time if (pev->maxspeed <= 0.0f) { @@ -1735,7 +1747,7 @@ int Bot::findNearestNode () { if (at == m_currentNodeIndex) { continue; } - float distance = (graph[at].origin - pev->origin).lengthSq (); + float distance = graph[at].origin.distanceSq (pev->origin); if (distance < minimum) { @@ -1763,7 +1775,7 @@ int Bot::findBombNode () { const auto &audible = isBombAudible (); // take the nearest to bomb nodes instead of goal if close enough - if ((pev->origin - bomb).lengthSq () < cr::square (96.0f)) { + if (pev->origin.distanceSq (bomb) < cr::square (96.0f)) { int node = graph.getNearest (bomb, 420.0f); m_bombSearchOverridden = true; @@ -1786,7 +1798,7 @@ int Bot::findBombNode () { // find nearest goal node either to bomb (if "heard" or player) for (auto &point : goals) { - float distance = (graph[point].origin - bomb).lengthSq (); + float distance = bomb.distanceSq (graph[point].origin); // check if we got more close distance if (distance < lastDistance) { @@ -1888,7 +1900,7 @@ int Bot::findDefendNode (const Vector &origin) { IntArray found; for (int i = 0; i < graph.length (); ++i) { - if ((graph[i].origin - origin).lengthSq () <= cr::square (1248.0f) && !graph.isVisible (i, posIndex) && !isOccupiedNode (i)) { + if (origin.distanceSq (graph[i].origin) <= cr::square (1248.0f) && !graph.isVisible (i, posIndex) && !isOccupiedNode (i)) { found.push (i); } } @@ -1911,7 +1923,7 @@ int Bot::findDefendNode (const Vector &origin) { int Bot::findCoverNode (float maxDistance) { // this function tries to find a good cover node if bot wants to hide - const float enemyMax = (m_lastEnemyOrigin - pev->origin).length (); + const float enemyMax = m_lastEnemyOrigin.distance (pev->origin); // do not move to a position near to the enemy if (maxDistance > enemyMax) { @@ -2172,7 +2184,7 @@ bool Bot::advanceMovement () { src = path.origin; dst = next.origin; - jumpDistance = (path.origin - next.origin).length (); + jumpDistance = path.origin.distance (next.origin); willJump = true; break; @@ -2661,7 +2673,7 @@ bool Bot::isDeadlyMove (const Vector &to) { } float lastHeight = tr.flFraction * 1000.0f; // height from ground - float distance = (to - check).length (); // distance from goal + float distance = to.distance (check); // distance from goal if (distance <= 30.0f && lastHeight > 150.0f) { return true; @@ -2686,7 +2698,7 @@ bool Bot::isDeadlyMove (const Vector &to) { return true; } lastHeight = height; - distance = (to - check).length (); // distance from goal + distance = to.distance (check); // distance from goal } return false; } @@ -2774,13 +2786,13 @@ int Bot::findCampingDirection () { if (count < 3) { indices[count] = i; - distTab[count] = (pev->origin - path.origin).lengthSq (); + distTab[count] = pev->origin.distanceSq (path.origin); visibility[count] = path.vis.crouch + path.vis.stand; ++count; } else { - float distance = (pev->origin - path.origin).lengthSq (); + float distance = pev->origin.distanceSq (path.origin); uint16 visBits = path.vis.crouch + path.vis.stand; for (int j = 0; j < 3; ++j) { @@ -3005,14 +3017,14 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) { } // do not check clients far away from us - if ((pev->origin - client.origin).lengthSq () > cr::square (320.0f)) { + if (pev->origin.distanceSq (client.origin) > cr::square (320.0f)) { continue; } if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) { continue; } - auto length = (graph[index].origin - client.origin).lengthSq (); + auto length = client.origin.distanceSq (graph[index].origin); if (length < cr::clamp (cr::square (graph[index].radius), cr::square (60.0f), cr::square (90.0f))) { return true; @@ -3049,7 +3061,7 @@ edict_t *Bot::lookupButton (const char *target) { // check if this place safe if (!isDeadlyMove (pos)) { - float distance = (pev->origin - pos).lengthSq (); + float distance = pev->origin.distanceSq (pos); // check if we got more close button if (distance <= nearest) { @@ -3073,7 +3085,7 @@ bool Bot::isReachableNode (int index) { const Vector &dst = graph[index].origin; // is the destination close enough? - if ((dst - src).lengthSq () >= cr::square (320.0f)) { + if (dst.distanceSq (src) >= cr::square (320.0f)) { return false; } @@ -3081,7 +3093,7 @@ bool Bot::isReachableNode (int index) { if (isOccupiedNode (index, true)) { return true; } - float ladderDist = (dst - src).length2d (); + float ladderDist = dst.distance2d (src); TraceResult tr {}; game.testLine (src, dst, TraceIgnore::Monsters, ent (), &tr); @@ -3112,6 +3124,9 @@ bool Bot::isReachableNode (int index) { } bool Bot::isBannedNode (int index) { + if (graph.exists (cv_debug_goal.int_ ())) { + return false; + } for (const auto &node : m_goalHistory) { if (node == index) { return true; diff --git a/src/support.cpp b/src/support.cpp index 003be9b..4c69e9d 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -347,7 +347,7 @@ bool BotSupport::findNearestPlayer (void **pvHolder, edict_t *to, float searchDi if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & ClientFlags::Alive)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & Weapon::C4))) { continue; // filter players with parameters } - float distance = (client.ent->v.origin - to->v.origin).length (); + float distance = client.ent->v.origin.distance (to->v.origin); if (distance < nearestPlayer && distance < searchDistance) { nearestPlayer = distance; @@ -398,7 +398,7 @@ void BotSupport::listenNoise (edict_t *ent, StringRef sample, float volume) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) { continue; } - auto distance = (client.origin - origin).lengthSq (); + auto distance = client.origin.distanceSq (origin); // now find nearest player if (distance < nearest) {