Fixed player avoidance code.

Speedup network message handling.
This commit is contained in:
joint 2019-08-12 14:16:28 +03:00 committed by jeefo
commit 9947e41549
26 changed files with 2398 additions and 2294 deletions

229
include/config.h Normal file
View file

@ -0,0 +1,229 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// botname structure definition
struct BotName {
String name = "";
int usedBy = -1;
public:
BotName () = default;
BotName (String &name, int usedBy) : name (cr::move (name)), usedBy (usedBy) { }
};
// voice config structure definition
struct ChatterItem {
String name;
float repeat;
float duration;
public:
ChatterItem (String name, float repeat, float duration) : name (cr::move (name)), repeat (repeat), duration (duration) { }
};
// language hasher
struct HashLangString {
uint32 operator () (const String &key) const {
auto str = reinterpret_cast <uint8 *> (const_cast <char *> (key.chars ()));
uint32 hash = 0;
while (*str++) {
if (!isalnum (*str)) {
continue;
}
hash = ((*str << 5) + hash) + *str;
}
return hash;
}
};
// mostly config stuff, and some stuff dealing with menus
class BotConfig final : public Singleton <BotConfig> {
private:
Array <StringArray> m_chat;
Array <Array <ChatterItem>> m_chatter;
Array <BotName> m_botNames;
Array <Keywords> m_replies;
SmallArray <WeaponInfo> m_weapons;
SmallArray <WeaponProp> m_weaponProps;
StringArray m_logos;
StringArray m_avatars;
Dictionary <String, String, HashLangString> m_language;
// default tables for personality weapon preferences, overridden by general.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 };
public:
BotConfig ();
~BotConfig () = default;
public:
// load the configuration files
void loadConfigs ();
// loads main config file
void loadMainConfig ();
// loads bot names
void loadNamesConfig ();
// loads weapons config
void loadWeaponsConfig ();
// loads chatter config
void loadChatterConfig ();
// loads chat config
void loadChatConfig ();
// loads language config
void loadLanguageConfig ();
// load bots logos config
void loadLogosConfig ();
// load bots avatars config
void loadAvatarsConfig ();
// sets memfile to use engine functions
void setupMemoryFiles ();
// picks random bot name
BotName *pickBotName ();
// remove bot name from used list
void clearUsedName (Bot *bot);
// initialize weapon info
void initWeapons ();
// fix weapon prices (ie for elite)
void adjustWeaponPrices ();
// find weapon info by weaponi d
WeaponInfo &findWeaponById (int id);
// translates bot message into needed language
const char *translate (const char *input);
private:
bool isCommentLine (const String &line) {
const char ch = line.at (0);
return ch == '#' || ch == '/' || ch == '\r' || ch == ';' || ch == 0 || ch == ' ';
};
public:
// checks whether chat banks contains messages
bool hasChatBank (int chatType) const {
return !m_chat[chatType].empty ();
}
// checks whether chatter banks contains messages
bool hasChatterBank (int chatterType) const {
return !m_chatter[chatterType].empty ();
}
// pick random phrase from chat bank
const String &pickRandomFromChatBank (int chatType) {
return m_chat[chatType].random ();
}
// pick random phrase from chatter bank
const ChatterItem &pickRandomFromChatterBank (int chatterType) {
return m_chatter[chatterType].random ();
}
// gets chatter repeat-interval
float getChatterMessageRepeatInterval (int chatterType) const {
return m_chatter[chatterType][0].repeat;
}
// get's the replies array
Array <Keywords> &getReplies () {
return m_replies;
}
// get's the weapon info data
SmallArray <WeaponInfo> &getWeapons () {
return m_weapons;
}
// get's raw weapon info
WeaponInfo *getRawWeapons () {
return m_weapons.begin ();
}
// set's the weapon properties
void setWeaponProp (WeaponProp prop) {
m_weaponProps[prop.id] = cr::move (prop);
}
// get's the weapons prop
const WeaponProp &getWeaponProp (int id) const {
return m_weaponProps[id];
}
// get's weapon preferences for personality
int32 *getWeaponPrefs (int personality) const {
switch (personality) {
case Personality::Normal:
default:
return m_normalWeaponPrefs.data ();
case Personality::Rusher:
return m_rusherWeaponPrefs.data ();
case Personality::Careful:
return m_carefulWeaponPrefs.data ();
}
}
// get economics value
int32 *getEconLimit () {
return m_botBuyEconomyTable.data ();
}
// get's grenade buy percents
bool chanceToBuyGrenade (int grenadeType) const {
return rg.chance (m_grenadeBuyPrecent[grenadeType]);
}
// get's random avatar for player (if any)
String getRandomAvatar () const {
if (!m_avatars.empty ()) {
return m_avatars.random ();
}
return "";
}
// get's random logo index
int getRandomLogoIndex () const {
return m_logos.index (m_logos.random ());
}
// get random name by index
const String &getRandomLogoName (int index) const {
return m_logos[index];
}
};
// explose global
static auto &conf = BotConfig::get ();

215
include/control.h Normal file
View file

