merge changes from the defunct ubot
This commit is contained in:
parent
1904b22977
commit
cf501b75b7
90 changed files with 11977 additions and 3907 deletions
251
inc/config.h
Normal file
251
inc/config.h
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// botname structure definition
|
||||
struct BotName {
|
||||
String name = "";
|
||||
int usedBy = -1;
|
||||
|
||||
public:
|
||||
BotName () = default;
|
||||
BotName (StringRef name, int usedBy) : name (name), usedBy (usedBy) { }
|
||||
};
|
||||
|
||||
// voice config structure definition
|
||||
struct ChatterItem {
|
||||
String name;
|
||||
float repeat;
|
||||
float duration;
|
||||
|
||||
public:
|
||||
ChatterItem (StringRef name, float repeat, float duration) : name (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> {
|
||||
public:
|
||||
struct DifficultyData {
|
||||
float reaction[2] {};
|
||||
int32 headshotPct {};
|
||||
int32 seenThruPct {};
|
||||
int32 hearThruPct {};
|
||||
};
|
||||
|
||||
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;
|
||||
Dictionary <int32, DifficultyData, IntNoHash <int32>> m_difficulty;
|
||||
|
||||
// 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 };
|
||||
|
||||
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 ();
|
||||
|
||||
// load bots difficulty config
|
||||
void loadDifficultyConfig ();
|
||||
|
||||
// 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 (StringRef input);
|
||||
|
||||
private:
|
||||
bool isCommentLine (StringRef line) const {
|
||||
if (line.empty ()) {
|
||||
return true;
|
||||
}
|
||||
return line.substr (0, 1).findFirstOf ("#/; \n\r") != String::InvalidIndex;
|
||||
};
|
||||
|
||||
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
|
||||
StringRef 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 ();
|
||||
}
|
||||
|
||||
// get's the weapons prop
|
||||
WeaponProp &getWeaponProp (int id) {
|
||||
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's the difficulty level tweaks
|
||||
DifficultyData *getDifficultyTweaks (int32 level) {
|
||||
if (level < Difficulty::Noob || level > Difficulty::Expert) {
|
||||
return &m_difficulty[Difficulty::Expert];
|
||||
}
|
||||
return &m_difficulty[level];
|
||||
}
|
||||
|
||||
// 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)
|
||||
StringRef getRandomAvatar () const {
|
||||
if (!m_avatars.empty ()) {
|
||||
return m_avatars.random ();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// get's random logo index
|
||||
int32 getRandomLogoIndex () const {
|
||||
return static_cast <int32> (m_logos.index (m_logos.random ()));
|
||||
}
|
||||
|
||||
// get random name by index
|
||||
StringRef getRandomLogoName (int index) {
|
||||
return m_logos[index];
|
||||
}
|
||||
};
|
||||
|
||||
// explose global
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotConfig, conf);
|
||||
217
inc/control.h
Normal file
217
inc/control.h
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#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 (StringRef name, StringRef format, StringRef help, Handler handler) : name (name), format (format), help (help), handler (cr::move (handler)) { }
|
||||
};
|
||||
|
||||
// single bot menu
|
||||
struct BotMenu {
|
||||
int ident, slots;
|
||||
String text;
|
||||
MenuHandler handler;
|
||||
|
||||
public:
|
||||
BotMenu (int ident, int slots, StringRef text, MenuHandler handler) : ident (ident), slots (slots), text (text), handler (cr::move (handler)) { }
|
||||
};
|
||||
|
||||
private:
|
||||
StringArray m_args;
|
||||
Array <BotCmd> m_cmds;
|
||||
Array <BotMenu> m_menus;
|
||||
IntArray m_campIterator;
|
||||
|
||||
edict_t *m_ent;
|
||||
Bot *m_djump;
|
||||
|
||||
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 cmdCvars ();
|
||||
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 ();
|
||||
int cmdNodeIterateCamp ();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int intValue (size_t arg) const {
|
||||
if (!hasArg (arg)) {
|
||||
return 0;
|
||||
}
|
||||
return m_args[arg].int_ ();
|
||||
}
|
||||
|
||||
StringRef strValue (size_t arg) {
|
||||
if (!hasArg (arg)) {
|
||||
return "";
|
||||
}
|
||||
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
|
||||
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) > 56) {
|
||||
game.clientPrint (m_ent, result);
|
||||
}
|
||||
else {
|
||||
game.centerPrint (m_ent, result);
|
||||
game.clientPrint (m_ent, result);
|
||||
}
|
||||
}
|
||||
|
||||
// explose global
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotControl, ctrl);
|
||||
677
inc/engine.h
Normal file
677
inc/engine.h
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// line draw
|
||||
CR_DECLARE_SCOPED_ENUM (DrawLine,
|
||||
Simple,
|
||||
Arrow,
|
||||
Count
|
||||
)
|
||||
|
||||
// trace ignore
|
||||
CR_DECLARE_SCOPED_ENUM (TraceIgnore,
|
||||
None = 0,
|
||||
Glass = cr::bit (0),
|
||||
Monsters = cr::bit (1),
|
||||
Everything = Glass | Monsters
|
||||
)
|
||||
|
||||
// variable type
|
||||
CR_DECLARE_SCOPED_ENUM (Var,
|
||||
Normal = 0,
|
||||
ReadOnly,
|
||||
Password,
|
||||
NoServer,
|
||||
GameRef
|
||||
)
|
||||
|
||||
// supported cs's
|
||||
CR_DECLARE_SCOPED_ENUM (GameFlags,
|
||||
Modern = cr::bit (0), // counter-strike 1.6 and above
|
||||
Xash3D = cr::bit (1), // counter-strike 1.6 under the xash engine (additional flag)
|
||||
ConditionZero = cr::bit (2), // counter-strike: condition zero
|
||||
Legacy = cr::bit (3), // counter-strike 1.3-1.5 with/without steam
|
||||
Mobility = cr::bit (4), // additional flag that bot is running on android (additional flag)
|
||||
CSBot = cr::bit (5), // additional flag that indicates official cs bots are in game
|
||||
Metamod = cr::bit (6), // game running under meta\mod
|
||||
CSDM = cr::bit (7), // csdm mod currently in use
|
||||
FreeForAll = cr::bit (8), // csdm mod with ffa mode
|
||||
ReGameDLL = cr::bit (9), // server dll is a regamedll
|
||||
HasFakePings = cr::bit (10), // on that game version we can fake bots pings
|
||||
HasBotVoice = cr::bit (11) // on that game version we can use chatter
|
||||
)
|
||||
|
||||
// defines map type
|
||||
CR_DECLARE_SCOPED_ENUM (MapFlags,
|
||||
Assassination = cr::bit (0),
|
||||
HostageRescue = cr::bit (1),
|
||||
Demolition = cr::bit (2),
|
||||
Escape = cr::bit (3),
|
||||
KnifeArena = cr::bit (4),
|
||||
Fun = cr::bit (5),
|
||||
HasDoors = cr::bit (10), // additional flags
|
||||
HasButtons = cr::bit (11) // map has buttons
|
||||
)
|
||||
|
||||
// recursive entity search
|
||||
CR_DECLARE_SCOPED_ENUM (EntitySearchResult,
|
||||
Continue,
|
||||
Break
|
||||
)
|
||||
|
||||
// variable reg pair
|
||||
struct ConVarReg {
|
||||
cvar_t reg;
|
||||
String info;
|
||||
String init;
|
||||
String regval;
|
||||
class ConVar *self;
|
||||
float initial, min, max;
|
||||
bool missing;
|
||||
bool bounded;
|
||||
int32 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 {
|
||||
public:
|
||||
EngineWrap () = default;
|
||||
~EngineWrap () = default;
|
||||
|
||||
private:
|
||||
const char *allocStr (const char *str) const {
|
||||
return STRING (engfuncs.pfnAllocString (str));
|
||||
}
|
||||
|
||||
public:
|
||||
int32 precacheModel (const char *model) const {
|
||||
return engfuncs.pfnPrecacheModel (allocStr (model));
|
||||
}
|
||||
|
||||
int32 precacheSound (const char *sound) const {
|
||||
return engfuncs.pfnPrecacheSound (allocStr (sound));
|
||||
}
|
||||
|
||||
void setModel (edict_t *ent, const char *model) {
|
||||
engfuncs.pfnSetModel (ent, allocStr (model));
|
||||
}
|
||||
};
|
||||
|
||||
// provides utility functions to not call original engine (less call-cost)
|
||||
class Game final : public Singleton <Game> {
|
||||
public:
|
||||
using EntitySearch = Lambda <EntitySearchResult (edict_t *)>;
|
||||
|
||||
private:
|
||||
int m_drawModels[DrawLine::Count];
|
||||
int m_spawnCount[Team::Unassigned];
|
||||
|
||||
// bot client command
|
||||
StringArray m_botArgs;
|
||||
|
||||
edict_t *m_startEntity;
|
||||
edict_t *m_localEntity;
|
||||
|
||||
Array <edict_t *> m_breakables;
|
||||
SmallArray <ConVarReg> m_cvars;
|
||||
SharedLibrary m_gameLib;
|
||||
EngineWrap m_engineWrap;
|
||||
|
||||
bool m_precached;
|
||||
|
||||
int m_gameFlags {};
|
||||
int m_mapFlags {};
|
||||
|
||||
float m_slowFrame; // per second updated frame
|
||||
|
||||
public:
|
||||
Game ();
|
||||
~Game () = default;
|
||||
|
||||
public:
|
||||
// precaches internal stuff
|
||||
void precache ();
|
||||
|
||||
// initialize levels
|
||||
void levelInitialize (edict_t *entities, int max);
|
||||
|
||||
// display world line
|
||||
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, const Color &color, int brightness, int speed, int life, DrawLine type = DrawLine::Simple);
|
||||
|
||||
// test line
|
||||
void testLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr);
|
||||
|
||||
// trace line with channel, but allows us to store last traceline bot has fired, saving us some cpu cycles
|
||||
bool testLineChannel (TraceChannel channel, const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr);
|
||||
|
||||
// test line
|
||||
void testHull (const Vector &start, const Vector &end, int ignoreFlags, int hullNumber, edict_t *ignoreEntity, TraceResult *ptr);
|
||||
|
||||
// get's the wave length
|
||||
float getWaveLen (const char *fileName);
|
||||
|
||||
// we are on dedicated server ?
|
||||
bool isDedicated ();
|
||||
|
||||
// get stripped down mod name
|
||||
const char *getModName ();
|
||||
|
||||
// get the valid mapname
|
||||
const char *getMapName ();
|
||||
|
||||
// get the "any" entity origin
|
||||
Vector getEntityWorldOrigin (edict_t *ent);
|
||||
|
||||
// registers a server command
|
||||
void registerEngineCommand (const char *command, void func ());
|
||||
|
||||
// play's sound to client
|
||||
void playSound (edict_t *ent, const char *sound);
|
||||
|
||||
// sends bot command
|
||||
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);
|
||||
|
||||
// check the cvar bounds
|
||||
void checkCvarsBounds ();
|
||||
|
||||
// sends local registration stack for engine registration
|
||||
void registerCvars (bool gameVars = false);
|
||||
|
||||
// checks whether softwared rendering is enabled
|
||||
bool isSoftwareRenderer ();
|
||||
|
||||
// load the cs binary in non metamod mode
|
||||
bool loadCSBinary ();
|
||||
|
||||
// do post-load stuff
|
||||
bool postload ();
|
||||
|
||||
// detects if csdm mod is in use
|
||||
void applyGameModes ();
|
||||
|
||||
// executes stuff every 1 second
|
||||
void slowFrame ();
|
||||
|
||||
// search entities by variable field
|
||||
void searchEntities (StringRef field, StringRef value, EntitySearch functor);
|
||||
|
||||
// search entities in sphere
|
||||
void searchEntities (const Vector &position, float radius, EntitySearch functor);
|
||||
|
||||
// this function is checking that pointed by ent pointer obstacle, can be destroyed
|
||||
bool isShootableBreakable (edict_t *ent);
|
||||
|
||||
// public inlines
|
||||
public:
|
||||
// get the current time on server
|
||||
float time () const {
|
||||
return globals->time;
|
||||
}
|
||||
|
||||
// get "maxplayers" limit on server
|
||||
int maxClients () const {
|
||||
return globals->maxClients;
|
||||
}
|
||||
|
||||
// get the fakeclient command interface
|
||||
bool isBotCmd () const {
|
||||
return !m_botArgs.empty ();
|
||||
}
|
||||
|
||||
// gets custom engine args for client command
|
||||
const char *botArgs () const {
|
||||
return strings.format (String::join (m_botArgs, " ", m_botArgs[0].startsWith ("say") ? 1 : 0).chars ());
|
||||
}
|
||||
|
||||
// gets custom engine argv for client command
|
||||
const char *botArgv (size_t index) const {
|
||||
if (index >= m_botArgs.length ()) {
|
||||
return "";
|
||||
}
|
||||
return m_botArgs[index].chars ();
|
||||
}
|
||||
|
||||
// gets custom engine argc for client command
|
||||
int32 botArgc () const {
|
||||
return m_botArgs.length <int32> ();
|
||||
}
|
||||
|
||||
// gets edict pointer out of entity index
|
||||
edict_t *entityOfIndex (const int index) {
|
||||
return static_cast <edict_t *> (m_startEntity + index);
|
||||
};
|
||||
|
||||
// gets edict pointer out of entity index (player)
|
||||
edict_t *playerOfIndex (const int index) {
|
||||
return entityOfIndex (index) + 1;
|
||||
};
|
||||
|
||||
// gets edict index out of it's pointer
|
||||
int indexOfEntity (const edict_t *ent) {
|
||||
return static_cast <int> (ent - m_startEntity);
|
||||
};
|
||||
|
||||
// gets edict index of it's pointer (player)
|
||||
int indexOfPlayer (const edict_t *ent) {
|
||||
return indexOfEntity (ent) - 1;
|
||||
}
|
||||
|
||||
// verify entity isn't null
|
||||
bool isNullEntity (const edict_t *ent) {
|
||||
return !ent || !indexOfEntity (ent) || ent->free;
|
||||
}
|
||||
|
||||
// get the wroldspawn entity
|
||||
edict_t *getStartEntity () {
|
||||
return m_startEntity;
|
||||
}
|
||||
|
||||
// get spawn count for team
|
||||
int getSpawnCount (int team) const {
|
||||
return m_spawnCount[team];
|
||||
}
|
||||
|
||||
// gets the player team
|
||||
int getTeam (edict_t *ent) {
|
||||
if (isNullEntity (ent)) {
|
||||
return Team::Unassigned;
|
||||
}
|
||||
return util.getClient (indexOfPlayer (ent)).team;
|
||||
}
|
||||
|
||||
// sets the precache to uninitialize
|
||||
void setUnprecached () {
|
||||
m_precached = false;
|
||||
}
|
||||
|
||||
// gets the local entity (host edict)
|
||||
edict_t *getLocalEntity () {
|
||||
return m_localEntity;
|
||||
}
|
||||
|
||||
// sets the local entity (host edict)
|
||||
void setLocalEntity (edict_t *ent) {
|
||||
m_localEntity = ent;
|
||||
}
|
||||
|
||||
// check the engine visibility wrapper
|
||||
bool checkVisibility (edict_t *ent, uint8 *set);
|
||||
|
||||
// get pvs/pas visibility set
|
||||
uint8 *getVisibilitySet (Bot *bot, bool pvs);
|
||||
|
||||
// what kind of game engine / game dll / mod / tool we're running ?
|
||||
bool is (const int type) const {
|
||||
return !!(m_gameFlags & type);
|
||||
}
|
||||
|
||||
// adds game flag
|
||||
void addGameFlag (const int type) {
|
||||
m_gameFlags |= type;
|
||||
}
|
||||
|
||||
// gets the map type
|
||||
bool mapIs (const int type) const {
|
||||
return !!(m_mapFlags & type);
|
||||
}
|
||||
|
||||
// get loaded gamelib
|
||||
const SharedLibrary &lib () {
|
||||
return m_gameLib;
|
||||
}
|
||||
|
||||
// get registered cvars list
|
||||
const SmallArray <ConVarReg> &getCvars () {
|
||||
return m_cvars;
|
||||
}
|
||||
|
||||
// check if map has breakables
|
||||
const Array <edict_t *> &getBreakables () {
|
||||
return m_breakables;
|
||||
}
|
||||
|
||||
// map has breakables ?
|
||||
bool hasBreakables () const {
|
||||
return !m_breakables.empty ();
|
||||
}
|
||||
|
||||
// helper to sending the client message
|
||||
void sendClientMessage (bool console, edict_t *ent, const char *message);
|
||||
|
||||
// send server command
|
||||
template <typename ...Args> void serverCommand (const char *fmt, Args &&...args) {
|
||||
engfuncs.pfnServerCommand (strings.concat (strings.format (fmt, cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
|
||||
}
|
||||
|
||||
// send a bot command
|
||||
template <typename ...Args> void botCommand (edict_t *ent, const char *fmt, Args &&...args) {
|
||||
prepareBotArgs (ent, strings.format (fmt, cr::forward <Args> (args)...));
|
||||
}
|
||||
|
||||
// prints data to servers console
|
||||
template <typename ...Args> void print (const char *fmt, Args &&...args) {
|
||||
engfuncs.pfnServerPrint (strings.concat (strings.format (conf.translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
|
||||
}
|
||||
|
||||
// prints center message to specified player
|
||||
template <typename ...Args> void clientPrint (edict_t *ent, const char *fmt, Args &&...args) {
|
||||
if (isNullEntity (ent)) {
|
||||
print (fmt, cr::forward <Args> (args)...);
|
||||
return;
|
||||
}
|
||||
sendClientMessage (true, ent, strings.concat (strings.format (conf.translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
|
||||
}
|
||||
|
||||
// prints message to client console
|
||||
template <typename ...Args> void centerPrint (edict_t *ent, const char *fmt, Args &&...args) {
|
||||
if (isNullEntity (ent)) {
|
||||
print (fmt, cr::forward <Args> (args)...);
|
||||
return;
|
||||
}
|
||||
sendClientMessage (false, ent, strings.concat (strings.format (conf.translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
|
||||
}
|
||||
};
|
||||
|
||||
// simplify access for console variables
|
||||
class ConVar final : public DenyCopying {
|
||||
public:
|
||||
cvar_t *ptr;
|
||||
|
||||
public:
|
||||
ConVar () = delete;
|
||||
~ConVar () = default;
|
||||
|
||||
public:
|
||||
ConVar (const char *name, const char *initval, int32 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) {
|
||||
Game::instance ().addNewCvar (name, initval, info, bounded, min, max, type, regMissing, regVal, this);
|
||||
}
|
||||
|
||||
bool bool_ () const {
|
||||
return ptr->value > 0.0f;
|
||||
}
|
||||
|
||||
int int_ () const {
|
||||
return static_cast <int> (ptr->value);
|
||||
}
|
||||
|
||||
float float_ () const {
|
||||
return ptr->value;
|
||||
}
|
||||
|
||||
const char *str () const {
|
||||
return ptr->string;
|
||||
}
|
||||
|
||||
void set (float val) {
|
||||
engfuncs.pfnCVarSetFloat (ptr->name, val);
|
||||
}
|
||||
|
||||
void set (int val) {
|
||||
set (static_cast <float> (val));
|
||||
}
|
||||
|
||||
void set (const char *val) {
|
||||
engfuncs.pfnCvar_DirectSet (ptr, const_cast <char *> (val));
|
||||
}
|
||||
};
|
||||
|
||||
class MessageWriter final {
|
||||
private:
|
||||
bool m_autoDestruct { false };
|
||||
|
||||
public:
|
||||
MessageWriter () = default;
|
||||
|
||||
MessageWriter (int dest, int type, const Vector &pos = nullptr, edict_t *to = nullptr) {
|
||||
start (dest, type, pos, to);
|
||||
m_autoDestruct = true;
|
||||
}
|
||||
|
||||
~MessageWriter () {
|
||||
if (m_autoDestruct) {
|
||||
end ();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
MessageWriter &start (int dest, int type, const Vector &pos = nullptr, edict_t *to = nullptr) {
|
||||
engfuncs.pfnMessageBegin (dest, type, pos, to);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void end () {
|
||||
engfuncs.pfnMessageEnd ();
|
||||
}
|
||||
|
||||
MessageWriter &writeByte (int val) {
|
||||
engfuncs.pfnWriteByte (val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MessageWriter &writeLong (int val) {
|
||||
engfuncs.pfnWriteLong (val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MessageWriter &writeChar (int val) {
|
||||
engfuncs.pfnWriteChar (val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MessageWriter &writeShort (int val) {
|
||||
engfuncs.pfnWriteShort (val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MessageWriter &writeCoord (float val) {
|
||||
engfuncs.pfnWriteCoord (val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MessageWriter &writeString (const char *val) {
|
||||
engfuncs.pfnWriteString (val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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 short fs16 (float value, float scale) {
|
||||
return cr::clamp <short> (static_cast <short> (value * cr::bit (static_cast <short> (scale))), -SHRT_MAX, SHRT_MAX);
|
||||
}
|
||||
};
|
||||
|
||||
class LightMeasure final : public Singleton <LightMeasure> {
|
||||
private:
|
||||
lightstyle_t m_lightstyle[MAX_LIGHTSTYLES] {};
|
||||
int m_lightstyleValue[MAX_LIGHTSTYLEVALUE] {};
|
||||
bool m_doAnimation = false;
|
||||
|
||||
Color m_point;
|
||||
model_t *m_worldModel = nullptr;
|
||||
|
||||
public:
|
||||
LightMeasure () {
|
||||
initializeLightstyles ();
|
||||
m_point.reset ();
|
||||
}
|
||||
|
||||
public:
|
||||
void initializeLightstyles ();
|
||||
void animateLight ();
|
||||
void updateLight (int style, char *value);
|
||||
|
||||
float getLightLevel (const Vector &point);
|
||||
float getSkyColor ();
|
||||
|
||||
private:
|
||||
template <typename S, typename M> bool recursiveLightPoint (const M *node, const Vector &start, const Vector &end);
|
||||
|
||||
public:
|
||||
void resetWorldModel () {
|
||||
m_worldModel = nullptr;
|
||||
}
|
||||
|
||||
void setWorldModel (model_t *model) {
|
||||
if (m_worldModel) {
|
||||
return;
|
||||
}
|
||||
m_worldModel = model;
|
||||
}
|
||||
|
||||
model_t *getWorldModel () const {
|
||||
return m_worldModel;
|
||||
}
|
||||
|
||||
void enableAnimation (bool enable) {
|
||||
m_doAnimation = enable;
|
||||
}
|
||||
};
|
||||
|
||||
// simple handler for parsing and rewriting queries (fake queries)
|
||||
class QueryBuffer {
|
||||
SmallArray <uint8> m_buffer;
|
||||
size_t m_cursor;
|
||||
|
||||
public:
|
||||
QueryBuffer (const uint8 *msg, size_t length, size_t shift) : m_cursor (0) {
|
||||
m_buffer.insert (0, msg, length);
|
||||
m_cursor += shift;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename T> T read () {
|
||||
T result;
|
||||
auto size = sizeof (T);
|
||||
|
||||
if (m_cursor + size > m_buffer.length ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy (&result, m_buffer.data () + m_cursor, size);
|
||||
m_cursor += size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// must be called right after read
|
||||
template <typename T> void write (T value) {
|
||||
auto size = sizeof (value);
|
||||
memcpy (m_buffer.data () + m_cursor - size, &value, size);
|
||||
}
|
||||
|
||||
template <typename T> void skip () {
|
||||
auto size = sizeof (T);
|
||||
|
||||
if (m_cursor + size > m_buffer.length ()) {
|
||||
return;
|
||||
}
|
||||
m_cursor += size;
|
||||
}
|
||||
|
||||
void skipString () {
|
||||
if (m_buffer.length () < m_cursor) {
|
||||
return;
|
||||
}
|
||||
for (; m_cursor < m_buffer.length () && m_buffer[m_cursor] != kNullChar; ++m_cursor) { }
|
||||
++m_cursor;
|
||||
}
|
||||
|
||||
void shiftToEnd () {
|
||||
m_cursor = m_buffer.length ();
|
||||
}
|
||||
|
||||
public:
|
||||
Twin <const uint8 *, size_t> data () {
|
||||
return { m_buffer.data (), m_buffer.length () };
|
||||
}
|
||||
};
|
||||
|
||||
// for android
|
||||
#if defined (CR_ANDROID) && defined(CR_ARCH_ARM)
|
||||
extern "C" void player (entvars_t *);
|
||||
#endif
|
||||
|
||||
class EntityLinkage : public Singleton <EntityLinkage> {
|
||||
private:
|
||||
#if defined (CR_WINDOWS)
|
||||
# define HOOK_FUNCTION GetProcAddress
|
||||
# define HOOK_CAST HMODULE
|
||||
#else
|
||||
# define HOOK_FUNCTION dlsym
|
||||
# define HOOK_CAST SharedLibrary::Handle
|
||||
#endif
|
||||
|
||||
private:
|
||||
template <typename K> struct CharHash {
|
||||
uint32 operator () (const char *key) const {
|
||||
auto str = const_cast <char *> (key);
|
||||
uint32 hash = 0;
|
||||
|
||||
while (*str++) {
|
||||
hash = ((hash << 5) + hash) + *str;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
SharedLibrary m_self;
|
||||
SimpleHook m_dlsym;
|
||||
Dictionary <const char *, SharedLibrary::Handle, CharHash <const char *>> m_exports;
|
||||
|
||||
public:
|
||||
EntityLinkage () = default;
|
||||
|
||||
~EntityLinkage () {
|
||||
m_dlsym.disable ();
|
||||
}
|
||||
public:
|
||||
void initialize ();
|
||||
SharedLibrary::Handle lookup (SharedLibrary::Handle module, const char *function);
|
||||
|
||||
public:
|
||||
void callPlayerFunction (edict_t *ent) {
|
||||
#if defined (CR_ANDROID) && defined (CR_ARCH_ARM)
|
||||
player (&ent->v);
|
||||
#else
|
||||
reinterpret_cast <EntityFunction> (lookup (Game::instance ().lib ().handle (), "player")) (&ent->v);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
static SharedLibrary::Handle CR_STDCALL replacement (SharedLibrary::Handle module, const char *function) {
|
||||
return EntityLinkage::instance ().lookup (module, function);
|
||||
}
|
||||
};
|
||||
|
||||
// expose globals
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (Game, game);
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (LightMeasure, illum);
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (EntityLinkage, ents);
|
||||
468
inc/graph.h
Normal file
468
inc/graph.h
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#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)
|
||||
Narrow = cr::bit (10), // node is inside some small space (corridor or such)
|
||||
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
|
||||
Exten = cr::bit (6) // this is additional flag indicates that there's extension 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;
|
||||
};
|
||||
|
||||
// extension header for graph information
|
||||
struct ExtenHeader {
|
||||
char author[32];
|
||||
int32 mapSize;
|
||||
};
|
||||
|
||||
// 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 (1);
|
||||
}
|
||||
|
||||
int32 &first () {
|
||||
return at (0);
|
||||
}
|
||||
|
||||
int32 &last () {
|
||||
return m_storage.last ();
|
||||
}
|
||||
|
||||
int32 &at (size_t index) {
|
||||
return m_storage.at (m_cursor + 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;
|
||||
}
|
||||
|
||||
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;
|
||||
bool m_narrowChecked;
|
||||
|
||||
Vector m_learnVelocity;
|
||||
Vector m_learnPosition;
|
||||
Vector m_bombOrigin;
|
||||
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 (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 ...Args> bool raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args);
|
||||
|
||||
void saveOldFormat ();
|
||||
void initGraph ();
|
||||
void frame ();
|
||||
void loadPractice ();
|
||||
void loadVisibility ();
|
||||
void initNodesTypes ();
|
||||
void initLightLevels ();
|
||||
void initNarrowPlaces ();
|
||||
void addPath (int addIndex, int pathIndex, float distance);
|
||||
void add (int type, const Vector &pos = nullptr);
|
||||
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 setBombOrigin (bool reset = false, const Vector &pos = nullptr);
|
||||
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 &getBombOrigin () const {
|
||||
return m_bombOrigin;
|
||||
}
|
||||
|
||||
// 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
|
||||
int32 length () const {
|
||||
return m_paths.length <int32> ();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
// we're need `bots`
|
||||
#include <manager.h>
|
||||
|
||||
// helper for reporting load errors
|
||||
template <typename ...Args> bool BotGraph::raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args) {
|
||||
auto result = strings.format (fmt, cr::forward <Args> (args)...);
|
||||
logger.error (result);
|
||||
|
||||
// if graph reset paths
|
||||
if (isGraph) {
|
||||
bots.kickEveryone (true);
|
||||
|
||||
m_tempStrings = result;
|
||||
m_paths.clear ();
|
||||
}
|
||||
file.close ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// explose global
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotGraph, graph);
|
||||
309
inc/manager.h
Normal file
309
inc/manager.h
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// bot creation tab
|
||||
struct BotRequest {
|
||||
bool manual;
|
||||
int difficulty;
|
||||
int team;
|
||||
int member;
|
||||
int personality;
|
||||
String name;
|
||||
};
|
||||
|
||||
// initial frustum data
|
||||
struct FrustumData : public Singleton <FrustumData> {
|
||||
private:
|
||||
float Fov = 75.0f;
|
||||
float AspectRatio = 1.33333f;
|
||||
|
||||
public:
|
||||
float MaxView = 4096.0f;
|
||||
float MinView = 5.0f;
|
||||
|
||||
public:
|
||||
float farHeight; // height of the far frustum
|
||||
float farWidth; // width of the far frustum
|
||||
|
||||
float nearHeight; // height of the near frustum
|
||||
float nearWidth; // width of the near frustum
|
||||
|
||||
public:
|
||||
FrustumData () {
|
||||
nearHeight = 2.0f * cr::tanf (Fov * 0.0174532925f * 0.5f) * MinView;
|
||||
nearWidth = nearHeight * AspectRatio;
|
||||
|
||||
farHeight = 2.0f * cr::tanf (Fov * 0.0174532925f * 0.5f) * MaxView;
|
||||
farWidth = farHeight * AspectRatio;
|
||||
}
|
||||
};
|
||||
|
||||
// declare global frustum data
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (FrustumData, frustum);
|
||||
|
||||
// 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_autoKillCheckTime; // time to kill all the bots ?
|
||||
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_roundOver;
|
||||
|
||||
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 <BotRequest> m_addRequests; // bot creation tab
|
||||
SmallArray <BotTask> m_filters; // task filters
|
||||
SmallArray <UniqueBot> m_bots; // all available bots
|
||||
|
||||
edict_t *m_killerEntity; // killer entity for bots
|
||||
FrustumData m_frustumData {};
|
||||
|
||||
protected:
|
||||
BotCreateResult create (StringRef 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 ();
|
||||
|
||||
float getConnectTime (int botId, float original);
|
||||
|
||||
void setBombPlanted (bool isPlanted);
|
||||
void frame ();
|
||||
void createKillerEntity ();
|
||||
void destroyKillerEntity ();
|
||||
void touchKillerEntity (Bot *bot);
|
||||
void destroy ();
|
||||
void addbot (StringRef name, int difficulty, int personality, int team, int member, bool manual);
|
||||
void addbot (StringRef name, StringRef difficulty, StringRef personality, StringRef team, StringRef 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);
|
||||
void kickBot (int index);
|
||||
void kickFromTeam (Team team, bool removeAll = false);
|
||||
void killAllBots (int team = -1);
|
||||
void maintainQuota ();
|
||||
void maintainAutoKill ();
|
||||
void maintainLeaders ();
|
||||
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 (edict_t *ent);
|
||||
void forEach (ForEachBot handler);
|
||||
void erase (Bot *bot);
|
||||
void handleDeath (edict_t *killer, edict_t *victim);
|
||||
void setLastWinner (int winner);
|
||||
|
||||
bool isTeamStacked (int team);
|
||||
bool kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
|
||||
|
||||
public:
|
||||
const Array <edict_t *> &getActiveGrenades () {
|
||||
return m_activeGrenades;
|
||||
}
|
||||
|
||||
const Array <edict_t *> &getIntrestingEntities () {
|
||||
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];
|
||||
}
|
||||
|
||||
int32 getLastWinner () const {
|
||||
return m_lastWinner;
|
||||
}
|
||||
|
||||
int32 getBotCount () {
|
||||
return m_bots.length <int32> ();
|
||||
}
|
||||
|
||||
// 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_roundOver;
|
||||
}
|
||||
|
||||
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
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotManager, bots);
|
||||
156
inc/message.h
Normal file
156
inc/message.h
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#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:
|
||||
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:
|
||||
int32 add (StringRef name, int32 id);
|
||||
int32 id (NetMsg msg);
|
||||
|
||||
void start (edict_t *ent, int32 type);
|
||||
void stop ();
|
||||
void ensureMessages ();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
void reset () {
|
||||
m_current = NetMsg::None;
|
||||
m_bot = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (MessageDispatcher, msgs);
|
||||
53
inc/product.h
Normal file
53
inc/product.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef VERSION_GENERATED
|
||||
# define VERSION_HEADER <version.build.h>
|
||||
#else
|
||||
# define VERSION_HEADER <version.h>
|
||||
#endif
|
||||
|
||||
#include VERSION_HEADER
|
||||
|
||||
// simple class for bot internal information
|
||||
class Product final : public Singleton <Product> {
|
||||
public:
|
||||
const struct Build {
|
||||
const StringRef hash { MODULE_BUILD_HASH };
|
||||
const StringRef author { MODULE_BUILD_AUTHOR };
|
||||
const StringRef count { MODULE_BUILD_COUNT };
|
||||
const StringRef machine { MODULE_BUILD_MACHINE };
|
||||
const StringRef compiler { MODULE_BUILD_COMPILER };
|
||||
const StringRef id { MODULE_BOT_BUILD_ID };
|
||||
} build { };
|
||||
|
||||
public:
|
||||
const StringRef name { "YaPB" };
|
||||
const StringRef year { __DATE__ + 8 };
|
||||
const StringRef author { "YaPB Development Team" };
|
||||
const StringRef email { "team@yapb.ru" };
|
||||
const StringRef url { "https://yapb.ru/" };
|
||||
const StringRef download { "http://data.yapb.ru/" };
|
||||
const StringRef folder { "yapb" };
|
||||
const StringRef logtag { "YB" };
|
||||
const StringRef dtime { __DATE__ " " __TIME__ };
|
||||
const StringRef date { __DATE__ };
|
||||
const StringRef version { MODULE_BOT_VERSION };
|
||||
};
|
||||
|
||||
// expose product info
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (Product, product);
|
||||
160
inc/support.h
Normal file
160
inc/support.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#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;
|
||||
float m_welcomeReceiveTime;
|
||||
|
||||
StringArray m_sentences;
|
||||
SmallArray <Client> m_clients;
|
||||
SmallArray <Twin <String, String>> m_tags;
|
||||
|
||||
Dictionary <int32, String, IntNoHash <int32>> m_weaponAlias;
|
||||
Dictionary <String, int32> m_noiseCache;
|
||||
SimpleHook m_sendToHook;
|
||||
|
||||
public:
|
||||
BotSupport ();
|
||||
~BotSupport () = default;
|
||||
|
||||
public:
|
||||
// need to send welcome message ?
|
||||
void checkWelcome ();
|
||||
|
||||
// converts weapon id to alias name
|
||||
StringRef weaponIdToAlias (int32 id);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 listenNoise (edict_t *ent, StringRef sample, float volume);
|
||||
|
||||
// simulate sound for players
|
||||
void simulateNoise (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 (StringRef 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 ();
|
||||
|
||||
// check if object inside frustum plane
|
||||
bool isObjectInsidePlane (FrustumPlane &plane, const Vector ¢er, float height, float radius);
|
||||
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 ();
|
||||
}
|
||||
|
||||
// gets the shooting cone deviation
|
||||
float getShootingCone (edict_t *ent, const Vector &position) {
|
||||
return ent->v.v_angle.forward () | (position - (ent->v.origin + ent->v.view_ofs)).normalize (); // he's facing it, he meant it
|
||||
}
|
||||
|
||||
// check if origin is inside view cone of entity
|
||||
bool isInViewCone (const Vector &origin, edict_t *ent) {
|
||||
return getShootingCone (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
|
||||
}
|
||||
|
||||
public:
|
||||
static int32 CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength);
|
||||
};
|
||||
|
||||
// explose global
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotSupport, util);
|
||||
27
inc/version.h
Normal file
27
inc/version.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// fallback if no git or custom build
|
||||
#define MODULE_BUILD_HASH "0"
|
||||
#define MODULE_BUILD_AUTHOR "team@yapb.ru"
|
||||
#define MODULE_BUILD_COUNT "0"
|
||||
#define MODULE_BUILD_MACHINE "localhost"
|
||||
#define MODULE_BUILD_COMPILER "unknown"
|
||||
|
||||
#define MODULE_BOT_VERSION "4.0.0"
|
||||
#define MODULE_BOT_VERSION_FILE 4,0,0,000
|
||||
#define MODULE_BOT_BUILD_ID "0:0"
|
||||
28
inc/version.h.in
Normal file
28
inc/version.h.in
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// generated by meson build system
|
||||
#ifndef MODULE_BUILD_HASH
|
||||
# define MODULE_BUILD_HASH "@commitHash@"
|
||||
# define MODULE_BUILD_AUTHOR @commitAuthor@
|
||||
# define MODULE_BUILD_COUNT "@commitCount@"
|
||||
# define MODULE_BUILD_MACHINE "@buildMachine@"
|
||||
# define MODULE_BUILD_COMPILER "@buildCompiler@"
|
||||
# define MODULE_BOT_VERSION "@buildVersion@"
|
||||
# define MODULE_BOT_VERSION_FILE @buildVersionWin@,@commitCount@
|
||||
# define MODULE_BOT_BUILD_ID "@commitCount@:@commitHash@"
|
||||
#endif
|
||||
1148
inc/yapb.h
Normal file
1148
inc/yapb.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue