diff --git a/inc/chatlib.h b/inc/chatlib.h index 63c5c33..8a2ac7a 100644 --- a/inc/chatlib.h +++ b/inc/chatlib.h @@ -9,9 +9,9 @@ // links keywords and replies together struct ChatKeywords { - StringArray keywords; - StringArray replies; - StringArray usedReplies; + StringArray keywords {}; + StringArray replies {}; + StringArray usedReplies {}; public: ChatKeywords () = default; diff --git a/inc/config.h b/inc/config.h index e9847bd..c442268 100644 --- a/inc/config.h +++ b/inc/config.h @@ -9,7 +9,7 @@ // botname structure definition struct BotName { - String name; + String name {}; int usedBy = -1; public: @@ -19,9 +19,9 @@ public: // voice config structure definition struct ChatterItem { - String name; - float repeat; - float duration; + String name {}; + float repeat {}; + float duration {}; public: ChatterItem (StringRef name, float repeat, float duration) : name (name), repeat (repeat), duration (duration) {} diff --git a/inc/control.h b/inc/control.h index b70b496..c27da39 100644 --- a/inc/control.h +++ b/inc/control.h @@ -29,7 +29,7 @@ public: public: // generic bot command struct BotCmd { - String name, format, help; + String name {}, format {}, help {}; Handler handler = nullptr; bool visible = true; @@ -42,9 +42,9 @@ public: // single bot menu struct BotMenu { - int ident, slots; - String text; - MenuHandler handler; + int ident {}, slots {}; + String text {}; + MenuHandler handler {}; public: explicit BotMenu (int ident, int slots, StringRef text, MenuHandler handler) : ident (ident), slots (slots), text (text), handler (cr::move (handler)) @@ -54,7 +54,7 @@ public: // queued text message to prevent overflow with rapid output struct PrintQueue { int32_t destination {}; - String text; + String text {}; public: explicit PrintQueue () = default; @@ -68,7 +68,7 @@ public: float timelimit {}; float freezetime {}; float roundtime {}; - } m_graphSaveVarValues; + } m_graphSaveVarValues {}; private: StringArray m_args {}; diff --git a/inc/engine.h b/inc/engine.h index ff80462..e931cf8 100644 --- a/inc/engine.h +++ b/inc/engine.h @@ -85,16 +85,16 @@ CR_DECLARE_SCOPED_ENUM (PlayerPart, // variable reg pair struct ConVarReg { - cvar_t reg; - String info; - String init; - String regval; - String name; - class ConVar *self; - float initial, min, max; - bool missing; - bool bounded; - int32_t type; + cvar_t reg {}; + String info {}; + String init {}; + String regval {}; + String name {}; + class ConVar *self {}; + float initial {}, min {}, max {}; + bool missing {}; + bool bounded {}; + int32_t type {}; }; // entity prototype diff --git a/inc/graph.h b/inc/graph.h index 302e163..117f9ef 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -91,11 +91,11 @@ CR_DECLARE_SCOPED_ENUM (NotifySound, // general waypoint header information structure for podbot struct PODGraphHeader { - char header[8]; - int32_t fileVersion; - int32_t pointNumber; - char mapName[32]; - char author[32]; + char header[8] {}; + int32_t fileVersion {}; + int32_t pointNumber {}; + char mapName[32] {}; + char author[32] {}; }; // defines linked nodes @@ -117,31 +117,31 @@ struct Path { // define waypoint structure for podbot (will convert on load) struct PODPath { - int32_t number, flags; - Vector origin; - float radius, csx, csy, cex, cey; - int16_t index[kMaxNodeLinks]; - uint16_t conflags[kMaxNodeLinks]; - Vector velocity[kMaxNodeLinks]; - int32_t distance[kMaxNodeLinks]; - PathVis vis; + int32_t number {}, flags {}; + Vector origin {}; + float radius {}, csx {}, csy {}, cex {}, cey {}; + int16_t index[kMaxNodeLinks] {}; + uint16_t conflags[kMaxNodeLinks] {}; + Vector velocity[kMaxNodeLinks] {}; + int32_t distance[kMaxNodeLinks] {}; + PathVis vis {}; }; // general storage header information structure struct StorageHeader { - int32_t magic; - int32_t version; - int32_t options; - int32_t length; - int32_t compressed; - int32_t uncompressed; + int32_t magic {}; + int32_t version {}; + int32_t options {}; + int32_t length {}; + int32_t compressed {}; + int32_t uncompressed {}; }; // extension header for graph information struct ExtenHeader { - char author[32]; // original author of graph - int32_t mapSize; // bsp size for checksumming map consistency - char modified[32]; // by whom modified + char author[32] {}; // original author of graph + int32_t mapSize {}; // bsp size for checksumming map consistency + char modified[32] {}; // by whom modified }; // graph operation class diff --git a/inc/hooks.h b/inc/hooks.h index 89d555f..fb76a18 100644 --- a/inc/hooks.h +++ b/inc/hooks.h @@ -87,7 +87,7 @@ private: using SendToProto = decltype (sendto); private: - Detour m_sendToDetour { }, m_sendToDetourSys {}; + Detour m_sendToDetour {}, m_sendToDetourSys {}; public: ServerQueryHook () = default; @@ -126,11 +126,11 @@ private: private: bool m_paused { false }; - Detour m_dlsym; - Detour m_dlclose; - HashMap m_exports; + Detour m_dlsym {}; + Detour m_dlclose {}; + HashMap m_exports {}; - SharedLibrary m_self; + SharedLibrary m_self {}; public: DynamicLinkerHook () = default; diff --git a/inc/manager.h b/inc/manager.h index 8512744..c1f026d 100644 --- a/inc/manager.h +++ b/inc/manager.h @@ -9,12 +9,12 @@ // bot creation tab struct BotRequest { - bool manual; - int difficulty; - int team; - int skin; - int personality; - String name; + bool manual {}; + int difficulty {}; + int team {}; + int skin {}; + int personality {}; + String name {}; }; // manager class diff --git a/inc/planner.h b/inc/planner.h index 1dd2d62..9d7f889 100644 --- a/inc/planner.h +++ b/inc/planner.h @@ -104,8 +104,8 @@ private: int m_length {}; - Array m_constructedPath; - Array m_smoothedPath; + Array m_constructedPath {}; + Array m_smoothedPath {}; private: // clears the currently built route diff --git a/inc/vision.h b/inc/vision.h index 6307c8a..166aeec 100644 --- a/inc/vision.h +++ b/inc/vision.h @@ -36,10 +36,10 @@ public: static constexpr float kMinViewDistance = 2.0f; private: - float m_farHeight; // height of the far frustum - float m_farWidth; // width of the far frustum - float m_nearHeight; // height of the near frustum - float m_nearWidth; // width of the near frustum + float m_farHeight {}; // height of the far frustum + float m_farWidth {}; // width of the far frustum + float m_nearHeight {}; // height of the near frustum + float m_nearWidth {}; // width of the near frustum public: explicit Frustum () { diff --git a/inc/vistable.h b/inc/vistable.h index ca00df1..975748f 100644 --- a/inc/vistable.h +++ b/inc/vistable.h @@ -17,7 +17,7 @@ CR_DECLARE_SCOPED_ENUM_TYPE (VisIndex, int32_t, // defines visibility count struct PathVis { - uint16_t stand, crouch; + uint16_t stand {}, crouch {}; }; class GraphVistable final : public Singleton { diff --git a/inc/yapb.h b/inc/yapb.h index 9a365ea..f0038c2 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -26,12 +26,12 @@ struct BotTask { using Function = void (Bot::*) (); public: - Function func; // corresponding exec function in bot class - Task id; // major task/action carried out - float desire; // desire (filled in) for this task - int data; // additional data (node index) - float time; // time task expires - bool resume; // if task can be continued if interrupted + Function func {}; // corresponding exec function in bot class + Task id {}; // major task/action carried out + float desire {}; // desire (filled in) for this task + int data {}; // additional data (node index) + float time {}; // time task expires + bool resume {}; // if task can be continued if interrupted public: BotTask (Function func, Task id, float desire, int data, float time, bool resume) : func (func), id (id), desire (desire), data (data), time (time), resume (resume) { } @@ -50,21 +50,21 @@ struct WeaponProp { // weapon info structure struct WeaponInfo { - int id; // the weapon id value - StringRef name; // name of the weapon when selecting it - StringRef model; // model name to separate cs weapons - int price; // price when buying - int minPrimaryAmmo; // minimum primary ammo - int teamStandard; // used by team (number) (standard map) - int teamAS; // used by team (as map) - int buyGroup; // group in buy menu (standard map) - int buySelect; // select item in buy menu (standard map) - int buySelectT; // for counter-strike v1.6 - int buySelectCT; // for counter-strike v1.6 - int penetratePower; // penetrate power - int maxClip; // max ammo in clip - int type; // weapon class - bool primaryFireHold; // hold down primary fire button to use? + int id {}; // the weapon id value + StringRef name {}; // name of the weapon when selecting it + StringRef model {}; // model name to separate cs weapons + int price {}; // price when buying + int minPrimaryAmmo {}; // minimum primary ammo + int teamStandard {}; // used by team (number) (standard map) + int teamAS {}; // used by team (as map) + int buyGroup {}; // group in buy menu (standard map) + int buySelect {}; // select item in buy menu (standard map) + int buySelectT {}; // for counter-strike v1.6 + int buySelectCT {}; // for counter-strike v1.6 + int penetratePower {}; // penetrate power + int maxClip {}; // max ammo in clip + int type {}; // weapon class + bool primaryFireHold {}; // hold down primary fire button to use? public: WeaponInfo (int id, @@ -227,7 +227,7 @@ private: int m_radioSelect {}; // radio entry int m_killsCount {}; // the kills count of a bot - int m_lastPredictIndex { kInvalidNodeIndex }; // last predicted path index + int m_lastPredictIndex {}; // last predicted path index int m_lastPredictLength {}; // last predicted path length int m_pickupType {}; // type of entity which needs to be used/picked up @@ -452,7 +452,7 @@ private: bool isEnemyInSight (Vector &endPos); bool isEnemyNoticeable (float range); bool isCreature (); - bool isOnLadderPath (); + bool isPreviousLadder (); bool isIgnoredItem (edict_t *ent); void doPlayerAvoidance (const Vector &normal); diff --git a/src/botlib.cpp b/src/botlib.cpp index f3328b7..590f2bd 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -97,7 +97,6 @@ void Bot::avoidGrenades () { auto model = pent->v.model.str (9); if (m_preventFlashing < game.time () - && m_personality == Personality::Rusher && cv_whose_your_daddy && model == kFlashbangModelName) { @@ -209,15 +208,15 @@ void Bot::checkBreakablesAround () { } const auto &origin = game.getEntityOrigin (breakable); - const auto lengthToObstacleSq = origin.distanceSq (pev->origin); + const auto distanceToObstacleSq = origin.distanceSq (pev->origin); // too far, skip it - if (lengthToObstacleSq > cr::sqrf (radius)) { + if (distanceToObstacleSq > cr::sqrf (radius)) { continue; } // too close, skip it - if (lengthToObstacleSq < cr::sqrf (100.0f)) { + if (distanceToObstacleSq < cr::sqrf (100.0f)) { continue; } @@ -343,7 +342,7 @@ void Bot::updatePickups () { } const auto &interesting = bots.getInterestingEntities (); - const float radius = cr::sqrf (cv_object_pickup_radius.as ()); + const float radiusSq = cr::sqrf (cv_object_pickup_radius.as ()); if (!game.isNullEntity (m_pickupItem)) { bool itemExists = false; @@ -358,7 +357,7 @@ void Bot::updatePickups () { const Vector &origin = game.getEntityOrigin (ent); // too far from us ? - if (pev->origin.distanceSq (origin) > radius) { + if (pev->origin.distanceSq (origin) > radiusSq) { continue; } @@ -397,7 +396,7 @@ void Bot::updatePickups () { } // too far from us ? - if (pev->origin.distanceSq (origin) > radius) { + if (pev->origin.distanceSq (origin) > radiusSq) { continue; } @@ -1705,6 +1704,7 @@ void Bot::overrideConditions () { // special handling for sniping if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) && m_shootTime - 0.4f <= game.time () + && m_shootTime + 0.1f > game.time () && m_sniperStopTime > game.time ()) { ignoreCollision (); @@ -1751,7 +1751,7 @@ void Bot::syncUpdatePredictedIndex () { } ScopedUnlock unlock (m_predictLock); - const auto lastEnemyOrigin = m_lastEnemyOrigin; + const auto &lastEnemyOrigin = m_lastEnemyOrigin; const auto currentNodeIndex = m_currentNodeIndex; const auto &botOrigin = pev->origin; @@ -1760,8 +1760,8 @@ void Bot::syncUpdatePredictedIndex () { return; } - int destIndex = graph.getNearest (lastEnemyOrigin); - int bestIndex = kInvalidNodeIndex; + const int destIndex = graph.getNearest (lastEnemyOrigin); + int bestIndex = m_currentNodeIndex; if (destIndex == kInvalidNodeIndex) { wipePredict (); @@ -1769,7 +1769,7 @@ void Bot::syncUpdatePredictedIndex () { } int pathLength = 0; - auto result = planner.find (destIndex, currentNodeIndex, [&] (int index) { + planner.find (destIndex, currentNodeIndex, [&] (int index) { ++pathLength; if (vistab.visible (currentNodeIndex, index) && botOrigin.distanceSq (graph[index].origin) < cr::sqrf (2048.0f)) { @@ -1779,13 +1779,12 @@ void Bot::syncUpdatePredictedIndex () { return true; }); - if (result && bestIndex != kInvalidNodeIndex) { + if (bestIndex != currentNodeIndex) { m_lastPredictIndex = bestIndex; m_lastPredictLength = pathLength; return; } - wipePredict (); } void Bot::updatePredictedIndex () { @@ -1947,7 +1946,7 @@ void Bot::setConditions () { if (!m_lastEnemyOrigin.empty ()) { const auto distanceSq = pev->origin.distanceSq (m_lastEnemyOrigin); - if (distanceSq > cr::sqrf (2048.0f) || (game.isNullEntity (m_enemy) && m_seeEnemyTime + 10.0f < game.time ())) { + if (distanceSq >= cr::sqrf (2048.0f) || (game.isNullEntity (m_enemy) && m_seeEnemyTime + 10.0f < game.time ())) { m_lastEnemyOrigin.clear (); m_lastEnemy = nullptr; @@ -2057,9 +2056,6 @@ void Bot::filterTasks () { else if (m_isVIP || m_isReloading || (sniping && usesSniper ())) { ratio *= 3.0f; // triple the seek cover desire if bot is VIP or reloading } - else if (m_lastEnemyOrigin.distanceSq2d (pev->origin) < cr::sqrf (256.0f)) { - ratio *= 3.0f; - } else if (game.is (GameFlags::CSDM)) { ratio = 0.0f; } diff --git a/src/combat.cpp b/src/combat.cpp index b8affd6..1378e55 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -1433,7 +1433,7 @@ void Bot::attackMovement () { if ((m_states & Sense::SeeingEnemy) && approach < 30 && !bots.isBombPlanted () - && (isInViewCone (m_enemy->v.origin) || m_isVIP)) { + && (isInViewCone (m_enemy->v.origin) || m_isVIP || m_isReloading)) { if (m_retreatTime < game.time ()) { startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true); @@ -1453,7 +1453,10 @@ void Bot::attackMovement () { const bool isFullView = !!(m_enemyParts & (Visibility::Head | Visibility::Body)); if (m_lastFightStyleCheck < game.time ()) { - if (usesSniper ()) { + if (usesSniper () + && m_shootTime - 0.4f <= game.time () + && m_shootTime + 0.1f > game.time () + && m_sniperStopTime > game.time ()) { m_fightStyle = Fight::Stay; } else if (usesRifle () || usesSubmachine () || usesHeavy ()) { diff --git a/src/manager.cpp b/src/manager.cpp index 0649703..00c52d9 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -1518,6 +1518,8 @@ void Bot::newRound () { m_trackingEdict = nullptr; m_enemyBodyPartSet = nullptr; m_timeNextTracking = 0.0f; + m_lastPredictIndex = kInvalidNodeIndex; + m_lastPredictLength = kInfiniteDistanceLong; m_buttonPushTime = 0.0f; m_enemyUpdateTime = 0.0f; diff --git a/src/navigate.cpp b/src/navigate.cpp index 389cb77..6e03ec8 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -573,8 +573,14 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { } // not stuck yet else { + bool isOnLadderPath = false; + + if ((m_pathFlags & NodeFlag::Ladder) && isPreviousLadder ()) { + isOnLadderPath = true; + } + // test if there's something ahead blocking the way - if (!isOnLadderPath () && !isOnLadder () && isBlockedForward (dirNormal, &tr)) { + if (!isOnLadderPath && !isOnLadder () && isBlockedForward (dirNormal, &tr)) { if (cr::fzero (m_firstCollideTime)) { m_firstCollideTime = game.time () + 0.2f; } @@ -874,10 +880,10 @@ void Bot::checkFall () { else if (pev->origin.z + 128.0f < m_checkFallPoint[1].z && pev->origin.z + 128.0f < m_checkFallPoint[0].z) { fixFall = true; } - else if (m_currentNodeIndex != kInvalidNodeIndex) { - if (pev->origin.distanceSq (m_checkFallPoint[1]) <= cr::sqrf (32.0f) && pev->origin.z + 64.0f < m_checkFallPoint[1].z) { - fixFall = true; - } + else if (m_currentNodeIndex != kInvalidNodeIndex + && nowDistanceSq <= cr::sqrf (32.0f) + && pev->origin.z + 64.0f < m_checkFallPoint[1].z) { + fixFall = true; } if (fixFall) { @@ -1052,27 +1058,15 @@ bool Bot::updateNavigation () { const auto prevNodeIndex = m_previousNodes[0]; const float ladderDistance = pev->origin.distance (m_pathOrigin); - if (graph.exists (prevNodeIndex) && !(graph[prevNodeIndex].flags & NodeFlag::Ladder)) { + // do a precise movement when very near + if (graph.exists (prevNodeIndex) && !(graph[prevNodeIndex].flags & NodeFlag::Ladder) && ladderDistance < 64.0f) { if (m_pathOrigin.z >= pev->origin.z + 16.0f) { m_pathOrigin = m_path->origin + kLadderOffset; } else if (m_pathOrigin.z < pev->origin.z - 16.0f) { m_pathOrigin = m_path->origin - kLadderOffset; } - } - else if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) { - m_moveSpeed = ladderDistance; - if (m_moveSpeed < 150.0f) { - m_moveSpeed = 150.0f; - } - else if (m_moveSpeed > pev->maxspeed) { - m_moveSpeed = pev->maxspeed; - } - } - - // do a precise movement when very near - if (graph.exists (prevNodeIndex) && !(graph[prevNodeIndex].flags & NodeFlag::Ladder) && ladderDistance < 64.0f) { if (!isDucking ()) { m_moveSpeed = pev->maxspeed * 0.4f; } @@ -1084,36 +1078,41 @@ bool Bot::updateNavigation () { m_approachingLadderTimer.start (m_frameInterval * 4.0f); } + if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) { + m_moveSpeed = ladderDistance; + + if (m_moveSpeed < 150.0f) { + m_moveSpeed = 150.0f; + } + else if (m_moveSpeed > pev->maxspeed) { + m_moveSpeed = pev->maxspeed; + } + } + // special detection if someone is using the ladder (to prevent to have bots-towers on ladders) for (const auto &client : util.getClients ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team - || client.ent == ent ()) { + || client.ent == ent () + || !(client.ent->v.movetype == MOVETYPE_FLY)) { continue; } - if (client.ent->v.origin.distanceSq (m_pathOrigin) > cr::sqrf (64.0f)) { - continue; - } bool foundGround = false; - int previousNode = 0; - // someone is above or below us and is using the ladder already - if (cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) { - const auto numPreviousNode = rg (0, 2); - - for (int i = 0; i < numPreviousNode; ++i) { - if (graph.exists (prevNodeIndex) && (graph[prevNodeIndex].flags & NodeFlag::Ladder)) { + if (cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f) { + if (isPreviousLadder ()) { + if (client.ent->v.origin.distanceSq (pev->origin) < cr::sqrf (64.0f)) { foundGround = true; - previousNode = m_previousNodes[i]; - break; } } - if (foundGround) { - findPath (m_previousNodes[0], previousNode, m_pathType); - } + } + + if (foundGround) { + clearSearchNodes (); + findNextBestNode (); } } } @@ -2345,6 +2344,11 @@ bool Bot::selectBestNextNode () { continue; } + // skip isn't visible links + if (!vistab.visible (link.index, nextNodeIndex) || !vistab.visible (link.index, prevNodeIndex)) { + continue; + } + // don't use ladder nodes as alternative if (graph[link.index].flags & (NodeFlag::Ladder | NodeFlag::Camp | PathFlag::Jump)) { continue; @@ -3298,13 +3302,11 @@ bool Bot::isReachableNode (int index) { return tr.flFraction >= 1.0f; } -bool Bot::isOnLadderPath () { +bool Bot::isPreviousLadder () { const auto prevNodeIndex = m_previousNodes[0]; // bot entered ladder path - return (m_pathFlags & NodeFlag::Ladder) - && graph.exists (prevNodeIndex) - && (graph[prevNodeIndex].flags & NodeFlag::Ladder); + return graph.exists (prevNodeIndex) && (graph[prevNodeIndex].flags & NodeFlag::Ladder); } void Bot::findShortestPath (int srcIndex, int destIndex) { diff --git a/src/tasks.cpp b/src/tasks.cpp index 4807899..f585b42 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -1398,7 +1398,10 @@ void Bot::escapeFromBomb_ () { pev->button |= IN_ATTACK2; } - if (!usesKnife () && m_numEnemiesLeft == 0) { + if (!usesKnife () + && m_lastEnemyOrigin.empty () + && !(m_states & Sense::SeeingEnemy) + && !util.isAlive (m_lastEnemy)) { selectWeaponById (Weapon::Knife); }