nav: minor change in ladder use.

aim: slight improvement in enemy prediction node recruitment.
This commit is contained in:
commandcobra7 2024-11-13 17:52:19 +03:00 committed by jeefo
commit 1d60f19402
16 changed files with 151 additions and 145 deletions

View file

@ -9,9 +9,9 @@
// links keywords and replies together // links keywords and replies together
struct ChatKeywords { struct ChatKeywords {
StringArray keywords; StringArray keywords {};
StringArray replies; StringArray replies {};
StringArray usedReplies; StringArray usedReplies {};
public: public:
ChatKeywords () = default; ChatKeywords () = default;

View file

@ -9,7 +9,7 @@
// botname structure definition // botname structure definition
struct BotName { struct BotName {
String name; String name {};
int usedBy = -1; int usedBy = -1;
public: public:
@ -19,9 +19,9 @@ public:
// voice config structure definition // voice config structure definition
struct ChatterItem { struct ChatterItem {
String name; String name {};
float repeat; float repeat {};
float duration; float duration {};
public: public:
ChatterItem (StringRef name, float repeat, float duration) : name (name), repeat (repeat), duration (duration) {} ChatterItem (StringRef name, float repeat, float duration) : name (name), repeat (repeat), duration (duration) {}

View file

@ -29,7 +29,7 @@ public:
public: public:
// generic bot command // generic bot command
struct BotCmd { struct BotCmd {
String name, format, help; String name {}, format {}, help {};
Handler handler = nullptr; Handler handler = nullptr;
bool visible = true; bool visible = true;
@ -42,9 +42,9 @@ public:
// single bot menu // single bot menu
struct BotMenu { struct BotMenu {
int ident, slots; int ident {}, slots {};
String text; String text {};
MenuHandler handler; MenuHandler handler {};
public: public:
explicit BotMenu (int ident, int slots, StringRef text, MenuHandler handler) : ident (ident), slots (slots), text (text), handler (cr::move (handler)) 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 // queued text message to prevent overflow with rapid output
struct PrintQueue { struct PrintQueue {
int32_t destination {}; int32_t destination {};
String text; String text {};
public: public:
explicit PrintQueue () = default; explicit PrintQueue () = default;
@ -68,7 +68,7 @@ public:
float timelimit {}; float timelimit {};
float freezetime {}; float freezetime {};
float roundtime {}; float roundtime {};
} m_graphSaveVarValues; } m_graphSaveVarValues {};
private: private:
StringArray m_args {}; StringArray m_args {};

View file

@ -85,16 +85,16 @@ CR_DECLARE_SCOPED_ENUM (PlayerPart,
// variable reg pair // variable reg pair
struct ConVarReg { struct ConVarReg {
cvar_t reg; cvar_t reg {};
String info; String info {};
String init; String init {};
String regval; String regval {};
String name; String name {};
class ConVar *self; class ConVar *self {};
float initial, min, max; float initial {}, min {}, max {};
bool missing; bool missing {};
bool bounded; bool bounded {};
int32_t type; int32_t type {};
}; };
// entity prototype // entity prototype

View file

@ -91,11 +91,11 @@ CR_DECLARE_SCOPED_ENUM (NotifySound,
// general waypoint header information structure for podbot // general waypoint header information structure for podbot
struct PODGraphHeader { struct PODGraphHeader {
char header[8]; char header[8] {};
int32_t fileVersion; int32_t fileVersion {};
int32_t pointNumber; int32_t pointNumber {};
char mapName[32]; char mapName[32] {};
char author[32]; char author[32] {};
}; };
// defines linked nodes // defines linked nodes
@ -117,31 +117,31 @@ struct Path {
// define waypoint structure for podbot (will convert on load) // define waypoint structure for podbot (will convert on load)
struct PODPath { struct PODPath {
int32_t number, flags; int32_t number {}, flags {};
Vector origin; Vector origin {};
float radius, csx, csy, cex, cey; float radius {}, csx {}, csy {}, cex {}, cey {};
int16_t index[kMaxNodeLinks]; int16_t index[kMaxNodeLinks] {};
uint16_t conflags[kMaxNodeLinks]; uint16_t conflags[kMaxNodeLinks] {};
Vector velocity[kMaxNodeLinks]; Vector velocity[kMaxNodeLinks] {};
int32_t distance[kMaxNodeLinks]; int32_t distance[kMaxNodeLinks] {};
PathVis vis; PathVis vis {};
}; };
// general storage header information structure // general storage header information structure
struct StorageHeader { struct StorageHeader {
int32_t magic; int32_t magic {};
int32_t version; int32_t version {};
int32_t options; int32_t options {};
int32_t length; int32_t length {};
int32_t compressed; int32_t compressed {};
int32_t uncompressed; int32_t uncompressed {};
}; };
// extension header for graph information // extension header for graph information
struct ExtenHeader { struct ExtenHeader {
char author[32]; // original author of graph char author[32] {}; // original author of graph
int32_t mapSize; // bsp size for checksumming map consistency int32_t mapSize {}; // bsp size for checksumming map consistency
char modified[32]; // by whom modified char modified[32] {}; // by whom modified
}; };
// graph operation class // graph operation class

View file

@ -87,7 +87,7 @@ private:
using SendToProto = decltype (sendto); using SendToProto = decltype (sendto);
private: private:
Detour <SendToProto> m_sendToDetour { }, m_sendToDetourSys {}; Detour <SendToProto> m_sendToDetour {}, m_sendToDetourSys {};
public: public:
ServerQueryHook () = default; ServerQueryHook () = default;
@ -126,11 +126,11 @@ private:
private: private:
bool m_paused { false }; bool m_paused { false };
Detour <DlsymProto> m_dlsym; Detour <DlsymProto> m_dlsym {};
Detour <DlcloseProto> m_dlclose; Detour <DlcloseProto> m_dlclose {};
HashMap <StringRef, SharedLibrary::Func> m_exports; HashMap <StringRef, SharedLibrary::Func> m_exports {};
SharedLibrary m_self; SharedLibrary m_self {};
public: public:
DynamicLinkerHook () = default; DynamicLinkerHook () = default;

View file

@ -9,12 +9,12 @@
// bot creation tab // bot creation tab
struct BotRequest { struct BotRequest {
bool manual; bool manual {};
int difficulty; int difficulty {};
int team; int team {};
int skin; int skin {};
int personality; int personality {};
String name; String name {};
}; };
// manager class // manager class

View file

@ -104,8 +104,8 @@ private:
int m_length {}; int m_length {};
Array <int> m_constructedPath; Array <int> m_constructedPath {};
Array <int> m_smoothedPath; Array <int> m_smoothedPath {};
private: private:
// clears the currently built route // clears the currently built route

View file

@ -36,10 +36,10 @@ public:
static constexpr float kMinViewDistance = 2.0f; static constexpr float kMinViewDistance = 2.0f;
private: private:
float m_farHeight; // height of the far frustum float m_farHeight {}; // height of the far frustum
float m_farWidth; // width of the far frustum float m_farWidth {}; // width of the far frustum
float m_nearHeight; // height of the near frustum float m_nearHeight {}; // height of the near frustum
float m_nearWidth; // width of the near frustum float m_nearWidth {}; // width of the near frustum
public: public:
explicit Frustum () { explicit Frustum () {

View file

@ -17,7 +17,7 @@ CR_DECLARE_SCOPED_ENUM_TYPE (VisIndex, int32_t,
// defines visibility count // defines visibility count
struct PathVis { struct PathVis {
uint16_t stand, crouch; uint16_t stand {}, crouch {};
}; };
class GraphVistable final : public Singleton <GraphVistable> { class GraphVistable final : public Singleton <GraphVistable> {

View file

@ -26,12 +26,12 @@ struct BotTask {
using Function = void (Bot::*) (); using Function = void (Bot::*) ();
public: public:
Function func; // corresponding exec function in bot class Function func {}; // corresponding exec function in bot class
Task id; // major task/action carried out Task id {}; // major task/action carried out
float desire; // desire (filled in) for this task float desire {}; // desire (filled in) for this task
int data; // additional data (node index) int data {}; // additional data (node index)
float time; // time task expires float time {}; // time task expires
bool resume; // if task can be continued if interrupted bool resume {}; // if task can be continued if interrupted
public: 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) { } 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 // weapon info structure
struct WeaponInfo { struct WeaponInfo {
int id; // the weapon id value int id {}; // the weapon id value
StringRef name; // name of the weapon when selecting it StringRef name {}; // name of the weapon when selecting it
StringRef model; // model name to separate cs weapons StringRef model {}; // model name to separate cs weapons
int price; // price when buying int price {}; // price when buying
int minPrimaryAmmo; // minimum primary ammo int minPrimaryAmmo {}; // minimum primary ammo
int teamStandard; // used by team (number) (standard map) int teamStandard {}; // used by team (number) (standard map)
int teamAS; // used by team (as map) int teamAS {}; // used by team (as map)
int buyGroup; // group in buy menu (standard map) int buyGroup {}; // group in buy menu (standard map)
int buySelect; // select item in buy menu (standard map) int buySelect {}; // select item in buy menu (standard map)
int buySelectT; // for counter-strike v1.6 int buySelectT {}; // for counter-strike v1.6
int buySelectCT; // for counter-strike v1.6 int buySelectCT {}; // for counter-strike v1.6
int penetratePower; // penetrate power int penetratePower {}; // penetrate power
int maxClip; // max ammo in clip int maxClip {}; // max ammo in clip
int type; // weapon class int type {}; // weapon class
bool primaryFireHold; // hold down primary fire button to use? bool primaryFireHold {}; // hold down primary fire button to use?
public: public:
WeaponInfo (int id, WeaponInfo (int id,
@ -227,7 +227,7 @@ private:
int m_radioSelect {}; // radio entry int m_radioSelect {}; // radio entry
int m_killsCount {}; // the kills count of a bot 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_lastPredictLength {}; // last predicted path length
int m_pickupType {}; // type of entity which needs to be used/picked up int m_pickupType {}; // type of entity which needs to be used/picked up
@ -452,7 +452,7 @@ private:
bool isEnemyInSight (Vector &endPos); bool isEnemyInSight (Vector &endPos);
bool isEnemyNoticeable (float range); bool isEnemyNoticeable (float range);
bool isCreature (); bool isCreature ();
bool isOnLadderPath (); bool isPreviousLadder ();
bool isIgnoredItem (edict_t *ent); bool isIgnoredItem (edict_t *ent);
void doPlayerAvoidance (const Vector &normal); void doPlayerAvoidance (const Vector &normal);

View file

@ -97,7 +97,6 @@ void Bot::avoidGrenades () {
auto model = pent->v.model.str (9); auto model = pent->v.model.str (9);
if (m_preventFlashing < game.time () if (m_preventFlashing < game.time ()
&& m_personality == Personality::Rusher
&& cv_whose_your_daddy && cv_whose_your_daddy
&& model == kFlashbangModelName) { && model == kFlashbangModelName) {
@ -209,15 +208,15 @@ void Bot::checkBreakablesAround () {
} }
const auto &origin = game.getEntityOrigin (breakable); const auto &origin = game.getEntityOrigin (breakable);
const auto lengthToObstacleSq = origin.distanceSq (pev->origin); const auto distanceToObstacleSq = origin.distanceSq (pev->origin);
// too far, skip it // too far, skip it
if (lengthToObstacleSq > cr::sqrf (radius)) { if (distanceToObstacleSq > cr::sqrf (radius)) {
continue; continue;
} }
// too close, skip it // too close, skip it
if (lengthToObstacleSq < cr::sqrf (100.0f)) { if (distanceToObstacleSq < cr::sqrf (100.0f)) {
continue; continue;
} }
@ -343,7 +342,7 @@ void Bot::updatePickups () {
} }
const auto &interesting = bots.getInterestingEntities (); const auto &interesting = bots.getInterestingEntities ();
const float radius = cr::sqrf (cv_object_pickup_radius.as <float> ()); const float radiusSq = cr::sqrf (cv_object_pickup_radius.as <float> ());
if (!game.isNullEntity (m_pickupItem)) { if (!game.isNullEntity (m_pickupItem)) {
bool itemExists = false; bool itemExists = false;
@ -358,7 +357,7 @@ void Bot::updatePickups () {
const Vector &origin = game.getEntityOrigin (ent); const Vector &origin = game.getEntityOrigin (ent);
// too far from us ? // too far from us ?
if (pev->origin.distanceSq (origin) > radius) { if (pev->origin.distanceSq (origin) > radiusSq) {
continue; continue;
} }
@ -397,7 +396,7 @@ void Bot::updatePickups () {
} }
// too far from us ? // too far from us ?
if (pev->origin.distanceSq (origin) > radius) { if (pev->origin.distanceSq (origin) > radiusSq) {
continue; continue;
} }
@ -1705,6 +1704,7 @@ void Bot::overrideConditions () {
// special handling for sniping // special handling for sniping
if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))
&& m_shootTime - 0.4f <= game.time () && m_shootTime - 0.4f <= game.time ()
&& m_shootTime + 0.1f > game.time ()
&& m_sniperStopTime > game.time ()) { && m_sniperStopTime > game.time ()) {
ignoreCollision (); ignoreCollision ();
@ -1751,7 +1751,7 @@ void Bot::syncUpdatePredictedIndex () {
} }
ScopedUnlock <Mutex> unlock (m_predictLock); ScopedUnlock <Mutex> unlock (m_predictLock);
const auto lastEnemyOrigin = m_lastEnemyOrigin; const auto &lastEnemyOrigin = m_lastEnemyOrigin;
const auto currentNodeIndex = m_currentNodeIndex; const auto currentNodeIndex = m_currentNodeIndex;
const auto &botOrigin = pev->origin; const auto &botOrigin = pev->origin;
@ -1760,8 +1760,8 @@ void Bot::syncUpdatePredictedIndex () {
return; return;
} }
int destIndex = graph.getNearest (lastEnemyOrigin); const int destIndex = graph.getNearest (lastEnemyOrigin);
int bestIndex = kInvalidNodeIndex; int bestIndex = m_currentNodeIndex;
if (destIndex == kInvalidNodeIndex) { if (destIndex == kInvalidNodeIndex) {
wipePredict (); wipePredict ();
@ -1769,7 +1769,7 @@ void Bot::syncUpdatePredictedIndex () {
} }
int pathLength = 0; int pathLength = 0;
auto result = planner.find (destIndex, currentNodeIndex, [&] (int index) { planner.find (destIndex, currentNodeIndex, [&] (int index) {
++pathLength; ++pathLength;
if (vistab.visible (currentNodeIndex, index) && botOrigin.distanceSq (graph[index].origin) < cr::sqrf (2048.0f)) { if (vistab.visible (currentNodeIndex, index) && botOrigin.distanceSq (graph[index].origin) < cr::sqrf (2048.0f)) {
@ -1779,13 +1779,12 @@ void Bot::syncUpdatePredictedIndex () {
return true; return true;
}); });
if (result && bestIndex != kInvalidNodeIndex) { if (bestIndex != currentNodeIndex) {
m_lastPredictIndex = bestIndex; m_lastPredictIndex = bestIndex;
m_lastPredictLength = pathLength; m_lastPredictLength = pathLength;
return; return;
} }
wipePredict ();
} }
void Bot::updatePredictedIndex () { void Bot::updatePredictedIndex () {
@ -1947,7 +1946,7 @@ void Bot::setConditions () {
if (!m_lastEnemyOrigin.empty ()) { if (!m_lastEnemyOrigin.empty ()) {
const auto distanceSq = pev->origin.distanceSq (m_lastEnemyOrigin); 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_lastEnemyOrigin.clear ();
m_lastEnemy = nullptr; m_lastEnemy = nullptr;
@ -2057,9 +2056,6 @@ void Bot::filterTasks () {
else if (m_isVIP || m_isReloading || (sniping && usesSniper ())) { else if (m_isVIP || m_isReloading || (sniping && usesSniper ())) {
ratio *= 3.0f; // triple the seek cover desire if bot is VIP or reloading 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)) { else if (game.is (GameFlags::CSDM)) {
ratio = 0.0f; ratio = 0.0f;
} }

View file

@ -1433,7 +1433,7 @@ void Bot::attackMovement () {
if ((m_states & Sense::SeeingEnemy) if ((m_states & Sense::SeeingEnemy)
&& approach < 30 && approach < 30
&& !bots.isBombPlanted () && !bots.isBombPlanted ()
&& (isInViewCone (m_enemy->v.origin) || m_isVIP)) { && (isInViewCone (m_enemy->v.origin) || m_isVIP || m_isReloading)) {
if (m_retreatTime < game.time ()) { if (m_retreatTime < game.time ()) {
startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true); 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)); const bool isFullView = !!(m_enemyParts & (Visibility::Head | Visibility::Body));
if (m_lastFightStyleCheck < game.time ()) { 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; m_fightStyle = Fight::Stay;
} }
else if (usesRifle () || usesSubmachine () || usesHeavy ()) { else if (usesRifle () || usesSubmachine () || usesHeavy ()) {

View file

@ -1518,6 +1518,8 @@ void Bot::newRound () {
m_trackingEdict = nullptr; m_trackingEdict = nullptr;
m_enemyBodyPartSet = nullptr; m_enemyBodyPartSet = nullptr;
m_timeNextTracking = 0.0f; m_timeNextTracking = 0.0f;
m_lastPredictIndex = kInvalidNodeIndex;
m_lastPredictLength = kInfiniteDistanceLong;
m_buttonPushTime = 0.0f; m_buttonPushTime = 0.0f;
m_enemyUpdateTime = 0.0f; m_enemyUpdateTime = 0.0f;

View file

@ -573,8 +573,14 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
} }
// not stuck yet // not stuck yet
else { else {
bool isOnLadderPath = false;
if ((m_pathFlags & NodeFlag::Ladder) && isPreviousLadder ()) {
isOnLadderPath = true;
}
// test if there's something ahead blocking the way // 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)) { if (cr::fzero (m_firstCollideTime)) {
m_firstCollideTime = game.time () + 0.2f; m_firstCollideTime = game.time () + 0.2f;
} }
@ -874,11 +880,11 @@ void Bot::checkFall () {
else if (pev->origin.z + 128.0f < m_checkFallPoint[1].z && pev->origin.z + 128.0f < m_checkFallPoint[0].z) { else if (pev->origin.z + 128.0f < m_checkFallPoint[1].z && pev->origin.z + 128.0f < m_checkFallPoint[0].z) {
fixFall = true; fixFall = true;
} }
else if (m_currentNodeIndex != kInvalidNodeIndex) { 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) { && nowDistanceSq <= cr::sqrf (32.0f)
&& pev->origin.z + 64.0f < m_checkFallPoint[1].z) {
fixFall = true; fixFall = true;
} }
}
if (fixFall) { if (fixFall) {
m_currentNodeIndex = kInvalidNodeIndex; m_currentNodeIndex = kInvalidNodeIndex;
@ -1052,27 +1058,15 @@ bool Bot::updateNavigation () {
const auto prevNodeIndex = m_previousNodes[0]; const auto prevNodeIndex = m_previousNodes[0];
const float ladderDistance = pev->origin.distance (m_pathOrigin); 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) { if (m_pathOrigin.z >= pev->origin.z + 16.0f) {
m_pathOrigin = m_path->origin + kLadderOffset; m_pathOrigin = m_path->origin + kLadderOffset;
} }
else if (m_pathOrigin.z < pev->origin.z - 16.0f) { else if (m_pathOrigin.z < pev->origin.z - 16.0f) {
m_pathOrigin = m_path->origin - kLadderOffset; 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 ()) { if (!isDucking ()) {
m_moveSpeed = pev->maxspeed * 0.4f; m_moveSpeed = pev->maxspeed * 0.4f;
} }
@ -1084,36 +1078,41 @@ bool Bot::updateNavigation () {
m_approachingLadderTimer.start (m_frameInterval * 4.0f); 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) // special detection if someone is using the ladder (to prevent to have bots-towers on ladders)
for (const auto &client : util.getClients ()) { for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) if (!(client.flags & ClientFlags::Used)
|| !(client.flags & ClientFlags::Alive) || !(client.flags & ClientFlags::Alive)
|| client.team != m_team || client.team != m_team
|| client.ent == ent ()) { || client.ent == ent ()
|| !(client.ent->v.movetype == MOVETYPE_FLY)) {
continue; continue;
} }
if (client.ent->v.origin.distanceSq (m_pathOrigin) > cr::sqrf (64.0f)) {
continue;
}
bool foundGround = false; 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) {
if (cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) { if (isPreviousLadder ()) {
const auto numPreviousNode = rg (0, 2); if (client.ent->v.origin.distanceSq (pev->origin) < cr::sqrf (64.0f)) {
for (int i = 0; i < numPreviousNode; ++i) {
if (graph.exists (prevNodeIndex) && (graph[prevNodeIndex].flags & NodeFlag::Ladder)) {
foundGround = true; foundGround = true;
previousNode = m_previousNodes[i];
break;
} }
} }
}
if (foundGround) { if (foundGround) {
findPath (m_previousNodes[0], previousNode, m_pathType); clearSearchNodes ();
} findNextBestNode ();
} }
} }
} }
@ -2345,6 +2344,11 @@ bool Bot::selectBestNextNode () {
continue; 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 // don't use ladder nodes as alternative
if (graph[link.index].flags & (NodeFlag::Ladder | NodeFlag::Camp | PathFlag::Jump)) { if (graph[link.index].flags & (NodeFlag::Ladder | NodeFlag::Camp | PathFlag::Jump)) {
continue; continue;
@ -3298,13 +3302,11 @@ bool Bot::isReachableNode (int index) {
return tr.flFraction >= 1.0f; return tr.flFraction >= 1.0f;
} }
bool Bot::isOnLadderPath () { bool Bot::isPreviousLadder () {
const auto prevNodeIndex = m_previousNodes[0]; const auto prevNodeIndex = m_previousNodes[0];
// bot entered ladder path // bot entered ladder path
return (m_pathFlags & NodeFlag::Ladder) return graph.exists (prevNodeIndex) && (graph[prevNodeIndex].flags & NodeFlag::Ladder);
&& graph.exists (prevNodeIndex)
&& (graph[prevNodeIndex].flags & NodeFlag::Ladder);
} }
void Bot::findShortestPath (int srcIndex, int destIndex) { void Bot::findShortestPath (int srcIndex, int destIndex) {

View file

@ -1398,7 +1398,10 @@ void Bot::escapeFromBomb_ () {
pev->button |= IN_ATTACK2; 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); selectWeaponById (Weapon::Knife);
} }