Fixed player avoidance code.
Speedup network message handling.
This commit is contained in:
parent
c090ca3c54
commit
9947e41549
26 changed files with 2398 additions and 2294 deletions
229
include/config.h
Normal file
229
include/config.h
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
//
|
||||||
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
|
// Copyright (c) YaPB Development Team.
|
||||||
|
//
|
||||||
|
// This software is licensed under the BSD-style license.
|
||||||
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
|
// https://yapb.ru/license
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// botname structure definition
|
||||||
|
struct BotName {
|
||||||
|
String name = "";
|
||||||
|
int usedBy = -1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotName () = default;
|
||||||
|
BotName (String &name, int usedBy) : name (cr::move (name)), usedBy (usedBy) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
// voice config structure definition
|
||||||
|
struct ChatterItem {
|
||||||
|
String name;
|
||||||
|
float repeat;
|
||||||
|
float duration;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChatterItem (String name, float repeat, float duration) : name (cr::move (name)), repeat (repeat), duration (duration) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
// language hasher
|
||||||
|
struct HashLangString {
|
||||||
|
uint32 operator () (const String &key) const {
|
||||||
|
auto str = reinterpret_cast <uint8 *> (const_cast <char *> (key.chars ()));
|
||||||
|
uint32 hash = 0;
|
||||||
|
|
||||||
|
while (*str++) {
|
||||||
|
if (!isalnum (*str)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hash = ((*str << 5) + hash) + *str;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// mostly config stuff, and some stuff dealing with menus
|
||||||
|
class BotConfig final : public Singleton <BotConfig> {
|
||||||
|
private:
|
||||||
|
Array <StringArray> m_chat;
|
||||||
|
Array <Array <ChatterItem>> m_chatter;
|
||||||
|
|
||||||
|
Array <BotName> m_botNames;
|
||||||
|
Array <Keywords> m_replies;
|
||||||
|
SmallArray <WeaponInfo> m_weapons;
|
||||||
|
SmallArray <WeaponProp> m_weaponProps;
|
||||||
|
|
||||||
|
StringArray m_logos;
|
||||||
|
StringArray m_avatars;
|
||||||
|
|
||||||
|
Dictionary <String, String, HashLangString> m_language;
|
||||||
|
|
||||||
|
// default tables for personality weapon preferences, overridden by general.cfg
|
||||||
|
SmallArray <int32> m_normalWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16 };
|
||||||
|
SmallArray <int32> m_rusherWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 24, 19, 22, 23, 20, 21, 10, 12, 13, 7, 8, 11, 9, 18, 17, 19, 25, 15, 16 };
|
||||||
|
SmallArray <int32> m_carefulWeaponPrefs = { 0, 2, 1, 4, 25, 6, 3, 7, 8, 12, 10, 13, 11, 9, 24, 18, 14, 17, 16, 15, 19, 20, 21, 22, 23, 5 };
|
||||||
|
SmallArray <int32> m_botBuyEconomyTable = { 1900, 2100, 2100, 4000, 6000, 7000, 16000, 1200, 800, 1000, 3000 };
|
||||||
|
SmallArray <int32> m_grenadeBuyPrecent = { 95, 85, 60 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotConfig ();
|
||||||
|
~BotConfig () = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// load the configuration files
|
||||||
|
void loadConfigs ();
|
||||||
|
|
||||||
|
// loads main config file
|
||||||
|
void loadMainConfig ();
|
||||||
|
|
||||||
|
// loads bot names
|
||||||
|
void loadNamesConfig ();
|
||||||
|
|
||||||
|
// loads weapons config
|
||||||
|
void loadWeaponsConfig ();
|
||||||
|
|
||||||
|
// loads chatter config
|
||||||
|
void loadChatterConfig ();
|
||||||
|
|
||||||
|
// loads chat config
|
||||||
|
void loadChatConfig ();
|
||||||
|
|
||||||
|
// loads language config
|
||||||
|
void loadLanguageConfig ();
|
||||||
|
|
||||||
|
// load bots logos config
|
||||||
|
void loadLogosConfig ();
|
||||||
|
|
||||||
|
// load bots avatars config
|
||||||
|
void loadAvatarsConfig ();
|
||||||
|
|
||||||
|
// sets memfile to use engine functions
|
||||||
|
void setupMemoryFiles ();
|
||||||
|
|
||||||
|
// picks random bot name
|
||||||
|
BotName *pickBotName ();
|
||||||
|
|
||||||
|
// remove bot name from used list
|
||||||
|
void clearUsedName (Bot *bot);
|
||||||
|
|
||||||
|
// initialize weapon info
|
||||||
|
void initWeapons ();
|
||||||
|
|
||||||
|
// fix weapon prices (ie for elite)
|
||||||
|
void adjustWeaponPrices ();
|
||||||
|
|
||||||
|
// find weapon info by weaponi d
|
||||||
|
WeaponInfo &findWeaponById (int id);
|
||||||
|
|
||||||
|
// translates bot message into needed language
|
||||||
|
const char *translate (const char *input);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isCommentLine (const String &line) {
|
||||||
|
const char ch = line.at (0);
|
||||||
|
return ch == '#' || ch == '/' || ch == '\r' || ch == ';' || ch == 0 || ch == ' ';
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// checks whether chat banks contains messages
|
||||||
|
bool hasChatBank (int chatType) const {
|
||||||
|
return !m_chat[chatType].empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks whether chatter banks contains messages
|
||||||
|
bool hasChatterBank (int chatterType) const {
|
||||||
|
return !m_chatter[chatterType].empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick random phrase from chat bank
|
||||||
|
const String &pickRandomFromChatBank (int chatType) {
|
||||||
|
return m_chat[chatType].random ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick random phrase from chatter bank
|
||||||
|
const ChatterItem &pickRandomFromChatterBank (int chatterType) {
|
||||||
|
return m_chatter[chatterType].random ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets chatter repeat-interval
|
||||||
|
float getChatterMessageRepeatInterval (int chatterType) const {
|
||||||
|
return m_chatter[chatterType][0].repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's the replies array
|
||||||
|
Array <Keywords> &getReplies () {
|
||||||
|
return m_replies;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's the weapon info data
|
||||||
|
SmallArray <WeaponInfo> &getWeapons () {
|
||||||
|
return m_weapons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's raw weapon info
|
||||||
|
WeaponInfo *getRawWeapons () {
|
||||||
|
return m_weapons.begin ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set's the weapon properties
|
||||||
|
void setWeaponProp (WeaponProp prop) {
|
||||||
|
m_weaponProps[prop.id] = cr::move (prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's the weapons prop
|
||||||
|
const WeaponProp &getWeaponProp (int id) const {
|
||||||
|
return m_weaponProps[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's weapon preferences for personality
|
||||||
|
int32 *getWeaponPrefs (int personality) const {
|
||||||
|
switch (personality) {
|
||||||
|
case Personality::Normal:
|
||||||
|
default:
|
||||||
|
return m_normalWeaponPrefs.data ();
|
||||||
|
|
||||||
|
case Personality::Rusher:
|
||||||
|
return m_rusherWeaponPrefs.data ();
|
||||||
|
|
||||||
|
case Personality::Careful:
|
||||||
|
return m_carefulWeaponPrefs.data ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get economics value
|
||||||
|
int32 *getEconLimit () {
|
||||||
|
return m_botBuyEconomyTable.data ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's grenade buy percents
|
||||||
|
bool chanceToBuyGrenade (int grenadeType) const {
|
||||||
|
return rg.chance (m_grenadeBuyPrecent[grenadeType]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's random avatar for player (if any)
|
||||||
|
String getRandomAvatar () const {
|
||||||
|
if (!m_avatars.empty ()) {
|
||||||
|
return m_avatars.random ();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// get's random logo index
|
||||||
|
int getRandomLogoIndex () const {
|
||||||
|
return m_logos.index (m_logos.random ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// get random name by index
|
||||||
|
const String &getRandomLogoName (int index) const {
|
||||||
|
return m_logos[index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// explose global
|
||||||
|
static auto &conf = BotConfig::get ();
|
||||||
215
include/control.h
Normal file
215
include/control.h
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
//
|
||||||
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
|
// Copyright (c) YaPB Development Team.
|
||||||
|
//
|
||||||
|
// This software is licensed under the BSD-style license.
|
||||||
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
|
// https://yapb.ru/license
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// command handler status
|
||||||
|
CR_DECLARE_SCOPED_ENUM (BotCommandResult,
|
||||||
|
Handled = 0, // command successfully handled
|
||||||
|
ListenServer, // command is only avaialble on listen server
|
||||||
|
BadFormat // wrong params
|
||||||
|
)
|
||||||
|
|
||||||
|
// bot command manager
|
||||||
|
class BotControl final : public Singleton <BotControl> {
|
||||||
|
public:
|
||||||
|
using Handler = int (BotControl::*) ();
|
||||||
|
using MenuHandler = int (BotControl::*) (int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// generic bot command
|
||||||
|
struct BotCmd {
|
||||||
|
String name, format, help;
|
||||||
|
Handler handler = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotCmd () = default;
|
||||||
|
BotCmd (String name, String format, String help, Handler handler) : name (cr::move (name)), format (cr::move (format)), help (cr::move (help)), handler (cr::move (handler)) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
// single bot menu
|
||||||
|
struct BotMenu {
|
||||||
|
int ident, slots;
|
||||||
|
String text;
|
||||||
|
MenuHandler handler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotMenu (int ident, int slots, String text, MenuHandler handler) : ident (ident), slots (slots), text (cr::move (text)), handler (cr::move (handler)) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringArray m_args;
|
||||||
|
Array <BotCmd> m_cmds;
|
||||||
|
Array <BotMenu> m_menus;
|
||||||
|
|
||||||
|
edict_t *m_ent;
|
||||||
|
|
||||||
|
bool m_isFromConsole;
|
||||||
|
bool m_rapidOutput;
|
||||||
|
bool m_isMenuFillCommand;
|
||||||
|
int m_menuServerFillTeam;
|
||||||
|
int m_interMenuData[4] = { 0, };
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotControl ();
|
||||||
|
~BotControl () = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int cmdAddBot ();
|
||||||
|
int cmdKickBot ();
|
||||||
|
int cmdKickBots ();
|
||||||
|
int cmdKillBots ();
|
||||||
|
int cmdFill ();
|
||||||
|
int cmdVote ();
|
||||||
|
int cmdWeaponMode ();
|
||||||
|
int cmdVersion ();
|
||||||
|
int cmdNodeMenu ();
|
||||||
|
int cmdMenu ();
|
||||||
|
int cmdList ();
|
||||||
|
int cmdNode ();
|
||||||
|
int cmdNodeOn ();
|
||||||
|
int cmdNodeOff ();
|
||||||
|
int cmdNodeAdd ();
|
||||||
|
int cmdNodeAddBasic ();
|
||||||
|
int cmdNodeSave ();
|
||||||
|
int cmdNodeLoad ();
|
||||||
|
int cmdNodeErase ();
|
||||||
|
int cmdNodeDelete ();
|
||||||
|
int cmdNodeCheck ();
|
||||||
|
int cmdNodeCache ();
|
||||||
|
int cmdNodeClean ();
|
||||||
|
int cmdNodeSetRadius ();
|
||||||
|
int cmdNodeSetFlags ();
|
||||||
|
int cmdNodeTeleport ();
|
||||||
|
int cmdNodePathCreate ();
|
||||||
|
int cmdNodePathDelete ();
|
||||||
|
int cmdNodePathSetAutoDistance ();
|
||||||
|
int cmdNodeAcquireEditor ();
|
||||||
|
int cmdNodeReleaseEditor ();
|
||||||
|
int cmdNodeUpload ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int menuMain (int item);
|
||||||
|
int menuFeatures (int item);
|
||||||
|
int menuControl (int item);
|
||||||
|
int menuWeaponMode (int item);
|
||||||
|
int menuPersonality (int item);
|
||||||
|
int menuDifficulty (int item);
|
||||||
|
int menuTeamSelect (int item);
|
||||||
|
int menuClassSelect (int item);
|
||||||
|
int menuCommands (int item);
|
||||||
|
int menuGraphPage1 (int item);
|
||||||
|
int menuGraphPage2 (int item);
|
||||||
|
int menuGraphRadius (int item);
|
||||||
|
int menuGraphType (int item);
|
||||||
|
int menuGraphFlag (int item);
|
||||||
|
int menuGraphPath (int item);
|
||||||
|
int menuAutoPathDistance (int item);
|
||||||
|
int menuKickPage1 (int item);
|
||||||
|
int menuKickPage2 (int item);
|
||||||
|
int menuKickPage3 (int item);
|
||||||
|
int menuKickPage4 (int item);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void enableDrawModels (bool enable);
|
||||||
|
void createMenus ();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool executeCommands ();
|
||||||
|
bool executeMenus ();
|
||||||
|
|
||||||
|
void showMenu (int id);
|
||||||
|
void kickBotByMenu (int page);
|
||||||
|
void assignAdminRights (edict_t *ent, char *infobuffer);
|
||||||
|
void maintainAdminRights ();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setFromConsole (bool console) {
|
||||||
|
m_isFromConsole = console;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRapidOutput (bool force) {
|
||||||
|
m_rapidOutput = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIssuer (edict_t *ent) {
|
||||||
|
m_ent = ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixMissingArgs (size_t num) {
|
||||||
|
if (num < m_args.length ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_args.resize (num);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getInt (size_t arg) const {
|
||||||
|
if (!hasArg (arg)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_args[arg].int_ ();
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &getStr (size_t arg) {
|
||||||
|
static String empty ("empty");
|
||||||
|
|
||||||
|
if (!hasArg (arg) || m_args[arg].empty ()) {
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
return m_args[arg];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasArg (size_t arg) const {
|
||||||
|
return arg < m_args.length ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void collectArgs () {
|
||||||
|
m_args.clear ();
|
||||||
|
|
||||||
|
for (int i = 0; i < engfuncs.pfnCmd_Argc (); ++i) {
|
||||||
|
m_args.emplace (engfuncs.pfnCmd_Argv (i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// global heloer for sending message to correct channel
|
||||||
|
template <typename ...Args> void msg (const char *fmt, Args ...args);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// for the server commands
|
||||||
|
static void handleEngineCommands ();
|
||||||
|
|
||||||
|
// for the client commands
|
||||||
|
bool handleClientCommands (edict_t *ent);
|
||||||
|
|
||||||
|
// for the client menu commands
|
||||||
|
bool handleMenuCommands (edict_t *ent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// global heloer for sending message to correct channel
|
||||||
|
template <typename ...Args> inline void BotControl::msg (const char *fmt, Args ...args) {
|
||||||
|
auto result = strings.format (fmt, cr::forward <Args> (args)...);
|
||||||
|
|
||||||
|
// if no receiver or many message have to appear, just print to server console
|
||||||
|
if (game.isNullEntity (m_ent) || m_rapidOutput) {
|
||||||
|
game.print (result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_isFromConsole || strlen (result) > 48) {
|
||||||
|
game.clientPrint (m_ent, result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
game.centerPrint (m_ent, result);
|
||||||
|
game.clientPrint (m_ent, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// explose global
|
||||||
|
static auto &ctrl = BotControl::get ();
|
||||||
|
|
@ -50,7 +50,7 @@ public:
|
||||||
rhs.reset ();
|
rhs.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
Array (std::initializer_list <T> list) {
|
Array (const std::initializer_list <T> &list) {
|
||||||
for (const auto &elem : list) {
|
for (const auto &elem : list) {
|
||||||
push (elem);
|
push (elem);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,13 @@ template <typename K> struct IntHash {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// template for np hashing integers
|
||||||
|
template <typename K> struct IntNoHash {
|
||||||
|
uint32 operator () (K key) const {
|
||||||
|
return static_cast <uint32> (key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct DictionaryList {
|
struct DictionaryList {
|
||||||
uint32 index;
|
uint32 index;
|
||||||
|
|
|
||||||
|
|
@ -862,6 +862,14 @@ public:
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks if string is not empty
|
||||||
|
bool isEmpty (const char *input) const {
|
||||||
|
if (input == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return *input == '\0';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// expose global string pool
|
// expose global string pool
|
||||||
|
|
@ -1082,14 +1090,19 @@ public:
|
||||||
|
|
||||||
String strToUpper (const String &in) {
|
String strToUpper (const String &in) {
|
||||||
String result (in);
|
String result (in);
|
||||||
|
|
||||||
auto ptr = const_cast <char *> (result.chars ());
|
auto ptr = const_cast <char *> (result.chars ());
|
||||||
|
|
||||||
int32 len = 0;
|
int32 len = 0;
|
||||||
wchar_t wide;
|
|
||||||
|
|
||||||
while (*ptr && len < static_cast <int32> (result.length ())) {
|
while (*ptr) {
|
||||||
|
wchar_t wide = 0;
|
||||||
|
|
||||||
multiByteToWideChar (&wide, ptr);
|
multiByteToWideChar (&wide, ptr);
|
||||||
ptr += wideCharToMultiByte (ptr, toUpper (wide));
|
len += wideCharToMultiByte (ptr, toUpper (wide));
|
||||||
|
|
||||||
|
if (static_cast <size_t> (len) >= result.length ()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Vector &null () {
|
static const Vector &null () {
|
||||||
static const auto s_zero = Vector (0.0f, 0.0f, 0.0f);
|
static const Vector &s_null {};
|
||||||
return s_zero;
|
return s_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear () {
|
void clear () {
|
||||||
|
|
@ -224,6 +224,27 @@ public:
|
||||||
upward->z = cosines[roll] * cosines[pitch];
|
upward->z = cosines[roll] * cosines[pitch];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Vector &forward () {
|
||||||
|
static Vector s_fwd {};
|
||||||
|
buildVectors (&s_fwd, nullptr, nullptr);
|
||||||
|
|
||||||
|
return s_fwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector &upward () {
|
||||||
|
static Vector s_up {};
|
||||||
|
buildVectors (nullptr, nullptr, &s_up);
|
||||||
|
|
||||||
|
return s_up;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector &right () {
|
||||||
|
static Vector s_right {};
|
||||||
|
buildVectors (nullptr, &s_right, nullptr);
|
||||||
|
|
||||||
|
return s_right;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// expose global null vector
|
// expose global null vector
|
||||||
|
|
|
||||||
101
include/engine.h
101
include/engine.h
|
|
@ -33,34 +33,6 @@ CR_DECLARE_SCOPED_ENUM (Var,
|
||||||
NoRegister
|
NoRegister
|
||||||
)
|
)
|
||||||
|
|
||||||
// netmessage functions
|
|
||||||
CR_DECLARE_SCOPED_ENUM (NetMsg,
|
|
||||||
None = -1,
|
|
||||||
VGUI = 1,
|
|
||||||
ShowMenu = 2,
|
|
||||||
WeaponList = 3,
|
|
||||||
CurWeapon = 4,
|
|
||||||
AmmoX = 5,
|
|
||||||
AmmoPickup = 6,
|
|
||||||
Damage = 7,
|
|
||||||
Money = 8,
|
|
||||||
StatusIcon = 9,
|
|
||||||
DeathMsg = 10,
|
|
||||||
ScreenFade = 11,
|
|
||||||
HLTV = 12,
|
|
||||||
TextMsg = 13,
|
|
||||||
TeamInfo = 14,
|
|
||||||
BarTime = 15,
|
|
||||||
SendAudio = 17,
|
|
||||||
SayText = 18,
|
|
||||||
BotVoice = 19,
|
|
||||||
NVGToggle = 20,
|
|
||||||
FlashBat = 21,
|
|
||||||
Fashlight = 22,
|
|
||||||
ItemStatus = 23,
|
|
||||||
Count = 25
|
|
||||||
)
|
|
||||||
|
|
||||||
// supported cs's
|
// supported cs's
|
||||||
CR_DECLARE_SCOPED_ENUM (GameFlags,
|
CR_DECLARE_SCOPED_ENUM (GameFlags,
|
||||||
Modern = cr::bit (0), // counter-strike 1.6 and above
|
Modern = cr::bit (0), // counter-strike 1.6 and above
|
||||||
|
|
@ -97,19 +69,6 @@ struct VarPair {
|
||||||
class ConVar *self;
|
class ConVar *self;
|
||||||
};
|
};
|
||||||
|
|
||||||
// network message block
|
|
||||||
struct MessageBlock {
|
|
||||||
int bot;
|
|
||||||
int state;
|
|
||||||
int msg;
|
|
||||||
int regMsgs[NetMsg::Count];
|
|
||||||
};
|
|
||||||
|
|
||||||
// referentia vector info
|
|
||||||
struct RefVector {
|
|
||||||
Vector forward, right, up;
|
|
||||||
};
|
|
||||||
|
|
||||||
// entity prototype
|
// entity prototype
|
||||||
using EntityFunction = void (*) (entvars_t *);
|
using EntityFunction = void (*) (entvars_t *);
|
||||||
|
|
||||||
|
|
@ -126,9 +85,8 @@ private:
|
||||||
edict_t *m_startEntity;
|
edict_t *m_startEntity;
|
||||||
edict_t *m_localEntity;
|
edict_t *m_localEntity;
|
||||||
|
|
||||||
Array <VarPair> m_cvars;
|
SmallArray <VarPair> m_cvars;
|
||||||
SharedLibrary m_gameLib;
|
SharedLibrary m_gameLib;
|
||||||
MessageBlock m_msgBlock;
|
|
||||||
|
|
||||||
bool m_precached;
|
bool m_precached;
|
||||||
int m_gameFlags;
|
int m_gameFlags;
|
||||||
|
|
@ -136,12 +94,9 @@ private:
|
||||||
|
|
||||||
float m_slowFrame; // per second updated frame
|
float m_slowFrame; // per second updated frame
|
||||||
|
|
||||||
public:
|
|
||||||
RefVector vec;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Game ();
|
Game ();
|
||||||
~Game ();
|
~Game () = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// precaches internal stuff
|
// precaches internal stuff
|
||||||
|
|
@ -189,9 +144,6 @@ public:
|
||||||
// sends local registration stack for engine registration
|
// sends local registration stack for engine registration
|
||||||
void registerCvars (bool gameVars = false);
|
void registerCvars (bool gameVars = false);
|
||||||
|
|
||||||
// do actual network message processing
|
|
||||||
void processMessages (void *ptr);
|
|
||||||
|
|
||||||
// checks whether softwared rendering is enabled
|
// checks whether softwared rendering is enabled
|
||||||
bool isSoftwareRenderer ();
|
bool isSoftwareRenderer ();
|
||||||
|
|
||||||
|
|
@ -207,9 +159,6 @@ public:
|
||||||
// executes stuff every 1 second
|
// executes stuff every 1 second
|
||||||
void slowFrame ();
|
void slowFrame ();
|
||||||
|
|
||||||
// begin message handler
|
|
||||||
void beginMessage (edict_t *ent, int dest, int type);
|
|
||||||
|
|
||||||
// public inlines
|
// public inlines
|
||||||
public:
|
public:
|
||||||
// get the current time on server
|
// get the current time on server
|
||||||
|
|
@ -281,40 +230,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the player team
|
// gets the player team
|
||||||
int getTeam (edict_t *ent);
|
int getTeam (edict_t *ent) {
|
||||||
|
if (isNullEntity (ent)) {
|
||||||
// resets the message capture mechanism
|
return Team::Unassigned;
|
||||||
void resetMessages () {
|
|
||||||
m_msgBlock.msg = NetMsg::None;
|
|
||||||
m_msgBlock.state = 0;
|
|
||||||
m_msgBlock.bot = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// sets the currently executed message
|
|
||||||
void setCurrentMessageId (int message) {
|
|
||||||
m_msgBlock.msg = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the bot entity that receive this message
|
|
||||||
void setCurrentMessageOwner (int id) {
|
|
||||||
m_msgBlock.bot = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find registered message id
|
|
||||||
int getMessageId (int type) {
|
|
||||||
return m_msgBlock.regMsgs[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
// assigns message id for message type
|
|
||||||
void setMessageId (int type, int id) {
|
|
||||||
m_msgBlock.regMsgs[type] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tries to set needed message id
|
|
||||||
void captureMessage (int type, int msgId) {
|
|
||||||
if (type == m_msgBlock.regMsgs[msgId]) {
|
|
||||||
setCurrentMessageId (msgId);
|
|
||||||
}
|
}
|
||||||
|
return util.getClient (indexOfPlayer (ent)).team;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets the precache to uninitialize
|
// sets the precache to uninitialize
|
||||||
|
|
@ -332,11 +252,6 @@ public:
|
||||||
m_localEntity = ent;
|
m_localEntity = ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// builds referential vector
|
|
||||||
void makeVectors (const Vector &in) {
|
|
||||||
in.buildVectors (&vec.forward, &vec.right, &vec.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the engine visibility wrapper
|
// check the engine visibility wrapper
|
||||||
bool checkVisibility (edict_t *ent, uint8 *set);
|
bool checkVisibility (edict_t *ent, uint8 *set);
|
||||||
|
|
||||||
|
|
@ -401,7 +316,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// simplify access for console variables
|
// simplify access for console variables
|
||||||
class ConVar {
|
class ConVar final {
|
||||||
public:
|
public:
|
||||||
cvar_t *eptr;
|
cvar_t *eptr;
|
||||||
|
|
||||||
|
|
@ -439,7 +354,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MessageWriter {
|
class MessageWriter final {
|
||||||
private:
|
private:
|
||||||
bool m_autoDestruct { false };
|
bool m_autoDestruct { false };
|
||||||
|
|
||||||
|
|
|
||||||
436
include/graph.h
Normal file
436
include/graph.h
Normal file
|
|
@ -0,0 +1,436 @@
|
||||||
|
//
|
||||||
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
|
// Copyright (c) YaPB Development Team.
|
||||||
|
//
|
||||||
|
// This software is licensed under the BSD-style license.
|
||||||
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
|
// https://yapb.ru/license
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// defines for nodes flags field (32 bits are available)
|
||||||
|
CR_DECLARE_SCOPED_ENUM (NodeFlag,
|
||||||
|
Lift = cr::bit (1), // wait for lift to be down before approaching this node
|
||||||
|
Crouch = cr::bit (2), // must crouch to reach this node
|
||||||
|
Crossing = cr::bit (3), // a target node
|
||||||
|
Goal = cr::bit (4), // mission goal point (bomb, hostage etc.)
|
||||||
|
Ladder = cr::bit (5), // node is on ladder
|
||||||
|
Rescue = cr::bit (6), // node is a hostage rescue point
|
||||||
|
Camp = cr::bit (7), // node is a camping point
|
||||||
|
NoHostage = cr::bit (8), // only use this node if no hostage
|
||||||
|
DoubleJump = cr::bit (9), // bot help's another bot (requster) to get somewhere (using djump)
|
||||||
|
Sniper = cr::bit (28), // it's a specific sniper point
|
||||||
|
TerroristOnly = cr::bit (29), // it's a specific terrorist point
|
||||||
|
CTOnly = cr::bit (30), // it's a specific ct point
|
||||||
|
)
|
||||||
|
|
||||||
|
// defines for node connection flags field (16 bits are available)
|
||||||
|
CR_DECLARE_SCOPED_ENUM_TYPE (PathFlag, uint16,
|
||||||
|
Jump = cr::bit (0) // must jump for this connection
|
||||||
|
)
|
||||||
|
|
||||||
|
// enum pathfind search type
|
||||||
|
CR_DECLARE_SCOPED_ENUM (FindPath,
|
||||||
|
Fast = 0,
|
||||||
|
Optimal,
|
||||||
|
Safe
|
||||||
|
)
|
||||||
|
|
||||||
|
// defines node connection types
|
||||||
|
CR_DECLARE_SCOPED_ENUM (PathConnection,
|
||||||
|
Outgoing = 0,
|
||||||
|
Incoming,
|
||||||
|
Bidirectional
|
||||||
|
)
|
||||||
|
|
||||||
|
// defines node add commands
|
||||||
|
CR_DECLARE_SCOPED_ENUM (GraphAdd,
|
||||||
|
Normal = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
// a* route state
|
||||||
|
CR_DECLARE_SCOPED_ENUM (RouteState,
|
||||||
|
Open = 0,
|
||||||
|
Closed,
|
||||||
|
New
|
||||||
|
)
|
||||||
|
|
||||||
|
// node edit states
|
||||||
|
CR_DECLARE_SCOPED_ENUM (GraphEdit,
|
||||||
|
On = cr::bit (1),
|
||||||
|
Noclip = cr::bit (2),
|
||||||
|
Auto = cr::bit (3)
|
||||||
|
)
|
||||||
|
|
||||||
|
// storage header options
|
||||||
|
CR_DECLARE_SCOPED_ENUM (StorageOption,
|
||||||
|
Practice = cr::bit (0), // this is practice (experience) file
|
||||||
|
Matrix = cr::bit (1), // this is floyd warshal path & distance matrix
|
||||||
|
Vistable = cr::bit (2), // this is vistable data
|
||||||
|
Graph = cr::bit (3), // this is a node graph data
|
||||||
|
Official = cr::bit (4), // this is additional flag for graph indicates graph are official
|
||||||
|
Recovered = cr::bit (5), // this is additional flag indicates graph converted from podbot and was bad
|
||||||
|
Author = cr::bit (6) // this is additional flag indicates that there's author info
|
||||||
|
)
|
||||||
|
|
||||||
|
// storage header versions
|
||||||
|
CR_DECLARE_SCOPED_ENUM (StorageVersion,
|
||||||
|
Graph = 1,
|
||||||
|
Practice = 1,
|
||||||
|
Vistable = 1,
|
||||||
|
Matrix = 1,
|
||||||
|
Podbot = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// lift usage states
|
||||||
|
CR_DECLARE_SCOPED_ENUM (LiftState,
|
||||||
|
None = 0,
|
||||||
|
LookingButtonOutside,
|
||||||
|
WaitingFor,
|
||||||
|
EnteringIn,
|
||||||
|
WaitingForTeammates,
|
||||||
|
LookingButtonInside,
|
||||||
|
TravelingBy,
|
||||||
|
Leaving
|
||||||
|
)
|
||||||
|
|
||||||
|
// a* route
|
||||||
|
struct Route {
|
||||||
|
float g, f;
|
||||||
|
int parent;
|
||||||
|
RouteState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
// general stprage header information structure
|
||||||
|
struct StorageHeader {
|
||||||
|
int32 magic;
|
||||||
|
int32 version;
|
||||||
|
int32 options;
|
||||||
|
int32 length;
|
||||||
|
int32 compressed;
|
||||||
|
int32 uncompressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// general waypoint header information structure
|
||||||
|
struct PODGraphHeader {
|
||||||
|
char header[8];
|
||||||
|
int32 fileVersion;
|
||||||
|
int32 pointNumber;
|
||||||
|
char mapName[32];
|
||||||
|
char author[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
// floyd-warshall matrices
|
||||||
|
struct Matrix {
|
||||||
|
int16 dist;
|
||||||
|
int16 index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// experience data hold in memory while playing
|
||||||
|
struct Practice {
|
||||||
|
int16 damage[kGameTeamNum];
|
||||||
|
int16 index[kGameTeamNum];
|
||||||
|
int16 value[kGameTeamNum];
|
||||||
|
};
|
||||||
|
|
||||||
|
// defines linked waypoints
|
||||||
|
struct PathLink {
|
||||||
|
Vector velocity;
|
||||||
|
int32 distance;
|
||||||
|
uint16 flags;
|
||||||
|
int16 index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// defines visibility count
|
||||||
|
struct PathVis {
|
||||||
|
uint16 stand, crouch;
|
||||||
|
};
|
||||||
|
|
||||||
|
// define graph path structure for yapb
|
||||||
|
struct Path {
|
||||||
|
int32 number, flags;
|
||||||
|
Vector origin, start, end;
|
||||||
|
float radius, light, display;
|
||||||
|
PathLink links[kMaxNodeLinks];
|
||||||
|
PathVis vis;
|
||||||
|
};
|
||||||
|
|
||||||
|
// define waypoint structure for podbot (will convert on load)
|
||||||
|
struct PODPath {
|
||||||
|
int32 number, flags;
|
||||||
|
Vector origin;
|
||||||
|
float radius, csx, csy, cex, cey;
|
||||||
|
int16 index[kMaxNodeLinks];
|
||||||
|
uint16 conflags[kMaxNodeLinks];
|
||||||
|
Vector velocity[kMaxNodeLinks];
|
||||||
|
int32 distance[kMaxNodeLinks];
|
||||||
|
PathVis vis;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this structure links nodes returned from pathfinder
|
||||||
|
class PathWalk final : public DenyCopying {
|
||||||
|
private:
|
||||||
|
size_t m_cursor = 0;
|
||||||
|
Array <int, ReservePolicy::Single, kMaxRouteLength> m_storage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PathWalk () = default;
|
||||||
|
~PathWalk () = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int32 &next () {
|
||||||
|
return at (m_cursor + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 &first () {
|
||||||
|
return at (m_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 &last () {
|
||||||
|
return m_storage.last ();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 &at (size_t index) {
|
||||||
|
return m_storage.at (index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shift () {
|
||||||
|
++m_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverse () {
|
||||||
|
m_storage.reverse ();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length () const {
|
||||||
|
if (m_cursor > m_storage.length ()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_storage.length () - m_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cursor () const {
|
||||||
|
return m_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNext () const {
|
||||||
|
return m_cursor < m_storage.length ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty () const {
|
||||||
|
return !length ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void push (int node) {
|
||||||
|
m_storage.push (node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear () {
|
||||||
|
m_cursor = 0;
|
||||||
|
m_storage.clear ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// graph operation class
|
||||||
|
class BotGraph final : public Singleton <BotGraph> {
|
||||||
|
public:
|
||||||
|
friend class Bot;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Bucket {
|
||||||
|
int x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
int m_editFlags;
|
||||||
|
int m_loadAttempts;
|
||||||
|
int m_cacheNodeIndex;
|
||||||
|
int m_lastJumpNode;
|
||||||
|
int m_findWPIndex;
|
||||||
|
int m_facingAtIndex;
|
||||||
|
int m_highestDamage[kGameTeamNum];
|
||||||
|
|
||||||
|
float m_timeJumpStarted;
|
||||||
|
float m_autoPathDistance;
|
||||||
|
float m_pathDisplayTime;
|
||||||
|
float m_arrowDisplayTime;
|
||||||
|
|
||||||
|
bool m_isOnLadder;
|
||||||
|
bool m_endJumpPoint;
|
||||||
|
bool m_jumpLearnNode;
|
||||||
|
bool m_hasChanged;
|
||||||
|
bool m_needsVisRebuild;
|
||||||
|
|
||||||
|
Vector m_learnVelocity;
|
||||||
|
Vector m_learnPosition;
|
||||||
|
Vector m_bombPos;
|
||||||
|
Vector m_lastNode;
|
||||||
|
|
||||||
|
IntArray m_terrorPoints;
|
||||||
|
IntArray m_ctPoints;
|
||||||
|
IntArray m_goalPoints;
|
||||||
|
IntArray m_campPoints;
|
||||||
|
IntArray m_sniperPoints;
|
||||||
|
IntArray m_rescuePoints;
|
||||||
|
IntArray m_visitedGoals;
|
||||||
|
|
||||||
|
SmallArray <int32> m_buckets[kMaxBucketsInsidePos][kMaxBucketsInsidePos][kMaxBucketsInsidePos];
|
||||||
|
SmallArray <Matrix> m_matrix;
|
||||||
|
SmallArray <Practice> m_practice;
|
||||||
|
SmallArray <Path> m_paths;
|
||||||
|
SmallArray <uint8> m_vistable;
|
||||||
|
|
||||||
|
String m_tempStrings;
|
||||||
|
edict_t *m_editor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotGraph ();
|
||||||
|
~BotGraph () = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
int getFacingIndex ();
|
||||||
|
int getFarest (const Vector &origin, float maxDistance = 32.0);
|
||||||
|
int getNearest (const Vector &origin, float minDistance = kInfiniteDistance, int flags = -1);
|
||||||
|
int getNearestNoBuckets (const Vector &origin, float minDistance = kInfiniteDistance, int flags = -1);
|
||||||
|
int getEditorNeareset ();
|
||||||
|
int getDangerIndex (int team, int start, int goal);
|
||||||
|
int getDangerValue (int team, int start, int goal);
|
||||||
|
int getDangerDamage (int team, int start, int goal);
|
||||||
|
int getPathDist (int srcIndex, int destIndex);
|
||||||
|
int clearConnections (int index);
|
||||||
|
|
||||||
|
float calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin);
|
||||||
|
|
||||||
|
bool convertOldFormat ();
|
||||||
|
bool isVisible (int srcIndex, int destIndex);
|
||||||
|
bool isStandVisible (int srcIndex, int destIndex);
|
||||||
|
bool isDuckVisible (int srcIndex, int destIndex);
|
||||||
|
bool isConnected (int a, int b);
|
||||||
|
bool isConnected (int index);
|
||||||
|
bool isNodeReacheable (const Vector &src, const Vector &destination);
|
||||||
|
bool checkNodes (bool teleportPlayer);
|
||||||
|
bool loadPathMatrix ();
|
||||||
|
bool isVisited (int index);
|
||||||
|
|
||||||
|
bool saveGraphData ();
|
||||||
|
bool loadGraphData ();
|
||||||
|
|
||||||
|
template <typename U> bool saveStorage (const String &ext, const String &name, StorageOption options, StorageVersion version, const SmallArray <U> &data, uint8 *blob);
|
||||||
|
template <typename U> bool loadStorage (const String &ext, const String &name, StorageOption options, StorageVersion version, SmallArray <U> &data, uint8 *blob, int32 *outOptions);
|
||||||
|
|
||||||
|
void saveOldFormat ();
|
||||||
|
void initGraph ();
|
||||||
|
void frame ();
|
||||||
|
void loadPractice ();
|
||||||
|
void loadVisibility ();
|
||||||
|
void initNodesTypes ();
|
||||||
|
void initLightLevels ();
|
||||||
|
void addPath (int addIndex, int pathIndex, float distance);
|
||||||
|
void add (int type, const Vector &pos = nullvec);
|
||||||
|
void erase (int target);
|
||||||
|
void toggleFlags (int toggleFlag);
|
||||||
|
void setRadius (int index, float radius);
|
||||||
|
void rebuildVisibility ();
|
||||||
|
void pathCreate (char dir);
|
||||||
|
void erasePath ();
|
||||||
|
void cachePoint (int index);
|
||||||
|
void calculatePathRadius (int index);
|
||||||
|
void savePractice ();
|
||||||
|
void saveVisibility ();
|
||||||
|
void addBasic ();
|
||||||
|
void eraseFromDisk ();
|
||||||
|
void savePathMatrix ();
|
||||||
|
void setSearchIndex (int index);
|
||||||
|
void startLearnJump ();
|
||||||
|
void setVisited (int index);
|
||||||
|
void clearVisited ();
|
||||||
|
void initBuckets ();
|
||||||
|
void addToBucket (const Vector &pos, int index);
|
||||||
|
void eraseFromBucket (const Vector &pos, int index);
|
||||||
|
void setBombPos (bool reset = false, const Vector &pos = nullvec);
|
||||||
|
void updateGlobalPractice ();
|
||||||
|
void unassignPath (int from, int to);
|
||||||
|
void setDangerValue (int team, int start, int goal, int value);
|
||||||
|
void setDangerDamage (int team, int start, int goal, int value);
|
||||||
|
void convertFromPOD (Path &path, const PODPath &pod);
|
||||||
|
void convertToPOD (const Path &path, PODPath &pod);
|
||||||
|
void convertCampDirection (Path &path);
|
||||||
|
|
||||||
|
const char *getDataDirectory (bool isMemoryFile = false);
|
||||||
|
const char *getOldFormatGraphName (bool isMemoryFile = false);
|
||||||
|
|
||||||
|
Bucket locateBucket (const Vector &pos);
|
||||||
|
IntArray searchRadius (float radius, const Vector &origin, int maxCount = -1);
|
||||||
|
const SmallArray <int32> &getNodesInBucket (const Vector &pos);
|
||||||
|
|
||||||
|
public:
|
||||||
|
int getHighestDamageForTeam (int team) const {
|
||||||
|
return m_highestDamage[team];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHighestDamageForTeam (int team, int value) {
|
||||||
|
m_highestDamage[team] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getAuthor () const {
|
||||||
|
return m_tempStrings.chars ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasChanged () const {
|
||||||
|
return m_hasChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasEditFlag (int flag) const {
|
||||||
|
return !!(m_editFlags & flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEditFlag (int flag) {
|
||||||
|
m_editFlags |= flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearEditFlag (int flag) {
|
||||||
|
m_editFlags &= ~flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAutoPathDistance (const float distance) {
|
||||||
|
m_autoPathDistance = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector &getBombPos () const {
|
||||||
|
return m_bombPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// access paths
|
||||||
|
Path &operator [] (int index) {
|
||||||
|
return m_paths[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// check nodes range
|
||||||
|
bool exists (int index) const {
|
||||||
|
return index >= 0 && index < static_cast <int> (m_paths.length ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// get real nodes num
|
||||||
|
int length () const {
|
||||||
|
return m_paths.length ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if has editor
|
||||||
|
bool hasEditor () const {
|
||||||
|
return !!m_editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set's the node editor
|
||||||
|
void setEditor (edict_t *ent) {
|
||||||
|
m_editor = ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the current node editor
|
||||||
|
edict_t *getEditor () {
|
||||||
|
return m_editor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// explose global
|
||||||
|
static auto &graph = BotGraph::get ();
|
||||||
273
include/manager.h
Normal file
273
include/manager.h
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
//
|
||||||
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
|
// Copyright (c) YaPB Development Team.
|
||||||
|
//
|
||||||
|
// This software is licensed under the BSD-style license.
|
||||||
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
|
// https://yapb.ru/license
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// bot creation tab
|
||||||
|
struct CreateQueue {
|
||||||
|
bool manual;
|
||||||
|
int difficulty;
|
||||||
|
int team;
|
||||||
|
int member;
|
||||||
|
int personality;
|
||||||
|
String name;
|
||||||
|
};
|
||||||
|
|
||||||
|
// manager class
|
||||||
|
class BotManager final : public Singleton <BotManager> {
|
||||||
|
public:
|
||||||
|
using ForEachBot = Lambda <bool (Bot *)>;
|
||||||
|
using UniqueBot = UniquePtr <Bot>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_timeRoundStart;
|
||||||
|
float m_timeRoundEnd;
|
||||||
|
float m_timeRoundMid;
|
||||||
|
|
||||||
|
float m_maintainTime; // time to maintain bot creation
|
||||||
|
float m_quotaMaintainTime; // time to maintain bot quota
|
||||||
|
float m_grenadeUpdateTime; // time to update active grenades
|
||||||
|
float m_entityUpdateTime; // time to update intresting entities
|
||||||
|
float m_plantSearchUpdateTime; // time to update for searching planted bomb
|
||||||
|
float m_lastChatTime; // global chat time timestamp
|
||||||
|
float m_timeBombPlanted; // time the bomb were planted
|
||||||
|
float m_lastRadioTime[kGameTeamNum]; // global radio time
|
||||||
|
|
||||||
|
int m_lastWinner; // the team who won previous round
|
||||||
|
int m_lastDifficulty; // last bots difficulty
|
||||||
|
int m_bombSayStatus; // some bot is issued whine about bomb
|
||||||
|
int m_lastRadio[kGameTeamNum]; // last radio message for team
|
||||||
|
|
||||||
|
bool m_leaderChoosen[kGameTeamNum]; // is team leader choose theese round
|
||||||
|
bool m_economicsGood[kGameTeamNum]; // is team able to buy anything
|
||||||
|
bool m_bombPlanted;
|
||||||
|
bool m_botsCanPause;
|
||||||
|
bool m_roundEnded;
|
||||||
|
|
||||||
|
Array <edict_t *> m_activeGrenades; // holds currently active grenades on the map
|
||||||
|
Array <edict_t *> m_intrestingEntities; // holds currently intresting entities on the map
|
||||||
|
|
||||||
|
SmallArray <CreateQueue> m_creationTab; // bot creation tab
|
||||||
|
SmallArray <BotTask> m_filters; // task filters
|
||||||
|
SmallArray <UniqueBot> m_bots; // all available bots
|
||||||
|
|
||||||
|
edict_t *m_killerEntity; // killer entity for bots
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BotCreateResult create (const String &name, int difficulty, int personality, int team, int member);
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotManager ();
|
||||||
|
~BotManager () = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Twin <int, int> countTeamPlayers ();
|
||||||
|
|
||||||
|
Bot *findBotByIndex (int index);
|
||||||
|
Bot *findBotByEntity (edict_t *ent);
|
||||||
|
|
||||||
|
Bot *findAliveBot ();
|
||||||
|
Bot *findHighestFragBot (int team);
|
||||||
|
|
||||||
|
int getHumansCount (bool ignoreSpectators = false);
|
||||||
|
int getAliveHumansCount ();
|
||||||
|
int getBotCount ();
|
||||||
|
float getConnectionTime (int botId);
|
||||||
|
|
||||||
|
void setBombPlanted (bool isPlanted);
|
||||||
|
void slowFrame ();
|
||||||
|
void frame ();
|
||||||
|
void createKillerEntity ();
|
||||||
|
void destroyKillerEntity ();
|
||||||
|
void touchKillerEntity (Bot *bot);
|
||||||
|
void destroy ();
|
||||||
|
void addbot (const String &name, int difficulty, int personality, int team, int member, bool manual);
|
||||||
|
void addbot (const String &name, const String &difficulty, const String &personality, const String &team, const String &member, bool manual);
|
||||||
|
void serverFill (int selection, int personality = Personality::Normal, int difficulty = -1, int numToAdd = -1);
|
||||||
|
void kickEveryone (bool instant = false, bool zeroQuota = true);
|
||||||
|
bool kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
|
||||||
|
void kickBot (int index);
|
||||||
|
void kickFromTeam (Team team, bool removeAll = false);
|
||||||
|
void killAllBots (int team = -1);
|
||||||
|
void maintainQuota ();
|
||||||
|
void initQuota ();
|
||||||
|
void initRound ();
|
||||||
|
void decrementQuota (int by = 1);
|
||||||
|
void selectLeaders (int team, bool reset);
|
||||||
|
void listBots ();
|
||||||
|
void setWeaponMode (int selection);
|
||||||
|
void updateTeamEconomics (int team, bool setTrue = false);
|
||||||
|
void updateBotDifficulties ();
|
||||||
|
void reset ();
|
||||||
|
void initFilters ();
|
||||||
|
void resetFilters ();
|
||||||
|
void updateActiveGrenade ();
|
||||||
|
void updateIntrestingEntities ();
|
||||||
|
void captureChatRadio (const char *cmd, const char *arg, edict_t *ent);
|
||||||
|
void notifyBombDefuse ();
|
||||||
|
void execGameEntity (entvars_t *vars);
|
||||||
|
void forEach (ForEachBot handler);
|
||||||
|
void erase (Bot *bot);
|
||||||
|
void handleDeath (edict_t *killer, edict_t *victim);
|
||||||
|
|
||||||
|
bool isTeamStacked (int team);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Array <edict_t *> &searchActiveGrenades () {
|
||||||
|
return m_activeGrenades;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array <edict_t *> &searchIntrestingEntities () {
|
||||||
|
return m_intrestingEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasActiveGrenades () const {
|
||||||
|
return !m_activeGrenades.empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasIntrestingEntities () const {
|
||||||
|
return !m_intrestingEntities.empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkTeamEco (int team) const {
|
||||||
|
return m_economicsGood[team];
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLastWinner () const {
|
||||||
|
return m_lastWinner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLastWinner (int winner) {
|
||||||
|
m_lastWinner = winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the list of filters
|
||||||
|
SmallArray <BotTask> &getFilters () {
|
||||||
|
return m_filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createRandom (bool manual = false) {
|
||||||
|
addbot ("", -1, -1, -1, -1, manual);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBombPlanted () const {
|
||||||
|
return m_bombPlanted;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getTimeBombPlanted () const {
|
||||||
|
return m_timeBombPlanted;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRoundStartTime () const {
|
||||||
|
return m_timeRoundStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRoundMidTime () const {
|
||||||
|
return m_timeRoundMid;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRoundEndTime () const {
|
||||||
|
return m_timeRoundEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRoundOver () const {
|
||||||
|
return m_roundEnded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRoundOver (const bool over) {
|
||||||
|
m_roundEnded = over;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canPause () const {
|
||||||
|
return m_botsCanPause;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCanPause (const bool pause) {
|
||||||
|
m_botsCanPause = pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasBombSay (int type) {
|
||||||
|
return (m_bombSayStatus & type) == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearBombSay (int type) {
|
||||||
|
m_bombSayStatus &= ~type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPlantedBombSearchTimestamp (const float timestamp) {
|
||||||
|
m_plantSearchUpdateTime = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getPlantedBombSearchTimestamp () const {
|
||||||
|
return m_plantSearchUpdateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLastRadioTimestamp (const int team, const float timestamp) {
|
||||||
|
if (team == Team::CT || team == Team::Terrorist) {
|
||||||
|
m_lastRadioTime[team] = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float getLastRadioTimestamp (const int team) const {
|
||||||
|
if (team == Team::CT || team == Team::Terrorist) {
|
||||||
|
return m_lastRadioTime[team];
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLastRadio (const int team, const int radio) {
|
||||||
|
m_lastRadio[team] = radio;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLastRadio (const int team) const {
|
||||||
|
return m_lastRadio[team];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLastChatTimestamp (const float timestamp) {
|
||||||
|
m_lastChatTime = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getLastChatTimestamp () const {
|
||||||
|
return m_lastChatTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some bots are online ?
|
||||||
|
bool hasBotsOnline () {
|
||||||
|
return getBotCount () > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Bot *operator [] (int index) {
|
||||||
|
return findBotByIndex (index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bot *operator [] (edict_t *ent) {
|
||||||
|
return findBotByEntity (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
UniqueBot *begin () {
|
||||||
|
return m_bots.begin ();
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueBot *begin () const {
|
||||||
|
return m_bots.begin ();
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueBot *end () {
|
||||||
|
return m_bots.end ();
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueBot *end () const {
|
||||||
|
return m_bots.end ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// explose global
|
||||||
|
static auto &bots = BotManager::get ();
|
||||||
147
include/message.h
Normal file
147
include/message.h
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
//
|
||||||
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
|
// Copyright (c) YaPB Development Team.
|
||||||
|
//
|
||||||
|
// This software is licensed under the BSD-style license.
|
||||||
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
|
// https://yapb.ru/license
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// netmessage functions enum
|
||||||
|
CR_DECLARE_SCOPED_ENUM (NetMsg,
|
||||||
|
None = -1,
|
||||||
|
VGUIMenu = 1,
|
||||||
|
ShowMenu = 2,
|
||||||
|
WeaponList = 3,
|
||||||
|
CurWeapon = 4,
|
||||||
|
AmmoX = 5,
|
||||||
|
AmmoPickup = 6,
|
||||||
|
Damage = 7,
|
||||||
|
Money = 8,
|
||||||
|
StatusIcon = 9,
|
||||||
|
DeathMsg = 10,
|
||||||
|
ScreenFade = 11,
|
||||||
|
HLTV = 12,
|
||||||
|
TextMsg = 13,
|
||||||
|
TeamInfo = 14,
|
||||||
|
BarTime = 15,
|
||||||
|
SendAudio = 17,
|
||||||
|
BotVoice = 18,
|
||||||
|
NVGToggle = 19,
|
||||||
|
FlashBat = 20,
|
||||||
|
Fashlight = 21,
|
||||||
|
ItemStatus = 22
|
||||||
|
)
|
||||||
|
|
||||||
|
// vgui menus (since latest steam updates is obsolete, but left for old cs)
|
||||||
|
CR_DECLARE_SCOPED_ENUM (GuiMenu,
|
||||||
|
TeamSelect = 2, // menu select team
|
||||||
|
TerroristSelect = 26, // terrorist select menu
|
||||||
|
CTSelect = 27 // ct select menu
|
||||||
|
)
|
||||||
|
|
||||||
|
// cache flags for TextMsg message
|
||||||
|
CR_DECLARE_SCOPED_ENUM (TextMsgCache,
|
||||||
|
NeedHandle = cr::bit (0),
|
||||||
|
TerroristWin = cr::bit (1),
|
||||||
|
CounterWin = cr::bit (2),
|
||||||
|
Commencing = cr::bit (3),
|
||||||
|
BombPlanted = cr::bit (4),
|
||||||
|
RestartRound = cr::bit (5),
|
||||||
|
BurstOn = cr::bit (6),
|
||||||
|
BurstOff = cr::bit (7)
|
||||||
|
)
|
||||||
|
|
||||||
|
// cache flags for StatusIcon message
|
||||||
|
CR_DECLARE_SCOPED_ENUM (StatusIconCache,
|
||||||
|
NeedHandle = cr::bit (0),
|
||||||
|
BuyZone = cr::bit (1),
|
||||||
|
VipSafety = cr::bit (2),
|
||||||
|
C4 = cr::bit (3)
|
||||||
|
)
|
||||||
|
|
||||||
|
class MessageDispatcher final : public Singleton <MessageDispatcher> {
|
||||||
|
private:
|
||||||
|
using MsgFunc = void (MessageDispatcher::*) ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Args {
|
||||||
|
union {
|
||||||
|
float float_;
|
||||||
|
long long_;
|
||||||
|
const char *chars_;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Args (float value) : float_ (value) { }
|
||||||
|
Args (int value) : long_ (value) { }
|
||||||
|
Args (const char *value) : chars_ (value) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Dictionary <String, int32> m_textMsgCache; // cache strings for faster access for textmsg
|
||||||
|
Dictionary <String, int32> m_showMenuCache; // cache for the showmenu message
|
||||||
|
Dictionary <String, int32> m_statusIconCache; // cache for status icon message
|
||||||
|
Dictionary <String, int32> m_teamInfoCache; // cache for teaminfo message
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_broadcast; // message for all players
|
||||||
|
|
||||||
|
Bot *m_bot; // owner of a message
|
||||||
|
NetMsg m_current; // ongoing message id
|
||||||
|
|
||||||
|
SmallArray <Args> m_args; // args collected from write* functions
|
||||||
|
Dictionary <String, NetMsg> m_wanted; // wanted messages
|
||||||
|
|
||||||
|
Dictionary <NetMsg, int32, IntNoHash <int32>> m_maps; // maps our message to id to engine message id
|
||||||
|
Dictionary <NetMsg, MsgFunc, IntNoHash <int32>> m_handlers; // maps our message id to handler function
|
||||||
|
|
||||||
|
private:
|
||||||
|
void netMsgTextMsg ();
|
||||||
|
void netMsgVGUIMenu ();
|
||||||
|
void netMsgShowMenu ();
|
||||||
|
void netMsgWeaponList ();
|
||||||
|
void netMsgCurWeapon ();
|
||||||
|
void netMsgAmmoX ();
|
||||||
|
void netMsgAmmoPickup ();
|
||||||
|
void netMsgDamage ();
|
||||||
|
void netMsgMoney ();
|
||||||
|
void netMsgStatusIcon ();
|
||||||
|
void netMsgDeathMsg ();
|
||||||
|
void netMsgScreenFade ();
|
||||||
|
void netMsgHLTV ();
|
||||||
|
void netMsgTeamInfo ();
|
||||||
|
void netMsgBarTime ();
|
||||||
|
void netMsgItemStatus ();
|
||||||
|
void netMsgNVGToggle ();
|
||||||
|
void netMsgFlashBat ();
|
||||||
|
|
||||||
|
public:
|
||||||
|
MessageDispatcher ();
|
||||||
|
~MessageDispatcher () = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void registerMessage (const String &name, int32 id);
|
||||||
|
void start (edict_t *ent, int32 dest, int32 type);
|
||||||
|
void stop ();
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T> void collect (const T &value) {
|
||||||
|
if (m_current == NetMsg::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_args.emplace (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopCollection () {
|
||||||
|
m_current = NetMsg::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 id (NetMsg msg) const {
|
||||||
|
return m_maps[msg];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto &msgs = MessageDispatcher::get ();
|
||||||
137
include/utils.h
Normal file
137
include/utils.h
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
//
|
||||||
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
|
// Copyright (c) YaPB Development Team.
|
||||||
|
//
|
||||||
|
// This software is licensed under the BSD-style license.
|
||||||
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
|
// https://yapb.ru/license
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class BotUtils final : public Singleton <BotUtils> {
|
||||||
|
private:
|
||||||
|
bool m_needToSendWelcome;
|
||||||
|
float m_welcomeReceiveTime;
|
||||||
|
|
||||||
|
StringArray m_sentences;
|
||||||
|
SmallArray <Client> m_clients;
|
||||||
|
SmallArray <Twin <String, String>> m_tags;
|
||||||
|
|
||||||
|
SimpleHook m_sendToHook;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BotUtils ();
|
||||||
|
~BotUtils () = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// need to send welcome message ?
|
||||||
|
void checkWelcome ();
|
||||||
|
|
||||||
|
// gets the weapon alias as hlsting, maybe move to config...
|
||||||
|
int getWeaponAlias (bool needString, const char *weaponAlias, int weaponIndex = -1);
|
||||||
|
|
||||||
|
// gets the build number of bot
|
||||||
|
int buildNumber ();
|
||||||
|
|
||||||
|
// gets the shooting cone deviation
|
||||||
|
float getShootingCone (edict_t *ent, const Vector &position);
|
||||||
|
|
||||||
|
// check if origin is visible from the entity side
|
||||||
|
bool isVisible (const Vector &origin, edict_t *ent);
|
||||||
|
|
||||||
|
// check if entity is alive
|
||||||
|
bool isAlive (edict_t *ent);
|
||||||
|
|
||||||
|
// check if origin is inside view cone of entity
|
||||||
|
bool isInViewCone (const Vector &origin, edict_t *ent);
|
||||||
|
|
||||||
|
// checks if entitiy is fakeclient
|
||||||
|
bool isFakeClient (edict_t *ent);
|
||||||
|
|
||||||
|
// check if entitiy is a player
|
||||||
|
bool isPlayer (edict_t *ent);
|
||||||
|
|
||||||
|
// check if entity is a vip
|
||||||
|
bool isPlayerVIP (edict_t *ent);
|
||||||
|
|
||||||
|
// opens config helper
|
||||||
|
bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant = false);
|
||||||
|
|
||||||
|
// nearest player search helper
|
||||||
|
bool findNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false, bool needBotWithC4 = false);
|
||||||
|
|
||||||
|
// tracing decals for bots spraying logos
|
||||||
|
void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex);
|
||||||
|
|
||||||
|
// attaches sound to client struct
|
||||||
|
void attachSoundsToClients (edict_t *ent, const char *sample, float volume);
|
||||||
|
|
||||||
|
// simulate sound for players
|
||||||
|
void simulateSoundUpdates (int playerIndex);
|
||||||
|
|
||||||
|
// update stats on clients
|
||||||
|
void updateClients ();
|
||||||
|
|
||||||
|
// chat helper to strip the clantags out of the string
|
||||||
|
void stripTags (String &line);
|
||||||
|
|
||||||
|
// chat helper to make player name more human-like
|
||||||
|
void humanizePlayerName (String &playerName);
|
||||||
|
|
||||||
|
// chat helper to add errors to the bot chat string
|
||||||
|
void addChatErrors (String &line);
|
||||||
|
|
||||||
|
// chat helper to find keywords for given string
|
||||||
|
bool checkKeywords (const String &line, String &reply);
|
||||||
|
|
||||||
|
// generates ping bitmask for SVC_PINGS message
|
||||||
|
int getPingBitmask (edict_t *ent, int loss, int ping);
|
||||||
|
|
||||||
|
// calculate our own pings for all the players
|
||||||
|
void calculatePings ();
|
||||||
|
|
||||||
|
// send modified pings to all the clients
|
||||||
|
void sendPings (edict_t *to);
|
||||||
|
|
||||||
|
// installs the sendto function intreception
|
||||||
|
void installSendTo ();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// re-show welcome after changelevel ?
|
||||||
|
void setNeedForWelcome (bool need) {
|
||||||
|
m_needToSendWelcome = need;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get array of clients
|
||||||
|
SmallArray <Client> &getClients () {
|
||||||
|
return m_clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get clients as const-reference
|
||||||
|
const SmallArray <Client> &getClients () const {
|
||||||
|
return m_clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get single client as ref
|
||||||
|
Client &getClient (const int index) {
|
||||||
|
return m_clients[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// disables send hook
|
||||||
|
bool disableSendTo () {
|
||||||
|
return m_sendToHook.disable ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enables send hook
|
||||||
|
bool enableSendTo () {
|
||||||
|
return m_sendToHook.enable ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static int32 CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength);
|
||||||
|
};
|
||||||
|
|
||||||
|
// explose global
|
||||||
|
static auto &util = BotUtils::get ();
|
||||||
1243
include/yapb.h
1243
include/yapb.h
File diff suppressed because it is too large
Load diff
|
|
@ -11,6 +11,8 @@
|
||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\include\config.h" />
|
||||||
|
<ClInclude Include="..\include\control.h" />
|
||||||
<ClInclude Include="..\include\crlib\cr-alloc.h" />
|
<ClInclude Include="..\include\crlib\cr-alloc.h" />
|
||||||
<ClInclude Include="..\include\crlib\cr-array.h" />
|
<ClInclude Include="..\include\crlib\cr-array.h" />
|
||||||
<ClInclude Include="..\include\crlib\cr-basic.h" />
|
<ClInclude Include="..\include\crlib\cr-basic.h" />
|
||||||
|
|
@ -34,6 +36,10 @@
|
||||||
<ClInclude Include="..\include\crlib\cr-uniqueptr.h" />
|
<ClInclude Include="..\include\crlib\cr-uniqueptr.h" />
|
||||||
<ClInclude Include="..\include\crlib\cr-vector.h" />
|
<ClInclude Include="..\include\crlib\cr-vector.h" />
|
||||||
<ClInclude Include="..\include\engine\model.h" />
|
<ClInclude Include="..\include\engine\model.h" />
|
||||||
|
<ClInclude Include="..\include\graph.h" />
|
||||||
|
<ClInclude Include="..\include\manager.h" />
|
||||||
|
<ClInclude Include="..\include\message.h" />
|
||||||
|
<ClInclude Include="..\include\utils.h" />
|
||||||
<ClInclude Include="..\include\yapb.h" />
|
<ClInclude Include="..\include\yapb.h" />
|
||||||
<ClInclude Include="..\include\engine.h" />
|
<ClInclude Include="..\include\engine.h" />
|
||||||
<ClInclude Include="..\include\engine\const.h" />
|
<ClInclude Include="..\include\engine\const.h" />
|
||||||
|
|
@ -53,6 +59,7 @@
|
||||||
<ClCompile Include="..\source\chatlib.cpp" />
|
<ClCompile Include="..\source\chatlib.cpp" />
|
||||||
<ClCompile Include="..\source\control.cpp" />
|
<ClCompile Include="..\source\control.cpp" />
|
||||||
<ClCompile Include="..\source\interface.cpp" />
|
<ClCompile Include="..\source\interface.cpp" />
|
||||||
|
<ClCompile Include="..\source\message.cpp" />
|
||||||
<ClCompile Include="..\source\navigate.cpp" />
|
<ClCompile Include="..\source\navigate.cpp" />
|
||||||
<ClCompile Include="..\source\support.cpp" />
|
<ClCompile Include="..\source\support.cpp" />
|
||||||
<ClCompile Include="..\source\graph.cpp" />
|
<ClCompile Include="..\source\graph.cpp" />
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,24 @@
|
||||||
<ClInclude Include="..\include\crlib\cr-hook.h">
|
<ClInclude Include="..\include\crlib\cr-hook.h">
|
||||||
<Filter>include\crlib</Filter>
|
<Filter>include\crlib</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\config.h">
|
||||||
|
<Filter>include</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\graph.h">
|
||||||
|
<Filter>include</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\manager.h">
|
||||||
|
<Filter>include</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\utils.h">
|
||||||
|
<Filter>include</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\control.h">
|
||||||
|
<Filter>include</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\message.h">
|
||||||
|
<Filter>include</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\source\chatlib.cpp">
|
<ClCompile Include="..\source\chatlib.cpp">
|
||||||
|
|
@ -149,6 +167,9 @@
|
||||||
<ClCompile Include="..\source\android.cpp">
|
<ClCompile Include="..\source\android.cpp">
|
||||||
<Filter>source</Filter>
|
<Filter>source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\source\message.cpp">
|
||||||
|
<Filter>source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="yapb.rc">
|
<ResourceCompile Include="yapb.rc">
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
basecode.cpp \
|
basecode.cpp \
|
||||||
manager.cpp \
|
manager.cpp \
|
||||||
|
message.cpp \
|
||||||
chatlib.cpp \
|
chatlib.cpp \
|
||||||
combat.cpp \
|
combat.cpp \
|
||||||
control.cpp \
|
control.cpp \
|
||||||
|
|
|
||||||
|
|
@ -354,10 +354,8 @@ void Bot::avoidGrenades () {
|
||||||
float distanceMoved = ((pent->v.origin + pent->v.velocity * getFrameInterval ()) - pev->origin).length ();
|
float distanceMoved = ((pent->v.origin + pent->v.velocity * getFrameInterval ()) - pev->origin).length ();
|
||||||
|
|
||||||
if (distanceMoved < distance && distance < 500.0f) {
|
if (distanceMoved < distance && distance < 500.0f) {
|
||||||
game.makeVectors (pev->v_angle);
|
const auto &dirToPoint = (pev->origin - pent->v.origin).normalize2d ();
|
||||||
|
const auto &rightSide = pev->v_angle.right ().normalize2d ();
|
||||||
const Vector &dirToPoint = (pev->origin - pent->v.origin).normalize2d ();
|
|
||||||
const Vector &rightSide = game.vec.right.normalize2d ();
|
|
||||||
|
|
||||||
if ((dirToPoint | rightSide) > 0.0f) {
|
if ((dirToPoint | rightSide) > 0.0f) {
|
||||||
m_needAvoidGrenade = -1;
|
m_needAvoidGrenade = -1;
|
||||||
|
|
@ -854,7 +852,7 @@ void Bot::showChaterIcon (bool show) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sendBotVoice = [](bool show, edict_t *ent, int ownId) {
|
auto sendBotVoice = [](bool show, edict_t *ent, int ownId) {
|
||||||
MessageWriter (MSG_ONE, game.getMessageId (NetMsg::BotVoice), nullvec, ent) // begin message
|
MessageWriter (MSG_ONE, msgs.id (NetMsg::BotVoice), nullvec, ent) // begin message
|
||||||
.writeByte (show) // switch on/off
|
.writeByte (show) // switch on/off
|
||||||
.writeByte (ownId);
|
.writeByte (ownId);
|
||||||
};
|
};
|
||||||
|
|
@ -897,7 +895,7 @@ void Bot::instantChatter (int type) {
|
||||||
if (!(client.flags & ClientFlags::Used) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) {
|
if (!(client.flags & ClientFlags::Used) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
msg.start (MSG_ONE, game.getMessageId (NetMsg::SendAudio), nullvec, client.ent); // begin message
|
msg.start (MSG_ONE, msgs.id (NetMsg::SendAudio), nullvec, client.ent); // begin message
|
||||||
msg.writeByte (ownIndex);
|
msg.writeByte (ownIndex);
|
||||||
|
|
||||||
if (pev->deadflag & DEAD_DYING) {
|
if (pev->deadflag & DEAD_DYING) {
|
||||||
|
|
@ -1107,7 +1105,7 @@ void Bot::checkMsgQueue () {
|
||||||
bool Bot::isWeaponRestricted (int weaponIndex) {
|
bool Bot::isWeaponRestricted (int weaponIndex) {
|
||||||
// this function checks for weapon restrictions.
|
// this function checks for weapon restrictions.
|
||||||
|
|
||||||
if (util.isEmptyStr (yb_restricted_weapons.str ())) {
|
if (strings.isEmpty (yb_restricted_weapons.str ())) {
|
||||||
return isWeaponRestrictedAMX (weaponIndex); // no banned weapons
|
return isWeaponRestrictedAMX (weaponIndex); // no banned weapons
|
||||||
}
|
}
|
||||||
auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";");
|
auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";");
|
||||||
|
|
@ -1130,7 +1128,7 @@ bool Bot::isWeaponRestrictedAMX (int weaponIndex) {
|
||||||
if (cr::bit (weaponIndex) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) {
|
if (cr::bit (weaponIndex) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) {
|
||||||
const char *restrictedWeapons = engfuncs.pfnCVarGetString ("amx_restrweapons");
|
const char *restrictedWeapons = engfuncs.pfnCVarGetString ("amx_restrweapons");
|
||||||
|
|
||||||
if (util.isEmptyStr (restrictedWeapons)) {
|
if (strings.isEmpty (restrictedWeapons)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int indices[] = {4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11};
|
int indices[] = {4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11};
|
||||||
|
|
@ -1149,7 +1147,7 @@ bool Bot::isWeaponRestrictedAMX (int weaponIndex) {
|
||||||
else {
|
else {
|
||||||
const char *restrictedEquipment = engfuncs.pfnCVarGetString ("amx_restrequipammo");
|
const char *restrictedEquipment = engfuncs.pfnCVarGetString ("amx_restrequipammo");
|
||||||
|
|
||||||
if (util.isEmptyStr (restrictedEquipment)) {
|
if (strings.isEmpty (restrictedEquipment)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int indices[] = {-1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 0, 1, 5};
|
int indices[] = {-1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 0, 1, 5};
|
||||||
|
|
@ -1176,7 +1174,7 @@ bool Bot::canReplaceWeapon () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!util.isEmptyStr (yb_restricted_weapons.str ())) {
|
if (!strings.isEmpty (yb_restricted_weapons.str ())) {
|
||||||
auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";");
|
auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";");
|
||||||
|
|
||||||
// check if its banned
|
// check if its banned
|
||||||
|
|
@ -1435,10 +1433,10 @@ void Bot::buyStuff () {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (m_team == Team::Terrorist) {
|
if (m_team == Team::Terrorist) {
|
||||||
game.botCommand (ent (), "menuselect %d", selectedWeapon->newBuySelectT);
|
game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectT);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
game.botCommand (ent (), "menuselect %d", selectedWeapon->newBuySelectCT);
|
game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectCT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1529,10 +1527,10 @@ void Bot::buyStuff () {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (m_team == Team::Terrorist) {
|
if (m_team == Team::Terrorist) {
|
||||||
game.botCommand (ent (), "menuselect %d", selectedWeapon->newBuySelectT);
|
game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectT);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
game.botCommand (ent (), "menuselect %d", selectedWeapon->newBuySelectCT);
|
game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectCT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2346,9 +2344,7 @@ void Bot::checkRadioQueue () {
|
||||||
getTask ()->time = game.timebase ();
|
getTask ()->time = game.timebase ();
|
||||||
|
|
||||||
m_targetEntity = nullptr;
|
m_targetEntity = nullptr;
|
||||||
game.makeVectors (m_radioEntity->v.v_angle);
|
m_position = m_radioEntity->v.origin + m_radioEntity->v.v_angle.forward () * rg.float_ (1024.0f, 2048.0f);
|
||||||
|
|
||||||
m_position = m_radioEntity->v.origin + game.vec.forward * rg.float_ (1024.0f, 2048.0f);
|
|
||||||
|
|
||||||
clearSearchNodes ();
|
clearSearchNodes ();
|
||||||
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
||||||
|
|
@ -2403,9 +2399,7 @@ void Bot::checkRadioQueue () {
|
||||||
getTask ()->time = game.timebase ();
|
getTask ()->time = game.timebase ();
|
||||||
}
|
}
|
||||||
m_targetEntity = nullptr;
|
m_targetEntity = nullptr;
|
||||||
|
m_position = m_radioEntity->v.origin + m_radioEntity->v.v_angle.forward () * rg.float_ (1024.0f, 2048.0f);
|
||||||
game.makeVectors (m_radioEntity->v.v_angle);
|
|
||||||
m_position = m_radioEntity->v.origin + game.vec.forward * rg.float_ (1024.0f, 2048.0f);
|
|
||||||
|
|
||||||
clearSearchNodes ();
|
clearSearchNodes ();
|
||||||
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
||||||
|
|
@ -3030,14 +3024,10 @@ void Bot::normal_ () {
|
||||||
if (!(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && !m_reloadState) {
|
if (!(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && !m_reloadState) {
|
||||||
m_reloadState = Reload::Primary;
|
m_reloadState = Reload::Primary;
|
||||||
}
|
}
|
||||||
game.makeVectors (pev->v_angle);
|
|
||||||
|
|
||||||
m_timeCamping = game.timebase () + rg.float_ (10.0f, 25.0f);
|
m_timeCamping = game.timebase () + rg.float_ (10.0f, 25.0f);
|
||||||
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, m_timeCamping, true);
|
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, m_timeCamping, true);
|
||||||
|
|
||||||
game.makeVectors (m_path->start);
|
m_camp = m_path->origin + m_path->start.forward () * 500.0f;;
|
||||||
|
|
||||||
m_camp = m_path->origin + game.vec.forward * 500.0f;;
|
|
||||||
m_aimFlags |= AimFlags::Camp;
|
m_aimFlags |= AimFlags::Camp;
|
||||||
m_campDirection = 0;
|
m_campDirection = 0;
|
||||||
|
|
||||||
|
|
@ -3174,8 +3164,8 @@ void Bot::spraypaint_ () {
|
||||||
|
|
||||||
// bot didn't spray this round?
|
// bot didn't spray this round?
|
||||||
if (m_timeLogoSpray < game.timebase () && getTask ()->time > game.timebase ()) {
|
if (m_timeLogoSpray < game.timebase () && getTask ()->time > game.timebase ()) {
|
||||||
game.makeVectors (pev->v_angle);
|
const auto &forward = pev->v_angle.forward ();
|
||||||
Vector sprayOrigin = getEyesPos () + game.vec.forward * 128.0f;
|
Vector sprayOrigin = getEyesPos () + forward * 128.0f;
|
||||||
|
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
game.testLine (getEyesPos (), sprayOrigin, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (getEyesPos (), sprayOrigin, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
@ -3189,7 +3179,7 @@ void Bot::spraypaint_ () {
|
||||||
if (getTask ()->time - 0.5f < game.timebase ()) {
|
if (getTask ()->time - 0.5f < game.timebase ()) {
|
||||||
// emit spraycan sound
|
// emit spraycan sound
|
||||||
engfuncs.pfnEmitSound (ent (), CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100);
|
engfuncs.pfnEmitSound (ent (), CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100);
|
||||||
game.testLine (getEyesPos (), getEyesPos () + game.vec.forward * 128.0f, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (getEyesPos (), getEyesPos () + forward * 128.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
// paint the actual logo decal
|
// paint the actual logo decal
|
||||||
util.traceDecals (pev, &tr, m_logotypeIndex);
|
util.traceDecals (pev, &tr, m_logotypeIndex);
|
||||||
|
|
@ -3407,8 +3397,7 @@ void Bot::pause_ () {
|
||||||
if (m_moveSpeed < -pev->maxspeed) {
|
if (m_moveSpeed < -pev->maxspeed) {
|
||||||
m_moveSpeed = -pev->maxspeed;
|
m_moveSpeed = -pev->maxspeed;
|
||||||
}
|
}
|
||||||
game.makeVectors (pev->v_angle);
|
m_camp = getEyesPos () + pev->v_angle.forward () * 500.0f;
|
||||||
m_camp = getEyesPos () + game.vec.forward * 500.0f;
|
|
||||||
|
|
||||||
m_aimFlags |= AimFlags::Override;
|
m_aimFlags |= AimFlags::Override;
|
||||||
m_wantsToFire = true;
|
m_wantsToFire = true;
|
||||||
|
|
@ -3889,10 +3878,8 @@ void Bot::followUser_ () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_targetEntity->v.button & IN_ATTACK) {
|
if (m_targetEntity->v.button & IN_ATTACK) {
|
||||||
game.makeVectors (m_targetEntity->v.v_angle);
|
|
||||||
|
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
game.testLine (m_targetEntity->v.origin + m_targetEntity->v.view_ofs, game.vec.forward * 500.0f, TraceIgnore::Everything, ent (), &tr);
|
game.testLine (m_targetEntity->v.origin + m_targetEntity->v.view_ofs, m_targetEntity->v.v_angle.forward () * 500.0f, TraceIgnore::Everything, ent (), &tr);
|
||||||
|
|
||||||
if (!game.isNullEntity (tr.pHit) && util.isPlayer (tr.pHit) && game.getTeam (tr.pHit) != m_team) {
|
if (!game.isNullEntity (tr.pHit) && util.isPlayer (tr.pHit) && game.getTeam (tr.pHit) != m_team) {
|
||||||
m_targetEntity = nullptr;
|
m_targetEntity = nullptr;
|
||||||
|
|
@ -4181,10 +4168,8 @@ void Bot::doublejump_ () {
|
||||||
else if (inJump && !(m_oldButtons & IN_JUMP)) {
|
else if (inJump && !(m_oldButtons & IN_JUMP)) {
|
||||||
pev->button |= IN_JUMP;
|
pev->button |= IN_JUMP;
|
||||||
}
|
}
|
||||||
game.makeVectors (Vector (0.0f, pev->angles.y, 0.0f));
|
const auto &src = pev->origin + Vector (0.0f, 0.0f, 45.0f);
|
||||||
|
const auto &dest = src + Vector (0.0f, pev->angles.y, 0.0f).upward () * 256.0f;
|
||||||
Vector src = pev->origin + Vector (0.0f, 0.0f, 45.0f);
|
|
||||||
Vector dest = src + game.vec.up * 256.0f;
|
|
||||||
|
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
game.testLine (src, dest, TraceIgnore::None, ent (), &tr);
|
game.testLine (src, dest, TraceIgnore::None, ent (), &tr);
|
||||||
|
|
@ -4739,7 +4724,7 @@ void Bot::runAI () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if bot is trapped under shield yell for help !
|
// if bot is trapped under shield yell for help !
|
||||||
if (getCurrentTaskId () == Task::Camp && hasShield () && isShieldDrawn () && hasFriendNearby >= 2 && seesEnemy (m_enemy)) {
|
if (getCurrentTaskId () == Task::Camp && hasShield () && isShieldDrawn () && hasFriendNearby >= 2) {
|
||||||
pushChatterMessage (Chatter::PinnedDown);
|
pushChatterMessage (Chatter::PinnedDown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5014,16 +4999,12 @@ void Bot::showDebugOverlay () {
|
||||||
// blue = ideal angles
|
// blue = ideal angles
|
||||||
// red = view angles
|
// red = view angles
|
||||||
game.drawLine (game.getLocalEntity (), getEyesPos (), m_destOrigin, 10, 0, Color (0, 255, 0), 250, 5, 1, DrawLine::Arrow);
|
game.drawLine (game.getLocalEntity (), getEyesPos (), m_destOrigin, 10, 0, Color (0, 255, 0), 250, 5, 1, DrawLine::Arrow);
|
||||||
|
game.drawLine (game.getLocalEntity (), getEyesPos () - Vector (0.0f, 0.0f, 16.0f), getEyesPos () + m_idealAngles.forward () * 300.0f, 10, 0, Color (0, 0, 255), 250, 5, 1, DrawLine::Arrow);
|
||||||
game.makeVectors (m_idealAngles);
|
game.drawLine (game.getLocalEntity (), getEyesPos () - Vector (0.0f, 0.0f, 32.0f), getEyesPos () + pev->v_angle.forward () * 300.0f, 10, 0, Color (255, 0, 0), 250, 5, 1, DrawLine::Arrow);
|
||||||
game.drawLine (game.getLocalEntity (), getEyesPos () - Vector (0.0f, 0.0f, 16.0f), getEyesPos () + game.vec.forward * 300.0f, 10, 0, Color (0, 0, 255), 250, 5, 1, DrawLine::Arrow);
|
|
||||||
|
|
||||||
game.makeVectors (pev->v_angle);
|
|
||||||
game.drawLine (game.getLocalEntity (), getEyesPos () - Vector (0.0f, 0.0f, 32.0f), getEyesPos () + game.vec.forward * 300.0f, 10, 0, Color (255, 0, 0), 250, 5, 1, DrawLine::Arrow);
|
|
||||||
|
|
||||||
// now draw line from source to destination
|
// now draw line from source to destination
|
||||||
for (size_t i = 0; i < m_pathWalk.length () && i + 1 < m_pathWalk.length (); ++i) {
|
for (size_t i = m_pathWalk.cursor (); i < m_pathWalk.length () && i + 1 < m_pathWalk.length (); ++i) {
|
||||||
game.drawLine (game.getLocalEntity (), graph[m_pathWalk[i]].origin, graph[m_pathWalk[i + 1]].origin, 15, 0, Color (255, 100, 55), 200, 5, 1, DrawLine::Arrow);
|
game.drawLine (game.getLocalEntity (), graph[m_pathWalk.at (i)].origin, graph[m_pathWalk.at (i + 1)].origin, 15, 0, Color (255, 100, 55), 200, 5, 1, DrawLine::Arrow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5109,7 +5090,7 @@ void Bot::processDamage (edict_t *inflictor, int damage, int armor, int bits) {
|
||||||
// hurt by unusual damage like drowning or gas
|
// hurt by unusual damage like drowning or gas
|
||||||
else {
|
else {
|
||||||
// leave the camping/hiding position
|
// leave the camping/hiding position
|
||||||
if (!graph.isReachable (this, graph.getNearest (m_destOrigin))) {
|
if (!isReachableNode (graph.getNearest (m_destOrigin))) {
|
||||||
clearSearchNodes ();
|
clearSearchNodes ();
|
||||||
findBestNearestNode ();
|
findBestNearestNode ();
|
||||||
}
|
}
|
||||||
|
|
@ -5705,7 +5686,7 @@ void Bot::updateHearing () {
|
||||||
extern ConVar yb_shoots_thru_walls;
|
extern ConVar yb_shoots_thru_walls;
|
||||||
|
|
||||||
// check if heard enemy can be seen
|
// check if heard enemy can be seen
|
||||||
if (checkBodyParts (player, &m_enemyOrigin, &m_visibility)) {
|
if (checkBodyParts (player)) {
|
||||||
m_enemy = player;
|
m_enemy = player;
|
||||||
m_lastEnemy = player;
|
m_lastEnemy = player;
|
||||||
m_lastEnemyOrigin = m_enemyOrigin;
|
m_lastEnemyOrigin = m_enemyOrigin;
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ void Bot::checkForChat () {
|
||||||
void Bot::say (const char *text) {
|
void Bot::say (const char *text) {
|
||||||
// this function prints saytext message to all players
|
// this function prints saytext message to all players
|
||||||
|
|
||||||
if (util.isEmptyStr (text) || !yb_chat.bool_ ()) {
|
if (strings.isEmpty (text) || !yb_chat.bool_ ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
game.botCommand (ent (), "say \"%s\"", text);
|
game.botCommand (ent (), "say \"%s\"", text);
|
||||||
|
|
@ -367,7 +367,7 @@ void Bot::say (const char *text) {
|
||||||
void Bot::sayTeam (const char *text) {
|
void Bot::sayTeam (const char *text) {
|
||||||
// this function prints saytext message only for teammates
|
// this function prints saytext message only for teammates
|
||||||
|
|
||||||
if (util.isEmptyStr (text) || !yb_chat.bool_ ()) {
|
if (strings.isEmpty (text) || !yb_chat.bool_ ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
game.botCommand (ent (), "say_team \"%s\"", text);
|
game.botCommand (ent (), "say_team \"%s\"", text);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ int Bot::numFriendsNear (const Vector &origin, float radius) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((client.origin - origin).lengthSq () < cr::square (radius)) {
|
if ((client.origin - origin).lengthSq () < cr::square (radius)) {
|
||||||
++count;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
|
@ -39,7 +39,7 @@ int Bot::numEnemiesNear (const Vector &origin, float radius) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((client.origin - origin).lengthSq () < cr::square (radius)) {
|
if ((client.origin - origin).lengthSq () < cr::square (radius)) {
|
||||||
++count;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
|
@ -87,39 +87,39 @@ bool Bot::isEnemyHidden (edict_t *enemy) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::checkBodyParts (edict_t *target, Vector *origin, uint8 *bodyPart) {
|
bool Bot::checkBodyParts (edict_t *target) {
|
||||||
// this function checks visibility of a bot target.
|
// this function checks visibility of a bot target.
|
||||||
|
|
||||||
if (isEnemyHidden (target)) {
|
if (isEnemyHidden (target)) {
|
||||||
*bodyPart = 0;
|
m_enemyParts = Visibility::None;
|
||||||
origin->clear ();
|
m_enemyOrigin = nullvec;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TraceResult result;
|
TraceResult result;
|
||||||
|
auto eyes = getEyesPos ();
|
||||||
|
|
||||||
Vector eyes = getEyesPos ();
|
auto spot = target->v.origin;
|
||||||
Vector spot = target->v.origin;
|
auto self = pev->pContainingEntity;
|
||||||
|
|
||||||
*bodyPart = 0;
|
m_enemyParts = Visibility::None;
|
||||||
|
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
|
||||||
game.testLine (eyes, spot, TraceIgnore::Everything, ent (), &result);
|
|
||||||
|
|
||||||
if (result.flFraction >= 1.0f) {
|
if (result.flFraction >= 1.0f) {
|
||||||
*bodyPart |= Visibility::Body;
|
m_enemyParts |= Visibility::Body;
|
||||||
*origin = result.vecEndPos;
|
m_enemyOrigin = result.vecEndPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check top of head
|
// check top of head
|
||||||
spot.z += 25.0f;
|
spot.z += 25.0f;
|
||||||
game.testLine (eyes, spot, TraceIgnore::Everything, ent (), &result);
|
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
|
||||||
|
|
||||||
if (result.flFraction >= 1.0f) {
|
if (result.flFraction >= 1.0f) {
|
||||||
*bodyPart |= Visibility::Head;
|
m_enemyParts |= Visibility::Head;
|
||||||
*origin = result.vecEndPos;
|
m_enemyOrigin = result.vecEndPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*bodyPart != 0) {
|
if (m_enemyParts != 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,11 +132,11 @@ bool Bot::checkBodyParts (edict_t *target, Vector *origin, uint8 *bodyPart) {
|
||||||
else {
|
else {
|
||||||
spot.z = target->v.origin.z - standFeet;
|
spot.z = target->v.origin.z - standFeet;
|
||||||
}
|
}
|
||||||
game.testLine (eyes, spot, TraceIgnore::Everything, ent (), &result);
|
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
|
||||||
|
|
||||||
if (result.flFraction >= 1.0f) {
|
if (result.flFraction >= 1.0f) {
|
||||||
*bodyPart |= Visibility::Other;
|
m_enemyParts |= Visibility::Other;
|
||||||
*origin = result.vecEndPos;
|
m_enemyOrigin = result.vecEndPos;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -147,21 +147,21 @@ bool Bot::checkBodyParts (edict_t *target, Vector *origin, uint8 *bodyPart) {
|
||||||
Vector perp (-dir.y, dir.x, 0.0f);
|
Vector perp (-dir.y, dir.x, 0.0f);
|
||||||
spot = target->v.origin + Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0);
|
spot = target->v.origin + Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0);
|
||||||
|
|
||||||
game.testLine (eyes, spot, TraceIgnore::Everything, ent (), &result);
|
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
|
||||||
|
|
||||||
if (result.flFraction >= 1.0f) {
|
if (result.flFraction >= 1.0f) {
|
||||||
*bodyPart |= Visibility::Other;
|
m_enemyParts |= Visibility::Other;
|
||||||
*origin = result.vecEndPos;
|
m_enemyOrigin = result.vecEndPos;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
spot = target->v.origin - Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0);
|
spot = target->v.origin - Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0);
|
||||||
|
|
||||||
game.testLine (eyes, spot, TraceIgnore::Everything, ent (), &result);
|
game.testLine (eyes, spot, TraceIgnore::Everything, self, &result);
|
||||||
|
|
||||||
if (result.flFraction >= 1.0f) {
|
if (result.flFraction >= 1.0f) {
|
||||||
*bodyPart |= Visibility::Other;
|
m_enemyParts |= Visibility::Other;
|
||||||
*origin = result.vecEndPos;
|
m_enemyOrigin = result.vecEndPos;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +177,7 @@ bool Bot::seesEnemy (edict_t *player, bool ignoreFOV) {
|
||||||
ignoreFOV = true;
|
ignoreFOV = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ignoreFOV || isInViewCone (player->v.origin)) && checkBodyParts (player, &m_enemyOrigin, &m_visibility)) {
|
if ((ignoreFOV || isInViewCone (player->v.origin)) && checkBodyParts (player)) {
|
||||||
m_seeEnemyTime = game.timebase ();
|
m_seeEnemyTime = game.timebase ();
|
||||||
m_lastEnemy = player;
|
m_lastEnemy = player;
|
||||||
m_lastEnemyOrigin = m_enemyOrigin;
|
m_lastEnemyOrigin = m_enemyOrigin;
|
||||||
|
|
@ -207,12 +207,9 @@ bool Bot::lookupEnemies () {
|
||||||
m_states |= Sense::SuspectEnemy;
|
m_states |= Sense::SuspectEnemy;
|
||||||
m_aimFlags |= AimFlags::LastEnemy;
|
m_aimFlags |= AimFlags::LastEnemy;
|
||||||
}
|
}
|
||||||
m_visibility = 0;
|
m_enemyParts = Visibility::None;
|
||||||
m_enemyOrigin= nullvec;
|
m_enemyOrigin= nullvec;
|
||||||
|
|
||||||
// setup potential visibility set from engine
|
|
||||||
auto set = game.getVisibilitySet (this, true);
|
|
||||||
|
|
||||||
if (!game.isNullEntity (m_enemy)) {
|
if (!game.isNullEntity (m_enemy)) {
|
||||||
player = m_enemy;
|
player = m_enemy;
|
||||||
|
|
||||||
|
|
@ -224,13 +221,14 @@ bool Bot::lookupEnemies () {
|
||||||
|
|
||||||
// the old enemy is no longer visible or
|
// the old enemy is no longer visible or
|
||||||
if (game.isNullEntity (newEnemy)) {
|
if (game.isNullEntity (newEnemy)) {
|
||||||
|
auto set = game.getVisibilitySet (this, true); // setup potential visibility set from engine
|
||||||
|
|
||||||
// ignore shielded enemies, while we have real one
|
// ignore shielded enemies, while we have real one
|
||||||
edict_t *shieldEnemy = nullptr;
|
edict_t *shieldEnemy = nullptr;
|
||||||
|
|
||||||
// search the world for players...
|
// search the world for players...
|
||||||
for (const auto &client : util.getClients ()) {
|
for (const auto &client : util.getClients ()) {
|
||||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team == m_team || client.ent == ent ()) {
|
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team == m_team || client.ent == ent () || !client.ent) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
player = client.ent;
|
player = client.ent;
|
||||||
|
|
@ -264,7 +262,7 @@ bool Bot::lookupEnemies () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_enemyUpdateTime = game.timebase () + getFrameInterval () * 30.0f;
|
m_enemyUpdateTime = cr::clamp (game.timebase () + getFrameInterval () * 25.0f, 0.5f, 0.75f);
|
||||||
|
|
||||||
if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) {
|
if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) {
|
||||||
newEnemy = shieldEnemy;
|
newEnemy = shieldEnemy;
|
||||||
|
|
@ -422,38 +420,38 @@ const Vector &Bot::getEnemyBodyOffset () {
|
||||||
};
|
};
|
||||||
|
|
||||||
// if no visibility data, use last one
|
// if no visibility data, use last one
|
||||||
if (!m_visibility) {
|
if (!m_enemyParts) {
|
||||||
return m_enemyOrigin;
|
return m_enemyOrigin;
|
||||||
}
|
}
|
||||||
float distance = (m_enemy->v.origin - pev->origin).length ();
|
float distance = (m_enemy->v.origin - pev->origin).length ();
|
||||||
|
|
||||||
// do not aim at head, at long distance (only if not using sniper weapon)
|
// do not aim at head, at long distance (only if not using sniper weapon)
|
||||||
if ((m_visibility & Visibility::Body) && !usesSniper () && distance > (m_difficulty > 2 ? 2000.0f : 1000.0f)) {
|
if ((m_enemyParts & Visibility::Body) && !usesSniper () && distance > (m_difficulty > 2 ? 2000.0f : 1000.0f)) {
|
||||||
m_visibility &= ~Visibility::Head;
|
m_enemyParts &= ~Visibility::Head;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not aim at head while close enough to enemy and having sniper
|
// do not aim at head while close enough to enemy and having sniper
|
||||||
else if (distance < 800.0f && usesSniper ()) {
|
else if (distance < 800.0f && usesSniper ()) {
|
||||||
m_visibility &= ~Visibility::Head;
|
m_enemyParts &= ~Visibility::Head;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not aim at head while enemy is soooo close enough to enemy when recoil aims at head automatically
|
// do not aim at head while enemy is soooo close enough to enemy when recoil aims at head automatically
|
||||||
else if (distance < kSprayDistance) {
|
else if (distance < kSprayDistance) {
|
||||||
m_visibility &= ~Visibility::Head;
|
m_enemyParts &= ~Visibility::Head;
|
||||||
}
|
}
|
||||||
Vector aimPos = m_enemy->v.origin;
|
Vector aimPos = m_enemy->v.origin;
|
||||||
|
|
||||||
if (m_difficulty > 2 && !(m_visibility & Visibility::Other)) {
|
if (m_difficulty > 2 && !(m_enemyParts & Visibility::Other)) {
|
||||||
aimPos = (m_enemy->v.velocity - pev->velocity) * getFrameInterval () + aimPos;
|
aimPos = (m_enemy->v.velocity - pev->velocity) * getFrameInterval () + aimPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we only suspect an enemy behind a wall take the worst skill
|
// if we only suspect an enemy behind a wall take the worst skill
|
||||||
if (!m_visibility && (m_states & Sense::SuspectEnemy)) {
|
if (!m_enemyParts && (m_states & Sense::SuspectEnemy)) {
|
||||||
aimPos += getBodyOffsetError (distance);
|
aimPos += getBodyOffsetError (distance);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// now take in account different parts of enemy body
|
// now take in account different parts of enemy body
|
||||||
if (m_visibility & (Visibility::Head | Visibility::Body)) {
|
if (m_enemyParts & (Visibility::Head | Visibility::Body)) {
|
||||||
int headshotFreq[5] = { 20, 40, 60, 80, 100 };
|
int headshotFreq[5] = { 20, 40, 60, 80, 100 };
|
||||||
|
|
||||||
// now check is our skill match to aim at head, else aim at enemy body
|
// now check is our skill match to aim at head, else aim at enemy body
|
||||||
|
|
@ -464,13 +462,13 @@ const Vector &Bot::getEnemyBodyOffset () {
|
||||||
aimPos.z += getEnemyBodyOffsetCorrection (distance);
|
aimPos.z += getEnemyBodyOffsetCorrection (distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_visibility & Visibility::Body) {
|
else if (m_enemyParts & Visibility::Body) {
|
||||||
aimPos.z += getEnemyBodyOffsetCorrection (distance);
|
aimPos.z += getEnemyBodyOffsetCorrection (distance);
|
||||||
}
|
}
|
||||||
else if (m_visibility & Visibility::Other) {
|
else if (m_enemyParts & Visibility::Other) {
|
||||||
aimPos = m_enemyOrigin;
|
aimPos = m_enemyOrigin;
|
||||||
}
|
}
|
||||||
else if (m_visibility & Visibility::Head) {
|
else if (m_enemyParts & Visibility::Head) {
|
||||||
aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance);
|
aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -531,17 +529,16 @@ bool Bot::isFriendInLineOfFire (float distance) {
|
||||||
if (!mp_friendlyfire.bool_ () || game.is (GameFlags::CSDM)) {
|
if (!mp_friendlyfire.bool_ () || game.is (GameFlags::CSDM)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
game.makeVectors (pev->v_angle);
|
|
||||||
|
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
game.testLine (getEyesPos (), getEyesPos () + distance * pev->v_angle, TraceIgnore::None, ent (), &tr);
|
game.testLine (getEyesPos (), getEyesPos () + distance * pev->v_angle.normalize (), TraceIgnore::None, ent (), &tr);
|
||||||
|
|
||||||
// check if we hit something
|
// check if we hit something
|
||||||
if (util.isPlayer (tr.pHit) && tr.pHit != ent ()) {
|
if (util.isPlayer (tr.pHit) && tr.pHit != ent ()) {
|
||||||
auto hit = tr.pHit;
|
auto hit = tr.pHit;
|
||||||
|
|
||||||
// check valid range
|
// check valid range
|
||||||
if (util.isAlive (hit) && game.getTeam (hit) == m_team) {
|
if (game.getTeam (hit) == m_team && util.isAlive (hit)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -551,12 +548,9 @@ bool Bot::isFriendInLineOfFire (float distance) {
|
||||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team || client.ent == ent ()) {
|
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team || client.ent == ent ()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
edict_t *pent = client.ent;
|
auto friendDistance = (client.ent->v.origin - pev->origin).lengthSq ();
|
||||||
|
|
||||||
float friendDistance = (pent->v.origin - pev->origin).length ();
|
if (friendDistance <= distance && util.getShootingCone (ent (), client.ent->v.origin) > friendDistance / (friendDistance + 1089.0f)) {
|
||||||
float squareDistance = cr::sqrtf (1089.0f + cr::square (friendDistance));
|
|
||||||
|
|
||||||
if (friendDistance <= distance && util.getShootingCone (ent (), pent->v.origin) > cr::square (friendDistance) / cr::square (squareDistance)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -634,14 +628,14 @@ bool Bot::isPenetrableObstacle2 (const Vector &dest) {
|
||||||
game.testLine (source, dest, TraceIgnore::Everything, ent (), &tr);
|
game.testLine (source, dest, TraceIgnore::Everything, ent (), &tr);
|
||||||
|
|
||||||
while (tr.flFraction != 1.0f && numHits < 3) {
|
while (tr.flFraction != 1.0f && numHits < 3) {
|
||||||
++numHits;
|
numHits++;
|
||||||
++thikness;
|
thikness++;
|
||||||
|
|
||||||
point = tr.vecEndPos + direction;
|
point = tr.vecEndPos + direction;
|
||||||
|
|
||||||
while (engfuncs.pfnPointContents (point) == CONTENTS_SOLID && thikness < 98) {
|
while (engfuncs.pfnPointContents (point) == CONTENTS_SOLID && thikness < 98) {
|
||||||
point = point + direction;
|
point = point + direction;
|
||||||
++thikness;
|
thikness++;
|
||||||
}
|
}
|
||||||
game.testLine (point, dest, TraceIgnore::Everything, ent (), &tr);
|
game.testLine (point, dest, TraceIgnore::Everything, ent (), &tr);
|
||||||
}
|
}
|
||||||
|
|
@ -712,7 +706,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
||||||
// select this weapon if it isn't already selected
|
// select this weapon if it isn't already selected
|
||||||
if (m_currentWeapon != id) {
|
if (m_currentWeapon != id) {
|
||||||
const auto &prop = conf.getWeaponProp (id);
|
const auto &prop = conf.getWeaponProp (id);
|
||||||
selectWeaponByName (prop.classname);
|
selectWeaponByName (prop.classname.chars ());
|
||||||
|
|
||||||
// reset burst fire variables
|
// reset burst fire variables
|
||||||
m_firePause = 0.0f;
|
m_firePause = 0.0f;
|
||||||
|
|
@ -729,7 +723,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
||||||
if (tab[choosen].id == id) {
|
if (tab[choosen].id == id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++choosen;
|
choosen++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -741,7 +735,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
||||||
if (distance >= 750.0f && !isShieldDrawn ()) {
|
if (distance >= 750.0f && !isShieldDrawn ()) {
|
||||||
pev->button |= IN_ATTACK2; // draw the shield
|
pev->button |= IN_ATTACK2; // draw the shield
|
||||||
}
|
}
|
||||||
else if (isShieldDrawn () || (!game.isNullEntity (m_enemy) && ((m_enemy->v.button & IN_RELOAD) || !seesEnemy (m_enemy)))) {
|
else if (isShieldDrawn () || (!game.isNullEntity (m_enemy) && ((m_enemy->v.button & IN_RELOAD) || !seesEntity (m_enemy->v.origin)))) {
|
||||||
pev->button |= IN_ATTACK2; // draw out the shield
|
pev->button |= IN_ATTACK2; // draw out the shield
|
||||||
}
|
}
|
||||||
m_shieldCheckTime = game.timebase () + 1.0f;
|
m_shieldCheckTime = game.timebase () + 1.0f;
|
||||||
|
|
@ -893,7 +887,7 @@ void Bot::fireWeapons () {
|
||||||
choosenWeapon = selectIndex;
|
choosenWeapon = selectIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++selectIndex;
|
selectIndex++;
|
||||||
}
|
}
|
||||||
selectId = tab[choosenWeapon].id;
|
selectId = tab[choosenWeapon].id;
|
||||||
|
|
||||||
|
|
@ -923,7 +917,7 @@ void Bot::fireWeapons () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++selectIndex;
|
selectIndex++;
|
||||||
}
|
}
|
||||||
selectId = Weapon::Knife; // no available ammo, use knife!
|
selectId = Weapon::Knife; // no available ammo, use knife!
|
||||||
}
|
}
|
||||||
|
|
@ -1052,7 +1046,7 @@ void Bot::attackMovement () {
|
||||||
m_moveSpeed = -pev->maxspeed;
|
m_moveSpeed = -pev->maxspeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usesSniper () || !(m_visibility & (Visibility::Body | Visibility::Head))) {
|
if (usesSniper () || !(m_enemyParts & (Visibility::Body | Visibility::Head))) {
|
||||||
m_fightStyle = Fight::Stay;
|
m_fightStyle = Fight::Stay;
|
||||||
m_lastFightStyleCheck = game.timebase ();
|
m_lastFightStyleCheck = game.timebase ();
|
||||||
}
|
}
|
||||||
|
|
@ -1098,10 +1092,8 @@ void Bot::attackMovement () {
|
||||||
if (m_strafeSetTime < game.timebase ()) {
|
if (m_strafeSetTime < game.timebase ()) {
|
||||||
|
|
||||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||||
game.makeVectors (m_enemy->v.v_angle);
|
const auto &dirToPoint = (pev->origin - m_enemy->v.origin).normalize2d ();
|
||||||
|
const auto &rightSide = m_enemy->v.v_angle.right ().normalize2d ();
|
||||||
const Vector &dirToPoint = (pev->origin - m_enemy->v.origin).normalize2d ();
|
|
||||||
const Vector &rightSide = game.vec.right.normalize2d ();
|
|
||||||
|
|
||||||
if ((dirToPoint | rightSide) < 0) {
|
if ((dirToPoint | rightSide) < 0) {
|
||||||
m_combatStrafeDir = Dodge::Left;
|
m_combatStrafeDir = Dodge::Left;
|
||||||
|
|
@ -1148,7 +1140,7 @@ void Bot::attackMovement () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_fightStyle == Fight::Stay) {
|
else if (m_fightStyle == Fight::Stay) {
|
||||||
if ((m_visibility & (Visibility::Head | Visibility::Body)) && !(m_visibility & Visibility::Other) && getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::Hunt) {
|
if ((m_enemyParts & (Visibility::Head | Visibility::Body)) && !(m_enemyParts & Visibility::Other) && getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::Hunt) {
|
||||||
int enemyNearestIndex = graph.getNearest (m_enemy->v.origin);
|
int enemyNearestIndex = graph.getNearest (m_enemy->v.origin);
|
||||||
|
|
||||||
if (graph.isDuckVisible (m_currentNodeIndex, enemyNearestIndex) && graph.isDuckVisible (enemyNearestIndex, m_currentNodeIndex)) {
|
if (graph.isDuckVisible (m_currentNodeIndex, enemyNearestIndex) && graph.isDuckVisible (enemyNearestIndex, m_currentNodeIndex)) {
|
||||||
|
|
@ -1176,9 +1168,10 @@ void Bot::attackMovement () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) {
|
if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) {
|
||||||
game.makeVectors (pev->v_angle);
|
Vector right, forward;
|
||||||
|
pev->v_angle.buildVectors (&forward, &right, nullptr);
|
||||||
|
|
||||||
if (isDeadlyMove (pev->origin + (game.vec.forward * m_moveSpeed * 0.2f) + (game.vec.right * m_strafeSpeed * 0.2f) + (pev->velocity * getFrameInterval ()))) {
|
if (isDeadlyMove (pev->origin + (forward * m_moveSpeed * 0.2f) + (right * m_strafeSpeed * 0.2f) + (pev->velocity * getFrameInterval ()))) {
|
||||||
m_strafeSpeed = -m_strafeSpeed;
|
m_strafeSpeed = -m_strafeSpeed;
|
||||||
m_moveSpeed = -m_moveSpeed;
|
m_moveSpeed = -m_moveSpeed;
|
||||||
|
|
||||||
|
|
@ -1245,8 +1238,8 @@ bool Bot::usesRifle () {
|
||||||
if (m_currentWeapon == tab->id) {
|
if (m_currentWeapon == tab->id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++tab;
|
tab++;
|
||||||
++count;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab->id && count > 13) {
|
if (tab->id && count > 13) {
|
||||||
|
|
@ -1264,8 +1257,8 @@ bool Bot::usesPistol () {
|
||||||
if (m_currentWeapon == tab->id) {
|
if (m_currentWeapon == tab->id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++tab;
|
tab++;
|
||||||
++count;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab->id && count < 7) {
|
if (tab->id && count < 7) {
|
||||||
|
|
@ -1309,7 +1302,7 @@ int Bot::bestPrimaryCarried () {
|
||||||
if (weapons & cr::bit (weaponTab[*pref].id)) {
|
if (weapons & cr::bit (weaponTab[*pref].id)) {
|
||||||
weaponIndex = i;
|
weaponIndex = i;
|
||||||
}
|
}
|
||||||
++pref;
|
pref++;
|
||||||
}
|
}
|
||||||
return weaponIndex;
|
return weaponIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -1335,7 +1328,7 @@ int Bot::bestSecondaryCarried () {
|
||||||
weaponIndex = i;
|
weaponIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++pref;
|
pref++;
|
||||||
}
|
}
|
||||||
return weaponIndex;
|
return weaponIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -1366,7 +1359,7 @@ bool Bot::rateGroundWeapon (edict_t *ent) {
|
||||||
groundIndex = i;
|
groundIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++pref;
|
pref++;
|
||||||
}
|
}
|
||||||
int hasWeapon = 0;
|
int hasWeapon = 0;
|
||||||
|
|
||||||
|
|
@ -1405,7 +1398,7 @@ void Bot::selectBestWeapon () {
|
||||||
while (tab[selectIndex].id) {
|
while (tab[selectIndex].id) {
|
||||||
// is the bot NOT carrying this weapon?
|
// is the bot NOT carrying this weapon?
|
||||||
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
|
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
|
||||||
++selectIndex; // skip to next weapon
|
selectIndex++; // skip to next weapon
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1426,7 +1419,7 @@ void Bot::selectBestWeapon () {
|
||||||
if (ammoLeft) {
|
if (ammoLeft) {
|
||||||
chosenWeaponIndex = selectIndex;
|
chosenWeaponIndex = selectIndex;
|
||||||
}
|
}
|
||||||
++selectIndex;
|
selectIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
chosenWeaponIndex %= kNumWeapons + 1;
|
chosenWeaponIndex %= kNumWeapons + 1;
|
||||||
|
|
@ -1465,7 +1458,7 @@ int Bot::bestWeaponCarried () {
|
||||||
num = i;
|
num = i;
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
++tab;
|
tab++;
|
||||||
}
|
}
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
@ -1589,7 +1582,7 @@ void Bot::checkReload () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weapons == 0) {
|
if (weapons == 0) {
|
||||||
++m_reloadState;
|
m_reloadState++;
|
||||||
|
|
||||||
if (m_reloadState > Reload::Secondary) {
|
if (m_reloadState > Reload::Secondary) {
|
||||||
m_reloadState = Reload::None;
|
m_reloadState = Reload::None;
|
||||||
|
|
@ -1607,7 +1600,7 @@ void Bot::checkReload () {
|
||||||
|
|
||||||
if (m_ammoInClip[weaponIndex] < conf.findWeaponById (weaponIndex).maxClip * 0.8f && prop.ammo1 != -1 && prop.ammo1 < 32 && m_ammo[prop.ammo1] > 0) {
|
if (m_ammoInClip[weaponIndex] < conf.findWeaponById (weaponIndex).maxClip * 0.8f && prop.ammo1 != -1 && prop.ammo1 < 32 && m_ammo[prop.ammo1] > 0) {
|
||||||
if (m_currentWeapon != weaponIndex) {
|
if (m_currentWeapon != weaponIndex) {
|
||||||
selectWeaponByName (prop.classname);
|
selectWeaponByName (prop.classname.chars ());
|
||||||
}
|
}
|
||||||
pev->button &= ~IN_ATTACK;
|
pev->button &= ~IN_ATTACK;
|
||||||
|
|
||||||
|
|
@ -1622,7 +1615,7 @@ void Bot::checkReload () {
|
||||||
m_reloadState = Reload::None;
|
m_reloadState = Reload::None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
++m_reloadState;
|
m_reloadState++;
|
||||||
|
|
||||||
if (m_reloadState > Reload::Secondary) {
|
if (m_reloadState > Reload::Secondary) {
|
||||||
m_reloadState = Reload::None;
|
m_reloadState = Reload::None;
|
||||||
|
|
|
||||||
|
|
@ -1641,7 +1641,7 @@ void BotControl::showMenu (int id) {
|
||||||
Client &client = util.getClient (game.indexOfPlayer (m_ent));
|
Client &client = util.getClient (game.indexOfPlayer (m_ent));
|
||||||
|
|
||||||
if (id == Menu::None) {
|
if (id == Menu::None) {
|
||||||
MessageWriter (MSG_ONE_UNRELIABLE, game.getMessageId (NetMsg::ShowMenu), nullvec, m_ent)
|
MessageWriter (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullvec, m_ent)
|
||||||
.writeShort (0)
|
.writeShort (0)
|
||||||
.writeChar (0)
|
.writeChar (0)
|
||||||
.writeByte (0)
|
.writeByte (0)
|
||||||
|
|
@ -1657,7 +1657,7 @@ void BotControl::showMenu (int id) {
|
||||||
MessageWriter msg;
|
MessageWriter msg;
|
||||||
|
|
||||||
while (strlen (text) >= 64) {
|
while (strlen (text) >= 64) {
|
||||||
msg.start (MSG_ONE_UNRELIABLE, game.getMessageId (NetMsg::ShowMenu), nullvec, m_ent)
|
msg.start (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullvec, m_ent)
|
||||||
.writeShort (display.slots)
|
.writeShort (display.slots)
|
||||||
.writeChar (-1)
|
.writeChar (-1)
|
||||||
.writeByte (1);
|
.writeByte (1);
|
||||||
|
|
@ -1669,7 +1669,7 @@ void BotControl::showMenu (int id) {
|
||||||
text += 64;
|
text += 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter (MSG_ONE_UNRELIABLE, game.getMessageId (NetMsg::ShowMenu), nullvec, m_ent)
|
MessageWriter (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullvec, m_ent)
|
||||||
.writeShort (display.slots)
|
.writeShort (display.slots)
|
||||||
.writeChar (-1)
|
.writeChar (-1)
|
||||||
.writeByte (0)
|
.writeByte (0)
|
||||||
|
|
@ -1753,7 +1753,7 @@ void BotControl::maintainAdminRights () {
|
||||||
Client &client = util.getClient (i);
|
Client &client = util.getClient (i);
|
||||||
|
|
||||||
if (client.flags & ClientFlags::Admin) {
|
if (client.flags & ClientFlags::Admin) {
|
||||||
if (util.isEmptyStr (yb_password_key.str ()) && util.isEmptyStr (yb_password.str ())) {
|
if (strings.isEmpty (yb_password_key.str ()) && strings.isEmpty (yb_password.str ())) {
|
||||||
client.flags &= ~ClientFlags::Admin;
|
client.flags &= ~ClientFlags::Admin;
|
||||||
}
|
}
|
||||||
else if (!!strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast <char *> (yb_password_key.str ())))) {
|
else if (!!strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast <char *> (yb_password_key.str ())))) {
|
||||||
|
|
@ -1761,7 +1761,7 @@ void BotControl::maintainAdminRights () {
|
||||||
game.print ("Player %s had lost remote access to %s.", STRING (player->v.netname), PRODUCT_SHORT_NAME);
|
game.print ("Player %s had lost remote access to %s.", STRING (player->v.netname), PRODUCT_SHORT_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!(client.flags & ClientFlags::Admin) && !util.isEmptyStr (yb_password_key.str ()) && !util.isEmptyStr (yb_password.str ())) {
|
else if (!(client.flags & ClientFlags::Admin) && !strings.isEmpty (yb_password_key.str ()) && !strings.isEmpty (yb_password.str ())) {
|
||||||
if (strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast <char *> (yb_password_key.str ()))) == 0) {
|
if (strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast <char *> (yb_password_key.str ()))) == 0) {
|
||||||
client.flags |= ClientFlags::Admin;
|
client.flags |= ClientFlags::Admin;
|
||||||
game.print ("Player %s had gained full remote access to %s.", STRING (player->v.netname), PRODUCT_SHORT_NAME);
|
game.print ("Player %s had gained full remote access to %s.", STRING (player->v.netname), PRODUCT_SHORT_NAME);
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,6 @@ Game::Game () {
|
||||||
m_startEntity = nullptr;
|
m_startEntity = nullptr;
|
||||||
m_localEntity = nullptr;
|
m_localEntity = nullptr;
|
||||||
|
|
||||||
resetMessages ();
|
|
||||||
|
|
||||||
for (auto &msg : m_msgBlock.regMsgs) {
|
|
||||||
msg = NetMsg::None;
|
|
||||||
}
|
|
||||||
m_precached = false;
|
m_precached = false;
|
||||||
m_isBotCommand = false;
|
m_isBotCommand = false;
|
||||||
|
|
||||||
|
|
@ -35,10 +30,6 @@ Game::Game () {
|
||||||
m_cvars.clear ();
|
m_cvars.clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::~Game () {
|
|
||||||
resetMessages ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::precache () {
|
void Game::precache () {
|
||||||
if (m_precached) {
|
if (m_precached) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -387,7 +378,7 @@ uint8 *Game::getVisibilitySet (Bot *bot, bool pvs) {
|
||||||
void Game::sendClientMessage (bool console, edict_t *ent, const char *message) {
|
void Game::sendClientMessage (bool console, edict_t *ent, const char *message) {
|
||||||
// helper to sending the client message
|
// helper to sending the client message
|
||||||
|
|
||||||
MessageWriter (MSG_ONE, getMessageId (NetMsg::TextMsg), nullvec, ent)
|
MessageWriter (MSG_ONE, msgs.id (NetMsg::TextMsg), nullvec, ent)
|
||||||
.writeByte (console ? HUD_PRINTCONSOLE : HUD_PRINTCENTER)
|
.writeByte (console ? HUD_PRINTCONSOLE : HUD_PRINTCENTER)
|
||||||
.writeString (message);
|
.writeString (message);
|
||||||
}
|
}
|
||||||
|
|
@ -541,490 +532,6 @@ void Game::registerCvars (bool gameVars) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::processMessages (void *ptr) {
|
|
||||||
if (m_msgBlock.msg == NetMsg::None) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// some needed variables
|
|
||||||
static uint8 r, g, b;
|
|
||||||
static uint8 enabled;
|
|
||||||
|
|
||||||
static int damageArmor, damageTaken, damageBits;
|
|
||||||
static int killerIndex, victimIndex, playerIndex;
|
|
||||||
static int index, numPlayers;
|
|
||||||
static int state, id, clip;
|
|
||||||
|
|
||||||
static Vector damageOrigin;
|
|
||||||
static WeaponProp weaponProp;
|
|
||||||
|
|
||||||
// some widely used stuff
|
|
||||||
auto bot = bots[m_msgBlock.bot];
|
|
||||||
|
|
||||||
auto strVal = reinterpret_cast <char *> (ptr);
|
|
||||||
auto intVal = *reinterpret_cast <int *> (ptr);
|
|
||||||
auto byteVal = *reinterpret_cast <uint8 *> (ptr);
|
|
||||||
|
|
||||||
// now starts of network message execution
|
|
||||||
switch (m_msgBlock.msg) {
|
|
||||||
case NetMsg::VGUI:
|
|
||||||
// this message is sent when a VGUI menu is displayed.
|
|
||||||
|
|
||||||
if (bot != nullptr && m_msgBlock.state == 0) {
|
|
||||||
switch (intVal) {
|
|
||||||
case GuiMenu::TeamSelect:
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GuiMenu::TerroristSelect:
|
|
||||||
case GuiMenu::CTSelect:
|
|
||||||
bot->m_startAction = BotMsg::ClassSelect;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::ShowMenu:
|
|
||||||
// this message is sent when a text menu is displayed.
|
|
||||||
|
|
||||||
// ignore first 3 fields of message
|
|
||||||
if (m_msgBlock.state < 3 || bot == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (strVal, "#Team_Select") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#Team_Select_Spect") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#IG_Team_Select_Spect") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#IG_Team_Select") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#IG_VIP_Team_Select") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#IG_VIP_Team_Select_Spect") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::TeamSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#Terrorist_Select") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::ClassSelect;
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "#CT_Select") == 0) {
|
|
||||||
bot->m_startAction = BotMsg::ClassSelect;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::WeaponList:
|
|
||||||
// this message is sent when a client joins the game. All of the weapons are sent with the weapon ID and information about what ammo is used.
|
|
||||||
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
strncpy (weaponProp.classname, strVal, cr::bufsize (weaponProp.classname));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
weaponProp.ammo1 = intVal; // ammo index 1
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
weaponProp.ammo1Max = intVal; // max ammo 1
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
weaponProp.slot = intVal; // slot for this weapon
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
weaponProp.pos = intVal; // position in slot
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
|
||||||
weaponProp.id = intVal; // weapon ID
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8:
|
|
||||||
weaponProp.flags = intVal; // flags for weapon (WTF???)
|
|
||||||
conf.setWeaponProp (weaponProp); // store away this weapon with it's ammo information...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::CurWeapon:
|
|
||||||
// this message is sent when a weapon is selected (either by the bot chosing a weapon or by the server auto assigning the bot a weapon). In CS it's also called when Ammo is increased/decreased
|
|
||||||
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
state = intVal; // state of the current weapon (WTF???)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
id = intVal; // weapon ID of current weapon
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
clip = intVal; // ammo currently in the clip for this weapon
|
|
||||||
|
|
||||||
if (bot != nullptr && id <= 31) {
|
|
||||||
if (state != 0) {
|
|
||||||
bot->m_currentWeapon = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ammo amount decreased ? must have fired a bullet...
|
|
||||||
if (id == bot->m_currentWeapon && bot->m_ammoInClip[id] > clip) {
|
|
||||||
bot->m_timeLastFired = timebase (); // remember the last bullet time
|
|
||||||
}
|
|
||||||
bot->m_ammoInClip[id] = clip;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::AmmoX:
|
|
||||||
// this message is sent whenever ammo amounts are adjusted (up or down). NOTE: Logging reveals that CS uses it very unreliable!
|
|
||||||
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
index = intVal; // ammo index (for type of ammo)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (bot != nullptr) {
|
|
||||||
bot->m_ammo[index] = intVal; // store it away
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::AmmoPickup:
|
|
||||||
// this message is sent when the bot picks up some ammo (AmmoX messages are also sent so this message is probably
|
|
||||||
// not really necessary except it allows the HUD to draw pictures of ammo that have been picked up. The bots
|
|
||||||
// don't really need pictures since they don't have any eyes anyway.
|
|
||||||
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
index = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (bot != nullptr) {
|
|
||||||
bot->m_ammo[index] = intVal;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::Damage:
|
|
||||||
// this message gets sent when the bots are getting damaged.
|
|
||||||
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
damageArmor = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
damageTaken = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
damageBits = intVal;
|
|
||||||
|
|
||||||
if (bot != nullptr && (damageArmor > 0 || damageTaken > 0)) {
|
|
||||||
bot->processDamage (bot->pev->dmg_inflictor, damageTaken, damageArmor, damageBits);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::Money:
|
|
||||||
// this message gets sent when the bots money amount changes
|
|
||||||
|
|
||||||
if (bot != nullptr && m_msgBlock.state == 0) {
|
|
||||||
bot->m_moneyAmount = intVal; // amount of money
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::StatusIcon:
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
enabled = byteVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (bot != nullptr) {
|
|
||||||
if (strcmp (strVal, "buyzone") == 0) {
|
|
||||||
bot->m_inBuyZone = (enabled != 0);
|
|
||||||
|
|
||||||
// try to equip in buyzone
|
|
||||||
bot->processBuyzoneEntering (BuyState::PrimaryWeapon);
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "vipsafety") == 0) {
|
|
||||||
bot->m_inVIPZone = (enabled != 0);
|
|
||||||
}
|
|
||||||
else if (strcmp (strVal, "c4") == 0) {
|
|
||||||
bot->m_inBombZone = (enabled == 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::DeathMsg: // this message sends on death
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
killerIndex = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
victimIndex = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
if (killerIndex != 0 && killerIndex != victimIndex) {
|
|
||||||
edict_t *killer = entityOfIndex (killerIndex);
|
|
||||||
edict_t *victim = entityOfIndex (victimIndex);
|
|
||||||
|
|
||||||
if (isNullEntity (killer) || isNullEntity (victim)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yb_radio_mode.int_ () == 2) {
|
|
||||||
// need to send congrats on well placed shot
|
|
||||||
for (const auto ¬ify : bots) {
|
|
||||||
if (notify->m_notKilled && killer != notify->ent () && notify->seesEntity (victim->v.origin) && getTeam (killer) == notify->m_team && getTeam (killer) != getTeam (victim)) {
|
|
||||||
if (!bots[killer]) {
|
|
||||||
notify->processChatterMessage ("#Bot_NiceShotCommander");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
notify->processChatterMessage ("#Bot_NiceShotPall");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notice nearby to victim teammates, that attacker is near
|
|
||||||
for (const auto ¬ify : bots) {
|
|
||||||
if (notify->m_seeEnemyTime + 2.0f < timebase () && notify->m_notKilled && notify->m_team == getTeam (victim) && util.isVisible (killer->v.origin, notify->ent ()) && isNullEntity (notify->m_enemy) && getTeam (killer) != getTeam (victim)) {
|
|
||||||
notify->m_actualReactionTime = 0.0f;
|
|
||||||
notify->m_seeEnemyTime = timebase ();
|
|
||||||
notify->m_enemy = killer;
|
|
||||||
notify->m_lastEnemy = killer;
|
|
||||||
notify->m_lastEnemyOrigin = killer->v.origin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto notify = bots[killer];
|
|
||||||
|
|
||||||
// is this message about a bot who killed somebody?
|
|
||||||
if (notify != nullptr) {
|
|
||||||
notify->m_lastVictim = victim;
|
|
||||||
}
|
|
||||||
else // did a human kill a bot on his team?
|
|
||||||
{
|
|
||||||
auto target = bots[victim];
|
|
||||||
|
|
||||||
if (target != nullptr) {
|
|
||||||
if (getTeam (killer) == target->m_team) {
|
|
||||||
target->m_voteKickIndex = killerIndex;
|
|
||||||
}
|
|
||||||
target->m_notKilled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::ScreenFade: // this message gets sent when the screen fades (flashbang)
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 3:
|
|
||||||
r = byteVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
g = byteVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
b = byteVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
if (bot != nullptr && r >= 255 && g >= 255 && b >= 255 && byteVal > 170) {
|
|
||||||
bot->processBlind (byteVal);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::HLTV: // round restart in steam cs
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
numPlayers = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (numPlayers == 0 && intVal == 0) {
|
|
||||||
bots.initRound ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::TextMsg:
|
|
||||||
if (m_msgBlock.state == 1) {
|
|
||||||
if (strcmp (strVal, "#CTs_Win") == 0 ||
|
|
||||||
strcmp (strVal, "#Bomb_Defused") == 0 ||
|
|
||||||
strcmp (strVal, "#Terrorists_Win") == 0 ||
|
|
||||||
strcmp (strVal, "#Round_Draw") == 0 ||
|
|
||||||
strcmp (strVal, "#All_Hostages_Rescued") == 0 ||
|
|
||||||
strcmp (strVal, "#Target_Saved") == 0 ||
|
|
||||||
strcmp (strVal, "#Hostages_Not_Rescued") == 0 ||
|
|
||||||
strcmp (strVal, "#Terrorists_Not_Escaped") == 0 ||
|
|
||||||
strcmp (strVal, "#VIP_Not_Escaped") == 0 ||
|
|
||||||
strcmp (strVal, "#Escaping_Terrorists_Neutralized") == 0 ||
|
|
||||||
strcmp (strVal, "#VIP_Assassinated") == 0 ||
|
|
||||||
strcmp (strVal, "#VIP_Escaped") == 0 ||
|
|
||||||
strcmp (strVal, "#Terrorists_Escaped") == 0 ||
|
|
||||||
strcmp (strVal, "#CTs_PreventEscape") == 0 ||
|
|
||||||
strcmp (strVal, "#Target_Bombed") == 0 ||
|
|
||||||
strcmp (strVal, "#Game_Commencing") == 0 ||
|
|
||||||
strcmp (strVal, "#Game_will_restart_in") == 0) {
|
|
||||||
bots.setRoundOver (true);
|
|
||||||
|
|
||||||
if (strcmp (strVal, "#Game_Commencing") == 0) {
|
|
||||||
util.setNeedForWelcome (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (strVal, "#CTs_Win") == 0) {
|
|
||||||
bots.setLastWinner (Team::CT); // update last winner for economics
|
|
||||||
|
|
||||||
if (yb_radio_mode.int_ () == 2) {
|
|
||||||
Bot *notify = bots.findAliveBot ();
|
|
||||||
|
|
||||||
if (notify != nullptr && notify->m_notKilled) {
|
|
||||||
notify->processChatterMessage (strVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (strVal, "#Game_will_restart_in") == 0) {
|
|
||||||
bots.updateTeamEconomics (Team::CT, true);
|
|
||||||
bots.updateTeamEconomics (Team::Terrorist, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (strVal, "#Terrorists_Win") == 0) {
|
|
||||||
bots.setLastWinner (Team::Terrorist); // update last winner for economics
|
|
||||||
|
|
||||||
if (yb_radio_mode.int_ () == 2) {
|
|
||||||
Bot *notify = bots.findAliveBot ();
|
|
||||||
|
|
||||||
if (notify != nullptr && notify->m_notKilled) {
|
|
||||||
notify->processChatterMessage (strVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
graph.setBombPos (true);
|
|
||||||
}
|
|
||||||
else if (!bots.isBombPlanted () && strcmp (strVal, "#Bomb_Planted") == 0) {
|
|
||||||
bots.setBombPlanted (true);
|
|
||||||
|
|
||||||
for (const auto ¬ify : bots) {
|
|
||||||
if (notify->m_notKilled) {
|
|
||||||
notify->clearSearchNodes ();
|
|
||||||
notify->clearTasks ();
|
|
||||||
|
|
||||||
if (yb_radio_mode.int_ () == 2 && rg.chance (55) && notify->m_team == Team::CT) {
|
|
||||||
notify->pushChatterMessage (Chatter::WhereIsTheC4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
graph.setBombPos ();
|
|
||||||
}
|
|
||||||
else if (bot != nullptr && strcmp (strVal, "#Switch_To_BurstFire") == 0) {
|
|
||||||
bot->m_weaponBurstMode = BurstMode::On;
|
|
||||||
}
|
|
||||||
else if (bot != nullptr && strcmp (strVal, "#Switch_To_SemiAuto") == 0) {
|
|
||||||
bot->m_weaponBurstMode = BurstMode::Off;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::TeamInfo:
|
|
||||||
switch (m_msgBlock.state) {
|
|
||||||
case 0:
|
|
||||||
playerIndex = intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (playerIndex > 0 && playerIndex <= maxClients ()) {
|
|
||||||
int team = Team::Unassigned;
|
|
||||||
|
|
||||||
if (strVal[0] == 'U' && strVal[1] == 'N') {
|
|
||||||
team = Team::Unassigned;
|
|
||||||
}
|
|
||||||
else if (strVal[0] == 'T' && strVal[1] == 'E') {
|
|
||||||
team = Team::Terrorist;
|
|
||||||
}
|
|
||||||
else if (strVal[0] == 'C' && strVal[1] == 'T') {
|
|
||||||
team = Team::CT;
|
|
||||||
}
|
|
||||||
else if (strVal[0] == 'S' && strVal[1] == 'P') {
|
|
||||||
team = Team::Spectator;
|
|
||||||
}
|
|
||||||
auto &client = util.getClient (playerIndex - 1);
|
|
||||||
|
|
||||||
client.team2 = team;
|
|
||||||
client.team = is (GameFlags::FreeForAll) ? playerIndex : team;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::BarTime:
|
|
||||||
if (bot != nullptr && m_msgBlock.state == 0) {
|
|
||||||
if (intVal > 0) {
|
|
||||||
bot->m_hasProgressBar = true; // the progress bar on a hud
|
|
||||||
|
|
||||||
if (mapIs (MapFlags::Demolition) && bots.isBombPlanted () && bot->m_team == Team::CT) {
|
|
||||||
bots.notifyBombDefuse ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (intVal == 0) {
|
|
||||||
bot->m_hasProgressBar = false; // no progress bar or disappeared
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::ItemStatus:
|
|
||||||
if (bot != nullptr && m_msgBlock.state == 0) {
|
|
||||||
bot->m_hasNVG = (intVal & ItemStatus::Nightvision) ? true : false;
|
|
||||||
bot->m_hasDefuser = (intVal & ItemStatus::DefusalKit) ? true : false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::FlashBat:
|
|
||||||
if (bot != nullptr && m_msgBlock.state == 0) {
|
|
||||||
bot->m_flashLevel = static_cast <float> (intVal);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NetMsg::NVGToggle:
|
|
||||||
if (bot != nullptr && m_msgBlock.state == 0) {
|
|
||||||
bot->m_usesNVG = intVal > 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
logger.error ("Network message handler error. Call to unrecognized message id (%d).\n", m_msgBlock.msg);
|
|
||||||
}
|
|
||||||
++m_msgBlock.state; // and finally update network message state
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Game::loadCSBinary () {
|
bool Game::loadCSBinary () {
|
||||||
auto modname = getModName ();
|
auto modname = getModName ();
|
||||||
|
|
||||||
|
|
@ -1257,82 +764,6 @@ void Game::slowFrame () {
|
||||||
m_slowFrame = timebase () + 1.0f;
|
m_slowFrame = timebase () + 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::beginMessage (edict_t *ent, int dest, int type) {
|
|
||||||
// store the message type in our own variables, since the GET_USER_MSG_ID () will just do a lot of strcmp()'s...
|
|
||||||
if (is (GameFlags::Metamod) && getMessageId (NetMsg::Money) == -1) {
|
|
||||||
|
|
||||||
auto setMsgId = [&] (const char *name, NetMsg id) {
|
|
||||||
setMessageId (id, GET_USER_MSG_ID (PLID, name, nullptr));
|
|
||||||
};
|
|
||||||
setMsgId ("VGUIMenu", NetMsg::VGUI);
|
|
||||||
setMsgId ("ShowMenu", NetMsg::ShowMenu);
|
|
||||||
setMsgId ("WeaponList", NetMsg::WeaponList);
|
|
||||||
setMsgId ("CurWeapon", NetMsg::CurWeapon);
|
|
||||||
setMsgId ("AmmoX", NetMsg::AmmoX);
|
|
||||||
setMsgId ("AmmoPickup", NetMsg::AmmoPickup);
|
|
||||||
setMsgId ("Damage", NetMsg::Damage);
|
|
||||||
setMsgId ("Money", NetMsg::Money);
|
|
||||||
setMsgId ("StatusIcon", NetMsg::StatusIcon);
|
|
||||||
setMsgId ("DeathMsg", NetMsg::DeathMsg);
|
|
||||||
setMsgId ("ScreenFade", NetMsg::ScreenFade);
|
|
||||||
setMsgId ("HLTV", NetMsg::HLTV);
|
|
||||||
setMsgId ("TextMsg", NetMsg::TextMsg);
|
|
||||||
setMsgId ("TeamInfo", NetMsg::TeamInfo);
|
|
||||||
setMsgId ("BarTime", NetMsg::BarTime);
|
|
||||||
setMsgId ("SendAudio", NetMsg::SendAudio);
|
|
||||||
setMsgId ("SayText", NetMsg::SayText);
|
|
||||||
setMsgId ("FlashBat", NetMsg::FlashBat);
|
|
||||||
setMsgId ("Flashlight", NetMsg::Fashlight);
|
|
||||||
setMsgId ("NVGToggle", NetMsg::NVGToggle);
|
|
||||||
setMsgId ("ItemStatus", NetMsg::ItemStatus);
|
|
||||||
|
|
||||||
if (is (GameFlags::HasBotVoice)) {
|
|
||||||
setMessageId (NetMsg::BotVoice, GET_USER_MSG_ID (PLID, "BotVoice", nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!is (GameFlags::Legacy) || is (GameFlags::Xash3D)) && dest == MSG_SPEC && type == getMessageId (NetMsg::HLTV)) {
|
|
||||||
setCurrentMessageId (NetMsg::HLTV);
|
|
||||||
}
|
|
||||||
captureMessage (type, NetMsg::WeaponList);
|
|
||||||
|
|
||||||
if (!isNullEntity (ent) && !(ent->v.flags & FL_DORMANT)) {
|
|
||||||
auto bot = bots[ent];
|
|
||||||
|
|
||||||
// is this message for a bot?
|
|
||||||
if (bot != nullptr) {
|
|
||||||
setCurrentMessageOwner (bot->index ());
|
|
||||||
|
|
||||||
// message handling is done in usermsg.cpp
|
|
||||||
captureMessage (type, NetMsg::VGUI);
|
|
||||||
captureMessage (type, NetMsg::CurWeapon);
|
|
||||||
captureMessage (type, NetMsg::AmmoX);
|
|
||||||
captureMessage (type, NetMsg::AmmoPickup);
|
|
||||||
captureMessage (type, NetMsg::Damage);
|
|
||||||
captureMessage (type, NetMsg::Money);
|
|
||||||
captureMessage (type, NetMsg::StatusIcon);
|
|
||||||
captureMessage (type, NetMsg::ScreenFade);
|
|
||||||
captureMessage (type, NetMsg::BarTime);
|
|
||||||
captureMessage (type, NetMsg::TextMsg);
|
|
||||||
captureMessage (type, NetMsg::ShowMenu);
|
|
||||||
captureMessage (type, NetMsg::FlashBat);
|
|
||||||
captureMessage (type, NetMsg::NVGToggle);
|
|
||||||
captureMessage (type, NetMsg::ItemStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dest == MSG_ALL) {
|
|
||||||
captureMessage (type, NetMsg::TeamInfo);
|
|
||||||
captureMessage (type, NetMsg::DeathMsg);
|
|
||||||
captureMessage (type, NetMsg::TextMsg);
|
|
||||||
|
|
||||||
if (type == SVC_INTERMISSION) {
|
|
||||||
for (const auto &bot : bots) {
|
|
||||||
bot->m_notKilled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightMeasure::initializeLightstyles () {
|
void LightMeasure::initializeLightstyles () {
|
||||||
// this function initializes lighting information...
|
// this function initializes lighting information...
|
||||||
|
|
||||||
|
|
@ -1376,7 +807,7 @@ void LightMeasure::updateLight (int style, char *value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (util.isEmptyStr (value)){
|
if (strings.isEmpty (value)){
|
||||||
m_lightstyle[style].length = 0u;
|
m_lightstyle[style].length = 0u;
|
||||||
m_lightstyle[style].map[0] = '\0';
|
m_lightstyle[style].map[0] = '\0';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1037,18 +1037,18 @@ void BotGraph::calculatePathRadius (int index) {
|
||||||
|
|
||||||
for (float scanDistance = 32.0f; scanDistance < 128.0f; scanDistance += 16.0f) {
|
for (float scanDistance = 32.0f; scanDistance < 128.0f; scanDistance += 16.0f) {
|
||||||
start = path.origin;
|
start = path.origin;
|
||||||
game.makeVectors (nullvec);
|
auto null = nullvec;
|
||||||
|
|
||||||
direction = game.vec.forward * scanDistance;
|
direction = null.forward () * scanDistance;
|
||||||
direction = direction.angles ();
|
direction = direction.angles ();
|
||||||
|
|
||||||
path.radius = scanDistance;
|
path.radius = scanDistance;
|
||||||
|
|
||||||
for (float circleRadius = 0.0f; circleRadius < 360.0f; circleRadius += 20.0f) {
|
for (float circleRadius = 0.0f; circleRadius < 360.0f; circleRadius += 20.0f) {
|
||||||
game.makeVectors (direction);
|
const auto &forward = direction.forward ();
|
||||||
|
|
||||||
Vector radiusStart = start + game.vec.forward * scanDistance;
|
Vector radiusStart = start + forward * scanDistance;
|
||||||
Vector radiusEnd = start + game.vec.forward * scanDistance;
|
Vector radiusEnd = start + forward * scanDistance;
|
||||||
|
|
||||||
game.testHull (radiusStart, radiusEnd, TraceIgnore::Monsters, head_hull, nullptr, &tr);
|
game.testHull (radiusStart, radiusEnd, TraceIgnore::Monsters, head_hull, nullptr, &tr);
|
||||||
|
|
||||||
|
|
@ -1067,7 +1067,7 @@ void BotGraph::calculatePathRadius (int index) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector dropStart = start + game.vec.forward * scanDistance;
|
Vector dropStart = start + forward * scanDistance;
|
||||||
Vector dropEnd = dropStart - Vector (0.0f, 0.0f, scanDistance + 60.0f);
|
Vector dropEnd = dropStart - Vector (0.0f, 0.0f, scanDistance + 60.0f);
|
||||||
|
|
||||||
game.testHull (dropStart, dropEnd, TraceIgnore::Monsters, head_hull, nullptr, &tr);
|
game.testHull (dropStart, dropEnd, TraceIgnore::Monsters, head_hull, nullptr, &tr);
|
||||||
|
|
@ -1078,7 +1078,7 @@ void BotGraph::calculatePathRadius (int index) {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dropStart = start - game.vec.forward * scanDistance;
|
dropStart = start - forward * scanDistance;
|
||||||
dropEnd = dropStart - Vector (0.0f, 0.0f, scanDistance + 60.0f);
|
dropEnd = dropStart - Vector (0.0f, 0.0f, scanDistance + 60.0f);
|
||||||
|
|
||||||
game.testHull (dropStart, dropEnd, TraceIgnore::Monsters, head_hull, nullptr, &tr);
|
game.testHull (dropStart, dropEnd, TraceIgnore::Monsters, head_hull, nullptr, &tr);
|
||||||
|
|
@ -1680,7 +1680,7 @@ void BotGraph::saveOldFormat () {
|
||||||
|
|
||||||
const char *BotGraph::getOldFormatGraphName (bool isMemoryFile) {
|
const char *BotGraph::getOldFormatGraphName (bool isMemoryFile) {
|
||||||
static String buffer;
|
static String buffer;
|
||||||
buffer.assignf ("%s%s%s.pwf", getDataDirectory (isMemoryFile), util.isEmptyStr (yb_graph_subfolder.str ()) ? "" : yb_graph_subfolder.str (), game.getMapName ());
|
buffer.assignf ("%s%s%s.pwf", getDataDirectory (isMemoryFile), strings.isEmpty (yb_graph_subfolder.str ()) ? "" : yb_graph_subfolder.str (), game.getMapName ());
|
||||||
|
|
||||||
if (File::exists (buffer)) {
|
if (File::exists (buffer)) {
|
||||||
return buffer.chars ();
|
return buffer.chars ();
|
||||||
|
|
@ -1694,49 +1694,6 @@ float BotGraph::calculateTravelTime (float maxSpeed, const Vector &src, const Ve
|
||||||
return (origin - src).length2d () / maxSpeed;
|
return (origin - src).length2d () / maxSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BotGraph::isReachable (Bot *bot, int index) {
|
|
||||||
// this function return whether bot able to reach index node or not, depending on several factors.
|
|
||||||
|
|
||||||
if (!bot || !exists (index)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const Vector &src = bot->pev->origin;
|
|
||||||
const Vector &dst = m_paths[index].origin;
|
|
||||||
|
|
||||||
// is the destination close enough?
|
|
||||||
if ((dst - src).lengthSq () >= cr::square (320.0f)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
float ladderDist = (dst - src).length2d ();
|
|
||||||
|
|
||||||
TraceResult tr;
|
|
||||||
game.testLine (src, dst, TraceIgnore::Monsters, bot->ent (), &tr);
|
|
||||||
|
|
||||||
// if node is visible from current position (even behind head)...
|
|
||||||
if (tr.flFraction >= 1.0f) {
|
|
||||||
|
|
||||||
// it's should be not a problem to reach node inside water...
|
|
||||||
if (bot->pev->waterlevel == 2 || bot->pev->waterlevel == 3) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for ladder
|
|
||||||
bool nonLadder = !(m_paths[index].flags & NodeFlag::Ladder) || ladderDist > 16.0f;
|
|
||||||
|
|
||||||
// is dest node higher than src? (62 is max jump height)
|
|
||||||
if (nonLadder && dst.z > src.z + 62.0f) {
|
|
||||||
return false; // can't reach this one
|
|
||||||
}
|
|
||||||
|
|
||||||
// is dest node lower than src?
|
|
||||||
if (nonLadder && dst.z < src.z - 100.0f) {
|
|
||||||
return false; // can't reach this one
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) {
|
bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) {
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
|
|
||||||
|
|
@ -2135,13 +2092,9 @@ void BotGraph::frame () {
|
||||||
if (path.flags & NodeFlag::Crouch) {
|
if (path.flags & NodeFlag::Crouch) {
|
||||||
height = 18.0f;
|
height = 18.0f;
|
||||||
}
|
}
|
||||||
const Vector &source = Vector (path.origin.x, path.origin.y, path.origin.z + height); // source
|
const auto &source = Vector (path.origin.x, path.origin.y, path.origin.z + height); // source
|
||||||
|
const auto &start = path.origin + Vector (path.start.x, path.start.y, 0.0f).forward () * 500.0f; // camp start
|
||||||
game.makeVectors (Vector (path.start.x, path.start.y, 0));
|
const auto &end = path.origin + Vector (path.end.x, path.end.y, 0.0f).forward () * 500.0f; // camp end
|
||||||
const Vector &start = path.origin + game.vec.forward * 500.0f; // camp start
|
|
||||||
|
|
||||||
game.makeVectors (Vector (path.end.x, path.end.y, 0));
|
|
||||||
const Vector &end = path.origin + game.vec.forward * 500.0f; // camp end
|
|
||||||
|
|
||||||
// draw it now
|
// draw it now
|
||||||
game.drawLine (m_editor, source, start, 10, 0, Color (255, 0, 0), 200, 0, 10);
|
game.drawLine (m_editor, source, start, 10, 0, Color (255, 0, 0), 200, 0, 10);
|
||||||
|
|
|
||||||
|
|
@ -612,7 +612,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
functionTable->pfnMessageBegin = [] (int msgDest, int msgType, const float *origin, edict_t *ed) {
|
functionTable->pfnMessageBegin = [] (int msgDest, int msgType, const float *origin, edict_t *ed) {
|
||||||
// this function called each time a message is about to sent.
|
// this function called each time a message is about to sent.
|
||||||
|
|
||||||
game.beginMessage (ed, msgDest, msgType);
|
msgs.start (ed, msgDest, msgType);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -621,7 +621,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
};
|
};
|
||||||
|
|
||||||
functionTable->pfnMessageEnd = [] () {
|
functionTable->pfnMessageEnd = [] () {
|
||||||
game.resetMessages ();
|
msgs.stop ();
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -631,7 +631,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteByte = [] (int value) {
|
functionTable->pfnWriteByte = [] (int value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -641,7 +641,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteChar = [] (int value) {
|
functionTable->pfnWriteChar = [] (int value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -651,7 +651,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteShort = [] (int value) {
|
functionTable->pfnWriteShort = [] (int value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -661,7 +661,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteLong = [] (int value) {
|
functionTable->pfnWriteLong = [] (int value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -671,7 +671,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteAngle = [] (float value) {
|
functionTable->pfnWriteAngle = [] (float value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -681,7 +681,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteCoord = [] (float value) {
|
functionTable->pfnWriteCoord = [] (float value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -691,7 +691,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteString = [] (const char *sz) {
|
functionTable->pfnWriteString = [] (const char *sz) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (const_cast <char *> (sz)));
|
msgs.collect (sz);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -701,7 +701,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
|
|
||||||
functionTable->pfnWriteEntity = [] (int value) {
|
functionTable->pfnWriteEntity = [] (int value) {
|
||||||
// if this message is for a bot, call the client message function...
|
// if this message is for a bot, call the client message function...
|
||||||
game.processMessages (reinterpret_cast <void *> (&value));
|
msgs.collect (value);
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
RETURN_META (MRES_IGNORED);
|
RETURN_META (MRES_IGNORED);
|
||||||
|
|
@ -720,76 +720,13 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
||||||
// using pfnMessageBegin (), it will know what message ID number to send, and the engine will
|
// using pfnMessageBegin (), it will know what message ID number to send, and the engine will
|
||||||
// know what to do, only for non-metamod version
|
// know what to do, only for non-metamod version
|
||||||
|
|
||||||
if (game.is (GameFlags::Metamod)) {
|
|
||||||
RETURN_META_VALUE (MRES_IGNORED, 0);
|
|
||||||
}
|
|
||||||
int message = engfuncs.pfnRegUserMsg (name, size);
|
int message = engfuncs.pfnRegUserMsg (name, size);
|
||||||
|
|
||||||
if (strcmp (name, "VGUIMenu") == 0) {
|
// register message for our needs
|
||||||
game.setMessageId (NetMsg::VGUI, message);
|
msgs.registerMessage (name, message);
|
||||||
}
|
|
||||||
else if (strcmp (name, "ShowMenu") == 0) {
|
if (game.is (GameFlags::Metamod)) {
|
||||||
game.setMessageId (NetMsg::ShowMenu, message);
|
RETURN_META_VALUE (MRES_SUPERCEDE, message);
|
||||||
}
|
|
||||||
else if (strcmp (name, "WeaponList") == 0) {
|
|
||||||
game.setMessageId (NetMsg::WeaponList, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "CurWeapon") == 0) {
|
|
||||||
game.setMessageId (NetMsg::CurWeapon, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "AmmoX") == 0) {
|
|
||||||
game.setMessageId (NetMsg::AmmoX, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "AmmoPickup") == 0) {
|
|
||||||
game.setMessageId (NetMsg::AmmoPickup, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "Damage") == 0) {
|
|
||||||
game.setMessageId (NetMsg::Damage, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "Money") == 0) {
|
|
||||||
game.setMessageId (NetMsg::Money, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "StatusIcon") == 0) {
|
|
||||||
game.setMessageId (NetMsg::StatusIcon, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "DeathMsg") == 0) {
|
|
||||||
game.setMessageId (NetMsg::DeathMsg, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "ScreenFade") == 0) {
|
|
||||||
game.setMessageId (NetMsg::ScreenFade, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "HLTV") == 0) {
|
|
||||||
game.setMessageId (NetMsg::HLTV, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "TextMsg") == 0) {
|
|
||||||
game.setMessageId (NetMsg::TextMsg, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "TeamInfo") == 0) {
|
|
||||||
game.setMessageId (NetMsg::TeamInfo, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "BarTime") == 0) {
|
|
||||||
game.setMessageId (NetMsg::BarTime, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "SendAudio") == 0) {
|
|
||||||
game.setMessageId (NetMsg::SendAudio, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "SayText") == 0) {
|
|
||||||
game.setMessageId (NetMsg::SayText, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "BotVoice") == 0) {
|
|
||||||
game.setMessageId (NetMsg::BotVoice, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "NVGToggle") == 0) {
|
|
||||||
game.setMessageId (NetMsg::NVGToggle, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "FlashBat") == 0) {
|
|
||||||
game.setMessageId (NetMsg::FlashBat, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "Flashlight") == 0) {
|
|
||||||
game.setMessageId (NetMsg::Fashlight, message);
|
|
||||||
}
|
|
||||||
else if (strcmp (name, "ItemStatus") == 0) {
|
|
||||||
game.setMessageId (NetMsg::ItemStatus, message);
|
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -98,11 +98,11 @@ void BotManager::touchKillerEntity (Bot *bot) {
|
||||||
}
|
}
|
||||||
const auto &prop = conf.getWeaponProp (bot->m_currentWeapon);
|
const auto &prop = conf.getWeaponProp (bot->m_currentWeapon);
|
||||||
|
|
||||||
m_killerEntity->v.classname = MAKE_STRING (prop.classname);
|
m_killerEntity->v.classname = MAKE_STRING (prop.classname.chars ());
|
||||||
m_killerEntity->v.dmg_inflictor = bot->ent ();
|
m_killerEntity->v.dmg_inflictor = bot->ent ();
|
||||||
|
|
||||||
KeyValueData kv;
|
KeyValueData kv;
|
||||||
kv.szClassName = const_cast <char *> (prop.classname);
|
kv.szClassName = const_cast <char *> (prop.classname.chars ());
|
||||||
kv.szKeyName = "damagetype";
|
kv.szKeyName = "damagetype";
|
||||||
kv.szValue = const_cast <char *> (strings.format ("%d", cr::bit (4)));
|
kv.szValue = const_cast <char *> (strings.format ("%d", cr::bit (4)));
|
||||||
kv.fHandled = FALSE;
|
kv.fHandled = FALSE;
|
||||||
|
|
@ -191,7 +191,7 @@ BotCreateResult BotManager::create (const String &name, int difficulty, int pers
|
||||||
resultName = name;
|
resultName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!util.isEmptyStr (yb_name_prefix.str ())) {
|
if (!strings.isEmpty (yb_name_prefix.str ())) {
|
||||||
String prefixed; // temp buffer for storing modified name
|
String prefixed; // temp buffer for storing modified name
|
||||||
prefixed.assignf ("%s %s", yb_name_prefix.str (), resultName.chars ());
|
prefixed.assignf ("%s %s", yb_name_prefix.str (), resultName.chars ());
|
||||||
|
|
||||||
|
|
@ -664,10 +664,10 @@ void BotManager::setWeaponMode (int selection) {
|
||||||
void BotManager::listBots () {
|
void BotManager::listBots () {
|
||||||
// this function list's bots currently playing on the server
|
// this function list's bots currently playing on the server
|
||||||
|
|
||||||
ctrl.msg ("%-3.5s\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.4s\t%-3.4s", "index", "name", "personality", "team", "difficulty", "frags");
|
ctrl.msg ("%-3.5s\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.4s\t%-3.4s\t%-3.5s", "index", "name", "personality", "team", "difficulty", "frags", "alive");
|
||||||
|
|
||||||
for (const auto &bot : bots) {;
|
for (const auto &bot : bots) {;
|
||||||
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d", bot->index (), STRING (bot->pev->netname), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags));
|
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s", bot->index (), STRING (bot->pev->netname), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no");
|
||||||
}
|
}
|
||||||
ctrl.msg ("%d bots", m_bots.length ());
|
ctrl.msg ("%d bots", m_bots.length ());
|
||||||
}
|
}
|
||||||
|
|
@ -825,7 +825,7 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) {
|
||||||
char reject[256] = {0, };
|
char reject[256] = {0, };
|
||||||
MDLL_ClientConnect (bot, STRING (bot->v.netname), strings.format ("127.0.0.%d", clientIndex + 100), reject);
|
MDLL_ClientConnect (bot, STRING (bot->v.netname), strings.format ("127.0.0.%d", clientIndex + 100), reject);
|
||||||
|
|
||||||
if (!util.isEmptyStr (reject)) {
|
if (!strings.isEmpty (reject)) {
|
||||||
logger.error ("Server refused '%s' connection (%s)", STRING (bot->v.netname), reject);
|
logger.error ("Server refused '%s' connection (%s)", STRING (bot->v.netname), reject);
|
||||||
game.serverCommand ("kick \"%s\"", STRING (bot->v.netname)); // kick the bot player if the server refused it
|
game.serverCommand ("kick \"%s\"", STRING (bot->v.netname)); // kick the bot player if the server refused it
|
||||||
|
|
||||||
|
|
@ -958,14 +958,71 @@ bool BotManager::isTeamStacked (int team) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotManager::erase (Bot *bot) {
|
void BotManager::erase (Bot *bot) {
|
||||||
for (const auto &e : m_bots) {
|
for (auto &e : m_bots) {
|
||||||
if (e.get () == bot) {
|
if (e.get () == bot) {
|
||||||
|
e.reset ();
|
||||||
m_bots.remove (e); // remove from bots array
|
m_bots.remove (e); // remove from bots array
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BotManager::handleDeath (edict_t *killer, edict_t *victim) {
|
||||||
|
auto killerTeam = game.getTeam (killer);
|
||||||
|
auto victimTeam = game.getTeam (victim);
|
||||||
|
|
||||||
|
if (yb_radio_mode.int_ () == 2) {
|
||||||
|
// need to send congrats on well placed shot
|
||||||
|
for (const auto ¬ify : bots) {
|
||||||
|
if (notify->m_notKilled && killerTeam == notify->m_team && killerTeam != victimTeam && killer != notify->ent () && notify->seesEntity (victim->v.origin)) {
|
||||||
|
if (!(killer->v.flags & FL_FAKECLIENT)) {
|
||||||
|
notify->processChatterMessage ("#Bot_NiceShotCommander");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
notify->processChatterMessage ("#Bot_NiceShotPall");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Bot *killerBot = nullptr;
|
||||||
|
Bot *victimBot = nullptr;
|
||||||
|
|
||||||
|
// notice nearby to victim teammates, that attacker is near
|
||||||
|
for (const auto ¬ify : bots) {
|
||||||
|
if (notify->m_seeEnemyTime + 2.0f < game.timebase () && notify->m_notKilled && notify->m_team == victimTeam && game.isNullEntity (notify->m_enemy) && killerTeam != victimTeam && util.isVisible (killer->v.origin, notify->ent ())) {
|
||||||
|
notify->m_actualReactionTime = 0.0f;
|
||||||
|
notify->m_seeEnemyTime = game.timebase ();
|
||||||
|
notify->m_enemy = killer;
|
||||||
|
notify->m_lastEnemy = killer;
|
||||||
|
notify->m_lastEnemyOrigin = killer->v.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify->ent () == killer) {
|
||||||
|
killerBot = notify.get ();
|
||||||
|
}
|
||||||
|
else if (notify->ent () == victim) {
|
||||||
|
victimBot = notify.get ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is this message about a bot who killed somebody?
|
||||||
|
if (killerBot != nullptr) {
|
||||||
|
killerBot->m_lastVictim = victim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// did a human kill a bot on his team?
|
||||||
|
else {
|
||||||
|
if (victimBot != nullptr) {
|
||||||
|
if (killerTeam == victimBot->m_team) {
|
||||||
|
victimBot->m_voteKickIndex = game.indexOfEntity (killer);
|
||||||
|
}
|
||||||
|
victimBot->m_notKilled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Bot::newRound () {
|
void Bot::newRound () {
|
||||||
// this function initializes a bot after creation & at the start of each round
|
// this function initializes a bot after creation & at the start of each round
|
||||||
|
|
||||||
|
|
@ -1200,7 +1257,7 @@ void Bot::kick () {
|
||||||
// this function kick off one bot from the server.
|
// this function kick off one bot from the server.
|
||||||
auto username = STRING (pev->netname);
|
auto username = STRING (pev->netname);
|
||||||
|
|
||||||
if (!(pev->flags & FL_FAKECLIENT) || util.isEmptyStr (username)) {
|
if (!(pev->flags & FL_FAKECLIENT) || strings.isEmpty (username)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// clear fakeclient bit
|
// clear fakeclient bit
|
||||||
|
|
@ -1291,7 +1348,7 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
||||||
Bot *bot = nullptr;
|
Bot *bot = nullptr;
|
||||||
|
|
||||||
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), ent, 300.0f, true, true, true)) {
|
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), ent, 300.0f, true, true, true)) {
|
||||||
bot->dropWeaponForUser (ent, util.isEmptyStr (strstr (arg, "c4")) ? false : true);
|
bot->dropWeaponForUser (ent, strings.isEmpty (strstr (arg, "c4")) ? false : true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1312,7 +1369,7 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
||||||
if (target != nullptr) {
|
if (target != nullptr) {
|
||||||
target->m_sayTextBuffer.entityIndex = game.indexOfPlayer (ent);
|
target->m_sayTextBuffer.entityIndex = game.indexOfPlayer (ent);
|
||||||
|
|
||||||
if (util.isEmptyStr (engfuncs.pfnCmd_Args ())) {
|
if (strings.isEmpty (engfuncs.pfnCmd_Args ())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
target->m_sayTextBuffer.sayText = engfuncs.pfnCmd_Args ();
|
target->m_sayTextBuffer.sayText = engfuncs.pfnCmd_Args ();
|
||||||
|
|
|
||||||
495
source/message.cpp
Normal file
495
source/message.cpp
Normal file
|
|
@ -0,0 +1,495 @@
|
||||||
|
//
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgTextMsg () {
|
||||||
|
enum args { msg = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bots chatter notification
|
||||||
|
const auto dispatchChatterMessage = [&] () -> void {
|
||||||
|
if (yb_radio_mode.int_ () == 2) {
|
||||||
|
auto notify = bots.findAliveBot ();
|
||||||
|
|
||||||
|
if (notify && notify->m_notKilled) {
|
||||||
|
notify->processChatterMessage (m_args[msg].chars_);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// lookup cached message
|
||||||
|
auto cached = m_textMsgCache[m_args[msg].chars_];
|
||||||
|
|
||||||
|
// check if we're need to handle message
|
||||||
|
if (!(cached & TextMsgCache::NeedHandle)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset bomb position
|
||||||
|
if (game.mapIs (MapFlags::Demolition)) {
|
||||||
|
graph.setBombPos (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cached & TextMsgCache::Commencing) {
|
||||||
|
util.setNeedForWelcome (true);
|
||||||
|
}
|
||||||
|
else if (cached & TextMsgCache::CounterWin) {
|
||||||
|
bots.setLastWinner (Team::CT); // update last winner for economics
|
||||||
|
dispatchChatterMessage ();
|
||||||
|
}
|
||||||
|
else if (cached & TextMsgCache::RestartRound) {
|
||||||
|
bots.updateTeamEconomics (Team::CT, true);
|
||||||
|
bots.updateTeamEconomics (Team::Terrorist, true);
|
||||||
|
}
|
||||||
|
else if (cached & TextMsgCache::TerroristWin) {
|
||||||
|
bots.setLastWinner (Team::Terrorist); // update last winner for economics
|
||||||
|
dispatchChatterMessage ();
|
||||||
|
}
|
||||||
|
else if ((cached & TextMsgCache::BombPlanted) && !bots.isBombPlanted ()) {
|
||||||
|
bots.setBombPlanted (true);
|
||||||
|
|
||||||
|
for (const auto ¬ify : bots) {
|
||||||
|
if (notify->m_notKilled) {
|
||||||
|
notify->clearSearchNodes ();
|
||||||
|
notify->clearTasks ();
|
||||||
|
|
||||||
|
if (yb_radio_mode.int_ () == 2 && rg.chance (55) && notify->m_team == Team::CT) {
|
||||||
|
notify->pushChatterMessage (Chatter::WhereIsTheC4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph.setBombPos ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for burst fire message
|
||||||
|
if (m_bot) {
|
||||||
|
if (cached & TextMsgCache::BurstOn) {
|
||||||
|
m_bot->m_weaponBurstMode = BurstMode::On;
|
||||||
|
}
|
||||||
|
else if (cached & TextMsgCache::BurstOff) {
|
||||||
|
m_bot->m_weaponBurstMode = BurstMode::Off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgVGUIMenu () {
|
||||||
|
// this message is sent when a VGUI menu is displayed.
|
||||||
|
|
||||||
|
enum args { menu = 0, min = 1 };
|
||||||
|
|
||||||
|
// check the minimum states or existance of bot
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_args[menu].long_) {
|
||||||
|
case GuiMenu::TeamSelect:
|
||||||
|
m_bot->m_startAction = BotMsg::TeamSelect;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GuiMenu::TerroristSelect:
|
||||||
|
case GuiMenu::CTSelect:
|
||||||
|
m_bot->m_startAction = BotMsg::ClassSelect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgShowMenu () {
|
||||||
|
// this message is sent when a text menu is displayed.
|
||||||
|
|
||||||
|
enum args { menu = 3, min = 4 };
|
||||||
|
|
||||||
|
// check the minimum states or existance of bot
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bot->m_startAction = m_showMenuCache[m_args[menu].chars_];
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgWeaponList () {
|
||||||
|
// this message is sent when a client joins the game. All of the weapons are sent with the weapon ID and information about what ammo is used.
|
||||||
|
|
||||||
|
enum args { classname = 0, ammo_index_1 = 1, max_ammo_1 = 2, slot = 5, slot_pos = 6, id = 7, flags = 8, min = 9 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// register prop
|
||||||
|
WeaponProp prop {
|
||||||
|
m_args[classname].chars_,
|
||||||
|
m_args[ammo_index_1].long_,
|
||||||
|
m_args[max_ammo_1].long_,
|
||||||
|
m_args[slot].long_,
|
||||||
|
m_args[slot_pos].long_,
|
||||||
|
m_args[id].long_,
|
||||||
|
m_args[flags].long_
|
||||||
|
};
|
||||||
|
conf.setWeaponProp (cr::move (prop)); // store away this weapon with it's ammo information...
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgCurWeapon () {
|
||||||
|
// this message is sent when a weapon is selected (either by the bot chosing a weapon or by the server auto assigning the bot a weapon). In CS it's also called when Ammo is increased/decreased
|
||||||
|
|
||||||
|
enum args { state = 0, id = 1, clip = 2, min = 3 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_args[id].long_ < kMaxWeapons) {
|
||||||
|
if (m_args[state].long_ != 0) {
|
||||||
|
m_bot->m_currentWeapon = m_args[id].long_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ammo amount decreased ? must have fired a bullet...
|
||||||
|
if (m_args[id].long_ == m_bot->m_currentWeapon && m_bot->m_ammoInClip[m_args[id].long_] > m_args[clip].long_) {
|
||||||
|
m_bot->m_timeLastFired = game.timebase (); // remember the last bullet time
|
||||||
|
}
|
||||||
|
m_bot->m_ammoInClip[m_args[id].long_] = m_args[clip].long_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgAmmoX () {
|
||||||
|
// this message is sent whenever ammo amounts are adjusted (up or down). NOTE: Logging reveals that CS uses it very unreliable!
|
||||||
|
|
||||||
|
enum args { index = 0, value = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bot->m_ammo[m_args[index].long_] = m_args[value].long_; // store it away
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgAmmoPickup () {
|
||||||
|
// this message is sent when the bot picks up some ammo (AmmoX messages are also sent so this message is probably
|
||||||
|
// not really necessary except it allows the HUD to draw pictures of ammo that have been picked up. The bots
|
||||||
|
// don't really need pictures since they don't have any eyes anyway.
|
||||||
|
|
||||||
|
enum args { index = 0, value = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bot->m_ammo[m_args[index].long_] = m_args[value].long_; // store it away
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgDamage () {
|
||||||
|
// this message gets sent when the bots are getting damaged.
|
||||||
|
|
||||||
|
enum args { armor = 0, health = 1, bits = 2, min = 3 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle damage if any
|
||||||
|
if (m_args[armor].long_ > 0 || m_args[health].long_) {
|
||||||
|
m_bot->processDamage (m_bot->pev->dmg_inflictor, m_args[health].long_, m_args[armor].long_, m_args[bits].long_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgMoney () {
|
||||||
|
// this message gets sent when the bots money amount changes
|
||||||
|
|
||||||
|
enum args { money = 0, min = 1 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bot->m_moneyAmount = m_args[money].long_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgStatusIcon () {
|
||||||
|
enum args { enabled = 0, icon = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// lookup cached icon
|
||||||
|
auto cached = m_statusIconCache[m_args[icon].chars_];
|
||||||
|
|
||||||
|
// check if we're need to handle message
|
||||||
|
if (!(cached & TextMsgCache::NeedHandle)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle cases
|
||||||
|
if (cached & StatusIconCache::BuyZone) {
|
||||||
|
m_bot->m_inBuyZone = (m_args[enabled].long_ != 0);
|
||||||
|
|
||||||
|
// try to equip in buyzone
|
||||||
|
m_bot->processBuyzoneEntering (BuyState::PrimaryWeapon);
|
||||||
|
}
|
||||||
|
else if (cached & StatusIconCache::VipSafety) {
|
||||||
|
m_bot->m_inVIPZone = (m_args[enabled].long_ != 0);
|
||||||
|
}
|
||||||
|
else if (cached & StatusIconCache::C4) {
|
||||||
|
m_bot->m_inBombZone = (m_args[enabled].long_ == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgDeathMsg () {
|
||||||
|
// this message gets sent when player kills player
|
||||||
|
|
||||||
|
enum args { killer = 0, victim = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto killerEntity = game.entityOfIndex (m_args[killer].long_);
|
||||||
|
auto victimEntity = game.entityOfIndex (m_args[victim].long_);
|
||||||
|
|
||||||
|
if (game.isNullEntity (killerEntity) || game.isNullEntity (victimEntity) || victimEntity == killerEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bots.handleDeath (killerEntity, victimEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgScreenFade () {
|
||||||
|
// this message gets sent when the screen fades (flashbang)
|
||||||
|
|
||||||
|
enum args { r = 3, g = 4, b = 5, alpha = 6, min = 7 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen completely faded ?
|
||||||
|
if (m_args[r].long_ >= 255 && m_args[g].long_ >= 255 && m_args[b].long_ >= 255 && m_args[alpha].long_ > 170) {
|
||||||
|
m_bot->processBlind (m_args[alpha].long_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgHLTV () {
|
||||||
|
// this message gets sent when new round is started in modern cs versions
|
||||||
|
|
||||||
|
enum args { players = 0, fov = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to start new round ? (we're tracking FOV reset message)
|
||||||
|
if (m_args[players].long_ == 0 && m_args[fov].long_ == 0) {
|
||||||
|
bots.initRound ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgTeamInfo () {
|
||||||
|
// this message gets sent when player team index is changed
|
||||||
|
|
||||||
|
enum args { index = 0, team = 1, min = 2 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &client = util.getClient (m_args[index].long_ - 1);
|
||||||
|
|
||||||
|
// update player team
|
||||||
|
client.team2 = m_teamInfoCache[m_args[team].chars_]; // update real team
|
||||||
|
client.team = game.is (GameFlags::FreeForAll) ? m_args[index].long_ : client.team2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgBarTime () {
|
||||||
|
enum args { enabled = 0, min = 1 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if has progress bar
|
||||||
|
if (m_args[enabled].long_ > 0) {
|
||||||
|
m_bot->m_hasProgressBar = true; // the progress bar on a hud
|
||||||
|
|
||||||
|
// notify bots about defusing has started
|
||||||
|
if (game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_bot->m_team == Team::CT) {
|
||||||
|
bots.notifyBombDefuse ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_bot->m_hasProgressBar = false; // no progress bar or disappeared
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgItemStatus () {
|
||||||
|
enum args { value = 0, min = 1 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto mask = m_args[value].long_;
|
||||||
|
|
||||||
|
m_bot->m_hasNVG = !!(mask & ItemStatus::Nightvision);
|
||||||
|
m_bot->m_hasDefuser = !!(mask & ItemStatus::DefusalKit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgNVGToggle () {
|
||||||
|
enum args { value = 0, min = 1 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bot->m_usesNVG = m_args[value].long_ > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::netMsgFlashBat () {
|
||||||
|
enum args { value = 0, min = 1 };
|
||||||
|
|
||||||
|
// check the minimum states
|
||||||
|
if (m_args.length () < min || !m_bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bot->m_flashLevel = m_args[value].float_;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDispatcher::MessageDispatcher () {
|
||||||
|
|
||||||
|
// register wanted message
|
||||||
|
auto pushWanted = [&] (const String &name, NetMsg id, MsgFunc handler) -> void {
|
||||||
|
m_wanted[name] = id;
|
||||||
|
m_handlers[id] = handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
// we want to handle next messages
|
||||||
|
pushWanted ("TextMsg", NetMsg::TextMsg, &MessageDispatcher::netMsgTextMsg);
|
||||||
|
pushWanted ("VGUIMenu", NetMsg::VGUIMenu, &MessageDispatcher::netMsgVGUIMenu);
|
||||||
|
pushWanted ("ShowMenu", NetMsg::ShowMenu, &MessageDispatcher::netMsgShowMenu);
|
||||||
|
pushWanted ("WeaponList", NetMsg::WeaponList, &MessageDispatcher::netMsgWeaponList);
|
||||||
|
pushWanted ("CurWeapon", NetMsg::CurWeapon, &MessageDispatcher::netMsgCurWeapon);
|
||||||
|
pushWanted ("AmmoX", NetMsg::AmmoX, &MessageDispatcher::netMsgAmmoX);
|
||||||
|
pushWanted ("AmmoPickup", NetMsg::AmmoPickup, &MessageDispatcher::netMsgAmmoPickup);
|
||||||
|
pushWanted ("Damage", NetMsg::Damage, &MessageDispatcher::netMsgDamage);
|
||||||
|
pushWanted ("Money", NetMsg::Money, &MessageDispatcher::netMsgMoney);
|
||||||
|
pushWanted ("StatusIcon", NetMsg::StatusIcon, &MessageDispatcher::netMsgStatusIcon);
|
||||||
|
pushWanted ("DeathMsg", NetMsg::DeathMsg, &MessageDispatcher::netMsgDeathMsg);
|
||||||
|
pushWanted ("ScreenFade", NetMsg::ScreenFade, &MessageDispatcher::netMsgScreenFade);
|
||||||
|
pushWanted ("HLTV", NetMsg::HLTV, &MessageDispatcher::netMsgHLTV);
|
||||||
|
pushWanted ("TeamInfo", NetMsg::TeamInfo, &MessageDispatcher::netMsgTeamInfo);
|
||||||
|
pushWanted ("BarTime", NetMsg::BarTime, &MessageDispatcher::netMsgBarTime);
|
||||||
|
pushWanted ("ItemStatus", NetMsg::ItemStatus, &MessageDispatcher::netMsgItemStatus);
|
||||||
|
pushWanted ("NVGToggle", NetMsg::NVGToggle, &MessageDispatcher::netMsgNVGToggle);
|
||||||
|
pushWanted ("FlashBat", NetMsg::FlashBat, &MessageDispatcher::netMsgFlashBat);
|
||||||
|
|
||||||
|
// we're need next messages IDs but we're won't handle them, so they will be removed from wanted list as soon as they get engine IDs
|
||||||
|
pushWanted ("BotVoice", NetMsg::BotVoice, nullptr);
|
||||||
|
pushWanted ("SendAudio", NetMsg::SendAudio, nullptr);
|
||||||
|
|
||||||
|
// register text msg cache
|
||||||
|
m_textMsgCache["#CTs_Win"] = TextMsgCache::NeedHandle | TextMsgCache::CounterWin;
|
||||||
|
m_textMsgCache["#Bomb_Defused"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Bomb_Planted"] = TextMsgCache::NeedHandle | TextMsgCache::BombPlanted;
|
||||||
|
m_textMsgCache["#Terrorists_Win"] = TextMsgCache::NeedHandle | TextMsgCache::TerroristWin;
|
||||||
|
m_textMsgCache["#Round_Draw"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#All_Hostages_Rescued"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Target_Saved"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Hostages_Not_Rescued"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Terrorists_Not_Escaped"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#VIP_Not_Escaped"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Escaping_Terrorists_Neutralized"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#VIP_Assassinated"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#VIP_Escaped"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Terrorists_Escaped"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#CTs_PreventEscape"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Target_Bombed"] = TextMsgCache::NeedHandle;
|
||||||
|
m_textMsgCache["#Game_Commencing"] = TextMsgCache::NeedHandle | TextMsgCache::Commencing;
|
||||||
|
m_textMsgCache["#Game_will_restart_in"] = TextMsgCache::NeedHandle | TextMsgCache::RestartRound;
|
||||||
|
m_textMsgCache["#Switch_To_BurstFire"] = TextMsgCache::NeedHandle | TextMsgCache::BurstOn;
|
||||||
|
m_textMsgCache["#Switch_To_SemiAuto"] = TextMsgCache::NeedHandle | TextMsgCache::BurstOff;
|
||||||
|
|
||||||
|
// register show menu cache
|
||||||
|
m_showMenuCache["#Team_Select"] = BotMsg::TeamSelect;
|
||||||
|
m_showMenuCache["#Team_Select_Spect"] = BotMsg::TeamSelect;
|
||||||
|
m_showMenuCache["#IG_Team_Select_Spect"] = BotMsg::TeamSelect;
|
||||||
|
m_showMenuCache["#IG_Team_Select"] = BotMsg::TeamSelect;
|
||||||
|
m_showMenuCache["#IG_VIP_Team_Select"] = BotMsg::TeamSelect;
|
||||||
|
m_showMenuCache["#IG_VIP_Team_Select_Spect"] = BotMsg::TeamSelect;
|
||||||
|
m_showMenuCache["#Terrorist_Select"] = BotMsg::ClassSelect;
|
||||||
|
m_showMenuCache["#CT_Select"] = BotMsg::ClassSelect;
|
||||||
|
|
||||||
|
// register status icon cache
|
||||||
|
m_statusIconCache["buyzone"] = StatusIconCache::NeedHandle | StatusIconCache::BuyZone;
|
||||||
|
m_statusIconCache["vipsafety"] = StatusIconCache::NeedHandle | StatusIconCache::VipSafety;
|
||||||
|
m_statusIconCache["c4"] = StatusIconCache::NeedHandle | StatusIconCache::C4;
|
||||||
|
|
||||||
|
// register team info cache
|
||||||
|
m_teamInfoCache["TERRORIST"] = Team::Terrorist;
|
||||||
|
m_teamInfoCache["UNASSIGNED"] = Team::Unassigned;
|
||||||
|
m_teamInfoCache["SPECTATOR"] = Team::Spectator;
|
||||||
|
m_teamInfoCache["CT"] = Team::CT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::registerMessage (const String &name, int32 id) {
|
||||||
|
if (!m_wanted.exists (name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_maps[m_wanted[name]] = id; // add message from engine RegUserMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::start (edict_t *ent, int32 dest, int32 type) {
|
||||||
|
m_current = NetMsg::None;
|
||||||
|
|
||||||
|
// search if we need to handle this message
|
||||||
|
for (const auto &msg : m_maps) {
|
||||||
|
if (msg.value == type && m_handlers[msg.key]) {
|
||||||
|
m_current = msg.key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no messagem no processing
|
||||||
|
if (m_current == NetMsg::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// broadcast message ?
|
||||||
|
if (dest == MSG_ALL || dest == MSG_SPEC || dest == MSG_BROADCAST) {
|
||||||
|
m_broadcast = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// message for bot bot?
|
||||||
|
if (ent && (ent->v.flags & FL_FAKECLIENT)) {
|
||||||
|
m_bot = bots[ent];
|
||||||
|
|
||||||
|
if (!m_bot) {
|
||||||
|
m_current = NetMsg::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_args.clear (); // clear previous args
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDispatcher::stop () {
|
||||||
|
if (m_current == NetMsg::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(this->*m_handlers[m_current]) ();
|
||||||
|
m_current = NetMsg::None;
|
||||||
|
}
|
||||||
|
|
@ -322,6 +322,11 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
||||||
|
|
||||||
if (toProj > c) {
|
if (toProj > c) {
|
||||||
m_moveSpeed = -pev->maxspeed;
|
m_moveSpeed = -pev->maxspeed;
|
||||||
|
|
||||||
|
if (m_avoid->v.button & IN_DUCK) {
|
||||||
|
m_moveSpeed = pev->maxspeed;
|
||||||
|
pev->button |= IN_JUMP;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (toProj < -c) {
|
else if (toProj < -c) {
|
||||||
|
|
@ -331,12 +336,12 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
||||||
|
|
||||||
if (latProj >= c) {
|
if (latProj >= c) {
|
||||||
pev->button |= IN_MOVELEFT;
|
pev->button |= IN_MOVELEFT;
|
||||||
setStrafeSpeed (normal, pev->maxspeed);
|
setStrafeSpeed (normal, -pev->maxspeed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (latProj <= -c) {
|
else if (latProj <= -c) {
|
||||||
pev->button |= IN_MOVERIGHT;
|
pev->button |= IN_MOVERIGHT;
|
||||||
setStrafeSpeed (normal, -pev->maxspeed);
|
setStrafeSpeed (normal, pev->maxspeed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -349,7 +354,11 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
||||||
|
|
||||||
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
m_isStuck = false;
|
m_isStuck = false;
|
||||||
|
|
||||||
|
// if avoiding someone do not consider stuck
|
||||||
|
if (doPlayerAvoidance (dirNormal)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
|
|
||||||
// Standing still, no need to check?
|
// Standing still, no need to check?
|
||||||
|
|
@ -429,10 +438,11 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
state[i + 1] = 0;
|
state[i + 1] = 0;
|
||||||
|
|
||||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||||
game.makeVectors (m_moveAngles);
|
Vector right, forward;
|
||||||
|
m_moveAngles.buildVectors (&forward, &right, nullptr);
|
||||||
|
|
||||||
Vector dirToPoint = (pev->origin - m_destOrigin).normalize2d ();
|
Vector dirToPoint = (pev->origin - m_destOrigin).normalize2d ();
|
||||||
Vector rightSide = game.vec.right.normalize2d ();
|
Vector rightSide = right.normalize2d ();
|
||||||
|
|
||||||
bool dirRight = false;
|
bool dirRight = false;
|
||||||
bool dirLeft = false;
|
bool dirLeft = false;
|
||||||
|
|
@ -445,10 +455,10 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
else {
|
else {
|
||||||
dirLeft = true;
|
dirLeft = true;
|
||||||
}
|
}
|
||||||
const Vector &testDir = m_moveSpeed > 0.0f ? game.vec.forward : -game.vec.forward;
|
const Vector &testDir = m_moveSpeed > 0.0f ? forward : -forward;
|
||||||
|
|
||||||
// now check which side is blocked
|
// now check which side is blocked
|
||||||
src = pev->origin + game.vec.right * 32.0f;
|
src = pev->origin + right * 32.0f;
|
||||||
dst = src + testDir * 32.0f;
|
dst = src + testDir * 32.0f;
|
||||||
|
|
||||||
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
||||||
|
|
@ -456,7 +466,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
if (tr.flFraction != 1.0f) {
|
if (tr.flFraction != 1.0f) {
|
||||||
blockedRight = true;
|
blockedRight = true;
|
||||||
}
|
}
|
||||||
src = pev->origin - game.vec.right * 32.0f;
|
src = pev->origin - right * 32.0f;
|
||||||
dst = src + testDir * 32.0f;
|
dst = src + testDir * 32.0f;
|
||||||
|
|
||||||
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
||||||
|
|
@ -502,16 +512,16 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seesEntity (m_destOrigin)) {
|
if (seesEntity (m_destOrigin)) {
|
||||||
game.makeVectors (m_moveAngles);
|
const auto &right = m_moveAngles.right ();
|
||||||
|
|
||||||
src = getEyesPos ();
|
src = getEyesPos ();
|
||||||
src = src + game.vec.right * 15.0f;
|
src = src + right * 15.0f;
|
||||||
|
|
||||||
game.testLine (src, m_destOrigin, TraceIgnore::Everything, ent (), &tr);
|
game.testLine (src, m_destOrigin, TraceIgnore::Everything, ent (), &tr);
|
||||||
|
|
||||||
if (tr.flFraction >= 1.0f) {
|
if (tr.flFraction >= 1.0f) {
|
||||||
src = getEyesPos ();
|
src = getEyesPos ();
|
||||||
src = src - game.vec.right * 15.0f;
|
src = src - right * 15.0f;
|
||||||
|
|
||||||
game.testLine (src, m_destOrigin, TraceIgnore::Everything, ent (), &tr);
|
game.testLine (src, m_destOrigin, TraceIgnore::Everything, ent (), &tr);
|
||||||
|
|
||||||
|
|
@ -622,11 +632,6 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// avoid players if not already stuck
|
|
||||||
if (!m_isStuck) {
|
|
||||||
doPlayerAvoidance (dirNormal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::updateNavigation () {
|
bool Bot::updateNavigation () {
|
||||||
|
|
@ -640,9 +645,8 @@ bool Bot::updateNavigation () {
|
||||||
m_pathOrigin = m_path->origin;
|
m_pathOrigin = m_path->origin;
|
||||||
|
|
||||||
// if wayzone radios non zero vary origin a bit depending on the body angles
|
// if wayzone radios non zero vary origin a bit depending on the body angles
|
||||||
if (m_path->radius > 0) {
|
if (m_path->radius > 0.0f) {
|
||||||
game.makeVectors (Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f));
|
m_pathOrigin = m_pathOrigin + Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f).forward () * rg.float_ (0, m_path->radius);
|
||||||
m_pathOrigin = m_pathOrigin + game.vec.forward * rg.float_ (0, m_path->radius);
|
|
||||||
}
|
}
|
||||||
m_navTimeset = game.timebase ();
|
m_navTimeset = game.timebase ();
|
||||||
}
|
}
|
||||||
|
|
@ -1093,13 +1097,15 @@ bool Bot::updateNavigation () {
|
||||||
else if (m_currentTravelFlags & PathFlag::Jump) {
|
else if (m_currentTravelFlags & PathFlag::Jump) {
|
||||||
desiredDistance = 0.0f;
|
desiredDistance = 0.0f;
|
||||||
}
|
}
|
||||||
else if (isOccupiedPoint (m_currentNodeIndex)) {
|
|
||||||
desiredDistance = 120.0f;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
desiredDistance = m_path->radius;
|
desiredDistance = m_path->radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if desired distance is big enough and used by someone, increase desired radius by twice, and mark it as reached...
|
||||||
|
if (desiredDistance >= m_path->radius && isOccupiedPoint (m_currentNodeIndex)) {
|
||||||
|
desiredDistance *= 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// check if node has a special travelflag, so they need to be reached more precisely
|
// check if node has a special travelflag, so they need to be reached more precisely
|
||||||
for (const auto &link : m_path->links) {
|
for (const auto &link : m_path->links) {
|
||||||
if (link.flags != 0) {
|
if (link.flags != 0) {
|
||||||
|
|
@ -1201,7 +1207,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex) + graph.getHighestDamageForTeam (team));
|
auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex) + graph.getHighestDamageForTeam (team));
|
||||||
Path ¤t = graph[currentIndex];
|
const auto ¤t = graph[currentIndex];
|
||||||
|
|
||||||
for (auto &neighbour : current.links) {
|
for (auto &neighbour : current.links) {
|
||||||
if (neighbour.index != kInvalidNodeIndex) {
|
if (neighbour.index != kInvalidNodeIndex) {
|
||||||
|
|
@ -1217,7 +1223,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
|
|
||||||
// least kills and number of nodes to goal for a team
|
// least kills and number of nodes to goal for a team
|
||||||
auto gfunctionKillsDistCTWithHostage = [&gfunctionKillsDist] (int team, int currentIndex, int parentIndex) -> float {
|
auto gfunctionKillsDistCTWithHostage = [&gfunctionKillsDist] (int team, int currentIndex, int parentIndex) -> float {
|
||||||
Path ¤t = graph[currentIndex];
|
const auto ¤t = graph[currentIndex];
|
||||||
|
|
||||||
if (current.flags & NodeFlag::NoHostage) {
|
if (current.flags & NodeFlag::NoHostage) {
|
||||||
return 65355.0f;
|
return 65355.0f;
|
||||||
|
|
@ -1231,7 +1237,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
// least kills to goal for a team
|
// least kills to goal for a team
|
||||||
auto gfunctionKills = [] (int team, int currentIndex, int) -> float {
|
auto gfunctionKills = [] (int team, int currentIndex, int) -> float {
|
||||||
auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex));
|
auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex));
|
||||||
Path ¤t = graph[currentIndex];
|
const auto ¤t = graph[currentIndex];
|
||||||
|
|
||||||
for (auto &neighbour : current.links) {
|
for (auto &neighbour : current.links) {
|
||||||
if (neighbour.index != kInvalidNodeIndex) {
|
if (neighbour.index != kInvalidNodeIndex) {
|
||||||
|
|
@ -1250,7 +1256,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
if (parentIndex == kInvalidNodeIndex) {
|
if (parentIndex == kInvalidNodeIndex) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
Path ¤t = graph[currentIndex];
|
const auto ¤t = graph[currentIndex];
|
||||||
|
|
||||||
if (current.flags & NodeFlag::NoHostage) {
|
if (current.flags & NodeFlag::NoHostage) {
|
||||||
return 65355.0f;
|
return 65355.0f;
|
||||||
|
|
@ -1265,8 +1271,8 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
if (parentIndex == kInvalidNodeIndex) {
|
if (parentIndex == kInvalidNodeIndex) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
Path &parent = graph[parentIndex];
|
const auto &parent = graph[parentIndex];
|
||||||
Path ¤t = graph[currentIndex];
|
const auto ¤t = graph[currentIndex];
|
||||||
|
|
||||||
for (const auto &link : parent.links) {
|
for (const auto &link : parent.links) {
|
||||||
if (link.index == currentIndex) {
|
if (link.index == currentIndex) {
|
||||||
|
|
@ -1281,7 +1287,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
};
|
};
|
||||||
|
|
||||||
auto gfunctionPathDistWithHostage = [&gfunctionPathDist] (int, int currentIndex, int parentIndex) -> float {
|
auto gfunctionPathDistWithHostage = [&gfunctionPathDist] (int, int currentIndex, int parentIndex) -> float {
|
||||||
Path ¤t = graph[currentIndex];
|
const auto ¤t = graph[currentIndex];
|
||||||
|
|
||||||
if (current.flags & NodeFlag::NoHostage) {
|
if (current.flags & NodeFlag::NoHostage) {
|
||||||
return 65355.0f;
|
return 65355.0f;
|
||||||
|
|
@ -1294,8 +1300,8 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
|
|
||||||
// square distance heuristic
|
// square distance heuristic
|
||||||
auto hfunctionPathDist = [] (int index, int, int goalIndex) -> float {
|
auto hfunctionPathDist = [] (int index, int, int goalIndex) -> float {
|
||||||
Path &start = graph[index];
|
const auto &start = graph[index];
|
||||||
Path &goal = graph[goalIndex];
|
const auto &goal = graph[goalIndex];
|
||||||
|
|
||||||
float x = cr::abs (start.origin.x - goal.origin.x);
|
float x = cr::abs (start.origin.x - goal.origin.x);
|
||||||
float y = cr::abs (start.origin.y - goal.origin.y);
|
float y = cr::abs (start.origin.y - goal.origin.y);
|
||||||
|
|
@ -1442,7 +1448,9 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
||||||
|
|
||||||
} while (currentIndex != kInvalidNodeIndex);
|
} while (currentIndex != kInvalidNodeIndex);
|
||||||
|
|
||||||
|
// reverse path for path follower
|
||||||
m_pathWalk.reverse ();
|
m_pathWalk.reverse ();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto curRoute = &m_routes[currentIndex];
|
auto curRoute = &m_routes[currentIndex];
|
||||||
|
|
@ -1576,7 +1584,7 @@ bool Bot::findBestNearestNode () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore non-reacheable nodes...
|
// ignore non-reacheable nodes...
|
||||||
if (!graph.isReachable (this, at)) {
|
if (!isReachableNode (at)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1770,7 +1778,7 @@ int Bot::findNearestNode () {
|
||||||
if (distance < minimum) {
|
if (distance < minimum) {
|
||||||
|
|
||||||
// if bot doing navigation, make sure node really visible and not too high
|
// if bot doing navigation, make sure node really visible and not too high
|
||||||
if ((m_currentNodeIndex != kInvalidNodeIndex && graph.isVisible (m_currentNodeIndex, at)) || graph.isReachable (this, at)) {
|
if ((m_currentNodeIndex != kInvalidNodeIndex && graph.isVisible (m_currentNodeIndex, at)) || isReachableNode (at)) {
|
||||||
index = at;
|
index = at;
|
||||||
minimum = distance;
|
minimum = distance;
|
||||||
}
|
}
|
||||||
|
|
@ -2229,8 +2237,7 @@ bool Bot::advanceMovement () {
|
||||||
|
|
||||||
// if wayzone radius non zero vary origin a bit depending on the body angles
|
// if wayzone radius non zero vary origin a bit depending on the body angles
|
||||||
if (m_path->radius > 0.0f) {
|
if (m_path->radius > 0.0f) {
|
||||||
game.makeVectors (Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f));
|
m_pathOrigin = m_pathOrigin + Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f).forward () * rg.float_ (0.0f, m_path->radius);
|
||||||
m_pathOrigin = m_pathOrigin + game.vec.forward * rg.float_ (0.0f, m_path->radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOnLadder ()) {
|
if (isOnLadder ()) {
|
||||||
|
|
@ -2254,7 +2261,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||||
Vector src = getEyesPos ();
|
Vector src = getEyesPos ();
|
||||||
Vector forward = src + normal * 24.0f;
|
Vector forward = src + normal * 24.0f;
|
||||||
|
|
||||||
game.makeVectors (Vector (0.0f, pev->angles.y, 0.0f));
|
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
|
||||||
|
|
||||||
auto checkDoor = [] (TraceResult *tr) {
|
auto checkDoor = [] (TraceResult *tr) {
|
||||||
if (!game.mapIs (MapFlags::HasDoors)) {
|
if (!game.mapIs (MapFlags::HasDoors)) {
|
||||||
|
|
@ -2276,8 +2283,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||||
|
|
||||||
// bot's head is clear, check at shoulder level...
|
// bot's head is clear, check at shoulder level...
|
||||||
// trace from the bot's shoulder left diagonal forward to the right shoulder...
|
// trace from the bot's shoulder left diagonal forward to the right shoulder...
|
||||||
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - game.vec.right * -16.0f;
|
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - right * -16.0f;
|
||||||
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + game.vec.right * 16.0f + normal * 24.0f;
|
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + right * 16.0f + normal * 24.0f;
|
||||||
|
|
||||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
|
|
@ -2288,8 +2295,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||||
|
|
||||||
// bot's head is clear, check at shoulder level...
|
// bot's head is clear, check at shoulder level...
|
||||||
// trace from the bot's shoulder right diagonal forward to the left shoulder...
|
// trace from the bot's shoulder right diagonal forward to the left shoulder...
|
||||||
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + game.vec.right * 16.0f;
|
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + right * 16.0f;
|
||||||
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - game.vec.right * -16.0f + normal * 24.0f;
|
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - right * -16.0f + normal * 24.0f;
|
||||||
|
|
||||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
|
|
@ -2321,8 +2328,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// trace from the left waist to the right forward waist pos
|
// trace from the left waist to the right forward waist pos
|
||||||
src = pev->origin + Vector (0.0f, 0.0f, -17.0f) - game.vec.right * -16.0f;
|
src = pev->origin + Vector (0.0f, 0.0f, -17.0f) - right * -16.0f;
|
||||||
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) + game.vec.right * 16.0f + normal * 24.0f;
|
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) + right * 16.0f + normal * 24.0f;
|
||||||
|
|
||||||
// trace from the bot's waist straight forward...
|
// trace from the bot's waist straight forward...
|
||||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
@ -2333,8 +2340,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// trace from the left waist to the right forward waist pos
|
// trace from the left waist to the right forward waist pos
|
||||||
src = pev->origin + Vector (0.0f, 0.0f, -17.0f) + game.vec.right * 16.0f;
|
src = pev->origin + Vector (0.0f, 0.0f, -17.0f) + right * 16.0f;
|
||||||
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) - game.vec.right * -16.0f + normal * 24.0f;
|
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) - right * -16.0f + normal * 24.0f;
|
||||||
|
|
||||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
|
|
@ -2346,28 +2353,28 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||||
return false; // bot can move forward, return false
|
return false; // bot can move forward, return false
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEAD_CODE
|
|
||||||
bool Bot::canStrafeLeft (TraceResult *tr) {
|
bool Bot::canStrafeLeft (TraceResult *tr) {
|
||||||
// this function checks if bot can move sideways
|
// this function checks if bot can move sideways
|
||||||
|
|
||||||
makeVectors (pev->v_angle);
|
Vector right, forward;
|
||||||
|
pev->v_angle.buildVectors (&forward, &right, nullptr);
|
||||||
|
|
||||||
Vector src = pev->origin;
|
Vector src = pev->origin;
|
||||||
Vector left = src - game.vec.right * -40.0f;
|
Vector dest = src - right * -40.0f;
|
||||||
|
|
||||||
// trace from the bot's waist straight left...
|
// trace from the bot's waist straight left...
|
||||||
TraceLine (src, left, true, ent (), tr);
|
game.testLine (src, dest, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (tr->flFraction < 1.0f) {
|
if (tr->flFraction < 1.0f) {
|
||||||
return false; // bot's body will hit something
|
return false; // bot's body will hit something
|
||||||
}
|
}
|
||||||
|
|
||||||
src = left;
|
src = dest;
|
||||||
left = left + game.vec.forward * 40.0f;
|
dest = dest + forward * 40.0f;
|
||||||
|
|
||||||
// trace from the strafe pos straight forward...
|
// trace from the strafe pos straight forward...
|
||||||
TraceLine (src, left, true, ent (), tr);
|
game.testLine (src, dest, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (tr->flFraction < 1.0f) {
|
if (tr->flFraction < 1.0f) {
|
||||||
|
|
@ -2379,23 +2386,24 @@ bool Bot::canStrafeLeft (TraceResult *tr) {
|
||||||
bool Bot::canStrafeRight (TraceResult *tr) {
|
bool Bot::canStrafeRight (TraceResult *tr) {
|
||||||
// this function checks if bot can move sideways
|
// this function checks if bot can move sideways
|
||||||
|
|
||||||
makeVectors (pev->v_angle);
|
Vector right, forward;
|
||||||
|
pev->v_angle.buildVectors (&forward, &right, nullptr);
|
||||||
|
|
||||||
Vector src = pev->origin;
|
Vector src = pev->origin;
|
||||||
Vector right = src + game.vec.right * 40.0f;
|
Vector dest = src + right * 40.0f;
|
||||||
|
|
||||||
// trace from the bot's waist straight right...
|
// trace from the bot's waist straight right...
|
||||||
TraceLine (src, right, true, ent (), tr);
|
game.testLine (src, dest, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (tr->flFraction < 1.0f) {
|
if (tr->flFraction < 1.0f) {
|
||||||
return false; // bot's body will hit something
|
return false; // bot's body will hit something
|
||||||
}
|
}
|
||||||
src = right;
|
src = dest;
|
||||||
right = right + game.vec.forward * 40.0f;
|
dest = dest + forward * 40.0f;
|
||||||
|
|
||||||
// trace from the strafe pos straight forward...
|
// trace from the strafe pos straight forward...
|
||||||
TraceLine (src, right, true, ent (), tr);
|
game.testLine (src, dest, TraceIgnore::Monsters, ent (), tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (tr->flFraction < 1.0f) {
|
if (tr->flFraction < 1.0f) {
|
||||||
|
|
@ -2404,8 +2412,6 @@ bool Bot::canStrafeRight (TraceResult *tr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool Bot::canJumpUp (const Vector &normal) {
|
bool Bot::canJumpUp (const Vector &normal) {
|
||||||
// this function check if bot can jump over some obstacle
|
// this function check if bot can jump over some obstacle
|
||||||
|
|
||||||
|
|
@ -2415,9 +2421,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
|
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
|
||||||
// convert current view angle to vectors for traceline math...
|
|
||||||
game.makeVectors (Vector (0.0f, pev->angles.y, 0.0f));
|
|
||||||
|
|
||||||
// check for normal jump height first...
|
// check for normal jump height first...
|
||||||
Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
||||||
|
|
@ -2427,7 +2431,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
game.testLine (src, dest, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (src, dest, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
return doneCanJumpUp (normal);
|
return doneCanJumpUp (normal, right);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// now trace from jump height upward to check for obstructions...
|
// now trace from jump height upward to check for obstructions...
|
||||||
|
|
@ -2442,7 +2446,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check same height to one side of the bot...
|
// now check same height to one side of the bot...
|
||||||
src = pev->origin + game.vec.right * 16.0f + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
src = pev->origin + right * 16.0f + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
||||||
dest = src + normal * 32.0f;
|
dest = src + normal * 32.0f;
|
||||||
|
|
||||||
// trace a line forward at maximum jump height...
|
// trace a line forward at maximum jump height...
|
||||||
|
|
@ -2450,7 +2454,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
|
|
||||||
// if trace hit something, return false
|
// if trace hit something, return false
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
return doneCanJumpUp (normal);
|
return doneCanJumpUp (normal, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now trace from jump height upward to check for obstructions...
|
// now trace from jump height upward to check for obstructions...
|
||||||
|
|
@ -2465,7 +2469,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check same height on the other side of the bot...
|
// now check same height on the other side of the bot...
|
||||||
src = pev->origin + (-game.vec.right * 16.0f) + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
src = pev->origin + (-right * 16.0f) + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
||||||
dest = src + normal * 32.0f;
|
dest = src + normal * 32.0f;
|
||||||
|
|
||||||
// trace a line forward at maximum jump height...
|
// trace a line forward at maximum jump height...
|
||||||
|
|
@ -2473,7 +2477,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
|
|
||||||
// if trace hit something, return false
|
// if trace hit something, return false
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
return doneCanJumpUp (normal);
|
return doneCanJumpUp (normal, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now trace from jump height upward to check for obstructions...
|
// now trace from jump height upward to check for obstructions...
|
||||||
|
|
@ -2486,7 +2490,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
return tr.flFraction > 1.0f;
|
return tr.flFraction > 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::doneCanJumpUp (const Vector &normal) {
|
bool Bot::doneCanJumpUp (const Vector &normal, const Vector &right) {
|
||||||
// use center of the body first... maximum duck jump height is 62, so check one unit above that (63)
|
// use center of the body first... maximum duck jump height is 62, so check one unit above that (63)
|
||||||
Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 63.0f);
|
Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 63.0f);
|
||||||
Vector dest = src + normal * 32.0f;
|
Vector dest = src + normal * 32.0f;
|
||||||
|
|
@ -2513,7 +2517,7 @@ bool Bot::doneCanJumpUp (const Vector &normal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check same height to one side of the bot...
|
// now check same height to one side of the bot...
|
||||||
src = pev->origin + game.vec.right * 16.0f + Vector (0.0f, 0.0f, -36.0f + 63.0f);
|
src = pev->origin + right * 16.0f + Vector (0.0f, 0.0f, -36.0f + 63.0f);
|
||||||
dest = src + normal * 32.0f;
|
dest = src + normal * 32.0f;
|
||||||
|
|
||||||
// trace a line forward at maximum jump height...
|
// trace a line forward at maximum jump height...
|
||||||
|
|
@ -2536,7 +2540,7 @@ bool Bot::doneCanJumpUp (const Vector &normal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check same height on the other side of the bot...
|
// now check same height on the other side of the bot...
|
||||||
src = pev->origin + (-game.vec.right * 16.0f) + Vector (0.0f, 0.0f, -36.0f + 63.0f);
|
src = pev->origin + (-right * 16.0f) + Vector (0.0f, 0.0f, -36.0f + 63.0f);
|
||||||
dest = src + normal * 32.0f;
|
dest = src + normal * 32.0f;
|
||||||
|
|
||||||
// trace a line forward at maximum jump height...
|
// trace a line forward at maximum jump height...
|
||||||
|
|
@ -2563,9 +2567,6 @@ bool Bot::canDuckUnder (const Vector &normal) {
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
Vector baseHeight;
|
Vector baseHeight;
|
||||||
|
|
||||||
// convert current view angle to vectors for TraceLine math...
|
|
||||||
game.makeVectors (Vector (0.0f, pev->angles.y, 0.0f));
|
|
||||||
|
|
||||||
// use center of the body first...
|
// use center of the body first...
|
||||||
if (pev->flags & FL_DUCKING) {
|
if (pev->flags & FL_DUCKING) {
|
||||||
baseHeight = pev->origin + Vector (0.0f, 0.0f, -17.0f);
|
baseHeight = pev->origin + Vector (0.0f, 0.0f, -17.0f);
|
||||||
|
|
@ -2585,8 +2586,11 @@ bool Bot::canDuckUnder (const Vector &normal) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert current view angle to vectors for TraceLine math...
|
||||||
|
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
|
||||||
|
|
||||||
// now check same height to one side of the bot...
|
// now check same height to one side of the bot...
|
||||||
src = baseHeight + game.vec.right * 16.0f;
|
src = baseHeight + right * 16.0f;
|
||||||
dest = src + normal * 32.0f;
|
dest = src + normal * 32.0f;
|
||||||
|
|
||||||
// trace a line forward at duck height...
|
// trace a line forward at duck height...
|
||||||
|
|
@ -2598,7 +2602,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check same height on the other side of the bot...
|
// now check same height on the other side of the bot...
|
||||||
src = baseHeight + (-game.vec.right * 16.0f);
|
src = baseHeight + (-right * 16.0f);
|
||||||
dest = src + normal * 32.0f;
|
dest = src + normal * 32.0f;
|
||||||
|
|
||||||
// trace a line forward at duck height...
|
// trace a line forward at duck height...
|
||||||
|
|
@ -2608,19 +2612,18 @@ bool Bot::canDuckUnder (const Vector &normal) {
|
||||||
return tr.flFraction > 1.0f;
|
return tr.flFraction > 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEAD_CODE
|
|
||||||
|
|
||||||
bool Bot::isBlockedLeft () {
|
bool Bot::isBlockedLeft () {
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
int direction = 48;
|
float direction = 48.0f;
|
||||||
|
|
||||||
if (m_moveSpeed < 0.0f) {
|
if (m_moveSpeed < 0.0f) {
|
||||||
direction = -48;
|
direction = -48.0f;
|
||||||
}
|
}
|
||||||
makeVectors (pev->angles);
|
Vector right, forward;
|
||||||
|
pev->angles.buildVectors (&forward, &right, nullptr);
|
||||||
|
|
||||||
// do a trace to the left...
|
// do a trace to the left...
|
||||||
game.TestLine (pev->origin, game.vec.forward * direction - game.vec.right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (pev->origin, forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0) {
|
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0) {
|
||||||
|
|
@ -2631,15 +2634,16 @@ bool Bot::isBlockedLeft () {
|
||||||
|
|
||||||
bool Bot::isBlockedRight () {
|
bool Bot::isBlockedRight () {
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
int direction = 48;
|
float direction = 48.0f;
|
||||||
|
|
||||||
if (m_moveSpeed < 0) {
|
if (m_moveSpeed < 0.0f) {
|
||||||
direction = -48;
|
direction = -48.0f;
|
||||||
}
|
}
|
||||||
makeVectors (pev->angles);
|
Vector right, forward;
|
||||||
|
pev->angles.buildVectors (&forward, &right, nullptr);
|
||||||
|
|
||||||
// do a trace to the right...
|
// do a trace to the right...
|
||||||
game.TestLine (pev->origin, pev->origin + game.vec.forward * direction + game.vec.right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0)) {
|
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0)) {
|
||||||
|
|
@ -2648,13 +2652,9 @@ bool Bot::isBlockedRight () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool Bot::checkWallOnLeft () {
|
bool Bot::checkWallOnLeft () {
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
game.makeVectors (pev->angles);
|
game.testLine (pev->origin, pev->origin - pev->angles.right () * 40.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
game.testLine (pev->origin, pev->origin - game.vec.right * 40.0f, TraceIgnore::Monsters, ent (), &tr);
|
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
|
|
@ -2665,10 +2665,9 @@ bool Bot::checkWallOnLeft () {
|
||||||
|
|
||||||
bool Bot::checkWallOnRight () {
|
bool Bot::checkWallOnRight () {
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
game.makeVectors (pev->angles);
|
|
||||||
|
|
||||||
// do a trace to the right...
|
// do a trace to the right...
|
||||||
game.testLine (pev->origin, pev->origin + game.vec.right * 40.0f, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (pev->origin, pev->origin + pev->angles.right () * 40.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// check if the trace hit something...
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
|
|
@ -2680,17 +2679,11 @@ bool Bot::checkWallOnRight () {
|
||||||
bool Bot::isDeadlyMove (const Vector &to) {
|
bool Bot::isDeadlyMove (const Vector &to) {
|
||||||
// this function eturns if given location would hurt Bot with falling damage
|
// this function eturns if given location would hurt Bot with falling damage
|
||||||
|
|
||||||
Vector botPos = pev->origin;
|
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
|
const auto &direction = (to - pev->origin).normalize (); // 1 unit long
|
||||||
|
|
||||||
Vector move ((to - botPos).yaw (), 0.0f, 0.0f);
|
Vector check = to, down = to;
|
||||||
game.makeVectors (move);
|
down.z -= 1000.0f; // straight down 1000 units
|
||||||
|
|
||||||
Vector direction = (to - botPos).normalize (); // 1 unit long
|
|
||||||
Vector check = botPos;
|
|
||||||
Vector down = botPos;
|
|
||||||
|
|
||||||
down.z = down.z - 1000.0f; // straight down 1000 units
|
|
||||||
|
|
||||||
game.testHull (check, down, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
game.testHull (check, down, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
||||||
|
|
||||||
|
|
@ -2701,20 +2694,26 @@ bool Bot::isDeadlyMove (const Vector &to) {
|
||||||
float lastHeight = tr.flFraction * 1000.0f; // height from ground
|
float lastHeight = tr.flFraction * 1000.0f; // height from ground
|
||||||
float distance = (to - check).length (); // distance from goal
|
float distance = (to - check).length (); // distance from goal
|
||||||
|
|
||||||
while (distance > 16.0f) {
|
if (distance <= 30.0f && lastHeight > 150.0f) {
|
||||||
check = check + direction * 16.0f; // move 10 units closer to the goal...
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (distance > 30.0f) {
|
||||||
|
check = check + direction * 30.0f; // move 10 units closer to the goal...
|
||||||
|
|
||||||
down = check;
|
down = check;
|
||||||
down.z = down.z - 1000.0f; // straight down 1000 units
|
down.z -= 1000.0f; // straight down 1000 units
|
||||||
|
|
||||||
game.testHull (check, down, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
game.testHull (check, down, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
||||||
|
|
||||||
if (tr.fStartSolid) { // Wall blocking?
|
// wall blocking?
|
||||||
|
if (tr.fStartSolid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
float height = tr.flFraction * 1000.0f; // height from ground
|
float height = tr.flFraction * 1000.0f; // height from ground
|
||||||
|
|
||||||
if (lastHeight < height - 100.0f) {// Drops more than 100 Units?
|
// drops more than 150 units?
|
||||||
|
if (lastHeight < height - 150.0f) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
lastHeight = height;
|
lastHeight = height;
|
||||||
|
|
@ -2986,10 +2985,8 @@ void Bot::updateLookAnglesNewbie (const Vector &direction, float delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) {
|
void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) {
|
||||||
game.makeVectors (pev->angles);
|
|
||||||
|
|
||||||
const Vector &los = (moveDir - pev->origin).normalize2d ();
|
const Vector &los = (moveDir - pev->origin).normalize2d ();
|
||||||
float dot = los | game.vec.forward.get2d ();
|
float dot = los | pev->angles.forward ().get2d ();
|
||||||
|
|
||||||
if (dot > 0.0f && !checkWallOnRight ()) {
|
if (dot > 0.0f && !checkWallOnRight ()) {
|
||||||
m_strafeSpeed = strafeSpeed;
|
m_strafeSpeed = strafeSpeed;
|
||||||
|
|
@ -3062,7 +3059,7 @@ edict_t *Bot::lookupButton (const char *targetName) {
|
||||||
// this function tries to find nearest to current bot button, and returns pointer to
|
// this function tries to find nearest to current bot button, and returns pointer to
|
||||||
// it's entity, also here must be specified the target, that button must open.
|
// it's entity, also here must be specified the target, that button must open.
|
||||||
|
|
||||||
if (util.isEmptyStr (targetName)) {
|
if (strings.isEmpty (targetName)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
float nearestDistance = kInfiniteDistance;
|
float nearestDistance = kInfiniteDistance;
|
||||||
|
|
@ -3084,4 +3081,48 @@ edict_t *Bot::lookupButton (const char *targetName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return foundEntity;
|
return foundEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Bot::isReachableNode (int index) {
|
||||||
|
// this function return whether bot able to reach index node or not, depending on several factors.
|
||||||
|
|
||||||
|
if (!graph.exists (index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const Vector &src = pev->origin;
|
||||||
|
const Vector &dst = graph[index].origin;
|
||||||
|
|
||||||
|
// is the destination close enough?
|
||||||
|
if ((dst - src).lengthSq () >= cr::square (320.0f)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float ladderDist = (dst - src).length2d ();
|
||||||
|
|
||||||
|
TraceResult tr;
|
||||||
|
game.testLine (src, dst, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
|
// if node is visible from current position (even behind head)...
|
||||||
|
if (tr.flFraction >= 1.0f) {
|
||||||
|
|
||||||
|
// it's should be not a problem to reach node inside water...
|
||||||
|
if (pev->waterlevel == 2 || pev->waterlevel == 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for ladder
|
||||||
|
bool nonLadder = !(graph[index].flags & NodeFlag::Ladder) || ladderDist > 16.0f;
|
||||||
|
|
||||||
|
// is dest node higher than src? (62 is max jump height)
|
||||||
|
if (nonLadder && dst.z > src.z + 62.0f) {
|
||||||
|
return false; // can't reach this one
|
||||||
|
}
|
||||||
|
|
||||||
|
// is dest node lower than src?
|
||||||
|
if (nonLadder && dst.z < src.z - 100.0f) {
|
||||||
|
return false; // can't reach this one
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,7 @@ bool BotUtils::isAlive (edict_t *ent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float BotUtils::getShootingCone (edict_t *ent, const Vector &position) {
|
float BotUtils::getShootingCone (edict_t *ent, const Vector &position) {
|
||||||
game.makeVectors (ent->v.v_angle);
|
return ent->v.v_angle.forward () | (position - (ent->v.origin + ent->v.view_ofs)).normalize (); // he's facing it, he meant it
|
||||||
|
|
||||||
// he's facing it, he meant it
|
|
||||||
return game.vec.forward | (position - (ent->v.origin + ent->v.view_ofs)).normalize ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BotUtils::isInViewCone (const Vector &origin, edict_t *ent) {
|
bool BotUtils::isInViewCone (const Vector &origin, edict_t *ent) {
|
||||||
|
|
@ -169,7 +166,7 @@ bool BotUtils::isPlayer (edict_t *ent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots[ent] != nullptr) {
|
if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots[ent] != nullptr) {
|
||||||
return !isEmptyStr (STRING (ent->v.netname));
|
return !strings.isEmpty (STRING (ent->v.netname));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +243,7 @@ void BotUtils::checkWelcome () {
|
||||||
game.serverCommand ("speak \"%s\"", m_sentences.random ().chars ());
|
game.serverCommand ("speak \"%s\"", m_sentences.random ().chars ());
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter (MSG_ONE, game.getMessageId (NetMsg::TextMsg), nullvec, receiveEntity)
|
MessageWriter (MSG_ONE, msgs.id (NetMsg::TextMsg), nullvec, receiveEntity)
|
||||||
.writeByte (HUD_PRINTTALK)
|
.writeByte (HUD_PRINTTALK)
|
||||||
.writeString (strings.format ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL));
|
.writeString (strings.format ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL));
|
||||||
|
|
||||||
|
|
@ -319,7 +316,7 @@ void BotUtils::attachSoundsToClients (edict_t *ent, const char *sample, float vo
|
||||||
// this function called by the sound hooking code (in emit_sound) enters the played sound into
|
// this function called by the sound hooking code (in emit_sound) enters the played sound into
|
||||||
// the array associated with the entity
|
// the array associated with the entity
|
||||||
|
|
||||||
if (game.isNullEntity (ent) || isEmptyStr (sample)) {
|
if (game.isNullEntity (ent) || strings.isEmpty (sample)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const Vector &origin = game.getAbsPos (ent);
|
const Vector &origin = game.getAbsPos (ent);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue