2019-08-12 14:16:28 +03:00
|
|
|
//
|
2023-05-24 23:41:23 +03:00
|
|
|
// YaPB, based on PODBot by Markus Klinge ("CountFloyd").
|
|
|
|
|
// Copyright © YaPB Project Developers <yapb@jeefo.net>.
|
2019-08-12 14:16:28 +03:00
|
|
|
//
|
2020-11-03 08:57:12 +03:00
|
|
|
// SPDX-License-Identifier: MIT
|
2019-08-12 14:16:28 +03:00
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
// command handler status
|
|
|
|
|
CR_DECLARE_SCOPED_ENUM (BotCommandResult,
|
|
|
|
|
Handled = 0, // command successfully handled
|
2023-05-12 22:12:22 +03:00
|
|
|
ListenServer, // command is only available on listen server
|
2019-08-12 14:16:28 +03:00
|
|
|
BadFormat // wrong params
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-13 15:43:25 +03:00
|
|
|
// print queue destination
|
|
|
|
|
CR_DECLARE_SCOPED_ENUM (PrintQueueDestination,
|
|
|
|
|
ServerConsole, // use server console
|
|
|
|
|
ClientConsole // use client console
|
|
|
|
|
);
|
|
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
// 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 {
|
2024-11-13 17:52:19 +03:00
|
|
|
String name {}, format {}, help {};
|
2019-08-12 14:16:28 +03:00
|
|
|
Handler handler = nullptr;
|
2024-02-15 08:37:02 +03:00
|
|
|
bool visible = true;
|
2019-08-12 14:16:28 +03:00
|
|
|
|
|
|
|
|
public:
|
2020-11-23 00:06:18 +03:00
|
|
|
explicit BotCmd () = default;
|
2020-10-13 15:43:25 +03:00
|
|
|
|
2024-02-15 08:37:02 +03:00
|
|
|
BotCmd (StringRef name, StringRef format, StringRef help, Handler handler, bool visible = true) : name (name), format (format), help (help), handler (cr::move (handler)), visible (visible)
|
2020-10-13 15:43:25 +03:00
|
|
|
{ }
|
2019-08-12 14:16:28 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// single bot menu
|
|
|
|
|
struct BotMenu {
|
2024-11-13 17:52:19 +03:00
|
|
|
int ident {}, slots {};
|
|
|
|
|
String text {};
|
|
|
|
|
MenuHandler handler {};
|
2019-08-12 14:16:28 +03:00
|
|
|
|
|
|
|
|
public:
|
2020-11-23 00:06:18 +03:00
|
|
|
explicit BotMenu (int ident, int slots, StringRef text, MenuHandler handler) : ident (ident), slots (slots), text (text), handler (cr::move (handler))
|
2020-10-13 15:43:25 +03:00
|
|
|
{ }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// queued text message to prevent overflow with rapid output
|
|
|
|
|
struct PrintQueue {
|
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
|
|
|
int32_t destination {};
|
2024-11-13 17:52:19 +03:00
|
|
|
String text {};
|
2020-10-13 15:43:25 +03:00
|
|
|
|
|
|
|
|
public:
|
2020-11-23 00:06:18 +03:00
|
|
|
explicit PrintQueue () = default;
|
2020-10-13 15:43:25 +03:00
|
|
|
|
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
|
|
|
PrintQueue (int32_t destination, StringRef text) : destination (destination), text (text)
|
2020-10-13 15:43:25 +03:00
|
|
|
{ }
|
2019-08-12 14:16:28 +03:00
|
|
|
};
|
|
|
|
|
|
2024-04-11 16:26:12 +03:00
|
|
|
// save old values of changed cvars to revert them back when editing turned off
|
|
|
|
|
struct GraphSaveVarValue {
|
|
|
|
|
float timelimit {};
|
|
|
|
|
float freezetime {};
|
|
|
|
|
float roundtime {};
|
2024-11-13 17:52:19 +03:00
|
|
|
} m_graphSaveVarValues {};
|
2024-04-11 16:26:12 +03:00
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
private:
|
2023-04-02 12:17:12 +03:00
|
|
|
StringArray m_args {};
|
|
|
|
|
Array <BotCmd> m_cmds {};
|
|
|
|
|
Array <BotMenu> m_menus {};
|
|
|
|
|
Deque <PrintQueue> m_printQueue {};
|
|
|
|
|
IntArray m_campIterator {};
|
2019-08-12 14:16:28 +03:00
|
|
|
|
2023-04-02 12:17:12 +03:00
|
|
|
edict_t *m_ent {};
|
|
|
|
|
Bot *m_djump {};
|
2019-08-12 14:16:28 +03:00
|
|
|
|
2023-04-02 12:17:12 +03:00
|
|
|
bool m_isFromConsole {};
|
|
|
|
|
bool m_rapidOutput {};
|
|
|
|
|
bool m_isMenuFillCommand {};
|
|
|
|
|
bool m_ignoreTranslate {};
|
2025-01-17 22:43:35 +03:00
|
|
|
bool m_denyCommands {};
|
2019-08-30 10:52:31 +03:00
|
|
|
|
2023-04-02 12:17:12 +03:00
|
|
|
int m_menuServerFillTeam {};
|
2019-08-12 14:16:28 +03:00
|
|
|
int m_interMenuData[4] = { 0, };
|
|
|
|
|
|
2020-10-13 15:43:25 +03:00
|
|
|
float m_printQueueFlushTimestamp {};
|
|
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
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 ();
|
2019-08-24 12:43:42 +03:00
|
|
|
int cmdCvars ();
|
2020-12-15 15:28:58 +03:00
|
|
|
int cmdShowCustom ();
|
2024-02-15 08:37:02 +03:00
|
|
|
int cmdExec ();
|
2019-08-12 14:16:28 +03:00
|
|
|
int cmdNode ();
|
|
|
|
|
int cmdNodeOn ();
|
|
|
|
|
int cmdNodeOff ();
|
|
|
|
|
int cmdNodeAdd ();
|
|
|
|
|
int cmdNodeAddBasic ();
|
|
|
|
|
int cmdNodeSave ();
|
|
|
|
|
int cmdNodeLoad ();
|
|
|
|
|
int cmdNodeErase ();
|
2025-01-31 20:40:08 +03:00
|
|
|
int cmdNodeEraseTraining ();
|
2019-08-12 14:16:28 +03:00
|
|
|
int cmdNodeDelete ();
|
|
|
|
|
int cmdNodeCheck ();
|
|
|
|
|
int cmdNodeCache ();
|
|
|
|
|
int cmdNodeClean ();
|
|
|
|
|
int cmdNodeSetRadius ();
|
|
|
|
|
int cmdNodeSetFlags ();
|
|
|
|
|
int cmdNodeTeleport ();
|
|
|
|
|
int cmdNodePathCreate ();
|
|
|
|
|
int cmdNodePathDelete ();
|
|
|
|
|
int cmdNodePathSetAutoDistance ();
|
2023-08-08 11:48:37 +03:00
|
|
|
int cmdNodePathCleanAll ();
|
2019-08-12 14:16:28 +03:00
|
|
|
int cmdNodeAcquireEditor ();
|
|
|
|
|
int cmdNodeReleaseEditor ();
|
|
|
|
|
int cmdNodeUpload ();
|
2019-08-30 10:52:31 +03:00
|
|
|
int cmdNodeIterateCamp ();
|
2020-08-31 14:52:12 +03:00
|
|
|
int cmdNodeShowStats ();
|
2022-09-21 14:47:36 +03:00
|
|
|
int cmdNodeFileInfo ();
|
2024-02-15 08:37:02 +03:00
|
|
|
int cmdNodeAdjustHeight ();
|
2019-08-12 14:16:28 +03:00
|
|
|
|
|
|
|
|
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);
|
2023-01-22 19:12:03 +06:00
|
|
|
int menuGraphDebug (int item);
|
2019-08-12 14:16:28 +03:00
|
|
|
int menuGraphFlag (int item);
|
|
|
|
|
int menuGraphPath (int item);
|
2023-01-22 19:12:03 +06:00
|
|
|
int menuCampDirections (int item);
|
2019-08-12 14:16:28 +03:00
|
|
|
int menuAutoPathDistance (int item);
|
|
|
|
|
int menuKickPage1 (int item);
|
|
|
|
|
int menuKickPage2 (int item);
|
|
|
|
|
int menuKickPage3 (int item);
|
|
|
|
|
int menuKickPage4 (int item);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void createMenus ();
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
bool executeCommands ();
|
|
|
|
|
bool executeMenus ();
|
|
|
|
|
|
|
|
|
|
void showMenu (int id);
|
2020-10-11 14:01:19 +03:00
|
|
|
void closeMenu ();
|
|
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
void kickBotByMenu (int page);
|
|
|
|
|
void assignAdminRights (edict_t *ent, char *infobuffer);
|
|
|
|
|
void maintainAdminRights ();
|
2020-10-13 15:43:25 +03:00
|
|
|
void flushPrintQueue ();
|
2024-01-19 00:03:45 +03:00
|
|
|
void enableDrawModels (bool enable);
|
2019-08-12 14:16:28 +03:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void setFromConsole (bool console) {
|
|
|
|
|
m_isFromConsole = console;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setRapidOutput (bool force) {
|
|
|
|
|
m_rapidOutput = force;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-17 22:43:35 +03:00
|
|
|
void setDenyCommands (bool deny) {
|
|
|
|
|
m_denyCommands = deny;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
void setIssuer (edict_t *ent) {
|
|
|
|
|
m_ent = ent;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-01 21:45:52 +03:00
|
|
|
void resetFlushTimestamp () {
|
|
|
|
|
m_printQueueFlushTimestamp = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-25 15:03:39 +03:00
|
|
|
template <typename U> constexpr U arg (const size_t index) const {
|
|
|
|
|
if constexpr (cr::is_same <U, float>::value) {
|
|
|
|
|
if (!hasArg (index)) {
|
|
|
|
|
return 0.0f;
|
|
|
|
|
}
|
|
|
|
|
return m_args[index].as <float> ();
|
2019-08-12 14:16:28 +03:00
|
|
|
}
|
2024-04-25 15:03:39 +03:00
|
|
|
else if constexpr (cr::is_same <U, int>::value) {
|
|
|
|
|
if (!hasArg (index)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return m_args[index].as <int> ();
|
2022-12-22 21:32:45 +03:00
|
|
|
}
|
2024-04-25 15:03:39 +03:00
|
|
|
else if constexpr (cr::is_same <U, StringRef>::value) {
|
|
|
|
|
if (!hasArg (index)) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
return m_args[index];
|
2019-08-12 14:16:28 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasArg (size_t arg) const {
|
|
|
|
|
return arg < m_args.length ();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 08:57:12 +03:00
|
|
|
bool ignoreTranslate () const {
|
|
|
|
|
return m_ignoreTranslate;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
void collectArgs () {
|
|
|
|
|
m_args.clear ();
|
|
|
|
|
|
2025-03-16 18:25:15 +03:00
|
|
|
for (auto i = 0; i < engfuncs.pfnCmd_Argc (); ++i) {
|
|
|
|
|
String arg = engfuncs.pfnCmd_Argv (i);
|
|
|
|
|
|
|
|
|
|
// only make case-insensetive command itself and first argument
|
|
|
|
|
if (i < 2) {
|
|
|
|
|
arg = arg.lowercase ();
|
|
|
|
|
}
|
|
|
|
|
m_args.emplace (arg);
|
2019-08-12 14:16:28 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-02 16:52:28 +03:00
|
|
|
edict_t *getIssuer() {
|
|
|
|
|
return m_ent;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-12 22:12:22 +03:00
|
|
|
// global helper for sending message to correct channel
|
2020-06-12 18:52:38 +03:00
|
|
|
template <typename ...Args> void msg (const char *fmt, Args &&...args);
|
2019-08-12 14:16:28 +03:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// for the server commands
|
2019-08-18 21:00:00 +03:00
|
|
|
void handleEngineCommands ();
|
2019-08-12 14:16:28 +03:00
|
|
|
|
2022-09-10 15:47:41 +03:00
|
|
|
// wrapper for menus and commands
|
|
|
|
|
bool handleClientSideCommandsWrapper (edict_t *ent, bool isMenus);
|
|
|
|
|
|
2019-08-12 14:16:28 +03:00
|
|
|
// for the client commands
|
|
|
|
|
bool handleClientCommands (edict_t *ent);
|
|
|
|
|
|
|
|
|
|
// for the client menu commands
|
|
|
|
|
bool handleMenuCommands (edict_t *ent);
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-12 22:12:22 +03:00
|
|
|
// global helper for sending message to correct channel
|
2020-06-12 18:52:38 +03:00
|
|
|
template <typename ...Args> inline void BotControl::msg (const char *fmt, Args &&...args) {
|
2020-11-03 08:57:12 +03:00
|
|
|
m_ignoreTranslate = game.isDedicated () && game.isNullEntity (m_ent);
|
2020-10-12 20:59:48 +03:00
|
|
|
|
2020-08-22 01:38:59 +03:00
|
|
|
auto result = strings.format (conf.translate (fmt), cr::forward <Args> (args)...);
|
2019-08-12 14:16:28 +03:00
|
|
|
|
|
|
|
|
// if no receiver or many message have to appear, just print to server console
|
2020-10-13 15:43:25 +03:00
|
|
|
if (game.isNullEntity (m_ent)) {
|
|
|
|
|
|
|
|
|
|
if (m_rapidOutput) {
|
|
|
|
|
m_printQueue.emplaceLast (PrintQueueDestination::ServerConsole, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
game.print (result); // print the info
|
|
|
|
|
}
|
2019-08-12 14:16:28 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-03 15:29:42 +03:00
|
|
|
if (m_isFromConsole || strnlen (result, StringBuffer::StaticBufferSize) > 96 || m_rapidOutput) {
|
2020-10-13 15:43:25 +03:00
|
|
|
if (m_rapidOutput) {
|
|
|
|
|
m_printQueue.emplaceLast (PrintQueueDestination::ClientConsole, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
game.clientPrint (m_ent, result);
|
|
|
|
|
}
|
2019-08-12 14:16:28 +03:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
game.centerPrint (m_ent, result);
|
|
|
|
|
game.clientPrint (m_ent, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-12 22:12:22 +03:00
|
|
|
// graph helper for sending message to correct channel
|
2023-05-02 09:42:43 +03:00
|
|
|
template <typename ...Args> inline void BotGraph::msg (const char *fmt, Args &&...args) {
|
|
|
|
|
if (m_silenceMessages) {
|
|
|
|
|
return; // no messages while analyzing (too much spam)
|
|
|
|
|
}
|
|
|
|
|
BotControl::instance ().msg (strings.format (conf.translate (fmt), cr::forward <Args> (args)...));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-05-12 22:12:22 +03:00
|
|
|
// expose global
|
2020-02-08 00:03:52 +03:00
|
|
|
CR_EXPOSE_GLOBAL_SINGLETON (BotControl, ctrl);
|