nav: more fixes to ladder navigation

refactor: bot difficulty data and add graph refresh command
combat: fixes for smoke grenades (ref #743)
engine: fixes to spawn management (ref #744)
This commit is contained in:
jeefo 2025-11-12 21:31:23 +03:00
commit 17ed252b60
No known key found for this signature in database
GPG key ID: D696786B81B667C8
26 changed files with 506 additions and 408 deletions

View file

@ -29,16 +29,6 @@ public:
// mostly config stuff, and some stuff dealing with menus
class BotConfig final : public Singleton <BotConfig> {
public:
struct DifficultyData {
float reaction[2] {};
int32_t headshotPct {};
int32_t seenThruPct {};
int32_t hearThruPct {};
int32_t maxRecoil {};
Vector aimError {};
};
private:
Array <StringArray> m_chat {};
Array <Array <ChatterItem>> m_chatter {};
@ -52,7 +42,7 @@ private:
StringArray m_avatars {};
HashMap <uint32_t, String, Hash <int32_t>> m_language {};
HashMap <int32_t, DifficultyData> m_difficulty {};
HashMap <int32_t, BotDifficultyData> m_difficulty {};
HashMap <String, String> m_custom {};
// default tables for personality weapon preferences, overridden by weapon.cfg
@ -218,10 +208,7 @@ public:
}
// get's the difficulty level tweaks
DifficultyData *getDifficultyTweaks (int32_t level) {
if (level < Difficulty::Noob || level > Difficulty::Expert) {
return &m_difficulty[Difficulty::Expert];
}
BotDifficultyData *getDifficultyTweaks (int32_t level) {
return &m_difficulty[level];
}

View file

@ -69,8 +69,8 @@ CR_DECLARE_SCOPED_ENUM (Menu,
// bomb say string
CR_DECLARE_SCOPED_ENUM (BombPlantedSay,
ChatSay = cr::bit (1),
Chatter = cr::bit (2)
ChatSay = cr::bit (1),
Chatter = cr::bit (2)
)
// chat types id's
@ -419,7 +419,13 @@ CR_DECLARE_SCOPED_ENUM (GoalTactic,
RescueHostage
)
// some hard-coded desire defines used to override calculated ones
// ladder move direction
CR_DECLARE_SCOPED_ENUM (LadderDir,
Up = 0,
Down,
)
// some hard-coded desire defines used to override calculated ones
namespace TaskPri {
constexpr auto Normal { 35.0f };
constexpr auto Pause { 36.0f };
@ -447,7 +453,7 @@ constexpr auto kSprayDistanceX2 = kSprayDistance * 2;
constexpr auto kMaxChatterRepeatInterval = 99.0f;
constexpr auto kViewFrameUpdate = 1.0f / 25.0f;
constexpr auto kGrenadeDamageRadius = 385.0f;
constexpr auto kMinMovedDistance = 2.5f;
constexpr auto kMinMovedDistance = cr::sqrf (2.0f);
constexpr auto kInfiniteDistanceLong = static_cast <int> (kInfiniteDistance);
constexpr auto kMaxWeapons = 32;
@ -462,35 +468,35 @@ constexpr auto kConfigExtension = "cfg";
// weapon masks
constexpr auto kPrimaryWeaponMask = (cr::bit (Weapon::XM1014) |
cr::bit (Weapon::M3) |
cr::bit (Weapon::MAC10) |
cr::bit (Weapon::UMP45) |
cr::bit (Weapon::MP5) |
cr::bit (Weapon::TMP) |
cr::bit (Weapon::P90) |
cr::bit (Weapon::AUG) |
cr::bit (Weapon::M4A1) |
cr::bit (Weapon::SG552) |
cr::bit (Weapon::AK47) |
cr::bit (Weapon::Scout) |
cr::bit (Weapon::SG550) |
cr::bit (Weapon::AWP) |
cr::bit (Weapon::G3SG1) |
cr::bit (Weapon::M249) |
cr::bit (Weapon::Famas) |
cr::bit (Weapon::Galil));
cr::bit (Weapon::M3) |
cr::bit (Weapon::MAC10) |
cr::bit (Weapon::UMP45) |
cr::bit (Weapon::MP5) |
cr::bit (Weapon::TMP) |
cr::bit (Weapon::P90) |
cr::bit (Weapon::AUG) |
cr::bit (Weapon::M4A1) |
cr::bit (Weapon::SG552) |
cr::bit (Weapon::AK47) |
cr::bit (Weapon::Scout) |
cr::bit (Weapon::SG550) |
cr::bit (Weapon::AWP) |
cr::bit (Weapon::G3SG1) |
cr::bit (Weapon::M249) |
cr::bit (Weapon::Famas) |
cr::bit (Weapon::Galil));
constexpr auto kSecondaryWeaponMask = (cr::bit (Weapon::P228)
| cr::bit (Weapon::Elite)
| cr::bit (Weapon::USP)
| cr::bit (Weapon::Glock18)
| cr::bit (Weapon::Deagle)
| cr::bit (Weapon::FiveSeven));
| cr::bit (Weapon::Elite)
| cr::bit (Weapon::USP)
| cr::bit (Weapon::Glock18)
| cr::bit (Weapon::Deagle)
| cr::bit (Weapon::FiveSeven));
constexpr auto kSniperWeaponMask = (cr::bit (Weapon::Scout)
| cr::bit (Weapon::SG550)
| cr::bit (Weapon::AWP)
| cr::bit (Weapon::G3SG1));
| cr::bit (Weapon::SG550)
| cr::bit (Weapon::AWP)
| cr::bit (Weapon::G3SG1));
// weapons < 7 are secondary
constexpr auto kPrimaryWeaponMinIndex = 7;

View file

@ -23,8 +23,8 @@ CR_DECLARE_SCOPED_ENUM (PrintQueueDestination,
// bot command manager
class BotControl final : public Singleton <BotControl> {
public:
using Handler = int (BotControl::*) ();
using MenuHandler = int (BotControl::*) (int);
using Handler = int (BotControl:: *) ();
using MenuHandler = int (BotControl:: *) (int);
public:
// generic bot command
@ -36,8 +36,7 @@ public:
public:
explicit BotCmd () = default;
BotCmd (StringRef name, StringRef format, StringRef help, Handler handler, bool visible = true) : name (name), format (format), help (help), handler (cr::move (handler)), visible (visible)
{ }
BotCmd (StringRef name, StringRef format, StringRef help, Handler handler, bool visible = true) : name (name), format (format), help (help), handler (cr::move (handler)), visible (visible) {}
};
// single bot menu
@ -47,8 +46,7 @@ public:
MenuHandler handler {};
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)) {}
};
// queued text message to prevent overflow with rapid output
@ -57,10 +55,9 @@ public:
String text {};
public:
explicit PrintQueue () = default;
explicit PrintQueue () = default;
PrintQueue (int32_t destination, StringRef text) : destination (destination), text (text)
{ }
PrintQueue (int32_t destination, StringRef text) : destination (destination), text (text) {}
};
// save old values of changed cvars to revert them back when editing turned off
@ -118,6 +115,7 @@ private:
int cmdNodeSave ();
int cmdNodeLoad ();
int cmdNodeErase ();
int cmdNodeRefresh ();
int cmdNodeEraseTraining ();
int cmdNodeDelete ();
int cmdNodeCheck ();
@ -255,7 +253,7 @@ public:
}
}
edict_t *getIssuer() {
edict_t *getIssuer () {
return m_ent;
}

View file

@ -60,7 +60,7 @@ CR_DECLARE_SCOPED_ENUM (MapFlags,
Escape = cr::bit (3),
KnifeArena = cr::bit (4),
FightYard = cr::bit (5),
GrenadeWar = cr::bit(6),
GrenadeWar = cr::bit (6),
HasDoors = cr::bit (10), // additional flags
HasButtons = cr::bit (11) // map has buttons
)
@ -190,6 +190,9 @@ public:
// initialize levels
void levelInitialize (edict_t *entities, int max);
// when entity spawns
void onSpawnEntity (edict_t *ent);
// shutdown levels
void levelShutdown ();
@ -291,7 +294,7 @@ public:
bool isFakeClientEntity (edict_t *ent) const;
// check if entity is a player
bool isPlayerEntity (edict_t *ent) const ;
bool isPlayerEntity (edict_t *ent) const;
// check if entity is a monster
bool isMonsterEntity (edict_t *ent) const;
@ -333,7 +336,10 @@ public:
// gets custom engine args for client command
const char *botArgs () const {
return strings.format (String::join (m_botArgs, " ", m_botArgs[0].startsWith ("say") ? 1 : 0).chars ());
auto result = strings.chars ();
strings.copy (result, String::join (m_botArgs, " ", m_botArgs[0].startsWith ("say") ? 1 : 0).chars (), cr::StringBuffer::StaticBufferSize);
return result;
}
// gets custom engine argv for client command
@ -486,7 +492,7 @@ public:
// helper to sending the client message
void sendClientMessage (bool console, edict_t *ent, StringRef message);
// helper to sending the server message
void sendServerMessage (StringRef message);

View file

@ -227,6 +227,7 @@ public:
bool loadGraphData ();
bool canDownload ();
bool isAnalyzed () const;
bool isConverted () const;
void saveOldFormat ();
void reset ();

View file

@ -69,7 +69,7 @@ CR_DECLARE_SCOPED_ENUM (StatusIconCache,
class MessageDispatcher final : public Singleton <MessageDispatcher> {
private:
using MsgFunc = void (MessageDispatcher::*) ();
using MsgFunc = void (MessageDispatcher:: *) ();
using MsgHash = Hash <int32_t>;
private:
@ -81,9 +81,9 @@ private:
};
public:
Args (float value) : float_ (value) { }
Args (int32_t value) : long_ (value) { }
Args (const char *value) : chars_ (value) { }
Args (float value) : float_ (value) {}
Args (int32_t value) : long_ (value) {}
Args (const char *value) : chars_ (value) {}
};
private:

View file

@ -21,10 +21,10 @@ CR_DECLARE_SCOPED_ENUM (AStarResult,
Success = 0,
Failed,
InternalError,
)
)
// node added
using NodeAdderFn = const Lambda <bool (int)> &;
// node added
using NodeAdderFn = const Lambda <bool (int)> &;
// route twin node
template <typename HT> struct RouteTwin final {
@ -58,7 +58,7 @@ public:
static float gfunctionKillsDist (int team, int currentIndex, int parentIndex);
// least kills and number of nodes to goal for a team (when with hostage)
static float gfunctionKillsDistCTWithHostage (int team, int currentIndex, int parentIndex);
static float gfunctionKillsDistCTWithHostage (int team, int currentIndex, int parentIndex);
// least kills to goal for a team
static float gfunctionKills (int team, int currentIndex, int);

View file

@ -22,7 +22,8 @@ CR_DECLARE_SCOPED_ENUM (StorageOption,
Official = cr::bit (4), // this is additional flag for graph indicates graph are official
Recovered = cr::bit (5), // this is additional flag indicates graph converted from podbot and was bad
Exten = cr::bit (6), // this is additional flag indicates that there's extension info
Analyzed = cr::bit (7) // this graph has been analyzed
Analyzed = cr::bit (7), // this graph has been analyzed
Converted = cr::bit (8) // converted from a pwf format
)
// storage header versions

View file

@ -54,6 +54,11 @@ public:
bool isReady () const {
return !m_rebuild;
}
// is visible fromr both points ?
bool visibleBothSides (int srcIndex, int destIndex, VisIndex vis = VisIndex::Any) const {
return visible (srcIndex, destIndex, vis) && visible (destIndex, srcIndex, vis);
}
};
// expose global