@ -0,0 +1,215 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// command handler status
CR_DECLARE_SCOPED_ENUM (BotCommandResult,
Handled = 0, // command successfully handled
ListenServer, // command is only avaialble on listen server
BadFormat // wrong params
)
// bot command manager
class BotControl final : public Singleton <BotControl> {
public:
using Handler = int (BotControl::*) ();
using MenuHandler = int (BotControl::*) (int);
public:
// generic bot command
struct BotCmd {
String name, format, help;
Handler handler = nullptr;
public:
BotCmd () = default;
BotCmd (String name, String format, String help, Handler handler) : name (cr::move (name)), format (cr::move (format)), help (cr::move (help)), handler (cr::move (handler)) { }
};
// single bot menu
struct BotMenu {
int ident, slots;
String text;
MenuHandler handler;
public:
BotMenu (int ident, int slots, String text, MenuHandler handler) : ident (ident), slots (slots), text (cr::move (text)), handler (cr::move (handler)) { }
};
private:
StringArray m_args;
Array <BotCmd> m_cmds;
Array <BotMenu> m_menus;
edict_t *m_ent;
bool m_isFromConsole;
bool m_rapidOutput;
bool m_isMenuFillCommand;
int m_menuServerFillTeam;
int m_interMenuData[4] = { 0, };
public:
BotControl ();
~BotControl () = default;
private:
int cmdAddBot ();
int cmdKickBot ();
int cmdKickBots ();
int cmdKillBots ();
int cmdFill ();
int cmdVote ();
int cmdWeaponMode ();
int cmdVersion ();
int cmdNodeMenu ();
int cmdMenu ();
int cmdList ();
int cmdNode ();
int cmdNodeOn ();
int cmdNodeOff ();
int cmdNodeAdd ();
int cmdNodeAddBasic ();
int cmdNodeSave ();
int cmdNodeLoad ();
int cmdNodeErase ();
int cmdNodeDelete ();
int cmdNodeCheck ();
int cmdNodeCache ();
int cmdNodeClean ();
int cmdNodeSetRadius ();
int cmdNodeSetFlags ();
int cmdNodeTeleport ();
int cmdNodePathCreate ();
int cmdNodePathDelete ();
int cmdNodePathSetAutoDistance ();
int cmdNodeAcquireEditor ();
int cmdNodeReleaseEditor ();
int cmdNodeUpload ();
private:
int menuMain (int item);
int menuFeatures (int item);
int menuControl (int item);
int menuWeaponMode (int item);
int menuPersonality (int item);
int menuDifficulty (int item);
int menuTeamSelect (int item);
int menuClassSelect (int item);
int menuCommands (int item);
int menuGraphPage1 (int item);
int menuGraphPage2 (int item);
int menuGraphRadius (int item);
int menuGraphType (int item);
int menuGraphFlag (int item);
int menuGraphPath (int item);
int menuAutoPathDistance (int item);
int menuKickPage1 (int item);
int menuKickPage2 (int item);
int menuKickPage3 (int item);
int menuKickPage4 (int item);
private:
void enableDrawModels (bool enable);
void createMenus ();
public:
bool executeCommands ();
bool executeMenus ();
void showMenu (int id);
void kickBotByMenu (int page);
void assignAdminRights (edict_t *ent, char *infobuffer);
void maintainAdminRights ();
public:
void setFromConsole (bool console) {
m_isFromConsole = console;
}
void setRapidOutput (bool force) {
m_rapidOutput = force;
}
void setIssuer (edict_t *ent) {
m_ent = ent;
}
void fixMissingArgs (size_t num) {
if (num < m_args.length ()) {
return;
}
m_args.resize (num);
}
int getInt (size_t arg) const {
if (!hasArg (arg)) {
return 0;
}
return m_args[arg].int_ ();
}
const String &getStr (size_t arg) {
static String empty ("empty");
if (!hasArg (arg) || m_args[arg].empty ()) {
return empty;
}
return m_args[arg];
}
bool hasArg (size_t arg) const {
return arg < m_args.length ();
}
void collectArgs () {
m_args.clear ();
for (int i = 0; i < engfuncs.pfnCmd_Argc (); ++i) {
m_args.emplace (engfuncs.pfnCmd_Argv (i));
}
}
// global heloer for sending message to correct channel
template <typename ...Args> void msg (const char *fmt, Args ...args);
public:
// for the server commands
static void handleEngineCommands ();
// for the client commands
bool handleClientCommands (edict_t *ent);
// for the client menu commands
bool handleMenuCommands (edict_t *ent);
};
// global heloer for sending message to correct channel
template <typename ...Args> inline void BotControl::msg (const char *fmt, Args ...args) {
auto result = strings.format (fmt, cr::forward <Args> (args)...);
// if no receiver or many message have to appear, just print to server console
if (game.isNullEntity (m_ent) || m_rapidOutput) {
game.print (result);
return;
}
if (m_isFromConsole || strlen (result) > 48) {
game.clientPrint (m_ent, result);
}
else {
game.centerPrint (m_ent, result);
game.clientPrint (m_ent, result);
}
}
// explose global
static auto &ctrl = BotControl::get ();

View file

@ -50,7 +50,7 @@ public:
rhs.reset ();
}
Array (std::initializer_list <T> list) {
Array (const std::initializer_list <T> &list) {
for (const auto &elem : list) {
push (elem);
}

View file

@ -40,6 +40,13 @@ template <typename K> struct IntHash {
}
};
// template for np hashing integers
template <typename K> struct IntNoHash {
uint32 operator () (K key) const {
return static_cast <uint32> (key);
}
};
namespace detail {
struct DictionaryList {
uint32 index;

View file

@ -862,6 +862,14 @@ public:
return buffer;
}
// checks if string is not empty
bool isEmpty (const char *input) const {
if (input == nullptr) {
return true;
}
return *input == '\0';
}
};
// expose global string pool
@ -1082,14 +1090,19 @@ public:
String strToUpper (const String &in) {
String result (in);
auto ptr = const_cast <char *> (result.chars ());
int32 len = 0;
wchar_t wide;
while (*ptr && len < static_cast <int32> (result.length ())) {
while (*ptr) {
wchar_t wide = 0;
multiByteToWideChar (&wide, ptr);
ptr += wideCharToMultiByte (ptr, toUpper (wide));
len += wideCharToMultiByte (ptr, toUpper (wide));
if (static_cast <size_t> (len) >= result.length ()) {
break;
}
}
return result;
}

View file

@ -160,8 +160,8 @@ public:
}
static const Vector &null () {
static const auto s_zero = Vector (0.0f, 0.0f, 0.0f);
return s_zero;
static const Vector &s_null {};
return s_null;
}
void clear () {
@ -224,6 +224,27 @@ public:
upward->z = cosines[roll] * cosines[pitch];
}
}
const Vector &forward () {
static Vector s_fwd {};
buildVectors (&s_fwd, nullptr, nullptr);
return s_fwd;
}
const Vector &upward () {
static Vector s_up {};
buildVectors (nullptr, nullptr, &s_up);
return s_up;
}
const Vector &right () {
static Vector s_right {};
buildVectors (nullptr, &s_right, nullptr);
return s_right;
}
};
// expose global null vector

View file

@ -33,34 +33,6 @@ CR_DECLARE_SCOPED_ENUM (Var,
NoRegister
)
// netmessage functions
CR_DECLARE_SCOPED_ENUM (NetMsg,
None = -1,
VGUI = 1,
ShowMenu = 2,
WeaponList = 3,
CurWeapon = 4,
AmmoX = 5,
AmmoPickup = 6,
Damage = 7,
Money = 8,
StatusIcon = 9,
DeathMsg = 10,
ScreenFade = 11,
HLTV = 12,
TextMsg = 13,
TeamInfo = 14,
BarTime = 15,
SendAudio = 17,
SayText = 18,
BotVoice = 19,
NVGToggle = 20,
FlashBat = 21,
Fashlight = 22,
ItemStatus = 23,
Count = 25
)
// supported cs's
CR_DECLARE_SCOPED_ENUM (GameFlags,
Modern = cr::bit (0), // counter-strike 1.6 and above
@ -97,19 +69,6 @@ struct VarPair {
class ConVar *self;
};
// network message block
struct MessageBlock {
int bot;
int state;
int msg;
int regMsgs[NetMsg::Count];
};
// referentia vector info
struct RefVector {
Vector forward, right, up;
};
// entity prototype
using EntityFunction = void (*) (entvars_t *);
@ -126,9 +85,8 @@ private:
edict_t *m_startEntity;
edict_t *m_localEntity;
Array <VarPair> m_cvars;
SmallArray <VarPair> m_cvars;
SharedLibrary m_gameLib;
MessageBlock m_msgBlock;
bool m_precached;
int m_gameFlags;
@ -136,12 +94,9 @@ private:
float m_slowFrame; // per second updated frame
public:
RefVector vec;
public:
Game ();
~Game ();
~Game () = default;
public:
// precaches internal stuff
@ -189,9 +144,6 @@ public:
// sends local registration stack for engine registration
void registerCvars (bool gameVars = false);
// do actual network message processing
void processMessages (void *ptr);
// checks whether softwared rendering is enabled
bool isSoftwareRenderer ();
@ -207,9 +159,6 @@ public:
// executes stuff every 1 second
void slowFrame ();
// begin message handler
void beginMessage (edict_t *ent, int dest, int type);
// public inlines
public:
// get the current time on server
@ -281,40 +230,11 @@ public:
}
// gets the player team
int getTeam (edict_t *ent);
// resets the message capture mechanism
void resetMessages () {
m_msgBlock.msg = NetMsg::None;
m_msgBlock.state = 0;
m_msgBlock.bot = 0;
};
// sets the currently executed message
void setCurrentMessageId (int message) {
m_msgBlock.msg = message;
}
// set the bot entity that receive this message
void setCurrentMessageOwner (int id) {
m_msgBlock.bot = id;
}
// find registered message id
int getMessageId (int type) {
return m_msgBlock.regMsgs[type];
}
// assigns message id for message type
void setMessageId (int type, int id) {
m_msgBlock.regMsgs[type] = id;
}
// tries to set needed message id
void captureMessage (int type, int msgId) {
if (type == m_msgBlock.regMsgs[msgId]) {
setCurrentMessageId (msgId);
int getTeam (edict_t *ent) {
if (isNullEntity (ent)) {
return Team::Unassigned;
}
return util.getClient (indexOfPlayer (ent)).team;
}
// sets the precache to uninitialize
@ -332,11 +252,6 @@ public:
m_localEntity = ent;
}
// builds referential vector
void makeVectors (const Vector &in) {
in.buildVectors (&vec.forward, &vec.right, &vec.up);
}
// check the engine visibility wrapper
bool checkVisibility (edict_t *ent, uint8 *set);
@ -401,7 +316,7 @@ public:
};
// simplify access for console variables
class ConVar {
class ConVar final {
public:
cvar_t *eptr;
@ -439,7 +354,7 @@ public:
}
};
class MessageWriter {
class MessageWriter final {
private:
bool m_autoDestruct { false };

436
include/graph.h Normal file
View file

@ -0,0 +1,436 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// defines for nodes flags field (32 bits are available)
CR_DECLARE_SCOPED_ENUM (NodeFlag,
Lift = cr::bit (1), // wait for lift to be down before approaching this node
Crouch = cr::bit (2), // must crouch to reach this node
Crossing = cr::bit (3), // a target node
Goal = cr::bit (4), // mission goal point (bomb, hostage etc.)
Ladder = cr::bit (5), // node is on ladder
Rescue = cr::bit (6), // node is a hostage rescue point
Camp = cr::bit (7), // node is a camping point
NoHostage = cr::bit (8), // only use this node if no hostage
DoubleJump = cr::bit (9), // bot help's another bot (requster) to get somewhere (using djump)
Sniper = cr::bit (28), // it's a specific sniper point
TerroristOnly = cr::bit (29), // it's a specific terrorist point
CTOnly = cr::bit (30), // it's a specific ct point
)
// defines for node connection flags field (16 bits are available)
CR_DECLARE_SCOPED_ENUM_TYPE (PathFlag, uint16,
Jump = cr::bit (0) // must jump for this connection
)
// enum pathfind search type
CR_DECLARE_SCOPED_ENUM (FindPath,
Fast = 0,
Optimal,
Safe
)
// defines node connection types
CR_DECLARE_SCOPED_ENUM (PathConnection,
Outgoing = 0,
Incoming,
Bidirectional
)
// defines node add commands
CR_DECLARE_SCOPED_ENUM (GraphAdd,
Normal = 0,
)
// a* route state
CR_DECLARE_SCOPED_ENUM (RouteState,
Open = 0,
Closed,
New
)
// node edit states
CR_DECLARE_SCOPED_ENUM (GraphEdit,
On = cr::bit (1),
Noclip = cr::bit (2),
Auto = cr::bit (3)
)
// storage header options
CR_DECLARE_SCOPED_ENUM (StorageOption,
Practice = cr::bit (0), // this is practice (experience) file
Matrix = cr::bit (1), // this is floyd warshal path & distance matrix
Vistable = cr::bit (2), // this is vistable data
Graph = cr::bit (3), // this is a node graph data
Official = cr::bit (4), // this is additional flag for graph indicates graph are official
Recovered = cr::bit (5), // this is additional flag indicates graph converted from podbot and was bad
Author = cr::bit (6) // this is additional flag indicates that there's author info
)
// storage header versions
CR_DECLARE_SCOPED_ENUM (StorageVersion,
Graph = 1,
Practice = 1,
Vistable = 1,
Matrix = 1,
Podbot = 7
)
// lift usage states
CR_DECLARE_SCOPED_ENUM (LiftState,
None = 0,
LookingButtonOutside,
WaitingFor,
EnteringIn,
WaitingForTeammates,
LookingButtonInside,
TravelingBy,
Leaving
)
// a* route
struct Route {
float g, f;
int parent;
RouteState state;
};
// general stprage header information structure
struct StorageHeader {
int32 magic;
int32 version;
int32 options;
int32 length;
int32 compressed;
int32 uncompressed;
};
// general waypoint header information structure
struct PODGraphHeader {
char header[8];
int32 fileVersion;
int32 pointNumber;
char mapName[32];
char author[32];
};
// floyd-warshall matrices
struct Matrix {
int16 dist;
int16 index;
};
// experience data hold in memory while playing
struct Practice {
int16 damage[kGameTeamNum];
int16 index[kGameTeamNum];
int16 value[kGameTeamNum];
};
// defines linked waypoints
struct PathLink {
Vector velocity;
int32 distance;
uint16 flags;
int16 index;
};
// defines visibility count
struct PathVis {
uint16 stand, crouch;
};
// define graph path structure for yapb
struct Path {
int32 number, flags;
Vector origin, start, end;
float radius, light, display;
PathLink links[kMaxNodeLinks];
PathVis vis;
};
// define waypoint structure for podbot (will convert on load)
struct PODPath {
int32 number, flags;
Vector origin;
float radius, csx, csy, cex, cey;
int16 index[kMaxNodeLinks];
uint16 conflags[kMaxNodeLinks];
Vector velocity[kMaxNodeLinks];
int32 distance[kMaxNodeLinks];
PathVis vis;
};
// this structure links nodes returned from pathfinder
class PathWalk final : public DenyCopying {
private:
size_t m_cursor = 0;
Array <int, ReservePolicy::Single, kMaxRouteLength> m_storage;
public:
explicit PathWalk () = default;
~PathWalk () = default;
public:
int32 &next () {
return at (m_cursor + 1);
}
int32 &first () {
return at (m_cursor);
}
int32 &last () {
return m_storage.last ();
}
int32 &at (size_t index) {
return m_storage.at (index);
}
void shift () {
++m_cursor;
}
void reverse () {
m_storage.reverse ();
}
size_t length () const {
if (m_cursor > m_storage.length ()) {
return 0;
}
return m_storage.length () - m_cursor;
}
size_t cursor () const {
return m_cursor;
}
bool hasNext () const {
return m_cursor < m_storage.length ();
}
bool empty () const {
return !length ();
}
void push (int node) {
m_storage.push (node);
}
void clear () {
m_cursor = 0;
m_storage.clear ();
}
};
// graph operation class
class BotGraph final : public Singleton <BotGraph> {
public:
friend class Bot;
private:
struct Bucket {
int x, y, z;
};
int m_editFlags;
int m_loadAttempts;
int m_cacheNodeIndex;
int m_lastJumpNode;
int m_findWPIndex;
int m_facingAtIndex;
int m_highestDamage[kGameTeamNum];
float m_timeJumpStarted;
float m_autoPathDistance;
float m_pathDisplayTime;
float m_arrowDisplayTime;
bool m_isOnLadder;
bool m_endJumpPoint;
bool m_jumpLearnNode;
bool m_hasChanged;
bool m_needsVisRebuild;
Vector m_learnVelocity;
Vector m_learnPosition;
Vector m_bombPos;
Vector m_lastNode;
IntArray m_terrorPoints;
IntArray m_ctPoints;
IntArray m_goalPoints;
IntArray m_campPoints;
IntArray m_sniperPoints;
IntArray m_rescuePoints;
IntArray m_visitedGoals;
SmallArray <int32> m_buckets[kMaxBucketsInsidePos][kMaxBucketsInsidePos][kMaxBucketsInsidePos];
SmallArray <Matrix> m_matrix;
SmallArray <Practice> m_practice;
SmallArray <Path> m_paths;
SmallArray <uint8> m_vistable;
String m_tempStrings;
edict_t *m_editor;
public:
BotGraph ();
~BotGraph () = default;
public:
int getFacingIndex ();
int getFarest (const Vector &origin, float maxDistance = 32.0);
int getNearest (const Vector &origin, float minDistance = kInfiniteDistance, int flags = -1);
int getNearestNoBuckets (const Vector &origin, float minDistance = kInfiniteDistance, int flags = -1);
int getEditorNeareset ();
int getDangerIndex (int team, int start, int goal);
int getDangerValue (int team, int start, int goal);
int getDangerDamage (int team, int start, int goal);
int getPathDist (int srcIndex, int destIndex);
int clearConnections (int index);
float calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin);
bool convertOldFormat ();
bool isVisible (int srcIndex, int destIndex);
bool isStandVisible (int srcIndex, int destIndex);
bool isDuckVisible (int srcIndex, int destIndex);
bool isConnected (int a, int b);
bool isConnected (int index);
bool isNodeReacheable (const Vector &src, const Vector &destination);
bool checkNodes (bool teleportPlayer);
bool loadPathMatrix ();
bool isVisited (int index);
bool saveGraphData ();
bool loadGraphData ();
template <typename U> bool saveStorage (const String &ext, const String &name, StorageOption options, StorageVersion version, const SmallArray <U> &data, uint8 *blob);
template <typename U> bool loadStorage (const String &ext, const String &name, StorageOption options, StorageVersion version, SmallArray <U> &data, uint8 *blob, int32 *outOptions);
void saveOldFormat ();
void initGraph ();
void frame ();
void loadPractice ();
void loadVisibility ();
void initNodesTypes ();
void initLightLevels ();
void addPath (int addIndex, int pathIndex, float distance);
void add (int type, const Vector &pos = nullvec);
void erase (int target);
void toggleFlags (int toggleFlag);
void setRadius (int index, float radius);
void rebuildVisibility ();
void pathCreate (char dir);
void erasePath ();
void cachePoint (int index);
void calculatePathRadius (int index);
void savePractice ();
void saveVisibility ();
void addBasic ();
void eraseFromDisk ();
void savePathMatrix ();
void setSearchIndex (int index);
void startLearnJump ();
void setVisited (int index);
void clearVisited ();
void initBuckets ();
void addToBucket (const Vector &pos, int index);
void eraseFromBucket (const Vector &pos, int index);
void setBombPos (bool reset = false, const Vector &pos = nullvec);
void updateGlobalPractice ();
void unassignPath (int from, int to);
void setDangerValue (int team, int start, int goal, int value);
void setDangerDamage (int team, int start, int goal, int value);
void convertFromPOD (Path &path, const PODPath &pod);
void convertToPOD (const Path &path, PODPath &pod);
void convertCampDirection (Path &path);
const char *getDataDirectory (bool isMemoryFile = false);
const char *getOldFormatGraphName (bool isMemoryFile = false);
Bucket locateBucket (const Vector &pos);
IntArray searchRadius (float radius, const Vector &origin, int maxCount = -1);
const SmallArray <int32> &getNodesInBucket (const Vector &pos);
public:
int getHighestDamageForTeam (int team) const {
return m_highestDamage[team];
}
void setHighestDamageForTeam (int team, int value) {
m_highestDamage[team] = value;
}
const char *getAuthor () const {
return m_tempStrings.chars ();
}
bool hasChanged () const {
return m_hasChanged;
}
bool hasEditFlag (int flag) const {
return !!(m_editFlags & flag);
}
void setEditFlag (int flag) {
m_editFlags |= flag;
}
void clearEditFlag (int flag) {
m_editFlags &= ~flag;
}
void setAutoPathDistance (const float distance) {
m_autoPathDistance = distance;
}
const Vector &getBombPos () const {
return m_bombPos;
}
// access paths
Path &operator [] (int index) {
return m_paths[index];
}
// check nodes range
bool exists (int index) const {
return index >= 0 && index < static_cast <int> (m_paths.length ());
}
// get real nodes num
int length () const {
return m_paths.length ();
}
// check if has editor
bool hasEditor () const {
return !!m_editor;
}
// set's the node editor
void setEditor (edict_t *ent) {
m_editor = ent;
}
// get the current node editor
edict_t *getEditor () {
return m_editor;
}
};
// explose global
static auto &graph = BotGraph::get ();

273
include/manager.h Normal file
View file

@ -0,0 +1,273 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// bot creation tab
struct CreateQueue {
bool manual;
int difficulty;
int team;
int member;
int personality;
String name;
};
// manager class
class BotManager final : public Singleton <BotManager> {
public:
using ForEachBot = Lambda <bool (Bot *)>;
using UniqueBot = UniquePtr <Bot>;
private:
float m_timeRoundStart;
float m_timeRoundEnd;
float m_timeRoundMid;
float m_maintainTime; // time to maintain bot creation
float m_quotaMaintainTime; // time to maintain bot quota
float m_grenadeUpdateTime; // time to update active grenades
float m_entityUpdateTime; // time to update intresting entities
float m_plantSearchUpdateTime; // time to update for searching planted bomb
float m_lastChatTime; // global chat time timestamp
float m_timeBombPlanted; // time the bomb were planted
float m_lastRadioTime[kGameTeamNum]; // global radio time
int m_lastWinner; // the team who won previous round
int m_lastDifficulty; // last bots difficulty
int m_bombSayStatus; // some bot is issued whine about bomb
int m_lastRadio[kGameTeamNum]; // last radio message for team
bool m_leaderChoosen[kGameTeamNum]; // is team leader choose theese round
bool m_economicsGood[kGameTeamNum]; // is team able to buy anything
bool m_bombPlanted;
bool m_botsCanPause;
bool m_roundEnded;
Array <edict_t *> m_activeGrenades; // holds currently active grenades on the map
Array <edict_t *> m_intrestingEntities; // holds currently intresting entities on the map
SmallArray <CreateQueue> m_creationTab; // bot creation tab
SmallArray <BotTask> m_filters; // task filters
SmallArray <UniqueBot> m_bots; // all available bots
edict_t *m_killerEntity; // killer entity for bots
protected:
BotCreateResult create (const String &name, int difficulty, int personality, int team, int member);
public:
BotManager ();
~BotManager () = default;
public:
Twin <int, int> countTeamPlayers ();
Bot *findBotByIndex (int index);
Bot *findBotByEntity (edict_t *ent);
Bot *findAliveBot ();
Bot *findHighestFragBot (int team);
int getHumansCount (bool ignoreSpectators = false);
int getAliveHumansCount ();
int getBotCount ();
float getConnectionTime (int botId);
void setBombPlanted (bool isPlanted);
void slowFrame ();
void frame ();
void createKillerEntity ();
void destroyKillerEntity ();
void touchKillerEntity (Bot *bot);
void destroy ();
void addbot (const String &name, int difficulty, int personality, int team, int member, bool manual);
void addbot (const String &name, const String &difficulty, const String &personality, const String &team, const String &member, bool manual);
void serverFill (int selection, int personality = Personality::Normal, int difficulty = -1, int numToAdd = -1);
void kickEveryone (bool instant = false, bool zeroQuota = true);
bool kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
void kickBot (int index);
void kickFromTeam (Team team, bool removeAll = false);
void killAllBots (int team = -1);
void maintainQuota ();
void initQuota ();
void initRound ();
void decrementQuota (int by = 1);
void selectLeaders (int team, bool reset);
void listBots ();
void setWeaponMode (int selection);
void updateTeamEconomics (int team, bool setTrue = false);
void updateBotDifficulties ();
void reset ();
void initFilters ();
void resetFilters ();
void updateActiveGrenade ();
void updateIntrestingEntities ();
void captureChatRadio (const char *cmd, const char *arg, edict_t *ent);
void notifyBombDefuse ();
void execGameEntity (entvars_t *vars);
void forEach (ForEachBot handler);
void erase (Bot *bot);
void handleDeath (edict_t *killer, edict_t *victim);
bool isTeamStacked (int team);
public:
Array <edict_t *> &searchActiveGrenades () {
return m_activeGrenades;
}
Array <edict_t *> &searchIntrestingEntities () {
return m_intrestingEntities;
}
bool hasActiveGrenades () const {
return !m_activeGrenades.empty ();
}
bool hasIntrestingEntities () const {
return !m_intrestingEntities.empty ();
}
bool checkTeamEco (int team) const {
return m_economicsGood[team];
}
int getLastWinner () const {
return m_lastWinner;
}
void setLastWinner (int winner) {
m_lastWinner = winner;
}
// get the list of filters
SmallArray <BotTask> &getFilters () {
return m_filters;
}
void createRandom (bool manual = false) {
addbot ("", -1, -1, -1, -1, manual);
}
bool isBombPlanted () const {
return m_bombPlanted;
}
float getTimeBombPlanted () const {
return m_timeBombPlanted;
}
float getRoundStartTime () const {
return m_timeRoundStart;
}
float getRoundMidTime () const {
return m_timeRoundMid;
}
float getRoundEndTime () const {
return m_timeRoundEnd;
}
bool isRoundOver () const {
return m_roundEnded;
}
void setRoundOver (const bool over) {
m_roundEnded = over;
}
bool canPause () const {
return m_botsCanPause;
}
void setCanPause (const bool pause) {
m_botsCanPause = pause;
}
bool hasBombSay (int type) {
return (m_bombSayStatus & type) == type;
}
void clearBombSay (int type) {
m_bombSayStatus &= ~type;
}
void setPlantedBombSearchTimestamp (const float timestamp) {
m_plantSearchUpdateTime = timestamp;
}
float getPlantedBombSearchTimestamp () const {
return m_plantSearchUpdateTime;
}
void setLastRadioTimestamp (const int team, const float timestamp) {
if (team == Team::CT || team == Team::Terrorist) {
m_lastRadioTime[team] = timestamp;
}
}
float getLastRadioTimestamp (const int team) const {
if (team == Team::CT || team == Team::Terrorist) {
return m_lastRadioTime[team];
}
return 0.0f;
}
void setLastRadio (const int team, const int radio) {
m_lastRadio[team] = radio;
}
int getLastRadio (const int team) const {
return m_lastRadio[team];
}
void setLastChatTimestamp (const float timestamp) {
m_lastChatTime = timestamp;
}
float getLastChatTimestamp () const {
return m_lastChatTime;
}
// some bots are online ?
bool hasBotsOnline () {
return getBotCount () > 0;
}
public:
Bot *operator [] (int index) {
return findBotByIndex (index);
}
Bot *operator [] (edict_t *ent) {
return findBotByEntity (ent);
}
public:
UniqueBot *begin () {
return m_bots.begin ();
}
UniqueBot *begin () const {
return m_bots.begin ();
}
UniqueBot *end () {
return m_bots.end ();
}
UniqueBot *end () const {
return m_bots.end ();
}
};
// explose global
static auto &bots = BotManager::get ();

147
include/message.h Normal file
View file

@ -0,0 +1,147 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// netmessage functions enum
CR_DECLARE_SCOPED_ENUM (NetMsg,
None = -1,
VGUIMenu = 1,
ShowMenu = 2,
WeaponList = 3,
CurWeapon = 4,
AmmoX = 5,
AmmoPickup = 6,
Damage = 7,
Money = 8,
StatusIcon = 9,
DeathMsg = 10,
ScreenFade = 11,
HLTV = 12,
TextMsg = 13,
TeamInfo = 14,
BarTime = 15,
SendAudio = 17,
BotVoice = 18,
NVGToggle = 19,
FlashBat = 20,
Fashlight = 21,
ItemStatus = 22
)
// vgui menus (since latest steam updates is obsolete, but left for old cs)
CR_DECLARE_SCOPED_ENUM (GuiMenu,
TeamSelect = 2, // menu select team
TerroristSelect = 26, // terrorist select menu
CTSelect = 27 // ct select menu
)
// cache flags for TextMsg message
CR_DECLARE_SCOPED_ENUM (TextMsgCache,
NeedHandle = cr::bit (0),
TerroristWin = cr::bit (1),
CounterWin = cr::bit (2),
Commencing = cr::bit (3),
BombPlanted = cr::bit (4),
RestartRound = cr::bit (5),
BurstOn = cr::bit (6),
BurstOff = cr::bit (7)
)
// cache flags for StatusIcon message
CR_DECLARE_SCOPED_ENUM (StatusIconCache,
NeedHandle = cr::bit (0),
BuyZone = cr::bit (1),
VipSafety = cr::bit (2),
C4 = cr::bit (3)
)
class MessageDispatcher final : public Singleton <MessageDispatcher> {
private:
using MsgFunc = void (MessageDispatcher::*) ();
private:
struct Args {
union {
float float_;
long long_;
const char *chars_;
};
public:
Args (float value) : float_ (value) { }
Args (int value) : long_ (value) { }
Args (const char *value) : chars_ (value) { }
};
private:
Dictionary <String, int32> m_textMsgCache; // cache strings for faster access for textmsg
Dictionary <String, int32> m_showMenuCache; // cache for the showmenu message
Dictionary <String, int32> m_statusIconCache; // cache for status icon message
Dictionary <String, int32> m_teamInfoCache; // cache for teaminfo message
private:
bool m_broadcast; // message for all players
Bot *m_bot; // owner of a message
NetMsg m_current; // ongoing message id
SmallArray <Args> m_args; // args collected from write* functions
Dictionary <String, NetMsg> m_wanted; // wanted messages
Dictionary <NetMsg, int32, IntNoHash <int32>> m_maps; // maps our message to id to engine message id
Dictionary <NetMsg, MsgFunc, IntNoHash <int32>> m_handlers; // maps our message id to handler function
private:
void netMsgTextMsg ();
void netMsgVGUIMenu ();
void netMsgShowMenu ();
void netMsgWeaponList ();
void netMsgCurWeapon ();
void netMsgAmmoX ();
void netMsgAmmoPickup ();
void netMsgDamage ();
void netMsgMoney ();
void netMsgStatusIcon ();
void netMsgDeathMsg ();
void netMsgScreenFade ();
void netMsgHLTV ();
void netMsgTeamInfo ();
void netMsgBarTime ();
void netMsgItemStatus ();
void netMsgNVGToggle ();
void netMsgFlashBat ();
public:
MessageDispatcher ();
~MessageDispatcher () = default;
public:
void registerMessage (const String &name, int32 id);
void start (edict_t *ent, int32 dest, int32 type);
void stop ();
public:
template <typename T> void collect (const T &value) {
if (m_current == NetMsg::None) {
return;
}
m_args.emplace (value);
}
void stopCollection () {
m_current = NetMsg::None;
}
int32 id (NetMsg msg) const {
return m_maps[msg];
}
};
static auto &msgs = MessageDispatcher::get ();

137
include/utils.h Normal file
View file

@ -0,0 +1,137 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
class BotUtils final : public Singleton <BotUtils> {
private:
bool m_needToSendWelcome;
float m_welcomeReceiveTime;
StringArray m_sentences;
SmallArray <Client> m_clients;
SmallArray <Twin <String, String>> m_tags;
SimpleHook m_sendToHook;
public:
BotUtils ();
~BotUtils () = default;
public:
// need to send welcome message ?
void checkWelcome ();
// gets the weapon alias as hlsting, maybe move to config...
int getWeaponAlias (bool needString, const char *weaponAlias, int weaponIndex = -1);
// gets the build number of bot
int buildNumber ();
// gets the shooting cone deviation
float getShootingCone (edict_t *ent, const Vector &position);
// check if origin is visible from the entity side
bool isVisible (const Vector &origin, edict_t *ent);
// check if entity is alive
bool isAlive (edict_t *ent);
// check if origin is inside view cone of entity
bool isInViewCone (const Vector &origin, edict_t *ent);
// checks if entitiy is fakeclient
bool isFakeClient (edict_t *ent);
// check if entitiy is a player
bool isPlayer (edict_t *ent);
// check if entity is a vip
bool isPlayerVIP (edict_t *ent);
// opens config helper
bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant = false);
// nearest player search helper
bool findNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false, bool needBotWithC4 = false);
// tracing decals for bots spraying logos
void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex);
// attaches sound to client struct
void attachSoundsToClients (edict_t *ent, const char *sample, float volume);
// simulate sound for players
void simulateSoundUpdates (int playerIndex);
// update stats on clients
void updateClients ();
// chat helper to strip the clantags out of the string
void stripTags (String &line);
// chat helper to make player name more human-like
void humanizePlayerName (String &playerName);
// chat helper to add errors to the bot chat string
void addChatErrors (String &line);
// chat helper to find keywords for given string
bool checkKeywords (const String &line, String &reply);
// generates ping bitmask for SVC_PINGS message
int getPingBitmask (edict_t *ent, int loss, int ping);
// calculate our own pings for all the players
void calculatePings ();
// send modified pings to all the clients
void sendPings (edict_t *to);
// installs the sendto function intreception
void installSendTo ();
public:
// re-show welcome after changelevel ?
void setNeedForWelcome (bool need) {
m_needToSendWelcome = need;
}
// get array of clients
SmallArray <Client> &getClients () {
return m_clients;
}
// get clients as const-reference
const SmallArray <Client> &getClients () const {
return m_clients;
}
// get single client as ref
Client &getClient (const int index) {
return m_clients[index];
}
// disables send hook
bool disableSendTo () {
return m_sendToHook.disable ();
}
// enables send hook
bool enableSendTo () {
return m_sendToHook.enable ();
}
public:
static int32 CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength);
};
// explose global
static auto &util = BotUtils::get ();

File diff suppressed because it is too large Load diff