From ff6c56aeac3e2c6d497e36100883dbc7986b0486 Mon Sep 17 00:00:00 2001 From: jeefo Date: Sat, 14 Sep 2019 23:13:55 +0300 Subject: [PATCH] Fixed crash in message dispatcher after 'meta unload'. Various cosmetic changes. --- include/control.h | 2 +- include/crlib/cr-basic.h | 28 +++-- include/crlib/cr-dict.h | 9 +- include/crlib/cr-files.h | 4 + include/crlib/cr-hook.h | 55 +++++++--- include/crlib/cr-http.h | 8 +- include/crlib/cr-lambda.h | 6 +- include/crlib/cr-library.h | 8 +- include/crlib/cr-logger.h | 5 - include/crlib/cr-platform.h | 41 ++++--- include/crlib/cr-string.h | 99 ++++++++--------- include/crlib/cr-twin.h | 11 +- include/crlib/cr-ulz.h | 3 +- include/crlib/cr-uniqueptr.h | 26 ++--- include/engine.h | 30 +++++- include/engine/const.h | 191 ++++++++++++++++++++++++++++---- include/engine/eiface.h | 71 +++--------- include/engine/extdll.h | 109 ++++++++++--------- include/engine/metamod.h | 203 +++++++++++++++++++++++++++++++++++ include/engine/model.h | 17 --- include/engine/progdefs.h | 9 +- include/message.h | 7 +- include/utils.h | 5 +- include/yapb.h | 21 +++- project/makefile | 2 +- source/android.cpp | 2 +- source/basecode.cpp | 145 +++++++++++-------------- source/chatlib.cpp | 6 +- source/combat.cpp | 10 +- source/control.cpp | 8 +- source/engine.cpp | 45 ++++---- source/graph.cpp | 17 +-- source/linkage.cpp | 30 +++--- source/manager.cpp | 28 ++--- source/message.cpp | 22 ++++ source/navigate.cpp | 163 +++++++++++++--------------- source/support.cpp | 105 ++++++++---------- 37 files changed, 949 insertions(+), 602 deletions(-) create mode 100644 include/engine/metamod.h diff --git a/include/control.h b/include/control.h index 3b3802b..265002d 100644 --- a/include/control.h +++ b/include/control.h @@ -207,7 +207,7 @@ template inline void BotControl::msg (const char *fmt, Args . return; } - if (m_isFromConsole || strlen (result) > 48) { + if (m_isFromConsole || strlen (result) > 56) { game.clientPrint (m_ent, result); } else { diff --git a/include/crlib/cr-basic.h b/include/crlib/cr-basic.h index 8463353..967d42c 100644 --- a/include/crlib/cr-basic.h +++ b/include/crlib/cr-basic.h @@ -13,14 +13,12 @@ #define CR_NAMESPACE_BEGIN namespace cr { #define CR_NAMESPACE_END } -// undef base max -#if defined (max) -# undef max -#endif - -// undef base min -#if defined (min) -# undef min +// disable microsoft deprecation warning +#if defined (_MSC_VER) +# if !defined (_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +# define _WINSOCK_DEPRECATED_NO_WARNINGS +# endif #endif #include @@ -34,13 +32,13 @@ CR_NAMESPACE_BEGIN // some useful type definitions // namespace types { -using int8 = signed char; -using int16 = signed short; -using int32 = signed int; -using uint8 = unsigned char; -using uint16 = unsigned short; -using uint32 = unsigned int; -using uint64 = unsigned long long; + using int8 = signed char; + using int16 = signed short; + using int32 = signed int; + using uint8 = unsigned char; + using uint16 = unsigned short; + using uint32 = unsigned int; + using uint64 = unsigned long long; } // make types available for our own use diff --git a/include/crlib/cr-dict.h b/include/crlib/cr-dict.h index cf950fc..f5baf0c 100644 --- a/include/crlib/cr-dict.h +++ b/include/crlib/cr-dict.h @@ -19,7 +19,7 @@ CR_NAMESPACE_BEGIN // template for hashing our string template struct StringHash { uint32 operator () (const K &key) const { - char *str = const_cast (key.chars ()); + auto str = const_cast (key.chars ()); uint32 hash = 0; while (*str++) { @@ -68,6 +68,7 @@ namespace detail { template , size_t HashSize = 36> class Dictionary final : public DenyCopying { private: using DictBucket = detail::DictionaryBucket ; + using DictList = detail::DictionaryList; public: enum : size_t { @@ -75,7 +76,7 @@ public: }; private: - Array m_table; + Array m_table; Array m_buckets; H m_hasher; @@ -98,7 +99,7 @@ private: size_t created = m_buckets.length (); m_buckets.resize (created + 1); - auto allocated = alloc.allocate (); + auto allocated = alloc.allocate (); allocated->index = created; allocated->next = m_table[pos]; @@ -163,7 +164,7 @@ public: auto pos = hashed % m_table.length (); auto *bucket = m_table[pos]; - detail::DictionaryList *next = nullptr; + DictList *next = nullptr; while (bucket != nullptr) { if (m_buckets[bucket->index].hash == hashed) { diff --git a/include/crlib/cr-files.h b/include/crlib/cr-files.h index 26c4040..b9b59d2 100644 --- a/include/crlib/cr-files.h +++ b/include/crlib/cr-files.h @@ -35,6 +35,10 @@ public: public: bool open (const String &file, const String &mode) { + if (*this) { + close (); + } + if ((m_handle = fopen (file.chars (), mode.chars ())) == nullptr) { return false; } diff --git a/include/crlib/cr-hook.h b/include/crlib/cr-hook.h index e078528..4abd524 100644 --- a/include/crlib/cr-hook.h +++ b/include/crlib/cr-hook.h @@ -20,15 +20,25 @@ CR_NAMESPACE_BEGIN class SimpleHook : DenyCopying { private: enum : uint32 { - CodeLength = 12 +#if defined (CR_ARCH_X64) + CodeLength = 14 +#else + CodeLength = 6 +#endif }; +#if defined (CR_ARCH_X64) + using uint = uint64; +#else + using uint = uint32; +#endif + private: bool m_patched; - uint32 m_pageSize; - uint32 m_origFunc; - uint32 m_hookFunc; + uint m_pageSize; + uint m_origFunc; + uint m_hookFunc; uint8 m_origBytes[CodeLength] {}; uint8 m_hookBytes[CodeLength] {}; @@ -43,11 +53,13 @@ private: #else m_pageSize = sysconf (_SC_PAGESIZE); #endif - } +} - inline void *align (void *address) { +#if !defined (CR_WINDOWS) + void *align (void *address) { return reinterpret_cast ((reinterpret_cast (address) & ~(m_pageSize - 1))); } +#endif bool unprotect () { auto orig = reinterpret_cast (m_origFunc); @@ -74,25 +86,36 @@ public: public: bool patch (void *address, void *replacement) { - if (plat.isArm) { + const uint16 jmp = 0x25ff; + + if (plat.arm) { return false; } - uint8 *ptr = reinterpret_cast (address); + auto ptr = reinterpret_cast (address); - while (*reinterpret_cast (ptr) == 0x25ff) { - ptr = **reinterpret_cast ((ptr + 2)); + while (*reinterpret_cast (ptr) == jmp) { + ptr = **reinterpret_cast (ptr + 2); } - m_origFunc = reinterpret_cast (ptr); + m_origFunc = reinterpret_cast (address); if (!m_origFunc) { return false; } - m_hookFunc = reinterpret_cast (replacement); + m_hookFunc = reinterpret_cast (replacement); - m_hookBytes[0] = 0x68; - m_hookBytes[5] = 0xc3; + if (plat.x64) { + const uint16 nop = 0x00000000; - *reinterpret_cast (&m_hookBytes[1]) = m_hookFunc; + memcpy (&m_hookBytes[0], &jmp, sizeof (uint16)); + memcpy (&m_hookBytes[2], &nop, sizeof (uint16)); + memcpy (&m_hookBytes[6], &replacement, sizeof (uint)); + } + else { + m_hookBytes[0] = 0x68; + m_hookBytes[5] = 0xc3; + + memcpy (&m_hookBytes[1], &m_hookFunc, sizeof (uint)); + } if (unprotect ()) { memcpy (m_origBytes, reinterpret_cast (m_origFunc), CodeLength); @@ -130,6 +153,6 @@ public: bool enabled () const { return m_patched; } -}; + }; CR_NAMESPACE_END \ No newline at end of file diff --git a/include/crlib/cr-http.h b/include/crlib/cr-http.h index 3d7a006..d9ab525 100644 --- a/include/crlib/cr-http.h +++ b/include/crlib/cr-http.h @@ -374,19 +374,19 @@ public: if (boundarySlash != String::InvalidIndex) { boundaryName = localPath.substr (boundarySlash + 1); } - const String &kBoundary = "---crlib_upload_boundary_1337"; + const String &boundaryLine = "---crlib_upload_boundary_1337"; String request, start, end; - start.appendf ("--%s\r\n", kBoundary.chars ()); + start.appendf ("--%s\r\n", boundaryLine.chars ()); start.appendf ("Content-Disposition: form-data; name='file'; filename='%s'\r\n", boundaryName.chars ()); start.append ("Content-Type: application/octet-stream\r\n\r\n"); - end.appendf ("\r\n--%s--\r\n\r\n", kBoundary.chars ()); + end.appendf ("\r\n--%s--\r\n\r\n", boundaryLine.chars ()); request.appendf ("POST /%s HTTP/1.1\r\n", uri.path.chars ()); request.appendf ("Host: %s\r\n", uri.host.chars ()); request.appendf ("User-Agent: %s\r\n", m_userAgent.chars ()); - request.appendf ("Content-Type: multipart/form-data; boundary=%s\r\n", kBoundary.chars ()); + request.appendf ("Content-Type: multipart/form-data; boundary=%s\r\n", boundaryLine.chars ()); request.appendf ("Content-Length: %d\r\n\r\n", file.length () + start.length () + end.length ()); // send the main request diff --git a/include/crlib/cr-lambda.h b/include/crlib/cr-lambda.h index 077dcad..c26fbd9 100644 --- a/include/crlib/cr-lambda.h +++ b/include/crlib/cr-lambda.h @@ -67,7 +67,7 @@ private: } UniquePtr clone () const override { - return createUniqueBase , LambdaFunctorWrapper> (m_callable); + return makeUnique > (m_callable); } }; @@ -93,7 +93,7 @@ private: } public: - Lambda () noexcept : Lambda (nullptr) + explicit Lambda () noexcept : Lambda (nullptr) { } Lambda (decltype (nullptr)) noexcept : m_functor (nullptr), m_smallObject (false) @@ -124,7 +124,7 @@ public: template Lambda (F function) { if (cr::fix (sizeof (function) > LamdaSmallBufferLength)) { m_smallObject = false; - new (m_small) UniquePtr (createUniqueBase , LambdaFunctorWrapper> (cr::move (function))); + new (m_small) UniquePtr (makeUnique > (cr::move (function))); } else { m_smallObject = true; diff --git a/include/crlib/cr-library.h b/include/crlib/cr-library.h index 600ed8b..612cd1d 100644 --- a/include/crlib/cr-library.h +++ b/include/crlib/cr-library.h @@ -18,12 +18,8 @@ # include # include # include -#elif defined (CR_WINDOWS) -# define WIN32_LEAN_AND_MEAN -# include #endif - CR_NAMESPACE_BEGIN // handling dynamic library loading @@ -47,6 +43,10 @@ public: public: bool load (const String &file) noexcept { + if (*this) { + unload (); + } + #if defined (CR_WINDOWS) m_handle = LoadLibraryA (file.chars ()); #else diff --git a/include/crlib/cr-logger.h b/include/crlib/cr-logger.h index 0bab998..03b81b5 100644 --- a/include/crlib/cr-logger.h +++ b/include/crlib/cr-logger.h @@ -14,11 +14,6 @@ #include #include -#if defined (CR_WINDOWS) -# define WIN32_LEAN_AND_MEAN -# include -#endif - CR_NAMESPACE_BEGIN class SimpleLogger final : public Singleton { diff --git a/include/crlib/cr-platform.h b/include/crlib/cr-platform.h index aafe0d9..368bc9c 100644 --- a/include/crlib/cr-platform.h +++ b/include/crlib/cr-platform.h @@ -70,6 +70,15 @@ CR_NAMESPACE_END # define WIN32_LEAN_AND_MEAN # include # include + +# if defined (max) +# undef max +# endif + +# if defined (min) +# undef min +# endif + #else # include # include @@ -77,8 +86,10 @@ CR_NAMESPACE_END # include #endif +#include #include #include +#include #if defined (CR_ANDROID) # include @@ -88,42 +99,42 @@ CR_NAMESPACE_BEGIN // helper struct for platform detection struct Platform : public Singleton { - bool isWindows = false; - bool isLinux = false; - bool isOSX = false; - bool isAndroid = false; - bool isAndroidHardFP = false; - bool isX64 = false; - bool isArm = false; + bool win32 = false; + bool linux = false; + bool osx = false; + bool android = false; + bool hfp = false; + bool x64 = false; + bool arm = false; Platform () { #if defined(CR_WINDOWS) - isWindows = true; + win32 = true; #endif #if defined(CR_ANDROID) - isAndroid = true; + android = true; # if defined (CR_ANDROID_HARD_FP) - isAndroidHardFP = true; + hfp = true; # endif #endif #if defined(CR_LINUX) - isLinux = true; + linux = true; #endif #if defined(CR_OSX) - isOSX = true; + osx = true; #endif #if defined(CR_ARCH_X64) || defined(CR_ARCH_ARM64) - isX64 = true; + x64 = true; #endif #if defined(CR_ARCH_ARM) - isArm = true; - isAndroid = true; + arm = true; + android = true; #endif } diff --git a/include/crlib/cr-string.h b/include/crlib/cr-string.h index 3442c3f..0970da0 100644 --- a/include/crlib/cr-string.h +++ b/include/crlib/cr-string.h @@ -20,6 +20,38 @@ CR_NAMESPACE_BEGIN +namespace detail { + template uint8 &getMostSignificantByte (T &object) { + return *(reinterpret_cast (&object) + sizeof (object) - 1); + } + + template bool getLeastSignificantBit (uint8 byte) { + return byte & cr::bit (N); + } + + template bool getMostSignificantBit (uint8 byte) { + return byte & cr::bit (CHAR_BIT - N - 1); + } + + template void setLeastSignificantBit (uint8 &byte, bool bit) { + if (bit) { + byte |= cr::bit (N); + } + else { + byte &= ~cr::bit (N); + } + } + + template void setMostSignificantBit (uint8 &byte, bool bit) { + if (bit) { + byte |= cr::bit (CHAR_BIT - N - 1); + } + else { + byte &= ~cr::bit (CHAR_BIT - N - 1); + } + } +} + // small-string optimized string class, sso stuff based on: https://github.com/elliotgoodrich/SSO-23/ class String final { public: @@ -30,7 +62,6 @@ public: private: enum : size_t { ExcessSpace = 32, - CharBit = CHAR_BIT }; private: @@ -77,8 +108,7 @@ public: assign (ch); } - String (String &&rhs) noexcept { - m_data = rhs.m_data; + String (String &&rhs) noexcept : m_data (rhs.m_data) { rhs.setMoved (); } @@ -87,35 +117,6 @@ public: } private: - template static uint8 &getMostSignificantByte (T &object) { - return *(reinterpret_cast (&object) + sizeof (object) - 1); - } - - template static bool getLeastSignificantBit (uint8 byte) { - return byte & cr::bit (N); - } - - template static bool getMostSignificantBit (uint8 byte) { - return byte & cr::bit (CharBit - N - 1); - } - - template static void setLeastSignificantBit (uint8 &byte, bool bit) { - if (bit) { - byte |= cr::bit (N); - } - else { - byte &= ~cr::bit (N); - } - } - - template static void setMostSignificantBit (uint8 &byte, bool bit) { - if (bit) { - byte |= cr::bit (CharBit - N - 1); - } - else { - byte &= ~cr::bit (CharBit - N - 1); - } - } void destroy () { if (!isSmall ()) { @@ -162,7 +163,7 @@ private: } bool isSmall () const { - return !getLeastSignificantBit <0> (m_data.small.length) && !getLeastSignificantBit <1> (m_data.small.length); + return !detail::getLeastSignificantBit <0> (m_data.small.length) && !detail::getLeastSignificantBit <1> (m_data.small.length); } void setSmallLength (uint8 length) { @@ -174,18 +175,18 @@ private: } void setDataNonSmall (size_t length, size_t capacity) { - uint8 &lengthHighByte = getMostSignificantByte (length); - uint8 &capacityHighByte = getMostSignificantByte (capacity); + uint8 &lengthHighByte = detail::getMostSignificantByte (length); + uint8 &capacityHighByte = detail::getMostSignificantByte (capacity); - const bool lengthHasHighBit = getMostSignificantBit <0> (lengthHighByte); - const bool capacityHasHighBit = getMostSignificantBit <0> (capacityHighByte); - const bool capacityHasSecHighBit = getMostSignificantBit <1> (capacityHighByte); + const bool lengthHasHighBit = detail::getMostSignificantBit <0> (lengthHighByte); + const bool capacityHasHighBit = detail::getMostSignificantBit <0> (capacityHighByte); + const bool capacityHasSecHighBit = detail::getMostSignificantBit <1> (capacityHighByte); - setMostSignificantBit <0> (lengthHighByte, capacityHasSecHighBit); + detail::setMostSignificantBit <0> (lengthHighByte, capacityHasSecHighBit); capacityHighByte <<= 2; - setLeastSignificantBit <0> (capacityHighByte, capacityHasHighBit); - setLeastSignificantBit <1> (capacityHighByte, !lengthHasHighBit); + detail::setLeastSignificantBit <0> (capacityHighByte, capacityHasHighBit); + detail::setLeastSignificantBit <1> (capacityHighByte, !lengthHasHighBit); m_data.big.length = length; m_data.big.capacity = capacity; @@ -195,18 +196,18 @@ private: size_t length = m_data.big.length; size_t capacity = m_data.big.capacity; - uint8 &lengthHighByte = getMostSignificantByte (length); - uint8 &capacityHighByte = getMostSignificantByte (capacity); + uint8 &lengthHighByte = detail::getMostSignificantByte (length); + uint8 &capacityHighByte = detail::getMostSignificantByte (capacity); - const bool capacityHasHighBit = getLeastSignificantBit <0> (capacityHighByte); - const bool lengthHasHighBit = !getLeastSignificantBit <1> (capacityHighByte); - const bool capacityHasSecHighBit = getMostSignificantBit <0> (lengthHighByte); + const bool capacityHasHighBit = detail::getLeastSignificantBit <0> (capacityHighByte); + const bool lengthHasHighBit = !detail::getLeastSignificantBit <1> (capacityHighByte); + const bool capacityHasSecHighBit = detail::getMostSignificantBit <0> (lengthHighByte); - setMostSignificantBit <0> (lengthHighByte, lengthHasHighBit); + detail::setMostSignificantBit <0> (lengthHighByte, lengthHasHighBit); capacityHighByte >>= 2; - setMostSignificantBit <0> (capacityHighByte, capacityHasHighBit); - setMostSignificantBit <1> (capacityHighByte, capacityHasSecHighBit); + detail::setMostSignificantBit <0> (capacityHighByte, capacityHasHighBit); + detail::setMostSignificantBit <1> (capacityHighByte, capacityHasSecHighBit); return { length, capacity }; } diff --git a/include/crlib/cr-twin.h b/include/crlib/cr-twin.h index ff53dd8..269efa1 100644 --- a/include/crlib/cr-twin.h +++ b/include/crlib/cr-twin.h @@ -15,7 +15,7 @@ CR_NAMESPACE_BEGIN // simple pair (twin) -template class Twin { +template class Twin final { public: A first; B second; @@ -55,13 +55,4 @@ public: } }; -// creating pairs -template constexpr Twin makeTwin (A &&a, B &&b) { - return Twin (cr::forward (a), cr::forward (b)); -} - -template constexpr Twin makeTwin (const A &a, const B &b) { - return Twin (a, b); -} - CR_NAMESPACE_END \ No newline at end of file diff --git a/include/crlib/cr-ulz.h b/include/crlib/cr-ulz.h index 290e125..96a5e65 100644 --- a/include/crlib/cr-ulz.h +++ b/include/crlib/cr-ulz.h @@ -41,10 +41,11 @@ private: SmallArray m_prevTable; public: - ULZ () { + explicit ULZ () { m_hashTable.resize (HashLength); m_prevTable.resize (WindowSize); } + ~ULZ () = default; public: diff --git a/include/crlib/cr-uniqueptr.h b/include/crlib/cr-uniqueptr.h index 3e6990b..c88fa74 100644 --- a/include/crlib/cr-uniqueptr.h +++ b/include/crlib/cr-uniqueptr.h @@ -21,12 +21,15 @@ private: public: UniquePtr () = default; + explicit UniquePtr (T *ptr) : m_ptr (ptr) { } - UniquePtr (UniquePtr &&rhs) noexcept : m_ptr (rhs.m_ptr) { - rhs.m_ptr = nullptr; - } + UniquePtr (UniquePtr &&rhs) noexcept : m_ptr (rhs.release ()) + { } + + template UniquePtr (UniquePtr &&rhs) noexcept : m_ptr (rhs.release ()) + { } ~UniquePtr () { destroy (); @@ -60,10 +63,14 @@ private: public: UniquePtr &operator = (UniquePtr &&rhs) noexcept { if (this != &rhs) { - destroy (); + reset (rhs.release ()); + } + return *this; + } - m_ptr = rhs.m_ptr; - rhs.m_ptr = nullptr; + template UniquePtr &operator = (UniquePtr &&rhs) noexcept { + if (this != &rhs) { + reset (rhs.release ()); } return *this; } @@ -87,13 +94,8 @@ public: }; // create unique -template UniquePtr createUnique (Args &&... args) { +template UniquePtr makeUnique (Args &&... args) { return UniquePtr (alloc.create (cr::forward (args)...)); } -// create unique (base class) -template UniquePtr createUniqueBase (Args &&... args) { - return UniquePtr (alloc.create (cr::forward (args)...)); -} - CR_NAMESPACE_END \ No newline at end of file diff --git a/include/engine.h b/include/engine.h index 194958a..096157d 100644 --- a/include/engine.h +++ b/include/engine.h @@ -65,7 +65,7 @@ CR_DECLARE_SCOPED_ENUM (MapFlags, CR_DECLARE_SCOPED_ENUM (EntitySearchResult, Continue, Break -); +) // variable reg pair struct VarPair { @@ -82,6 +82,31 @@ struct VarPair { // entity prototype using EntityFunction = void (*) (entvars_t *); +// rehlds has this fixed, but original hlds doesn't allocate string space passed to precache* argument, so game will crash when unloading module using metamod +class EngineWrap final : public DenyCopying { +public: + EngineWrap () = default; + ~EngineWrap () = default; + +private: + const char *allocStr (const char *str) const { + return STRING (engfuncs.pfnAllocString (str)); + } + +public: + int32 precacheModel (const char *model) const { + return engfuncs.pfnPrecacheModel (allocStr (model)); + } + + int32 precacheSound (const char *sound) const { + return engfuncs.pfnPrecacheSound (allocStr (sound)); + } + + void setModel (edict_t *ent, const char *model) { + engfuncs.pfnSetModel (ent, allocStr (model)); + } +}; + // provides utility functions to not call original engine (less call-cost) class Game final : public Singleton { public: @@ -101,6 +126,7 @@ private: Array m_breakables; SmallArray m_cvars; SharedLibrary m_gameLib; + EngineWrap m_engineWrap; bool m_precached; int m_gameFlags; @@ -621,7 +647,7 @@ public: public: void initialize () { - if (plat.isArm) { + if (plat.arm) { return; } m_dlsym.patch (reinterpret_cast (&LookupSymbol), reinterpret_cast (&DynamicEntityLink::replacement)); diff --git a/include/engine/const.h b/include/engine/const.h index 197e77b..99c240e 100644 --- a/include/engine/const.h +++ b/include/engine/const.h @@ -19,6 +19,24 @@ // This header file included by engine files and DLL files. // Most came from server.h +#define INTERFACE_VERSION 140 + +// Dot products for view cone checking +#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees +#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks +#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks +#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks + +#define FCVAR_ARCHIVE (1 << 0) // set to cause it to be saved to vars.rc +#define FCVAR_USERINFO (1 << 1) // changes the client's info string +#define FCVAR_SERVER (1 << 2) // notifies players when changed +#define FCVAR_EXTDLL (1 << 3) // defined by external DLL +#define FCVAR_CLIENTDLL (1 << 4) // defined by the client dll +#define FCVAR_PROTECTED (1 << 5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value +#define FCVAR_SPONLY (1 << 6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_PRINTABLEONLY (1 << 7) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). +#define FCVAR_UNLOGGED (1 << 8) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log + // edict->flags #define FL_FLY (1 << 0) // Changes the SV_Movestep() behavior to not need to be on ground #define FL_SWIM (1 << 1) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) @@ -667,6 +685,122 @@ #define TE_BOUNCE_SHELL 1 #define TE_BOUNCE_SHOTSHELL 2 +#define MAX_ENT_LEAFS 48 + +#define MAX_WEAPON_SLOTS 5 // hud item selection slots +#define MAX_ITEM_TYPES 6 // hud item selection slots + +#define MAX_ITEMS 5 // hard coded item types + +#define HIDEHUD_WEAPONS (1 << 0) +#define HIDEHUD_FLASHLIGHT (1 << 1) +#define HIDEHUD_ALL (1 << 2) +#define HIDEHUD_HEALTH (1 << 3) + +#define MAX_AMMO_TYPES 32 // ??? +#define MAX_AMMO_SLOTS 32 // not really slots + +#define HUD_PRINTNOTIFY 1 +#define HUD_PRINTCONSOLE 2 +#define HUD_PRINTTALK 3 +#define HUD_PRINTCENTER 4 + +#define WEAPON_SUIT 31 + +#define VERTEXSIZE 7 +#define MAXLIGHTMAPS 4 +#define NUM_AMBIENTS 4 +#define MAX_MAP_HULLS 4 +#define MAX_PHYSINFO_STRING 256 +#define MAX_PHYSENTS 600 +#define MAX_MOVEENTS 64 +#define MAX_LIGHTSTYLES 64 +#define MAX_LIGHTSTYLEVALUE 256 +#define SURF_DRAWTILED 0x20 + + +#define AMBIENT_SOUND_STATIC 0 // medium radius attenuation +#define AMBIENT_SOUND_EVERYWHERE 1 +#define AMBIENT_SOUND_SMALLRADIUS 2 +#define AMBIENT_SOUND_MEDIUMRADIUS 4 +#define AMBIENT_SOUND_LARGERADIUS 8 +#define AMBIENT_SOUND_START_SILENT 16 +#define AMBIENT_SOUND_NOT_LOOPING 32 + +#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements + +#define SND_SPAWNING (1 << 8) // duplicated in protocol.h we're spawing, used in some cases for ambients +#define SND_STOP (1 << 5) // duplicated in protocol.h stop sound +#define SND_CHANGE_VOL (1 << 6) // duplicated in protocol.h change sound vol +#define SND_CHANGE_PITCH (1 << 7) // duplicated in protocol.h change sound pitch + +#define LFO_SQUARE 1 +#define LFO_TRIANGLE 2 +#define LFO_RANDOM 3 + +// func_rotating +#define SF_BRUSH_ROTATE_Y_AXIS 0 +#define SF_BRUSH_ROTATE_INSTANT 1 +#define SF_BRUSH_ROTATE_BACKWARDS 2 +#define SF_BRUSH_ROTATE_Z_AXIS 4 +#define SF_BRUSH_ROTATE_X_AXIS 8 +#define SF_PENDULUM_AUTO_RETURN 16 +#define SF_PENDULUM_PASSABLE 32 + +#define SF_BRUSH_ROTATE_SMALLRADIUS 128 +#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 +#define SF_BRUSH_ROTATE_LARGERADIUS 512 + +#define PUSH_BLOCK_ONLY_X 1 +#define PUSH_BLOCK_ONLY_Y 2 + +#define VEC_HULL_MIN Vector(-16, -16, -36) +#define VEC_HULL_MAX Vector(16, 16, 36) +#define VEC_HUMAN_HULL_MIN Vector(-16, -16, 0) +#define VEC_HUMAN_HULL_MAX Vector(16, 16, 72) +#define VEC_HUMAN_HULL_DUCK Vector(16, 16, 36) + +#define VEC_VIEW Vector(0, 0, 28) + +#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18) +#define VEC_DUCK_HULL_MAX Vector(16, 16, 18) +#define VEC_DUCK_VIEW Vector(0, 0, 12) + +#define SVC_TEMPENTITY 23 +#define SVC_CENTERPRINT 26 +#define SVC_INTERMISSION 30 +#define SVC_CDTRACK 32 +#define SVC_WEAPONANIM 35 +#define SVC_ROOMTYPE 37 +#define SVC_DIRECTOR 51 + +// triggers +#define SF_TRIGGER_ALLOWMONSTERS 1 // monsters allowed to fire this trigger +#define SF_TRIGGER_NOCLIENTS 2 // players not allowed to fire this trigger +#define SF_TRIGGER_PUSHABLES 4 // only pushables can fire this trigger + +// func breakable +#define SF_BREAK_TRIGGER_ONLY 1 // may only be broken by trigger +#define SF_BREAK_TOUCH 2 // can be 'crashed through' by running player (plate glass) +#define SF_BREAK_PRESSURE 4 // can be broken by a player standing on it +#define SF_BREAK_CROWBAR 256 // instant break if hit with crowbar + +// func_pushable (it's also func_breakable, so don't collide with those flags) +#define SF_PUSH_BREAKABLE 128 + +#define SF_LIGHT_START_OFF 1 + +#define SPAWNFLAG_NOMESSAGE 1 +#define SPAWNFLAG_NOTOUCH 1 +#define SPAWNFLAG_DROIDONLY 4 + +#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) + +#define TELE_PLAYER_ONLY 1 +#define TELE_SILENT 2 + +#define SF_TRIG_PUSH_ONCE 1 + // Rendering constants enum { kRenderNormal, // src @@ -701,29 +835,46 @@ enum { kRenderFxClampMinScale // Keep this sprite from getting very small (SPRITES only!) }; -typedef int func_t; -typedef int string_t; +typedef enum { + ignore_monsters = 1, + dont_ignore_monsters = 0, + missile = 2 +} IGNORE_MONSTERS; -typedef struct link_s { - struct link_s *prev, *next; -} link_t; +typedef enum { + ignore_glass = 1, + dont_ignore_glass = 0 +} IGNORE_GLASS; -typedef struct edict_s edict_t; +typedef enum { + point_hull = 0, + human_hull = 1, + large_hull = 2, + head_hull = 3 +} HULL; -typedef struct { - vec3_t normal; - float dist; -} plane_t; +typedef enum { + at_notice, + at_console, // same as at_notice, but forces a ConPrintf, not a message box + at_aiconsole, // same as at_console, but only shown if developer level is 2! + at_warning, + at_error, + at_logged // Server print to console ( only in multiplayer games ). +} ALERT_TYPE; + +// 4-22-98 JOHN: added for use in pfnClientPrintf +typedef enum { + print_console, + print_center, + print_chat +} PRINT_TYPE; + +// For integrity checking of content on clients +typedef enum { + force_exactfile, // File on client must exactly match server's file + force_model_samebounds, // For model files only, the geometry must fit in the same bbox + force_model_specifybounds // For model files only, the geometry must fit in the specified bbox +} FORCE_TYPE; -typedef struct { - int allsolid; // if true, plane is not valid - int startsolid; // if true, the initial point was in a solid area - int inopen, inwater; - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - plane_t plane; // surface normal at impact - edict_t *ent; // entity the surface is on - int hitgroup; // 0 == generic, non zero is specific body part -} trace_t; #endif diff --git a/include/engine/eiface.h b/include/engine/eiface.h index 8e0bd8a..68d72b5 100644 --- a/include/engine/eiface.h +++ b/include/engine/eiface.h @@ -12,22 +12,7 @@ * use or distribution of this code by or to any unlicensed person is illegal. * ****/ -#ifndef EIFACE_H -#define EIFACE_H - -#define INTERFACE_VERSION 140 - -#include - -#define FCVAR_ARCHIVE (1 << 0) // set to cause it to be saved to vars.rc -#define FCVAR_USERINFO (1 << 1) // changes the client's info string -#define FCVAR_SERVER (1 << 2) // notifies players when changed -#define FCVAR_EXTDLL (1 << 3) // defined by external DLL -#define FCVAR_CLIENTDLL (1 << 4) // defined by the client dll -#define FCVAR_PROTECTED (1 << 5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value -#define FCVAR_SPONLY (1 << 6) // This cvar cannot be changed by clients connected to a multiplayer server. -#define FCVAR_PRINTABLEONLY (1 << 7) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). -#define FCVAR_UNLOGGED (1 << 8) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log +#pragma once struct cvar_t { char *name; @@ -37,39 +22,18 @@ struct cvar_t { cvar_t *next; }; -// -// Defines entity interface between engine and DLLs. -// This header file included by engine files and DLL files. -// -// Before including this header, DLLs must: -// include progdefs.h -// This is conveniently done for them in extdll.h -// - -#ifdef _WIN32 -#define DLLEXPORT __stdcall -#else -#define DLLEXPORT /* */ -#endif - -typedef enum { - at_notice, - at_console, // same as at_notice, but forces a ConPrintf, not a message box - at_aiconsole, // same as at_console, but only shown if developer level is 2! - at_warning, - at_error, - at_logged // Server print to console ( only in multiplayer games ). -} ALERT_TYPE; - -// 4-22-98 JOHN: added for use in pfnClientPrintf -typedef enum { print_console, print_center, print_chat } PRINT_TYPE; - -// For integrity checking of content on clients -typedef enum { - force_exactfile, // File on client must exactly match server's file - force_model_samebounds, // For model files only, the geometry must fit in the same bbox - force_model_specifybounds // For model files only, the geometry must fit in the specified bbox -} FORCE_TYPE; +typedef struct hudtextparms_s { + float x; + float y; + int effect; + uint8 r1, g1, b1, a1; + uint8 r2, g2, b2, a2; + float fadeinTime; + float fadeoutTime; + float holdTime; + float fxTime; + int channel; +} hudtextparms_t; // Returned by TraceLine typedef struct { @@ -109,8 +73,8 @@ typedef uint32 CRC32_t; // Engine hands this to DLLs for functionality callbacks typedef struct enginefuncs_s { - int (*pfnPrecacheModel) (char *s); - int (*pfnPrecacheSound) (char *s); + int (*pfnPrecacheModel) (const char *s); + int (*pfnPrecacheSound) (const char *s); void (*pfnSetModel) (edict_t *e, const char *m); int (*pfnModelIndex) (const char *m); int (*pfnModelFrames) (int modelIndex); @@ -375,9 +339,6 @@ typedef struct { int (*pfnAllowLagCompensation) (); } gamefuncs_t; -// Current version. -#define NEWGAMEDLLFUNCS_VERSION 1 - typedef struct { // Called right before the object's memory is freed. // Calls its destructor. @@ -388,5 +349,3 @@ typedef struct { void (*pfnCvarValue) (const edict_t *pEnt, const char *value); void (*pfnCvarValue2) (const edict_t *pEnt, int requestID, const char *cvarName, const char *value); } newgamefuncs_t; - -#endif /* EIFACE_H */ diff --git a/include/engine/extdll.h b/include/engine/extdll.h index 9a73784..9303545 100644 --- a/include/engine/extdll.h +++ b/include/engine/extdll.h @@ -16,54 +16,58 @@ #ifndef EXTDLL_H #define EXTDLL_H -#ifdef _MSC_VER -/* disable deprecation warnings concerning unsafe CRT functions */ -#if !defined _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#endif - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOWINRES -#define NOSERVICE -#define NOMCX -#define NOIME -#include "windows.h" -#include "winsock2.h" -#else // _WIN32 -#define FALSE 0 -#define TRUE (!FALSE) -#define MAX_PATH PATH_MAX -#include -#include -#ifndef min -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef max -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#define _vsnprintf(a, b, c, d) vsnprintf (a, b, c, d) -#endif -#endif //_WIN32 - -#include "stdio.h" -#include "stdlib.h" typedef int func_t; // -typedef int string_t; // from engine's pr_comp.h; typedef float vec_t; // needed before including progdefs.h +typedef vec_t vec2_t[2]; +typedef vec_t vec4_t[4]; +typedef int qboolean; +typedef unsigned char byte; #include +// idea from regamedll +class string_t final { +private: + int offset; + +public: + explicit string_t () : offset (0) { } + string_t (int offset) : offset (offset) { } + + ~string_t () = default; + +public: + const char *chars () const; + +public: + operator int () const { + return offset; + } + + string_t &operator = (const string_t &rhs) { + offset = rhs.offset; + return *this; + } +}; + typedef cr::Vector vec3_t; using namespace cr::types; +typedef struct edict_s edict_t; + #include "const.h" #include "progdefs.h" #include "model.h" -#define MAX_ENT_LEAFS 48 +typedef struct link_s { + struct link_s *prev, *next; +} link_t; + +typedef struct { + vec3_t normal; + float dist; +} plane_t; struct edict_s { int free; @@ -79,24 +83,29 @@ struct edict_s { #include "eiface.h" -#define MAX_WEAPON_SLOTS 5 // hud item selection slots -#define MAX_ITEM_TYPES 6 // hud item selection slots +extern globalvars_t *globals; +extern enginefuncs_t engfuncs; +extern gamefuncs_t dllapi; -#define MAX_ITEMS 5 // hard coded item types +// Use this instead of ALLOC_STRING on constant strings +#define STRING(offset) (const char *)(globals->pStringBase + (int)offset) -#define HIDEHUD_WEAPONS (1 << 0) -#define HIDEHUD_FLASHLIGHT (1 << 1) -#define HIDEHUD_ALL (1 << 2) -#define HIDEHUD_HEALTH (1 << 3) +// form fwgs-hlsdk +#if defined (CR_ARCH_X64) +static inline int MAKE_STRING (const char *val) { + long long ptrdiff = val - STRING (0); -#define MAX_AMMO_TYPES 32 // ??? -#define MAX_AMMO_SLOTS 32 // not really slots + if (ptrdiff > INT_MAX || ptrdiff < INT_MIN) { + return engfuncs.pfnAllocString (val); + } + return static_cast (ptrdiff); +} +#else +#define MAKE_STRING(str) ((uint64)(str) - (uint64)(STRING(0))) +#endif -#define HUD_PRINTNOTIFY 1 -#define HUD_PRINTCONSOLE 2 -#define HUD_PRINTTALK 3 -#define HUD_PRINTCENTER 4 - -#define WEAPON_SUIT 31 +inline const char *string_t::chars () const { + return STRING (offset); +} #endif // EXTDLL_H diff --git a/include/engine/metamod.h b/include/engine/metamod.h new file mode 100644 index 0000000..cbe9814 --- /dev/null +++ b/include/engine/metamod.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2001-2006 Will Day + * See the file "dllapi.h" in this folder for full information + */ + +// Simplified version by Wei Mingzhi + +#ifndef METAMOD_H +#define METAMOD_H + +typedef enum { + PNL_NULL = 0, + PNL_INI_DELETED, + PNL_FILE_NEWER, + PNL_COMMAND, + PNL_CMD_FORCED, + PNL_DELAYED, + PNL_PLUGIN, + PNL_PLG_FORCED, + PNL_RELOAD +} PL_UNLOAD_REASON; + +typedef enum { + MRES_UNSET = 0, + MRES_IGNORED, + MRES_HANDLED, + MRES_OVERRIDE, + MRES_SUPERCEDE +} META_RES; + +typedef enum { + PT_NEVER = 0, + PT_STARTUP, + PT_CHANGELEVEL, + PT_ANYTIME, + PT_ANYPAUSE +} PLUG_LOADTIME; + +// for getgameinfo: +typedef enum { + GINFO_NAME = 0, + GINFO_DESC, + GINFO_GAMEDIR, + GINFO_DLL_FULLPATH, + GINFO_DLL_FILENAME, + GINFO_REALDLL_FULLPATH +} ginfo_t; + + +typedef int (*GETENTITYAPI_FN) (gamefuncs_t *pFunctionTable, int interfaceVersion); +typedef int (*GETENTITYAPI2_FN) (gamefuncs_t *pFunctionTable, int *interfaceVersion); +typedef int (*GETNEWDLLFUNCTIONS_FN) (newgamefuncs_t *pFunctionTable, int *interfaceVersion); +typedef int (*GET_ENGINE_FUNCTIONS_FN) (enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion); + +#define META_INTERFACE_VERSION "5:13" + +typedef struct { + char const *ifvers; + char const *name; + char const *version; + char const *date; + char const *author; + char const *url; + char const *logtag; + PLUG_LOADTIME loadable; + PLUG_LOADTIME unloadable; +} plugin_info_t; + +extern plugin_info_t Plugin_info; +typedef plugin_info_t *plid_t; + +#define PLID &Plugin_info + +typedef struct meta_globals_s { + META_RES mres; + META_RES prev_mres; + META_RES status; + void *orig_ret; + void *override_ret; +} meta_globals_t; + +extern meta_globals_t *gpMetaGlobals; + +#define RETURN_META(result) \ + { \ + gpMetaGlobals->mres = result; \ + return; \ + } + +#define RETURN_META_VALUE(result, value) \ + { \ + gpMetaGlobals->mres = result; \ + return (value); \ + } + +#define META_RESULT_STATUS gpMetaGlobals->status +#define META_RESULT_PREVIOUS gpMetaGlobals->prev_mres +#define META_RESULT_ORIG_RET(type) *(type *)gpMetaGlobals->orig_ret +#define META_RESULT_OVERRIDE_RET(type) *(type *)gpMetaGlobals->override_ret + +typedef struct { + GETENTITYAPI_FN pfnGetEntityAPI; + GETENTITYAPI_FN pfnGetEntityAPI_Post; + GETENTITYAPI2_FN pfnGetEntityAPI2; + GETENTITYAPI2_FN pfnGetEntityAPI2_Post; + GETNEWDLLFUNCTIONS_FN pfnGetNewDLLFunctions; + GETNEWDLLFUNCTIONS_FN pfnGetNewDLLFunctions_Post; + GET_ENGINE_FUNCTIONS_FN pfnGetEngineFunctions; + GET_ENGINE_FUNCTIONS_FN pfnGetEngineFunctions_Post; +} metamod_funcs_t; + +// max buffer size for printed messages +#define MAX_LOGMSG_LEN 1024 + + +// Meta Utility Function table type. +typedef struct meta_util_funcs_s { + void (*pfnLogConsole) (plid_t plid, const char *szFormat, ...); + void (*pfnLogMessage) (plid_t plid, const char *szFormat, ...); + void (*pfnLogError) (plid_t plid, const char *szFormat, ...); + void (*pfnLogDeveloper) (plid_t plid, const char *szFormat, ...); + void (*pfnCenterSay) (plid_t plid, const char *szFormat, ...); + void (*pfnCenterSayParms) (plid_t plid, hudtextparms_t tparms, const char *szFormat, ...); + void (*pfnCenterSayVarargs) (plid_t plid, hudtextparms_t tparms, const char *szFormat, va_list ap); + int (*pfnCallGameEntity) (plid_t plid, const char *entStr, entvars_t *pev); + int (*pfnGetUserMsgID) (plid_t plid, const char *msgname, int *size); + const char *(*pfnGetUserMsgName) (plid_t plid, int msgid, int *size); + const char *(*pfnGetPluginPath) (plid_t plid); + const char *(*pfnGetGameInfo) (plid_t plid, ginfo_t tag); + int (*pfnLoadPlugin) (plid_t plid, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); + int (*pfnUnloadPlugin) (plid_t plid, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + int (*pfnUnloadPluginByHandle) (plid_t plid, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + const char *(*pfnIsQueryingClienCVar_t) (plid_t plid, const edict_t *player); + int (*pfnMakeRequestID) (plid_t plid); + void (*pfnGetHookTables) (plid_t plid, enginefuncs_t **peng, gamefuncs_t **pdll, newgamefuncs_t **pnewdll); +} mutil_funcs_t; + +typedef struct { + gamefuncs_t *dllapi_table; + newgamefuncs_t *newapi_table; +} gamedll_funcs_t; + +extern gamedll_funcs_t *gpGamedllFuncs; +extern mutil_funcs_t *gpMetaUtilFuncs; +extern metamod_funcs_t gMetaFunctionTable; + +#define MDLL_FUNC gpGamedllFuncs->dllapi_table + +#define MDLL_GameDLLInit MDLL_FUNC->pfnGameInit +#define MDLL_Spawn MDLL_FUNC->pfnSpawn +#define MDLL_Think MDLL_FUNC->pfnThink +#define MDLL_Use MDLL_FUNC->pfnUse +#define MDLL_Touch MDLL_FUNC->pfnTouch +#define MDLL_Blocked MDLL_FUNC->pfnBlocked +#define MDLL_KeyValue MDLL_FUNC->pfnKeyValue +#define MDLL_Save MDLL_FUNC->pfnSave +#define MDLL_Restore MDLL_FUNC->pfnRestore +#define MDLL_ObjectCollsionBox MDLL_FUNC->pfnAbsBox +#define MDLL_SaveWriteFields MDLL_FUNC->pfnSaveWriteFields +#define MDLL_SaveReadFields MDLL_FUNC->pfnSaveReadFields +#define MDLL_SaveGlobalState MDLL_FUNC->pfnSaveGlobalState +#define MDLL_RestoreGlobalState MDLL_FUNC->pfnRestoreGlobalState +#define MDLL_ResetGlobalState MDLL_FUNC->pfnResetGlobalState +#define MDLL_ClientConnect MDLL_FUNC->pfnClientConnect +#define MDLL_ClientDisconnect MDLL_FUNC->pfnClientDisconnect +#define MDLL_ClientKill MDLL_FUNC->pfnClientKill +#define MDLL_ClientPutInServer MDLL_FUNC->pfnClientPutInServer +#define MDLL_ClientCommand MDLL_FUNC->pfnClientCommand +#define MDLL_ClientUserInfoChanged MDLL_FUNC->pfnClientUserInfoChanged +#define MDLL_ServerActivate MDLL_FUNC->pfnServerActivate +#define MDLL_ServerDeactivate MDLL_FUNC->pfnServerDeactivate +#define MDLL_PlayerPreThink MDLL_FUNC->pfnPlayerPreThink +#define MDLL_PlayerPostThink MDLL_FUNC->pfnPlayerPostThink +#define MDLL_StartFrame MDLL_FUNC->pfnStartFrame +#define MDLL_ParmsNewLevel MDLL_FUNC->pfnParmsNewLevel +#define MDLL_ParmsChangeLevel MDLL_FUNC->pfnParmsChangeLevel +#define MDLL_GetGameDescription MDLL_FUNC->pfnGetGameDescription +#define MDLL_PlayerCustomization MDLL_FUNC->pfnPlayerCustomization +#define MDLL_SpectatorConnect MDLL_FUNC->pfnSpectatorConnect +#define MDLL_SpectatorDisconnect MDLL_FUNC->pfnSpectatorDisconnect +#define MDLL_SpectatorThink MDLL_FUNC->pfnSpectatorThink +#define MDLL_Sys_Error MDLL_FUNC->pfnSys_Error +#define MDLL_PM_Move MDLL_FUNC->pfnPM_Move +#define MDLL_PM_Init MDLL_FUNC->pfnPM_Init +#define MDLL_PM_FindTextureType MDLL_FUNC->pfnPM_FindTextureType +#define MDLL_SetupVisibility MDLL_FUNC->pfnSetupVisibility +#define MDLL_UpdateClientData MDLL_FUNC->pfnUpdateClientData +#define MDLL_AddToFullPack MDLL_FUNC->pfnAddToFullPack +#define MDLL_CreateBaseline MDLL_FUNC->pfnCreateBaseline +#define MDLL_RegisterEncoders MDLL_FUNC->pfnRegisterEncoders +#define MDLL_GetWeaponData MDLL_FUNC->pfnGetWeaponData +#define MDLL_CmdStart MDLL_FUNC->pfnCmdStart +#define MDLL_CmdEnd MDLL_FUNC->pfnCmdEnd +#define MDLL_ConnectionlessPacket MDLL_FUNC->pfnConnectionlessPacket +#define MDLL_GetHullBounds MDLL_FUNC->pfnGetHullBounds +#define MDLL_CreateInstancedBaselines MDLL_FUNC->pfnCreateInstancedBaselines +#define MDLL_InconsistentFile MDLL_FUNC->pfnInconsistentFile +#define MDLL_AllowLagCompensation MDLL_FUNC->pfnAllowLagCompensation + +#define CALL_GAME_ENTITY (*gpMetaUtilFuncs->pfnCallGameEntity) +#define GET_USER_MSG_ID (*gpMetaUtilFuncs->pfnGetUserMsgID) + +#endif diff --git a/include/engine/model.h b/include/engine/model.h index 006be1d..cfe5e78 100644 --- a/include/engine/model.h +++ b/include/engine/model.h @@ -18,23 +18,6 @@ #ifndef MODEL_H #define MODEL_H -typedef vec_t vec2_t[2]; -typedef vec_t vec4_t[4]; - -typedef int qboolean; -typedef unsigned char byte; - -#define VERTEXSIZE 7 -#define MAXLIGHTMAPS 4 -#define NUM_AMBIENTS 4 -#define MAX_MAP_HULLS 4 -#define MAX_PHYSINFO_STRING 256 -#define MAX_PHYSENTS 600 -#define MAX_MOVEENTS 64 -#define MAX_LIGHTSTYLES 64 -#define MAX_LIGHTSTYLEVALUE 256 -#define SURF_DRAWTILED 0x20 - typedef struct mplane_s { vec3_t normal; float dist; diff --git a/include/engine/progdefs.h b/include/engine/progdefs.h index 5412d27..6310586 100644 --- a/include/engine/progdefs.h +++ b/include/engine/progdefs.h @@ -12,11 +12,7 @@ * use or distribution of this code by or to any unlicensed person is illegal. * ****/ -#ifndef PROGDEFS_H -#define PROGDEFS_H -#ifdef _WIN32 #pragma once -#endif typedef struct { float time; @@ -84,8 +80,8 @@ typedef struct entvars_s { int modelindex; string_t model; - int viewmodel; // player's viewmodel - int weaponmodel; // what other players see + string_t viewmodel; // player's viewmodel + string_t weaponmodel; // what other players see vec3_t absmin; // BB max translated to world coord vec3_t absmax; // BB max translated to world coord @@ -216,4 +212,3 @@ typedef struct entvars_s { edict_t *euser4; } entvars_t; -#endif diff --git a/include/message.h b/include/message.h index cdef77f..d8242d1 100644 --- a/include/message.h +++ b/include/message.h @@ -122,8 +122,11 @@ public: public: int32 add (const String &name, int32 id); + int32 id (NetMsg msg); + void start (edict_t *ent, int32 type); void stop (); + void ensureMessages (); public: template void collect (const T &value) { @@ -137,10 +140,6 @@ public: m_current = NetMsg::None; } - int32 id (NetMsg msg) const { - return m_maps[msg]; - } - private: void reset () { m_current = NetMsg::None; diff --git a/include/utils.h b/include/utils.h index 63380bc..386e53d 100644 --- a/include/utils.h +++ b/include/utils.h @@ -30,6 +30,7 @@ private: SmallArray m_clients; SmallArray > m_tags; + Dictionary > m_weaponAlias; Dictionary m_noiseCache; SimpleHook m_sendToHook; @@ -41,8 +42,8 @@ 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); + // converts weapon id to alias name + const String &weaponIdToAlias (const int32 id); // gets the build number of bot int buildNumber (); diff --git a/include/yapb.h b/include/yapb.h index d678b8d..e09d8e0 100644 --- a/include/yapb.h +++ b/include/yapb.h @@ -10,7 +10,7 @@ #pragma once #include -#include +#include #include @@ -790,7 +790,7 @@ private: void findShortestPath (int srcIndex, int destIndex); void findPath (int srcIndex, int destIndex, FindPath pathType = FindPath::Fast); void clearRoute (); - void sayDebug (const char *format, ...); + void debugMsgInternal (const char *str); void frame (); void resetCollision (); void ignoreCollision (); @@ -1039,13 +1039,23 @@ public: return pev->pContainingEntity; }; + // bots array index int index () const { return m_index; } + // entity index with worldspawn shift int entindex () const { return m_index + 1; } + + // prints debug message + template void debugMsg (const char *fmt, Args ...args) { + debugMsgInternal (strings.format (fmt, cr::forward (args)...)); + } + + // execute client command helper + template void issueCommand (const char *fmt, Args ...args); }; #include @@ -1062,4 +1072,9 @@ extern ConVar yb_ignore_enemies; extern ConVar yb_chat; extern ConVar yb_language; extern ConVar yb_show_latency; -extern ConVar yb_enable_query_hook; \ No newline at end of file +extern ConVar yb_enable_query_hook; + +// execute client command helper +template void Bot::issueCommand (const char * fmt, Args ...args) { + game.botCommand (ent (), strings.format (fmt, cr::forward (args)...)); +} diff --git a/project/makefile b/project/makefile index 0e4c18b..6eb33cd 100644 --- a/project/makefile +++ b/project/makefile @@ -18,7 +18,7 @@ ifeq "$(DEBUG)" "true" COMPILER_FLAGS += -g3 -DCR_DEBUG BINARY_DIR = debug else - COMPILER_FLAGS += -pipe -O3 -march=core2 -msse2 -mfpmath=sse -ffast-math -fno-builtin -fno-threadsafe-statics -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fvisibility-inlines-hidden + COMPILER_FLAGS += -pipe -O2 -march=core2 -msse2 -mfpmath=sse -ffast-math -fno-builtin -fno-threadsafe-statics -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fvisibility-inlines-hidden BINARY_DIR = release endif diff --git a/source/android.cpp b/source/android.cpp index 9941fc5..eaa0724 100644 --- a/source/android.cpp +++ b/source/android.cpp @@ -21,7 +21,7 @@ CR_EXPORT int Server_GetBlendingInterface (int version, struct sv_blending_inter if (!api_GetBlendingInterface) { logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__); - return FALSE; + return false; } return api_GetBlendingInterface (version, ppinterface, pstudio, rotationmatrix, bonetransform); } diff --git a/source/basecode.cpp b/source/basecode.cpp index 2b60da7..71dbc25 100644 --- a/source/basecode.cpp +++ b/source/basecode.cpp @@ -100,7 +100,7 @@ bool Bot::seesItem (const Vector &destination, const char *itemName) { // check if line of sight to object is not blocked (i.e. visible) if (tr.flFraction != 1.0f) { - return strcmp (STRING (tr.pHit->v.classname), itemName) == 0; + return strcmp (tr.pHit->v.classname.chars (), itemName) == 0; } return true; } @@ -330,7 +330,7 @@ void Bot::avoidGrenades () { if (!seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) > pev->fov * 0.5f) { continue; } - auto model = STRING (pent->v.model) + 9; + auto model = pent->v.model.chars () + 9; if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == 4 && strcmp (model, "flashbang.mdl") == 0) { // don't look at flash bang @@ -500,7 +500,7 @@ void Bot::updatePickups () { } if (ent == pickupItem) { - if (seesItem (origin, STRING (ent->v.classname))) { + if (seesItem (origin, ent->v.classname.chars ())) { itemExists = true; } @@ -538,8 +538,8 @@ void Bot::updatePickups () { continue; } - auto classname = STRING (ent->v.classname); - auto model = STRING (ent->v.model) + 9; + auto classname = ent->v.classname.chars (); + auto model = ent->v.model.chars () + 9; // check if line of sight to object is not blocked (i.e. visible) if (seesItem (origin, classname)) { @@ -1089,19 +1089,19 @@ void Bot::checkMsgQueue () { if (m_radioSelect != -1) { if ((m_radioSelect != Radio::ReportingIn && m_forceRadio) || yb_radio_mode.int_ () != 2 || !conf.hasChatterBank (m_radioSelect) || !game.is (GameFlags::HasBotVoice)) { if (m_radioSelect < Radio::GoGoGo) { - game.botCommand (ent (), "radio1"); + issueCommand ("radio1"); } else if (m_radioSelect < Radio::RogerThat) { m_radioSelect -= Radio::GoGoGo - 1; - game.botCommand (ent (), "radio2"); + issueCommand ("radio2"); } else { m_radioSelect -= Radio::RogerThat - 1; - game.botCommand (ent (), "radio3"); + issueCommand ("radio3"); } // select correct menu item for this radio message - game.botCommand (ent (), "menuselect %d", m_radioSelect); + issueCommand ("menuselect %d", m_radioSelect); } else if (m_radioSelect != Radio::ReportingIn) { instantChatter (m_radioSelect); @@ -1136,13 +1136,12 @@ bool Bot::isWeaponRestricted (int weaponIndex) { if (strings.isEmpty (yb_restricted_weapons.str ())) { return isWeaponRestrictedAMX (weaponIndex); // no banned weapons } - auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";"); - - for (auto &ban : bannedWeapons) { - const char *banned = STRING (util.getWeaponAlias (true, nullptr, weaponIndex)); + const auto &bannedWeapons = String (yb_restricted_weapons.str ()).split (";"); + const auto &alias = util.weaponIdToAlias (weaponIndex); + for (const auto &ban : bannedWeapons) { // check is this weapon is banned - if (strncmp (ban.chars (), banned, ban.length ()) == 0) { + if (ban == alias) { return true; } } @@ -1154,7 +1153,7 @@ bool Bot::isWeaponRestrictedAMX (int weaponIndex) { // check for weapon restrictions if (cr::bit (weaponIndex) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) { - const char *restrictedWeapons = engfuncs.pfnCVarGetString ("amx_restrweapons"); + auto restrictedWeapons = engfuncs.pfnCVarGetString ("amx_restrweapons"); if (strings.isEmpty (restrictedWeapons)) { return false; @@ -1173,7 +1172,7 @@ bool Bot::isWeaponRestrictedAMX (int weaponIndex) { // check for equipment restrictions else { - const char *restrictedEquipment = engfuncs.pfnCVarGetString ("amx_restrequipammo"); + auto restrictedEquipment = engfuncs.pfnCVarGetString ("amx_restrequipammo"); if (strings.isEmpty (restrictedEquipment)) { return false; @@ -1202,17 +1201,6 @@ bool Bot::canReplaceWeapon () { return false; } - if (!strings.isEmpty (yb_restricted_weapons.str ())) { - auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";"); - - // check if its banned - for (auto &ban : bannedWeapons) { - if (m_currentWeapon == util.getWeaponAlias (false, ban.chars ())) { - return true; - } - } - } - if (m_currentWeapon == Weapon::Scout && m_moneyAmount > 5000) { return true; } @@ -1222,7 +1210,7 @@ bool Bot::canReplaceWeapon () { else if ((m_currentWeapon == Weapon::M3 || m_currentWeapon == Weapon::XM1014) && m_moneyAmount > 4000) { return true; } - return false; + return isWeaponRestricted (m_currentWeapon); } int Bot::pickBestWeapon (int *vec, int count, int moneySave) { @@ -1458,17 +1446,17 @@ void Bot::buyStuff () { } if (selectedWeapon != nullptr) { - game.botCommand (ent (), "buy;menuselect %d", selectedWeapon->buyGroup); + issueCommand ("buy;menuselect %d", selectedWeapon->buyGroup); if (isOldGame) { - game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelect); + issueCommand ("menuselect %d", selectedWeapon->buySelect); } else { if (m_team == Team::Terrorist) { - game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectT); + issueCommand ("menuselect %d", selectedWeapon->buySelectT); } else { - game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectCT); + issueCommand ("menuselect %d", selectedWeapon->buySelectCT); } } } @@ -1487,10 +1475,10 @@ void Bot::buyStuff () { if (pev->armorvalue < rg.int_ (50, 80) && (isPistolMode || (teamEcoValid && hasPrimaryWeapon ()))) { // if bot is rich, buy kevlar + helmet, else buy a single kevlar if (m_moneyAmount > 1500 && !isWeaponRestricted (Weapon::ArmorHelm)) { - game.botCommand (ent (), "buyequip;menuselect 2"); + issueCommand ("buyequip;menuselect 2"); } else if (!isWeaponRestricted (Weapon::Armor)) { - game.botCommand (ent (), "buyequip;menuselect 1"); + issueCommand ("buyequip;menuselect 1"); } } break; @@ -1552,17 +1540,17 @@ void Bot::buyStuff () { } if (selectedWeapon != nullptr) { - game.botCommand (ent (), "buy;menuselect %d", selectedWeapon->buyGroup); + issueCommand ("buy;menuselect %d", selectedWeapon->buyGroup); if (isOldGame) { - game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelect); + issueCommand ("menuselect %d", selectedWeapon->buySelect); } else { if (m_team == Team::Terrorist) { - game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectT); + issueCommand ("menuselect %d", selectedWeapon->buySelectT); } else { - game.botCommand (ent (), "menuselect %d", selectedWeapon->buySelectCT); + issueCommand ("menuselect %d", selectedWeapon->buySelectCT); } } } @@ -1573,30 +1561,30 @@ void Bot::buyStuff () { // buy a he grenade if (conf.chanceToBuyGrenade (0) && m_moneyAmount >= 400 && !isWeaponRestricted (Weapon::Explosive)) { - game.botCommand (ent (), "buyequip"); - game.botCommand (ent (), "menuselect 4"); + issueCommand ("buyequip"); + issueCommand ("menuselect 4"); } // buy a concussion grenade, i.e., 'flashbang' if (conf.chanceToBuyGrenade (1) && m_moneyAmount >= 300 && teamEcoValid && !isWeaponRestricted (Weapon::Flashbang)) { - game.botCommand (ent (), "buyequip"); - game.botCommand (ent (), "menuselect 3"); + issueCommand ("buyequip"); + issueCommand ("menuselect 3"); } // buy a smoke grenade if (conf.chanceToBuyGrenade (2) && m_moneyAmount >= 400 && teamEcoValid && !isWeaponRestricted (Weapon::Smoke)) { - game.botCommand (ent (), "buyequip"); - game.botCommand (ent (), "menuselect 5"); + issueCommand ("buyequip"); + issueCommand ("menuselect 5"); } break; case BuyState::DefusalKit: // if bot is CT and we're on a bomb map, randomly buy the defuse kit if (game.mapIs (MapFlags::Demolition) && m_team == Team::CT && rg.chance (80) && m_moneyAmount > 200 && !isWeaponRestricted (Weapon::Defuser)) { if (isOldGame) { - game.botCommand (ent (), "buyequip;menuselect 6"); + issueCommand ("buyequip;menuselect 6"); } else { - game.botCommand (ent (), "defuser"); // use alias in steamcs + issueCommand ("defuser"); // use alias in steamcs } } break; @@ -1609,10 +1597,10 @@ void Bot::buyStuff () { // if it's somewhat darkm do buy nightvision goggles if ((skyColor >= 50.0f && lightLevel <= 15.0f) || (skyColor < 50.0f && lightLevel < 40.0f)) { if (isOldGame) { - game.botCommand (ent (), "buyequip;menuselect 7"); + issueCommand ("buyequip;menuselect 7"); } else { - game.botCommand (ent (), "nvgs"); // use alias in steamcs + issueCommand ("nvgs"); // use alias in steamcs } } } @@ -1620,16 +1608,16 @@ void Bot::buyStuff () { case BuyState::Ammo: // buy enough primary & secondary ammo (do not check for money here) for (int i = 0; i <= 5; ++i) { - game.botCommand (ent (), "buyammo%d", rg.int_ (1, 2)); // simulate human + issueCommand ("buyammo%d", rg.int_ (1, 2)); // simulate human } // buy enough secondary ammo if (hasPrimaryWeapon ()) { - game.botCommand (ent (), "buy;menuselect 7"); + issueCommand ("buy;menuselect 7"); } // buy enough primary ammo - game.botCommand (ent (), "buy;menuselect 6"); + issueCommand ("buy;menuselect 6"); // try to reload secondary weapon if (m_reloadState != Reload::Primary) { @@ -2827,10 +2815,10 @@ void Bot::checkDarkness () { pev->impulse = 100; } else if (!m_usesNVG && ((skyColor > 50.0f && m_path->light < 15.0f) || (skyColor <= 50.0f && m_path->light < 40.0f))) { - game.botCommand (ent (), "nightvision"); + issueCommand ("nightvision"); } else if (m_usesNVG && ((m_path->light > 20.0f && skyColor > 50.0f) || (m_path->light > 45.0f && skyColor <= 50.0f))) { - game.botCommand (ent (), "nightvision"); + issueCommand ("nightvision"); } } m_checkDarkTime = game.time (); @@ -2926,7 +2914,7 @@ void Bot::update () { else if (!m_notKilled) { // we got a teamkiller? vote him away... if (m_voteKickIndex != m_lastVoteKick && yb_tkpunish.bool_ ()) { - game.botCommand (ent (), "vote %d", m_voteKickIndex); + issueCommand ("vote %d", m_voteKickIndex); m_lastVoteKick = m_voteKickIndex; // if bot tk punishment is enabled slay the tk @@ -2941,7 +2929,7 @@ void Bot::update () { // host wants us to kick someone else if (m_voteMap != 0) { - game.botCommand (ent (), "votemap %d", m_voteMap); + issueCommand ("votemap %d", m_voteMap); m_voteMap = 0; } } @@ -4386,7 +4374,7 @@ void Bot::pickupItem_ () { auto &info = conf.getWeapons (); for (index = 0; index < 7; ++index) { - if (strcmp (info[index].model, STRING (m_pickupItem->v.model) + 9) == 0) { + if (strcmp (info[index].model, m_pickupItem->v.model.chars () + 9) == 0) { break; } } @@ -4403,10 +4391,10 @@ void Bot::pickupItem_ () { if (wid > 0) { selectWeaponById (wid); - game.botCommand (ent (), "drop"); + issueCommand ("drop"); if (hasShield ()) { - game.botCommand (ent (), "drop"); // discard both shield and pistol + issueCommand ("drop"); // discard both shield and pistol } } enteredBuyZone (BuyState::PrimaryWeapon); @@ -4417,7 +4405,7 @@ void Bot::pickupItem_ () { if (wid == Weapon::Shield || wid > 6 || hasShield ()) { selectWeaponById (wid); - game.botCommand (ent (), "drop"); + issueCommand ("drop"); } if (!wid) { @@ -4448,7 +4436,7 @@ void Bot::pickupItem_ () { if (wid > 6) { selectWeaponById (wid); - game.botCommand (ent (), "drop"); + issueCommand ("drop"); } } break; @@ -4666,7 +4654,7 @@ void Bot::checkSpawnConditions () { if (m_difficulty >= 2 && rg.chance (m_personality == Personality::Rusher ? 99 : 50) && !m_isReloading && game.mapIs (MapFlags::HostageRescue | MapFlags::Demolition | MapFlags::Escape | MapFlags::Assassination)) { if (yb_jasonmode.bool_ ()) { selectSecondary (); - game.botCommand (ent (), "drop"); + issueCommand ("drop"); } else { selectWeaponByName ("weapon_knife"); @@ -4944,9 +4932,9 @@ void Bot::showDebugOverlay () { static float timeDebugUpdate = 0.0f; static int index, goal, taskID; - static Dictionary > tasks; - static Dictionary > personalities; - static Dictionary > flags; + static Dictionary > tasks; + static Dictionary > personalities; + static Dictionary > flags; if (tasks.empty ()) { tasks.push (Task::Normal, "Normal"); @@ -4993,15 +4981,15 @@ void Bot::showDebugOverlay () { String enemy = "(none)"; if (!game.isNullEntity (m_enemy)) { - enemy = STRING (m_enemy->v.netname); + enemy = m_enemy->v.netname.chars (); } else if (!game.isNullEntity (m_lastEnemy)) { - enemy.assignf ("%s (L)", STRING (m_lastEnemy->v.netname)); + enemy.assignf ("%s (L)", m_lastEnemy->v.netname.chars ()); } String pickup = "(none)"; if (!game.isNullEntity (m_pickupItem)) { - pickup = STRING (m_pickupItem->v.netname); + pickup = m_pickupItem->v.netname.chars (); } String aimFlags; @@ -5012,10 +5000,10 @@ void Bot::showDebugOverlay () { aimFlags.appendf (" %s", flags[cr::bit (i)].chars ()); } } - String weapon = STRING (util.getWeaponAlias (true, nullptr, m_currentWeapon)); + const String &weapon = util.weaponIdToAlias (m_currentWeapon); String debugData; - debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s\n", STRING (pev->netname), pev->health, pev->armorvalue, taskID, tasks[taskID].chars (), getTask ()->desire, weapon.chars (), getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim ().chars (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy.chars (), pickup.chars (), personalities[m_personality].chars ()); + debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s\n", pev->netname.chars (), pev->health, pev->armorvalue, taskID, tasks[taskID].chars (), getTask ()->desire, weapon.chars (), getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim ().chars (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy.chars (), pickup.chars (), personalities[m_personality].chars ()); MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, game.getLocalEntity ()) .writeByte (TE_TEXTMESSAGE) @@ -5288,11 +5276,11 @@ void Bot::dropWeaponForUser (edict_t *user, bool discardC4) { if (discardC4) { selectWeaponByName ("weapon_c4"); - game.botCommand (ent (), "drop"); + issueCommand ("drop"); } else { selectBestWeapon (); - game.botCommand (ent (), "drop"); + issueCommand ("drop"); } m_pickupItem = nullptr; @@ -5317,7 +5305,7 @@ void Bot::startDoubleJump (edict_t *ent) { m_doubleJumpEntity = ent; startTask (Task::DoubleJump, TaskPri::DoubleJump, kInvalidNodeIndex, game.time (), true); - sayTeam (strings.format ("Ok %s, i will help you!", STRING (ent->v.netname))); + sayTeam (strings.format ("Ok %s, i will help you!", ent->v.netname.chars ())); } void Bot::resetDoubleJump () { @@ -5330,7 +5318,7 @@ void Bot::resetDoubleJump () { m_jumpReady = false; } -void Bot::sayDebug (const char *format, ...) { +void Bot::debugMsgInternal (const char *str) { if (game.isDedicated ()) { return; } @@ -5339,15 +5327,8 @@ void Bot::sayDebug (const char *format, ...) { if (level <= 2) { return; } - va_list ap; - auto result = strings.chars (); - - va_start (ap, format); - vsnprintf (result, StringBuffer::StaticBufferSize, format, ap); - va_end (ap); - String printBuf; - printBuf.assignf ("%s: %s", STRING (pev->netname), result); + printBuf.assignf ("%s: %s", pev->netname.chars (), str); bool playMessage = false; @@ -5462,7 +5443,7 @@ edict_t *Bot::correctGrenadeVelocity (const char *model) { edict_t *result = nullptr; game.searchEntities ("classname", "grenade", [&] (edict_t *ent) { - if (ent->v.owner == this->ent () && strcmp (STRING (ent->v.model) + 9, model) == 0) { + if (ent->v.owner == this->ent () && strcmp (ent->v.model.chars () + 9, model) == 0) { result = ent; // set the correct velocity for the grenade diff --git a/source/chatlib.cpp b/source/chatlib.cpp index eb4bd6e..fa93de0 100644 --- a/source/chatlib.cpp +++ b/source/chatlib.cpp @@ -152,7 +152,7 @@ void Bot::prepareChatMessage (const String &message) { if (!util.isPlayer (ent)) { return "unknown"; } - String playerName = STRING (ent->v.netname); + String playerName = ent->v.netname.chars (); util.humanizePlayerName (playerName); return playerName; @@ -361,7 +361,7 @@ void Bot::say (const char *text) { if (strings.isEmpty (text) || !yb_chat.bool_ ()) { return; } - game.botCommand (ent (), "say \"%s\"", text); + issueCommand ("say \"%s\"", text); } void Bot::sayTeam (const char *text) { @@ -370,5 +370,5 @@ void Bot::sayTeam (const char *text) { if (strings.isEmpty (text) || !yb_chat.bool_ ()) { return; } - game.botCommand (ent (), "say_team \"%s\"", text); + issueCommand ("say_team \"%s\"", text); } diff --git a/source/combat.cpp b/source/combat.cpp index a5608b4..579b7be 100644 --- a/source/combat.cpp +++ b/source/combat.cpp @@ -1196,7 +1196,7 @@ bool Bot::hasSecondaryWeapon () { bool Bot::hasShield () { // this function returns true, if bot has a tactical shield - return strncmp (STRING (pev->viewmodel), "models/shield/v_shield_", 23) == 0; + return strncmp (pev->viewmodel.chars (), "models/shield/v_shield_", 23) == 0; } bool Bot::isShieldDrawn () { @@ -1216,7 +1216,7 @@ bool Bot::isEnemyBehindShield (edict_t *enemy) { } // check if enemy has shield and this shield is drawn - if ((enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7) && strncmp (STRING (enemy->v.viewmodel), "models/shield/v_shield_", 23) == 0) { + if ((enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7) && strncmp (enemy->v.viewmodel.chars (), "models/shield/v_shield_", 23) == 0) { if (util.isInViewCone (pev->origin, enemy)) { return true; } @@ -1355,7 +1355,7 @@ bool Bot::rateGroundWeapon (edict_t *ent) { auto tab = conf.getRawWeapons (); for (int i = 0; i < kNumWeapons; ++i) { - if (strcmp (tab[*pref].model, STRING (ent->v.model) + 9) == 0) { + if (strcmp (tab[*pref].model, ent->v.model.chars () + 9) == 0) { groundIndex = i; break; } @@ -1464,13 +1464,13 @@ int Bot::bestWeaponCarried () { } void Bot::selectWeaponByName (const char *name) { - game.botCommand (ent (), name); + issueCommand (name); } void Bot::selectWeaponById (int num) { auto tab = conf.getRawWeapons (); - game.botCommand (ent (), tab[num].name); + issueCommand (tab[num].name); } void Bot::decideFollowUser () { diff --git a/source/control.cpp b/source/control.cpp index fdfea2e..1a3a7b2 100644 --- a/source/control.cpp +++ b/source/control.cpp @@ -790,7 +790,7 @@ int BotControl::cmdNodeAcquireEditor () { } if (graph.hasEditor ()) { - msg ("Sorry, players \"%s\" already acquired rights to edit graph on this server.", STRING (graph.getEditor ()->v.netname)); + msg ("Sorry, players \"%s\" already acquired rights to edit graph on this server.", graph.getEditor ()->v.netname.chars ()); return BotCommandResult::Handled; } graph.setEditor (m_ent); @@ -1854,7 +1854,7 @@ void BotControl::kickBotByMenu (int page) { // check for fakeclient bit, since we're clear it upon kick, but actual bot struct destroyed after client disconnected if (bot != nullptr && (bot->pev->flags & FL_FAKECLIENT)) { menuKeys |= cr::bit (cr::abs (i - menuKey)); - menus.appendf ("%1.1d. %s%s\n", i - menuKey + 1, STRING (bot->pev->netname), bot->m_team == Team::CT ? " \\y(CT)\\w" : " \\r(T)\\w"); + menus.appendf ("%1.1d. %s%s\n", i - menuKey + 1, bot->pev->netname.chars (), bot->m_team == Team::CT ? " \\y(CT)\\w" : " \\r(T)\\w"); } else { menus.appendf ("\\d %1.1d. Not a Bot\\w\n", i - menuKey + 1); @@ -1915,13 +1915,13 @@ void BotControl::maintainAdminRights () { } else if (!!strcmp (yb_password.str (), engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast (yb_password_key.str ())))) { client.flags &= ~ClientFlags::Admin; - 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.", player->v.netname.chars (), PRODUCT_SHORT_NAME); } } 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 (yb_password_key.str ()))) == 0) { 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.", player->v.netname.chars (), PRODUCT_SHORT_NAME); } } } diff --git a/source/engine.cpp b/source/engine.cpp index a184f92..0202969 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -38,15 +38,17 @@ void Game::precache () { } m_precached = true; - m_drawModels[DrawLine::Simple] = engfuncs.pfnPrecacheModel (ENGINE_STR ("sprites/laserbeam.spr")); - m_drawModels[DrawLine::Arrow] = engfuncs.pfnPrecacheModel (ENGINE_STR ("sprites/arrow1.spr")); + - engfuncs.pfnPrecacheSound (ENGINE_STR ("weapons/xbow_hit1.wav")); // waypoint add - engfuncs.pfnPrecacheSound (ENGINE_STR ("weapons/mine_activate.wav")); // waypoint delete - engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_hudoff.wav")); // path add/delete start - engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_hudon.wav")); // path add/delete done - engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_moveselect.wav")); // path add/delete cancel - engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_denyselect.wav")); // path add/delete error + m_drawModels[DrawLine::Simple] = m_engineWrap.precacheModel ("sprites/laserbeam.spr"); + m_drawModels[DrawLine::Arrow] = m_engineWrap.precacheModel ("sprites/arrow1.spr"); + + m_engineWrap.precacheSound ("weapons/xbow_hit1.wav"); // waypoint add + m_engineWrap.precacheSound ("weapons/mine_activate.wav"); // waypoint delete + m_engineWrap.precacheSound ("common/wpn_hudoff.wav"); // path add/delete start + m_engineWrap.precacheSound ("common/wpn_hudon.wav"); // path add/delete done + m_engineWrap.precacheSound ("common/wpn_moveselect.wav"); // path add/delete cancel + m_engineWrap.precacheSound ("common/wpn_denyselect.wav"); // path add/delete error m_mapFlags = 0; // reset map type as worldspawn is the first entity spawned @@ -74,7 +76,7 @@ void Game::levelInitialize (edict_t *entities, int max) { if (!ent || ent->free || ent->v.classname == 0) { continue; } - auto classname = STRING (ent->v.classname); + auto classname = ent->v.classname.chars (); if (strcmp (classname, "worldspawn") == 0) { m_startEntity = ent; @@ -86,7 +88,7 @@ void Game::levelInitialize (edict_t *entities, int max) { util.installSendTo (); } else if (strcmp (classname, "player_weaponstrip") == 0) { - if ((is (GameFlags::Legacy)) && (STRING (ent->v.target))[0] == '\0') { + if (is (GameFlags::Legacy) && ent->v.target.chars ()[0] == '\0') { ent->v.target = ent->v.targetname = engfuncs.pfnAllocString ("fake"); } else { @@ -94,7 +96,7 @@ void Game::levelInitialize (edict_t *entities, int max) { } } else if (strcmp (classname, "info_player_start") == 0) { - engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/urban/urban.mdl")); + m_engineWrap.setModel (ent, "models/player/urban/urban.mdl"); ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency ent->v.renderamt = 127; // set its transparency amount @@ -103,7 +105,7 @@ void Game::levelInitialize (edict_t *entities, int max) { ++m_spawnCount[Team::CT]; } else if (strcmp (classname, "info_player_deathmatch") == 0) { - engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/terror/terror.mdl")); + m_engineWrap.setModel (ent, "models/player/terror/terror.mdl"); ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency ent->v.renderamt = 127; // set its transparency amount @@ -113,7 +115,7 @@ void Game::levelInitialize (edict_t *entities, int max) { } else if (strcmp (classname, "info_vip_start") == 0) { - engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/vip/vip.mdl")); + m_engineWrap.setModel (ent, "models/player/vip/vip.mdl"); ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency ent->v.renderamt = 127; // set its transparency amount @@ -306,7 +308,7 @@ const char *Game::getModName () { const char *Game::getMapName () { // this function gets the map name and store it in the map_name global string variable. - return strings.format ("%s", STRING (globals->mapname)); + return strings.format ("%s", globals->mapname.chars ()); } Vector Game::getEntityWorldOrigin (edict_t *ent) { @@ -465,7 +467,7 @@ bool Game::isSoftwareRenderer () { } // and on only windows version you can use software-render game. Linux, OSX always defaults to OpenGL - if (plat.isWindows) { + if (plat.win32) { return plat.hasModule ("sw"); } return false; @@ -577,15 +579,15 @@ bool Game::loadCSBinary () { } StringArray libs; - if (plat.isWindows) { + if (plat.win32) { libs.push ("mp.dll"); libs.push ("cs.dll"); } - else if (plat.isLinux) { + else if (plat.linux) { libs.push ("cs.so"); libs.push ("cs_i386.so"); } - else if (plat.isOSX) { + else if (plat.osx) { libs.push ("cs.dylib"); } @@ -739,13 +741,13 @@ bool Game::postload () { print ("%s v%s.0.%d successfully loaded for game: Counter-Strike %s (%s).\n", PRODUCT_SHORT_NAME, PRODUCT_VERSION, util.buildNumber (), gameVersionStr.chars (), String::join (gameVersionFlags, ", ").chars ()); }; - if (plat.isAndroid) { + if (plat.android) { m_gameFlags |= (GameFlags::Xash3D | GameFlags::Mobility | GameFlags::HasBotVoice | GameFlags::ReGameDLL); if (is (GameFlags::Metamod)) { return true; // we should stop the attempt for loading the real gamedll, since metamod handle this for us } - auto gamedll = strings.format ("%s/%s", getenv ("XASH3D_GAMELIBDIR"), plat.isAndroidHardFP ? "libserver_hardfp.so" : "libserver.so"); + auto gamedll = strings.format ("%s/%s", getenv ("XASH3D_GAMELIBDIR"), plat.hfp ? "libserver_hardfp.so" : "libserver.so"); if (!m_gameLib.load (gamedll)) { logger.fatal ("Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gamedll, getModName ()); @@ -879,9 +881,8 @@ bool Game::isShootableBreakable (edict_t *ent) { if (isNullEntity (ent)) { return false; } - auto classname = STRING (ent->v.classname); - if (strcmp (classname, "func_breakable") == 0 || (strcmp (classname, "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE))) { + if (strcmp (ent->v.classname.chars (), "func_breakable") == 0 || (strcmp (ent->v.classname.chars (), "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE))) { return ent->v.takedamage != DAMAGE_NO && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY) && ent->v.health < 500.0f; } return false; diff --git a/source/graph.cpp b/source/graph.cpp index 3ace7da..ccc799e 100644 --- a/source/graph.cpp +++ b/source/graph.cpp @@ -1098,7 +1098,7 @@ void BotGraph::calculatePathRadius (int index) { if (tr.flFraction < 1.0f) { game.testLine (radiusStart, radiusEnd, TraceIgnore::Monsters, nullptr, &tr); - if (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) == 0) { + if (strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) { path.radius = 0.0f; wayBlocked = true; @@ -1676,7 +1676,7 @@ bool BotGraph::saveGraphData () { options |= StorageOption::Recovered; } else if (!game.isNullEntity (m_editor)) { - author = STRING (m_editor->v.netname); + author = m_editor->v.netname.chars (); } else { author = "YAPB"; @@ -1691,9 +1691,10 @@ bool BotGraph::saveGraphData () { } void BotGraph::saveOldFormat () { - PODGraphHeader header; + PODGraphHeader header {}; + strcpy (header.header, kPodbotMagic); - strncpy (header.author, STRING (m_editor->v.netname), cr::bufsize (header.author)); + strncpy (header.author, m_editor->v.netname.chars (), cr::bufsize (header.author)); strncpy (header.mapName, game.getMapName (), cr::bufsize (header.mapName)); header.mapName[31] = 0; @@ -1750,7 +1751,7 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) { // check if we go through a func_illusionary, in which case return false game.testHull (src, destination, TraceIgnore::Monsters, head_hull, m_editor, &tr); - if (!game.isNullEntity (tr.pHit) && strcmp ("func_illusionary", STRING (tr.pHit->v.classname)) == 0) { + if (!game.isNullEntity (tr.pHit) && strcmp ("func_illusionary", tr.pHit->v.classname.chars ()) == 0) { return false; // don't add pathnodes through func_illusionaries } @@ -1758,9 +1759,9 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) { game.testLine (src, destination, TraceIgnore::Monsters, m_editor, &tr); // if node is visible from current position (even behind head)... - if (tr.flFraction >= 1.0f || strncmp ("func_door", STRING (tr.pHit->v.classname), 9) == 0) { + if (tr.flFraction >= 1.0f || strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) { // if it's a door check if nothing blocks behind - if (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) == 0) { + if (strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) { game.testLine (tr.vecEndPos, destination, TraceIgnore::Monsters, tr.pHit, &tr); if (tr.flFraction < 1.0f) { @@ -2649,7 +2650,7 @@ void BotGraph::setBombOrigin (bool reset, const Vector &pos) { } game.searchEntities ("classname", "grenade", [&] (edict_t *ent) { - if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) { + if (strcmp (ent->v.model.chars () + 9, "c4.mdl") == 0) { m_bombOrigin = game.getEntityWorldOrigin (ent); return EntitySearchResult::Break; } diff --git a/source/linkage.cpp b/source/linkage.cpp index 7904486..49b3450 100644 --- a/source/linkage.cpp +++ b/source/linkage.cpp @@ -53,12 +53,14 @@ namespace variadic { auto buffer = strings.chars (); va_start (ap, format); - _vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap); + vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap); va_end (ap); if (ent && (ent->v.flags & (FL_FAKECLIENT | FL_DORMANT))) { - if (bots[ent]) { - game.botCommand (ent, buffer); + auto bot = bots[ent]; + + if (bot) { + bot->issueCommand (buffer); } if (game.is (GameFlags::Metamod)) { @@ -441,7 +443,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *table, int *) { } dllapi.pfnPM_Move (playerMove, server); }; - return TRUE; + return true; } CR_LINKAGE_C int GetEntityAPI2_Post (gamefuncs_t *table, int *) { @@ -498,7 +500,7 @@ CR_LINKAGE_C int GetEntityAPI2_Post (gamefuncs_t *table, int *) { RETURN_META (MRES_IGNORED); }; - return TRUE; + return true; } CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { @@ -787,7 +789,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { table->pfnClientCommand = variadic::clientCommand; - return TRUE; + return true; } CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *table, int *interfaceVersion) { @@ -801,11 +803,11 @@ CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *table, int *interfaceVersion) if (!api_GetNewDLLFunctions || !api_GetNewDLLFunctions (table, interfaceVersion)) { logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__); - return FALSE; + return false; } dllfuncs.newapi_table = table; - return TRUE; + return true; } CR_LINKAGE_C int GetEngineFunctions_Post (enginefuncs_t *table, int *) { @@ -834,7 +836,7 @@ CR_LINKAGE_C int GetEngineFunctions_Post (enginefuncs_t *table, int *) { RETURN_META_VALUE (MRES_IGNORED, 0); }; - return TRUE; + return true; } CR_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) { @@ -845,7 +847,7 @@ CR_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMet gpMetaUtilFuncs = pMetaUtilFuncs; *pPlugInfo = &Plugin_info; - return TRUE; // tell metamod this plugin looks safe + return true; // tell metamod this plugin looks safe } CR_EXPORT int Meta_Attach (PLUG_LOADTIME now, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) { @@ -867,7 +869,7 @@ CR_EXPORT int Meta_Attach (PLUG_LOADTIME now, metamod_funcs_t *functionTable, me if (now > Plugin_info.loadable) { logger.error ("%s: plugin NOT attaching (can't load plugin right now)", Plugin_info.name); - return FALSE; // returning FALSE prevents metamod from attaching this plugin + return false; // returning FALSE prevents metamod from attaching this plugin } // keep track of the pointers to engine function tables metamod gives us @@ -875,7 +877,7 @@ CR_EXPORT int Meta_Attach (PLUG_LOADTIME now, metamod_funcs_t *functionTable, me memcpy (functionTable, &metamodFunctionTable, sizeof (metamod_funcs_t)); gpGamedllFuncs = pGamedllFuncs; - return TRUE; // returning true enables metamod to attach this plugin + return true; // returning true enables metamod to attach this plugin } CR_EXPORT int Meta_Detach (PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { @@ -884,7 +886,7 @@ CR_EXPORT int Meta_Detach (PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { if (now > Plugin_info.unloadable && reason != PNL_CMD_FORCED) { logger.error ("%s: plugin NOT detaching (can't unload plugin right now)", Plugin_info.name); - return FALSE; // returning FALSE prevents metamod from unloading this plugin + return false; // returning FALSE prevents metamod from unloading this plugin } bots.kickEveryone (true); // kick all bots off this server @@ -892,7 +894,7 @@ CR_EXPORT int Meta_Detach (PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { graph.savePractice (); util.disableSendTo (); - return TRUE; + return true; } CR_EXPORT void Meta_Init () { diff --git a/source/manager.cpp b/source/manager.cpp index 954bd98..ae96991 100644 --- a/source/manager.cpp +++ b/source/manager.cpp @@ -47,12 +47,16 @@ BotManager::BotManager () { m_bombPlanted = false; m_botsCanPause = false; + m_roundEnded = false; m_bombSayStatus = BombPlantedSay::ChatSay | BombPlantedSay::Chatter; for (int i = 0; i < kGameTeamNum; ++i) { m_leaderChoosen[i] = false; m_economicsGood[i] = true; + + m_lastRadioTime[i] = 0.0f; + m_lastRadio[i] = -1; } reset (); @@ -106,7 +110,7 @@ void BotManager::touchKillerEntity (Bot *bot) { kv.szClassName = const_cast (prop.classname.chars ()); kv.szKeyName = "damagetype"; kv.szValue = const_cast (strings.format ("%d", cr::bit (4))); - kv.fHandled = FALSE; + kv.fHandled = false; MDLL_KeyValue (m_killerEntity, &kv); MDLL_Touch (m_killerEntity, bot->ent ()); @@ -205,7 +209,7 @@ BotCreateResult BotManager::create (const String &name, int difficulty, int pers ctrl.msg ("Maximum players reached (%d/%d). Unable to create Bot.", game.maxClients (), game.maxClients ()); return BotCreateResult::MaxPlayersReached; } - auto object = cr::createUnique (bot, difficulty, personality, team, member); + auto object = cr::makeUnique (bot, difficulty, personality, team, member); auto index = object->index (); // assign owner of bot name @@ -660,7 +664,7 @@ void BotManager::listBots () { 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) {; - 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 (bot->pev->frags), bot->m_notKilled ? "yes" : "no"); + ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s", bot->index (), bot->pev->netname.chars (), 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 (bot->pev->frags), bot->m_notKilled ? "yes" : "no"); } ctrl.msg ("%d bots", m_bots.length ()); } @@ -816,11 +820,11 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) { } char reject[256] = {0, }; - MDLL_ClientConnect (bot, STRING (bot->v.netname), strings.format ("127.0.0.%d", clientIndex + 100), reject); + MDLL_ClientConnect (bot, bot->v.netname.chars (), strings.format ("127.0.0.%d", clientIndex + 100), reject); if (!strings.isEmpty (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 + logger.error ("Server refused '%s' connection (%s)", bot->v.netname.chars (), reject); + game.serverCommand ("kick \"%s\"", bot->v.netname.chars ()); // kick the bot player if the server refused it bot->v.flags |= FL_KILLME; return; @@ -1246,7 +1250,7 @@ void Bot::kill () { void Bot::kick () { // this function kick off one bot from the server. - auto username = STRING (pev->netname); + auto username = pev->netname.chars (); if (!(pev->flags & FL_FAKECLIENT) || strings.isEmpty (username)) { return; @@ -1304,7 +1308,7 @@ void Bot::updateTeamJoin () { } // select the team the bot wishes to join... - game.botCommand (ent (), "menuselect %d", m_wantedTeam); + issueCommand ("menuselect %d", m_wantedTeam); } else if (m_startAction == BotMsg::ClassSelect) { m_startAction = BotMsg::None; // switch back to idle @@ -1317,7 +1321,7 @@ void Bot::updateTeamJoin () { } // select the class the bot wishes to use... - game.botCommand (ent (), "menuselect %d", m_wantedClass); + issueCommand ("menuselect %d", m_wantedClass); // bot has now joined the game (doesn't need to be started) m_notStarted = false; @@ -1409,7 +1413,7 @@ void BotManager::updateActiveGrenade () { // search the map for any type of grenade game.searchEntities ("classname", "grenade", [&] (edict_t *e) { // do not count c4 as a grenade - if (strcmp (STRING (e->v.model) + 9, "c4.mdl") == 0) { + if (strcmp (e->v.model.chars () + 9, "c4.mdl") == 0) { return EntitySearchResult::Continue; } m_activeGrenades.push (e); @@ -1430,7 +1434,7 @@ void BotManager::updateIntrestingEntities () { // search the map for any type of grenade game.searchEntities (nullptr, kInfiniteDistance, [&] (edict_t *e) { - auto classname = STRING (e->v.classname); + auto classname = e->v.classname.chars (); // search for grenades, weaponboxes, weapons, items and armoury entities if (strncmp ("weapon", classname, 6) == 0 || strncmp ("grenade", classname, 7) == 0 || strncmp ("item", classname, 4) == 0 || strncmp ("armoury", classname, 7) == 0) { @@ -1689,7 +1693,7 @@ void BotConfig::loadMainConfig () { firstLoad = false; // android is abit hard to play, lower the difficulty by default - if (plat.isAndroid && yb_difficulty.int_ () > 3) { + if (plat.android && yb_difficulty.int_ () > 3) { yb_difficulty.set (3); } return; diff --git a/source/message.cpp b/source/message.cpp index fabac6c..62916fe 100644 --- a/source/message.cpp +++ b/source/message.cpp @@ -507,3 +507,25 @@ void MessageDispatcher::stop () { (this->*m_handlers[m_current]) (); m_current = NetMsg::None; } + +void MessageDispatcher::ensureMessages () { + // we're getting messages ids in regusermsg for metamod, but when we're unloaded, we're lost our ids on next 'meta load'. + // this function tries to associate appropriate message ids. + + // check if we're have one + if (m_maps.exists (NetMsg::Money)) { + return; + } + + // re-register our message + for (const auto &msg : m_wanted) { + add (msg.key, GET_USER_MSG_ID (PLID, msg.key.chars (), nullptr)); + } +} + +int32 MessageDispatcher::id (NetMsg msg) { + if (game.is (GameFlags::Metamod)) { + ensureMessages (); + } + return m_maps[msg]; +} diff --git a/source/navigate.cpp b/source/navigate.cpp index b8a44a5..5319f21 100644 --- a/source/navigate.cpp +++ b/source/navigate.cpp @@ -19,7 +19,7 @@ int Bot::findBestGoal () { int result = kInvalidNodeIndex; game.searchEntities ("classname", "weaponbox", [&] (edict_t *ent) { - if (strcmp (STRING (ent->v.model), "models/w_backpack.mdl") == 0) { + if (strcmp (ent->v.model.chars () + 9, "backpack.mdl") == 0) { result = graph.getNearest (game.getEntityWorldOrigin (ent)); if (graph.exists (result)) { @@ -708,7 +708,7 @@ bool Bot::updateNavigation () { if (game.mapIs (MapFlags::HasDoors)) { game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Monsters, ent (), &tr); - if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (STRING (tr.pHit->v.classname), "func_door", 9) == 0) { + if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) { // if the door is near enough... if ((game.getEntityWorldOrigin (tr.pHit) - pev->origin).lengthSq () < 2500.0f) { ignoreCollision (); // don't consider being stuck @@ -728,7 +728,7 @@ bool Bot::updateNavigation () { m_aimFlags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath); m_canChooseAimDirection = false; - auto button = lookupButton (STRING (tr.pHit->v.targetname)); + auto button = lookupButton (tr.pHit->v.targetname.chars ()); // check if we got valid button if (!game.isNullEntity (button)) { @@ -849,7 +849,7 @@ bool Bot::updateLiftHandling () { // update node time set m_navTimeset = game.time (); - TraceResult tr, tr2; + TraceResult tr; // wait for something about for lift auto wait = [&] () { @@ -865,9 +865,9 @@ bool Bot::updateLiftHandling () { }; // trace line to door - game.testLine (pev->origin, m_path->origin, TraceIgnore::Everything, ent (), &tr2); + game.testLine (pev->origin, m_path->origin, TraceIgnore::Everything, ent (), &tr); - if (tr2.flFraction < 1.0f && strcmp (STRING (tr2.pHit->v.classname), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr2.pHit) { + if (tr.flFraction < 1.0f && strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) { if (m_liftState == LiftState::None) { m_liftState = LiftState::LookingButtonOutside; m_liftUsageTime = game.time () + 7.0f; @@ -879,8 +879,8 @@ bool Bot::updateLiftHandling () { game.testLine (m_path->origin, m_path->origin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr); // if trace result shows us that it is a lift - if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (strcmp (STRING (tr.pHit->v.classname), "func_door") == 0 || strcmp (STRING (tr.pHit->v.classname), "func_plat") == 0 || strcmp (STRING (tr.pHit->v.classname), "func_train") == 0) && !liftClosedDoorExists) { - if ((m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && tr.pHit->v.velocity.z == 0.0f) { + if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_train") == 0) && !liftClosedDoorExists) { + if ((m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && cr::fzero (tr.pHit->v.velocity.z)) { if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) { m_liftEntity = tr.pHit; m_liftState = LiftState::EnteringIn; @@ -901,7 +901,7 @@ bool Bot::updateLiftHandling () { if (graph.exists (nextNode) && (graph[nextNode].flags & NodeFlag::Lift)) { game.testLine (m_path->origin, graph[nextNode].origin, TraceIgnore::Everything, ent (), &tr); - if (!game.isNullEntity (tr.pHit) && (strcmp (STRING (tr.pHit->v.classname), "func_door") == 0 || strcmp (STRING (tr.pHit->v.classname), "func_plat") == 0 || strcmp (STRING (tr.pHit->v.classname), "func_train") == 0)) { + if (!game.isNullEntity (tr.pHit) && (strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_train") == 0)) { m_liftEntity = tr.pHit; } } @@ -983,7 +983,7 @@ bool Bot::updateLiftHandling () { // bot is trying to find button inside a lift if (m_liftState == LiftState::LookingButtonInside) { - auto button = lookupButton (STRING (m_liftEntity->v.targetname)); + auto button = lookupButton (m_liftEntity->v.targetname.chars ()); // got a valid button entity ? if (!game.isNullEntity (button) && pev->groundentity == m_liftEntity && m_buttonPushTime + 1.0f < game.time () && m_liftEntity->v.velocity.z == 0.0f && isOnFloor ()) { @@ -1032,7 +1032,7 @@ bool Bot::updateLiftHandling () { } } else if (!game.isNullEntity (m_liftEntity)) { - auto button = lookupButton (STRING (m_liftEntity->v.targetname)); + auto button = lookupButton (m_liftEntity->v.targetname.chars ()); // if we got a valid button entity if (!game.isNullEntity (button)) { @@ -1331,63 +1331,40 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: return hfunctionPathDist (index, startIndex, goalIndex) / 128.0f * 10.0f; }; + // holders for heuristic functions + Lambda gcalc, hcalc; + // get correct calculation for heuristic - auto calculate = [&] (bool hfun, int a, int b, int c) -> float { - if (pathType == FindPath::Optimal) { - if (game.mapIs (MapFlags::HostageRescue) && hasHostage ()) { - if (hfun) { - return hfunctionPathDistWithHostage (a, b, c); - } - else { - return gfunctionKillsDistCTWithHostage (a, b, c); - } - } - else { - if (hfun) { - return hfunctionPathDist (a, b, c); - } - else { - return gfunctionKillsDist (a, b, c); - } - } - } - else if (pathType == FindPath::Safe) { - if (game.mapIs (MapFlags::HostageRescue) && hasHostage ()) { - if (hfun) { - return hfunctionNone (a, b, c); - } - else { - return gfunctionKillsCTWithHostage (a, b, c); - } - } - else { - if (hfun) { - return hfunctionNone (a, b, c); - } - else { - return gfunctionKills (a, b, c); - } - } + if (pathType == FindPath::Optimal) { + if (game.mapIs (MapFlags::HostageRescue) && hasHostage ()) { + hcalc = hfunctionPathDistWithHostage; + gcalc = gfunctionKillsDistCTWithHostage; } else { - if (game.mapIs (MapFlags::HostageRescue) && hasHostage ()) { - if (hfun) { - return hfunctionPathDistWithHostage (a, b, c); - } - else { - return gfunctionPathDistWithHostage (a, b, c); - } - } - else { - if (hfun) { - return hfunctionPathDist (a, b, c); - } - else { - return gfunctionPathDist (a, b, c); - } - } + hcalc = hfunctionPathDist; + gcalc = gfunctionKillsDist; } - }; + } + else if (pathType == FindPath::Safe) { + if (game.mapIs (MapFlags::HostageRescue) && hasHostage ()) { + hcalc = hfunctionNone; + gcalc = gfunctionKillsCTWithHostage; + } + else { + hcalc = hfunctionNone; + gcalc = gfunctionKills; + } + } + else { + if (game.mapIs (MapFlags::HostageRescue) && hasHostage ()) { + hcalc = hfunctionPathDistWithHostage; + gcalc = gfunctionPathDistWithHostage; + } + else { + hcalc = hfunctionPathDist; + gcalc = gfunctionPathDist; + } + } if (!graph.exists (srcIndex)) { logger.error ("Pathfinder source path index not valid (%d)", srcIndex); @@ -1406,8 +1383,8 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: auto srcRoute = &m_routes[srcIndex]; // put start node into open list - srcRoute->g = calculate (false, m_team, srcIndex, kInvalidNodeIndex); - srcRoute->f = srcRoute->g + calculate (true, srcIndex, srcIndex, destIndex); + srcRoute->g = gcalc (m_team, srcIndex, kInvalidNodeIndex); + srcRoute->f = srcRoute->g + hcalc (srcIndex, srcIndex, destIndex); srcRoute->state = RouteState::Open; m_routeQue.clear (); @@ -1419,7 +1396,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: // safes us from bad graph... if (m_routeQue.length () >= kMaxRouteLength - 1) { - logger.error ("A* Search for bots \"%s\" has tried to build path with at least %d nodes. Seems to be graph is broken.", STRING (pev->netname), m_routeQue.length ()); + logger.error ("A* Search for bot \"%s\" has tried to build path with at least %d nodes. Seems to be graph is broken.", pev->netname.chars (), m_routeQue.length ()); // bail out to shortest path findShortestPath (srcIndex, destIndex); @@ -1458,8 +1435,8 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: auto childRoute = &m_routes[child.index]; // calculate the F value as F = G + H - float g = curRoute->g + calculate (false, m_team, child.index, currentIndex); - float h = calculate (true, child.index, srcIndex, destIndex); + float g = curRoute->g + gcalc (m_team, child.index, currentIndex); + float h = hcalc (child.index, srcIndex, destIndex); float f = g + h; if (childRoute->state == RouteState::New || childRoute->f > f) { @@ -1474,6 +1451,9 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: } } } + logger.error ("A* Search for bot \"%s\" has failed. Falling back to shortest-path algorithm. Seems to be graph is broken.", pev->netname.chars ()); + + // fallback to shortest path findShortestPath (srcIndex, destIndex); // A* found no path, try floyd pathfinder instead } @@ -2098,6 +2078,22 @@ bool Bot::advanceMovement () { m_pathWalk.shift (); // advance in list m_currentTravelFlags = 0; // reset travel flags (jumping etc) + // helper to change bot's goal + auto changeNextGoal = [&] { + int newGoal = findBestGoal (); + + m_prevGoalIndex = newGoal; + m_chosenGoalIndex = newGoal; + + // remember index + getTask ()->data = newGoal; + + // do path finding if it's not the current node + if (newGoal != m_currentNodeIndex) { + findPath (m_currentNodeIndex, newGoal, m_pathType); + } + }; + // we're not at the end of the list? if (!m_pathWalk.empty ()) { // if in between a route, postprocess the node (find better alternatives)... @@ -2142,22 +2138,15 @@ bool Bot::advanceMovement () { // force terrorist bot to plant bomb if (m_inBombZone && !m_hasProgressBar && m_hasC4) { - int newGoal = findBestGoal (); - - m_prevGoalIndex = newGoal; - m_chosenGoalIndex = newGoal; - - // remember index - getTask ()->data = newGoal; - - // do path finding if it's not the current node - if (newGoal != m_currentNodeIndex) { - findPath (m_currentNodeIndex, newGoal, m_pathType); - } + changeNextGoal (); return false; } } } + else if (m_pathWalk.hasNext () && m_pathWalk.next () == m_pathWalk.last () && isOccupiedNode (m_pathWalk.last ())) { + changeNextGoal (); + return false; + } if (!m_pathWalk.empty ()) { const int destIndex = m_pathWalk.first (); @@ -2192,7 +2181,7 @@ bool Bot::advanceMovement () { src = path.origin; dst = next.origin; - jumpDistance = (src - dst).length (); + jumpDistance = (path.origin - next.origin).length (); willJump = true; break; @@ -2255,7 +2244,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) { if (!game.mapIs (MapFlags::HasDoors)) { return false; } - return tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0; + return tr->flFraction < 1.0f && strncmp ("func_door", tr->pHit->v.classname.chars (), 9) != 0; }; // trace from the bot's eyes straight forward... @@ -2263,7 +2252,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) { // check if the trace hit something... if (tr->flFraction < 1.0f) { - if (game.mapIs (MapFlags::HasDoors) && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) == 0) { + if (game.mapIs (MapFlags::HasDoors) && strncmp ("func_door", tr->pHit->v.classname.chars (), 9) == 0) { return false; } return true; // bot's head will hit something @@ -2614,7 +2603,7 @@ bool Bot::isBlockedLeft () { game.testLine (pev->origin, forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr); // 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", tr.pHit->v.classname.chars (), 9) != 0) { return true; // bot's body will hit something } return false; @@ -2634,7 +2623,7 @@ bool Bot::isBlockedRight () { game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr); // 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", tr.pHit->v.classname.chars (), 9) != 0)) { return true; // bot's body will hit something } return false; @@ -2995,7 +2984,7 @@ int Bot::getNearestToPlantedBomb () { // search the bomb on the map game.searchEntities ("classname", "grenade", [&result] (edict_t *ent) { - if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) { + if (strcmp (ent->v.model.chars () + 9, "c4.mdl") == 0) { result = graph.getNearest (game.getEntityWorldOrigin (ent)); if (graph.exists (result)) { diff --git a/source/support.cpp b/source/support.cpp index b988bf4..896e17a 100644 --- a/source/support.cpp +++ b/source/support.cpp @@ -67,6 +67,40 @@ BotUtils::BotUtils () { m_noiseCache["debris/bust"] = Noise::NeedHandle | Noise::Broke; m_noiseCache["doors/doorm"] = Noise::NeedHandle | Noise::Door; + // register weapon aliases + m_weaponAlias.push (Weapon::USP, "usp"); // HK USP .45 Tactical + m_weaponAlias.push (Weapon::Glock18, "glock"); // Glock18 Select Fire + m_weaponAlias.push (Weapon::Deagle, "deagle"); // Desert Eagle .50AE + m_weaponAlias.push (Weapon::P228, "p228"); // SIG P228 + m_weaponAlias.push (Weapon::Elite, "elite"); // Dual Beretta 96G Elite + m_weaponAlias.push (Weapon::FiveSeven, "fn57"); // FN Five-Seven + m_weaponAlias.push (Weapon::M3, "m3"); // Benelli M3 Super90 + m_weaponAlias.push (Weapon::XM1014, "xm1014"); // Benelli XM1014 + m_weaponAlias.push (Weapon::MP5, "mp5"); // HK MP5-Navy + m_weaponAlias.push (Weapon::TMP, "tmp"); // Steyr Tactical Machine Pistol + m_weaponAlias.push (Weapon::P90, "p90"); // FN P90 + m_weaponAlias.push (Weapon::MAC10, "mac10"); // Ingram MAC-10 + m_weaponAlias.push (Weapon::UMP45, "ump45"); // HK UMP45 + m_weaponAlias.push (Weapon::AK47, "ak47"); // Automat Kalashnikov AK-47 + m_weaponAlias.push (Weapon::Galil, "galil"); // IMI Galil + m_weaponAlias.push (Weapon::Famas, "famas"); // GIAT FAMAS + m_weaponAlias.push (Weapon::SG552, "sg552"); // Sig SG-552 Commando + m_weaponAlias.push (Weapon::M4A1, "m4a1"); // Colt M4A1 Carbine + m_weaponAlias.push (Weapon::AUG, "aug"); // Steyr Aug + m_weaponAlias.push (Weapon::Scout, "scout"); // Steyr Scout + m_weaponAlias.push (Weapon::AWP, "awp"); // AI Arctic Warfare/Magnum + m_weaponAlias.push (Weapon::G3SG1, "g3sg1"); // HK G3/SG-1 Sniper Rifle + m_weaponAlias.push (Weapon::SG550, "sg550"); // Sig SG-550 Sniper + m_weaponAlias.push (Weapon::M249, "m249"); // FN M249 Para + m_weaponAlias.push (Weapon::Flashbang, "flash"); // Concussion Grenade + m_weaponAlias.push (Weapon::Explosive, "hegren"); // High-Explosive Grenade + m_weaponAlias.push (Weapon::Smoke, "sgren"); // Smoke Grenade + m_weaponAlias.push (Weapon::Armor, "vest"); // Kevlar Vest + m_weaponAlias.push (Weapon::ArmorHelm, "vesthelm"); // Kevlar Vest and Helmet + m_weaponAlias.push (Weapon::Defuser, "defuser"); // Defuser Kit + m_weaponAlias.push (Weapon::Shield, "shield"); // Tactical Shield + m_weaponAlias.push (Weapon::Knife, "knife"); // Knife + m_clients.resize (kGameMaxPlayers + 1); } @@ -169,7 +203,7 @@ bool BotUtils::isPlayer (edict_t *ent) { } if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots[ent] != nullptr) { - return !strings.isEmpty (STRING (ent->v.netname)); + return !strings.isEmpty (ent->v.netname.chars ()); } return false; } @@ -609,7 +643,7 @@ void BotUtils::installSendTo () { } // enable only on modern games - if (game.is (GameFlags::Modern) && (plat.isLinux || plat.isWindows) && !plat.isArm && !m_sendToHook.enabled ()) { + if (game.is (GameFlags::Modern) && (plat.linux || plat.win32) && !plat.arm && !m_sendToHook.enabled ()) { m_sendToHook.patch (reinterpret_cast (&sendto), reinterpret_cast (&BotUtils::sendTo)); } } @@ -709,66 +743,11 @@ int BotUtils::buildNumber () { return buildNumber; } -int BotUtils::getWeaponAlias (bool needString, const char *weaponAlias, int weaponIndex) { - // this function returning weapon id from the weapon alias and vice versa. +const String &BotUtils::weaponIdToAlias (const int32 id) { + static const String &none = "none"; - // structure definition for weapon tab - struct WeaponTab_t { - Weapon weaponIndex; // weapon id - const char *alias; // weapon alias - }; - - // weapon enumeration - WeaponTab_t weaponTab[] = { - {Weapon::USP, "usp"}, // HK USP .45 Tactical - {Weapon::Glock18, "glock"}, // Glock18 Select Fire - {Weapon::Deagle, "deagle"}, // Desert Eagle .50AE - {Weapon::P228, "p228"}, // SIG P228 - {Weapon::Elite, "elite"}, // Dual Beretta 96G Elite - {Weapon::FiveSeven, "fn57"}, // FN Five-Seven - {Weapon::M3, "m3"}, // Benelli M3 Super90 - {Weapon::XM1014, "xm1014"}, // Benelli XM1014 - {Weapon::MP5, "mp5"}, // HK MP5-Navy - {Weapon::TMP, "tmp"}, // Steyr Tactical Machine Pistol - {Weapon::P90, "p90"}, // FN P90 - {Weapon::MAC10, "mac10"}, // Ingram MAC-10 - {Weapon::UMP45, "ump45"}, // HK UMP45 - {Weapon::AK47, "ak47"}, // Automat Kalashnikov AK-47 - {Weapon::Galil, "galil"}, // IMI Galil - {Weapon::Famas, "famas"}, // GIAT FAMAS - {Weapon::SG552, "sg552"}, // Sig SG-552 Commando - {Weapon::M4A1, "m4a1"}, // Colt M4A1 Carbine - {Weapon::AUG, "aug"}, // Steyr Aug - {Weapon::Scout, "scout"}, // Steyr Scout - {Weapon::AWP, "awp"}, // AI Arctic Warfare/Magnum - {Weapon::G3SG1, "g3sg1"}, // HK G3/SG-1 Sniper Rifle - {Weapon::SG550, "sg550"}, // Sig SG-550 Sniper - {Weapon::M249, "m249"}, // FN M249 Para - {Weapon::Flashbang, "flash"}, // Concussion Grenade - {Weapon::Explosive, "hegren"}, // High-Explosive Grenade - {Weapon::Smoke, "sgren"}, // Smoke Grenade - {Weapon::Armor, "vest"}, // Kevlar Vest - {Weapon::ArmorHelm, "vesthelm"}, // Kevlar Vest and Helmet - {Weapon::Defuser, "defuser"}, // Defuser Kit - {Weapon::Shield, "shield"}, // Tactical Shield - {Weapon::Knife, "knife"} // Knife - }; - - // if we need to return the string, find by weapon id - if (needString && weaponIndex != -1) { - for (auto &tab : weaponTab) { - if (tab.weaponIndex == weaponIndex) { // is weapon id found? - return MAKE_STRING (tab.alias); - } - } - return MAKE_STRING ("(none)"); // return none + if (m_weaponAlias.exists (id)) { + return m_weaponAlias[id]; } - - // else search weapon by name and return weapon id - for (auto &tab : weaponTab) { - if (strncmp (tab.alias, weaponAlias, strlen (tab.alias)) == 0) { - return tab.weaponIndex; - } - } - return -1; // no weapon was found return -1 -} \ No newline at end of file + return none; +}