View file

@ -23,7 +23,7 @@ using namespace cr;
// tasks definition
struct BotTask {
using Function = void (Bot::*) ();
using Function = void (Bot:: *) ();
public:
Function func {}; // corresponding exec function in bot class
@ -34,7 +34,7 @@ public:
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) { }
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) {}
};
// weapon properties structure
@ -68,7 +68,7 @@ struct WeaponInfo {
bool primaryFireHold {}; // hold down primary fire button to use?
public:
WeaponInfo (int id,
WeaponInfo (int id,
StringRef name,
StringRef model,
int price,
@ -78,14 +78,13 @@ public:
int buyGroup,
int buySelect,
int buySelectT,
int buySelectCT,
int buySelectCT,
int penetratePower,
int maxClip,
int type,
bool fireHold) : id (id), name (name), model (model), price (price), minPrimaryAmmo (minPriAmmo), teamStandard (teamStd),
bool fireHold) : id (id), name (name), model (model), price (price), minPrimaryAmmo (minPriAmmo), teamStandard (teamStd),
teamAS (teamAs), buyGroup (buyGroup), buySelect (buySelect), buySelectT (buySelectT), buySelectCT (buySelectCT),
penetratePower (penetratePower), maxClip (maxClip), type (type), primaryFireHold (fireHold)
{ }
penetratePower (penetratePower), maxClip (maxClip), type (type), primaryFireHold (fireHold) {}
};
// clients noise
@ -123,6 +122,16 @@ struct BotTeamData {
int32_t lastRadioSlot = { kInvalidRadioSlot }; // last radio message for team
};
// bot difficulty data
struct BotDifficultyData {
float reaction[2] {};
int32_t headshotPct {};
int32_t seenThruPct {};
int32_t hearThruPct {};
int32_t maxRecoil {};
Vector aimError {};
};
// include bot graph stuff
#include <graph.h>
#include <vision.h>
@ -210,6 +219,8 @@ private:
mutable Mutex m_pathFindLock {};
mutable Mutex m_predictLock {};
float f_wpt_tim_str_chg;
private:
uint32_t m_states {}; // sensing bitstates
uint32_t m_collideMoves[kMaxCollideMoves] {}; // sorted array of movements
@ -240,11 +251,10 @@ private:
int m_lastPredictLength {}; // last predicted path length
int m_pickupType {}; // type of entity which needs to be used/picked up
float m_headedTime {};
float m_headedTime {}; // last time followed by radio entity
float m_prevTime {}; // time previously checked movement speed
float m_heavyTimestamp {}; // is it time to execute heavy-weight functions
float m_prevSpeed {}; // speed some frames before
float m_prevVelocity {}; // velocity some frames before
float m_timeDoorOpen {}; // time to next door open check
float m_timeHitDoor {}; // specific time after hitting the door
float m_lastChatTime {}; // time bot last chatted
@ -361,7 +371,6 @@ private:
Vector m_lookAtPredict {}; // aiming vector when predicting
Vector m_desiredVelocity {}; // desired velocity for jump nodes
Vector m_breakableOrigin {}; // origin of breakable
Vector m_rightRef {}; // right referential vector
Vector m_checkFallPoint[2] {}; // check fall point
Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
@ -370,6 +379,7 @@ private:
UniquePtr <class PlayerHitboxEnumerator> m_hitboxEnumerator {};
BotDifficultyData *m_difficultyData {};
Path *m_path {}; // pointer to the current path node
String m_chatBuffer {}; // space for strings (say text...)
Frustum::Planes m_viewFrustum {};
@ -400,7 +410,7 @@ private:
int numEnemiesNear (const Vector &origin, const float radius) const;
int numFriendsNear (const Vector &origin, const float radius) const;
float getEstimatedNodeReachTime ();
float isInFOV (const Vector &dest) const;
float getShiftSpeed ();
@ -529,7 +539,6 @@ private:
void syncUpdatePredictedIndex ();
void updatePredictedIndex ();
void refreshCreatureStatus (char *infobuffer);
void updateRightRef ();
void donateC4ToHuman ();
void clearAmmoInfo ();
void handleChatterTaskChange (Task tid);
@ -681,6 +690,7 @@ public:
int m_ammoInClip[kMaxWeapons] {}; // ammo in clip for each weapons
int m_ammo[MAX_AMMO_SLOTS] {}; // total ammo amounts
int m_deathCount {}; // number of bot deaths
int m_ladderDir {}; // ladder move direction
bool m_isVIP {}; // bot is vip?
bool m_isAlive {}; // has the player been killed or has he just respawned
@ -700,7 +710,7 @@ public:
bool m_hasHostage {}; // does bot owns some hostages
bool m_hasProgressBar {}; // has progress bar on a HUD
bool m_jumpReady {}; // is double jump ready
bool m_canChooseAimDirection {}; // can choose aiming direction
bool m_canSetAimDirection {}; // can choose aiming direction
bool m_isEnemyReachable {}; // direct line to enemy
bool m_kickedByRotation {}; // is bot kicked due to rotation ?
bool m_kickMeFromServer {}; // kick the bot off the server?
@ -768,6 +778,7 @@ public:
void startDoubleJump (edict_t *ent);
void sendBotToOrigin (const Vector &origin);
void markStale ();
void setNewDifficulty (int32_t newDifficulty);
bool hasHostage ();
bool hasPrimaryWeapon () const;
bool hasSecondaryWeapon () const;
@ -792,7 +803,7 @@ public:
bool isDucking () const {
return !!(pev->flags & FL_DUCKING);
}
Vector getCenter () const {
return (pev->absmax + pev->absmin) * 0.5;
};
@ -942,6 +953,7 @@ extern ConVar cv_ignore_enemies_after_spawn_time;
extern ConVar cv_camping_time_min;
extern ConVar cv_camping_time_max;
extern ConVar cv_smoke_grenade_checks;
extern ConVar cv_smoke_greande_checks_radius;
extern ConVar cv_check_darkness;
extern ConVar cv_use_hitbox_enemy_targeting;
extern ConVar cv_restricted_weapons;