yapb-noob-edition/source/control.cpp
jeefo 68f2f010fd Major refactoring (#86)
Major code refactoring.
2019-09-22 00:08:37 +03:00

2031 lines
56 KiB
C++

//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#include <yapb.h>
ConVar yb_display_menu_text ("yb_display_menu_text", "1");
ConVar yb_password ("yb_password", "", VT_PASSWORD);
ConVar yb_password_key ("yb_password_key", "_ybpw");
int BotControl::cmdAddBot (void) {
enum args { alias = 1, difficulty, personality, team, model, name, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// if team is specified, modify args to set team
if (m_args[alias].find ("_ct", 0) != String::INVALID_INDEX) {
m_args.set (team, "2");
}
else if (m_args[alias].find ("_t", 0) != String::INVALID_INDEX) {
m_args.set (team, "1");
}
// if highskilled bot is requsted set personality to rusher and maxout difficulty
if (m_args[alias].find ("hs", 0) != String::INVALID_INDEX) {
m_args.set (difficulty, "4");
m_args.set (personality, "1");
}
bots.addbot (m_args[name], m_args[difficulty], m_args[personality], m_args[team], m_args[model], true);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdKickBot (void) {
enum args { alias = 1, team, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// if team is specified, kick from specified tram
if (m_args[alias].find ("_ct", 0) != String::INVALID_INDEX || getInt (team) == 2 || getStr (team) == "ct") {
bots.kickFromTeam (TEAM_COUNTER);
}
else if (m_args[alias].find ("_t", 0) != String::INVALID_INDEX || getInt (team) == 1 || getStr (team) == "t") {
bots.kickFromTeam (TEAM_TERRORIST);
}
else {
bots.kickRandom ();
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdKickBots (void) {
enum args { alias = 1, instant, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// check if we're need to remove bots instantly
auto kickInstant = getStr (instant) == "instant";
// kick the bots
bots.kickEveryone (kickInstant);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdKillBots (void) {
enum args { alias = 1, team, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// if team is specified, kick from specified tram
if (m_args[alias].find ("_ct", 0) != String::INVALID_INDEX || getInt (team) == 2 || getStr (team) == "ct") {
bots.killAllBots (TEAM_COUNTER);
}
else if (m_args[alias].find ("_t", 0) != String::INVALID_INDEX || getInt (team) == 1 || getStr (team) == "t") {
bots.killAllBots (TEAM_TERRORIST);
}
else {
bots.killAllBots ();
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdFill (void) {
enum args { alias = 1, team, count, difficulty, personality, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
if (!hasArg (team)) {
return CMD_STATUS_BADFORMAT;
}
bots.serverFill (getInt (team), hasArg (personality) ? getInt (personality) : -1, hasArg (difficulty) ? getInt (difficulty) : -1, hasArg (count) ? getInt (count) : -1);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdVote (void) {
enum args { alias = 1, mapid, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
if (!hasArg (mapid)) {
return CMD_STATUS_BADFORMAT;
}
int mapID = getInt (mapid);
// loop through all players
for (int i = 0; i < game.maxClients (); i++) {
auto bot = bots.getBot (i);
if (bot != nullptr) {
bot->m_voteMap = mapID;
}
}
msg ("All dead bots will vote for map #%d", mapID);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWeaponMode (void) {
enum args { alias = 1, type, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
if (!hasArg (type)) {
return CMD_STATUS_BADFORMAT;
}
HashMap <String, int> modes;
modes.put ("kinfe", 1);
modes.put ("pistol", 2);
modes.put ("shotgun", 3);
modes.put ("smg", 4);
modes.put ("rifle", 5);
modes.put ("sniper", 6);
modes.put ("stanard", 7);
auto mode = getStr (type);
// check if selected mode exists
if (!modes.exists (mode)) {
return CMD_STATUS_BADFORMAT;
}
bots.setWeaponMode (modes[mode]);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdVersion (void) {
msg ("%s v%s (build %u)", PRODUCT_NAME, PRODUCT_VERSION, util.buildNumber ());
msg (" compiled: %s %s by %s", __DATE__, __TIME__, PRODUCT_GIT_COMMIT_AUTHOR);
msg (" commit: %scommit/%s", PRODUCT_COMMENTS, PRODUCT_GIT_HASH);
msg (" url: %s", PRODUCT_URL);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointMenu (void) {
enum args { alias = 1, max };
// waypoints is not supported on DS yet
if (!waypoints.hasEditor ()) {
msg ("Unable to open waypoint editor without setting the editor player.");
return CMD_STATUS_HANDLED;
}
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE1);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdMenu (void) {
enum args { alias = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// reset the current menu
showMenu (BOT_MENU_INVALID);
if (getStr (cmd) == "cmd" && util.isAlive (m_ent)) {
showMenu (BOT_MENU_COMMANDS);
}
else {
showMenu (BOT_MENU_MAIN);
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdList (void) {
enum args { alias = 1, max };
bots.listBots ();
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypoint (void) {
enum args { root, alias, cmd, cmd2, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// waypoints is not supported on DS yet
if (game.isDedicated () && !waypoints.hasEditor () && getStr (cmd) != "acquire_editor") {
msg ("Unable to use waypoint edit commands without setting waypoint editor player. Please use \"waypoint acquire_editor\" to acquire rights for waypoint editing");
return CMD_STATUS_HANDLED;
}
// should be moved to class?
static HashMap <String, BotCmd> commands;
static Array <String> descriptions;
// fill only once
if (descriptions.empty ()) {
// separate function
auto addWaypointCommand = [&] (const String &cmd, const String &format, const String &help, Handler handler) -> void {
commands.put (cmd, { cmd, format, help, cr::forward <Handler> (handler) });
descriptions.push (cmd);
};
// add waypoint commands
addWaypointCommand ("on", "on [display|auto|noclip|models]", "Enables displaying of waypoints, autowaypoint, noclip cheat", &BotControl::cmdWaypointOn);
addWaypointCommand ("off", "off [display|auto|noclip|models]", "Disables displaying of waypoints, autowaypoint, noclip cheat", &BotControl::cmdWaypointOff);
addWaypointCommand ("menu", "menu [noarguments]", "Opens and displays bots waypoint edtior.", &BotControl::cmdWaypointMenu);
addWaypointCommand ("add", "add [noarguments]", "Opens and displays bots waypoint add menu.", &BotControl::cmdWaypointAdd);
addWaypointCommand ("addbasic", "menu [noarguments]", "Adds basic waypoints such as player spawn points, goals and ladders.", &BotControl::cmdWaypointAddBasic);
addWaypointCommand ("save", "save [noarguments]", "Save waypoint file to disk.", &BotControl::cmdWaypointSave);
addWaypointCommand ("load", "load [noarguments]", "Load waypoint file from disk.", &BotControl::cmdWaypointLoad);
addWaypointCommand ("erase", "erase [iamsure]", "Erases the waypoint file from disk.", &BotControl::cmdWaypointErase);
addWaypointCommand ("delete", "delete [nearest|index]", "Deletes single waypoint from map.", &BotControl::cmdWaypointDelete);
addWaypointCommand ("check", "check [noarguments]", "Check if waypoints working correctly.", &BotControl::cmdWaypointCheck);
addWaypointCommand ("cache", "cache [nearest|index]", "Caching waypoint for future use.", &BotControl::cmdWaypointCache);
addWaypointCommand ("clean", "clean [all|nearest|index]", "Clean useless path connections from all or single waypoint.", &BotControl::cmdWaypointClean);
addWaypointCommand ("setradius", "setradius [radius] [nearest|index]", "Sets the radius for waypoint.", &BotControl::cmdWaypointSetRadius);
addWaypointCommand ("flags", "flags [noarguments]", "Open and displays menu for modifying flags for nearest point.", &BotControl::cmdWaypointSetFlags);
addWaypointCommand ("teleport", "teleport [index]", "Teleports player to specified waypoint index.", &BotControl::cmdWaypointTeleport);
// add path commands
addWaypointCommand ("path_create", "path_create [noarguments]", "Opens and displays path creation menu.", &BotControl::cmdWaypointPathCreate);
addWaypointCommand ("path_create_in", "path_create_in [noarguments]", "Opens and displays path creation menu.", &BotControl::cmdWaypointPathCreate);
addWaypointCommand ("path_create_out", "path_create_out [noarguments]", "Opens and displays path creation menu.", &BotControl::cmdWaypointPathCreate);
addWaypointCommand ("path_create_both", "path_create_both [noarguments]", "Opens and displays path creation menu.", &BotControl::cmdWaypointPathCreate);
addWaypointCommand ("path_delete", "path_create_both [noarguments]", "Opens and displays path creation menu.", &BotControl::cmdWaypointPathDelete);
addWaypointCommand ("path_set_autopath", "path_set_autoath [max_distance]", "Opens and displays path creation menu.", &BotControl::cmdWaypointPathSetAutoDistance);
// remote waypoint editing stuff
if (game.isDedicated ()) {
addWaypointCommand ("acquire_editor", "acquire_editor [max_distance]", "Acquires rights to edit waypoints on dedicated server.", &BotControl::cmdWaypointAcquireEditor);
addWaypointCommand ("release_editor", "acquire_editor [max_distance]", "Releases waypoint editing rights.", &BotControl::cmdWaypointAcquireEditor);
}
}
if (commands.exists (getStr (cmd))) {
auto &item = commands[getStr (cmd)];
// waypoints have only bad format return status
int status = (this->*item.handler) ();
if (status == CMD_STATUS_BADFORMAT) {
msg ("Incorrect usage of \"%s %s %s\" command. Correct usage is:\n\n\t%s\n\nPlease use correct format.", m_args[root].chars (), m_args[alias].chars (), item.name.chars (), item.format.chars ());
}
}
else {
if (getStr (cmd) == "help" && hasArg (cmd2) && commands.exists (getStr (cmd2))) {
auto &item = commands[getStr (cmd2)];
msg ("Command: \"%s %s %s\"\nFormat: %s\nHelp: %s", m_args[root].chars (), m_args[alias].chars (), item.name.chars (), item.format.chars (), item.help.chars ());
}
else {
for (auto &desc : descriptions) {
auto &item = commands[desc];
msg (" %s - %s", item.name.chars (), item.help.chars ());
}
msg ("Currently waypoints are %s", waypoints.hasEditFlag (WS_EDIT_ENABLED) ? "Enabled" : "Disabled");
}
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointOn (void) {
enum args { alias = 1, cmd, option, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// enable various features of editor
if (getStr (option) == "empty" || getStr (option) == "display" || getStr (option) == "models") {
waypoints.setEditFlag (WS_EDIT_ENABLED);
enableDrawModels (true);
msg ("Waypoint editor has been enabled.");
}
else if (getStr (option) == "noclip") {
m_ent->v.movetype = MOVETYPE_NOCLIP;
waypoints.setEditFlag (WS_EDIT_ENABLED | WS_EDIT_NOCLIP);
enableDrawModels (true);
msg ("Waypoint editor has been enabled with noclip mode.");
}
else if (getStr (option) == "auto") {
waypoints.setEditFlag (WS_EDIT_ENABLED | WS_EDIT_AUTO);
enableDrawModels (true);
msg ("Waypoint editor has been enabled with autowaypoint mode.");
}
if (waypoints.hasEditFlag (WS_EDIT_ENABLED)) {
extern ConVar mp_roundtime, mp_freezetime, mp_timelimit;
mp_roundtime.set (9);
mp_freezetime.set (0);
mp_timelimit.set (0);
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointOff (void) {
enum args { waypoint = 1, cmd, option, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// enable various features of editor
if (getStr (option) == "empty" || getStr (option) == "display") {
waypoints.clearEditFlag (WS_EDIT_ENABLED | WS_EDIT_AUTO | WS_EDIT_NOCLIP);
enableDrawModels (false);
msg ("Waypoint editor has been disabled.");
}
else if (getStr (option) == "models") {
enableDrawModels (false);
msg ("Waypoint editor has disabled spawn points highlighting.");
}
else if (getStr (option) == "noclip") {
m_ent->v.movetype = MOVETYPE_WALK;
waypoints.clearEditFlag (WS_EDIT_NOCLIP);
msg ("Waypoint editor has disabled noclip mode.");
}
else if (getStr (option) == "auto") {
waypoints.clearEditFlag (WS_EDIT_AUTO);
msg ("Waypoint editor has disabled autowaypoint mode.");
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointAdd (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
// show the menu
showMenu (BOT_MENU_WAYPOINT_TYPE);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointAddBasic (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
waypoints.addBasic ();
msg ("Basic waypoints was added.");
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointSave (void) {
enum args { waypoint = 1, cmd, nocheck, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// if no check is set save anyway
if (getStr (nocheck) == "nocheck") {
waypoints.save ();
msg ("All waypoints has been saved and written to disk (IGNORING QUALITY CONTROL).");
}
else {
if (waypoints.checkNodes ()) {
waypoints.save ();
msg ("All waypoints has been saved and written to disk.");
}
else {
msg ("Could not save save waypoints to disk. Waypoint check has failed.");
}
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointLoad (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// just save waypoints on request
if (waypoints.load ()) {
msg ("Waypoints successfully loaded.");
}
else {
msg ("Could not load waypoints. See console...");
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointErase (void) {
enum args { waypoint = 1, cmd, iamsure, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// prevent accidents when waypoints are deleted unintentionally
if (getStr (iamsure) == "iamsure") {
waypoints.eraseFromDisk ();
}
else {
msg ("Please, append \"iamsure\" as parameter to get waypoints erased from the disk.");
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointDelete (void) {
enum args { waypoint = 1, cmd, nearest, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
// if "neareset" or nothing passed delete neareset, else delete by index
if (getStr (nearest) == "empty" || getStr (nearest) == "nearest") {
waypoints.erase (INVALID_WAYPOINT_INDEX);
}
else {
int index = getInt (nearest);
// check for existence
if (waypoints.exists (index)) {
waypoints.erase (index);
msg ("Waypoint #%d has beed deleted.", index);
}
else {
msg ("Could not delete waypoints #%d.", index);
}
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointCheck (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// check if nodes are ok
if (waypoints.checkNodes ()) {
msg ("Waypoints seems to be OK.");
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointCache (void) {
enum args { waypoint = 1, cmd, nearest, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
// if "neareset" or nothing passed delete neareset, else delete by index
if (getStr (nearest) == "empty" || getStr (nearest) == "nearest") {
waypoints.cachePoint (INVALID_WAYPOINT_INDEX);
msg ("Nearest waypoint has been put into the memory.");
}
else {
int index = getInt (nearest);
// check for existence
if (waypoints.exists (index)) {
waypoints.cachePoint (index);
msg ("Waypoint #%d has been put into the memory.", index);
}
else {
msg ("Could not put waypoint #%d into the memory.", index);
}
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointClean (void) {
enum args { waypoint = 1, cmd, option, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
// if "all" passed clean up all the paths
if (getStr (option) == "all") {
int removed = 0;
for (int i = 0; i < waypoints.length (); i++) {
removed += waypoints.clearConnections (i);
}
msg ("Done. Processed %d waypoints. %d useless paths was cleared.", waypoints.length (), removed);
}
else if (getStr (option) == "empty" || getStr (option) == "nearest") {
int removed = waypoints.clearConnections (waypoints.getEditorNeareset ());
msg ("Done. Processed waypoint #%d. %d useless paths was cleared.", waypoints.getEditorNeareset (), removed);
}
else {
int index = getInt (option);
// check for existence
if (waypoints.exists (index)) {
int removed = waypoints.clearConnections (index);
msg ("Done. Processed waypoint #%d. %d useless paths was cleared.", index, removed);
}
else {
msg ("Could not process waypoint #%d clearance.", index);
}
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointSetRadius (void) {
enum args { waypoint = 1, cmd, radius, index, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// radius is a must
if (!hasArg (radius)) {
return CMD_STATUS_BADFORMAT;
}
int radiusIndex = INVALID_WAYPOINT_INDEX;
if (getStr (index) == "empty" || getStr (index) == "nearest") {
radiusIndex = waypoints.getEditorNeareset ();
}
else {
radiusIndex = getInt (index);
}
float value = getStr (radius).toFloat ();
waypoints.setRadius (radiusIndex, value);
msg ("Waypoint #%d has been set to radius %.2f.", radiusIndex, value);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointSetFlags (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
//show the flag menu
showMenu (BOT_MENU_WAYPOINT_FLAG);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointTeleport (void) {
enum args { waypoint = 1, cmd, teleport_index, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
if (!hasArg (teleport_index)) {
return CMD_STATUS_BADFORMAT;
}
int index = getInt (teleport_index);
// check for existence
if (waypoints.exists (index)) {
engfuncs.pfnSetOrigin (waypoints.getEditor (), waypoints[index].origin);
msg ("You have been teleported to waypoint #%d.", index);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED | WS_EDIT_NOCLIP);
}
else {
msg ("Could not teleport to waypoint #%d.", index);
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointPathCreate (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
// choose the direction for path creation
if (m_args[cmd].find ("_both", 0) != String::INVALID_INDEX) {
waypoints.pathCreate (CONNECTION_BOTHWAYS);
}
else if (m_args[cmd].find ("_in", 0) != String::INVALID_INDEX) {
waypoints.pathCreate (CONNECTION_INCOMING);
}
else if (m_args[cmd].find ("_out", 0) != String::INVALID_INDEX) {
waypoints.pathCreate (CONNECTION_OUTGOING);
}
else {
showMenu (BOT_MENU_WAYPOINT_PATH);
}
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointPathDelete (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
// delete the patch
waypoints.erasePath ();
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointPathSetAutoDistance (void) {
enum args { waypoint = 1, cmd, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
// turn waypoints on
waypoints.setEditFlag (WS_EDIT_ENABLED);
showMenu (BOT_MENU_WAYPOINT_AUTOPATH);
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointAcquireEditor (void) {
enum args { waypoint = 1, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
if (game.isNullEntity (m_ent)) {
msg ("This command should not be executed from HLDS console.");
return CMD_STATUS_HANDLED;
}
if (waypoints.hasEditor ()) {
msg ("Sorry, players \"%s\" already acquired rights to edit waypoints on this server.", STRING (waypoints.getEditor ()->v.netname));
return CMD_STATUS_HANDLED;
}
waypoints.setEditor (m_ent);
msg ("You're acquired rights to edit waypoints on this server. You're now able to use waypoint commands.");
return CMD_STATUS_HANDLED;
}
int BotControl::cmdWaypointReleaseEditor (void) {
enum args { waypoint = 1, max };
// adding more args to args array, if not enough passed
fixMissingArgs (max);
if (!waypoints.hasEditor ()) {
msg ("No one is currently has rights to edit. Nothing to release.");
return CMD_STATUS_HANDLED;
}
waypoints.setEditor (nullptr);
msg ("Waypoint editor rights freed. You're now not able to use waypoint commands.");
return CMD_STATUS_HANDLED;
}
int BotControl::menuMain (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
m_isMenuFillCommand = false;
showMenu (BOT_MENU_CONTROL);
break;
case 2:
showMenu (BOT_MENU_FEATURES);
break;
case 3:
m_isMenuFillCommand = true;
showMenu (BOT_MENU_TEAM_SELECT);
break;
case 4:
bots.killAllBots ();
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
default:
showMenu (BOT_MENU_MAIN);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuFeatures (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
showMenu (BOT_MENU_WEAPON_MODE);
break;
case 2:
showMenu (waypoints.hasEditor () ? BOT_MENU_WAYPOINT_MAIN_PAGE1 : BOT_MENU_FEATURES);
break;
case 3:
showMenu (BOT_MENU_PERSONALITY);
break;
case 4:
extern ConVar yb_debug;
yb_debug.set (yb_debug.integer () ^ 1);
showMenu (BOT_MENU_FEATURES);
break;
case 5:
if (util.isAlive (m_ent)) {
showMenu (BOT_MENU_COMMANDS);
}
else {
showMenu (BOT_MENU_INVALID); // reset menu display
msg ("You're dead, and have no access to this menu");
}
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuControl (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
bots.createRandom (true);
showMenu (BOT_MENU_CONTROL);
break;
case 2:
showMenu (BOT_MENU_DIFFICULTY);
break;
case 3:
bots.kickRandom ();
showMenu (BOT_MENU_CONTROL);
break;
case 4:
bots.kickEveryone ();
break;
case 5:
kickBotByMenu (1);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWeaponMode (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
bots.setWeaponMode (item);
showMenu (BOT_MENU_WEAPON_MODE);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuPersonality (int item) {
if (m_isMenuFillCommand) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
bots.serverFill (m_menuServerFillTeam, item - 2, m_interMenuData[0]);
showMenu (BOT_MENU_INVALID);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
m_interMenuData[3] = item - 2;
showMenu (BOT_MENU_TEAM_SELECT);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuDifficulty (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
m_interMenuData[0] = 0;
break;
case 2:
m_interMenuData[0] = 1;
break;
case 3:
m_interMenuData[0] = 2;
break;
case 4:
m_interMenuData[0] = 3;
break;
case 5:
m_interMenuData[0] = 4;
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
showMenu (BOT_MENU_PERSONALITY);
return CMD_STATUS_HANDLED;
}
int BotControl::menuTeamSelect (int item) {
if (m_isMenuFillCommand) {
showMenu (BOT_MENU_INVALID); // reset menu display
if (item < 3) {
extern ConVar mp_limitteams, mp_autoteambalance;
// turn off cvars if specified team
mp_limitteams.set (0);
mp_autoteambalance.set (0);
}
switch (item) {
case 1:
case 2:
case 5:
m_menuServerFillTeam = item;
showMenu (BOT_MENU_DIFFICULTY);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 5:
m_interMenuData[1] = item;
if (item == 5) {
m_interMenuData[2] = item;
bots.addbot ("", m_interMenuData[0], m_interMenuData[3], m_interMenuData[1], m_interMenuData[2], true);
}
else {
showMenu (item == 1 ? BOT_MENU_TERRORIST_SELECT : BOT_MENU_CT_SELECT);
}
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuClassSelect (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
m_interMenuData[2] = item;
bots.addbot ("", m_interMenuData[0], m_interMenuData[3], m_interMenuData[1], m_interMenuData[2], true);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuCommands (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
Bot *bot = nullptr;
switch (item) {
case 1:
case 2:
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), m_ent, 600.0f, true, true, true) && bot->m_hasC4 && !bot->hasHostage ()) {
if (item == 1) {
bot->startDoubleJump (m_ent);
}
else {
bot->resetDoubleJump ();
}
}
showMenu (BOT_MENU_COMMANDS);
break;
case 3:
case 4:
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), m_ent, 600.0f, true, true, true, true, item == 4 ? false : true)) {
bot->dropWeaponForUser (m_ent, item == 4 ? false : true);
}
showMenu (BOT_MENU_COMMANDS);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWaypointPage1 (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
if (waypoints.hasEditFlag (WS_EDIT_ENABLED)) {
waypoints.clearEditFlag (WS_EDIT_ENABLED);
enableDrawModels (false);
msg ("Waypoint editor has been disabled.");
}
else {
waypoints.setEditFlag (WS_EDIT_ENABLED);
enableDrawModels (true);
msg ("Waypoint editor has been enabled.");
}
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE1);
break;
case 2:
waypoints.setEditFlag (WS_EDIT_ENABLED);
waypoints.cachePoint (INVALID_WAYPOINT_INDEX);
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE1);
break;
case 3:
waypoints.setEditFlag (WS_EDIT_ENABLED);
showMenu (BOT_MENU_WAYPOINT_PATH);
break;
case 4:
waypoints.setEditFlag (WS_EDIT_ENABLED);
waypoints.erasePath ();
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE1);
break;
case 5:
waypoints.setEditFlag (WS_EDIT_ENABLED);
showMenu (BOT_MENU_WAYPOINT_TYPE);
break;
case 6:
waypoints.setEditFlag (WS_EDIT_ENABLED);
waypoints.erase (INVALID_WAYPOINT_INDEX);
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE1);
break;
case 7:
waypoints.setEditFlag (WS_EDIT_ENABLED);
showMenu (BOT_MENU_WAYPOINT_AUTOPATH);
break;
case 8:
waypoints.setEditFlag (WS_EDIT_ENABLED);
showMenu (BOT_MENU_WAYPOINT_RADIUS);
break;
case 9:
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWaypointPage2 (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1: {
int terrPoints = 0;
int ctPoints = 0;
int goalPoints = 0;
int rescuePoints = 0;
int campPoints = 0;
int sniperPoints = 0;
int noHostagePoints = 0;
for (int i = 0; i < waypoints.length (); i++) {
Path &path = waypoints[i];
if (path.flags & FLAG_TF_ONLY) {
terrPoints++;
}
if (path.flags & FLAG_CF_ONLY) {
ctPoints++;
}
if (path.flags & FLAG_GOAL) {
goalPoints++;
}
if (path.flags & FLAG_RESCUE) {
rescuePoints++;
}
if (path.flags & FLAG_CAMP) {
campPoints++;
}
if (path.flags & FLAG_SNIPER) {
sniperPoints++;
}
if (path.flags & FLAG_NOHOSTAGE) {
noHostagePoints++;
}
}
msg ("Waypoints: %d - T Points: %d\n"
"CT Points: %d - Goal Points: %d\n"
"Rescue Points: %d - Camp Points: %d\n"
"Block Hostage Points: %d - Sniper Points: %d\n",
waypoints.length (), terrPoints, ctPoints, goalPoints, rescuePoints, campPoints, noHostagePoints, sniperPoints);
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
} break;
case 2:
waypoints.setEditFlag (WS_EDIT_ENABLED);
if (waypoints.hasEditFlag (WS_EDIT_AUTO)) {
waypoints.clearEditFlag (WS_EDIT_AUTO);
}
else {
waypoints.setEditFlag (WS_EDIT_AUTO);
}
msg ("Auto-Waypoint %s", waypoints.hasEditFlag (WS_EDIT_AUTO) ? "Enabled" : "Disabled");
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 3:
waypoints.setEditFlag (WS_EDIT_ENABLED);
showMenu (BOT_MENU_WAYPOINT_FLAG);
break;
case 4:
if (waypoints.checkNodes ()) {
waypoints.save ();
}
else {
msg ("Waypoint not saved\nThere are errors, see console");
}
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 5:
waypoints.save ();
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 6:
waypoints.load ();
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 7:
if (waypoints.checkNodes ()) {
msg ("Nodes works fine");
}
else {
msg ("There are errors, see console");
}
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 8:
waypoints.setEditFlag (WS_EDIT_ENABLED | WS_EDIT_NOCLIP);
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE2);
break;
case 9:
showMenu (BOT_MENU_WAYPOINT_MAIN_PAGE1);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWaypointRadius (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
waypoints.setEditFlag (WS_EDIT_ENABLED); // turn waypoints on in case
constexpr float radius[] = { 0.0f, 8.0f, 16.0f, 32.0f, 48.0f, 64.0f, 80.0f, 96.0f, 128.0f };
if (item >= 1 && item <= 9) {
waypoints.setRadius (INVALID_WAYPOINT_INDEX, radius[item - 1]);
showMenu (BOT_MENU_WAYPOINT_RADIUS);
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWaypointType (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
waypoints.push (item - 1);
showMenu (BOT_MENU_WAYPOINT_TYPE);
break;
case 8:
waypoints.push (100);
showMenu (BOT_MENU_WAYPOINT_TYPE);
break;
case 9:
waypoints.startLearnJump ();
showMenu (BOT_MENU_WAYPOINT_TYPE);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWaypointFlag (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
waypoints.toggleFlags (FLAG_NOHOSTAGE);
showMenu (BOT_MENU_WAYPOINT_FLAG);
break;
case 2:
waypoints.toggleFlags (FLAG_TF_ONLY);
showMenu (BOT_MENU_WAYPOINT_FLAG);
break;
case 3:
waypoints.toggleFlags (FLAG_CF_ONLY);
showMenu (BOT_MENU_WAYPOINT_FLAG);
break;
case 4:
waypoints.toggleFlags (FLAG_LIFT);
showMenu (BOT_MENU_WAYPOINT_FLAG);
break;
case 5:
waypoints.toggleFlags (FLAG_SNIPER);
showMenu (BOT_MENU_WAYPOINT_FLAG);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuAutoPathDistance (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
constexpr float distances[] = { 0.0f, 100.0f, 130.0f, 160.0f, 190.0f, 220.0f, 250.0f };
float result = 0.0f;
if (item >= 1 && item <= 7) {
result = distances[item - 1];
waypoints.setAutoPathDistance (result);
}
if (cr::fzero (result)) {
msg ("Autopathing is now disabled.");
}
else {
msg ("Autopath distance is set to %.2f.", result);
}
showMenu (BOT_MENU_WAYPOINT_AUTOPATH);
return CMD_STATUS_HANDLED;
}
int BotControl::menuKickPage1 (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
bots.kickBot (item - 1);
kickBotByMenu (1);
break;
case 9:
kickBotByMenu (2);
break;
case 10:
showMenu (BOT_MENU_CONTROL);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuKickPage2 (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
bots.kickBot (item + 8 - 1);
kickBotByMenu (2);
break;
case 9:
kickBotByMenu (3);
break;
case 10:
kickBotByMenu (1);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuKickPage3 (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
bots.kickBot (item + 16 - 1);
kickBotByMenu (3);
break;
case 9:
kickBotByMenu (4);
break;
case 10:
kickBotByMenu (2);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuKickPage4 (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
bots.kickBot (item + 24 - 1);
kickBotByMenu (4);
break;
case 10:
kickBotByMenu (3);
break;
}
return CMD_STATUS_HANDLED;
}
int BotControl::menuWaypointPath (int item) {
showMenu (BOT_MENU_INVALID); // reset menu display
switch (item) {
case 1:
waypoints.pathCreate (CONNECTION_OUTGOING);
showMenu (BOT_MENU_WAYPOINT_PATH);
break;
case 2:
waypoints.pathCreate (CONNECTION_INCOMING);
showMenu (BOT_MENU_WAYPOINT_PATH);
break;
case 3:
waypoints.pathCreate (CONNECTION_BOTHWAYS);
showMenu (BOT_MENU_WAYPOINT_PATH);
break;
case 10:
showMenu (BOT_MENU_INVALID);
break;
}
return CMD_STATUS_HANDLED;
}
bool BotControl::executeCommands (void) {
if (m_args.empty ()) {
return false;
}
// handle only "yb" and "yapb" commands
if (m_args[0] != "yb" && m_args[0] != "yapb") {
return false;
}
Client &client = util.getClient (game.indexOfEntity (m_ent) - 1);
// do not allow to execute stuff for non admins
if (m_ent != game.getLocalEntity () && !(client.flags & CF_ADMIN)) {
msg ("Access to YaPB commands is restricted.");
return true;
}
auto aliasMatch = [] (String &test, const String &cmd, String &aliasName) -> bool {
for (auto &alias : test.split ("|")) {
if (alias == cmd) {
aliasName = alias;
return true;
}
}
return false;
};
String cmd;
// give some help
if (m_args.length () > 1 && stricmp ("help", m_args[1].chars ()) == 0) {
for (auto &item : m_cmds) {
if (aliasMatch (item.name, m_args[2], cmd)) {
msg ("Command: \"%s %s\"\nFormat: %s\nHelp: %s", m_args[0].chars (), cmd.chars (), item.format.chars (), item.help.chars ());
String aliases;
for (auto &alias : item.name.split ("|")) {
aliases.append ("%s, ", alias.chars ());
}
aliases.rtrim (", ");
msg ("Aliases: %s", aliases.chars ());
return true;
}
}
if (m_args[2].empty ()) {
return true;
}
else {
msg ("No help found for \"%s\"", m_args[2].chars ());
}
return true;
}
cmd.clear ();
// if no args passed just print all the commands
if (m_args.length () == 1) {
msg ("usage %s <command> [arguments]", m_args[0].chars ());
msg ("valid commands are: ");
for (auto &item : m_cmds) {
msg (" %s - %s", item.name.split ("|")[0].chars (), item.help.chars ());
}
return true;
}
// first search for a actual cmd
for (auto &item : m_cmds) {
auto root = m_args[0].chars ();
if (aliasMatch (item.name, m_args[1], cmd)) {
auto alias = cmd.chars ();
switch ((this->*item.handler) ()) {
case CMD_STATUS_HANDLED:
default:
break;
case CMD_STATUS_LISTENSERV:
msg ("Command \"%s %s\" is only available from the listenserver console.", root, alias);
break;
case CMD_STATUS_BADFORMAT:
msg ("Incorrect usage of \"%s %s\" command. Correct usage is:\n\n\t%s\n\nPlease type \"%s help %s\" to get more information.", root, alias, item.format.chars (), root, alias);
break;
}
m_isFromConsole = false;
return true;
}
}
msg ("Unrecognized command: %s", m_args[1].chars ());
return true;
}
bool BotControl::executeMenus (void) {
if (!util.isPlayer (m_ent) || game.isBotCmd ()) {
return false;
}
auto &issuer = util.getClient (game.indexOfEntity (m_ent) - 1);
// check if it's menu select, and some key pressed
if (getStr (0) != "menuselect" || getStr (1).empty () || issuer.menu == BOT_MENU_INVALID) {
return false;
}
// let's get handle
for (auto &menu : m_menus) {
if (menu.ident == issuer.menu) {
return (this->*menu.handler) (getStr (1).toInt32 ());
}
}
return false;
}
void BotControl::showMenu (int id) {
static bool s_menusParsed = false;
// make menus looks like we need only once
if (!s_menusParsed) {
for (auto &parsed : m_menus) {
const String &translated = game.translate (parsed.text.chars ());
// translate all the things
parsed.text = translated;
// make menu looks best
if (!(game.is (GAME_LEGACY))) {
for (int j = 0; j < 10; j++) {
parsed.text.replace (util.format ("%d.", j), util.format ("\\r%d.\\w", j));
}
}
}
s_menusParsed = true;
}
if (!util.isPlayer (m_ent)) {
return;
}
Client &client = util.getClient (game.indexOfEntity (m_ent) - 1);
if (id == BOT_MENU_INVALID) {
MessageWriter (MSG_ONE_UNRELIABLE, game.getMessageId (NETMSG_SHOWMENU), Vector::null (), m_ent)
.writeShort (0)
.writeChar (0)
.writeByte (0)
.writeString ("");
client.menu = id;
return;
}
for (auto &display : m_menus) {
if (display.ident == id) {
const char *text = (game.is (GAME_XASH_ENGINE | GAME_MOBILITY) && !yb_display_menu_text.boolean ()) ? " " : display.text.chars ();
MessageWriter msg;
while (strlen (text) >= 64) {
msg.start (MSG_ONE_UNRELIABLE, game.getMessageId (NETMSG_SHOWMENU), Vector::null (), m_ent)
.writeShort (display.slots)
.writeChar (-1)
.writeByte (1);
for (int i = 0; i < 64; i++) {
msg.writeChar (text[i]);
}
msg.end ();
text += 64;
}
MessageWriter (MSG_ONE_UNRELIABLE, game.getMessageId (NETMSG_SHOWMENU), Vector::null (), m_ent)
.writeShort (display.slots)
.writeChar (-1)
.writeByte (0)
.writeString (text);
client.menu = id;
engfuncs.pfnClientCommand (m_ent, "speak \"player/geiger1\"\n"); // Stops others from hearing menu sounds..
}
}
}
void BotControl::kickBotByMenu (int page) {
if (page > 4 || page < 1) {
return;
}
String menus;
menus.assign ("\\yBots Remove Menu (%d/4):\\w\n\n", page);
int menuKeys = (page == 4) ? cr::bit (9) : (cr::bit (8) | cr::bit (9));
int menuKey = (page - 1) * 8;
for (int i = menuKey; i < page * 8; i++) {
auto bot = bots.getBot (i);
if (bot != nullptr) {
menuKeys |= cr::bit (cr::abs (i - menuKey));
menus.append ("%1.1d. %s%s\n", i - menuKey + 1, STRING (bot->pev->netname), bot->m_team == TEAM_COUNTER ? " \\y(CT)\\w" : " \\r(T)\\w");
}
else {
menus.append ("\\d %1.1d. Not a Bot\\w\n", i - menuKey + 1);
}
}
menus.append ("\n%s 0. Back", (page == 4) ? "" : " 9. More...\n");
// force to clear current menu
showMenu (BOT_MENU_INVALID);
auto id = BOT_MENU_KICK_PAGE_1 - 1 + page;
for (auto &menu : m_menus) {
if (menu.ident == id) {
menu.slots = menuKeys & static_cast <unsigned int> (-1);
menu.text = menus;
break;
}
}
showMenu (id);
}
void BotControl::msg (const char *fmt, ...) {
va_list ap;
char buffer[MAX_PRINT_BUFFER];
va_start (ap, fmt);
vsnprintf (buffer, cr::bufsize (buffer), fmt, ap);
va_end (ap);
if (game.isNullEntity (m_ent)) {
game.print (buffer);
return;
}
if (m_isFromConsole || strlen (buffer) > 48) {
game.clientPrint (m_ent, buffer);
}
else {
game.centerPrint (m_ent, buffer);
game.clientPrint (m_ent, buffer);
}
}
void BotControl::assignAdminRights (edict_t *ent, char *infobuffer) {
if (!game.isDedicated () || util.isFakeClient (ent)) {
return;
}
const String &key = yb_password_key.str ();
const String &password = yb_password.str ();
if (!key.empty () && !password.empty ()) {
auto &client = util.getClient (game.indexOfEntity (ent) - 1);
if (password == engfuncs.pfnInfoKeyValue (infobuffer, key.chars ())) {
client.flags |= CF_ADMIN;
}
else {
client.flags &= ~CF_ADMIN;
}
}
}
void BotControl::maintainAdminRights (void) {
if (!game.isDedicated ()) {
return;
}
for (int i = 0; i < game.maxClients (); i++) {
edict_t *player = game.entityOfIndex (i + 1);
// code below is executed only on dedicated server
if (util.isPlayer (player) && !util.isFakeClient (player)) {
Client &client = util.getClient (i);
if (client.flags & CF_ADMIN) {
if (util.isEmptyStr (yb_password_key.str ()) && util.isEmptyStr (yb_password.str ())) {
client.flags &= ~CF_ADMIN;
}
else if (!!strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast <char *> (yb_password_key.str ())))) {
client.flags &= ~CF_ADMIN;
game.print ("Player %s had lost remote access to %s.", STRING (player->v.netname), PRODUCT_SHORT_NAME);
}
}
else if (!(client.flags & CF_ADMIN) && !util.isEmptyStr (yb_password_key.str ()) && !util.isEmptyStr (yb_password.str ())) {
if (strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast <char *> (yb_password_key.str ()))) == 0) {
client.flags |= CF_ADMIN;
game.print ("Player %s had gained full remote access to %s.", STRING (player->v.netname), PRODUCT_SHORT_NAME);
}
}
}
}
}
BotControl::BotControl (void) {
m_ent = nullptr;
m_isFromConsole = false;
m_isMenuFillCommand = false;
m_menuServerFillTeam = 5;
auto addCommand = [&] (const String &cmd, const String &format, const String &help, Handler handler) -> void {
m_cmds.push ({ cmd, format, help, cr::forward <Handler> (handler) });
};
addCommand ("add|addbot|add_ct|addbot_ct|add_t|addbot_t|addhs|addhs_t|addhs_ct", "add [difficulty[personality[team[model[name]]]]]", "Adding specific bot into the game.", &BotControl::cmdAddBot);
addCommand ("kick|kickone|kick_ct|kick_t|kickbot_ct|kickbot_t", "kick [team]", "Kicks off the random bot from the game.", &BotControl::cmdKickBot);
addCommand ("removebots|kickbots|kickall", "removebots [instant]", "Kicks all the bots from the game.", &BotControl::cmdKickBots);
addCommand ("kill|killbots|killall|kill_ct|kill_t", "kill [team]", "Kills the specified team / all the bots.", &BotControl::cmdKillBots);
addCommand ("fill|fillserver", "fill [team[count[difficulty[pesonality]]]]", "Fill the server (add bots) with specified parameters.", &BotControl::cmdFill);
addCommand ("vote|votemap", "vote [map_id]", "Forces all the bot to vote to specified map.", &BotControl::cmdVote);
addCommand ("weapons|weaponmode", "weapons [knife|pistol|shotgun|smg|rifle|sniper|standard]", "Sets the bots weapon mode to use", &BotControl::cmdWeaponMode);
addCommand ("menu|botmenu", "menu [cmd]", "Opens the main bot menu, or command menu if specified.", &BotControl::cmdMenu);
addCommand ("version|ver|about", "version [no arguments]", "Displays version information about bot build.", &BotControl::cmdVersion);
addCommand ("wpmenu|wptmenu", "wpmenu [noarguments]", "Opens and displays bots waypoint edtior.", &BotControl::cmdWaypointMenu);
addCommand ("list|listbots", "list [noarguments]", "Lists the bots currently playing on server.", &BotControl::cmdList);
addCommand ("waypoint|wp|wpt|waypoint", "waypoint [help]", "Handles waypoint operations.", &BotControl::cmdWaypoint);
// declare the menus
createMenus ();
}
void BotControl::handleEngineCommands (void) {
ctrl.setArgs (cr::move (ctrl.collectArgs ()));
ctrl.setIssuer (game.getLocalEntity ());
ctrl.setFromConsole (true);
ctrl.executeCommands ();
}
bool BotControl::handleClientCommands (edict_t *ent) {
setArgs (cr::move (collectArgs ()));
setIssuer (ent);
setFromConsole (true);
return executeCommands ();
}
bool BotControl::handleMenuCommands (edict_t *ent) {
setArgs (cr::move (collectArgs ()));
setIssuer (ent);
setFromConsole (false);
return ctrl.executeMenus ();
}
void BotControl::enableDrawModels (bool enable) {
StringArray entities;
entities.push ("info_player_start");
entities.push ("info_player_deathmatch");
entities.push ("info_vip_start");
for (auto &entity : entities) {
edict_t *ent = nullptr;
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityByString (ent, "classname", entity.chars ()))) {
if (enable) {
ent->v.effects &= ~EF_NODRAW;
}
else {
ent->v.effects |= EF_NODRAW;
}
}
}
}
void BotControl::createMenus (void) {
auto keys = [] (int numKeys) -> int {
int result = 0;
for (int i = 0; i < numKeys; i++) {
result |= cr::bit (i);
}
result |= cr::bit (9);
return result;
};
// bots main menu
m_menus.push ({
BOT_MENU_MAIN, keys (4),
"\\yMain Menu\\w\n\n"
"1. Control Bots\n"
"2. Features\n\n"
"3. Fill Server\n"
"4. End Round\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuMain) });
// bots features menu
m_menus.push ({
BOT_MENU_FEATURES, keys (5),
"\\yBots Features\\w\n\n"
"1. Weapon Mode Menu\n"
"2. Waypoint Menu\n"
"3. Select Personality\n\n"
"4. Toggle Debug Mode\n"
"5. Command Menu\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuFeatures) });
// bot control menu
m_menus.push ({
BOT_MENU_CONTROL, keys (5),
"\\yBots Control Menu\\w\n\n"
"1. Add a Bot, Quick\n"
"2. Add a Bot, Specified\n\n"
"3. Remove Random Bot\n"
"4. Remove All Bots\n\n"
"5. Remove Bot Menu\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuControl) });
// weapon mode select menu
m_menus.push ({
BOT_MENU_WEAPON_MODE, keys (7),
"\\yBots Weapon Mode\\w\n\n"
"1. Knives only\n"
"2. Pistols only\n"
"3. Shotguns only\n"
"4. Machine Guns only\n"
"5. Rifles only\n"
"6. Sniper Weapons only\n"
"7. All Weapons\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWeaponMode) });
// personality select menu
m_menus.push ({
BOT_MENU_PERSONALITY, keys (4),
"\\yBots Personality\\w\n\n"
"1. Random\n"
"2. Normal\n"
"3. Aggressive\n"
"4. Careful\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuPersonality) });
// difficulty select menu
m_menus.push ({
BOT_MENU_DIFFICULTY, keys (5),
"\\yBots Difficulty Level\\w\n\n"
"1. Newbie\n"
"2. Average\n"
"3. Normal\n"
"4. Professional\n"
"5. Godlike\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuDifficulty) });
// team select menu
m_menus.push ({
BOT_MENU_TEAM_SELECT, keys (5),
"\\ySelect a team\\w\n\n"
"1. Terrorist Force\n"
"2. Counter-Terrorist Force\n\n"
"5. Auto-select\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuTeamSelect) });
// terrorist model select menu
m_menus.push ({
BOT_MENU_TERRORIST_SELECT, keys (5),
"\\ySelect an appearance\\w\n\n"
"1. Phoenix Connexion\n"
"2. L337 Krew\n"
"3. Arctic Avengers\n"
"4. Guerilla Warfare\n\n"
"5. Auto-select\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuClassSelect) });
// counter-terrorist model select menu
m_menus.push ({
BOT_MENU_CT_SELECT, keys (5),
"\\ySelect an appearance\\w\n\n"
"1. Seal Team 6 (DEVGRU)\n"
"2. German GSG-9\n"
"3. UK SAS\n"
"4. French GIGN\n\n"
"5. Auto-select\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuClassSelect) });
// command menu
m_menus.push ({
BOT_MENU_COMMANDS, keys (4),
"\\yBot Command Menu\\w\n\n"
"1. Make Double Jump\n"
"2. Finish Double Jump\n\n"
"3. Drop the C4 Bomb\n"
"4. Drop the Weapon\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuCommands) });
// main waypoint menu
m_menus.push ({
BOT_MENU_WAYPOINT_MAIN_PAGE1, keys (9),
"\\yWaypoint Operations (Page 1)\\w\n\n"
"1. Show/Hide waypoints\n"
"2. Cache waypoint\n"
"3. Create path\n"
"4. Delete path\n"
"5. Add waypoint\n"
"6. Delete waypoint\n"
"7. Set Autopath Distance\n"
"8. Set Radius\n\n"
"9. Next...\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWaypointPage1) });
// main waypoint menu (page 2)
m_menus.push ({
BOT_MENU_WAYPOINT_MAIN_PAGE2, keys (9),
"\\yWaypoint Operations (Page 2)\\w\n\n"
"1. Waypoint stats\n"
"2. Autowaypoint on/off\n"
"3. Set flags\n"
"4. Save waypoints\n"
"5. Save without checking\n"
"6. Load waypoints\n"
"7. Check waypoints\n"
"8. Noclip cheat on/off\n\n"
"9. Previous...\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWaypointPage2) });
// select waypoint radius menu
m_menus.push ({
BOT_MENU_WAYPOINT_RADIUS, keys (9),
"\\yWaypoint Radius\\w\n\n"
"1. SetRadius 0\n"
"2. SetRadius 8\n"
"3. SetRadius 16\n"
"4. SetRadius 32\n"
"5. SetRadius 48\n"
"6. SetRadius 64\n"
"7. SetRadius 80\n"
"8. SetRadius 96\n"
"9. SetRadius 128\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWaypointRadius) });
// waypoint add menu
m_menus.push ({
BOT_MENU_WAYPOINT_TYPE, keys (9),
"\\yWaypoint Type\\w\n\n"
"1. Normal\n"
"\\r2. Terrorist Important\n"
"3. Counter-Terrorist Important\n"
"\\w4. Block with hostage / Ladder\n"
"\\y5. Rescue Zone\n"
"\\w6. Camping\n"
"7. Camp End\n"
"\\r8. Map Goal\n"
"\\w9. Jump\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWaypointType) });
// set waypoint flag menu
m_menus.push ({
BOT_MENU_WAYPOINT_FLAG, keys (5),
"\\yToggle Waypoint Flags\\w\n\n"
"1. Block with Hostage\n"
"2. Terrorists Specific\n"
"3. CTs Specific\n"
"4. Use Elevator\n"
"5. Sniper Point (\\yFor Camp Points Only!\\w)\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWaypointFlag) });
// auto-path max distance
m_menus.push ({
BOT_MENU_WAYPOINT_AUTOPATH, keys (7),
"\\yAutoPath Distance\\w\n\n"
"1. Distance 0\n"
"2. Distance 100\n"
"3. Distance 130\n"
"4. Distance 160\n"
"5. Distance 190\n"
"6. Distance 220\n"
"7. Distance 250 (Default)\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuAutoPathDistance) });
// path connections
m_menus.push ({
BOT_MENU_WAYPOINT_PATH, keys (3),
"\\yCreate Path (Choose Direction)\\w\n\n"
"1. Outgoing Path\n"
"2. Incoming Path\n"
"3. Bidirectional (Both Ways)\n\n"
"0. Exit",
cr::forward <MenuHandler> (&BotControl::menuWaypointPath) });
const String &empty = "";
// kick menus
m_menus.push ({ BOT_MENU_KICK_PAGE_1, 0x0, empty, cr::forward <MenuHandler> (&BotControl::menuKickPage1) });
m_menus.push ({ BOT_MENU_KICK_PAGE_2, 0x0, empty, cr::forward <MenuHandler> (&BotControl::menuKickPage2) });
m_menus.push ({ BOT_MENU_KICK_PAGE_3, 0x0, empty, cr::forward <MenuHandler> (&BotControl::menuKickPage3) });
m_menus.push ({ BOT_MENU_KICK_PAGE_4, 0x0, empty, cr::forward <MenuHandler> (&BotControl::menuKickPage4) });
}