aim: verify camp angles from nav data before using them

aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
This commit is contained in:
jeefo 2023-04-07 14:46:49 +03:00
commit 29c00565dc
No known key found for this signature in database
GPG key ID: 927BCA0779BEA8ED
31 changed files with 1395 additions and 1305 deletions

@ -1 +1 @@
Subproject commit dade2ade585d7fbd3ac91dec93cd70c92225e832
Subproject commit 58136b52a1391af4c603f846cd8e46f7cdb4dd1f

View file

@ -26,8 +26,8 @@ typedef struct hudtextparms_s {
float x;
float y;
int effect;
uint8 r1, g1, b1, a1;
uint8 r2, g2, b2, a2;
uint8_t r1, g1, b1, a1;
uint8_t r2, g2, b2, a2;
float fadeinTime;
float fadeoutTime;
float holdTime;
@ -68,7 +68,7 @@ typedef struct usercmd_s {
vec3_t impact_position;
} usercmd_t;
typedef uint32 CRC32_t;
typedef uint32_t CRC32_t;
// Engine hands this to DLLs for functionality callbacks
@ -136,7 +136,7 @@ typedef struct enginefuncs_s {
void (*pfnCVarSetString) (const char *szVarName, const char *szValue);
void (*pfnAlertMessage) (ALERT_TYPE atype, const char *szFmt, ...);
void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...);
void *(*pfnPvAllocEntPrivateData) (edict_t *ent, int32 cb);
void *(*pfnPvAllocEntPrivateData) (edict_t *ent, int32_t cb);
void *(*pfnPvEntPrivateData) (edict_t *ent);
void (*pfnFreeEntPrivateData) (edict_t *ent);
const char *(*pfnSzFromIndex) (int stingPtr);
@ -151,8 +151,8 @@ typedef struct enginefuncs_s {
int (*pfnRegUserMsg) (const char *pszName, int iSize);
void (*pfnAnimationAutomove) (const edict_t *ent, float flTime);
void (*pfnGetBonePosition) (const edict_t *ent, int iBone, float *rgflOrigin, float *rgflAngles);
uint32 (*pfnFunctionFromName) (const char *pName);
const char *(*pfnNameForFunction) (uint32 function);
uint32_t (*pfnFunctionFromName) (const char *pName);
const char *(*pfnNameForFunction) (uint32_t function);
void (*pfnClientPrintf) (edict_t *ent, PRINT_TYPE ptype, const char *szMsg); // JOHN: engine callbacks so game DLL can print messages to individual clients
void (*pfnServerPrint) (const char *szMsg);
const char *(*pfnCmd_Args) (); // these 3 added
@ -161,14 +161,14 @@ typedef struct enginefuncs_s {
void (*pfnGetAttachment) (const edict_t *ent, int iAttachment, float *rgflOrigin, float *rgflAngles);
void (*pfnCRC32_Init) (CRC32_t *pulCRC);
void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len);
void (*pfnCRC32_ProcessByte) (CRC32_t *pulCRC, uint8 ch);
void (*pfnCRC32_ProcessByte) (CRC32_t *pulCRC, uint8_t ch);
CRC32_t (*pfnCRC32_Final) (CRC32_t pulCRC);
int32 (*pfnRandomLong) (int32 lLow, int32 lHigh);
int32_t (*pfnRandomLong) (int32_t lLow, int32_t lHigh);
float (*pfnRandomFloat) (float flLow, float flHigh);
void (*pfnSetView) (const edict_t *client, const edict_t *pViewent);
float (*pfnTime) ();
void (*pfnCrosshairAngle) (const edict_t *client, float pitch, float yaw);
uint8 *(*pfnLoadFileForMe) (char const *szFilename, int *pLength);
uint8_t *(*pfnLoadFileForMe) (char const *szFilename, int *pLength);
void (*pfnFreeFile) (void *buffer);
void (*pfnEndSection) (const char *pszSectionName); // trigger_endsection
int (*pfnCompareFileTime) (char *filename1, char *filename2, int *compare);
@ -177,7 +177,7 @@ typedef struct enginefuncs_s {
void (*pfnFadeClientVolume) (const edict_t *ent, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds);
void (*pfnSetClientMaxspeed) (const edict_t *ent, float fNewMaxspeed);
edict_t *(*pfnCreateFakeClient) (const char *netname); // returns nullptr if fake client can't be created
void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, uint16 buttons, uint8 impulse, uint8 msec);
void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, uint16_t buttons, uint8_t impulse, uint8_t msec);
int (*pfnNumberOfEntities) ();
char *(*pfnGetInfoKeyBuffer) (edict_t *e); // passing in nullptr gets the serverinfo
char *(*pfnInfoKeyValue) (char *infobuffer, char const *key);
@ -196,14 +196,14 @@ typedef struct enginefuncs_s {
const char *(*pfnGetPhysicsKeyValue) (const edict_t *client, const char *key);
void (*pfnSetPhysicsKeyValue) (const edict_t *client, const char *key, const char *value);
const char *(*pfnGetPhysicsInfoString) (const edict_t *client);
uint16 (*pfnPrecacheEvent) (int type, const char *psz);
void (*pfnPlaybackEvent) (int flags, const edict_t *pInvoker, uint16 evIndexOfEntity, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2);
uint8 *(*pfnSetFatPVS) (float *org);
uint8 *(*pfnSetFatPAS) (float *org);
int (*pfnCheckVisibility) (const edict_t *entity, uint8 *pset);
uint16_t (*pfnPrecacheEvent) (int type, const char *psz);
void (*pfnPlaybackEvent) (int flags, const edict_t *pInvoker, uint16_t evIndexOfEntity, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2);
uint8_t *(*pfnSetFatPVS) (float *org);
uint8_t *(*pfnSetFatPAS) (float *org);
int (*pfnCheckVisibility) (const edict_t *entity, uint8_t *pset);
void (*pfnDeltaSetField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaUnsetField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaAddEncoder) (char *name, void (*conditionalencode) (struct delta_s *pFields, const uint8 *from, const uint8 *to));
void (*pfnDeltaAddEncoder) (char *name, void (*conditionalencode) (struct delta_s *pFields, const uint8_t *from, const uint8_t *to));
int (*pfnGetCurrentPlayer) ();
int (*pfnCanSkipPlayer) (const edict_t *player);
int (*pfnDeltaFindField) (struct delta_s *pFields, const char *fieldname);
@ -246,7 +246,7 @@ typedef struct KeyValueData_s {
char *szClassName; // in: entity classname
char const *szKeyName; // in: name of key
char *szValue; // in: value of key
int32 fHandled; // out: DLL sets to true if key-value pair was understood
int32_t fHandled; // out: DLL sets to true if key-value pair was understood
} KeyValueData;
typedef struct customization_s customization_t;
@ -306,9 +306,9 @@ typedef struct {
void (*pfnPM_Move) (struct playermove_s *ppmove, int server);
void (*pfnPM_Init) (struct playermove_s *ppmove);
char (*pfnPM_FindTextureType) (char *name);
void (*pfnSetupVisibility) (struct edict_s *pViewEntity, struct edict_s *client, uint8 **pvs, uint8 **pas);
void (*pfnSetupVisibility) (struct edict_s *pViewEntity, struct edict_s *client, uint8_t **pvs, uint8_t **pas);
void (*pfnUpdateClientData) (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd);
int (*pfnAddToFullPack) (struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, uint8 *pSet);
int (*pfnAddToFullPack) (struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, uint8_t *pSet);
void (*pfnCreateBaseline) (int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, float *player_mins, float *player_maxs);
void (*pfnRegisterEncoders) ();
int (*pfnGetWeaponData) (struct edict_s *player, struct weapon_data_s *info);

View file

@ -56,8 +56,6 @@ public:
};
typedef cr::Vector vec3_t;
using namespace cr::types;
typedef struct edict_s edict_t;
#include "const.h"
@ -105,7 +103,7 @@ static inline int MAKE_STRING (const char *val) {
return static_cast <int> (ptrdiff);
}
#else
#define MAKE_STRING(str) ((uint64)(str) - (uint64)(STRING(0)))
#define MAKE_STRING(str) ((uint64_t)(str) - (uint64_t)(STRING(0)))
#endif
inline const char *string_t::chars (size_t shift) const {
@ -114,7 +112,7 @@ inline const char *string_t::chars (size_t shift) const {
return cr::strings.isEmpty (result) ? &cr::kNullChar : (result + shift);
}
enum HLBool : int32 {
enum HLBool : int32_t {
HLFalse, HLTrue
};

View file

@ -109,8 +109,8 @@ typedef struct entvars_s {
float frame; // % playback position in animation sequences (0..255)
float animtime; // world time when frame was set
float framerate; // animation playback rate (-8x to 8x)
uint8 controller[4]; // bone controller setting (0..255)
uint8 blending[2]; // blending amount between sub-sequences (0..255)
uint8_t controller[4]; // bone controller setting (0..255)
uint8_t blending[2]; // blending amount between sub-sequences (0..255)
float scale; // sprite rendering scale (0..255)

View file

@ -34,7 +34,7 @@ static inline int MAKE_STRING (const char *val) {
return static_cast <int> (ptrdiff);
}
#else
#define MAKE_STRING(str) ((uint64)(str) - (uint64)(STRING(0)))
#define MAKE_STRING(str) ((uint64_t)(str) - (uint64_t)(STRING(0)))
#endif
#define ENGINE_STR(str) (const_cast <char *> (STRING (engfuncs.pfnAllocString (str))))
@ -53,8 +53,8 @@ typedef struct hudtextparms_s {
float x;
float y;
int effect;
uint8 r1, g1, b1, a1;
uint8 r2, g2, b2, a2;
uint8_t r1, g1, b1, a1;
uint8_t r2, g2, b2, a2;
float fadeinTime;
float fadeoutTime;
float holdTime;

View file

@ -32,9 +32,9 @@ class BotConfig final : public Singleton <BotConfig> {
public:
struct DifficultyData {
float reaction[2] {};
int32 headshotPct {};
int32 seenThruPct {};
int32 hearThruPct {};
int32_t headshotPct {};
int32_t seenThruPct {};
int32_t hearThruPct {};
};
private:
@ -49,16 +49,16 @@ private:
StringArray m_logos {};
StringArray m_avatars {};
HashMap <uint32, String, Hash <int32>> m_language {};
HashMap <int32, DifficultyData> m_difficulty {};
HashMap <uint32_t, String, Hash <int32_t>> m_language {};
HashMap <int32_t, DifficultyData> m_difficulty {};
HashMap <String, String> m_custom {};
// default tables for personality weapon preferences, overridden by weapon.cfg
SmallArray <int32> m_normalWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16 };
SmallArray <int32> m_rusherWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 24, 19, 22, 23, 20, 21, 10, 12, 13, 7, 8, 11, 9, 18, 17, 19, 25, 15, 16 };
SmallArray <int32> m_carefulWeaponPrefs = { 0, 2, 1, 4, 25, 6, 3, 7, 8, 12, 10, 13, 11, 9, 24, 18, 14, 17, 16, 15, 19, 20, 21, 22, 23, 5 };
SmallArray <int32> m_botBuyEconomyTable = { 1900, 2100, 2100, 4000, 6000, 7000, 16000, 1200, 800, 1000, 3000 };
SmallArray <int32> m_grenadeBuyPrecent = { 95, 85, 60 };
SmallArray <int32_t> m_normalWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16 };
SmallArray <int32_t> m_rusherWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 24, 19, 22, 23, 20, 21, 10, 12, 13, 7, 8, 11, 9, 18, 17, 19, 25, 15, 16 };
SmallArray <int32_t> m_carefulWeaponPrefs = { 0, 2, 1, 4, 25, 6, 3, 7, 8, 12, 10, 13, 11, 9, 24, 18, 14, 17, 16, 15, 19, 20, 21, 22, 23, 5 };
SmallArray <int32_t> m_botBuyEconomyTable = { 1900, 2100, 2100, 4000, 6000, 7000, 16000, 1200, 800, 1000, 3000 };
SmallArray <int32_t> m_grenadeBuyPrecent = { 95, 85, 60 };
public:
BotConfig ();
@ -138,7 +138,7 @@ private:
};
// hash the lang string, only the letters
uint32 hashLangString (StringRef str);
uint32_t hashLangString (StringRef str);
public:
@ -188,7 +188,7 @@ public:
}
// get's weapons type by id
int32 getWeaponType (int id) const {
int32_t getWeaponType (int id) const {
for (const auto &weapon : m_weapons) {
if (weapon.id == id) {
return weapon.type;
@ -198,7 +198,7 @@ public:
}
// get's weapon preferences for personality
int32 *getWeaponPrefs (int personality) const {
int32_t *getWeaponPrefs (int personality) const {
switch (personality) {
case Personality::Normal:
default:
@ -213,7 +213,7 @@ public:
}
// get's the difficulty level tweaks
DifficultyData *getDifficultyTweaks (int32 level) {
DifficultyData *getDifficultyTweaks (int32_t level) {
if (level < Difficulty::Noob || level > Difficulty::Expert) {
return &m_difficulty[Difficulty::Expert];
}
@ -221,7 +221,7 @@ public:
}
// get economics value
int32 *getEconLimit () {
int32_t *getEconLimit () {
return m_botBuyEconomyTable.data ();
}
@ -239,8 +239,8 @@ public:
}
// get's random logo index
int32 getRandomLogoIndex () const {
return static_cast <int32> (m_logos.index (m_logos.random ()));
int32_t getRandomLogoIndex () const {
return static_cast <int32_t> (m_logos.index (m_logos.random ()));
}
// get random name by index

View file

@ -52,13 +52,13 @@ public:
// queued text message to prevent overflow with rapid output
struct PrintQueue {
int32 destination {};
int32_t destination {};
String text;
public:
explicit PrintQueue () = default;
PrintQueue (int32 destination, StringRef text) : destination (destination), text (text)
PrintQueue (int32_t destination, StringRef text) : destination (destination), text (text)
{ }
};

View file

@ -75,14 +75,14 @@ struct ConVarReg {
float initial, min, max;
bool missing;
bool bounded;
int32 type;
int32_t type;
};
// entity prototype
using EntityFunction = void (*) (entvars_t *);
// rehlds has this fixed, but original hlds doesn't allocate string space passed to precache* argument, so game will crash when unloading module using metamod
class EngineWrap final : public DenyCopying {
class EngineWrap final {
public:
EngineWrap () = default;
~EngineWrap () = default;
@ -93,11 +93,11 @@ private:
}
public:
int32 precacheModel (const char *model) const {
int32_t precacheModel (const char *model) const {
return engfuncs.pfnPrecacheModel (allocStr (model));
}
int32 precacheSound (const char *sound) const {
int32_t precacheSound (const char *sound) const {
return engfuncs.pfnPrecacheSound (allocStr (sound));
}
@ -182,7 +182,7 @@ public:
void prepareBotArgs (edict_t *ent, String str);
// adds cvar to registration stack
void addNewCvar (const char *name, const char *value, const char *info, bool bounded, float min, float max, int32 varType, bool missingAction, const char *regval, class ConVar *self);
void addNewCvar (const char *name, const char *value, const char *info, bool bounded, float min, float max, int32_t varType, bool missingAction, const char *regval, class ConVar *self);
// check the cvar bounds
void checkCvarsBounds ();
@ -240,7 +240,7 @@ public:
}
// gets custom engine argv for client command
const char *botArgv (int32 index) const {
const char *botArgv (int32_t index) const {
if (static_cast <size_t> (index) >= m_botArgs.length ()) {
return "";
}
@ -248,8 +248,8 @@ public:
}
// gets custom engine argc for client command
int32 botArgc () const {
return m_botArgs.length <int32> ();
int32_t botArgc () const {
return m_botArgs.length <int32_t> ();
}
// gets edict pointer out of entity index
@ -314,10 +314,10 @@ public:
void setPlayerStartDrawModels ();
// check the engine visibility wrapper
bool checkVisibility (edict_t *ent, uint8 *set);
bool checkVisibility (edict_t *ent, uint8_t *set);
// get pvs/pas visibility set
uint8 *getVisibilitySet (Bot *bot, bool pvs);
uint8_t *getVisibilitySet (Bot *bot, bool pvs);
// what kind of game engine / game dll / mod / tool we're running ?
bool is (const int type) const {
@ -409,11 +409,11 @@ public:
~ConVar () = default;
public:
ConVar (const char *name, const char *initval, int32 type = Var::NoServer, bool regMissing = false, const char *regVal = nullptr) : ptr (nullptr) {
ConVar (const char *name, const char *initval, int32_t type = Var::NoServer, bool regMissing = false, const char *regVal = nullptr) : ptr (nullptr) {
Game::instance ().addNewCvar (name, initval, "", false, 0.0f, 0.0f, type, regMissing, regVal, this);
}
ConVar (const char *name, const char *initval, const char *info, bool bounded = true, float min = 0.0f, float max = 1.0f, int32 type = Var::NoServer, bool regMissing = false, const char *regVal = nullptr) : ptr (nullptr) {
ConVar (const char *name, const char *initval, const char *info, bool bounded = true, float min = 0.0f, float max = 1.0f, int32_t type = Var::NoServer, bool regMissing = false, const char *regVal = nullptr) : ptr (nullptr) {
Game::instance ().addNewCvar (name, initval, info, bounded, min, max, type, regMissing, regVal, this);
}
@ -505,8 +505,8 @@ public:
}
public:
static inline uint16 fu16 (float value, float scale) {
return cr::clamp <uint16> (static_cast <uint16> (value * cr::bit (static_cast <short> (scale))), 0, USHRT_MAX);
static inline uint16_t fu16 (float value, float scale) {
return cr::clamp <uint16_t> (static_cast <uint16_t> (value * cr::bit (static_cast <short> (scale))), 0, USHRT_MAX);
}
static inline short fs16 (float value, float scale) {
@ -517,7 +517,7 @@ public:
class LightMeasure final : public Singleton <LightMeasure> {
private:
lightstyle_t m_lightstyle[MAX_LIGHTSTYLES] {};
uint32 m_lightstyleValue[MAX_LIGHTSTYLEVALUE] {};
uint32_t m_lightstyleValue[MAX_LIGHTSTYLEVALUE] {};
bool m_doAnimation = false;
Color m_point;
@ -563,11 +563,11 @@ public:
// simple handler for parsing and rewriting queries (fake queries)
class QueryBuffer {
SmallArray <uint8> m_buffer {};
SmallArray <uint8_t> m_buffer {};
size_t m_cursor {};
public:
QueryBuffer (const uint8 *msg, size_t length, size_t shift) : m_cursor (0) {
QueryBuffer (const uint8_t *msg, size_t length, size_t shift) : m_cursor (0) {
m_buffer.insert (0, msg, length);
m_cursor += shift;
}
@ -630,7 +630,7 @@ public:
}
public:
Twin <const uint8 *, size_t> data () {
Twin <const uint8_t *, size_t> data () {
return { m_buffer.data (), m_buffer.length () };
}
};

View file

@ -25,7 +25,7 @@ CR_DECLARE_SCOPED_ENUM (NodeFlag,
)
// defines for node connection flags field (16 bits are available)
CR_DECLARE_SCOPED_ENUM_TYPE (PathFlag, uint16,
CR_DECLARE_SCOPED_ENUM_TYPE (PathFlag, uint16_t,
Jump = cr::bit (0) // must jump for this connection
)
@ -112,59 +112,59 @@ struct Route {
// general stprage header information structure
struct StorageHeader {
int32 magic;
int32 version;
int32 options;
int32 length;
int32 compressed;
int32 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 mapSize; // bsp size for checksumming map consistency
int32_t mapSize; // bsp size for checksumming map consistency
char modified[32]; // by whom modified
};
// general waypoint header information structure
struct PODGraphHeader {
char header[8];
int32 fileVersion;
int32 pointNumber;
int32_t fileVersion;
int32_t pointNumber;
char mapName[32];
char author[32];
};
// floyd-warshall matrices
struct Matrix {
int16 dist;
int16 index;
int16_t dist;
int16_t index;
};
// experience data hold in memory while playing
struct Practice {
int16 damage[kGameTeamNum];
int16 index[kGameTeamNum];
int16 value[kGameTeamNum];
int16_t damage[kGameTeamNum];
int16_t index[kGameTeamNum];
int16_t value[kGameTeamNum];
};
// defines linked waypoints
struct PathLink {
Vector velocity;
int32 distance;
uint16 flags;
int16 index;
int32_t distance;
uint16_t flags;
int16_t index;
};
// defines visibility count
struct PathVis {
uint16 stand, crouch;
uint16_t stand, crouch;
};
// define graph path structure for yapb
struct Path {
int32 number, flags;
int32_t number, flags;
Vector origin, start, end;
float radius, light, display;
PathLink links[kMaxNodeLinks];
@ -173,13 +173,13 @@ struct Path {
// define waypoint structure for podbot (will convert on load)
struct PODPath {
int32 number, flags;
int32_t number, flags;
Vector origin;
float radius, csx, csy, cex, cey;
int16 index[kMaxNodeLinks];
uint16 conflags[kMaxNodeLinks];
int16_t index[kMaxNodeLinks];
uint16_t conflags[kMaxNodeLinks];
Vector velocity[kMaxNodeLinks];
int32 distance[kMaxNodeLinks];
int32_t distance[kMaxNodeLinks];
PathVis vis;
};
@ -189,26 +189,26 @@ private:
size_t m_cursor {};
size_t m_length {};
UniquePtr <int32[]> m_path {};
UniquePtr <int32_t[]> m_path {};
public:
explicit PathWalk () = default;
~PathWalk () = default;
public:
int32 &next () {
int32_t &next () {
return at (1);
}
int32 &first () {
int32_t &first () {
return at (0);
}
int32 &last () {
int32_t &last () {
return at (length () - 1);
}
int32 &at (size_t index) {
int32_t &at (size_t index) {
return m_path[m_cursor + index];
}
@ -237,7 +237,7 @@ public:
return !length ();
}
void add (int32 node) {
void add (int32_t node) {
m_path[m_length++] = node;
}
@ -249,7 +249,7 @@ public:
}
void init (size_t length) {
m_path = cr::makeUnique <int32 []> (length);
m_path = cr::makeUnique <int32_t []> (length);
}
};
@ -297,11 +297,11 @@ private:
IntArray m_rescuePoints {};
IntArray m_visitedGoals {};
SmallArray <int32> m_buckets[kMaxBucketsInsidePos][kMaxBucketsInsidePos][kMaxBucketsInsidePos];
SmallArray <int32_t> m_buckets[kMaxBucketsInsidePos][kMaxBucketsInsidePos][kMaxBucketsInsidePos];
SmallArray <Matrix> m_matrix {};
SmallArray <Practice> m_practice {};
SmallArray <Path> m_paths {};
SmallArray <uint8> m_vistable {};
SmallArray <uint8_t> m_vistable {};
String m_graphAuthor {};
String m_graphModified {};
@ -346,7 +346,7 @@ public:
bool canDownload ();
template <typename U> bool saveStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, const SmallArray <U> &data, ExtenHeader *exten);
template <typename U> bool loadStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, SmallArray <U> &data, ExtenHeader *exten, int32 *outOptions);
template <typename U> bool loadStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, SmallArray <U> &data, ExtenHeader *exten, int32_t *outOptions);
template <typename ...Args> bool raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args);
void saveOldFormat ();
@ -396,7 +396,7 @@ public:
Bucket locateBucket (const Vector &pos);
IntArray searchRadius (float radius, const Vector &origin, int maxCount = -1);
const SmallArray <int32> &getNodesInBucket (const Vector &pos);
const SmallArray <int32_t> &getNodesInBucket (const Vector &pos);
public:
size_t getMaxRouteLength () const {
@ -446,12 +446,12 @@ public:
// check nodes range
bool exists (int index) const {
return index >= 0 && index < static_cast <int> (m_paths.length ());
return index >= 0 && index < length ();
}
// get real nodes num
int32 length () const {
return m_paths.length <int32> ();
int32_t length () const {
return m_paths.length <int32_t> ();
}
// check if has editor

View file

@ -25,7 +25,7 @@ private:
public:
float MaxView = 4096.0f;
float MinView = 5.0f;
float MinView = 2.0f;
public:
float farHeight; // height of the far frustum
@ -108,6 +108,7 @@ public:
int getHumansCount (bool ignoreSpectators = false);
int getAliveHumansCount ();
int getPlayerPriority (edict_t *ent);
float getConnectTime (StringRef name, float original);
float getAverageTeamKPD (bool calcForBots);
@ -174,12 +175,12 @@ public:
return m_economicsGood[team];
}
int32 getLastWinner () const {
int32_t getLastWinner () const {
return m_lastWinner;
}
int32 getBotCount () {
return m_bots.length <int32> ();
int32_t getBotCount () {
return m_bots.length <int32_t> ();
}
// get the list of filters

View file

@ -65,27 +65,27 @@ CR_DECLARE_SCOPED_ENUM (StatusIconCache,
class MessageDispatcher final : public Singleton <MessageDispatcher> {
private:
using MsgFunc = void (MessageDispatcher::*) ();
using MsgHash = Hash <int32>;
using MsgHash = Hash <int32_t>;
private:
struct Args {
union {
float float_;
int32 long_;
int32_t long_;
const char *chars_;
};
public:
Args (float value) : float_ (value) { }
Args (int32 value) : long_ (value) { }
Args (int32_t value) : long_ (value) { }
Args (const char *value) : chars_ (value) { }
};
private:
HashMap <String, int32> m_textMsgCache {}; // cache strings for faster access for textmsg
HashMap <String, int32> m_showMenuCache {}; // cache for the showmenu message
HashMap <String, int32> m_statusIconCache {}; // cache for status icon message
HashMap <String, int32> m_teamInfoCache {}; // cache for teaminfo message
HashMap <String, int32_t> m_textMsgCache {}; // cache strings for faster access for textmsg
HashMap <String, int32_t> m_showMenuCache {}; // cache for the showmenu message
HashMap <String, int32_t> m_statusIconCache {}; // cache for status icon message
HashMap <String, int32_t> m_teamInfoCache {}; // cache for teaminfo message
private:
Bot *m_bot {}; // owner of a message
@ -94,9 +94,9 @@ private:
SmallArray <Args> m_args {}; // args collected from write* functions
HashMap <String, NetMsg> m_wanted {}; // wanted messages
HashMap <int32, NetMsg> m_reverseMap {}; // maps engine message id to our message id
HashMap <int32_t, NetMsg> m_reverseMap {}; // maps engine message id to our message id
HashMap <NetMsg, int32, MsgHash> m_maps {}; // maps our message to id to engine message id
HashMap <NetMsg, int32_t, MsgHash> m_maps {}; // maps our message to id to engine message id
HashMap <NetMsg, MsgFunc, MsgHash> m_handlers {}; // maps our message id to handler function
private:
@ -122,17 +122,17 @@ private:
void netMsgScoreAttrib ();
private:
Bot *pickBot (int32 index);
Bot *pickBot (int32_t index);
public:
MessageDispatcher ();
~MessageDispatcher () = default;
public:
int32 add (StringRef name, int32 id);
int32 id (NetMsg msg);
int32_t add (StringRef name, int32_t id);
int32_t id (NetMsg msg);
void start (edict_t *ent, int32 type);
void start (edict_t *ent, int32_t type);
void stop ();
void ensureMessages ();

40
inc/sounds.h Normal file
View file

@ -0,0 +1,40 @@
//
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
// Copyright © 2004-2023 YaPB Project <yapb@jeefo.net>.
//
// SPDX-License-Identifier: MIT
//
#pragma once
// noise types
CR_DECLARE_SCOPED_ENUM (Noise,
NeedHandle = cr::bit (0),
HitFall = cr::bit (1),
Pickup = cr::bit (2),
Zoom = cr::bit (3),
Ammo = cr::bit (4),
Hostage = cr::bit (5),
Broke = cr::bit (6),
Door = cr::bit (7),
Defuse = cr::bit (8)
)
class BotSounds final : public Singleton <BotSounds> {
private:
HashMap <String, int32_t> m_noiseCache {};
public:
BotSounds ();
~BotSounds () = default;
public:
// attaches sound to client struct
void listenNoise (edict_t *ent, StringRef sample, float volume);
// simulate sound for players
void simulateNoise (int playerIndex);
};
// explose global
CR_EXPOSE_GLOBAL_SINGLETON (BotSounds, sounds);

View file

@ -7,18 +7,6 @@
#pragma once
// noise types
CR_DECLARE_SCOPED_ENUM (Noise,
NeedHandle = cr::bit (0),
HitFall = cr::bit (1),
Pickup = cr::bit (2),
Zoom = cr::bit (3),
Ammo = cr::bit (4),
Hostage = cr::bit (5),
Broke = cr::bit (6),
Door = cr::bit (7)
)
class BotSupport final : public Singleton <BotSupport> {
private:
bool m_needToSendWelcome {};
@ -28,8 +16,7 @@ private:
SmallArray <Client> m_clients {};
SmallArray <Twin <String, String>> m_tags {};
HashMap <int32, String> m_weaponAlias {};
HashMap <String, int32> m_noiseCache {};
HashMap <int32_t, String> m_weaponAlias {};
Detour <decltype (sendto)> m_sendToDetour { "ws2_32.dll", "sendto", sendto };
public:
@ -41,7 +28,7 @@ public:
void checkWelcome ();
// converts weapon id to alias name
StringRef weaponIdToAlias (int32 id);
StringRef weaponIdToAlias (int32_t id);
// check if origin is visible from the entity side
bool isVisible (const Vector &origin, edict_t *ent);
@ -73,12 +60,6 @@ public:
// tracing decals for bots spraying logos
void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex);
// attaches sound to client struct
void listenNoise (edict_t *ent, StringRef sample, float volume);
// simulate sound for players
void simulateNoise (int playerIndex);
// update stats on clients
void updateClients ();
@ -153,7 +134,7 @@ public:
}
public:
static int32 CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength);
static int32_t CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength);
};
// explose global

View file

@ -46,6 +46,7 @@ CR_DECLARE_SCOPED_ENUM (Task,
Hide,
Blind,
Spraypaint,
Max
)
// bot menu ids
@ -349,7 +350,7 @@ CR_DECLARE_SCOPED_ENUM (Reload,
)
// collision probes
CR_DECLARE_SCOPED_ENUM (CollisionProbe, uint32,
CR_DECLARE_SCOPED_ENUM (CollisionProbe, uint32_t,
Jump = cr::bit (0), // probe jump when colliding
Duck = cr::bit (1), // probe duck when colliding
Strafe = cr::bit (2) // probe strafing when colliding
@ -367,7 +368,7 @@ CR_DECLARE_SCOPED_ENUM (BotMsg,
)
// sensing states
CR_DECLARE_SCOPED_ENUM_TYPE (Sense, uint32,
CR_DECLARE_SCOPED_ENUM_TYPE (Sense, uint32_t,
SeeingEnemy = cr::bit (0), // seeing an enemy
HearingEnemy = cr::bit (1), // hearing an enemy
SuspectEnemy = cr::bit (2), // suspect enemy behind obstacle
@ -378,7 +379,7 @@ CR_DECLARE_SCOPED_ENUM_TYPE (Sense, uint32,
)
// positions to aim at
CR_DECLARE_SCOPED_ENUM_TYPE (AimFlags, uint32,
CR_DECLARE_SCOPED_ENUM_TYPE (AimFlags, uint32_t,
Nav = cr::bit (0), // aim at nav point
Camp = cr::bit (1), // aim at camp vector
PredictPath = cr::bit (2), // aim at predicted path
@ -445,11 +446,11 @@ namespace TaskPri {
// storage file magic
constexpr char kPodbotMagic[8] = "PODWAY!";
constexpr int32 kStorageMagic = 0x59415042; // storage magic for yapb-data files
constexpr int32 kStorageMagicUB = 0x544f4255; //support also the fork format (merged back into yapb)
constexpr int32_t kStorageMagic = 0x59415042; // storage magic for yapb-data files
constexpr int32_t kStorageMagicUB = 0x544f4255; //support also the fork format (merged back into yapb)
constexpr float kInfiniteDistance = 9999999.0f;
constexpr float kGrenadeCheckTime = 2.15f;
constexpr float kGrenadeCheckTime = 0.6f;
constexpr float kSprayDistance = 260.0f;
constexpr float kDoubleSprayDistance = kSprayDistance * 2;
constexpr float kMaxChatterRepeatInterval = 99.0f;
@ -610,12 +611,12 @@ public:
friend class BotManager;
private:
uint32 m_states {}; // sensing bitstates
uint32 m_collideMoves[kMaxCollideMoves] {}; // sorted array of movements
uint32 m_collisionProbeBits {}; // bits of possible collision moves
uint32 m_collStateIndex {}; // index into collide moves
uint32 m_aimFlags {}; // aiming conditions
uint32 m_currentTravelFlags {}; // connection flags like jumping
uint32_t m_states {}; // sensing bitstates
uint32_t m_collideMoves[kMaxCollideMoves] {}; // sorted array of movements
uint32_t m_collisionProbeBits {}; // bits of possible collision moves
uint32_t m_collStateIndex {}; // index into collide moves
uint32_t m_aimFlags {}; // aiming conditions
uint32_t m_currentTravelFlags {}; // connection flags like jumping
int m_traceSkip[TraceChannel::Num] {}; // trace need to be skipped?
int m_messageQueue[32] {}; // stack for messages
@ -688,6 +689,7 @@ private:
float m_playServerTime {}; // time bot spent in the game
float m_changeViewTime {}; // timestamp to change look at while at freezetime
float m_breakableTime {}; // breakeble acquired time
float m_jumpDistance {}; // last jump distance
bool m_moveToGoal {}; // bot currently moving to goal??
bool m_isStuck {}; // bot is stuck
@ -706,6 +708,7 @@ private:
bool m_checkTerrain {}; // check for terrain
bool m_moveToC4 {}; // ct is moving to bomb
bool m_grenadeRequested {}; // bot requested change to grenade
bool m_needToSendWelcomeChat {}; // bot needs to greet people on server?
Pickup m_pickupType {}; // type of entity which needs to be used/picked up
PathWalk m_pathWalk {}; // pointer to current node from path
@ -713,7 +716,7 @@ private:
Fight m_fightStyle {}; // combat style to use
CollisionState m_collisionState {}; // collision State
FindPath m_pathType {}; // which pathfinder to use
uint8 m_enemyParts {}; // visibility flags
uint8_t m_enemyParts {}; // visibility flags
TraceResult m_lastTrace[TraceChannel::Num] {}; // last trace result
edict_t *m_pickupItem {}; // pointer to entity of item to use/pickup
@ -723,6 +726,7 @@ private:
edict_t *m_lastBreakable {}; // last acquired breakable
edict_t *m_targetEntity {}; // the entity that the bot is trying to reach
edict_t *m_avoidGrenade {}; // pointer to grenade entity to avoid
edict_t *m_hindrance {}; // the hidrance
Vector m_liftTravelPos {}; // lift travel position
Vector m_moveAngles {}; // bot move angles
@ -744,7 +748,7 @@ private:
Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
Array <edict_t *> m_hostages {}; // pointer to used hostage entities
Array <Route> m_routes {}; // pointer
Array <int32> m_goalHistory {}; // history of selected goals
Array <int32_t> m_nodeHistory {}; // history of selected goals
BinaryHeap <RouteTwin> m_routeQue {};
Path *m_path {}; // pointer to the current path node
@ -753,7 +757,7 @@ private:
private:
int pickBestWeapon (int *vec, int count, int moneySave);
int findCampingDirection ();
int getRandomCampDir ();
int findAimingNode (const Vector &to, int &pathLength);
int findNearestNode ();
int findBombNode ();
@ -766,12 +770,12 @@ private:
int bestSecondaryCarried ();
int bestGrenadeCarried ();
int bestWeaponCarried ();
int changePointIndex (int index);
int changeNodeIndex (int index);
int numEnemiesNear (const Vector &origin, float radius);
int numFriendsNear (const Vector &origin, float radius);
float getBombTimeleft ();
float getReachTime ();
float getEstimatedNodeReachTime ();
float isInFOV (const Vector &dest);
float getShiftSpeed ();
float getEnemyBodyOffsetCorrection (float distance);
@ -795,7 +799,6 @@ private:
bool isInViewCone (const Vector &origin);
bool checkBodyParts (edict_t *target);
bool seesEnemy (edict_t *player, bool ignoreFOV = false);
bool doPlayerAvoidance (const Vector &normal);
bool hasActiveGoal ();
bool advanceMovement ();
bool isBombDefusing (const Vector &bombOrigin);
@ -828,6 +831,8 @@ private:
bool updateLiftStates ();
bool canRunHeavyWeight ();
void doPlayerAvoidance (const Vector &normal);
void selectCampButtons (int index);
void markStale ();
void instantChatter (int type);
void update ();
@ -851,10 +856,10 @@ private:
void updateHearing ();
void postprocessGoals (const IntArray &goals, int result[]);
void updatePickups ();
void ensureEntitiesClear ();
void checkTerrain (float movedDistance, const Vector &dirNormal);
void checkDarkness ();
void checkParachute ();
void getCampDirection (Vector *dest);
void updatePracticeValue (int damage);
void updatePracticeDamage (edict_t *attacker, int damage);
void findShortestPath (int srcIndex, int destIndex);
@ -880,13 +885,15 @@ private:
void focusEnemy ();
void selectBestWeapon ();
void selectSecondary ();
void selectWeaponByName (StringRef name);
void selectWeaponById (int id);
void selectWeaponByIndex (int index);
void completeTask ();
void tasks ();
void executeTasks ();
void trackEnemies ();
void choiceFreezetimeEntity ();
void logicDuringFreezetime ();
void translateInput ();
void moveToGoal ();
void normal_ ();
void spraypaint_ ();
@ -911,15 +918,16 @@ private:
edict_t *lookupButton (const char *target);
edict_t *lookupBreakable ();
edict_t *correctGrenadeVelocity (const char *model);
edict_t *setCorrectGrenadeVelocity (const char *model);
const Vector &getEnemyBodyOffset ();
Vector calcThrow (const Vector &start, const Vector &stop);
Vector calcToss (const Vector &start, const Vector &stop);
Vector isBombAudible ();
Vector getBodyOffsetError (float distance);
Vector getCampDirection (const Vector &dest);
uint8 computeMsec ();
uint8_t computeMsec ();
private:
bool isOnLadder () const {
@ -927,7 +935,7 @@ private:
}
bool isOnFloor () const {
return (pev->flags & (FL_ONGROUND | FL_PARTIALGROUND)) != 0;
return !!(pev->flags & (FL_ONGROUND | FL_PARTIALGROUND));
}
bool isInWater () const {
@ -938,6 +946,10 @@ private:
return (m_pathFlags & NodeFlag::Narrow);
}
void dropCurrentWeapon () {
issueCommand ("drop");
}
public:
entvars_t *pev {};
@ -1044,7 +1056,7 @@ public:
BurstMode m_weaponBurstMode {}; // bot using burst mode? (famas/glock18, but also silencer mode)
Personality m_personality {}; // bots type
Array <BotTask> m_tasks {};
Deque <int32> m_msgQueue {};
Deque <int32_t> m_msgQueue {};
public:
Bot (edict_t *bot, int difficulty, int personality, int team, int skin);
@ -1097,7 +1109,7 @@ public:
bool hasSecondaryWeapon ();
bool hasShield ();
bool isShieldDrawn ();
bool findBestNearestNode ();
bool findNextBestNode ();
bool seesEntity (const Vector &dest, bool fromBody = false);
bool canSkipNextTrace (TraceChannel channel);
@ -1105,7 +1117,6 @@ public:
int getAmmo (int id);
int getNearestToPlantedBomb ();
float getFrameInterval ();
float getConnectionTime ();
BotTask *getTask ();
@ -1114,7 +1125,9 @@ public:
return m_ammoInClip[m_currentWeapon];
}
bool isLowOnAmmo (const int index, const float factor) const;
bool isDucking () const {
return !!(pev->flags & FL_DUCKING);
}
Vector getCenter () const {
return (pev->absmax + pev->absmin) * 0.5;
@ -1157,6 +1170,9 @@ public:
return m_lastTrace[channel];
}
// is low on admmo on index?
bool isLowOnAmmo (const int index, const float factor) const;
// prints debug message
template <typename ...Args> void debugMsg (const char *fmt, Args &&...args) {
debugMsgInternal (strings.format (fmt, cr::forward <Args> (args)...));
@ -1168,6 +1184,7 @@ public:
#include "config.h"
#include "support.h"
#include "sounds.h"
#include "message.h"
#include "engine.h"
#include "manager.h"

View file

@ -230,6 +230,7 @@ sources = files (
'src/module.cpp',
'src/message.cpp',
'src/navigate.cpp',
'src/sounds.cpp',
'src/support.cpp'
)

File diff suppressed because it is too large Load diff

View file

@ -54,7 +54,7 @@ void BotSupport::addChatErrors (String &line) {
if (rg.chance (8) && strcmp (cv_language.str (), "en") == 0) {
line.lowercase ();
}
auto length = static_cast <int32> (line.length ());
auto length = static_cast <int32_t> (line.length ());
if (length > 15) {
auto percentile = length / 2;

View file

@ -14,6 +14,7 @@ ConVar cv_check_enemy_invincibility ("yb_check_enemy_invincibility", "0", "Enabl
ConVar cv_stab_close_enemies ("yb_stab_close_enemies", "1", "Enables or disables bot ability to stab the enemy with knife if bot is in good condition.");
ConVar mp_friendlyfire ("mp_friendlyfire", nullptr, Var::GameRef);
ConVar sv_gravity ("sv_gravity", nullptr, Var::GameRef);
int Bot::numFriendsNear (const Vector &origin, float radius) {
int count = 0;
@ -133,17 +134,15 @@ bool Bot::checkBodyParts (edict_t *target) {
}
// check top of head
if (util.isPlayer (target)) {
spot.z += 25.0f;
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
spot.z += 25.0f;
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
if (result.flFraction >= 1.0f) {
m_enemyParts |= Visibility::Head;
m_enemyOrigin = result.vecEndPos;
}
if (result.flFraction >= 1.0f) {
m_enemyParts |= Visibility::Head;
m_enemyOrigin = result.vecEndPos;
}
if (m_enemyParts != 0) {
if (m_enemyParts != Visibility::None) {
return true;
}
@ -197,10 +196,6 @@ bool Bot::seesEnemy (edict_t *player, bool ignoreFOV) {
return false;
}
if (m_difficulty == Difficulty::Expert && m_kpdRatio < 1.5f && util.isPlayer (pev->dmg_inflictor) && game.getTeam (pev->dmg_inflictor) != m_team) {
ignoreFOV = true;
}
if ((ignoreFOV || isInViewCone (player->v.origin)) && isEnemyInFrustum (player) && checkBodyParts (player)) {
m_seeEnemyTime = game.time ();
m_lastEnemy = player;
@ -276,7 +271,7 @@ bool Bot::lookupEnemies () {
float scaleFactor = (1.0f / calculateScaleFactor (intresting));
float distance = intresting->v.origin.distanceSq (pev->origin) * scaleFactor;
if (distance < nearestDistance) {
if (distance * 0.65f < nearestDistance) {
nearestDistance = distance;
newEnemy = intresting;
}
@ -309,7 +304,7 @@ bool Bot::lookupEnemies () {
}
float distance = player->v.origin.distanceSq (pev->origin);
if (distance < nearestDistance) {
if (distance * 0.65f < nearestDistance) {
nearestDistance = distance;
newEnemy = player;
@ -320,7 +315,7 @@ bool Bot::lookupEnemies () {
}
}
}
m_enemyUpdateTime = cr::clamp (game.time () + getFrameInterval () * 25.0f, 0.5f, 0.75f);
m_enemyUpdateTime = cr::clamp (game.time () + m_frameInterval * 25.0f, 0.5f, 0.75f);
if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) {
newEnemy = shieldEnemy;
@ -349,18 +344,7 @@ bool Bot::lookupEnemies () {
pushRadioMessage (Radio::EnemySpotted);
}
m_targetEntity = nullptr; // stop following when we see an enemy...
if (m_difficulty == Difficulty::Expert) {
m_enemySurpriseTime = m_actualReactionTime * 0.5f;
}
else {
m_enemySurpriseTime = m_actualReactionTime;
}
if (usesSniper ()) {
m_enemySurpriseTime *= 0.5f;
}
m_enemySurpriseTime += game.time ();
m_enemySurpriseTime = game.time () + m_actualReactionTime;
// zero out reaction time
m_actualReactionTime = 0.0f;
@ -461,7 +445,7 @@ Vector Bot::getBodyOffsetError (float distance) {
}
if (m_aimErrorTime < game.time ()) {
const float error = distance / (cr::clamp (static_cast <float> (m_difficulty), 1.0f, 3.0f) * 1000.0f);
const float error = distance / (cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 1000.0f);
auto &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins;
m_aimLastError = Vector (rg.get (mins.x * error, maxs.x * error), rg.get (mins.y * error, maxs.y * error), rg.get (mins.z * error, maxs.z * error));
@ -474,10 +458,6 @@ const Vector &Bot::getEnemyBodyOffset () {
// the purpose of this function, is to make bot aiming not so ideal. it's mutate m_enemyOrigin enemy vector
// returned from visibility check function.
const auto headOffset = [] (edict_t *e) {
return e->v.absmin.z + e->v.size.z * 0.81f;
};
// if no visibility data, use last one
if (!m_enemyParts) {
return m_enemyOrigin;
@ -485,7 +465,7 @@ const Vector &Bot::getEnemyBodyOffset () {
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)) {
if ((m_enemyParts & Visibility::Body) && !usesSniper () && distance > (m_difficulty >= Difficulty::Normal ? 2000.0f : 1000.0f)) {
m_enemyParts &= ~Visibility::Head;
}
@ -493,18 +473,21 @@ const Vector &Bot::getEnemyBodyOffset () {
else if (distance < 800.0f && usesSniper ()) {
m_enemyParts &= ~Visibility::Head;
}
else if (distance < kSprayDistance / 2 && !usesPistol ()) {
m_enemyParts &= ~Visibility::Head;
}
Vector aimPos = m_enemy->v.origin;
if (m_difficulty > Difficulty::Normal) {
aimPos += (m_enemy->v.velocity - pev->velocity) * (getFrameInterval () * 2.0f);
Vector spot = m_enemy->v.origin;
Vector compensation = 1.0f * m_frameInterval * m_enemy->v.velocity - 1.0f * m_frameInterval * pev->velocity;
if (!usesSniper () && distance > kDoubleSprayDistance) {
compensation *= m_frameInterval;
compensation.z = 0.0f;
}
else {
compensation = nullptr;
}
// if we only suspect an enemy behind a wall take the worst skill
if (!m_enemyParts && (m_states & Sense::SuspectEnemy)) {
aimPos += getBodyOffsetError (distance);
spot += getBodyOffsetError (distance);
}
else if (util.isPlayer (m_enemy)) {
const float highOffset = m_kpdRatio < 1.0f ? 1.5f : 0.0f;
@ -522,29 +505,29 @@ const Vector &Bot::getEnemyBodyOffset () {
// now check is our skill match to aim at head, else aim at enemy body
if (rg.chance (headshotPct)) {
if (onLoosingStreak) {
aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance);
spot = m_enemyOrigin + Vector (0.0f, 0.0f, getEnemyBodyOffsetCorrection (distance));
}
else {
aimPos = m_enemy->v.origin + m_enemy->v.view_ofs;
spot = m_enemyOrigin;
}
}
else {
aimPos.z += highOffset;
spot.z += highOffset;
}
}
else if (m_enemyParts & Visibility::Body) {
aimPos.z += highOffset;
spot = m_enemyOrigin + Vector (0.0f, 0.0f, getEnemyBodyOffsetCorrection (distance));
}
else if (m_enemyParts & Visibility::Other) {
aimPos = m_enemyOrigin;
spot = m_enemyOrigin;
}
else if (m_enemyParts & Visibility::Head) {
aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance);
spot = m_enemyOrigin + Vector (0.0f, 0.0f, getEnemyBodyOffsetCorrection (distance));
}
}
m_enemyOrigin = aimPos;
m_lastEnemyOrigin = aimPos;
m_enemyOrigin = spot + compensation;
m_lastEnemyOrigin = spot + compensation;
// add some error to unskilled bots
if (m_difficulty < Difficulty::Hard) {
@ -559,19 +542,19 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
};
static float offsetRanges[9][3] = {
{ 0.0f, 0.0f, 0.0f }, // none
{ 0.0f, 0.0f, 0.0f }, // melee
{ 1.5f, 1.5f, -1.2f }, // pistol
{ 6.5f, 0.0f, -9.9f }, // shotgun
{ 0.5f, -8.5f, -11.0f }, // zoomrifle
{ 0.5f, -8.5f, -11.5f }, // rifle
{ 2.5f, 0.5f, -4.5f }, // smg
{ 0.5f, 0.5f, 1.5f }, // sniper
{ 1.5f, -2.0f, -12.0f } // heavy
{ 0.0f, 0.0f, 0.0f }, // none
{ 0.0f, 0.0f, 4.0f }, // melee
{ 3.5f, 2.0f, 1.5f }, // pistol
{ 9.5f, 5.0f, -8.0f }, // shotgun
{ 4.5f -3.5f, -5.0f }, // zoomrifle
{ 5.5f, -3.0f, -5.5f }, // rifle
{ 5.5f, -2.5f, -5.5f }, // smg
{ 3.5f, 1.5f, -6.0f }, // sniper
{ 2.5f, -4.0f, -9.0f } // heavy
};
// only highskilled bots do that
if (m_difficulty < Difficulty::Normal) {
if (m_difficulty != Difficulty::Expert) {
return 0.0f;
}
@ -710,7 +693,7 @@ bool Bot::isPenetrableObstacle2 (const Vector &dest) {
}
if (numHits < 3 && thikness < 98) {
if (dest.distanceSq (point) < 13143.0f) {
if (dest.distanceSq (point) < cr::square (112.0f)) {
return true;
}
}
@ -774,19 +757,16 @@ bool Bot::needToPauseFiring (float distance) {
}
float offset = 4.25f;
if (distance < kSprayDistance / 4) {
if (distance < kSprayDistance) {
return false;
}
else if (distance < kSprayDistance) {
offset = 10.0f;
}
else if (distance < kDoubleSprayDistance) {
offset = 8.0f;
}
const float xPunch = cr::deg2rad (pev->punchangle.x);
const float yPunch = cr::deg2rad (pev->punchangle.y);
const float interval = getFrameInterval ();
const float interval = m_frameInterval;
const float tolerance = (100.0f - static_cast <float> (m_difficulty) * 25.0f) / 99.0f;
// check if we need to compensate recoil
@ -813,8 +793,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
// select this weapon if it isn't already selected
if (m_currentWeapon != id) {
const auto &prop = conf.getWeaponProp (id);
selectWeaponByName (prop.classname.chars ());
selectWeaponById (id);
// reset burst fire variables
m_firePause = 0.0f;
@ -1072,7 +1051,7 @@ void Bot::focusEnemy () {
// aim for the head and/or body
m_lookAt = getEnemyBodyOffset ();
if (m_enemySurpriseTime > game.time ()) {
if (m_enemySurpriseTime > game.time () && !usesKnife ()) {
return;
}
float distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum?
@ -1121,7 +1100,7 @@ void Bot::attackMovement () {
return;
}
if (m_lastUsedNodesTime - getFrameInterval () > game.time ()) {
if (m_lastUsedNodesTime - m_frameInterval > game.time ()) {
return;
}
@ -1194,17 +1173,29 @@ void Bot::attackMovement () {
else {
m_fightStyle = Fight::Stay;
}
// do not try to strafe while ducking
if (isDucking () || isInNarrowPlace ()) {
m_fightStyle = Fight::Stay;
}
m_lastFightStyleCheck = game.time ();
}
if (m_fightStyle == Fight::Strafe) {
if (m_strafeSetTime < game.time ()) {
auto swapStrafeCombatDir = [&] () {
m_combatStrafeDir = (m_combatStrafeDir == Dodge::Left ? Dodge::Right : Dodge::Left);
};
// to start strafing, we have to first figure out if the target is on the left side or right side
auto strafeUpdateTime = [] () {
return game.time () + rg.get (0.5f, 1.0f);
};
// to start strafing, we have to first figure out if the target is on the left side or right side
if (m_strafeSetTime < game.time ()) {
const auto &dirToPoint = (pev->origin - m_enemy->v.origin).normalize2d ();
const auto &rightSide = m_enemy->v.v_angle.right ().normalize2d ();
if ((dirToPoint | rightSide) < 0) {
if ((dirToPoint | rightSide) < 0.0f) {
m_combatStrafeDir = Dodge::Left;
}
else {
@ -1212,31 +1203,43 @@ void Bot::attackMovement () {
}
if (rg.chance (30)) {
m_combatStrafeDir = (m_combatStrafeDir == Dodge::Left ? Dodge::Right : Dodge::Left);
swapStrafeCombatDir ();
}
m_strafeSetTime = game.time () + rg.get (1.2f, 2.4f);
m_strafeSetTime = strafeUpdateTime ();
}
if (m_combatStrafeDir == Dodge::Right) {
if (!checkWallOnLeft ()) {
m_strafeSpeed = -pev->maxspeed;
}
else if (!checkWallOnRight ()) {
swapStrafeCombatDir ();
m_strafeSetTime = strafeUpdateTime ();
m_strafeSpeed = pev->maxspeed;
}
else {
m_combatStrafeDir = Dodge::Left;
m_strafeSetTime = game.time () + rg.get (0.8f, 1.2f);
m_strafeSpeed = 0.0f;
m_strafeSetTime = strafeUpdateTime ();
}
}
else {
if (!checkWallOnRight ()) {
m_strafeSpeed = pev->maxspeed;
}
else if (!checkWallOnLeft ()) {
swapStrafeCombatDir ();
m_strafeSetTime = strafeUpdateTime ();
m_strafeSpeed = -pev->maxspeed;
}
else {
m_combatStrafeDir = Dodge::Right;
m_strafeSetTime = game.time () + rg.get (0.8f, 1.2f);
m_strafeSpeed = 0.0f;
m_strafeSetTime = strafeUpdateTime ();
}
}
if (m_difficulty >= Difficulty::Hard && (m_jumpTime + 5.0f < game.time () && isOnFloor () && rg.get (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.length2d () > 150.0f) && !usesSniper ()) {
if (m_difficulty >= Difficulty::Normal && (m_jumpTime + 5.0f < game.time () && isOnFloor () && rg.get (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.length2d () > 150.0f) && !usesSniper ()) {
pev->button |= IN_JUMP;
}
}
@ -1244,16 +1247,19 @@ void Bot::attackMovement () {
if ((m_enemyParts & (Visibility::Head | Visibility::Body)) && getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::Hunt) {
int enemyNearestIndex = graph.getNearest (m_enemy->v.origin);
if (graph.isDuckVisible (m_currentNodeIndex, enemyNearestIndex) && graph.isDuckVisible (enemyNearestIndex, m_currentNodeIndex)) {
m_duckTime = game.time () + 0.64f;
if (graph.isVisible (m_currentNodeIndex, enemyNearestIndex) && graph.isDuckVisible (m_currentNodeIndex, enemyNearestIndex) && graph.isDuckVisible (enemyNearestIndex, m_currentNodeIndex)) {
m_duckTime = game.time () + m_frameInterval * 2.0f;
}
}
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
m_navTimeset = game.time ();
}
if (m_difficulty >= Difficulty::Hard && isOnFloor () && (m_duckTime < game.time ())) {
m_navTimeset = game.time ();
m_moveToGoal = false;
m_checkTerrain = false;
if (m_difficulty >= Difficulty::Normal && isOnFloor () && m_duckTime < game.time ()) {
if (distance < 768.0f) {
if (rg.get (0, 1000) < rg.get (7, 12) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) {
pev->button |= IN_JUMP;
@ -1261,13 +1267,18 @@ void Bot::attackMovement () {
}
}
if (m_isReloading) {
m_moveSpeed = -pev->maxspeed;
m_duckTime = game.time () - 1.0f;
}
if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) {
Vector right, forward;
pev->v_angle.angleVectors (&forward, &right, nullptr);
const auto &front = right * m_moveSpeed * 0.2f;
const auto &side = right * m_strafeSpeed * 0.2f;
const auto &spot = pev->origin + front + side + pev->velocity * getFrameInterval ();
const auto &spot = pev->origin + front + side + pev->velocity * m_frameInterval;
if (isDeadlyMove (spot)) {
m_strafeSpeed = -m_strafeSpeed;
@ -1461,9 +1472,9 @@ void Bot::selectBestWeapon () {
// this function chooses best weapon, from weapons that bot currently own, and change
// current weapon to best one.
// if knife mode activated, force bot to use knife
if (cv_jasonmode.bool_ ()) {
// if knife mode activated, force bot to use knife
selectWeaponByName ("weapon_knife");
selectWeaponById (Weapon::Knife);
return;
}
@ -1505,11 +1516,11 @@ void Bot::selectBestWeapon () {
chosenWeaponIndex %= kNumWeapons + 1;
selectIndex = chosenWeaponIndex;
int id = tab[selectIndex].id;
const int id = tab[selectIndex].id;
// select this weapon if it isn't already selected
if (m_currentWeapon != id) {
selectWeaponByName (tab[selectIndex].name);
selectWeaponById (tab[selectIndex].id);
}
m_isReloading = false;
m_reloadState = Reload::None;
@ -1543,15 +1554,6 @@ int Bot::bestWeaponCarried () {
return num;
}
void Bot::selectWeaponByName (StringRef name) {
issueCommand (name.chars ());
}
void Bot::selectWeaponByIndex (int index) {
auto tab = conf.getRawWeapons ();
issueCommand (tab[index].name);
}
void Bot::decideFollowUser () {
// this function forces bot to follow user
Array <edict_t *> users;
@ -1685,7 +1687,7 @@ void Bot::checkReload () {
if (isLowOnAmmo (prop.id, 0.75f) && getAmmo (prop.id) > 0) {
if (m_currentWeapon != prop.id) {
selectWeaponByName (prop.classname);
selectWeaponById (prop.id);
}
pev->button &= ~IN_ATTACK;
@ -1719,3 +1721,311 @@ float Bot::calculateScaleFactor (edict_t *ent) {
return entArea / botArea;
}
Vector Bot::calcToss (const Vector &start, const Vector &stop) {
// this function returns the velocity at which an object should looped from start to land near end.
// returns null vector if toss is not feasible.
TraceResult tr {};
float gravity = sv_gravity.float_ () * 0.55f;
Vector end = stop - pev->velocity;
end.z -= 15.0f;
if (cr::abs (end.z - start.z) > 500.0f) {
return nullptr;
}
Vector midPoint = start + (end - start) * 0.5f;
game.testHull (midPoint, midPoint + Vector (0.0f, 0.0f, 500.0f), TraceIgnore::Monsters, head_hull, ent (), &tr);
if (tr.flFraction < 1.0f && tr.pHit) {
midPoint = tr.vecEndPos;
midPoint.z = tr.pHit->v.absmin.z - 1.0f;
}
if (midPoint.z < start.z || midPoint.z < end.z) {
return nullptr;
}
float timeOne = cr::sqrtf ((midPoint.z - start.z) / (0.5f * gravity));
float timeTwo = cr::sqrtf ((midPoint.z - end.z) / (0.5f * gravity));
if (timeOne < 0.1f) {
return nullptr;
}
Vector velocity = (end - start) / (timeOne + timeTwo);
velocity.z = gravity * timeOne;
Vector apex = start + velocity * timeOne;
apex.z = midPoint.z;
game.testHull (start, apex, TraceIgnore::None, head_hull, ent (), &tr);
if (tr.flFraction < 1.0f || tr.fAllSolid) {
return nullptr;
}
game.testHull (end, apex, TraceIgnore::Monsters, head_hull, ent (), &tr);
if (!cr::fequal (tr.flFraction, 1.0f)) {
float dot = -(tr.vecPlaneNormal | (apex - end).normalize ());
if (dot > 0.75f || tr.flFraction < 0.8f) {
return nullptr;
}
}
return velocity * 0.777f;
}
Vector Bot::calcThrow (const Vector &start, const Vector &stop) {
// this function returns the velocity vector at which an object should be thrown from start to hit end.
// returns null vector if throw is not feasible.
Vector velocity = stop - start;
TraceResult tr {};
float gravity = sv_gravity.float_ () * 0.55f;
float time = velocity.length () / 195.0f;
if (time < 0.01f) {
return nullptr;
}
else if (time > 2.0f) {
time = 1.2f;
}
const float half = time * 0.5f;
velocity = velocity * (1.0f / time);
velocity.z += gravity * half * half;
Vector apex = start + (stop - start) * 0.5f;
apex.z += 0.5f * gravity * half;
game.testHull (start, apex, TraceIgnore::None, head_hull, ent (), &tr);
if (!cr::fequal (tr.flFraction, 1.0f)) {
return nullptr;
}
game.testHull (stop, apex, TraceIgnore::Monsters, head_hull, ent (), &tr);
if (!cr::fequal (tr.flFraction, 1.0) || tr.fAllSolid) {
float dot = -(tr.vecPlaneNormal | (apex - stop).normalize ());
if (dot > 0.75f || tr.flFraction < 0.8f) {
return nullptr;
}
}
return velocity * 0.7793f;
}
edict_t *Bot::setCorrectGrenadeVelocity (const char *model) {
edict_t *result = nullptr;
game.searchEntities ("classname", "grenade", [&] (edict_t *ent) {
if (ent->v.owner == this->ent () && util.isModel (ent, model)) {
result = ent;
// set the correct velocity for the grenade
if (m_grenade.lengthSq () > 100.0f) {
ent->v.velocity = m_grenade + (m_grenade * m_frameInterval * 4.0f);
}
m_grenadeCheckTime = game.time () + 3.0f;
selectBestWeapon ();
completeTask ();
return EntitySearchResult::Break;
}
return EntitySearchResult::Continue;
});
return result;
}
void Bot::checkGrenadesThrow () {
// do not check cancel if we have grenade in out hands
bool preventibleTasks = getCurrentTaskId () == Task::PlantBomb || getCurrentTaskId () == Task::DefuseBomb;
auto clearThrowStates = [] (uint32_t &states) {
states &= ~(Sense::ThrowExplosive | Sense::ThrowFlashbang | Sense::ThrowSmoke);
};
// check if throwing a grenade is a good thing to do...
if (preventibleTasks || isInNarrowPlace () || cv_ignore_enemies.bool_ () || m_isUsingGrenade || m_grenadeRequested || m_isReloading || cv_jasonmode.bool_ () || (m_grenadeRequested || m_grenadeCheckTime >= game.time ())) {
clearThrowStates (m_states);
return;
}
// check again in some seconds
m_grenadeCheckTime = game.time () + kGrenadeCheckTime;
if (!util.isAlive (m_lastEnemy) || !(m_states & (Sense::SuspectEnemy | Sense::HearingEnemy))) {
clearThrowStates (m_states);
return;
}
// check if we have grenades to throw
int grenadeToThrow = bestGrenadeCarried ();
// if we don't have grenades no need to check it this round again
if (grenadeToThrow == -1) {
m_grenadeCheckTime = game.time () + 15.0f; // changed since, conzero can drop grens from dead players
clearThrowStates (m_states);
return;
}
else {
int cancelProb = 20;
if (grenadeToThrow == Weapon::Flashbang) {
cancelProb = 25;
}
else if (grenadeToThrow == Weapon::Smoke) {
cancelProb = 35;
}
if (rg.chance (cancelProb)) {
clearThrowStates (m_states);
return;
}
}
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) {
distance = kInfiniteDistance;
}
// too high to throw?
if (m_lastEnemy->v.origin.z > pev->origin.z + 500.0f) {
distance = kInfiniteDistance;
}
// enemy within a good throw distance?
if (!m_lastEnemyOrigin.empty () && distance > (grenadeToThrow == Weapon::Smoke ? 200.0f : 400.0f) && distance < 1200.0f) {
bool allowThrowing = true;
// care about different grenades
switch (grenadeToThrow) {
case Weapon::Explosive:
if (numFriendsNear (m_lastEnemy->v.origin, 256.0f) > 0) {
allowThrowing = false;
}
else {
float radius = m_lastEnemy->v.velocity.length2d ();
const Vector &pos = (m_lastEnemy->v.velocity * 0.5f).get2d () + m_lastEnemy->v.origin;
if (radius < 164.0f) {
radius = 164.0f;
}
auto predicted = graph.searchRadius (radius, pos, 12);
if (predicted.empty ()) {
m_states &= ~Sense::ThrowExplosive;
break;
}
for (const auto predict : predicted) {
allowThrowing = true;
if (!graph.exists (predict)) {
allowThrowing = false;
continue;
}
m_throw = graph[predict].origin;
auto throwPos = calcThrow (getEyesPos (), m_throw);
if (throwPos.lengthSq () < 100.0f) {
throwPos = calcToss (getEyesPos (), m_throw);
}
if (throwPos.empty ()) {
allowThrowing = false;
}
else {
m_throw.z += 110.0f;
break;
}
}
}
if (allowThrowing) {
m_states |= Sense::ThrowExplosive;
}
else {
m_states &= ~Sense::ThrowExplosive;
}
break;
case Weapon::Flashbang:
{
int nearest = graph.getNearest ((m_lastEnemy->v.velocity * 0.5f).get2d () + m_lastEnemy->v.origin);
if (nearest != kInvalidNodeIndex) {
m_throw = graph[nearest].origin;
if (numFriendsNear (m_throw, 256.0f) > 0) {
allowThrowing = false;
}
}
else {
allowThrowing = false;
}
if (allowThrowing) {
auto throwPos = calcThrow (getEyesPos (), m_throw);
if (throwPos.lengthSq () < 100.0f) {
throwPos = calcToss (getEyesPos (), m_throw);
}
if (throwPos.empty ()) {
allowThrowing = false;
}
else {
m_throw.z += 110.0f;
}
}
if (allowThrowing) {
m_states |= Sense::ThrowFlashbang;
}
else {
m_states &= ~Sense::ThrowFlashbang;
}
break;
}
case Weapon::Smoke:
if (allowThrowing && !game.isNullEntity (m_lastEnemy)) {
if (util.getShootingCone (m_lastEnemy, pev->origin) >= 0.9f) {
allowThrowing = false;
}
}
if (allowThrowing) {
m_states |= Sense::ThrowSmoke;
}
else {
m_states &= ~Sense::ThrowSmoke;
}
break;
default:
clearThrowStates (m_states);
return;
}
const float kMaxThrowTime = game.time () + kGrenadeCheckTime * 6.0f;
if (m_states & Sense::ThrowExplosive) {
startTask (Task::ThrowExplosive, TaskPri::Throw, kInvalidNodeIndex, kMaxThrowTime, false);
}
else if (m_states & Sense::ThrowFlashbang) {
startTask (Task::ThrowFlashbang, TaskPri::Throw, kInvalidNodeIndex, kMaxThrowTime, false);
}
else if (m_states & Sense::ThrowSmoke) {
startTask (Task::ThrowSmoke, TaskPri::Throw, kInvalidNodeIndex, kMaxThrowTime, false);
}
}
else {
clearThrowStates (m_states);
}
}

View file

@ -158,7 +158,7 @@ void BotConfig::loadWeaponsConfig () {
}
};
auto addIntEntries = [] (SmallArray <int32> &to, StringRef name, const StringArray &data) {
auto addIntEntries = [] (SmallArray <int32_t> &to, StringRef name, const StringArray &data) {
if (data.length () != to.length ()) {
logger.error ("%s entry in weapons config is not valid or malformed (%d/%d).", name, data.length (), to.length ());
return;
@ -334,9 +334,9 @@ void BotConfig::loadChatterConfig () {
for (const auto &event : chatterEventMap) {
if (event.str == items[0]) {
// this does common work of parsing comma-separated chatter line
auto sounds = items[1].split (",");
auto sentences = items[1].split (",");
for (auto &sound : sounds) {
for (auto &sound : sentences) {
sound.trim ().trim ("\"");
auto duration = game.getWaveLen (sound.chars ());
@ -344,7 +344,7 @@ void BotConfig::loadChatterConfig () {
m_chatter[event.code].emplace (cr::move (sound), event.repeat, duration);
}
}
sounds.clear ();
sentences.clear ();
}
}
}
@ -553,10 +553,10 @@ void BotConfig::loadDifficultyConfig () {
};
// currently, mindelay, maxdelay, headprob, seenthruprob, heardthruprob
constexpr uint32 kMaxDifficultyValues = 5;
constexpr uint32_t kMaxDifficultyValues = 5;
// helper for parsing each level
auto parseLevel = [&] (int32 level, StringRef data) {
auto parseLevel = [&] (int32_t level, StringRef data) {
auto values = data.split <String> (",");
if (values.length () != kMaxDifficultyValues) {
@ -796,14 +796,14 @@ const char *BotConfig::translate (StringRef input) {
}
void BotConfig::showCustomValues () {
game.print ("Current values for custom config items:");
ctrl.msg ("Current values for custom config items:");
m_custom.foreach ([&] (const String &key, const String &val) {
game.print (" %s = %s", key, val);
ctrl.msg (" %s = %s", key, val);
});
}
uint32 BotConfig::hashLangString (StringRef str) {
uint32_t BotConfig::hashLangString (StringRef str) {
auto test = [] (const char ch) {
return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
};

View file

@ -24,15 +24,15 @@ int BotControl::cmdAddBot () {
m_args.resize (max);
// if team is specified, modify args to set team
if (strValue (alias).find ("_ct", 0) != String::InvalidIndex) {
if (strValue (alias).endsWith ("_ct")) {
m_args.set (team, "2");
}
else if (strValue (alias).find ("_t", 0) != String::InvalidIndex) {
else if (strValue (alias).endsWith ("_t")) {
m_args.set (team, "1");
}
// if highskilled bot is requsted set personality to rusher and maxout difficulty
if (strValue (alias).find ("hs", 0) != String::InvalidIndex) {
if (strValue (alias).endsWith ("_hs")) {
m_args.set (difficulty, "4");
m_args.set (personality, "1");
}
@ -676,13 +676,13 @@ int BotControl::cmdNodePathCreate () {
graph.setEditFlag (GraphEdit::On);
// choose the direction for path creation
if (strValue (cmd).find ("_both", 0) != String::InvalidIndex) {
if (strValue (cmd).endsWith ("_both")) {
graph.pathCreate (PathConnection::Bidirectional);
}
else if (strValue (cmd).find ("_in", 0) != String::InvalidIndex) {
else if (strValue (cmd).endsWith ("_in")) {
graph.pathCreate (PathConnection::Incoming);
}
else if (strValue (cmd).find ("_out", 0) != String::InvalidIndex) {
else if (strValue (cmd).endsWith ("_out")) {
graph.pathCreate (PathConnection::Outgoing);
}
else {
@ -1852,7 +1852,7 @@ void BotControl::showMenu (int id) {
}
auto &client = util.getClient (game.indexOfPlayer (m_ent));
auto sendMenu = [&] (int32 slots, bool last, StringRef text) {
auto sendMenu = [&] (int32_t slots, bool last, StringRef text) {
MessageWriter (MSG_ONE, msgs.id (NetMsg::ShowMenu), nullptr, m_ent)
.writeShort (slots)
.writeChar (-1)
@ -1943,7 +1943,7 @@ void BotControl::kickBotByMenu (int page) {
for (auto &menu : m_menus) {
if (menu.ident == id) {
menu.slots = static_cast <int> (static_cast <uint32> (menuKeys) & static_cast <uint32> (-1));
menu.slots = static_cast <int> (static_cast <uint32_t> (menuKeys) & static_cast <uint32_t> (-1));
menu.text = menus;
break;

View file

@ -423,7 +423,7 @@ void Game::setPlayerStartDrawModels () {
});
}
bool Game::checkVisibility (edict_t *ent, uint8 *set) {
bool Game::checkVisibility (edict_t *ent, uint8_t *set) {
if (!set) {
return true;
}
@ -452,13 +452,13 @@ bool Game::checkVisibility (edict_t *ent, uint8 *set) {
return engfuncs.pfnCheckVisibility (ent, set) > 0;
}
uint8 *Game::getVisibilitySet (Bot *bot, bool pvs) {
uint8_t *Game::getVisibilitySet (Bot *bot, bool pvs) {
if (is (GameFlags::Xash3D)) {
return nullptr;
}
auto eyes = bot->getEyesPos ();
if (bot->pev->flags & FL_DUCKING) {
if (bot->isDucking ()) {
eyes += VEC_HULL_MIN - VEC_DUCK_HULL_MIN;
}
float org[3] { eyes.x, eyes.y, eyes.z };
@ -609,7 +609,7 @@ bool Game::isSoftwareRenderer () {
return false;
}
void Game::addNewCvar (const char *name, const char *value, const char *info, bool bounded, float min, float max, int32 varType, bool missingAction, const char *regval, ConVar *self) {
void Game::addNewCvar (const char *name, const char *value, const char *info, bool bounded, float min, float max, int32_t varType, bool missingAction, const char *regval, ConVar *self) {
// this function adds globally defined variable to registration stack
ConVarReg reg {};
@ -1112,7 +1112,7 @@ void LightMeasure::animateLight () {
m_lightstyleValue[j] = 256;
continue;
}
m_lightstyleValue[j] = static_cast <uint32> (m_lightstyle[j].map[index % m_lightstyle[j].length] - 'a') * 22u;
m_lightstyleValue[j] = static_cast <uint32_t> (m_lightstyle[j].map[index % m_lightstyle[j].length] - 'a') * 22u;
}
}
@ -1214,7 +1214,7 @@ template <typename S, typename M> bool LightMeasure::recursiveLightPoint (const
// compute the lightmap color at a particular point
for (int maps = 0u; maps < MAXLIGHTMAPS && surf->styles[maps] != 255u; ++maps) {
uint32 scale = m_lightstyleValue[surf->styles[maps]];
uint32_t scale = m_lightstyleValue[surf->styles[maps]];
m_point.red += lightmap->r * scale;
m_point.green += lightmap->g * scale;

View file

@ -7,7 +7,7 @@
#include <yapb.h>
// on other tran win32/linux platforms i.e. arm we're using xash3d engine to run which exposes
// on other than win32/linux platforms i.e. arm we're using xash3d engine to run which exposes
// nice interface to handle with linkents. if ever rehlds or hlds engine will ever run on ARM or
// other platforms, and you wan't to run bot on it without metamod, consider enabling LINKENT_STATIC_THUNKS
// when compiling the bot, to get it supported.

View file

@ -417,7 +417,7 @@ void BotGraph::addPath (int addIndex, int pathIndex, float distance) {
// check for free space in the connection indices
for (auto &link : path.links) {
if (link.index == kInvalidNodeIndex) {
link.index = static_cast <int16> (pathIndex);
link.index = static_cast <int16_t> (pathIndex);
link.distance = cr::abs (static_cast <int> (distance));
ctrl.msg ("Path added from %d to %d.", addIndex, pathIndex);
@ -439,7 +439,7 @@ void BotGraph::addPath (int addIndex, int pathIndex, float distance) {
if (slot != kInvalidNodeIndex) {
ctrl.msg ("Path added from %d to %d.", addIndex, pathIndex);
path.links[slot].index = static_cast <int16> (pathIndex);
path.links[slot].index = static_cast <int16_t> (pathIndex);
path.links[slot].distance = cr::abs (static_cast <int> (distance));
}
}
@ -524,7 +524,7 @@ IntArray BotGraph::searchRadius (float radius, const Vector &origin, int maxCoun
// returns all nodes within radius from position
IntArray result;
auto &bucket = getNodesInBucket (origin);
const auto &bucket = getNodesInBucket (origin);
if (bucket.empty ()) {
result.push (getNearestNoBuckets (origin, radius));
@ -928,7 +928,7 @@ bool BotGraph::isConnected (int a, int b) {
int BotGraph::getFacingIndex () {
// find the waypoint the user is pointing at
Twin <int32, float> result { kInvalidNodeIndex, 5.32f };
Twin <int32_t, float> result { kInvalidNodeIndex, 5.32f };
auto nearestNode = getEditorNearest ();
// check bounds from eyes of editor
@ -1171,7 +1171,7 @@ void BotGraph::calculatePathRadius (int index) {
TraceResult tr {};
bool wayBlocked = false;
for (int32 scanDistance = 32; scanDistance < 128; scanDistance += 16) {
for (int32_t scanDistance = 32; scanDistance < 128; scanDistance += 16) {
auto scan = static_cast <float> (scanDistance);
start = path.origin;
@ -1180,7 +1180,7 @@ void BotGraph::calculatePathRadius (int index) {
path.radius = scan;
for (int32 circleRadius = 0; circleRadius < 360; circleRadius += 20) {
for (int32_t circleRadius = 0; circleRadius < 360; circleRadius += 20) {
const auto &forward = direction.forward ();
auto radiusStart = start + forward * scan;
@ -1305,7 +1305,7 @@ void BotGraph::loadVisibility () {
if (m_paths.empty ()) {
return;
}
bool dataLoaded = loadStorage <uint8> ("vis", "Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr, nullptr);
bool dataLoaded = loadStorage <uint8_t> ("vis", "Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr, nullptr);
// if loaded, do not recalculate visibility
if (dataLoaded) {
@ -1317,7 +1317,7 @@ void BotGraph::saveVisibility () {
if (m_paths.empty () || m_hasChanged || m_needsVisRebuild) {
return;
}
saveStorage <uint8> ("vis", "Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr);
saveStorage <uint8_t> ("vis", "Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr);
}
bool BotGraph::loadPathMatrix () {
@ -1345,8 +1345,8 @@ bool BotGraph::loadPathMatrix () {
if (!exists (link.index)) {
continue;
}
(matrix + (i * count) + link.index)->dist = static_cast <int16> (link.distance);
(matrix + (i * count) + link.index)->index = static_cast <int16> (link.index);
(matrix + (i * count) + link.index)->dist = static_cast <int16_t> (link.distance);
(matrix + (i * count) + link.index)->index = static_cast <int16_t> (link.index);
}
}
@ -1360,7 +1360,7 @@ bool BotGraph::loadPathMatrix () {
int distance = (matrix + (i * count) + k)->dist + (matrix + (k * count) + j)->dist;
if (distance < (matrix + (i * count) + j)->dist) {
(matrix + (i * count) + j)->dist = static_cast <int16> (distance);
(matrix + (i * count) + j)->dist = static_cast <int16_t> (distance);
(matrix + (i * count) + j)->index = (matrix + (i * count) + k)->index;
}
}
@ -1404,7 +1404,7 @@ void BotGraph::initNarrowPlaces () {
if (m_paths.empty () || m_narrowChecked) {
return;
}
constexpr int32 kNarrowPlacesMinGraphVersion = 2;
constexpr int32_t kNarrowPlacesMinGraphVersion = 2;
// if version 2 or higher, narrow places already initialized and saved into file
if (m_graphHeader.version >= kNarrowPlacesMinGraphVersion) {
@ -1463,6 +1463,7 @@ void BotGraph::initNarrowPlaces () {
return false;
};
if (directionCheck (-forward * distance)) {
accumWeight += 1;
}
@ -1625,10 +1626,10 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
return false;
}
auto rawLength = data.length () * sizeof (U);
SmallArray <uint8> compressed (rawLength + sizeof (uint8) * ULZ::Excess);
SmallArray <uint8_t> compressed (rawLength + sizeof (uint8_t) * ULZ::Excess);
// try to compress
auto compressedLength = static_cast <size_t> (ulz.compress (reinterpret_cast <uint8 *> (data.data ()), static_cast <int32> (rawLength), reinterpret_cast <uint8 *> (compressed.data ())));
auto compressedLength = static_cast <size_t> (ulz.compress (reinterpret_cast <uint8_t *> (data.data ()), static_cast <int32_t> (rawLength), reinterpret_cast <uint8_t *> (compressed.data ())));
if (compressedLength > 0) {
StorageHeader hdr {};
@ -1637,11 +1638,11 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
hdr.version = version;
hdr.options = options;
hdr.length = length ();
hdr.compressed = static_cast <int32> (compressedLength);
hdr.uncompressed = static_cast <int32> (rawLength);
hdr.compressed = static_cast <int32_t> (compressedLength);
hdr.uncompressed = static_cast <int32_t> (rawLength);
file.write (&hdr, sizeof (StorageHeader));
file.write (compressed.data (), sizeof (uint8), compressedLength);
file.write (compressed.data (), sizeof (uint8_t), compressedLength);
// add extension
if ((options & StorageOption::Exten) && exten != nullptr) {
@ -1656,7 +1657,7 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
return true;
}
template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, SmallArray <U> &data, ExtenHeader *exten, int32 *outOptions) {
template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, SmallArray <U> &data, ExtenHeader *exten, int32_t *outOptions) {
String filename;
filename.assignf ("%s.%s", game.getMapName (), ext).lowercase ();
@ -1790,7 +1791,7 @@ template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name,
auto compressedSize = static_cast <size_t> (hdr.compressed);
auto numberNodes = static_cast <size_t> (hdr.length);
SmallArray <uint8> compressed (compressedSize + sizeof (uint8) * ULZ::Excess);
SmallArray <uint8_t> compressed (compressedSize + sizeof (uint8_t) * ULZ::Excess);
// graph is not resized upon load
if (isGraph) {
@ -1798,10 +1799,10 @@ template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name,
}
// read compressed data
if (file.read (compressed.data (), sizeof (uint8), compressedSize) == compressedSize) {
if (file.read (compressed.data (), sizeof (uint8_t), compressedSize) == compressedSize) {
// try to uncompress
if (ulz.uncompress (compressed.data (), hdr.compressed, reinterpret_cast <uint8 *> (data.data ()), hdr.uncompressed) == ULZ::UncompressFailure) {
if (ulz.uncompress (compressed.data (), hdr.compressed, reinterpret_cast <uint8_t *> (data.data ()), hdr.uncompressed) == ULZ::UncompressFailure) {
return raiseLoadingError (isGraph, file, "Unable to decompress ULZ data for %s (filename: '%s').", name, filename);
}
else {
@ -1844,7 +1845,7 @@ template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name,
bool BotGraph::loadGraphData () {
ExtenHeader exten {};
int32 outOptions = 0;
int32_t outOptions = 0;
m_graphHeader = {};
m_extenHeader = {};
@ -2079,7 +2080,7 @@ void BotGraph::rebuildVisibility () {
}
TraceResult tr {};
uint8 res, shift;
uint8_t res, shift;
for (const auto &vis : m_paths) {
Vector sourceDuck = vis.origin;
@ -2093,7 +2094,7 @@ void BotGraph::rebuildVisibility () {
sourceDuck.z += -18.0f + 12.0f;
sourceStand.z += 28.0f;
}
uint16 standCount = 0, crouchCount = 0;
uint16_t standCount = 0, crouchCount = 0;
for (const auto &path : m_paths) {
// first check ducked visibility
@ -2146,9 +2147,9 @@ void BotGraph::rebuildVisibility () {
res &= 2;
}
}
shift = static_cast <uint8> ((path.number % 4) << 1);
shift = static_cast <uint8_t> ((path.number % 4) << 1);
m_vistable[vis.number * length () + path.number] &= static_cast <uint8> (~(3 << shift));
m_vistable[vis.number * length () + path.number] &= ~static_cast <uint8_t> (3 << shift);
m_vistable[vis.number * length () + path.number] |= res << shift;
if (!(res & 2)) {
@ -2171,7 +2172,7 @@ bool BotGraph::isVisible (int srcIndex, int destIndex) {
return false;
}
uint8 res = m_vistable[srcIndex * length () + destIndex];
uint8_t res = m_vistable[srcIndex * length () + destIndex];
res >>= (destIndex % 4) << 1;
return !((res & 3) == 3);
@ -2182,7 +2183,7 @@ bool BotGraph::isDuckVisible (int srcIndex, int destIndex) {
return false;
}
uint8 res = m_vistable[srcIndex * length () + destIndex];
uint8_t res = m_vistable[srcIndex * length () + destIndex];
res >>= (destIndex % 4) << 1;
return !((res & 2) == 2);
@ -2193,7 +2194,7 @@ bool BotGraph::isStandVisible (int srcIndex, int destIndex) {
return false;
}
uint8 res = m_vistable[srcIndex * length () + destIndex];
uint8_t res = m_vistable[srcIndex * length () + destIndex];
res >>= (destIndex % 4) << 1;
return !((res & 1) == 1);
@ -3046,7 +3047,7 @@ BotGraph::Bucket BotGraph::locateBucket (const Vector &pos) {
};
}
const SmallArray <int32> &BotGraph::getNodesInBucket (const Vector &pos) {
const SmallArray <int32_t> &BotGraph::getNodesInBucket (const Vector &pos) {
const auto &bucket = locateBucket (pos);
return m_buckets[bucket.x][bucket.y][bucket.z];
}
@ -3084,7 +3085,7 @@ void BotGraph::updateGlobalPractice () {
if (maxDamage > kMaxPracticeDamageValue) {
adjustValues = true;
}
(practice + (i * length ()) + i)->index[team] = static_cast <int16> (bestIndex);
(practice + (i * length ()) + i)->index[team] = static_cast <int16_t> (bestIndex);
}
}
constexpr auto kHalfDamageVal = static_cast <int> (kMaxPracticeDamageValue * 0.5);
@ -3097,7 +3098,7 @@ void BotGraph::updateGlobalPractice () {
if (i == j) {
continue;
}
(practice + (i * length ()) + j)->damage[team] = static_cast <int16> (cr::clamp (getDangerDamage (team, i, j) - kHalfDamageVal, 0, kMaxPracticeDamageValue));
(practice + (i * length ()) + j)->damage[team] = static_cast <int16_t> (cr::clamp (getDangerDamage (team, i, j) - kHalfDamageVal, 0, kMaxPracticeDamageValue));
}
}
}
@ -3232,7 +3233,7 @@ void BotGraph::setDangerValue (int team, int start, int goal, int value) {
if (!exists (start) || !exists (goal)) {
return;
}
(m_practice.data () + (start * length ()) + goal)->value[team] = static_cast <int16> (value);
(m_practice.data () + (start * length ()) + goal)->value[team] = static_cast <int16_t> (value);
}
void BotGraph::setDangerDamage (int team, int start, int goal, int value) {
@ -3244,5 +3245,5 @@ void BotGraph::setDangerDamage (int team, int start, int goal, int value) {
if (!exists (start) || !exists (goal)) {
return;
}
(m_practice.data () + (start * length ()) + goal)->damage[team] = static_cast <int16> (value);
(m_practice.data () + (start * length ()) + goal)->damage[team] = static_cast <int16_t> (value);
}

View file

@ -553,7 +553,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) {
// SoundAttachToThreat() to bring the sound to the ears of the bots. Since bots have no client DLL
// to handle this for them, such a job has to be done manually.
util.listenNoise (entity, sample, volume);
sounds.listenNoise (entity, sample, volume);
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);

View file

@ -782,7 +782,7 @@ void BotManager::listBots () {
ctrl.msg ("%-3.5s\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.4s\t%-3.4s\t%-3.5s\t%-3.8s", "index", "name", "personality", "team", "difficulty", "frags", "alive", "timeleft");
for (const auto &bot : bots) {
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%-3.0f secs", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no", bot->m_stayTime - game.time ());
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%-3.0f secs", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no", cv_rotate_bots.bool_ () ? bot->m_stayTime - game.time () : 0.0f);
}
ctrl.msg ("%d bots", m_bots.length ());
}
@ -799,7 +799,7 @@ float BotManager::getConnectTime (StringRef name, float original) {
}
float BotManager::getAverageTeamKPD (bool calcForBots) {
Twin <float, int32> calc {};
Twin <float, int32_t> calc {};
for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used)) {
@ -913,7 +913,7 @@ void BotManager::updateBotDifficulties () {
void BotManager::balanceBotDifficulties () {
// difficulty chaning once per round (time)
auto updateDifficulty = [] (Bot *bot, int32 offset) {
auto updateDifficulty = [] (Bot *bot, int32_t offset) {
bot->m_difficulty = cr::clamp (static_cast <Difficulty> (bot->m_difficulty + offset), Difficulty::Noob, Difficulty::Expert);
};
@ -1080,11 +1080,9 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) {
m_wantedTeam = team;
m_wantedSkin = skin;
newRound ();
}
m_tasks.reserve (Task::Max);
float Bot::getFrameInterval () {
return m_frameInterval;
newRound ();
}
float Bot::getConnectionTime () {
@ -1126,6 +1124,24 @@ int BotManager::getAliveHumansCount () {
return count;
}
int BotManager::getPlayerPriority (edict_t *ent) {
constexpr auto highPrio = 512;
// always check for only our own bots
auto bot = bots[ent];
// if player just return high prio
if (!bot) {
return game.indexOfEntity (ent) + highPrio;
}
// give bots some priority
if (bot->m_hasC4 || bot->m_isVIP || bot->hasHostage () || bot->m_healthValue < ent->v.health) {
return bot->entindex () + highPrio;
}
return bot->entindex ();
}
bool BotManager::isTeamStacked (int team) {
if (team != Team::CT && team != Team::Terrorist) {
return false;
@ -1422,7 +1438,7 @@ void Bot::newRound () {
msg = BotMsg::None;
}
m_msgQueue.clear ();
m_goalHistory.clear ();
m_nodeHistory.clear ();
m_ignoredBreakable.clear ();
// clear last trace
@ -1532,13 +1548,15 @@ void Bot::updateTeamJoin () {
if (m_startAction == BotMsg::TeamSelect) {
m_startAction = BotMsg::None; // switch back to idle
char teamJoin = cv_join_team.str ()[0];
if (m_wantedTeam == -1) {
char teamJoin = cv_join_team.str ()[0];
if (teamJoin == 'C' || teamJoin == 'c') {
m_wantedTeam = 2;
}
else if (teamJoin == 'T' || teamJoin == 't') {
m_wantedTeam = 1;
if (teamJoin == 'C' || teamJoin == 'c') {
m_wantedTeam = 2;
}
else if (teamJoin == 'T' || teamJoin == 't') {
m_wantedTeam = 1;
}
}
if (m_wantedTeam != 1 && m_wantedTeam != 2) {
@ -1595,7 +1613,7 @@ void Bot::updateTeamJoin () {
// check for greeting other players, since we connected
if (rg.chance (20)) {
pushChatMessage (Chat::Hello);
m_needToSendWelcomeChat = true;
}
}
}

View file

@ -352,7 +352,7 @@ void MessageDispatcher::netMsgScoreAttrib () {
// if we're have bot, set the vip state
if (bot != nullptr) {
constexpr int32 kPlayerIsVIP = cr::bit (2);
constexpr int32_t kPlayerIsVIP = cr::bit (2);
bot->m_isVIP = !!(m_args[flags].long_ & kPlayerIsVIP);
}
@ -492,7 +492,7 @@ MessageDispatcher::MessageDispatcher () {
m_teamInfoCache["CT"] = Team::CT;
}
int32 MessageDispatcher::add (StringRef name, int32 id) {
int32_t MessageDispatcher::add (StringRef name, int32_t id) {
if (!m_wanted.has (name)) {
return id;
}
@ -503,7 +503,7 @@ int32 MessageDispatcher::add (StringRef name, int32 id) {
return id;
}
void MessageDispatcher::start (edict_t *ent, int32 type) {
void MessageDispatcher::start (edict_t *ent, int32_t type) {
reset ();
if (game.is (GameFlags::Metamod)) {
@ -551,16 +551,16 @@ void MessageDispatcher::ensureMessages () {
}
// re-register our message
m_wanted.foreach ([&] (const String &key, const int32 &) {
m_wanted.foreach ([&] (const String &key, const int32_t &) {
add (key, GET_USER_MSG_ID (PLID, key.chars (), nullptr));
});
}
int32 MessageDispatcher::id (NetMsg msg) {
int32_t MessageDispatcher::id (NetMsg msg) {
return m_maps[msg];
}
Bot *MessageDispatcher::pickBot (int32 index) {
Bot *MessageDispatcher::pickBot (int32_t index) {
const auto &client = util.getClient (m_args[index].long_ - 1);
// get the bot in this msg

View file

@ -12,8 +12,8 @@ ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bou
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 () {
auto pushToHistroy = [&] (int32 goal) -> int32 {
m_goalHistory.push (goal);
auto pushToHistroy = [&] (int32_t goal) -> int32_t {
m_nodeHistory.push (goal);
return goal;
};
@ -188,12 +188,9 @@ int Bot::findBestGoalWhenBombAction () {
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, timeMidBlowup, true); // push camp task on to stack
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, result, timeMidBlowup, true); // push move command
if (path.vis.crouch <= path.vis.stand) {
m_campButtons |= IN_DUCK;
}
else {
m_campButtons &= ~IN_DUCK;
}
// decide to duck or not to duck
selectCampButtons (result);
if (rg.chance (90)) {
pushChatterMessage (Chatter::DefendingBombsite);
}
@ -259,8 +256,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
postprocessGoals (graph.m_goalPoints, goalChoices);
}
}
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) // rescue goal
{
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) {
// force ct with hostage(s) to select closest rescue goal
float minDist = kInfiniteDistance;
int count = 0;
@ -286,7 +282,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
}
if (!graph.exists (m_currentNodeIndex)) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
if (goalChoices[0] == kInvalidNodeIndex) {
@ -320,7 +316,7 @@ void Bot::postprocessGoals (const IntArray &goals, int result[]) {
for (int index = 0; index < 4; ++index) {
auto goal = goals.random ();
if (searchCount <= 8 && (isBannedNode (goal) || m_prevGoalIndex == goal || ((result[0] == goal || result[1] == goal || result[2] == goal || result[3] == goal) && goals.length () > 4)) && !isOccupiedNode (goal)) {
if (searchCount <= 8 && (m_prevGoalIndex == goal || ((result[0] == goal || result[1] == goal || result[2] == goal || result[3] == goal) && goals.length () > 4)) && !isOccupiedNode (goal)) {
if (index > 0) {
index--;
}
@ -370,13 +366,14 @@ void Bot::ignoreCollision () {
m_prevOrigin = pev->origin;
}
bool Bot::doPlayerAvoidance (const Vector &normal) {
edict_t *hindrance = nullptr;
float distance = cr::square (300.0f);
void Bot::doPlayerAvoidance (const Vector &normal) {
m_hindrance = nullptr;
float distance = cr::square (348.0f);
if (getCurrentTaskId () == Task::Attack || getCurrentTaskId () == Task::SeekCover || isOnLadder ()) {
return false;
if (getCurrentTaskId () == Task::Attack || isOnLadder ()) {
return;
}
const auto ownPrio = bots.getPlayerPriority (ent ());
// find nearest player to bot
for (const auto &client : util.getClients ()) {
@ -391,28 +388,29 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
continue;
}
// skip if it's not standing
if (client.ent->v.flags & FL_DUCKING) {
continue;
}
// our team, alive and not myself?
if (client.team != m_team || client.ent == ent ()) {
continue;
}
const auto otherPrio = bots.getPlayerPriority (client.ent);
// give some priorities to bot avoidance
if (ownPrio > otherPrio) {
continue;
}
auto nearest = client.ent->v.origin.distanceSq (pev->origin);
if (nearest < cr::square (pev->maxspeed) && nearest < distance) {
hindrance = client.ent;
m_hindrance = client.ent;
distance = nearest;
}
}
// found somebody?
if (!hindrance) {
return false;
if (game.isNullEntity (m_hindrance)) {
return;
}
const float interval = getFrameInterval () * 4.0f;
const float interval = m_frameInterval * 7.25f;
// use our movement angles, try to predict where we should be next frame
Vector right, forward;
@ -423,44 +421,39 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
predict += right * m_strafeSpeed * interval;
predict += pev->velocity * interval;
auto movedDistance = hindrance->v.origin.distanceSq (predict);
auto nextFrameDistance = pev->origin.distanceSq (hindrance->v.origin + hindrance->v.velocity * interval);
auto movedDistance = m_hindrance->v.origin.distanceSq (predict);
auto nextFrameDistance = pev->origin.distanceSq (m_hindrance->v.origin + m_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)) {
auto dir = (pev->origin - hindrance->v.origin).normalize2d ();
auto dir = (pev->origin - m_hindrance->v.origin).normalize2d ();
// to start strafing, we have to first figure out if the target is on the left side or right side
if ((dir | right.get2d ()) > 0.0f) {
if ((dir | right.normalize2d ()) > 0.0f) {
setStrafeSpeed (normal, pev->maxspeed);
}
else {
setStrafeSpeed (normal, -pev->maxspeed);
}
if (distance < cr::square (56.0f)) {
if ((dir | forward.get2d ()) < 0.0f) {
if (distance < cr::square (76.0f)) {
if ((dir | forward.normalize2d ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed;
}
}
return true;
}
return false;
}
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// if avoiding someone do not consider stuck
TraceResult tr {};
m_isStuck = doPlayerAvoidance (dirNormal);
float checkSpeed = isDucking () ? 4.0f : 10.0f;
if (m_isStuck) {
resetCollision ();
return;
}
m_isStuck = false;
// Standing still, no need to check?
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way
if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < game.time () && m_seeEnemyTime + 0.8f < game.time () && getCurrentTaskId () != Task::Attack) {
// standing still, no need to check?
if ((m_moveSpeed >= checkSpeed || m_strafeSpeed >= checkSpeed) && m_lastCollTime < game.time ()) {
// didn't we move enough previously?
if (movedDistance < 2.0f && m_prevSpeed >= 20.0f) {
@ -504,7 +497,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// bot is stuc, but not yet decided what to do?
if (m_collisionState == CollisionState::Undecided) {
uint32 bits = 0;
uint32_t bits = 0;
if (isOnLadder ()) {
bits |= CollisionProbe::Strafe;
@ -515,7 +508,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// collision check allowed if not flying through the air
if (isOnFloor () || isOnLadder () || isInWater ()) {
uint32 state[kMaxCollideMoves * 2 + 1] {};
uint32_t state[kMaxCollideMoves * 2 + 1] {};
int i = 0;
Vector src {}, dst {};
@ -623,7 +616,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
}
}
}
if (pev->flags & FL_DUCKING) {
if (isDucking ()) {
src = pev->origin;
}
else {
@ -724,13 +717,105 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
}
}
void Bot::moveToGoal () {
findValidNode ();
bool prevLadder = false;
if (graph.exists (m_previousNodes[0])) {
if (graph[m_previousNodes[0]].flags & NodeFlag::Ladder) {
prevLadder = true;
}
}
// press duck button if we need to
if ((m_pathFlags & NodeFlag::Crouch) && !(m_pathFlags & (NodeFlag::Camp | NodeFlag::Goal))) {
pev->button |= IN_DUCK;
}
m_lastUsedNodesTime = game.time ();
// press jump button if we need to leave the ladder
if (!(m_pathFlags & NodeFlag::Ladder) && prevLadder && isOnFloor () && isOnLadder () && m_moveSpeed > 50.0f && pev->velocity.length () < 50.0f) {
pev->button |= IN_JUMP;
m_jumpTime = game.time () + 1.0f;
}
if (m_pathFlags & NodeFlag::Ladder) {
if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) {
if (!prevLadder) {
m_moveSpeed = pev->origin.distance (m_pathOrigin);
}
else {
m_moveSpeed = 150.0f;
}
if (m_moveSpeed < 150.0f) {
m_moveSpeed = 150.0f;
}
else if (m_moveSpeed > pev->maxspeed) {
m_moveSpeed = pev->maxspeed;
}
}
}
// special movement for swimming here
if (isInWater ()) {
// check if we need to go forward or back press the correct buttons
if (isInFOV (m_destOrigin - getEyesPos ()) > 90.0f) {
pev->button |= IN_BACK;
}
else {
pev->button |= IN_FORWARD;
}
if (m_moveAngles.x > 60.0f) {
pev->button |= IN_DUCK;
}
else if (m_moveAngles.x < -60.0f) {
pev->button |= IN_JUMP;
}
}
}
void Bot::translateInput () {
if (m_duckTime >= game.time ()) {
pev->button |= IN_DUCK;
}
if (pev->button & IN_JUMP) {
m_jumpTime = game.time ();
}
if (m_jumpTime + 0.85f > game.time ()) {
if (!isOnFloor () && !isInWater () && !isOnLadder ()) {
pev->button |= IN_DUCK;
}
}
if (!(pev->button & (IN_FORWARD | IN_BACK))) {
if (m_moveSpeed > 0.0f) {
pev->button |= IN_FORWARD;
}
else if (m_moveSpeed < 0.0f) {
pev->button |= IN_BACK;
}
}
if (!(pev->button & (IN_MOVELEFT | IN_MOVERIGHT))) {
if (m_strafeSpeed > 0.0f) {
pev->button |= IN_MOVERIGHT;
}
else if (m_strafeSpeed < 0.0f) {
pev->button |= IN_MOVELEFT;
}
}
}
bool Bot::updateNavigation () {
// this function is a main path navigation
// check if we need to find a node...
if (m_currentNodeIndex == kInvalidNodeIndex) {
findValidNode ();
m_pathOrigin = m_path->origin;
// if graph node radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) {
@ -749,7 +834,9 @@ bool Bot::updateNavigation () {
// if bot's on the ground or on the ladder we're free to jump. actually setting the correct velocity is cheating.
// pressing the jump button gives the illusion of the bot actual jumping.
if (isOnFloor () || isOnLadder ()) {
pev->velocity = m_desiredVelocity;
if (m_desiredVelocity.length2d () > 0.0f) {
pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.05f;
}
pev->button |= IN_JUMP;
m_jumpFinished = true;
@ -759,10 +846,16 @@ bool Bot::updateNavigation () {
}
else if (!cv_jasonmode.bool_ () && usesKnife () && isOnFloor ()) {
selectBestWeapon ();
// if jump distance was big enough, cooldown a little
if (m_jumpDistance > 180.0f) {
startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.45f, false);
}
m_jumpDistance = 0.0f;
}
}
if (m_path->flags & NodeFlag::Ladder) {
if (m_pathFlags & NodeFlag::Ladder) {
if (!m_pathWalk.empty ()) {
if (m_pathWalk.hasNext ()) {
if (graph[m_pathWalk.next ()].flags & NodeFlag::Ladder || isOnLadder ()) {
@ -776,10 +869,10 @@ bool Bot::updateNavigation () {
m_pathOrigin.z += pev->origin.z - m_pathOrigin.z;
}
if (m_pathOrigin.z > (pev->origin.z + 16.0f)) {
m_pathOrigin = m_path->origin - Vector (0.0f, 0.0f, 16.0f);
m_pathOrigin = m_pathOrigin - Vector (0.0f, 0.0f, 16.0f);
}
if (m_pathOrigin.z < (pev->origin.z - 16.0f)) {
m_pathOrigin = m_path->origin + Vector (0.0f, 0.0f, 16.0f);
m_pathOrigin = m_pathOrigin + Vector (0.0f, 0.0f, 16.0f);
}
}
m_destOrigin = m_pathOrigin;
@ -813,7 +906,7 @@ bool Bot::updateNavigation () {
}
}
else {
game.testHull (getEyesPos (), m_path->origin, TraceIgnore::Monsters, pev->flags & FL_DUCKING ? head_hull : human_hull, ent (), &tr);
game.testHull (getEyesPos (), m_pathOrigin, TraceIgnore::Monsters, isDucking () ? head_hull : human_hull, ent (), &tr);
// someone is above or below us and is using the ladder already
if (tr.pHit == client.ent && cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) {
@ -843,7 +936,7 @@ bool Bot::updateNavigation () {
}
if (foundGround) {
if (getCurrentTaskId () != Task::MoveToPosition || !cr::fequal (getTask ()->desire, TaskPri::PlantBomb)) {
m_currentNodeIndex = m_previousNodes[0];
changeNodeIndex (m_previousNodes[0]);
startTask (Task::MoveToPosition, TaskPri::PlantBomb, previousNode, 0.0f, true);
}
break;
@ -856,7 +949,7 @@ bool Bot::updateNavigation () {
}
// special lift handling (code merged from podbotmm)
if (m_path->flags & NodeFlag::Lift) {
if (m_pathFlags & NodeFlag::Lift) {
if (updateLiftHandling ()) {
if (!updateLiftStates ()) {
return false;
@ -929,16 +1022,16 @@ bool Bot::updateNavigation () {
}
float desiredDistance = 8.0f;
float nodeDistance = pev->origin.distance2d (m_pathOrigin);
float nodeDistance = pev->origin.distance (m_pathOrigin);
// initialize the radius for a special node type, where the node is considered to be reached
if (m_path->flags & NodeFlag::Lift) {
if (m_pathFlags & NodeFlag::Lift) {
desiredDistance = 50.0f;
}
else if ((pev->flags & FL_DUCKING) || (m_path->flags & NodeFlag::Goal)) {
else if (isDucking () || (m_pathFlags & NodeFlag::Goal)) {
desiredDistance = 25.0f;
}
else if (m_path->flags & NodeFlag::Ladder) {
else if (m_pathFlags & NodeFlag::Ladder) {
desiredDistance = 24.0f;
}
else if (m_currentTravelFlags & PathFlag::Jump) {
@ -963,7 +1056,7 @@ bool Bot::updateNavigation () {
}
// needs precise placement - check if we get past the point
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * getFrameInterval ()) >= cr::square (nodeDistance)) {
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= cr::square (nodeDistance)) {
desiredDistance = nodeDistance + 1.0f;
}
@ -1048,7 +1141,7 @@ bool Bot::updateLiftHandling () {
};
// trace line to door
game.testLine (pev->origin, m_path->origin, TraceIgnore::Everything, ent (), &tr);
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
if (tr.flFraction < 1.0f && tr.pHit && strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) {
if (m_liftState == LiftState::None) {
@ -1059,7 +1152,7 @@ bool Bot::updateLiftHandling () {
}
// trace line down
game.testLine (m_path->origin, m_path->origin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
game.testLine (m_path->origin, m_pathOrigin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
// if trace result shows us that it is a lift
if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_train") == 0) && !liftClosedDoorExists) {
@ -1067,7 +1160,7 @@ bool Bot::updateLiftHandling () {
if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) {
m_liftEntity = tr.pHit;
m_liftState = LiftState::EnteringIn;
m_liftTravelPos = m_path->origin;
m_liftTravelPos = m_pathOrigin;
m_liftUsageTime = game.time () + 5.0f;
}
}
@ -1289,7 +1382,7 @@ bool Bot::updateLiftHandling () {
m_liftUsageTime = 0.0f;
clearSearchNodes ();
findBestNearestNode ();
findNextBestNode ();
if (graph.exists (m_previousNodes[2])) {
findPath (m_currentNodeIndex, m_previousNodes[2], FindPath::Fast);
@ -1302,7 +1395,7 @@ bool Bot::updateLiftHandling () {
}
bool Bot::updateLiftStates () {
if (!game.isNullEntity (m_liftEntity) && !(m_path->flags & NodeFlag::Lift)) {
if (!game.isNullEntity (m_liftEntity) && !(m_pathFlags & NodeFlag::Lift)) {
if (m_liftState == LiftState::TravelingBy) {
m_liftState = LiftState::Leaving;
m_liftUsageTime = game.time () + 10.0f;
@ -1324,14 +1417,14 @@ bool Bot::updateLiftStates () {
if (graph.exists (m_previousNodes[0])) {
if (!(graph[m_previousNodes[0]].flags & NodeFlag::Lift)) {
changePointIndex (m_previousNodes[0]);
changeNodeIndex (m_previousNodes[0]);
}
else {
findBestNearestNode ();
findNextBestNode ();
}
}
else {
findBestNearestNode ();
findNextBestNode ();
}
return false;
}
@ -1630,9 +1723,9 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
auto childRoute = &m_routes[child.index];
// calculate the F value as F = G + H
float g = curRoute->g + gcalc (m_team, child.index, currentIndex);
float h = hcalc (child.index, srcIndex, destIndex);
float f = g + h;
const float g = curRoute->g + gcalc (m_team, child.index, currentIndex);
const float h = hcalc (child.index, srcIndex, destIndex);
const float f = g + h;
if (childRoute->state == RouteState::New || childRoute->f > f) {
// put the current child into open list
@ -1674,7 +1767,7 @@ int Bot::findAimingNode (const Vector &to, int &pathLength) {
// return the most distant node which is seen from the bot to the target and is within count
if (m_currentNodeIndex == kInvalidNodeIndex) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
int destIndex = graph.getNearest (to);
@ -1704,7 +1797,7 @@ int Bot::findAimingNode (const Vector &to, int &pathLength) {
return bestIndex;
}
bool Bot::findBestNearestNode () {
bool Bot::findNextBestNode () {
// this function find a node in the near of the bot if bot had lost his path of pathfinder needs
// to be restarted over again.
@ -1714,7 +1807,7 @@ bool Bot::findBestNearestNode () {
int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex , kInvalidNodeIndex };
auto &bucket = graph.getNodesInBucket (pev->origin);
int numToSkip = cr::clamp (rg.get (0, 3), 0, static_cast <int> (bucket.length () / 2));
int numToSkip = cr::clamp (rg.get (0, 2), 0, static_cast <int> (bucket.length () / 2));
for (const int at : bucket) {
bool skip = !!(at == m_currentNodeIndex);
@ -1748,7 +1841,7 @@ bool Bot::findBestNearestNode () {
}
// ignore non-reacheable nodes...
if (!isReachableNode (at) || isBannedNode (at)) {
if (!isReachableNode (at)) {
continue;
}
@ -1809,25 +1902,14 @@ bool Bot::findBestNearestNode () {
if (selected == kInvalidNodeIndex) {
selected = findNearestNode ();
}
m_goalHistory.push (selected);
changePointIndex (selected);
m_nodeHistory.push (selected);
changeNodeIndex (selected);
return true;
}
float Bot::getReachTime () {
auto task = getCurrentTaskId ();
float estimatedTime = 0.0f;
switch (task) {
case Task::Pause:
case Task::Camp:
case Task::Hide:
return 0.0f;
default:
estimatedTime = 2.8f; // time to reach next node
}
float Bot::getEstimatedNodeReachTime () {
float estimatedTime = 2.8f;
// calculate 'real' time that we need to get from one node to another
if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) {
@ -1840,7 +1922,7 @@ float Bot::getReachTime () {
else {
estimatedTime = 3.0f * distance / pev->maxspeed;
}
bool longTermReachability = (m_path->flags & NodeFlag::Crouch) || (m_path->flags & NodeFlag::Ladder) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK);
bool longTermReachability = (m_pathFlags & NodeFlag::Crouch) || (m_pathFlags & NodeFlag::Ladder) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK);
// check for special nodes, that can slowdown our movement
if (longTermReachability) {
@ -1848,7 +1930,7 @@ float Bot::getReachTime () {
}
estimatedTime = cr::clamp (estimatedTime, 2.0f, longTermReachability ? 8.0f : 5.0f);
}
return estimatedTime;
return estimatedTime + m_frameInterval;
}
void Bot::findValidNode () {
@ -1857,11 +1939,9 @@ void Bot::findValidNode () {
// if bot hasn't got a node we need a new one anyway or if time to get there expired get new one as well
if (m_currentNodeIndex == kInvalidNodeIndex) {
clearSearchNodes ();
findBestNearestNode ();
m_pathOrigin = m_path->origin;
findNextBestNode ();
}
else if (m_navTimeset + getReachTime () < game.time () && game.isNullEntity (m_enemy)) {
else if (m_navTimeset + getEstimatedNodeReachTime () < game.time () && game.isNullEntity (m_enemy)) {
// increase danager for both teams
for (int team = Team::Terrorist; team < kGameTeamNum; ++team) {
@ -1880,13 +1960,11 @@ void Bot::findValidNode () {
graph.setDangerDamage (m_team, m_currentNodeIndex, m_currentNodeIndex, damageValue);
}
clearSearchNodes ();
findBestNearestNode ();
m_pathOrigin = m_path->origin;
findNextBestNode ();
}
}
int Bot::changePointIndex (int index) {
int Bot::changeNodeIndex (int index) {
if (index == kInvalidNodeIndex) {
return 0;
}
@ -1896,11 +1974,14 @@ int Bot::changePointIndex (int index) {
m_previousNodes[0] = m_currentNodeIndex;
m_currentNodeIndex = index;
m_navTimeset = game.time ();
m_collideTime = game.time ();
m_path = &graph[index];
m_path = &graph[m_currentNodeIndex];
m_pathFlags = m_path->flags;
m_pathOrigin = m_path->origin;
return m_currentNodeIndex; // to satisfy static-code analyzers
}
@ -2004,7 +2085,7 @@ int Bot::findDefendNode (const Vector &origin) {
// provides enough cover point, and is far away from the defending position
if (m_currentNodeIndex == kInvalidNodeIndex) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
TraceResult tr {};
@ -2020,7 +2101,7 @@ int Bot::findDefendNode (const Vector &origin) {
int srcIndex = m_currentNodeIndex;
// max search distance
const int kMaxDistance = 128 * bots.getBotCount ();
const int kMaxDistance = cr::clamp (148 * bots.getBotCount (), 256, 1024);
// some of points not found, return random one
if (srcIndex == kInvalidNodeIndex || posIndex == kInvalidNodeIndex) {
@ -2177,7 +2258,7 @@ int Bot::findCoverNode (float maxDistance) {
}
// ensure we're on valid point
changePointIndex (srcIndex);
changeNodeIndex (srcIndex);
// find the best node now
for (int i = 0; i < graph.length (); ++i) {
@ -2207,35 +2288,35 @@ int Bot::findCoverNode (float maxDistance) {
continue;
}
if (distance > minDistance[0]) {
if (distance < minDistance[0]) {
nodeIndex[0] = i;
minDistance[0] = distance;
}
else if (distance > minDistance[1]) {
else if (distance < minDistance[1]) {
nodeIndex[1] = i;
minDistance[1] = distance;
}
else if (distance > minDistance[2]) {
else if (distance < minDistance[2]) {
nodeIndex[2] = i;
minDistance[2] = distance;
}
else if (distance > minDistance[3]) {
else if (distance < minDistance[3]) {
nodeIndex[3] = i;
minDistance[3] = distance;
}
else if (distance > minDistance[4]) {
else if (distance < minDistance[4]) {
nodeIndex[4] = i;
minDistance[4] = distance;
}
else if (distance > minDistance[5]) {
else if (distance < minDistance[5]) {
nodeIndex[5] = i;
minDistance[5] = distance;
}
else if (distance > minDistance[6]) {
else if (distance < minDistance[6]) {
nodeIndex[6] = i;
minDistance[6] = distance;
}
else if (distance > minDistance[7]) {
else if (distance < minDistance[7]) {
nodeIndex[7] = i;
minDistance[7] = distance;
}
@ -2411,7 +2492,7 @@ bool Bot::advanceMovement () {
}
// check if bot is going to jump
bool willJump = false;
float jumpDistance = 0.0f;
m_jumpDistance = 0.0f;
Vector src;
Vector dst;
@ -2420,15 +2501,15 @@ bool Bot::advanceMovement () {
if (m_pathWalk.hasNext ()) {
auto nextIndex = m_pathWalk.next ();
const Path &path = graph[destIndex];
const Path &next = graph[nextIndex];
const auto &path = graph[destIndex];
const auto &next = graph[nextIndex];
for (const auto &link : path.links) {
if (link.index == nextIndex && (link.flags & PathFlag::Jump)) {
src = path.origin;
dst = next.origin;
jumpDistance = path.origin.distance (next.origin);
m_jumpDistance = path.origin.distance (next.origin);
willJump = true;
break;
@ -2437,14 +2518,14 @@ bool Bot::advanceMovement () {
}
// is there a jump node right ahead and do we need to draw out the light weapon ?
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (jumpDistance > 200.0f || (dst.z - 32.0f > src.z && jumpDistance > 150.0f)) && !(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))) {
selectWeaponByName ("weapon_knife"); // draw out the knife if we needed
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (m_jumpDistance > 175.0f || (dst.z - 32.0f > src.z && m_jumpDistance > 135.0f)) && !(m_states & Sense::SeeingEnemy)) {
selectWeaponById (Weapon::Knife); // draw out the knife if we needed
}
// bot not already on ladder but will be soon?
if ((graph[destIndex].flags & NodeFlag::Ladder) && !isOnLadder ()) {
// get ladder nodes used by other (first moving) bots
// get ladder nodes used by other (first moving) bots
for (const auto &other : bots) {
// if another bot uses this ladder, wait 3 secs
if (other.get () != this && other->m_notKilled && other->m_currentNodeIndex == destIndex) {
@ -2454,10 +2535,9 @@ bool Bot::advanceMovement () {
}
}
}
changePointIndex (destIndex);
changeNodeIndex (destIndex);
}
}
m_pathOrigin = m_path->origin;
// if wayzone radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) {
@ -2484,7 +2564,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
// first do a trace from the bot's eyes forward...
auto src = getEyesPos ();
auto forward = src + normal * 24.0f;
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
bool traceResult = false;
@ -2531,7 +2611,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
}
// now check below waist
if (pev->flags & FL_DUCKING) {
if (isDucking ()) {
src = pev->origin + Vector (0.0f, 0.0f, -19.0f + 19.0f);
forward = src + Vector (0.0f, 0.0f, 10.0f) + normal * 24.0f;
@ -2646,7 +2726,7 @@ bool Bot::canJumpUp (const Vector &normal) {
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
return false;
}
auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
// check for normal jump height first...
auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
@ -2793,7 +2873,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
Vector baseHeight;
// use center of the body first...
if (pev->flags & FL_DUCKING) {
if (isDucking ()) {
baseHeight = pev->origin + Vector (0.0f, 0.0f, -17.0f);
}
else {
@ -2812,7 +2892,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
}
// convert current view angle to vectors for TraceLine math...
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
// now check same height to one side of the bot...
src = baseHeight + right * 16.0f;
@ -3008,16 +3088,17 @@ void Bot::changeYaw (float speed) {
pev->angles.y = pev->v_angle.y;
}
int Bot::findCampingDirection () {
int Bot::getRandomCampDir () {
// find a good node to look at when camping
if (m_currentNodeIndex == kInvalidNodeIndex) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
constexpr int kMaxNodesToSearch = 5;
int count = 0, indices[3] {};
float distTab[3] {};
uint16 visibility[3] {};
int count = 0, indices[kMaxNodesToSearch] {};
float distTab[kMaxNodesToSearch] {};
uint16_t visibility[kMaxNodesToSearch] {};
int currentNode = m_currentNodeIndex;
@ -3025,9 +3106,9 @@ int Bot::findCampingDirection () {
if (currentNode == i || !graph.isVisible (currentNode, i)) {
continue;
}
const Path &path = graph[i];
const auto &path = graph[i];
if (count < 3) {
if (count < kMaxNodesToSearch) {
indices[count] = i;
distTab[count] = pev->origin.distanceSq (path.origin);
@ -3037,9 +3118,9 @@ int Bot::findCampingDirection () {
}
else {
float distance = pev->origin.distanceSq (path.origin);
uint16 visBits = path.vis.crouch + path.vis.stand;
uint16_t visBits = path.vis.crouch + path.vis.stand;
for (int j = 0; j < 3; ++j) {
for (int j = 0; j < kMaxNodesToSearch; ++j) {
if (visBits >= visibility[j] && distance > distTab[j]) {
indices[j] = i;
@ -3077,7 +3158,7 @@ void Bot::updateLookAngles () {
// adjust all body and view angles to face an absolute vector
Vector direction = (m_lookAt - getEyesPos ()).angles ();
direction.x *= -1.0f; // invert for engine
direction.x = -direction.x; // invert for engine
direction.clampAngles ();
@ -3089,18 +3170,14 @@ void Bot::updateLookAngles () {
return;
}
if (m_difficulty == Difficulty::Expert && (m_aimFlags & AimFlags::Enemy) && (m_wantsToFire || usesSniper ()) && m_kpdRatio < 1.0f && m_healthValue < 50.0f) {
pev->v_angle = direction;
updateBodyAngles ();
return;
}
float accelerate = 3000.0f;
float stiffness = 200.0f;
float damping = 25.0f;
if ((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) && m_difficulty > Difficulty::Hard) {
if (((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
if (m_difficulty == Difficulty::Expert) {
accelerate += 600.0f;
}
stiffness += 100.0f;
damping += 5.0f;
}
@ -3275,14 +3352,10 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
}
auto bot = bots[client.ent];
if (bot == nullptr || bot == this || !bot->m_notKilled || bot->getTask ()->data == index) {
if (bot == nullptr || bot == this || !bot->m_notKilled) {
continue;
}
auto occupyId = util.getShootingCone (bot->ent (), pev->origin) >= 0.7f ? bot->m_previousNodes[0] : bot->m_currentNodeIndex;
if (index == occupyId) {
return true;
}
return index == bot->m_currentNodeIndex || bot->getTask ()->data == index;
}
return false;
}
@ -3366,10 +3439,17 @@ bool Bot::isReachableNode (int index) {
}
bool Bot::isBannedNode (int index) {
if (graph.exists (cv_debug_goal.int_ ())) {
if (graph.exists (cv_debug_goal.int_ ()) || !graph.exists (index)) {
return false;
}
for (const auto &node : m_goalHistory) {
const auto &bucket = graph.getNodesInBucket (graph[index].origin);
// too few nodes in bucket near location, do not ban anything
if (bucket.length <int> () <= kMaxNodeLinks) {
return false;
}
for (const auto &node : m_nodeHistory) {
if (node == index) {
return true;
}

181
src/sounds.cpp Normal file
View file

@ -0,0 +1,181 @@
//
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
// Copyright © 2004-2023 YaPB Project <yapb@jeefo.net>.
//
// SPDX-License-Identifier: MIT
//
#include <yapb.h>
BotSounds::BotSounds () {
// register noise cache
m_noiseCache["player/bhit"] = Noise::NeedHandle | Noise::HitFall;
m_noiseCache["player/head"] = Noise::NeedHandle | Noise::HitFall;
m_noiseCache["items/gunpi"] = Noise::NeedHandle | Noise::Pickup;
m_noiseCache["items/9mmcl"] = Noise::NeedHandle | Noise::Ammo;
m_noiseCache["weapons/zoo"] = Noise::NeedHandle | Noise::Zoom;
m_noiseCache["hostage/hos"] = Noise::NeedHandle | Noise::Hostage;
m_noiseCache["debris/bust"] = Noise::NeedHandle | Noise::Broke;
m_noiseCache["doors/doorm"] = Noise::NeedHandle | Noise::Door;
m_noiseCache["weapons/c4_"] = Noise::NeedHandle | Noise::Defuse;
}
void BotSounds::listenNoise (edict_t *ent, StringRef sample, float volume) {
// this function called by the sound hooking code (in emit_sound) enters the played sound into the array associated with the entity
if (game.isNullEntity (ent) || sample.empty ()) {
return;
}
const auto &origin = game.getEntityOrigin (ent);
// something wrong with sound...
if (origin.empty ()) {
return;
}
auto noise = m_noiseCache[sample.substr (0, 11)];
// we're not handling theese
if (!(noise & Noise::NeedHandle)) {
return;
}
// find nearest player to sound origin
auto findNearbyClient = [&origin] () {
float nearest = kInfiniteDistance;
Client *result = nullptr;
// loop through all players
for (auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) {
continue;
}
auto distance = client.origin.distanceSq (origin);
// now find nearest player
if (distance < nearest) {
result = &client;
nearest = distance;
}
}
return result;
};
auto client = findNearbyClient ();
// update noise stats
auto registerNoise = [&origin, &client, &volume] (float distance, float lasting) {
client->noise.dist = distance * volume;
client->noise.last = game.time () + lasting;
client->noise.pos = origin;
};
// client wasn't found
if (!client) {
return;
}
// hit/fall sound?
if (noise & Noise::HitFall) {
registerNoise (768.0f, 0.52f);
}
// weapon pickup?
else if (noise & Noise::Pickup) {
registerNoise (768.0f, 0.45f);
}
// sniper zooming?
else if (noise & Noise::Zoom) {
registerNoise (512.0f, 0.10f);
}
// ammo pickup?
else if (noise & Noise::Ammo) {
registerNoise (512.0f, 0.25f);
}
// ct used hostage?
else if (noise & Noise::Hostage) {
registerNoise (1024.0f, 5.00);
}
// broke something?
else if (noise & Noise::Broke) {
registerNoise (1024.0f, 2.00f);
}
// someone opened a door
else if (noise & Noise::Door) {
registerNoise (1024.0f, 3.00f);
}
// some one started to defuse
else if ((noise & Noise::Defuse) && sample[11] == 'd') {
registerNoise (512.0f, 0.5f);
}
}
void BotSounds::simulateNoise (int playerIndex) {
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
// captured through server sound hooking
if (playerIndex < 0 || playerIndex >= game.maxClients ()) {
return; // reliability check
}
auto &client = util.getClient (playerIndex);
ClientNoise noise {};
auto buttons = client.ent->v.button | client.ent->v.oldbuttons;
// pressed attack button?
if (buttons & IN_ATTACK) {
noise.dist = 2048.0f;
noise.last = game.time () + 0.3f;
}
// pressed used button?
else if (buttons & IN_USE) {
noise.dist = 512.0f;
noise.last = game.time () + 0.5f;
}
// pressed reload button?
else if (buttons & IN_RELOAD) {
noise.dist = 512.0f;
noise.last = game.time () + 0.5f;
}
// uses ladder?
else if (client.ent->v.movetype == MOVETYPE_FLY) {
if (cr::abs (client.ent->v.velocity.z) > 50.0f) {
noise.dist = 1024.0f;
noise.last = game.time () + 0.3f;
}
}
else {
if (mp_footsteps.bool_ ()) {
// moves fast enough?
noise.dist = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
noise.last = game.time () + 0.3f;
}
}
if (noise.dist <= 0.0f) {
return; // didn't issue sound?
}
// some sound already associated
if (client.noise.last > game.time ()) {
if (client.noise.dist <= noise.dist) {
// override it with new
client.noise.dist = noise.dist;
client.noise.last = noise.last;
client.noise.pos = client.ent->v.origin;
}
}
else if (!cr::fzero (noise.last)) {
// just remember it
client.noise.dist = noise.dist;
client.noise.last = noise.last;
client.noise.pos = client.ent->v.origin;
}
}

View file

@ -54,16 +54,6 @@ BotSupport::BotSupport () {
m_tags.emplace ("(", ")");
m_tags.emplace (")", "(");
// register noise cache
m_noiseCache["player/bhit"] = Noise::NeedHandle | Noise::HitFall;
m_noiseCache["player/head"] = Noise::NeedHandle | Noise::HitFall;
m_noiseCache["items/gunpi"] = Noise::NeedHandle | Noise::Pickup;
m_noiseCache["items/9mmcl"] = Noise::NeedHandle | Noise::Ammo;
m_noiseCache["weapons/zoo"] = Noise::NeedHandle | Noise::Zoom;
m_noiseCache["hostage/hos"] = Noise::NeedHandle | Noise::Hostage;
m_noiseCache["debris/bust"] = Noise::NeedHandle | Noise::Broke;
m_noiseCache["doors/doorm"] = Noise::NeedHandle | Noise::Door;
// register weapon aliases
m_weaponAlias[Weapon::USP] = "usp"; // HK USP .45 Tactical
m_weaponAlias[Weapon::Glock18] = "glock"; // Glock18 Select Fire
@ -375,161 +365,6 @@ bool BotSupport::findNearestPlayer (void **pvHolder, edict_t *to, float searchDi
return true;
}
void BotSupport::listenNoise (edict_t *ent, StringRef sample, float volume) {
// this function called by the sound hooking code (in emit_sound) enters the played sound into the array associated with the entity
if (game.isNullEntity (ent) || sample.empty ()) {
return;
}
const auto &origin = game.getEntityOrigin (ent);
// something wrong with sound...
if (origin.empty ()) {
return;
}
auto noise = m_noiseCache[sample.substr (0, 11)];
// we're not handling theese
if (!(noise & Noise::NeedHandle)) {
return;
}
// find nearest player to sound origin
auto findNearbyClient = [&origin] () {
float nearest = kInfiniteDistance;
Client *result = nullptr;
// loop through all players
for (auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) {
continue;
}
auto distance = client.origin.distanceSq (origin);
// now find nearest player
if (distance < nearest) {
result = &client;
nearest = distance;
}
}
return result;
};
auto client = findNearbyClient ();
// update noise stats
auto registerNoise = [&origin, &client, &volume] (float distance, float lasting) {
client->noise.dist = distance * volume;
client->noise.last = game.time () + lasting;
client->noise.pos = origin;
};
// client wasn't found
if (!client) {
return;
}
// hit/fall sound?
if (noise & Noise::HitFall) {
registerNoise (768.0f, 0.52f);
}
// weapon pickup?
else if (noise & Noise::Pickup) {
registerNoise (768.0f, 0.45f);
}
// sniper zooming?
else if (noise & Noise::Zoom) {
registerNoise (512.0f, 0.10f);
}
// ammo pickup?
else if (noise & Noise::Ammo) {
registerNoise (512.0f, 0.25f);
}
// ct used hostage?
else if (noise & Noise::Hostage) {
registerNoise (1024.0f, 5.00);
}
// broke something?
else if (noise & Noise::Broke) {
registerNoise (1024.0f, 2.00f);
}
// someone opened a door
else if (noise & Noise::Door) {
registerNoise (1024.0f, 3.00f);
}
}
void BotSupport::simulateNoise (int playerIndex) {
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
// captured through server sound hooking
if (playerIndex < 0 || playerIndex >= game.maxClients ()) {
return; // reliability check
}
Client &client = m_clients[playerIndex];
ClientNoise noise {};
auto buttons = client.ent->v.button | client.ent->v.oldbuttons;
// pressed attack button?
if (buttons & IN_ATTACK) {
noise.dist = 2048.0f;
noise.last = game.time () + 0.3f;
}
// pressed used button?
else if (buttons & IN_USE) {
noise.dist = 512.0f;
noise.last = game.time () + 0.5f;
}
// pressed reload button?
else if (buttons & IN_RELOAD) {
noise.dist = 512.0f;
noise.last = game.time () + 0.5f;
}
// uses ladder?
else if (client.ent->v.movetype == MOVETYPE_FLY) {
if (cr::abs (client.ent->v.velocity.z) > 50.0f) {
noise.dist = 1024.0f;
noise.last = game.time () + 0.3f;
}
}
else {
if (mp_footsteps.bool_ ()) {
// moves fast enough?
noise.dist = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
noise.last = game.time () + 0.3f;
}
}
if (noise.dist <= 0.0f) {
return; // didn't issue sound?
}
// some sound already associated
if (client.noise.last > game.time ()) {
if (client.noise.dist <= noise.dist) {
// override it with new
client.noise.dist = noise.dist;
client.noise.last = noise.last;
client.noise.pos = client.ent->v.origin;
}
}
else if (!cr::fzero (noise.last)) {
// just remember it
client.noise.dist = noise.dist;
client.noise.last = noise.last;
client.noise.pos = client.ent->v.origin;
}
}
void BotSupport::updateClients () {
// record some stats of all players on the server
@ -550,7 +385,7 @@ void BotSupport::updateClients () {
if (client.flags & ClientFlags::Alive) {
client.origin = player->v.origin;
simulateNoise (i);
sounds.simulateNoise (i);
}
}
else {
@ -703,24 +538,24 @@ String BotSupport::getCurrentDateTime () {
return String (timebuf);
}
int32 BotSupport::sendTo (int socket, const void *message, size_t length, int flags, const sockaddr *dest, int destLength) {
const auto send = [&] (const Twin <const uint8 *, size_t> &msg) -> int32 {
int32_t BotSupport::sendTo (int socket, const void *message, size_t length, int flags, const sockaddr *dest, int destLength) {
const auto send = [&] (const Twin <const uint8_t *, size_t> &msg) -> int32_t {
return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength);
};
auto packet = reinterpret_cast <const uint8 *> (message);
auto packet = reinterpret_cast <const uint8_t *> (message);
// player replies response
if (length > 5 && packet[0] == 0xff && packet[1] == 0xff && packet[2] == 0xff && packet[3] == 0xff) {
if (packet[4] == 'D') {
QueryBuffer buffer (packet, length, 5);
auto count = buffer.read <uint8> ();
auto count = buffer.read <uint8_t> ();
for (uint8 i = 0; i < count; ++i) {
buffer.skip <uint8> (); // number
for (uint8_t i = 0; i < count; ++i) {
buffer.skip <uint8_t> (); // number
auto name = buffer.readString (); // name
buffer.skip <int32> (); // score
buffer.skip <int32_t> (); // score
auto ctime = buffer.read <float> (); // override connection time
buffer.write <float> (bots.getConnectTime (name, ctime));
@ -729,17 +564,17 @@ int32 BotSupport::sendTo (int socket, const void *message, size_t length, int fl
}
else if (packet[4] == 'I') {
QueryBuffer buffer (packet, length, 5);
buffer.skip <uint8> (); // protocol
buffer.skip <uint8_t> (); // protocol
// skip server name, folder, map game
for (size_t i = 0; i < 4; ++i) {
buffer.skipString ();
}
buffer.skip <short> (); // steam app id
buffer.skip <uint8> (); // players
buffer.skip <uint8> (); // maxplayers
buffer.skip <uint8> (); // bots
buffer.write <uint8> (0); // zero out bot count
buffer.skip <uint8_t> (); // players
buffer.skip <uint8_t> (); // maxplayers
buffer.skip <uint8_t> (); // bots
buffer.write <uint8_t> (0); // zero out bot count
return send (buffer.data ());
}
@ -747,7 +582,7 @@ int32 BotSupport::sendTo (int socket, const void *message, size_t length, int fl
QueryBuffer buffer (packet, length, 5);
buffer.shiftToEnd (); // shift to the end of buffer
buffer.write <uint8> (0); // zero out bot count
buffer.write <uint8_t> (0); // zero out bot count
return send (buffer.data ());
}
@ -755,7 +590,7 @@ int32 BotSupport::sendTo (int socket, const void *message, size_t length, int fl
return send ({ packet, length });
}
StringRef BotSupport::weaponIdToAlias (int32 id) {
StringRef BotSupport::weaponIdToAlias (int32_t id) {
StringRef none = "none";
if (m_weaponAlias.has (id)) {

View file

@ -52,12 +52,16 @@
<ClInclude Include="..\inc\message.h" />
<ClInclude Include="..\inc\module.h" />
<ClInclude Include="..\inc\product.h" />
<ClInclude Include="..\inc\sounds.h" />
<ClInclude Include="..\inc\support.h" />
<ClInclude Include="..\inc\yapb.h" />
<ClInclude Include="..\inc\version.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\entities.cpp" />
<ClCompile Include="..\src\entities.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\src\botlib.cpp" />
<ClCompile Include="..\src\chatlib.cpp" />
<ClCompile Include="..\src\combat.cpp" />
@ -70,6 +74,7 @@
<ClCompile Include="..\src\message.cpp" />
<ClCompile Include="..\src\module.cpp" />
<ClCompile Include="..\src\navigate.cpp" />
<ClCompile Include="..\src\sounds.cpp" />
<ClCompile Include="..\src\support.cpp" />
</ItemGroup>
<ItemGroup>
@ -90,7 +95,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<PlatformToolset>ClangCL</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<WholeProgramOptimization>true</WholeProgramOptimization>
<EnableASAN>false</EnableASAN>
@ -98,7 +103,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v143</PlatformToolset>
<PlatformToolset>ClangCL</PlatformToolset>
<UseDebugLibraries>false</UseDebugLibraries>
<EnableASAN>false</EnableASAN>
</PropertyGroup>

View file

@ -153,6 +153,9 @@
<ClInclude Include="..\ext\crlib\crlib\simd.h">
<Filter>inc\ext\crlib</Filter>
</ClInclude>
<ClInclude Include="..\inc\sounds.h">
<Filter>inc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\botlib.cpp">
@ -197,6 +200,9 @@
<ClCompile Include="..\src\entities.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\sounds.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="yapb.rc">