diff --git a/.gitignore b/.gitignore index f0970d1..6ffb045 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,4 @@ Icon *.json *.html *.settings +*.sarif diff --git a/.travis.yml b/.travis.yml index e5a4db9..7fd5941 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,32 @@ +language: cpp +dist: trusty + +compiler: + - clang + addons: apt: packages: - - lib32stdc++6 - - lib32z1-dev - - libc6-dev-i386 - - linux-libc-dev - - gcc-multilib - - g++-multilib -language: cpp -compiler: - - clang + - libc6-dev-i386 + - linux-libc-dev + - gcc-multilib + - g++-multilib os: - linux - osx + before_script: - - wget https://yapb.ru/ci/scripts/gitrev.sh && chmod a+x ./gitrev.sh && ./gitrev.sh + - sed -i.bak "s/unspecified_hash/$(git rev-parse HEAD 2>/dev/null)/g;s/unspecified_author/$(git log --pretty="%ae" -1 2>/dev/null)/g;s/0000/$(git rev-list HEAD --count 2>/dev/null)/g" include/resource.h && rm include/resource.h.bak + script: - cd project && CC=clang && make all + after_success: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl --ftp-create-dirs -T ./release/yapb.so -u $FTP_USER:$FTP_PASS ftp://$FTP_HOST/project/release/yapb.so && curl --ftp-create-dirs -T ./debug/yapb.so -u $FTP_USER:$FTP_PASS ftp://$FTP_HOST/project/debug/yapb.so && curl -X GET "https://yapb.ru/agent/packager.php?key=$PACKAGER_KEY&os=linux"; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl --ftp-create-dirs -T ./release/yapb.dylib -u $FTP_USER:$FTP_PASS ftp://$FTP_HOST/project/release/yapb.dylib && curl --ftp-create-dirs -T ./debug/yapb.dylib -u $FTP_USER:$FTP_PASS ftp://$FTP_HOST/project/debug/yapb.dylib && curl -X GET "https://yapb.ru/agent/packager.php?key=$PACKAGER_KEY&os=osx"; fi - + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + - curl -F "key=$CI_KEY" -F "mode=build" -F "type=release" -F "lib=@./release/yapb.so" https://yapb.ru/agent/ci.php + - curl -F "key=$CI_KEY" -F "mode=build" -F "type=debug" -F "lib=@./debug/yapb.so" https://yapb.ru/agent/ci.php + - fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + - curl -F "key=$CI_KEY" -F "mode=build" -F "type=release" -F "lib=@./release/yapb.dylib" https://yapb.ru/agent/ci.php + - curl -F "key=$CI_KEY" -F "mode=build" -F "type=debug" -F "lib=@./debug/yapb.dylib" https://yapb.ru/agent/ci.php + - fi diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/include/compress.h b/include/compress.h index 1d2e6dd..6cca319 100644 --- a/include/compress.h +++ b/include/compress.h @@ -4,84 +4,82 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // #pragma once -const int N = 4096, F = 18, THRESHOLD = 2, NIL = N; +static constexpr int MAXBUF = 4096, PADDING = 18, THRESHOLD = 2, NIL = MAXBUF; -class Compressor -{ +class Compress { protected: - unsigned long int m_textSize; - unsigned long int m_codeSize; + unsigned long int m_csize; - uint8 m_textBuffer[N + F - 1]; - int m_matchPosition; - int m_matchLength; + uint8 m_buffer[MAXBUF + PADDING - 1]; + int m_matchPos; + int m_matchLen; - int m_left[N + 1]; - int m_right[N + 257]; - int m_parent[N + 1]; + int m_left[MAXBUF + 1]; + int m_right[MAXBUF + 257]; + int m_parent[MAXBUF + 1]; private: - void InitTree (void) - { - for (int i = N + 1; i <= N + 256; i++) + void initTrees (void) { + for (int i = MAXBUF + 1; i <= MAXBUF + 256; i++) { m_right[i] = NIL; + } - for (int j = 0; j < N; j++) + for (int j = 0; j < MAXBUF; j++) { m_parent[j] = NIL; + } } - void InsertNode (int node) - { + void insert (int node) { int i; int compare = 1; - uint8 *key = &m_textBuffer[node]; - int temp = N + 1 + key[0]; + uint8 *key = &m_buffer[node]; + int temp = MAXBUF + 1 + key[0]; m_right[node] = m_left[node] = NIL; - m_matchLength = 0; + m_matchLen = 0; - for (;;) - { - if (compare >= 0) - { - if (m_right[temp] != NIL) + for (;;) { + if (compare >= 0) { + if (m_right[temp] != NIL) { temp = m_right[temp]; - else - { + } + else { m_right[temp] = node; m_parent[node] = temp; return; } } - else - { - if (m_left[temp] != NIL) + else { + if (m_left[temp] != NIL) { temp = m_left[temp]; - else - { + } + else { m_left[temp] = node; m_parent[node] = temp; + return; } } - for (i = 1; i < F; i++) - if ((compare = key[i] - m_textBuffer[temp + i]) != 0) + for (i = 1; i < PADDING; i++) { + if ((compare = key[i] - m_buffer[temp + i]) != 0) { break; + } + } - if (i > m_matchLength) - { - m_matchPosition = temp; - - if ((m_matchLength = i) >= F) + if (i > m_matchLen) { + m_matchPos = temp; + + if ((m_matchLen = i) >= PADDING) { break; + } } } @@ -91,37 +89,35 @@ private: m_parent[m_left[temp]] = node; m_parent[m_right[temp]] = node; - if (m_right[m_parent[temp]] == temp) + if (m_right[m_parent[temp]] == temp) { m_right[m_parent[temp]] = node; - else + } + else { m_left[m_parent[temp]] = node; - + } m_parent[temp] = NIL; } - - void DeleteNode (int node) - { + void erase (int node) { int temp; - if (m_parent[node] == NIL) + if (m_parent[node] == NIL) { return; // not in tree + } - if (m_right[node] == NIL) + if (m_right[node] == NIL) { temp = m_left[node]; - - else if (m_left[node] == NIL) + } + else if (m_left[node] == NIL) { temp = m_right[node]; - - else - { + } + else { temp = m_left[node]; - if (m_right[temp] != NIL) - { - do + if (m_right[temp] != NIL) { + do { temp = m_right[temp]; - while (m_right[temp] != NIL); + } while (m_right[temp] != NIL); m_right[m_parent[temp]] = m_left[temp]; m_parent[m_left[temp]] = m_parent[temp]; @@ -132,240 +128,208 @@ private: m_right[temp] = m_right[node]; m_parent[m_right[node]] = temp; } - m_parent[temp] = m_parent[node]; - if (m_right[m_parent[node]] == node) + if (m_right[m_parent[node]] == node) { m_right[m_parent[node]] = temp; - else + } + else { m_left[m_parent[node]] = temp; - + } m_parent[node] = NIL; } public: - Compressor (void) - { - m_textSize = 0; - m_codeSize = 0; - - m_matchPosition = 0; - m_matchLength = 0; - - memset (m_textBuffer, 0, sizeof (m_textBuffer)); + Compress (void) : m_csize (0), m_matchPos (0), m_matchLen (0) { memset (m_right, 0, sizeof (m_right)); memset (m_left, 0, sizeof (m_left)); memset (m_parent, 0, sizeof (m_parent)); + memset (m_buffer, 0, sizeof (m_buffer)); } - ~Compressor (void) - { - m_textSize = 0; - m_codeSize = 0; - - m_matchPosition = 0; - m_matchLength = 0; - - memset (m_textBuffer, 0, sizeof (m_textBuffer)); - memset (m_right, 0, sizeof (m_right)); - memset (m_left, 0, sizeof (m_left)); - memset (m_parent, 0, sizeof (m_parent)); - } - - int InternalEncode (const char *fileName, uint8 *header, int headerSize, uint8 *buffer, int bufferSize) - { - int i, length, node, strPtr, lastMatchLength, codeBufferPtr, bufferPtr = 0; - uint8 codeBuffer[17], mask; + ~Compress (void) = default; + int encode_ (const char *fileName, uint8 *header, int headerSize, uint8 *buffer, int bufferSize) { File fp (fileName, "wb"); - if (!fp.IsValid ()) + if (!fp.isValid ()) { return -1; + } + int i, length, node, ptr, last, cbp, bp = 0; + uint8 cb[17] = {0, }, mask, bit; - uint8 bit; + fp.write (header, headerSize, 1); + initTrees (); - fp.Write (header, headerSize, 1); - InitTree (); + cb[0] = 0; + cbp = mask = 1; + ptr = 0; + node = MAXBUF - PADDING; - codeBuffer[0] = 0; - codeBufferPtr = mask = 1; - strPtr = 0; - node = N - F; + for (i = ptr; i < node; i++) + m_buffer[i] = ' '; - for (i = strPtr; i < node; i++) - m_textBuffer[i] = ' '; - - for (length = 0; (length < F) && (bufferPtr < bufferSize); length++) - { - bit = buffer[bufferPtr++]; - m_textBuffer[node + length] = bit; + for (length = 0; (length < PADDING) && (bp < bufferSize); length++) { + bit = buffer[bp++]; + m_buffer[node + length] = bit; } - if ((m_textSize = length) == 0) + if (length == 0) { return -1; + } - for (i = 1; i <= F; i++) - InsertNode (node - i); - InsertNode (node); + for (i = 1; i <= PADDING; i++) { + insert (node - i); + } + insert (node); - do - { - if (m_matchLength > length) - m_matchLength = length; - - if (m_matchLength <= THRESHOLD) - { - m_matchLength = 1; - codeBuffer[0] |= mask; - codeBuffer[codeBufferPtr++] = m_textBuffer[node]; + do { + if (m_matchLen > length) { + m_matchLen = length; } - else - { - codeBuffer[codeBufferPtr++] = (uint8) m_matchPosition; - codeBuffer[codeBufferPtr++] = (uint8) (((m_matchPosition >> 4) & 0xf0) | (m_matchLength - (THRESHOLD + 1))); + if (m_matchLen <= THRESHOLD) { + m_matchLen = 1; + + cb[0] |= mask; + cb[cbp++] = m_buffer[node]; + } + else { + cb[cbp++] = (uint8) (m_matchPos & 0xff); + cb[cbp++] = (uint8) (((m_matchPos >> 4) & 0xf0) | (m_matchLen - (THRESHOLD + 1))); } - if ((mask <<= 1) == 0) - { - for (i = 0; i < codeBufferPtr; i++) - fp.PutChar (codeBuffer[i]); - - m_codeSize += codeBufferPtr; - codeBuffer[0] = 0; - codeBufferPtr = mask = 1; + if ((mask <<= 1) == 0) { + for (i = 0; i < cbp; i++) { + fp.putch (cb[i]); + } + m_csize += cbp; + cb[0] = 0; + cbp = mask = 1; } - lastMatchLength = m_matchLength; + last = m_matchLen; - for (i = 0; (i < lastMatchLength) && (bufferPtr < bufferSize); i++) - { - bit = buffer[bufferPtr++]; - DeleteNode (strPtr); + for (i = 0; (i < last) && (bp < bufferSize); i++) { + bit = buffer[bp++]; + erase (ptr); - m_textBuffer[strPtr] = bit; + m_buffer[ptr] = bit; - if (strPtr < F - 1) - m_textBuffer[strPtr + N] = bit; - - strPtr = (strPtr + 1) & (N - 1); - node = (node + 1) & (N - 1); - InsertNode (node); + if (ptr < PADDING - 1) { + m_buffer[ptr + MAXBUF] = bit; + } + ptr = (ptr + 1) & (MAXBUF - 1); + node = (node + 1) & (MAXBUF - 1); + insert (node); } - while (i++ < lastMatchLength) - { - DeleteNode (strPtr); + while (i++ < last) { + erase (ptr); - strPtr = (strPtr + 1) & (N - 1); - node = (node + 1) & (N - 1); + ptr = (ptr + 1) & (MAXBUF - 1); + node = (node + 1) & (MAXBUF - 1); - if (length--) - InsertNode (node); + if (length--) { + insert (node); + } } } while (length > 0); - if (codeBufferPtr > 1) - { - for (i = 0; i < codeBufferPtr; i++) - fp.PutChar (codeBuffer[i]); - - m_codeSize += codeBufferPtr; + if (cbp > 1) { + for (i = 0; i < cbp; i++) { + fp.putch (cb[i]); + } + m_csize += cbp; } - fp.Close (); + fp.close (); - return m_codeSize; + return m_csize; } - int InternalDecode (const char *fileName, int headerSize, uint8 *buffer, int bufferSize) - { + int decode_ (const char *fileName, int headerSize, uint8 *buffer, int bufferSize) { int i, j, k, node; unsigned int flags; - int bufferPtr = 0; + int bp = 0; uint8 bit; File fp (fileName, "rb"); - if (!fp.IsValid ()) + if (!fp.isValid ()) { return -1; + } + fp.seek (headerSize, SEEK_SET); - fp.Seek (headerSize, SEEK_SET); - - node = N - F; - for (i = 0; i < node; i++) - m_textBuffer[i] = ' '; + node = MAXBUF - PADDING; + for (i = 0; i < node; i++) { + m_buffer[i] = ' '; + } flags = 0; - for (;;) - { - if (((flags >>= 1) & 256) == 0) - { - int read = fp.GetChar (); + for (;;) { + if (((flags >>= 1) & 256) == 0) { + int read = fp.getch (); - if (read == EOF) + if (read == EOF) { break; - + } bit = static_cast (read); - flags = bit | 0xff00; - } - - if (flags & 1) - { - int read = fp.GetChar (); - - if (read == EOF) - break; - - bit = static_cast (read); - buffer[bufferPtr++] = bit; - - if (bufferPtr > bufferSize) - return -1; - - m_textBuffer[node++] = bit; - node &= (N - 1); } - else - { - if ((i = fp.GetChar ()) == EOF) - break; - if ((j = fp.GetChar ()) == EOF) + if (flags & 1) { + int read = fp.getch (); + + if (read == EOF) { break; + } + bit = static_cast (read); + buffer[bp++] = bit; + + if (bp > bufferSize) { + return -1; + } + m_buffer[node++] = bit; + node &= (MAXBUF - 1); + } + else { + if ((i = fp.getch ()) == EOF) { + break; + } + + if ((j = fp.getch ()) == EOF) { + break; + } i |= ((j & 0xf0) << 4); j = (j & 0x0f) + THRESHOLD; - for (k = 0; k <= j; k++) - { - bit = m_textBuffer[(i + k) & (N - 1)]; - buffer[bufferPtr++] = bit; + for (k = 0; k <= j; k++) { + bit = m_buffer[(i + k) & (MAXBUF - 1)]; + buffer[bp++] = bit; - if (bufferPtr > bufferSize) + if (bp > bufferSize) { return -1; - - m_textBuffer[node++] = bit; - node &= (N - 1); + } + m_buffer[node++] = bit; + node &= (MAXBUF - 1); } } } - fp.Close (); + fp.close (); - return bufferPtr; + return bp; } // external decoder - static int Uncompress (const char *fileName, int headerSize, uint8 *buffer, int bufferSize) - { - static Compressor compressor = Compressor (); - return compressor.InternalDecode (fileName, headerSize, buffer, bufferSize); + static int decode (const char *fileName, int headerSize, uint8 *buffer, int bufferSize) { + static Compress compressor; + return compressor.decode_ (fileName, headerSize, buffer, bufferSize); } // external encoder - static int Compress(const char *fileName, uint8 *header, int headerSize, uint8 *buffer, int bufferSize) - { - static Compressor compressor = Compressor (); - return compressor.InternalEncode (fileName, header, headerSize, buffer, bufferSize); + static int encode (const char *fileName, uint8 *header, int headerSize, uint8 *buffer, int bufferSize) { + static Compress compressor; + return compressor.encode_ (fileName, header, headerSize, buffer, bufferSize); } }; diff --git a/include/corelib.h b/include/corelib.h index b5fec2b..04c9c19 100644 --- a/include/corelib.h +++ b/include/corelib.h @@ -4,592 +4,418 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license +// // #pragma once -#include -#include -#include #include +#include +#include +#include -#include -#include -#include -#include -#include #include - +#include +#include +#include +#include #include #ifdef PLATFORM_WIN32 -#include + #include #else -#include + #include #endif -#ifdef ENABLE_SSE_INTRINSICS -#include +#ifndef PLATFORM_WIN32 + #define _unlink unlink + #define _mkdir(p) mkdir (p, 0777) + #define stricmp strcasecmp +#else + #define stricmp _stricmp #endif -// -// Basic Types -// -typedef signed char int8; -typedef signed short int16; -typedef signed long int32; -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned long uint32; +#ifdef PLATFORM_HAS_SSE2 + #include +#endif -// Fast stricmp got somewhere from chromium -static inline int A_stricmp (const char *str1, const char *str2, int length = -1) -{ - int iter = 0; +#undef min +#undef max - if (length == -1) - length = strlen (str1); +// to remove ugly A_ prefixes +namespace cr { - for (; iter < length; iter++) - { - if ((str1[iter] | 32) != (str2[iter] | 32)) - break; - } +namespace types { - if (iter != length) - return 1; +using int8 = signed char; +using int16 = signed short; +using int32 = signed long; +using uint8 = unsigned char; +using uint16 = unsigned short; +using uint32 = unsigned long; - return 0; } -// Cross platform strdup -static inline char *A_strdup (const char *str) -{ - return strcpy (new char[strlen (str) + 1], str); -} +using namespace cr::types; -// From metamod-p -static inline bool A_IsValidCodePointer (const void *ptr) -{ +constexpr float ONEPSILON = 0.01f; +constexpr float EQEPSILON = 0.001f; +constexpr float FLEPSILON = 1.192092896e-07f; + +constexpr float PI = 3.141592653589793115997963468544185161590576171875f; +constexpr float PI_RECIPROCAL = 1.0f / PI; +constexpr float PI_HALF = PI / 2; + +constexpr float D2R = PI / 180.0f; +constexpr float R2D = 180.0f / PI; + +// from metamod-p +static inline bool checkptr (const void *ptr) { #ifdef PLATFORM_WIN32 if (IsBadCodePtr (reinterpret_cast (ptr))) return false; #endif - (void) (ptr); - - // do not check on linux + (void)(ptr); return true; } -#ifndef PLATFORM_WIN32 -#define _unlink(p) unlink (p) -#define _mkdir(p) mkdir (p, 0777) -#endif - -// -// Title: Utility Classes Header -// - -// -// Function: Hash -// Hash template for , -// -template unsigned int Hash (const T &); - -// -// Function: Hash -// Hash for integer. -// -// Parameters: -// tag - Value that should be hashed. -// -// Returns: -// Hashed value. -// -template unsigned int Hash (const int &tag) -{ - unsigned int key = (unsigned int) tag; - - key += ~(key << 16); - key ^= (key >> 5); - - key += (key << 3); - key ^= (key >> 13); - - key += ~(key << 9); - key ^= (key >> 17); - - return key; +template constexpr size_t bufsize (const T (&)[N]) { + return N - 1; } -// -// Namespace: Math -// Some math utility functions. -// -namespace Math -{ - const float MATH_ONEPSILON = 0.01f; - const float MATH_EQEPSILON = 0.001f; - const float MATH_FLEPSILON = 1.192092896e-07f; +template constexpr size_t arrsize (const T (&)[N]) { + return N; +} - // - // Constant: MATH_PI - // Mathematical PI value. - // - const float MATH_PI = 3.141592653589793f; +constexpr float square (const float value) { + return value * value; +} - const float MATH_D2R = MATH_PI / 180.0f; - const float MATH_R2D = 180.0f / MATH_PI; +template constexpr T min (const T a, const T b) { + return a < b ? a : b; +} -#ifdef ENABLE_SSE_INTRINSICS +template constexpr T max (const T a, const T b) { + return a > b ? a : b; +} - // - // Function: sse_abs - // - // mm version if abs - // - static inline __m128 sse_abs (__m128 val) - { - return _mm_andnot_ps (_mm_castsi128_ps (_mm_set1_epi32 (0x80000000)), val); +template constexpr T clamp (const T x, const T a, const T b) { + return min (max (x, a), b); +} + +template constexpr T abs (const T a) { + return a > 0 ? a : -a; +} + +static inline float powf (const float x, const float y) { + union { + float d; + int x; + } res { x }; + + res.x = static_cast (y * (res.x - 1064866805) + 1064866805); + return res.d; +} + +static inline float sqrtf (const float value) { + return powf (value, 0.5f); +} + +static inline float sinf (const float value) { + const signed long sign = static_cast (value * PI_RECIPROCAL); + const float calc = (value - static_cast (sign) * PI); + + const float sqr = square (calc); + const float res = 1.00000000000000000000e+00f + sqr * (-1.66666671633720397949e-01f + sqr * (8.33333376795053482056e-03f + sqr * (-1.98412497411482036114e-04f + + sqr * (2.75565571428160183132e-06f + sqr * (-2.50368472620721149724e-08f + sqr * (1.58849267073435385100e-10f + sqr * -6.58925550841432672300e-13f)))))); + + return (sign & 1) ? -calc * res : value * res; +} + +static inline float cosf (const float value) { + const signed long sign = static_cast (value * PI_RECIPROCAL); + const float calc = (value - static_cast (sign) * PI); + + const float sqr = square (calc); + const float res = sqr * (-5.00000000000000000000e-01f + sqr * (4.16666641831398010254e-02f + sqr * (-1.38888671062886714935e-03f + sqr * (2.48006890615215525031e-05f + + sqr * (-2.75369927749125054106e-07f + sqr * (2.06207229069832465029e-09f + sqr * -9.77507137733812925262e-12f)))))); + + const float f = -1.00000000000000000000e+00f; + + return (sign & 1) ? f - res : -f + res; +} + +static inline float tanf (const float value) { + return sinf (value) / cosf (value); +} + +static inline float atan2f (const float y, const float x) { + auto atanf = [](const float x) { + const float sqr = square (x); + return x * (48.70107004404898384f + sqr * (49.5326263772254345f + sqr * 9.40604244231624f)) / (48.70107004404996166f + sqr * (65.7663163908956299f + sqr * (21.587934067020262f + sqr))); }; - // - // Function: sse_sine - // - // mm version if sine - // - static inline __m128 sse_sine (__m128 inp) - { - __m128 pi2 = _mm_set1_ps (MATH_PI * 2); - __m128 val = _mm_cmpnlt_ps (inp, _mm_set1_ps (MATH_PI)); + const float ax = abs (x); + const float ay = abs (y); + + if (ax < 1e-7f && ay < 1e-7f) { + return 0.0f; + } + + if (ax > ay) { + if (x < 0.0f) { + if (y >= 0.0f) { + return atanf (y / x) + PI; + } + return atanf (y / x) - PI; + } + return atanf (y / x); + } + + if (y < 0.0f) { + return atanf (-x / y) - PI_HALF; + } + return atanf (-x / y) + PI_HALF; +} + +static inline float ceilf (const float x) { + return static_cast (65536 - static_cast (65536.0f - x)); +} + +static inline void sincosf (const float x, const float y, const float z, float *sines, float *cosines) { + // this is the only place where sse2 stuff actually faster than chebyshev pads as we're can calculate 3 sines + 3 cosines + // using only two sse calls instead of 6 calls to sin/cos with standard functions + +#if defined (PLATFORM_HAS_SSE2) + auto rad = _mm_set_ps (x, y, z, 0.0f); + + auto _mm_sin = [] (__m128 rad) -> __m128 { + static auto pi2 = _mm_set_ps1 (PI * 2); + static auto rp1 = _mm_set_ps1 (4.0f / PI); + static auto rp2 = _mm_set_ps1 (-4.0f / (PI * PI)); + static auto val = _mm_cmpnlt_ps (rad, _mm_set_ps1 (PI)); + static auto csi = _mm_castsi128_ps (_mm_set1_epi32 (0x80000000)); val = _mm_and_ps (val, pi2); - inp = _mm_sub_ps (inp, val); - val = _mm_cmpngt_ps (inp, _mm_set1_ps (-MATH_PI)); + rad = _mm_sub_ps (rad, val); + val = _mm_cmpngt_ps (rad, _mm_set_ps1 (-PI)); val = _mm_and_ps (val, pi2); - inp = _mm_add_ps (inp, val); - val = _mm_mul_ps (sse_abs (inp), _mm_set1_ps (-4.0f / (MATH_PI * MATH_PI))); - val = _mm_add_ps (val, _mm_set1_ps (4.0f / MATH_PI)); + rad = _mm_add_ps (rad, val); + val = _mm_mul_ps (_mm_andnot_ps (csi, rad), rp2); + val = _mm_add_ps (val, rp1); - __m128 res = _mm_mul_ps (val, inp); + auto si = _mm_mul_ps (val, rad); - val = _mm_mul_ps (sse_abs (res), res); - val = _mm_sub_ps (val, res); - val = _mm_mul_ps (val, _mm_set1_ps (0.225f)); - res = _mm_add_ps (val, res); + val = _mm_mul_ps (_mm_andnot_ps (csi, si), si); + val = _mm_sub_ps (val, si); + val = _mm_mul_ps (val, _mm_set_ps1 (0.225f)); - return res; - } -#endif - // - // Function: A_sqrtf - // - // SIMD version of sqrtf. - // - static inline float A_sqrtf (float value) - { -#ifdef ENABLE_SSE_INTRINSICS - return _mm_cvtss_f32 (_mm_sqrt_ss (_mm_set1_ps (value))); + return _mm_add_ps (val, si); + }; + static auto hpi = _mm_set_ps1 (PI_HALF); + + auto s = _mm_sin (rad); + auto c = _mm_sin (_mm_add_ps (rad, hpi)); + + _mm_store_ps (sines, _mm_shuffle_ps (s, s, _MM_SHUFFLE (0, 1, 2, 3))); + _mm_store_ps (cosines, _mm_shuffle_ps (c, c, _MM_SHUFFLE (0, 1, 2, 3))); #else - return sqrtf (value); + sines[0] = sinf (x); + sines[1] = sinf (y); + sines[2] = sinf (z); + + cosines[0] = cosf (x); + cosines[1] = cosf (y); + cosines[2] = cosf (z); #endif - } +} - // own min/max implementation - template static inline T A_min (T a, T b) - { - return a < b ? a : b; - } +constexpr bool fzero (const float entry) { + return abs (entry) < ONEPSILON; +} - template static inline T A_max (T a, T b) - { - return a > b ? a : b; - } +constexpr bool fequal (const float entry1, const float entry2) { + return abs (entry1 - entry2) < EQEPSILON; +} - template static inline T A_clamp (T x, T a, T b) - { - return A_min (A_max (x, a), b); - } +constexpr float rad2deg (const float radian) { + return radian * R2D; +} - static inline float F_clamp (float x, float a, float b) - { -#ifdef ENABLE_SSE_INTRINSICS - return _mm_cvtss_f32 (_mm_min_ss (_mm_max_ss (_mm_set1_ps (x), _mm_set1_ps (a)), _mm_set1_ps (b))); -#else - return A_clamp (x, a, b); -#endif - } +constexpr float deg2rad (const float degree) { + return degree * D2R; +} - // - // Function: A_sinf - // - // SIMD version of sinf. - // - static inline float A_sinf (float value) - { -#ifdef ENABLE_SSE_INTRINSICS - return _mm_cvtss_f32 (sse_sine (_mm_set1_ps (value))); -#else - return sinf (value); -#endif - } +constexpr float angleMod (const float angle) { + return 360.0f / 65536.0f * (static_cast (angle * (65536.0f / 360.0f)) & 65535); +} - // - // Function: A_cosf - // - // SIMD version of cosf. - // - static inline float A_cosf (float value) - { -#ifdef ENABLE_SSE_INTRINSICS - return _mm_cvtss_f32 (sse_sine (_mm_set1_ps (value + MATH_PI / 2.0f))); -#else - return cosf (value); -#endif - } +constexpr float angleNorm (const float angle) { + return 360.0f / 65536.0f * (static_cast ((angle + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f; +} - // - // Function: A_sincosf - // - // SIMD version of sincosf. - // - static inline void A_sincosf (float rad, float *sine, float *cosine) - { -#ifdef ENABLE_SSE_INTRINSICS - __m128 m_sincos = sse_sine (_mm_set_ps (0.0f, 0.0f, rad + MATH_PI / 2.f, rad)); - __m128 m_cos = _mm_shuffle_ps (m_sincos, m_sincos, _MM_SHUFFLE (0, 0, 0, 1)); +constexpr float angleDiff (const float dest, const float src) { + return angleNorm (dest - src); +} - *sine = _mm_cvtss_f32 (m_sincos); - *cosine = _mm_cvtss_f32 (m_cos); -#else - *sine = sinf (rad); - *cosine = cosf (rad); -#endif - } +template struct ClearRef { + using Type = T; +}; - // - // Function: FltZero - // - // Checks whether input entry float is zero. - // - // Parameters: - // entry - Input float. - // - // Returns: - // True if float is zero, false otherwise. - // - // See Also: - // - // - // Remarks: - // This eliminates Intel C++ Compiler's warning about float equality/inquality. - // - static inline bool FltZero (float entry) - { - return fabsf (entry) < MATH_ONEPSILON; - } +template struct ClearRef { + using Type = T; +}; - // - // Function: FltEqual - // - // Checks whether input floats are equal. - // - // Parameters: - // entry1 - First entry float. - // entry2 - Second entry float. - // - // Returns: - // True if floats are equal, false otherwise. - // - // See Also: - // - // - // Remarks: - // This eliminates Intel C++ Compiler's warning about float equality/inquality. - // - static inline bool FltEqual (float entry1, float entry2) - { - return fabsf (entry1 - entry2) < MATH_EQEPSILON; - } +template struct ClearRef { + using Type = T; +}; - // - // Function: RadianToDegree - // - // Converts radians to degrees. - // - // Parameters: - // radian - Input radian. - // - // Returns: - // Degree converted from radian. - // - // See Also: - // - // - static inline float RadianToDegree (float radian) - { - return radian * MATH_R2D; - } +template static inline typename ClearRef ::Type &&move (T &&type) { + return static_cast ::Type &&> (type); +} - // - // Function: DegreeToRadian - // - // Converts degrees to radians. - // - // Parameters: - // degree - Input degree. - // - // Returns: - // Radian converted from degree. - // - // See Also: - // - // - static inline float DegreeToRadian (float degree) - { - return degree * MATH_D2R; - } +template static inline void swap (T &left, T &right) { + auto temp = move (left); + left = move (right); + right = move (temp); +} - // - // Function: AngleMod - // - // Adds or subtracts 360.0f enough times need to given angle in order to set it into the range [0.0, 360.0f). - // - // Parameters: - // angle - Input angle. - // - // Returns: - // Resulting angle. - // - static inline float AngleMod (float angle) - { - return 360.0f / 65536.0f * (static_cast (angle * (65536.0f / 360.0f)) & 65535); - } +template static constexpr inline T &&forward (typename ClearRef ::Type &type) noexcept { + return static_cast (type); +} - // - // Function: AngleNormalize - // - // Adds or subtracts 360.0f enough times need to given angle in order to set it into the range [-180.0, 180.0f). - // - // Parameters: - // angle - Input angle. - // - // Returns: - // Resulting angle. - // - static inline float AngleNormalize (float angle) - { - return 360.0f / 65536.0f * (static_cast ((angle + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f; - } +template static constexpr inline T &&forward (typename ClearRef ::Type &&type) noexcept { + return static_cast (type); +} - - // - // Function: SineCosine - // Very fast platform-dependent sine and cosine calculation routine. - // - // Parameters: - // rad - Input degree. - // sin - Output for Sine. - // cos - Output for Cosine. - // - static inline void SineCosine (float rad, float *sine, float *cosine) - { -#if defined (__ANDROID__) - *sine = sinf (rad); - *cosine = cosf (rad); -#else - A_sincosf (rad, sine, cosine); -#endif - } - - static inline float AngleDiff (float destAngle, float srcAngle) - { - return AngleNormalize (destAngle - srcAngle); +template static inline void transfer (T *dest, T *src, size_t length) { + for (size_t i = 0; i < length; i++) { + dest[i] = move (src[i]); } } -// -// Class: RandomSequenceOfUnique -// Random number generator used by the bot code. -// See: https://github.com/preshing/RandomSequence/ -// -class RandomSequenceOfUnique -{ +namespace classes { + +class NonCopyable { +protected: + NonCopyable (void) = default; + ~NonCopyable (void) = default; + +private: + NonCopyable (const NonCopyable &) = delete; + NonCopyable &operator = (const NonCopyable &) = delete; +}; + +template class Singleton : private NonCopyable { +public: + inline static T *ptr (void) { + return &ref (); + } + + inline static T &ref (void) { + static T ref; + return ref; + }; +}; + +// see: https://github.com/preshing/RandomSequence/ +class RandomSequence : public Singleton { private: unsigned int m_index; unsigned int m_intermediateOffset; unsigned long long m_divider; private: - unsigned int PermuteQPR (unsigned int x) - { - static const unsigned int prime = 4294967291u; + unsigned int premute (unsigned int x) { + static constexpr unsigned int prime = 4294967291u; - if (x >= prime) + if (x >= prime) { return x; - - unsigned int residue = (static_cast (x)* x) % prime; - + } + const unsigned int residue = (static_cast (x) * x) % prime; return (x <= prime / 2) ? residue : prime - residue; } - unsigned int Random (void) - { - return PermuteQPR ((PermuteQPR (m_index++) + m_intermediateOffset) ^ 0x5bf03635); + unsigned int random (void) { + return premute ((premute (m_index++) + m_intermediateOffset) ^ 0x5bf03635); } -public: - RandomSequenceOfUnique (void) - { - unsigned int seedBase = static_cast (time (nullptr)); - unsigned int seedOffset = seedBase + 1; - m_index = PermuteQPR (PermuteQPR (seedBase) + 0x682f0161); - m_intermediateOffset = PermuteQPR (PermuteQPR (seedOffset) + 0x46790905); +public: + RandomSequence (void) { + const unsigned int seedBase = static_cast (time (nullptr)); + const unsigned int seedOffset = seedBase + 1; + + m_index = premute (premute (seedBase) + 0x682f0161); + m_intermediateOffset = premute (premute (seedOffset) + 0x46790905); m_divider = (static_cast (1)) << 32; } - inline int Int (int low, int high) - { - return static_cast (Random () * (static_cast (high) - static_cast (low) + 1.0) / m_divider + static_cast (low)); + template inline U getInt (U low, U high) { + return static_cast (random () * (static_cast (high) - static_cast (low) + 1.0) / m_divider + static_cast (low)); } - inline float Float (float low, float high) - { - return static_cast (Random () * (static_cast (high) - static_cast (low)) / (m_divider - 1) + static_cast (low)); + inline float getFloat (float low, float high) { + return static_cast (random () * (static_cast (high) - static_cast (low)) / (m_divider - 1) + static_cast (low)); } }; -// -// Class: Vector -// Standard 3-dimensional vector. -// -class Vector -{ - // - // Group: Variables. - // +class Vector final { public: - // - // Variable: x,y,z - // X, Y and Z axis members. - // float x, y, z; - // - // Group: (Con/De)structors. - // public: - // - // Function: Vector - // - // Constructs Vector from float, and assign it's value to all axises. - // - // Parameters: - // scaler - Value for axises. - // - inline Vector (float scaler = 0.0f) : x (scaler), y (scaler), z (scaler) - { - } + inline Vector (float scaler = 0.0f) : x (scaler), y (scaler), z (scaler) {} + inline Vector (float inputX, float inputY, float inputZ) : x (inputX), y (inputY), z (inputZ) {} + inline Vector (float *other) : x (other[0]), y (other[1]), z (other[2]) {} + inline Vector (const Vector &right) : x (right.x), y (right.y), z (right.z) {} - // - // Function: Vector - // - // Standard Vector Constructor. - // - // Parameters: - // inputX - Input X axis. - // inputY - Input Y axis. - // inputZ - Input Z axis. - // - inline Vector (float inputX, float inputY, float inputZ) : x (inputX), y (inputY), z (inputZ) - { - } - - // - // Function: Vector - // - // Constructs Vector from float pointer. - // - // Parameters: - // other - Float pointer. - // - inline Vector (float *other) : x (other[0]), y (other[1]), z (other[2]) - { - } - - // - // Function: Vector - // - // Constructs Vector from another Vector. - // - // Parameters: - // right - Other Vector, that should be assigned. - // - inline Vector (const Vector &right) : x (right.x), y (right.y), z (right.z) - { - } - // - // Group: Operators. - // public: - inline operator float * (void) - { + inline operator float * (void) { return &x; } - inline operator const float * (void) const - { + inline operator const float * (void) const { return &x; } - - inline const Vector operator + (const Vector &right) const - { + inline const Vector operator+ (const Vector &right) const { return Vector (x + right.x, y + right.y, z + right.z); } - inline const Vector operator - (const Vector &right) const - { + inline const Vector operator- (const Vector &right) const { return Vector (x - right.x, y - right.y, z - right.z); } - inline const Vector operator - (void) const - { + inline const Vector operator- (void) const { return Vector (-x, -y, -z); } - friend inline const Vector operator * (const float vec, const Vector &right) - { + friend inline const Vector operator* (const float vec, const Vector &right) { return Vector (right.x * vec, right.y * vec, right.z * vec); } - inline const Vector operator * (float vec) const - { + inline const Vector operator* (float vec) const { return Vector (vec * x, vec * y, vec * z); } - inline const Vector operator / (float vec) const - { + inline const Vector operator/ (float vec) const { const float inv = 1 / vec; return Vector (inv * x, inv * y, inv * z); } // cross product - inline const Vector operator ^ (const Vector &right) const - { + inline const Vector operator^ (const Vector &right) const { return Vector (y * right.z - z * right.y, z * right.x - x * right.z, x * right.y - y * right.x); } // dot product - inline float operator | (const Vector &right) const - { + inline float operator| (const Vector &right) const { return x * right.x + y * right.y + z * right.z; } - inline const Vector &operator += (const Vector &right) - { + inline const Vector &operator+= (const Vector &right) { x += right.x; y += right.y; z += right.z; @@ -597,8 +423,7 @@ public: return *this; } - inline const Vector &operator -= (const Vector &right) - { + inline const Vector &operator-= (const Vector &right) { x -= right.x; y -= right.y; z -= right.z; @@ -606,8 +431,7 @@ public: return *this; } - inline const Vector &operator *= (float vec) - { + inline const Vector &operator*= (float vec) { x *= vec; y *= vec; z *= vec; @@ -615,8 +439,7 @@ public: return *this; } - inline const Vector &operator /= (float vec) - { + inline const Vector &operator/= (float vec) { const float inv = 1 / vec; x *= inv; @@ -626,3024 +449,1135 @@ public: return *this; } - inline bool operator == (const Vector &right) const - { - return Math::FltEqual (x, right.x) && Math::FltEqual (y, right.y) && Math::FltEqual (z, right.z); + inline bool operator== (const Vector &right) const { + return fequal (x, right.x) && fequal (y, right.y) && fequal (z, right.z); } - inline bool operator != (const Vector &right) const - { - return !Math::FltEqual (x, right.x) && !Math::FltEqual (y, right.y) && !Math::FltEqual (z, right.z); + inline bool operator!= (const Vector &right) const { + return !fequal (x, right.x) && !fequal (y, right.y) && !fequal (z, right.z); } - inline const Vector &operator = (const Vector &right) - { + inline const Vector &operator= (const Vector &right) { x = right.x; y = right.y; z = right.z; return *this; } - // - // Group: Functions. - // + public: - // - // Function: GetLength - // - // Gets length (magnitude) of 3D vector. - // - // Returns: - // Length (magnitude) of the 3D vector. - // - // See Also: - // - // - inline float GetLength (void) const - { - return Math::A_sqrtf (x * x + y * y + z * z); + inline float length (void) const { + return sqrtf (x * x + y * y + z * z); } - // - // Function: GetLength2D - // - // Gets length (magnitude) of vector ignoring Z axis. - // - // Returns: - // 2D length (magnitude) of the vector. - // - // See Also: - // - // - inline float GetLength2D (void) const - { - return Math::A_sqrtf (x * x + y * y); + inline float length2D (void) const { + return sqrtf (x * x + y * y); } - // - // Function: GetLengthSquared - // - // Gets squared length (magnitude) of 3D vector. - // - // Returns: - // Squared length (magnitude) of the 3D vector. - // - // See Also: - // - // - inline float GetLengthSquared (void) const - { + inline float lengthSq (void) const { return x * x + y * y + z * z; } - // - // Function: GetLengthSquared2D - // - // Gets squared length (magnitude) of vector ignoring Z axis. - // - // Returns: - // 2D squared length (magnitude) of the vector. - // - // See Also: - // - // - inline float GetLengthSquared2D (void) const - { - return x * x + y * y; - } - - // - // Function: Get2D - // - // Gets vector without Z axis. - // - // Returns: - // 2D vector from 3D vector. - // - inline Vector Get2D (void) const - { + inline Vector make2D (void) const { return Vector (x, y, 0.0f); } - // - // Function: Normalize - // - // Normalizes a vector. - // - // Returns: - // The previous length of the 3D vector. - // - inline Vector Normalize (void) const - { - float length = GetLength () + static_cast (Math::MATH_FLEPSILON); + inline Vector normalize (void) const { + float len = length () + static_cast (FLEPSILON); - if (Math::FltZero (length)) - return Vector (0, 0, 1.0f); - - length = 1.0f / length; - - return Vector (x * length, y * length, z * length); + if (fzero (len)) { + return Vector (0.0f, 0.0f, 1.0f); + } + len = 1.0f / len; + return Vector (x * len, y * len, z * len); } - // - // Function: Normalize - // - // Normalizes a 2D vector. - // - // Returns: - // The previous length of the 2D vector. - // - inline Vector Normalize2D (void) const - { - float length = GetLength2D () + static_cast (Math::MATH_FLEPSILON); + inline Vector normalize2D (void) const { + float len = length2D () + static_cast (FLEPSILON); - if (Math::FltZero (length)) - return Vector (0, 1.0, 0); - - length = 1.0f / length; - - return Vector (x * length, y * length, 0.0f); + if (fzero (len)) { + return Vector (0.0f, 1.0f, 0.0f); + } + len = 1.0f / len; + return Vector (x * len, y * len, 0.0f); } - // - // Function: IsZero - // - // Checks whether vector is null. - // - // Returns: - // True if this vector is (0.0f, 0.0f, 0.0f) within tolerance, false otherwise. - // - inline bool IsZero (void) const - { - return Math::FltZero (x) && Math::FltZero (y) && Math::FltZero (z); + inline bool empty (void) const { + return fzero (x) && fzero (y) && fzero (z); } - // - // Function: GetNull - // - // Gets a nulled vector. - // - // Returns: - // Nulled vector. - // - inline static const Vector &GetZero (void) - { + inline static const Vector &null (void) { static const Vector s_zero = Vector (0.0f, 0.0f, 0.0f); return s_zero; } - inline void Zero (void) - { - x = 0.0f; - y = 0.0f; - z = 0.0f; + inline void nullify (void) { + x = y = z = 0.0f; } - // - // Function: ClampAngles - // - // Clamps the angles (ignore Z component). - // - // Returns: - // 3D vector with clamped angles (ignore Z component). - // - inline Vector ClampAngles (void) - { - x = Math::AngleNormalize (x); - y = Math::AngleNormalize (y); + inline Vector clampAngles (void) { + x = angleNorm (x); + y = angleNorm (y); z = 0.0f; return *this; } - // - // Function: ToPitch - // - // Converts a spatial location determined by the vector passed into an absolute X angle (pitch) from the origin of the world. - // - // Returns: - // Pitch angle. - // - inline float ToPitch (void) const - { - if (Math::FltZero (x) && Math::FltZero (y)) + inline float toPitch (void) const { + if (fzero (x) && fzero (y)) { return 0.0f; - - return Math::RadianToDegree (atan2f (z, GetLength2D ())); + } + return rad2deg (atan2f (z, length2D ())); } - // - // Function: ToYaw - // - // Converts a spatial location determined by the vector passed into an absolute Y angle (yaw) from the origin of the world. - // - // Returns: - // Yaw angle. - // - inline float ToYaw (void) const - { - if (Math::FltZero (x) && Math::FltZero (y)) + inline float toYaw (void) const { + if (fzero (x) && fzero (y)) { return 0.0f; - - return Math::RadianToDegree (atan2f (y, x)); + } + return rad2deg (atan2f (y, x)); } - // - // Function: ToAngles - // - // Convert a spatial location determined by the vector passed in into constant absolute angles from the origin of the world. - // - // Returns: - // Converted from vector, constant angles. - // - inline Vector ToAngles (void) const - { - // is the input vector absolutely vertical? - if (Math::FltZero (x) && Math::FltZero (y)) + inline Vector toAngles (void) const { + if (fzero (x) && fzero (y)) { return Vector (z > 0.0f ? 90.0f : 270.0f, 0.0, 0.0f); - - // else it's another sort of vector compute individually the pitch and yaw corresponding to this vector. - return Vector (Math::RadianToDegree (atan2f (z, GetLength2D ())), Math::RadianToDegree (atan2f (y, x)), 0.0f); + } + return Vector (rad2deg (atan2f (z, length2D ())), rad2deg (atan2f (y, x)), 0.0f); } - // - // Function: BuildVectors - // - // Builds a 3D referential from a view angle, that is to say, the relative "forward", "right" and "upward" direction - // that a player would have if he were facing this view angle. World angles are stored in Vector structs too, the - // "x" component corresponding to the X angle (horizontal angle), and the "y" component corresponding to the Y angle - // (vertical angle). - // - // Parameters: - // forward - Forward referential vector. - // right - Right referential vector. - // upward - Upward referential vector. - // - inline void BuildVectors (Vector *forward, Vector *right, Vector *upward) const - { - float sinePitch = 0.0f, cosinePitch = 0.0f, sineYaw = 0.0f, cosineYaw = 0.0f, sineRoll = 0.0f, cosineRoll = 0.0f; + inline void makeVectors (Vector *forward, Vector *right, Vector *upward) const { + enum { pitch, yaw, roll, unused, max }; - Math::SineCosine (Math::DegreeToRadian (x), &sinePitch, &cosinePitch); // compute the sine and cosine of the pitch component - Math::SineCosine (Math::DegreeToRadian (y), &sineYaw, &cosineYaw); // compute the sine and cosine of the yaw component - Math::SineCosine (Math::DegreeToRadian (z), &sineRoll, &cosineRoll); // compute the sine and cosine of the roll component + float sines[max] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f }; - if (forward != nullptr) - { - forward->x = cosinePitch * cosineYaw; - forward->y = cosinePitch * sineYaw; - forward->z = -sinePitch; + // compute the sine and cosine compontents + sincosf (deg2rad (x), deg2rad (y), deg2rad (y), sines, cosines); + + if (forward) { + forward->x = cosines[pitch] * cosines[yaw]; + forward->y = cosines[pitch] * sines[yaw]; + forward->z = -sines[pitch]; } - if (right != nullptr) - { - right->x = -sineRoll * sinePitch * cosineYaw + cosineRoll * sineYaw; - right->y = -sineRoll * sinePitch * sineYaw - cosineRoll * cosineYaw; - right->z = -sineRoll * cosinePitch; + if (right) { + right->x = -sines[roll] * sines[pitch] * cosines[yaw] + cosines[roll] * sines[yaw]; + right->y = -sines[roll] * sines[pitch] * sines[yaw] - cosines[roll] * cosines[yaw]; + right->z = -sines[roll] * cosines[pitch]; } - if (upward != nullptr) - { - upward->x = cosineRoll * sinePitch * cosineYaw + sineRoll * sineYaw; - upward->y = cosineRoll * sinePitch * sineYaw - sineRoll * cosineYaw; - upward->z = cosineRoll * cosinePitch; + if (upward) { + upward->x = cosines[roll] * sines[pitch] * cosines[yaw] + sines[roll] * sines[yaw]; + upward->y = cosines[roll] * sines[pitch] * sines[yaw] - sines[roll] * cosines[yaw]; + upward->z = cosines[roll] * cosines[pitch]; } } }; -// -// Class: List -// Simple linked list container. -// -template class List -{ -public: - template class Node - { - public: - Node *m_next; - Node *m_prev; - T *m_data; - - public: - Node (void) - { - m_next = nullptr; - m_prev = nullptr; - m_data = nullptr; - } - }; - +class Library { private: - Node *m_first; - Node *m_last; - int m_count; + void *m_ptr; -// -// Group: (Con/De)structors public: - List (void) - { - Destory (); - } - - virtual ~List (void) { }; - -// -// Group: Functions -// -public: - - // - // Function: Destory - // Resets list to empty state by abandoning contents. - // - void Destory (void) - { - m_first = nullptr; - m_last = nullptr; - m_count = 0; - } - - // - // Function: GetSize - // Gets the number of elements in linked list. - // - // Returns: - // Number of elements in list. - // - inline int GetSize (void) const - { - return m_count; - } - - // - // Function: GetFirst - // Gets the first list entry. nullptr in case list is empty. - // - // Returns: - // First list entry. - // - inline T *GetFirst (void) const - { - if (m_first != nullptr) - return m_first->m_data; - - return nullptr; - }; - - // - // Function: GetLast - // Gets the last list entry. nullptr in case list is empty. - // - // Returns: - // Last list entry. - // - inline T *GetLast (void) const - { - if (m_last != nullptr) - return m_last->m_data; - - return nullptr; - }; - - // - // Function: GetNext - // Gets the next element from linked list. - // - // Parameters: - // current - Current node. - // - // Returns: - // Node data. - // - T *GetNext (T *current) - { - if (current == nullptr) - return GetFirst (); - - Node *next = FindNode (current)->m_next; - - if (next != nullptr) - return next->m_data; - - return nullptr; - } - - // - // Function: GetPrev - // Gets the previous element from linked list. - // - // Parameters: - // current - Current node. - // - // Returns: - // Node data. - // - T *GetPrev (T *current) - { - if (current == nullptr) - return GetLast (); - - Node *prev = FindNode (current)->m_prev; - - if (prev != nullptr) - return prev->m_prev; - - return nullptr; - } - - // - // Function: Link - // Adds item to linked list. - // - // Parameters: - // entry - Node that should be inserted in linked list. - // next - Next node to be inserted into linked list. - // - // Returns: - // Item if operation success, nullptr otherwise. - // - T *Link (T *entry, T *next = nullptr) - { - Node *prevNode = nullptr; - Node *nextNode = FindNode (next); - Node *newNode = new Node (); - - newNode->m_data = entry; - - if (nextNode == nullptr) - { - prevNode = m_last; - m_last = newNode; - } - else - { - prevNode = nextNode->m_prev; - nextNode->m_prev = newNode; - } - - if (prevNode == nullptr) - m_first = newNode; - else - prevNode->m_next = newNode; - - newNode->m_next = nextNode; - newNode->m_prev = prevNode; - - m_count++; - - return entry; - } - - // - // Function: Link - // Adds item to linked list (as reference). - // - // Parameters: - // entry - Node that should be inserted in linked list. - // next - Next node to be inserted into linked list. - // - // Returns: - // Item if operation success, nullptr otherwise. - // - T *Link (T &entry, T *next = nullptr) - { - T *newEntry = new T (); - *newEntry = entry; - - return Link (newEntry, next); - } - - // - // Function: Unlink - // Removes element from linked list. - // - // Parameters: - // entry - Element that should be moved out of list. - // - void Unlink (T *entry) - { - Node *entryNode = FindNode (entry); - - if (entryNode == nullptr) - return; - - if (entryNode->m_prev == nullptr) - m_first = entryNode->m_next; - else - entryNode->m_prev->m_next = entryNode->m_next; - - if (entryNode->m_next == nullptr) - m_last = entryNode->m_prev; - else - entryNode->m_next->m_prev = entryNode->m_prev; - - delete entryNode; - m_count--; - } - - // - // Function: Allocate - // Inserts item into linked list, and allocating it automatically. - // - // Parameters: - // next - Optional next element. - // - // Returns: - // Item that was inserted. - // - T *Allocate (T *next = nullptr) - { - T *entry = new T (); - - return Link (entry, next); - } - - // - // Function: Destory - // Removes element from list, and destroys it. - // - // Parameters: - // entry - Entry to perform operation on. - // - void Destory (T *entry) - { - Unlink (entry); - delete entry; - } - - // - // Function: RemoveAll - // Removes all elements from list, and destroys them. - // - void RemoveAll (void) - { - Node *node = nullptr; - - while ((node = GetIterator (node)) != nullptr) - { - Node *nodeToKill = node; - node = node->m_prev; - - free (nodeToKill->m_data); - } - } - - // - // Function: FindNode - // Find node by it's entry. - // - // Parameters: - // entry - Entry to search. - // - // Returns: - // Node pointer. - // - Node *FindNode (T *entry) - { - Node *iter = nullptr; - - while ((iter = GetIterator (iter)) != nullptr) - { - if (iter->m_data == entry) - return iter; - } - return nullptr; - } - - // - // Function: GetIterator - // Utility node iterator. - // - // Parameters: - // entry - Previous entry. - // - // Returns: - // Node pointer. - // - Node *GetIterator (Node *entry = nullptr) - { - if (entry == nullptr) - return m_first; - - return entry->m_next; - } -}; - -// -// Class: Array -// Universal template array container. -// -template class Array -{ -private: - T *m_elements; - - int m_resizeStep; - int m_itemSize; - int m_itemCount; - -// -// Group: (Con/De)structors -// -public: - - // - // Function: Array - // Default array constructor. - // - // Parameters: - // resizeStep - Array resize step, when new items added, or old deleted. - // - Array (int resizeStep = 0) - { - m_elements = nullptr; - m_itemSize = 0; - m_itemCount = 0; - m_resizeStep = resizeStep; - } - - // - // Function: Array - // Array copying constructor. - // - // Parameters: - // other - Other array that should be assigned to this one. - // - Array (const Array &other) - { - m_elements = nullptr; - m_itemSize = 0; - m_itemCount = 0; - m_resizeStep = 0; - - AssignFrom (other); - } - - // - // Function: ~Array - // Default array destructor. - // - virtual ~Array(void) - { - Destory (); - } - -// -// Group: Functions -// -public: - - // - // Function: Destory - // Destroys array object, and all elements. - // - void Destory (void) - { - delete [] m_elements; - - m_elements = nullptr; - m_itemSize = 0; - m_itemCount = 0; - } - - // - // Function: SetSize - // Sets the size of the array. - // - // Parameters: - // newSize - Size to what array should be resized. - // keepData - Keep exiting data, while resizing array or not. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool SetSize (int newSize, bool keepData = true) - { - if (newSize == 0) - { - Destory (); - return true; - } - - int checkSize = 0; - - if (m_resizeStep != 0) - checkSize = m_itemCount + m_resizeStep; - else - { - checkSize = m_itemCount / 8; - - if (checkSize < 4) - checkSize = 4; - - if (checkSize > 1024) - checkSize = 1024; - - checkSize += m_itemCount; - } - - if (newSize > checkSize) - checkSize = newSize; - - T *buffer = new T[checkSize]; - - if (keepData && m_elements != nullptr) - { - if (checkSize < m_itemCount) - m_itemCount = checkSize; - - for (int i = 0; i < m_itemCount; i++) - buffer[i] = m_elements[i]; - } - delete [] m_elements; - - m_elements = buffer; - m_itemSize = checkSize; - - return true; - } - - // - // Function: GetSize - // Gets allocated size of array. - // - // Returns: - // Number of allocated items. - // - int GetSize (void) const - { - return m_itemSize; - } - - // - // Function: GetElementNumber - // Gets real number currently in array. - // - // Returns: - // Number of elements. - // - int GetElementNumber (void) const - { - return m_itemCount; - } - - // - // Function: SetEnlargeStep - // Sets step, which used while resizing array data. - // - // Parameters: - // resizeStep - Step that should be set. - // - void SetEnlargeStep (int resizeStep = 0) - { - m_resizeStep = resizeStep; - } - - // - // Function: GetEnlargeStep - // Gets the current enlarge step. - // - // Returns: - // Current resize step. - // - int GetEnlargeStep (void) - { - return m_resizeStep; - } - - // - // Function: SetAt - // Sets element data, at specified index. - // - // Parameters: - // index - Index where object should be assigned. - // object - Object that should be assigned. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool SetAt (int index, T object, bool enlarge = true) - { - if (index >= m_itemSize) - { - if (!enlarge || !SetSize (index + 1)) - return false; - } - m_elements[index] = object; - - if (index >= m_itemCount) - m_itemCount = index + 1; - - return true; - } - - // - // Function: GetAt - // Gets element from specified index - // - // Parameters: - // index - Element index to retrieve. - // - // Returns: - // Element object. - // - T &GetAt (int index) - { - return m_elements[index]; - } - - // - // Function: GetAt - // Gets element at specified index, and store it in reference object. - // - // Parameters: - // index - Element index to retrieve. - // object - Holder for element reference. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool GetAt (int index, T &object) - { - if (index >= m_itemCount) - return false; - - object = m_elements[index]; - return true; - } - - // - // Function: InsertAt - // Inserts new element at specified index. - // - // Parameters: - // index - Index where element should be inserted. - // object - Object that should be inserted. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool InsertAt (int index, T object, bool enlarge = true) - { - return InsertAt (index, &object, 1, enlarge); - } - - // - // Function: InsertAt - // Inserts number of element at specified index. - // - // Parameters: - // index - Index where element should be inserted. - // objects - Pointer to object list. - // count - Number of element to insert. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool InsertAt (int index, T *objects, int count = 1, bool enlarge = true) - { - if (objects == nullptr || count < 1) - return false; - - int newSize = 0; - - if (m_itemCount > index) - newSize = m_itemCount + count; - else - newSize = index + count; - - if (newSize >= m_itemSize) - { - if (!enlarge || !SetSize (newSize)) - return false; - } - - if (index >= m_itemCount) - { - for (int i = 0; i < count; i++) - m_elements[i + index] = objects[i]; - - m_itemCount = newSize; - } - else - { - int i = 0; - - for (i = m_itemCount; i > index; i--) - m_elements[i + count - 1] = m_elements[i - 1]; - - for (i = 0; i < count; i++) - m_elements[i + index] = objects[i]; - - m_itemCount += count; - } - return true; - } - - // - // Function: InsertAt - // Inserts other array reference into the our array. - // - // Parameters: - // index - Index where element should be inserted. - // objects - Pointer to object list. - // count - Number of element to insert. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool InsertAt (int index, Array &other, bool enlarge = true) - { - if (&other == this) - return false; - - return InsertAt (index, other.m_elements, other.m_itemCount, enlarge); - } - - // - // Function: RemoveAt - // Removes elements from specified index. - // - // Parameters: - // index - Index, where element should be removed. - // count - Number of elements to remove. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool RemoveAt (int index, int count = 1) - { - if (index + count > m_itemCount) - return false; - - if (count < 1) - return true; - - m_itemCount -= count; - - for (int i = index; i < m_itemCount; i++) - m_elements[i] = m_elements[i + count]; - - return true; - } - - // - // Function: Push - // Appends element to the end of array. - // - // Parameters: - // object - Object to append. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool Push (T object, bool enlarge = true) - { - return InsertAt (m_itemCount, &object, 1, enlarge); - } - - // - // Function: Push - // Appends number of elements to the end of array. - // - // Parameters: - // objects - Pointer to object list. - // count - Number of element to insert. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool Push (T *objects, int count = 1, bool enlarge = true) - { - return InsertAt (m_itemCount, objects, count, enlarge); - } - - // - // Function: Push - // Inserts other array reference into the our array. - // - // Parameters: - // objects - Pointer to object list. - // count - Number of element to insert. - // enlarge - Checks whether array must be resized in case, allocated size + enlarge step is exceeded. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool Push (Array &other, bool enlarge = true) - { - if (&other == this) - return false; - - return InsertAt (m_itemCount, other.m_elements, other.m_itemCount, enlarge); - } - - // - // Function: GetData - // Gets the pointer to all element in array. - // - // Returns: - // Pointer to object list. - // - T *GetData (void) - { - return m_elements; - } - - // - // Function: RemoveAll - // Resets array, and removes all elements out of it. - // - void RemoveAll (void) - { - m_itemCount = 0; - SetSize (m_itemCount); - } - - // - // Function: IsEmpty - // Checks whether element is empty. - // - // Returns: - // True if element is empty, false otherwise. - // - inline bool IsEmpty (void) - { - return m_itemCount <= 0; - } - - // - // Function: FreeExtra - // Frees unused space. - // - void FreeSpace (bool destroyIfEmpty = true) - { - if (m_itemCount == 0) - { - if (destroyIfEmpty) - Destory (); - + Library (const char *filename) : m_ptr (nullptr) { + if (!filename) { return; } + load (filename); + } - T *buffer = new T[m_itemCount]; - - if (m_elements != nullptr) - { - for (int i = 0; i < m_itemCount; i++) - buffer[i] = m_elements[i]; + virtual ~Library (void) { + if (!isValid ()) { + return; } - delete [] m_elements; - - m_elements = buffer; - m_itemSize = m_itemCount; +#ifdef PLATFORM_WIN32 + FreeLibrary ((HMODULE)m_ptr); +#else + dlclose (m_ptr); +#endif } - // - // Function: Pop - // Pops element from array. - // - // Returns: - // Object popped from the end of array. - // - T Pop (void) - { - T element = m_elements[m_itemCount - 1]; - RemoveAt (m_itemCount - 1); - - return element; +public: + inline void *load (const char *filename) { +#ifdef PLATFORM_WIN32 + m_ptr = LoadLibrary (filename); +#else + m_ptr = dlopen (filename, RTLD_NOW); +#endif + return m_ptr; } - T &Last (void) - { - return m_elements[m_itemCount - 1]; + template R resolve (const char *function) { + if (!isValid ()) { + return nullptr; + } + return (R) +#ifdef PLATFORM_WIN32 + GetProcAddress (static_cast (m_ptr), function); +#else + dlsym (m_ptr, function); +#endif } - bool GetLast (T &item) - { - if (m_itemCount <= 0) - return false; - - item = m_elements[m_itemCount - 1]; - - return true; + template R handle (void) { + return static_cast (m_ptr); } - // - // Function: AssignFrom - // Reassigns current array with specified one. - // - // Parameters: - // other - Other array that should be assigned. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool AssignFrom (const Array &other) - { - if (&other == this) - return true; - - if (!SetSize (other.m_itemCount, false)) - return false; - - for (int i = 0; i < other.m_itemCount; i++) - m_elements[i] = other.m_elements[i]; - - m_itemCount = other.m_itemCount; - m_resizeStep = other.m_resizeStep; - - return true; - } - - // - // Function: GetRandomElement - // Gets the random element from the array. - // - // Returns: - // Random element reference. - // - T &GetRandomElement (void) const - { - extern class RandomSequenceOfUnique Random; - - return m_elements[Random.Int (0, m_itemCount - 1)]; - } - - Array &operator = (const Array &other) - { - AssignFrom (other); - return *this; - } - - T &operator [] (int index) - { - if (index < m_itemSize && index >= m_itemCount) - m_itemCount = index + 1; - - return GetAt (index); + inline bool isValid (void) const { + return m_ptr != nullptr; } }; -// -// Class: Map -// Represents associative map container. -// -template class Map -{ +template class Pair final { public: - struct MapItem - { - K key; - V value; - }; + A first; + B second; + +public: + Pair (A a, B b) : first (a), second (b) { + } + +public: + Pair (void) = default; + ~Pair (void) = default; +}; + +template class Array : private NonCopyable { +public: + static constexpr size_t INVALID_INDEX = static_cast (-1); protected: - struct HashItem - { - public: - int m_index; - HashItem *m_next; + T *m_data; + size_t m_capacity; + size_t m_length; - public: - HashItem (void) - { - m_next = nullptr; - m_index = 0; +public: + Array (void) : m_data (nullptr), m_capacity (0), m_length (0) { + } + + Array (Array &&other) noexcept { + m_data = other.m_data; + m_length = other.m_length; + m_capacity = other.m_capacity; + + other.reset (); + } + + virtual ~Array (void) { + destroy (); + } + +public: + void destroy (void) { + delete[] m_data; + reset (); + } + + void reset (void) { + m_data = nullptr; + m_capacity = 0; + m_length = 0; + } + + bool reserve (size_t growSize) { + if (m_length + growSize < m_capacity) { + return true; + } + size_t maxSize = max (m_capacity + sizeof (T), static_cast (16)); + + while (m_length + growSize > maxSize) { + maxSize *= 2; } - HashItem (int index, HashItem *next) - { - m_index = index; - m_next = next; - } - }; - - int m_hashSize; - HashItem **m_table; - Array m_mapTable; - -// Group: (Con/De)structors -public: - - // - // Function: Map - // Default constructor for map container. - // - // Parameters: - // hashSize - Initial hash size. - // - Map (int hashSize = 36) - { - m_hashSize = hashSize; - m_table = new HashItem *[hashSize]; - - for (int i = 0; i < hashSize; i++) - m_table[i] = 0; - } - - // - // Function: ~Map - // Default map container destructor. - // - // Parameters: - // hashSize - Initial hash size. - // - virtual ~Map (void) - { - RemoveAll (); - delete [] m_table; - } - -// Group: Functions -public: - - // - // Function: IsExists - // Checks whether specified element exists in container. - // - // Parameters: - // keyName - Key that should be looked up. - // - // Returns: - // True if key exists, false otherwise. - // - inline bool IsExists (const K &keyName) - { - return GetIndex (keyName, false) >= 0; - } - - // - // Function: SetupMap - // Initializes map, if not initialized automatically. - // - // Parameters: - // hashSize - Initial hash size. - // - inline void SetupMap (int hashSize) - { - m_hashSize = hashSize; - m_table = new HashItem *[hashSize]; - - for (int i = 0; i < hashSize; i++) - m_table[i] = 0; - } - - // - // Function: IsEmpty - // Checks whether map container is currently empty. - // - // Returns: - // True if no elements exists, false otherwise. - // - inline bool IsEmpty (void) const - { - return m_mapTable.GetSize () == 0; - } - - // - // Function: GetSize - // Retrieves size of the map container. - // - // Returns: - // Number of elements currently in map container. - // - inline int GetSize (void) const - { - return m_mapTable.GetSize (); - } - - // - // Function: GetKey - // Gets the key object, by it's index. - // - // Parameters: - // index - Index of key. - // - // Returns: - // Object containing the key. - // - inline const K &GetKey (int index) const - { - return m_mapTable[index].key; - } - - // - // Function: GetValue - // Gets the constant value object, by it's index. - // - // Parameters: - // index - Index of value. - // - // Returns: - // Object containing the value. - // - inline const V &GetValue (int index) const - { - return m_mapTable[index].value; - } - - // Function: GetValue - // Gets the value object, by it's index. - // - // Parameters: - // index - Index of value. - // - // Returns: - // Object containing the value. - // - inline V &GetValue (int index) - { - return m_mapTable[index].value; - } - - // - // Function: GetElements - // Gets the all elements of container. - // - // Returns: - // Array of elements, containing inside container. - // - // See Also: - // - // - inline Array &GetElements (void) - { - return m_mapTable; - } - - V &operator [] (const K &keyName) - { - int index = GetIndex (keyName, true); - return m_mapTable[index].value; - } - - // - // Function: Find - // Finds element by his key name. - // - // Parameters: - // keyName - Key name to be searched. - // element - Holder for element object. - // - // Returns: - // True if element found, false otherwise. - // - bool Find (const K &keyName, V &element) const - { - int index = const_cast *> (this)->GetIndex (keyName, false); - - if (index < 0) + if (maxSize >= INT_MAX / sizeof (T)) { + assert (!"Allocation Overflow!"); return false; + } + auto buffer = new T[maxSize]; + + if (m_data != nullptr) { + if (maxSize < m_length) { + m_length = maxSize; + } + transfer (buffer, m_data, m_length); + delete[] m_data; + } + m_data = move (buffer); + m_capacity = move (maxSize); - element = m_mapTable[index].value; return true; } - // - // Function: Find - // Finds element by his key name. - // - // Parameters: - // keyName - Key name to be searched. - // elementPtr - Holder for element pointer. - // - // Returns: - // True if element found, false otherwise. - // - bool Find (const K &keyName, V *&elementPtr) const - { - int index = const_cast *> (this)->GetIndex (keyName, false); + bool resize (size_t newSize) { + bool res = reserve (newSize); - if (index < 0) - return false; + while (m_length < newSize) { + push (T ()); + } + return res; + } - elementPtr = const_cast (&m_mapTable[index].value); + inline size_t length (void) const { + return m_length; + } + + inline size_t capacity (void) const { + return m_capacity; + } + + bool set (size_t index, T object) { + if (index >= m_capacity) { + if (!reserve (index + 2)) { + return false; + } + } + m_data[index] = forward (object); + + if (index >= m_length) { + m_length = index + 1; + } return true; } - // - // Function: Remove - // Removes element from container. - // - // Parameters: - // keyName - Key name of element, that should be removed. - // - // Returns: - // True if key was removed successfully, false otherwise. - // - bool Remove (const K &keyName) - { - int hashId = Hash (keyName) % m_hashSize; - HashItem *hashItem = m_table[hashId], *nextHash = 0; - - while (hashItem != nullptr) - { - if (m_mapTable[hashItem->m_index].key == keyName) - { - if (nextHash == 0) - m_table[hashId] = hashItem->m_next; - else - nextHash->m_next = hashItem->m_next; - - m_mapTable.RemoveAt (hashItem->m_index); - delete hashItem; - - return true; - } - nextHash = hashItem; - hashItem = hashItem->m_next; - } - return false; + inline T &at (size_t index) { + return m_data[index]; } - // - // Function: RemoveAll - // Removes all elements from container. - // - void RemoveAll (void) - { - for (int i = m_hashSize; --i >= 0;) - { - HashItem *ptr = m_table[i]; - - while (ptr != nullptr) - { - HashItem *m_next = ptr->m_next; - - delete ptr; - ptr = m_next; - } - m_table[i] = 0; + bool at (size_t index, T &object) { + if (index >= m_length) { + return false; } - m_mapTable.RemoveAll (); + object = m_data[index]; + return true; } - // - // Function: GetIndex - // Gets index of element. - // - // Parameters: - // keyName - Key of element. - // create - If true and no element found by a keyName, create new element. - // - // Returns: - // Either found index, created index, or -1 in case of error. - // - int GetIndex (const K &keyName, bool create) - { - int hashId = Hash (keyName) % m_hashSize; + bool insert (size_t index, T object) { + return insert (index, &object, 1); + } - for (HashItem *ptr = m_table[hashId]; ptr != nullptr; ptr = ptr->m_next) - { - if (m_mapTable[ptr->m_index].key == keyName) - return ptr->m_index; + bool insert (size_t index, T *objects, size_t count = 1) { + if (!objects || !count) { + return false; + } + size_t newSize = (m_length > index ? m_length : index) + count; + + if (newSize >= m_capacity && !reserve (newSize)) { + return false; } - if (create) - { - int item = m_mapTable.GetSize (); - m_mapTable.SetSize (static_cast (item + 1)); - - m_table[hashId] = new HashItem (item, m_table[hashId]); - m_mapTable[item].key = keyName; - - return item; + if (index >= m_length) { + for (size_t i = 0; i < count; i++) { + m_data[i + index] = forward (objects[i]); + } + m_length = newSize; } - return -1; + else { + size_t i = 0; + + for (i = m_length; i > index; i--) { + m_data[i + count - 1] = move (m_data[i - 1]); + } + for (i = 0; i < count; i++) { + m_data[i + index] = forward (objects[i]); + } + m_length += count; + } + return true; + } + + bool insert (size_t at, Array &other) { + if (&other == this) { + return false; + } + return insert (at, other.m_data, other.m_length); + } + + bool erase (size_t index, size_t count) { + if (index + count > m_capacity) { + return false; + } + m_length -= count; + + for (size_t i = index; i < m_length; i++) { + m_data[i] = move (m_data[i + count]); + } + return true; + } + + bool shift (void) { + return erase (0, 1); + } + + bool unshift (T object) { + return insert (0, &object); + } + + bool erase (T &item) { + return erase (index (item), 1); + } + + bool push (T object) { + return insert (m_length, &object); + } + + bool push (T *objects, size_t count) { + return insert (m_length, objects, count); + } + + bool extend (Array &other) { + if (&other == this) { + return false; + } + return insert (m_length, other.m_data, other.m_length); + } + + void clear (void) { + m_length = 0; + } + + inline bool empty (void) const { + return m_length <= 0; + } + + void shrink (bool destroyEmpty = true) { + if (!m_length) { + if (destroyEmpty) { + destroy (); + } + return; + } + T *buffer = new T[m_length]; + + if (m_data != nullptr) { + for (size_t i = 0; i < m_length; i++) { + transfer (buffer, m_data); + } + delete[] m_data; + } + m_data = move (buffer); + m_capacity = move (m_length); + } + + size_t index (T &item) { + return &item - &m_data[0]; + } + + T pop (void) { + T item = move (m_data[m_length - 1]); + erase (m_length - 1, 1); + + return item; + } + + const T &back (void) const { + return m_data[m_length - 1]; + } + + T &back (void) { + return m_data[m_length - 1]; + } + + bool last (T &item) { + if (m_length <= 0) { + return false; + } + item = m_data[m_length - 1]; + return true; + } + + bool assign (const Array &other) { + if (&other == this) { + return true; + } + if (!reserve (other.m_length)) { + return false; + } + transfer (m_data, other.m_data, other.m_length); + m_length = other.m_length; + + return true; + } + + void reverse (void) { + for (size_t i = 0; i < m_length / 2; i++) { + swap (m_data[i], m_data[m_length - 1 - i]); + } + } + + T &random (void) const { + return m_data[RandomSequence::ref ().getInt (0, static_cast (m_length - 1))]; + } + + Array &operator = (Array &&other) noexcept { + destroy (); + + m_data = other.m_data; + m_length = other.m_length; + m_capacity = other.m_capacity; + + other.reset (); + return *this; + } + + T &operator [] (size_t index) { + return at (index); + } + + const T &operator [] (size_t index) const { + return at (index); + } + + T *begin (void) { + return m_data; + } + + T *begin (void) const { + return m_data; + } + + T *end (void) { + return m_data + m_length; + } + + T *end (void) const { + return m_data + m_length; } }; -// -// Class: String -// Reference counted string class. -// -class String -{ +template class BinaryHeap final : private Array > { private: - char *m_bufferPtr; - int m_allocatedSize; - int m_stringLength; + using type = Pair ; + using base = Array ; + + using base::m_data; + using base::m_length; + +public: + BinaryHeap (void) { + base::reserve (Capacity); + } + BinaryHeap (BinaryHeap &&other) noexcept : base (move (other)) { } + +public: + void push (const A &first, const B &second) { + push ({ first, second }); + } + + void push (type &&pair) { + base::push (forward (pair)); + + if (length () > 1) { + siftUp (); + } + } + + A pop (void) { + assert (!empty ()); + + if (length () == 1) { + return base::pop ().first; + } + type value = move (m_data[0]); + + m_data[0] = move (base::back ()); + base::pop (); + + siftDown (); + return value.first; + } + + inline bool empty (void) const { + return !length (); + } + + inline size_t length (void) const { + return m_length; + } + + inline void clear (void) { + base::clear (); + } + + void siftUp (void) { + size_t child = m_length - 1; + + while (child != 0) { + size_t parentIndex = getParent (child); + + if (m_data[parentIndex].second <= m_data[child].second) { + break; + } + swap (m_data[child], m_data[parentIndex]); + child = parentIndex; + } + } + + void siftDown (void) { + size_t parent = 0; + size_t leftIndex = getLeft (parent); + + auto ref = move (m_data[parent]); + + while (leftIndex < m_length) { + size_t rightIndex = getRight (parent); + + if (rightIndex < m_length) { + if (m_data[rightIndex].second < m_data[leftIndex].second) { + leftIndex = rightIndex; + } + } + + if (ref.second <= m_data[leftIndex].second) { + break; + } + m_data[parent] = move (m_data[leftIndex]); + + parent = leftIndex; + leftIndex = getLeft (parent); + } + m_data[parent] = move (ref); + } + + BinaryHeap &operator = (BinaryHeap &&other) noexcept { + base::operator = (move (other)); + return *this; + } -// -// Group: Private functions -// private: - - // - // Function: UpdateBufferSize - // Updates the buffer size. - // - // Parameters: - // size - New size of buffer. - // - void UpdateBufferSize (int size) - { - if (size <= m_allocatedSize) - return; - - m_allocatedSize = size + 16; - char *tempBuffer = new char[size + 1]; - - if (m_bufferPtr != nullptr) - { - strcpy (tempBuffer, m_bufferPtr); - tempBuffer[m_stringLength] = 0; - - delete [] m_bufferPtr; - } - m_bufferPtr = tempBuffer; - m_allocatedSize = size; + static constexpr size_t getLeft (size_t index) { + return index << 1 | 1; } - // - // Function: MoveItems - // Moves characters inside buffer pointer. - // - // Parameters: - // destIndex - Destination index. - // sourceIndex - Source index. - // - void MoveItems (int destIndex, int sourceIndex) - { - memmove (m_bufferPtr + destIndex, m_bufferPtr + sourceIndex, sizeof (char) *(m_stringLength - sourceIndex + 1)); + static constexpr size_t getRight (size_t index) { + return ++index << 1; } - // - // Function: Initialize - // Initializes string buffer. - // - // Parameters: - // length - Initial length of string. - // - void Initialize (int length) - { - int freeSize = m_allocatedSize - m_stringLength - 1; - - if (length <= freeSize) - return; - - int delta = 4; - - if (m_allocatedSize > 64) - delta = static_cast (m_allocatedSize * 0.5); - else if (m_allocatedSize > 8) - delta = 16; - - if (freeSize + delta < length) - delta = length - freeSize; - - UpdateBufferSize (m_allocatedSize + delta); + static constexpr size_t getParent (size_t index) { + return --index >> 1; } +}; - // - // Function: CorrectIndex - // Gets the correct string end index. - // - // Parameters: - // index - Holder for index. - // - void CorrectIndex (int &index) const - { - if (index > m_stringLength) - index = m_stringLength; - } - - // - // Function: InsertSpace - // Inserts space at specified location, with specified length. - // - // Parameters: - // index - Location to insert space. - // size - Size of space insert. - // - void InsertSpace (int &index, int size) - { - CorrectIndex (index); - Initialize (size); - - MoveItems (index + size, index); - } - - // - // Function: IsTrimChar - // Checks whether input is trimming character. - // - // Parameters: - // input - Input to check for. - // - // Returns: - // True if it's a trim char, false otherwise. - // - bool IsTrimChar (char input) - { - return input == ' ' || input == '\t' || input == '\n'; - } - -// -// Group: (Con/De)structors -// -public: - String (void) - { - m_bufferPtr = nullptr; - m_allocatedSize = 0; - m_stringLength = 0; - } - - ~String (void) - { - delete [] m_bufferPtr; - } - - String (const char *bufferPtr) - { - m_bufferPtr = nullptr; - m_allocatedSize = 0; - m_stringLength = 0; - - Assign (bufferPtr); - } - - String (char input) - { - m_bufferPtr = nullptr; - m_allocatedSize = 0; - m_stringLength = 0; - - Assign (input); - } - - String (const String &inputString) - { - m_bufferPtr = nullptr; - m_allocatedSize = 0; - m_stringLength = 0; - - Assign (inputString.GetBuffer ()); - } - -// -// Group: Functions -// -public: - - // - // Function: GetBuffer - // Gets the string buffer. - // - // Returns: - // Pointer to buffer. - // - const char *GetBuffer (void) - { - if (m_bufferPtr == nullptr || *m_bufferPtr == 0x0) - return ""; - - return &m_bufferPtr[0]; - } - - // - // Function: GetBuffer - // Gets the string buffer (constant). - // - // Returns: - // Pointer to constant buffer. - // - const char *GetBuffer (void) const - { - if (m_bufferPtr == nullptr || *m_bufferPtr == 0x0) - return ""; - - return &m_bufferPtr[0]; - } - - // - // Function: ToFloat - // Gets the string as float, if possible. - // - // Returns: - // Float value of string. - // - float ToFloat (void) - { - return static_cast (atof (m_bufferPtr)); - } - - // - // Function: ToInt - // Gets the string as integer, if possible. - // - // Returns: - // Integer value of string. - // - int ToInt (void) const - { - return atoi (m_bufferPtr); - } - - // - // Function: ReleaseBuffer - // Terminates the string with null character. - // - void ReleaseBuffer (void) - { - ReleaseBuffer (strlen (m_bufferPtr)); - } - - // - // Function: ReleaseBuffer - // Terminates the string with null character with specified buffer end. - // - // Parameters: - // newLength - End of buffer. - // - void ReleaseBuffer (int newLength) - { - m_bufferPtr[newLength] = 0; - m_stringLength = newLength; - } - - // - // Function: GetBuffer - // Gets the buffer with specified length. - // - // Parameters: - // minLength - Length to retrieve. - // - // Returns: - // Pointer to string buffer. - // - char *GetBuffer (int minLength) - { - if (minLength >= m_allocatedSize) - UpdateBufferSize (minLength + 1); - - return m_bufferPtr; - } - - // - // Function: GetBufferSetLength - // Gets the buffer with specified length, and terminates string with that length. - // - // Parameters: - // minLength - Length to retrieve. - // - // Returns: - // Pointer to string buffer. - // - char *GetBufferSetLength (int length) - { - char *buffer = GetBuffer (length); - - m_stringLength = length; - m_bufferPtr[length] = 0; - - return buffer; - } - - // - // Function: Append - // Appends the string to existing buffer. - // - // Parameters: - // bufferPtr - String buffer to append. - // - void Append (const char *bufferPtr) - { - UpdateBufferSize (m_stringLength + strlen (bufferPtr) + 1); - strcat (m_bufferPtr, bufferPtr); - - m_stringLength = strlen (m_bufferPtr); - } - - // - // Function: Append - // Appends the character to existing buffer. - // - // Parameters: - // input - Character to append. - // - void Append (const char input) - { - UpdateBufferSize (m_stringLength + 2); - - m_bufferPtr[m_stringLength] = input; - m_bufferPtr[m_stringLength++] = 0; - } - - // - // Function: Append - // Appends the string to existing buffer. - // - // Parameters: - // inputString - String buffer to append. - // - void Append (const String &inputString) - { - const char *bufferPtr = inputString.GetBuffer (); - UpdateBufferSize (m_stringLength + strlen (bufferPtr)); - - strcat (m_bufferPtr, bufferPtr); - m_stringLength = strlen (m_bufferPtr); - } - - // - // Function: AppendFormat - // Appends the formatted string to existing buffer. - // - // Parameters: - // fmt - Formatted, tring buffer to append. - // - void AppendFormat (const char *fmt, ...) - { - va_list ap; - char buffer[1024]; - - va_start (ap, fmt); - vsnprintf (buffer, sizeof (buffer) - 1, fmt, ap); - va_end (ap); - - Append (buffer); - } - - // - // Function: Assign - // Assigns the string to existing buffer. - // - // Parameters: - // inputString - String buffer to assign. - // - void Assign (const String &inputString) - { - Assign (inputString.GetBuffer ()); - } - - // - // Function: Assign - // Assigns the character to existing buffer. - // - // Parameters: - // input - Character to assign. - // - void Assign (char input) - { - char psz[2] = {input, 0}; - Assign (psz); - } - - // - // Function: Assign - // Assigns the string to existing buffer. - // - // Parameters: - // bufferPtr - String buffer to assign. - // - void Assign (const char *bufferPtr) - { - if (bufferPtr == nullptr) - { - UpdateBufferSize (1); - m_stringLength = 0; - - return; - } - UpdateBufferSize (strlen (bufferPtr)); - - if (m_bufferPtr != nullptr) - { - strcpy (m_bufferPtr, bufferPtr); - m_stringLength = strlen (m_bufferPtr); - } - else - m_stringLength = 0; - } - - // - // Function: Assign - // Assigns the formatted string to existing buffer. - // - // Parameters: - // fmt - Formatted string buffer to assign. - // - void AssignFormat (const char *fmt, ...) - { - va_list ap; - char buffer[1024]; - - va_start (ap, fmt); - vsnprintf (buffer, sizeof (buffer) - 1, fmt, ap); - va_end (ap); - - Assign (buffer); - } - - // - // Function: Empty - // Empties the string. - // - void Empty (void) - { - if (m_bufferPtr != nullptr) - { - m_bufferPtr[0] = 0; - m_stringLength = 0; - } - } - - // - // Function: IsEmpty - // Checks whether string is empty. - // - // Returns: - // True if string is empty, false otherwise. - // - bool IsEmpty (void) const - { - if (m_bufferPtr == nullptr || m_stringLength == 0) - return true; +class String final : private Array { +private: + using base = Array ; +private: + bool isTrimChar (char chr, const char *chars) { + do { + if (*chars == chr) { + return true; + } + chars++; + } while (*chars); return false; } - // - // Function: GetLength - // Gets the string length. - // - // Returns: - // Length of string, 0 in case of error. - // - int GetLength (void) const - { - if (m_bufferPtr == nullptr) - return 0; +public: - return m_stringLength; + String (void) = default; + String (String &&other) noexcept : base (move (other)) { } + ~String (void) = default; + +public: + String (const char chr) { + assign (chr); } - operator const char * (void) const - { - return GetBuffer (); + String (const char *str, size_t length = 0) { + assign (str, length); } - operator char * (void) - { - return const_cast (GetBuffer ()); - } - - operator int (void) - { - return ToInt (); - } - - operator long (void) - { - return static_cast (ToInt ()); - } - - operator float (void) - { - return ToFloat (); - } - - operator double (void) - { - return static_cast (ToFloat ()); - } - - friend String operator + (const String &s1, const String &s2) - { - String result (s1); - result += s2; - - return result; - } - - friend String operator + (const String &holder, char ch) - { - String result (holder); - result += ch; - - return result; - } - - friend String operator + (char ch, const String &holder) - { - String result (ch); - result += holder; - - return result; - } - - friend String operator + (const String &holder, const char *str) - { - String result (holder); - result += str; - - return result; - } - - friend String operator + (const char *str, const String &holder) - { - String result (const_cast (str)); - result += holder; - - return result; - } - - friend bool operator == (const String &s1, const String &s2) - { - return s1.Compare (s2) == 0; - } - - friend bool operator < (const String &s1, const String &s2) - { - return s1.Compare (s2) < 0; - } - - friend bool operator > (const String &s1, const String &s2) - { - return s1.Compare (s2) > 0; - } - - friend bool operator == (const char *s1, const String &s2) - { - return s2.Compare (s1) == 0; - } - - friend bool operator == (const String &s1, const char *s2) - { - return s1.Compare (s2) == 0; - } - - friend bool operator != (const String &s1, const String &s2) - { - return s1.Compare (s2) != 0; - } - - friend bool operator != (const char *s1, const String &s2) - { - return s2.Compare (s1) != 0; - } - - friend bool operator != (const String &s1, const char *s2) - { - return s1.Compare (s2) != 0; - } - - String &operator = (const String &inputString) - { - Assign (inputString); - return *this; - } - - String &operator = (const char *bufferPtr) - { - Assign (bufferPtr); - return *this; - } - - String &operator = (char input) - { - Assign (input); - return *this; - } - - String &operator += (const String &inputString) - { - Append (inputString); - return *this; - } - - String &operator += (const char *bufferPtr) - { - Append (bufferPtr); - return *this; - } - - char operator [] (int index) - { - if (index > m_stringLength) - return -1; - - return m_bufferPtr[index]; - } - - // - // Function: Mid - // Gets the substring by specified bounds. - // - // Parameters: - // startIndex - Start index to get from. - // count - Number of characters to get. - // - // Returns: - // Tokenized string. - // - String Mid (int startIndex, int count = -1) - { - String result; - - if (startIndex >= m_stringLength || !m_bufferPtr) - return result; - - if (count == -1) - count = m_stringLength - startIndex; - else if (startIndex+count >= m_stringLength) - count = m_stringLength - startIndex; - - int i = 0, j = 0; - char *holder = new char[m_stringLength+1]; - - for (i = startIndex; i < startIndex + count; i++) - holder[j++] = m_bufferPtr[i]; - - holder[j] = 0; - result.Assign (holder); - - delete [] holder; - return result; - } - - // - // Function: Mid - // Gets the substring by specified bounds. - // - // Parameters: - // startIndex - Start index to get from. - // - // Returns: - // Tokenized string. - // - String Mid (int startIndex) - { - return Mid (startIndex, m_stringLength - startIndex); - } - - // - // Function: Left - // Gets the string from left side. - // - // Parameters: - // count - Number of characters to get. - // - // Returns: - // Tokenized string. - // - String Left (int count) - { - return Mid (0, count); - } - - // - // Function: Right - // Gets the string from right side. - // - // Parameters: - // count - Number of characters to get. - // - // Returns: - // Tokenized string. - // - String Right (int count) - { - if (count > m_stringLength) - count = m_stringLength; - - return Mid (m_stringLength - count, count); - } - - // - // Function: ToUpper - // Gets the string in upper case. - // - // Returns: - // Upped sting. - // - String ToUpper (void) - { - String result; - - for (int i = 0; i < GetLength (); i++) - result += static_cast (toupper (static_cast (m_bufferPtr[i]))); - - return result; - } - - // - // Function: ToUpper - // Gets the string in upper case. - // - // Returns: - // Lowered sting. - // - String ToLower (void) - { - String result; - - for (int i = 0; i < GetLength (); i++) - result += static_cast (tolower (static_cast (m_bufferPtr[i]))); - - return result; - } - - // - // Function: ToReverse - // Reverses the string. - // - // Returns: - // Reversed string. - // - String ToReverse (void) - { - char *source = m_bufferPtr + GetLength () - 1; - char *dest = m_bufferPtr; - - while (source > dest) - { - if (*source == *dest) - { - source--; - dest++; - } - else - { - char ch = *source; - - *source-- = *dest; - *dest++ = ch; - } - } - return m_bufferPtr; - } - - // - // Function: MakeUpper - // Converts string to upper case. - // - void MakeUpper (void) - { - *this = ToUpper (); - } - - // - // Function: MakeLower - // Converts string to lower case. - // - void MakeLower (void) - { - *this = ToLower (); - } - - // - // Function: MakeReverse - // Converts string into reverse order. - // - void MakeReverse (void) - { - *this = ToReverse (); - } - - // - // Function: Compare - // Compares string with other string. - // - // Parameters: - // string - String t compare with. - // - // Returns: - // Zero if they are equal. - // - int Compare (const String &string) const - { - return strcmp (m_bufferPtr, string.m_bufferPtr); - } - - // - // Function: Compare - // Compares string with other string. - // - // Parameters: - // str - String t compare with. - // - // Returns: - // Zero if they are equal. - // - int Compare (const char *str) const - { - return strcmp (m_bufferPtr, str); - } - - // - // Function: Collate - // Collate the string. - // - // Parameters: - // string - String to collate. - // - // Returns: - // One on success. - // - int Collate (const String &string) const - { - return strcoll (m_bufferPtr, string.m_bufferPtr); - } - - // - // Function: Find - // Find the character. - // - // Parameters: - // input - Character to search for. - // - // Returns: - // Index of character. - // - int Find (char input) const - { - return Find (input, 0); - } - - // - // Function: Find - // Find the character. - // - // Parameters: - // input - Character to search for. - // startIndex - Start index to search from. - // - // Returns: - // Index of character. - // - int Find (char input, int startIndex) const - { - char *str = m_bufferPtr + startIndex; - - for (;;) - { - if (*str == input) - return str - m_bufferPtr; - - if (*str == 0) - return -1; - - str++; - } - } - - // - // Function: Find - // Tries to find string. - // - // Parameters: - // string - String to search for. - // - // Returns: - // Position of found string. - // - int Find (const String &string) const - { - return Find (string, 0); - } - - // - // Function: Find - // Tries to find string from specified index. - // - // Parameters: - // string - String to search for. - // startIndex - Index to start search from. - // - // Returns: - // Position of found string. - // - int Find (const String &string, int startIndex) const - { - if (string.m_stringLength == 0) - return startIndex; - - for (; startIndex < m_stringLength; startIndex++) - { - int j; - - for (j = 0; j < string.m_stringLength && startIndex + j < m_stringLength; j++) - { - if (m_bufferPtr[startIndex + j] != string.m_bufferPtr[j]) - break; - } - - if (j == string.m_stringLength) - return startIndex; - } - return -1; - } - - // - // Function: ReverseFind - // Tries to find character in reverse order. - // - // Parameters: - // ch - Character to search for. - // - // Returns: - // Position of found character. - // - int ReverseFind (char ch) - { - if (m_stringLength == 0) - return -1; - - char *str = m_bufferPtr + m_stringLength - 1; - - for (;;) - { - if (*str == ch) - return str - m_bufferPtr; - - if (str == m_bufferPtr) - return -1; - str--; - } - } - - // - // Function: FindOneOf - // Find one of occurrences of string. - // - // Parameters: - // string - String to search for. - // - // Returns: - // -1 in case of nothing is found, start of string in buffer otherwise. - // - int FindOneOf (const String &string) - { - for (int i = 0; i < m_stringLength; i++) - { - if (string.Find (m_bufferPtr[i]) >= 0) - return i; - } - return -1; - } - - // - // Function: TrimRight - // Trims string from right side. - // - // Returns: - // Trimmed string. - // - String &TrimRight (void) - { - char *str = m_bufferPtr; - char *last = nullptr; - - while (*str != 0) - { - if (IsTrimChar (*str)) - { - if (last == nullptr) - last = str; - } - else - last = nullptr; - str++; - } - - if (last != nullptr) - Delete (last - m_bufferPtr); - - return *this; - } - - // - // Function: TrimLeft - // Trims string from left side. - // - // Returns: - // Trimmed string. - // - String &TrimLeft (void) - { - char *str = m_bufferPtr; - - while (IsTrimChar (*str)) - str++; - - if (str != m_bufferPtr) - { - int first = int (str - GetBuffer ()); - char *buffer = GetBuffer (GetLength ()); - - str = buffer + first; - int length = GetLength () - first; - - memmove (buffer, str, (length + 1) * sizeof (char)); - ReleaseBuffer (length); - } - return *this; - } - - // - // Function: Trim - // Trims string from both sides. - // - // Returns: - // Trimmed string. - // - String &Trim (void) - { - return TrimRight ().TrimLeft (); - } - - // - // Function: TrimRight - // Trims specified character at the right of the string. - // - // Parameters: - // ch - Character to trim. - // - void TrimRight (char ch) - { - const char *str = m_bufferPtr; - const char *last = nullptr; - - while (*str != 0) - { - if (*str == ch) - { - if (last == nullptr) - last = str; - } - else - last = nullptr; - - str++; - } - if (last != nullptr) - { - int i = last - m_bufferPtr; - Delete (i, m_stringLength - i); - } - } - - // - // Function: TrimLeft - // Trims specified character at the left of the string. - // - // Parameters: - // ch - Character to trim. - // - void TrimLeft (char ch) - { - char *str = m_bufferPtr; - - while (ch == *str) - str++; - - Delete (0, str - m_bufferPtr); - } - - // - // Function: Insert - // Inserts character at specified index. - // - // Parameters: - // index - Position to insert string. - // ch - Character to insert. - // - // Returns: - // New string length. - // - int Insert (int index, char ch) - { - InsertSpace (index, 1); - - m_bufferPtr[index] = ch; - m_stringLength++; - - return m_stringLength; - } - - // - // Function: Insert - // Inserts string at specified index. - // - // Parameters: - // index - Position to insert string. - // string - Text to insert. - // - // Returns: - // New string length. - // - int Insert (int index, const String &string) - { - CorrectIndex (index); - - if (string.m_stringLength == 0) - return m_stringLength; - - int numInsertChars = string.m_stringLength; - InsertSpace (index, numInsertChars); - - for(int i = 0; i < numInsertChars; i++) - m_bufferPtr[index + i] = string[i]; - m_stringLength += numInsertChars; - - return m_stringLength; - } - - // - // Function: Replace - // Replaces old characters with new one. - // - // Parameters: - // oldCharacter - Old character to replace. - // newCharacter - New character to replace with. - // - // Returns: - // Number of occurrences replaced. - // - int Replace (char oldCharacter, char newCharacter) - { - if (oldCharacter == newCharacter) - return 0; - - static int num = 0; - int position = 0; - - while (position < GetLength ()) - { - position = Find (oldCharacter, position); - - if (position < 0) - break; - - m_bufferPtr[position] = newCharacter; - - position++; - num++; - } - return num; - } - - // - // Function: Replace - // Replaces string in other string. - // - // Parameters: - // oldString - Old string to replace. - // newString - New string to replace with. - // - // Returns: - // Number of characters replaced. - // - int Replace (const String &oldString, const String &newString) - { - if (oldString.m_stringLength == 0) - return 0; - - if (newString.m_stringLength == 0) - return 0; - - int oldLength = oldString.m_stringLength; - int newLength = newString.m_stringLength; - - int num = 0; - int position = 0; - - while (position < m_stringLength) - { - position = Find (oldString, position); - - if (position < 0) - break; - - Delete (position, oldLength); - Insert (position, newString); - - position += newLength; - num++; - } - return num; - } - - // - // Function: Delete - // Deletes characters from string. - // - // Parameters: - // index - Start of characters remove. - // count - Number of characters to remove. - // - // Returns: - // New string length. - // - int Delete (int index, int count = 1) - { - if (index + count > m_stringLength) - count = m_stringLength - index; - - if (count > 0) - { - MoveItems (index, index + count); - m_stringLength -= count; - } - return m_stringLength; - } - - // - // Function: TrimQuotes - // Trims trailing quotes. - // - // Returns: - // Trimmed string. - // - String TrimQuotes (void) - { - TrimRight ('\"'); - TrimRight ('\''); - - TrimLeft ('\"'); - TrimLeft ('\''); - - return *this; - } - - // - // Function: Contains - // Checks whether string contains something. - // - // Parameters: - // what - String to check. - // - // Returns: - // True if string exists, false otherwise. - // - bool Contains (const String &what) - { - return strstr (m_bufferPtr, what.m_bufferPtr) != nullptr; - } - - // - // Function: Hash - // Gets the string hash. - // - // Returns: - // Hash of the string. - // - unsigned long Hash (void) - { - unsigned long hash = 0; - const char *ptr = m_bufferPtr; - - while (*ptr) - { - hash = (hash << 5) + hash + (*ptr); - ptr++; - } - return hash; - } - - // - // Function: Split - // Splits string using string separator. - // - // Parameters: - // separator - Separator to split with. - // - // Returns: - // Array of slitted strings. - // - // See Also: - // - // - Array Split (const char *separator) - { - Array holder; - int tokenLength, index = 0; - - do - { - index += strspn (&m_bufferPtr[index], separator); - tokenLength = strcspn (&m_bufferPtr[index], separator); - - if (tokenLength > 0) - holder.Push (Mid (index, tokenLength)); - - index += tokenLength; - - } while (tokenLength > 0); - - return holder; - } - - // - // Function: Split - // Splits string using character. - // - // Parameters: - // separator - Separator to split with. - // - // Returns: - // Array of slitted strings. - // - // See Also: - // - // - Array Split (char separator) - { - char sep[2]; - - sep[0] = separator; - sep[1] = 0x0; - - return Split (sep); + String (const String &str, size_t length = 0) : base () { + assign (str.chars (), length); } public: - // - // Function: TrimExternalBuffer - // Trims string from both sides. - // - // Returns: - // None - // - static inline void TrimExternalBuffer (char *str) - { - int pos = 0; + String &assign (const char *str, size_t length = 0) { + length = length > 0 ? length : strlen (str); + + clear (); + resize (length); + + memcpy (m_data, str, length); + terminate (); + + return *this; + } + + String &assign (const String &str, size_t length = 0) { + return assign (str.chars (), length); + } + + String &assign (const char chr) { + resize (2); + m_data[0] = chr; + + return *this; + } + + String &append (const char *str) { + if (empty ()) { + return assign (str); + } + const size_t maxLength = strlen (str) + 1; + + resize (length () + maxLength); + strncat (m_data, str, maxLength); + + return *this; + } + + String &append (const String &str) { + return append (str.chars ()); + } + + String &append (const char chr) { + const char app[] = { chr, '\0' }; + return append (app); + } + + const char *chars (void) const { + return empty () ? "" : m_data; + } + + size_t length (void) const { + return base::length (); + } + + bool empty (void) const { + return !length (); + } + + void clear (void) { + base::clear (); + + if (m_data) { + m_data[0] = '\0'; + } + } + + void erase (size_t index, size_t count = 1) { + base::erase (index, count); + terminate (); + } + + void reserve (size_t count) { + base::reserve (count); + } + + void resize (size_t count) { + base::resize (count); + terminate (); + } + + int32 toInt32 (void) const { + return atoi (chars ()); + } + + inline void terminate (void) { + m_data[m_length] = '\0'; + } + + inline char &at (size_t index) { + return m_data[index]; + } + + int32 compare (const String &what) const { + return strcmp (m_data, what.begin ()); + } + + int32 compare (const char *what) const { + return strcmp (begin (), what); + } + + bool contains (const String &what) const { + return strstr (m_data, what.begin ()) != nullptr; + } + + template void format (const char *fmt, ...) { + va_list ap; + char buffer[BufferSize]; + + va_start (ap, fmt); + vsnprintf (buffer, bufsize (buffer), fmt, ap); + va_end (ap); + + assign (buffer); + } + + template void formatAppend (const char *fmt, ...) { + va_list ap; + char buffer[BufferSize]; + + va_start (ap, fmt); + vsnprintf (buffer, bufsize (buffer), fmt, ap); + va_end (ap); + + append (buffer); + } + + size_t insert (size_t at, const String &str) { + if (base::insert (at, str.begin (), str.length ())) { + terminate (); + return length (); + } + return 0; + } + + size_t find (const String &search, size_t pos) const { + if (pos > length ()) { + return INVALID_INDEX; + } + size_t len = search.length (); + + for (size_t i = pos; len + i <= length (); i++) { + if (strncmp (m_data + i, search.chars (), len) == 0) { + return i; + } + } + return INVALID_INDEX; + } + + size_t replace (const String &what, const String &to) { + if (!what.length () || !to.length ()) { + return 0; + } + size_t numReplaced = 0, posIndex = 0; + + while (posIndex < length ()) { + posIndex = find (what, posIndex); + + if (posIndex == INVALID_INDEX) { + break; + } + erase (posIndex, what.length ()); + insert (posIndex, to); + + posIndex += to.length (); + numReplaced++; + } + return numReplaced; + } + + String substr (size_t start, size_t count = INVALID_INDEX) { + String result; + + if (start >= length () || empty ()) { + return result; + } + if (count == INVALID_INDEX) { + count = length () - start; + } + else if (start + count >= length ()) { + count = length () - start; + } + result.resize (count); + + transfer (&result[0], &m_data[start], count); + result[count] = '\0'; + + return result; + } + + char &operator[] (size_t index) { + return at (index); + } + + friend String operator + (const String &lhs, const String &rhs) { + return String (lhs).append (rhs); + } + + friend String operator + (const String &lhs, char rhs) { + return String (lhs).append (rhs); + } + + friend String operator + (char lhs, const String &rhs) { + return String (lhs).append (rhs); + } + + friend String operator + (const String &lhs, const char *rhs) { + return String (lhs).append (rhs); + } + + friend String operator + (const char *lhs, const String &rhs) { + return String (lhs).append (rhs); + } + + friend bool operator == (const String &lhs, const String &rhs) { + return lhs.compare (rhs) == 0; + } + + friend bool operator < (const String &lhs, const String &rhs) { + return lhs.compare (rhs) < 0; + } + + friend bool operator > (const String &lhs, const String &rhs) { + return lhs.compare (rhs) > 0; + } + + friend bool operator == (const char *lhs, const String &rhs) { + return rhs.compare (lhs) == 0; + } + + friend bool operator == (const String &lhs, const char *rhs) { + return lhs.compare (rhs) == 0; + } + + friend bool operator != (const String &lhs, const String &rhs) { + return lhs.compare (rhs) != 0; + } + + friend bool operator != (const char *lhs, const String &rhs) { + return rhs.compare (lhs) != 0; + } + + friend bool operator != (const String &lhs, const char *rhs) { + return lhs.compare (rhs) != 0; + } + + String &operator = (const String &rhs) { + return assign (rhs); + } + + String &operator = (const char *rhs) { + return assign (rhs); + } + + String &operator = (char rhs) { + return assign (rhs); + } + + String &operator = (String &&other) noexcept { + base::operator = (move (other)); + return *this; + } + + String &operator += (const String &rhs) { + return append (rhs); + } + + String &operator += (const char *rhs) { + return append (rhs); + } + + String &trimRight (const char *chars = "\r\n\t ") { + if (empty ()) { + return *this; + } + char *str = end () - 1; + + while (*str != 0) { + if (isTrimChar (*str, chars)) { + erase (str - begin ()); + str--; + } + else { + break; + } + } + return *this; + } + + String &trimLeft (const char *chars = "\r\n\t ") { + if (empty ()) { + return *this; + } + char *str = begin (); + + while (isTrimChar (*str, chars)) { + str++; + } + + if (begin () != str) { + erase (0, str - begin ()); + } + return *this; + } + + String &trim (const char *chars = "\r\n\t ") { + return trimLeft (chars).trimRight (chars); + } + + Array split (const char *delimiter) { + Array tokens; + size_t length, index = 0; + + do { + index += strspn (&m_data[index], delimiter); + length = strcspn (&m_data[index], delimiter); + + if (length > 0) { + tokens.push (move (substr (index, length))); + } + index += length; + } while (length > 0); + + return tokens; + } + +public: + static void trimChars (char *str) { + size_t pos = 0; char *dest = str; - while (str[pos] <= ' ' && str[pos] > 0) + while (str[pos] <= ' ' && str[pos] > 0) { pos++; + } - while (str[pos]) - { + while (str[pos]) { *(dest++) = str[pos]; pos++; } *(dest--) = '\0'; - while (dest >= str && *dest <= ' ' && *dest > 0) + while (dest >= str && *dest <= ' ' && *dest > 0) { *(dest--) = '\0'; + } } }; -// -// Class: File -// Simple STDIO file wrapper class. -// -class File -{ -protected: - FILE *m_handle; - int m_fileSize; +template struct StringHash { + unsigned long operator () (const K &key) const { + char *str = const_cast (key.chars ()); + size_t hash = key.length (); -// -// Group: (Con/De)structors -// + while (*str++) { + hash = ((hash << 5) + hash) + *str; + } + return hash; + } +}; + +template struct IntHash { + unsigned long operator () (K key) const { + key = ((key >> 16) ^ key) * 0x119de1f3; + key = ((key >> 16) ^ key) * 0x119de1f3; + key = (key >> 16) ^ key; + + return key; + } +}; + +template , size_t I = 256> class HashMap final : private NonCopyable { public: + using Bucket = Pair ; - // - // Function: File - // Default file class, constructor. - // - File (void) - { - m_handle = nullptr; - m_fileSize = 0; +private: + Array m_buckets[I]; + H m_hash; + + Array &getBucket (const K &key) { + return m_buckets[m_hash (key) % I]; } - // - // Function: File - // Default file class, constructor, with file opening. - // - File (String fileName, String mode = "rt") - { - Open (fileName, mode); +public: + HashMap (void) = default; + ~HashMap (void) = default; + +public: + bool exists (const K &key) { + return get (key, nullptr); } - // - // Function: ~File - // Default file class, destructor. - // - ~File (void) - { - Close (); + bool get (const K &key, V *val) { + for (auto &entry : getBucket (key)) { + if (entry.first == key) { + if (val != nullptr) { + *val = entry.second; + } + return true; + } + } + return false; } - // - // Function: Open - // Opens file and gets it's size. - // - // Parameters: - // fileName - String containing file name. - // mode - String containing open mode for file. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool Open (const String &fileName, const String &mode) - { - if ((m_handle = fopen (fileName.GetBuffer (), mode.GetBuffer ())) == nullptr) + bool get (const K &key, V &val) { + return get (key, &val); + } + + bool put (const K &key, const V &val) { + if (exists (key)) { return false; + } + getBucket (key).push (move (Pair (key, val))); + return true; + } + bool erase (const K &key) { + auto &bucket = getBucket (key); + + for (auto &entry : getBucket (key)) { + if (entry.first == key) { + bucket.erase (entry); + return true; + } + } + return false; + } + +public: + V &operator [] (const K &key) { + for (auto &entry : getBucket (key)) { + if (entry.first == key) { + return entry.second; + } + } + static V ret; + return ret; + } +}; + +class File : private NonCopyable { +private: + FILE *m_handle; + size_t m_size; + +public: + File (void) : m_handle (nullptr), m_size (0) {} + + File (const String &fileName, const String &mode = "rt") : m_handle (nullptr), m_size (0){ + open (fileName, mode); + } + + virtual ~File (void) { + close (); + } + + bool open (const String &fileName, const String &mode) { + if ((m_handle = fopen (fileName.chars (), mode.chars ())) == nullptr) { + return false; + } fseek (m_handle, 0L, SEEK_END); - m_fileSize = ftell (m_handle); + m_size = ftell (m_handle); fseek (m_handle, 0L, SEEK_SET); return true; } - // - // Function: Close - // Closes file, and destroys STDIO file object. - // - void Close (void) - { - if (m_handle != nullptr) - { + void close (void) { + if (m_handle != nullptr) { fclose (m_handle); m_handle = nullptr; } - m_fileSize = 0; + m_size = 0; } - // - // Function: Eof - // Checks whether we reached end of file. - // - // Returns: - // True if reached, false otherwise. - // - bool Eof (void) - { - assert (m_handle != nullptr); + bool eof (void) { return feof (m_handle) ? true : false; } - // - // Function: Flush - // Flushes file stream. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool Flush (void) - { - assert (m_handle != nullptr); + bool flush (void) { return fflush (m_handle) ? false : true; } - // - // Function: GetChar - // Pops one character from the file stream. - // - // Returns: - // Popped from stream character - // - int GetChar (void) - { - assert (m_handle != nullptr); + int getch (void) { return fgetc (m_handle); } - // - // Function: GetBuffer - // Gets the single line, from the non-binary stream. - // - // Parameters: - // buffer - Pointer to buffer, that should receive string. - // count - Max. size of buffer. - // - // Returns: - // Pointer to string containing popped line. - // - char *GetBuffer (char *buffer, int count) - { - assert (m_handle != nullptr); + char *gets (char *buffer, int count) { return fgets (buffer, count, m_handle); } - // - // Function: Printf - // Puts formatted buffer, into stream. - // - // Parameters: - // format - - // - // Returns: - // Number of bytes, that was written. - // - int Printf (const char *format, ...) - { + int writeFormat (const char *format, ...) { assert (m_handle != nullptr); va_list ap; @@ -3651,519 +1585,232 @@ public: int written = vfprintf (m_handle, format, ap); va_end (ap); - if (written < 0) + if (written < 0) { return 0; - + } return written; } - // - // Function: PutChar - // Puts character into file stream. - // - // Parameters: - // ch - Character that should be put into stream. - // - // Returns: - // Character that was putted into the stream. - // - char PutChar (char ch) - { - assert (m_handle != nullptr); + char putch (char ch) { return static_cast (fputc (ch, m_handle)); } - // - // Function: PutString - // Puts buffer into the file stream. - // - // Parameters: - // buffer - Buffer that should be put, into stream. - // - // Returns: - // True, if operation succeeded, false otherwise. - // - bool PutString (String buffer) - { + bool puts (const String &buffer) { assert (m_handle != nullptr); - if (fputs (buffer.GetBuffer (), m_handle) < 0) + if (fputs (buffer.chars (), m_handle) < 0) { return false; - + } return true; } - // - // Function: Read - // Reads buffer from file stream in binary format. - // - // Parameters: - // buffer - Holder for read buffer. - // size - Size of the buffer to read. - // count - Number of buffer chunks to read. - // - // Returns: - // Number of bytes red from file. - // - int Read (void *buffer, int size, int count = 1) - { - assert (m_handle != nullptr); + size_t read (void *buffer, size_t size, size_t count = 1) { return fread (buffer, size, count, m_handle); } - // - // Function: Write - // Writes binary buffer into file stream. - // - // Parameters: - // buffer - Buffer holder, that should be written into file stream. - // size - Size of the buffer that should be written. - // count - Number of buffer chunks to write. - // - // Returns: - // Numbers of bytes written to file. - // - int Write (void *buffer, int size, int count = 1) - { - assert (m_handle != nullptr); + size_t write (void *buffer, size_t size, size_t count = 1) { return fwrite (buffer, size, count, m_handle); } - // - // Function: Seek - // Seeks file stream with specified parameters. - // - // Parameters: - // offset - Offset where cursor should be set. - // origin - Type of offset set. - // - // Returns: - // True if operation success, false otherwise. - // - bool Seek (long offset, int origin) - { - assert (m_handle != nullptr); - - if (fseek (m_handle, offset, origin) != 0) + bool seek (long offset, int origin) { + if (fseek (m_handle, offset, origin) != 0) { return false; - + } return true; } - // - // Function: Rewind - // Rewinds the file stream. - // - void Rewind (void) - { - assert (m_handle != nullptr); - rewind (m_handle); + void rewind (void) { + ::rewind (m_handle); } - // - // Function: GetSize - // Gets the file size of opened file stream. - // - // Returns: - // Number of bytes in file. - // - int GetSize (void) - { - return m_fileSize; + size_t getSize (void) const { + return m_size; } - // - // Function: IsValid - // Checks whether file stream is valid. - // - // Returns: - // True if file stream valid, false otherwise. - // - bool IsValid (void) - { + bool isValid (void) const { return m_handle != nullptr; } public: - static inline bool Accessible (const String &filename) - { + static inline bool exists (const String &filename) { File fp; - if (fp.Open (filename, "rb")) - { - fp.Close (); + if (fp.open (filename, "rb")) { + fp.close (); return true; } return false; } - static inline void CreatePath (char *path) - { - for (char *ofs = path + 1; *ofs; ofs++) - { - if (*ofs == '/') - { - // create the directory - *ofs = 0; + static inline void pathCreate (char *path) { + for (char *str = path + 1; *str; str++) { + if (*str == '/') { + *str = 0; _mkdir (path); - *ofs = '/'; + *str = '/'; } } _mkdir (path); } }; -// -// Class: MemoryFile -// Simple Memory file wrapper class. (RO) -// -class MemoryFile -{ -public: - typedef uint8 *(*MF_Loader) (const char *, int *); - typedef void (*MF_Unloader) (uint8 *); +class MemoryLoader : public Singleton { +private: + using Load = uint8 * (*) (const char *, int *); + using Unload = void (*) (void *); + +private: + Load m_load; + Unload m_unload; public: - static MF_Loader Loader; - static MF_Unloader Unloader; + MemoryLoader (void) = default; + ~MemoryLoader (void) = default; -protected: - int m_size; - int m_pos; +public: + void setup (Load load, Unload unload) { + m_load = load; + m_unload = unload; + } + + uint8 *load (const char *name, int *size) { + if (m_load) { + return m_load (name, size); + } + return nullptr; + } + + void unload (void *buffer) { + if (m_unload) { + m_unload (buffer); + } + } +}; + +class MemFile : private NonCopyable { +private: + size_t m_size; + size_t m_pos; uint8 *m_buffer; - // - // Group: (Con/De)structors - // public: + MemFile (void) : m_size (0) , m_pos (0) , m_buffer (nullptr) {} - // - // Function: File - // Default file class, constructor. - // - MemoryFile (void) - { + MemFile (const String &filename) : m_size (0) , m_pos (0) , m_buffer (nullptr) { + open (filename); + } + + virtual ~MemFile (void) { + close (); + } + + bool open (const String &filename) { m_size = 0; m_pos = 0; + m_buffer = MemoryLoader::ref ().load (filename.chars (), reinterpret_cast (&m_size)); - m_buffer = nullptr; - } - - // - // Function: File - // Default file class, constructor, with file opening. - // - MemoryFile (const String &fileName) - { - m_size = 0; - m_pos = 0; - - m_buffer = nullptr; - - Open (fileName); - } - - // - // Function: ~File - // Default file class, destructor. - // - ~MemoryFile (void) - { - Close (); - } - - // - // Function: Open - // Opens file and gets it's size. - // - // Parameters: - // fileName - String containing file name. - // - // Returns: - // True if operation succeeded, false otherwise. - // - bool Open (const char *fileName) - { - if (!Loader) + if (!m_buffer) { return false; - - m_size = 0; - m_pos = 0; - - m_buffer = Loader (fileName, &m_size); - - if (m_buffer == nullptr || m_size < 0) - return false; - + } return true; } - // - // Function: Close - // Closes file, and destroys STDIO file object. - // - void Close (void) - { - if (Unloader != nullptr) - Unloader (m_buffer); + void close (void) { + MemoryLoader::ref ().unload (m_buffer); m_size = 0; m_pos = 0; - m_buffer = nullptr; } - // - // Function: GetChar - // Pops one character from the file stream. - // - // Returns: - // Popped from stream character - // - int GetChar (void) - { - if (m_buffer == nullptr || m_pos >= m_size) + int getch (void) { + if (!m_buffer || m_pos >= m_size) { return -1; - + } int readCh = m_buffer[m_pos]; m_pos++; return readCh; } - // - // Function: GetBuffer - // Gets the single line, from the non-binary stream. - // - // Parameters: - // buffer - Pointer to buffer, that should receive string. - // count - Max. size of buffer. - // - // Returns: - // Pointer to string containing popped line. - // - char *GetBuffer (char *buffer, int count) - { - if (m_buffer == nullptr || m_pos >= m_size) + char *gets (char *buffer, size_t count) { + if (!m_buffer || m_pos >= m_size) { return nullptr; - - int start = m_pos; - int end = m_size - 1; - - if (m_size - m_pos > count - 1) - end = m_pos + count - 1; - - while (m_pos < end) - { - if (m_buffer[m_pos] == 0x0a) - end = m_pos; - - m_pos++; } + size_t index = 0; + buffer[0] = 0; - if (m_pos == start) - return nullptr; + for (; index < count - 1;) { + if (m_pos < m_size) { + buffer[index] = m_buffer[m_pos++]; - int pos = start; - - for (; pos <= end; pos++) - buffer[pos - start] = m_buffer[pos]; - - if (buffer[pos - start - 2] == 0x0d) - { - buffer[pos - start - 2] = '\n'; - pos--; + if (buffer[index++] == '\n') { + break; + } + } + else { + break; + } } - - if (buffer[pos - start - 1] == 0x0d || buffer[pos - start - 1] == 0x0a) - buffer[pos - start - 1] = '\n'; - - buffer[pos - start] = 0; - - return buffer; + buffer[index] = 0; + return index ? buffer : nullptr; } - // - // Function: Read - // Reads buffer from file stream in binary format. - // - // Parameters: - // buffer - Holder for read buffer. - // size - Size of the buffer to read. - // count - Number of buffer chunks to read. - // - // Returns: - // Number of bytes red from file. - // - int Read (void *buffer, int size, int count = 1) - { - if (!m_buffer|| m_pos >= m_size || buffer == nullptr || !size || !count) + size_t read (void *buffer, size_t size, size_t count = 1) { + if (!m_buffer || m_size <= m_pos || !buffer || !size || !count) { return 0; - - int blocksRead = Math::A_min ((m_size - m_pos) / size, count) * size; + } + size_t blocksRead = size * count <= m_size - m_pos ? size * count : m_size - m_pos; memcpy (buffer, &m_buffer[m_pos], blocksRead); m_pos += blocksRead; - return blocksRead; + return blocksRead / size; } - // - // Function: Seek - // Seeks file stream with specified parameters. - // - // Parameters: - // offset - Offset where cursor should be set. - // origin - Type of offset set. - // - // Returns: - // True if operation success, false otherwise. - // - bool Seek (long offset, int origin) - { - if (m_buffer == nullptr || m_pos >= m_size) + bool seek (size_t offset, int origin) { + if (!m_buffer || m_pos >= m_size) { return false; - - if (origin == SEEK_SET) - { - if (offset >= m_size) + } + if (origin == SEEK_SET) { + if (offset >= m_size) { return false; - + } m_pos = offset; } - else if (origin == SEEK_END) - { - if (offset >= m_size) + else if (origin == SEEK_END) { + if (offset >= m_size) { return false; - + } m_pos = m_size - offset; } - else - { - if (m_pos + offset >= m_size) + else { + if (m_pos + offset >= m_size) { return false; - + } m_pos += offset; } return true; } - // - // Function: GetSize - // Gets the file size of opened file stream. - // - // Returns: - // Number of bytes in file. - // - int GetSize (void) - { + inline size_t getSize (void) const { return m_size; } - // - // Function: IsValid - // Checks whether file stream is valid. - // - // Returns: - // True if file stream valid, false otherwise. - // - bool IsValid (void) - { - return m_buffer != nullptr && m_size > 0; + bool isValid (void) const { + return m_buffer && m_size > 0; } }; -// -// Type: StrVec -// Array of strings. -// -typedef Array StrVec; - -#ifndef FORCEINLINE -#define FORCEINLINE inline -#endif - -// -// Class: Singleton -// Implements no-copying singleton. -// -template class Singleton -{ -// -// Group: (Con/De)structors -// -protected: - - // - // Function: Singleton - // Default singleton constructor. - // - Singleton (void) { } - - // - // Function: ~Singleton - // Default singleton destructor. - // - virtual ~Singleton (void) { } - -private: - Singleton (Singleton const &); - void operator = (Singleton const &); - -public: - - // - // Function: GetObject - // Gets the object of singleton. - // - // Returns: - // Object pointer. - // - // - static FORCEINLINE T *GetObject (void) - { - return &GetReference (); - } - - // - // Function: GetObject - // Gets the object of singleton as reference. - // - // Returns: - // Object reference. - // - // - static FORCEINLINE T &GetReference (void) - { - static T reference; - return reference; - }; -}; -// -// Macro: IterateArray -// Utility macro for iterating arrays. -// -// Parameters: -// iteratorName - Name of the iterator. -// arrayName - Name of the array that should be iterated. -// -// See Also: -// -// -#define FOR_EACH_AE(arrayName, iteratorName) \ - for (int iteratorName = 0; iteratorName != arrayName.GetElementNumber (); iteratorName++) +}} -// -// Sizeof bounds -// -#define SIZEOF_CHAR(in) sizeof (in) - 1 +namespace cr { +namespace types { + +using StringArray = cr::classes::Array ; +using IntArray = cr::classes::Array ; + +}} -// -// Squared Length -// -#define GET_SQUARE(in) (in * in) -// -// Stringify a string -#define STRINGIFY(str) (#str) \ No newline at end of file diff --git a/include/engine.h b/include/engine.h index 3a6aaaf..3940c01 100644 --- a/include/engine.h +++ b/include/engine.h @@ -4,24 +4,20 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license -// -// Purpose: Engine & Game interfaces. +// https://yapb.ru/license // #pragma once // line draw -enum DrawLineType -{ +enum DrawLineType { DRAW_SIMPLE, DRAW_ARROW, DRAW_NUM }; // trace ignore -enum TraceIgnore -{ +enum TraceIgnore { TRACE_IGNORE_NONE = 0, TRACE_IGNORE_GLASS = (1 << 0), TRACE_IGNORE_MONSTERS = (1 << 1), @@ -29,8 +25,7 @@ enum TraceIgnore }; // variable type -enum VarType -{ +enum VarType { VT_NORMAL = 0, VT_READONLY, VT_PASSWORD, @@ -39,8 +34,7 @@ enum VarType }; // netmessage functions -enum NetMsgId -{ +enum NetMsgId { NETMSG_UNDEFINED = -1, NETMSG_VGUI = 1, NETMSG_SHOWMENU = 2, @@ -55,7 +49,7 @@ enum NetMsgId NETMSG_SCREENFADE = 11, NETMSG_HLTV = 12, NETMSG_TEXTMSG = 13, - NETMSG_SCOREINFO = 14, + NETMSG_TEAMINFO = 14, NETMSG_BARTIME = 15, NETMSG_SENDAUDIO = 17, NETMSG_SAYTEXT = 18, @@ -64,8 +58,7 @@ enum NetMsgId }; // variable reg pair -struct VarPair -{ +struct VarPair { VarType type; cvar_t reg; class ConVar *self; @@ -74,25 +67,32 @@ struct VarPair const char *regVal; }; -// translation pair -struct TranslatorPair -{ - const char *original; - const char *translated; -}; - // network message block -struct MessageBlock -{ +struct MessageBlock { int bot; int state; int msg; int regMsgs[NETMSG_NUM]; }; +// compare language +struct LangComprarer { + size_t operator () (const String &key) const { + char *str = const_cast (key.chars ()); + size_t hash = key.length (); + + while (*str++) { + if (!isalpha (*str)) { + continue; + } + hash = ((*str << 5) + hash) + *str; + } + return hash; + } +}; + // provides utility functions to not call original engine (less call-cost) -class Engine : public Singleton -{ +class Engine : public Singleton { private: int m_drawModels[DRAW_NUM]; @@ -105,217 +105,293 @@ private: edict_t *m_localEntity; Array m_cvars; - Array m_language; + HashMap m_language; MessageBlock m_msgBlock; + bool m_precached; public: Engine (void); - ~Engine (void); - // public functions public: - // precaches internal stuff - void Precache (edict_t *startEntity); + void precache (void); + + // initialize levels + void levelInitialize (void); // prints data to servers console - void Printf (const char *fmt, ...); + void print (const char *fmt, ...); // prints chat message to all players - void ChatPrintf (const char *fmt, ...); + void chatPrint (const char *fmt, ...); // prints center message to all players - void CenterPrintf (const char *fmt, ...); + void centerPrint (const char *fmt, ...); // prints message to client console - void ClientPrintf (edict_t *ent, const char *fmt, ...); + void clientPrint (edict_t *ent, const char *fmt, ...); // display world line - void DrawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, int red, int green, int blue, int brightness, int speed, int life, DrawLineType type = DRAW_SIMPLE); + void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, int red, int green, int blue, int brightness, int speed, int life, DrawLineType type = DRAW_SIMPLE); // test line - void TestLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr); + void testLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr); // test line - void TestHull (const Vector &start, const Vector &end, int ignoreFlags, int hullNumber, edict_t *ignoreEntity, TraceResult *ptr); + void testHull (const Vector &start, const Vector &end, int ignoreFlags, int hullNumber, edict_t *ignoreEntity, TraceResult *ptr); // get's the wave length - float GetWaveLength (const char *fileName); + float getWaveLen (const char *fileName); // we are on dedicated server ? - bool IsDedicatedServer (void); + bool isDedicated (void); // get stripped down mod name - const char *GetModName (void); + const char *getModName (void); // get the valid mapname - const char *GetMapName (void); + const char *getMapName (void); // get the "any" entity origin - Vector GetAbsOrigin (edict_t *ent); + Vector getAbsPos (edict_t *ent); // send server command - void IssueCmd (const char *fmt, ...); + void execCmd (const char *fmt, ...); // registers a server command - void RegisterCmd (const char *command, void func (void)); + void registerCmd (const char *command, void func (void)); // play's sound to client - void EmitSound (edict_t *ent, const char *sound); + void playSound (edict_t *ent, const char *sound); // sends bot command - void IssueBotCommand (edict_t *ent, const char *fmt, ...); + void execBotCmd (edict_t *ent, const char *fmt, ...); // adds cvar to registration stack - void PushVariableToStack (const char *variable, const char *value, VarType varType, bool regMissing, const char *regVal, ConVar *self); + void pushVarToRegStack (const char *variable, const char *value, VarType varType, bool regMissing, const char *regVal, ConVar *self); // sends local registration stack for engine registration - void PushRegisteredConVarsToEngine (bool gameVars = false); + void pushRegStackToEngine (bool gameVars = false); // translates bot message into needed language - char *TraslateMessage (const char *input); - - // cleanup translator resources - void TerminateTranslator (void); + const char *translate (const char *input); // do actual network message processing - void ProcessMessageCapture (void *ptr); + void processMessages (void *ptr); // public inlines public: - // get the current time on server - FORCEINLINE float Time (void) - { + inline float timebase (void) { return g_pGlobals->time; } // get "maxplayers" limit on server - FORCEINLINE int MaxClients (void) - { + inline int maxClients (void) { return g_pGlobals->maxClients; } // get the fakeclient command interface - inline bool IsBotCommand (void) - { + inline bool isBotCmd (void) { return m_isBotCommand; } // gets custom engine args for client command - inline const char *GetOverrideArgs (void) - { - if (strncmp ("say ", m_arguments, 4) == 0) + inline const char *botArgs (void) { + if (strncmp ("say ", m_arguments, 4) == 0) { return &m_arguments[4]; - else if (strncmp ("say_team ", m_arguments, 9) == 0) + } + else if (strncmp ("say_team ", m_arguments, 9) == 0) { return &m_arguments[9]; - + } return m_arguments; } // gets custom engine argv for client command - inline const char *GetOverrideArgv (int num) - { - return ExtractSingleField (m_arguments, num); + inline const char *botArgv (int num) { + return getField (m_arguments, static_cast (num)); } // gets custom engine argc for client command - inline int GetOverrideArgc (void) - { + inline int botArgc (void) { return m_argumentCount; } // gets edict pointer out of entity index - FORCEINLINE edict_t *EntityOfIndex (const int index) - { + inline edict_t *entityOfIndex (const int index) { return static_cast (m_startEntity + index); }; // gets edict index out of it's pointer - FORCEINLINE int IndexOfEntity (const edict_t *ent) - { + inline int indexOfEntity (const edict_t *ent) { return static_cast (ent - m_startEntity); }; // verify entity isn't null - FORCEINLINE bool IsNullEntity (const edict_t *ent) - { - return !ent || !IndexOfEntity (ent); + inline bool isNullEntity (const edict_t *ent) { + return !ent || !indexOfEntity (ent) || ent->free; + } + + // get the wroldspawn entity + inline edict_t *getStartEntity (void) { + return m_startEntity; } // gets the player team - FORCEINLINE int GetTeam (edict_t *ent) - { + inline int getTeam (edict_t *ent) { extern Client g_clients[MAX_ENGINE_PLAYERS]; - return g_clients[IndexOfEntity (ent) - 1].team; + return g_clients[indexOfEntity (ent) - 1].team; } // adds translation pair from config - inline void PushTranslationPair (const TranslatorPair &lang) - { - m_language.Push (lang); + inline void addTranslation (const String &original, const String &translated) { + m_language.put (original, translated); } // resets the message capture mechanism - inline void ResetMessageCapture (void) - { + inline void resetMessages (void) { m_msgBlock.msg = NETMSG_UNDEFINED; m_msgBlock.state = 0; m_msgBlock.bot = 0; }; // sets the currently executed message - inline void SetOngoingMessageId (int message) - { + inline void setCurrentMessageId (int message) { m_msgBlock.msg = message; } // set the bot entity that receive this message - inline void SetOngoingMessageReceiver (int id) - { + inline void setCurrentMessageOwner (int id) { m_msgBlock.bot = id; } // find registered message id - FORCEINLINE int FindMessageId (int type) - { + inline int getMessageId (int type) { return m_msgBlock.regMsgs[type]; } // assigns message id for message type - inline void AssignMessageId (int type, int id) - { + inline void setMessageId (int type, int id) { m_msgBlock.regMsgs[type] = id; } // tries to set needed message id - FORCEINLINE void TryCaptureMessage (int type, int msgId) - { - if (type == m_msgBlock.regMsgs[msgId]) - SetOngoingMessageId (msgId); + inline void captureMessage (int type, int msgId) { + if (type == m_msgBlock.regMsgs[msgId]) { + setCurrentMessageId (msgId); + } + } + + // sets the precache to uninitialize + inline void setUnprecached (void) { + m_precached = false; } // static utility functions private: - const char *ExtractSingleField (const char *string, int id); + const char *getField (const char *string, size_t id); }; // simplify access for console variables -class ConVar -{ +class ConVar { public: cvar_t *m_eptr; public: - ConVar (const char *name, const char *initval, VarType type = VT_NOSERVER, bool regMissing = false, const char *regVal = nullptr); + ConVar (const char *name, const char *initval, VarType type = VT_NOSERVER, bool regMissing = false, const char *regVal = nullptr) : m_eptr (nullptr) { + Engine::ref ().pushVarToRegStack (name, initval, type, regMissing, regVal, this); + } - FORCEINLINE bool GetBool (void) { return m_eptr->value > 0.0f; } - FORCEINLINE int GetInt (void) { return static_cast (m_eptr->value); } - FORCEINLINE float GetFloat (void) { return m_eptr->value; } - FORCEINLINE const char *GetString (void) { return m_eptr->string; } - FORCEINLINE void SetFloat (float val) { g_engfuncs.pfnCVarSetFloat (m_eptr->name, val); } - FORCEINLINE void SetInt (int val) { SetFloat (static_cast (val)); } - FORCEINLINE void SetString (const char *val) { g_engfuncs.pfnCvar_DirectSet (m_eptr, const_cast (val)); } + inline bool boolean (void) const { + return m_eptr->value > 0.0f; + } + + inline int integer (void) const { + return static_cast (m_eptr->value); + } + + inline float flt (void) const { + return m_eptr->value; + } + + inline const char *str (void) const { + return m_eptr->string; + } + + inline void set (float val) const { + g_engfuncs.pfnCVarSetFloat (m_eptr->name, val); + } + + inline void set (int val) const { + set (static_cast (val)); + } + + inline void set (const char *val) const { + g_engfuncs.pfnCvar_DirectSet (m_eptr, const_cast (val)); + } +}; + +class MessageWriter { +private: + bool m_autoDestruct { false }; + +public: + MessageWriter (void) = default; + + MessageWriter (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) { + start (dest, type, pos, to); + m_autoDestruct = true; + } + + virtual ~MessageWriter (void) { + if (m_autoDestruct) { + end (); + } + } + +public: + MessageWriter &start (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) { + g_engfuncs.pfnMessageBegin (dest, type, pos, to); + return *this; + } + + void end (void) { + g_engfuncs.pfnMessageEnd (); + } + + MessageWriter &writeByte (int val) { + g_engfuncs.pfnWriteByte (val); + return *this; + } + + MessageWriter &writeChar (int val) { + g_engfuncs.pfnWriteChar (val); + return *this; + } + + MessageWriter &writeShort (int val) { + g_engfuncs.pfnWriteShort (val); + return *this; + } + + MessageWriter &writeCoord (float val) { + g_engfuncs.pfnWriteCoord (val); + return *this; + } + + MessageWriter &writeString (const char *val) { + g_engfuncs.pfnWriteString (val); + return *this; + } + +public: + static inline uint16 fu16 (float value, float scale) { + return cr::clamp (static_cast (value * scale), 0, 0xffff); + } + + static inline short fs16 (float value, float scale) { + return cr::clamp (static_cast (value * scale), -32767, 32767); + } }; diff --git a/include/engine/const.h b/include/engine/const.h index 0573cef..197e77b 100644 --- a/include/engine/const.h +++ b/include/engine/const.h @@ -1,17 +1,17 @@ /*** -* -* Copyright (c) 1999-2005, Valve Corporation. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ + * + * Copyright (c) 1999-2005, Valve Corporation. All rights reserved. + * + * This product contains software technology licensed from Id + * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. + * All Rights Reserved. + * + * This source code contains proprietary and confidential information of + * Valve LLC and its suppliers. Access to this code is restricted to + * persons who have executed a written SDK license with Valve. Any access, + * use or distribution of this code by or to any unlicensed person is illegal. + * + ****/ #ifndef CONST_H #define CONST_H // @@ -20,321 +20,320 @@ // Most came from server.h // 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) -#define FL_CONVEYOR (1 << 2) -#define FL_CLIENT (1 << 3) -#define FL_INWATER (1 << 4) -#define FL_MONSTER (1 << 5) -#define FL_GODMODE (1 << 6) -#define FL_NOTARGET (1 << 7) -#define FL_SKIPLOCALHOST (1 << 8) // Don't send entity to local host, it's predicting this entity itself -#define FL_ONGROUND (1 << 9) // At rest / on the ground -#define FL_PARTIALGROUND (1 << 10) // not all corners are valid -#define FL_WATERJUMP (1 << 11) // player jumping out of water -#define FL_FROZEN (1 << 12) // Player is frozen for 3rd person camera -#define FL_FAKECLIENT (1 << 13) // JAC: fake client, simulated server side; don't send network messages to them -#define FL_DUCKING (1 << 14) // Player flag -- Player is fully crouched -#define FL_FLOAT (1 << 15) // Apply floating force to this entity when in water -#define FL_GRAPHED (1 << 16) // worldgraph has this ent listed as something that blocks a connection +#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) +#define FL_CONVEYOR (1 << 2) +#define FL_CLIENT (1 << 3) +#define FL_INWATER (1 << 4) +#define FL_MONSTER (1 << 5) +#define FL_GODMODE (1 << 6) +#define FL_NOTARGET (1 << 7) +#define FL_SKIPLOCALHOST (1 << 8) // Don't send entity to local host, it's predicting this entity itself +#define FL_ONGROUND (1 << 9) // At rest / on the ground +#define FL_PARTIALGROUND (1 << 10) // not all corners are valid +#define FL_WATERJUMP (1 << 11) // player jumping out of water +#define FL_FROZEN (1 << 12) // Player is frozen for 3rd person camera +#define FL_FAKECLIENT (1 << 13) // JAC: fake client, simulated server side; don't send network messages to them +#define FL_DUCKING (1 << 14) // Player flag -- Player is fully crouched +#define FL_FLOAT (1 << 15) // Apply floating force to this entity when in water +#define FL_GRAPHED (1 << 16) // worldgraph has this ent listed as something that blocks a connection // UNDONE: Do we need these? -#define FL_IMMUNE_WATER (1 << 17) -#define FL_IMMUNE_SLIME (1 << 18) -#define FL_IMMUNE_LAVA (1 << 19) +#define FL_IMMUNE_WATER (1 << 17) +#define FL_IMMUNE_SLIME (1 << 18) +#define FL_IMMUNE_LAVA (1 << 19) -#define FL_PROXY (1 << 20) // This is a spectator proxy -#define FL_ALWAYSTHINK (1 << 21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) -#define FL_BASEVELOCITY (1 << 22) // Base velocity has been applied this frame (used to convert base velocity into momentum) -#define FL_MONSTERCLIP (1 << 23) // Only collide in with monsters who have FL_MONSTERCLIP set -#define FL_ONTRAIN (1 << 24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. -#define FL_WORLDBRUSH (1 << 25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) -#define FL_SPECTATOR (1 << 26) // This client is a spectator, don't run touch functions, etc. -#define FL_CUSTOMENTITY (1 << 29) // This is a custom entity -#define FL_KILLME (1 << 30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time -#define FL_DORMANT (1 << 31) // Entity is dormant, no updates to client +#define FL_PROXY (1 << 20) // This is a spectator proxy +#define FL_ALWAYSTHINK (1 << 21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) +#define FL_BASEVELOCITY (1 << 22) // Base velocity has been applied this frame (used to convert base velocity into momentum) +#define FL_MONSTERCLIP (1 << 23) // Only collide in with monsters who have FL_MONSTERCLIP set +#define FL_ONTRAIN (1 << 24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. +#define FL_WORLDBRUSH (1 << 25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) +#define FL_SPECTATOR (1 << 26) // This client is a spectator, don't run touch functions, etc. +#define FL_CUSTOMENTITY (1 << 29) // This is a custom entity +#define FL_KILLME (1 << 30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time +#define FL_DORMANT (1 << 31) // Entity is dormant, no updates to client // Goes into globalvars_t.trace_flags -#define FTRACE_SIMPLEBOX (1 << 0) // Traceline with a simple box +#define FTRACE_SIMPLEBOX (1 << 0) // Traceline with a simple box // walkmove modes -#define WALKMOVE_NORMAL 0 // normal walkmove -#define WALKMOVE_WORLDONLY 1 // doesn't hit ANY entities, no matter what the solid type -#define WALKMOVE_CHECKONLY 2 // move, but don't touch triggers +#define WALKMOVE_NORMAL 0 // normal walkmove +#define WALKMOVE_WORLDONLY 1 // doesn't hit ANY entities, no matter what the solid type +#define WALKMOVE_CHECKONLY 2 // move, but don't touch triggers // edict->movetype values -#define MOVETYPE_NONE 0 // never moves -#define MOVETYPE_WALK 3 // Player only - moving on the ground -#define MOVETYPE_STEP 4 // gravity, special edge handling -- monsters use this -#define MOVETYPE_FLY 5 // No gravity, but still collides with stuff -#define MOVETYPE_TOSS 6 // gravity/collisions -#define MOVETYPE_PUSH 7 // no clip to world, push and crush -#define MOVETYPE_NOCLIP 8 // No gravity, no collisions, still do velocity/avelocity -#define MOVETYPE_FLYMISSILE 9 // extra size to monsters -#define MOVETYPE_BOUNCE 10 // Just like Toss, but reflect velocity when contacting surfaces -#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity -#define MOVETYPE_FOLLOW 12 // track movement of aiment -#define MOVETYPE_PUSHSTEP 13 // BSP model that needs physics/world collisions (uses nearest hull for world collision) +#define MOVETYPE_NONE 0 // never moves +#define MOVETYPE_WALK 3 // Player only - moving on the ground +#define MOVETYPE_STEP 4 // gravity, special edge handling -- monsters use this +#define MOVETYPE_FLY 5 // No gravity, but still collides with stuff +#define MOVETYPE_TOSS 6 // gravity/collisions +#define MOVETYPE_PUSH 7 // no clip to world, push and crush +#define MOVETYPE_NOCLIP 8 // No gravity, no collisions, still do velocity/avelocity +#define MOVETYPE_FLYMISSILE 9 // extra size to monsters +#define MOVETYPE_BOUNCE 10 // Just like Toss, but reflect velocity when contacting surfaces +#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity +#define MOVETYPE_FOLLOW 12 // track movement of aiment +#define MOVETYPE_PUSHSTEP 13 // BSP model that needs physics/world collisions (uses nearest hull for world collision) // edict->solid values // NOTE: Some movetypes will cause collisions independent of SOLID_NOT/SOLID_TRIGGER when the entity moves // SOLID only effects OTHER entities colliding with this one when they move - UGH! -#define SOLID_NOT 0 // no interaction with other objects -#define SOLID_TRIGGER 1 // touch on edge, but not blocking -#define SOLID_BBOX 2 // touch on edge, block -#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground -#define SOLID_BSP 4 // bsp clip, touch on edge, block +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block // edict->deadflag values -#define DEAD_NO 0 // alive -#define DEAD_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground -#define DEAD_DEAD 2 // dead. lying still. -#define DEAD_RESPAWNABLE 3 -#define DEAD_DISCARDBODY 4 +#define DEAD_NO 0 // alive +#define DEAD_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground +#define DEAD_DEAD 2 // dead. lying still. +#define DEAD_RESPAWNABLE 3 +#define DEAD_DISCARDBODY 4 -#define DAMAGE_NO 0 -#define DAMAGE_YES 1 -#define DAMAGE_AIM 2 +#define DAMAGE_NO 0 +#define DAMAGE_YES 1 +#define DAMAGE_AIM 2 // entity effects -#define EF_BRIGHTFIELD 1 // swirling cloud of particles -#define EF_MUZZLEFLASH 2 // single frame ELIGHT on entity attachment 0 -#define EF_BRIGHTLIGHT 4 // DLIGHT centered at entity origin -#define EF_DIMLIGHT 8 // player flashlight -#define EF_INVLIGHT 16 // get lighting from ceiling -#define EF_NOINTERP 32 // don't interpolate the next frame -#define EF_LIGHT 64 // rocket flare glow sprite -#define EF_NODRAW 128 // don't draw entity +#define EF_BRIGHTFIELD 1 // swirling cloud of particles +#define EF_MUZZLEFLASH 2 // single frame ELIGHT on entity attachment 0 +#define EF_BRIGHTLIGHT 4 // DLIGHT centered at entity origin +#define EF_DIMLIGHT 8 // player flashlight +#define EF_INVLIGHT 16 // get lighting from ceiling +#define EF_NOINTERP 32 // don't interpolate the next frame +#define EF_LIGHT 64 // rocket flare glow sprite +#define EF_NODRAW 128 // don't draw entity // entity flags -#define EFLAG_SLERP 1 // do studio interpolation of this entity +#define EFLAG_SLERP 1 // do studio interpolation of this entity // // temp entity events // -#define TE_BEAMPOINTS 0 // beam effect between two points -// coord coord coord (start position) -// coord coord coord (end position) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMPOINTS 0 // beam effect between two points +// coord coord coord (start position) +// coord coord coord (end position) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_BEAMENTPOINT 1 // beam effect between point and entity -// short (start entity) -// coord coord coord (end position) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMENTPOINT 1 // beam effect between point and entity +// short (start entity) +// coord coord coord (end position) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_GUNSHOT 2 // particle effect plus ricochet sound -// coord coord coord (position) +#define TE_GUNSHOT 2 // particle effect plus ricochet sound +// coord coord coord (position) -#define TE_EXPLOSION 3 // additive sprite, 2 dynamic lights, flickering particles, explosion sound, move vertically 8 pps -// coord coord coord (position) +#define TE_EXPLOSION 3 // additive sprite, 2 dynamic lights, flickering particles, explosion sound, move vertically 8 pps +// coord coord coord (position) // short (sprite index) // byte (scale in 0.1's) // byte (framerate) // byte (flags) // // The Explosion effect has some flags to control performance/aesthetic features: -#define TE_EXPLFLAG_NONE 0 // all flags clear makes default Half-Life explosion -#define TE_EXPLFLAG_NOADDITIVE 1 // sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) -#define TE_EXPLFLAG_NODLIGHTS 2 // do not render dynamic lights -#define TE_EXPLFLAG_NOSOUND 4 // do not play client explosion sound -#define TE_EXPLFLAG_NOPARTICLES 8 // do not draw particles +#define TE_EXPLFLAG_NONE 0 // all flags clear makes default Half-Life explosion +#define TE_EXPLFLAG_NOADDITIVE 1 // sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) +#define TE_EXPLFLAG_NODLIGHTS 2 // do not render dynamic lights +#define TE_EXPLFLAG_NOSOUND 4 // do not play client explosion sound +#define TE_EXPLFLAG_NOPARTICLES 8 // do not draw particles +#define TE_TAREXPLOSION 4 // Quake1 "tarbaby" explosion with sound +// coord coord coord (position) -#define TE_TAREXPLOSION 4 // Quake1 "tarbaby" explosion with sound -// coord coord coord (position) - -#define TE_SMOKE 5 // alphablend sprite, move vertically 30 pps -// coord coord coord (position) +#define TE_SMOKE 5 // alphablend sprite, move vertically 30 pps +// coord coord coord (position) // short (sprite index) // byte (scale in 0.1's) // byte (framerate) -#define TE_TRACER 6 // tracer effect from point to point -// coord, coord, coord (start) +#define TE_TRACER 6 // tracer effect from point to point +// coord, coord, coord (start) // coord, coord, coord (end) -#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters -// coord, coord, coord (start) -// coord, coord, coord (end) -// byte (life in 0.1's) -// byte (width in 0.1's) +#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters +// coord, coord, coord (start) +// coord, coord, coord (end) +// byte (life in 0.1's) +// byte (width in 0.1's) // byte (amplitude in 0.01's) // short (sprite model index) -#define TE_BEAMENTS 8 -// short (start entity) -// short (end entity) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMENTS 8 +// short (start entity) +// short (end entity) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_SPARKS 9 // 8 random tracers with gravity, ricochet sprite -// coord coord coord (position) +#define TE_SPARKS 9 // 8 random tracers with gravity, ricochet sprite +// coord coord coord (position) -#define TE_LAVASPLASH 10 // Quake1 lava splash -// coord coord coord (position) +#define TE_LAVASPLASH 10 // Quake1 lava splash +// coord coord coord (position) -#define TE_TELEPORT 11 // Quake1 teleport splash -// coord coord coord (position) +#define TE_TELEPORT 11 // Quake1 teleport splash +// coord coord coord (position) -#define TE_EXPLOSION2 12 // Quake1 colormaped (base palette) particle explosion with sound -// coord coord coord (position) +#define TE_EXPLOSION2 12 // Quake1 colormaped (base palette) particle explosion with sound +// coord coord coord (position) // byte (starting color) // byte (num colors) -#define TE_BSPDECAL 13 // Decal from the .BSP file +#define TE_BSPDECAL 13 // Decal from the .BSP file // coord, coord, coord (x,y,z), decal position (center of texture in world) // short (texture index of precached decal texture name) // short (entity index) // [optional - only included if previous short is non-zero (not the world)] short (index of model of above entity) -#define TE_IMPLOSION 14 // tracers moving toward a point +#define TE_IMPLOSION 14 // tracers moving toward a point // coord, coord, coord (position) // byte (radius) // byte (count) -// byte (life in 0.1's) +// byte (life in 0.1's) -#define TE_SPRITETRAIL 15 // line of moving glow sprites with gravity, fadeout, and collisions -// coord, coord, coord (start) -// coord, coord, coord (end) +#define TE_SPRITETRAIL 15 // line of moving glow sprites with gravity, fadeout, and collisions +// coord, coord, coord (start) +// coord, coord, coord (end) // short (sprite index) // byte (count) -// byte (life in 0.1's) -// byte (scale in 0.1's) +// byte (life in 0.1's) +// byte (scale in 0.1's) // byte (velocity along Vector in 10's) // byte (randomness of velocity in 10's) -#define TE_BEAM 16 // obsolete +#define TE_BEAM 16 // obsolete -#define TE_SPRITE 17 // additive sprite, plays 1 cycle -// coord, coord, coord (position) -// short (sprite index) -// byte (scale in 0.1's) +#define TE_SPRITE 17 // additive sprite, plays 1 cycle +// coord, coord, coord (position) +// short (sprite index) +// byte (scale in 0.1's) // byte (brightness) -#define TE_BEAMSPRITE 18 // A beam with a sprite at the end -// coord, coord, coord (start position) -// coord, coord, coord (end position) -// short (beam sprite index) -// short (end sprite index) +#define TE_BEAMSPRITE 18 // A beam with a sprite at the end +// coord, coord, coord (start position) +// coord, coord, coord (end position) +// short (beam sprite index) +// short (end sprite index) -#define TE_BEAMTORUS 19 // screen aligned beam ring, expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMTORUS 19 // screen aligned beam ring, expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_BEAMDISK 20 // disk that expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMDISK 20 // disk that expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_BEAMCYLINDER 21 // cylinder that expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMCYLINDER 21 // cylinder that expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_BEAMFOLLOW 22 // create a line of decaying beam segments until entity stops moving +#define TE_BEAMFOLLOW 22 // create a line of decaying beam segments until entity stops moving // short (entity:attachment to follow) // short (sprite index) -// byte (life in 0.1's) -// byte (line width in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) // byte,byte,byte (color) // byte (brightness) -#define TE_GLOWSPRITE 23 +#define TE_GLOWSPRITE 23 // coord, coord, coord (pos) short (model index) byte (scale / 10) -#define TE_BEAMRING 24 // connect a beam ring to two entities -// short (start entity) -// short (end entity) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) +#define TE_BEAMRING 24 // connect a beam ring to two entities +// short (start entity) +// short (end entity) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) // byte,byte,byte (color) // byte (brightness) // byte (scroll speed in 0.1's) -#define TE_STREAK_SPLASH 25 // oriented shower of tracers -// coord coord coord (start position) -// coord coord coord (direction Vector) +#define TE_STREAK_SPLASH 25 // oriented shower of tracers +// coord coord coord (start position) +// coord coord coord (direction Vector) // byte (color) // short (count) // short (base speed) // short (ramdon velocity) -#define TE_BEAMHOSE 26 // obsolete +#define TE_BEAMHOSE 26 // obsolete -#define TE_DLIGHT 27 // dynamic light, effect world, minor entity effect -// coord, coord, coord (pos) -// byte (radius in 10's) +#define TE_DLIGHT 27 // dynamic light, effect world, minor entity effect +// coord, coord, coord (pos) +// byte (radius in 10's) // byte byte byte (color) // byte (brightness) // byte (life in 10's) // byte (decay rate in 10's) -#define TE_ELIGHT 28 // point entity light, no world effect +#define TE_ELIGHT 28 // point entity light, no world effect // short (entity:attachment to follow) -// coord coord coord (initial position) +// coord coord coord (initial position) // coord (radius) // byte byte byte (color) // byte (life in 0.1's) // coord (decay rate) -#define TE_TEXTMESSAGE 29 +#define TE_TEXTMESSAGE 29 // short 1.2.13 x (-1 = center) // short 1.2.13 y (-1 = center) // byte Effect 0 = fade in/fade out - // 1 is flickery credits - // 2 is write out (training room) +// 1 is flickery credits +// 2 is write out (training room) // 4 bytes r,g,b,a color1 (text color) // 4 bytes r,g,b,a color2 (effect color) @@ -343,68 +342,68 @@ // ushort 8.8 hold time // optional ushort 8.8 fxtime (time the highlight lags behing the leading text in effect 2) // string text message (512 chars max sz string) -#define TE_LINE 30 +#define TE_LINE 30 // coord, coord, coord startpos // coord, coord, coord endpos // short life in 0.1 s // 3 bytes r, g, b -#define TE_BOX 31 +#define TE_BOX 31 // coord, coord, coord boxmins // coord, coord, coord boxmaxs // short life in 0.1 s // 3 bytes r, g, b -#define TE_KILLBEAM 99 // kill all beams attached to entity +#define TE_KILLBEAM 99 // kill all beams attached to entity // short (entity) -#define TE_LARGEFUNNEL 100 +#define TE_LARGEFUNNEL 100 // coord coord coord (funnel position) -// short (sprite index) -// short (flags) +// short (sprite index) +// short (flags) -#define TE_BLOODSTREAM 101 // particle spray +#define TE_BLOODSTREAM 101 // particle spray // coord coord coord (start position) // coord coord coord (spray Vector) // byte (color) // byte (speed) -#define TE_SHOWLINE 102 // line of particles every 5 units, dies in 30 seconds +#define TE_SHOWLINE 102 // line of particles every 5 units, dies in 30 seconds // coord coord coord (start position) // coord coord coord (end position) -#define TE_BLOOD 103 // particle spray +#define TE_BLOOD 103 // particle spray // coord coord coord (start position) // coord coord coord (spray Vector) // byte (color) // byte (speed) -#define TE_DECAL 104 // Decal applied to a brush entity (not the world) +#define TE_DECAL 104 // Decal applied to a brush entity (not the world) // coord, coord, coord (x,y,z), decal position (center of texture in world) // byte (texture index of precached decal texture name) // short (entity index) -#define TE_FIZZ 105 // create alpha sprites inside of entity, float upwards +#define TE_FIZZ 105 // create alpha sprites inside of entity, float upwards // short (entity) // short (sprite index) // byte (density) -#define TE_MODEL 106 // create a moving model that bounces and makes a sound when it hits -// coord, coord, coord (position) +#define TE_MODEL 106 // create a moving model that bounces and makes a sound when it hits +// coord, coord, coord (position) // coord, coord, coord (velocity) // angle (initial yaw) // short (model index) // byte (bounce sound type) // byte (life in 0.1's) -#define TE_EXPLODEMODEL 107 // spherical shower of models, picks from set +#define TE_EXPLODEMODEL 107 // spherical shower of models, picks from set // coord, coord, coord (origin) // coord (velocity) // short (model index) // short (count) // byte (life in 0.1's) -#define TE_BREAKMODEL 108 // box of models or sprites +#define TE_BREAKMODEL 108 // box of models or sprites // coord, coord, coord (position) // coord, coord, coord (size) // coord, coord, coord (velocity) @@ -414,12 +413,12 @@ // byte (life in 0.1 secs) // byte (flags) -#define TE_GUNSHOTDECAL 109 // decal and ricochet sound +#define TE_GUNSHOTDECAL 109 // decal and ricochet sound // coord, coord, coord (position) // short (entity index???) // byte (decal???) -#define TE_SPRITE_SPRAY 110 // spay of alpha sprites +#define TE_SPRITE_SPRAY 110 // spay of alpha sprites // coord, coord, coord (position) // coord, coord, coord (velocity) // short (sprite index) @@ -427,18 +426,18 @@ // byte (speed) // byte (noise) -#define TE_ARMOR_RICOCHET 111 // quick spark sprite, client ricochet sound. +#define TE_ARMOR_RICOCHET 111 // quick spark sprite, client ricochet sound. // coord, coord, coord (position) // byte (scale in 0.1's) -#define TE_PLAYERDECAL 112 // ??? +#define TE_PLAYERDECAL 112 // ??? // byte (playerindex) // coord, coord, coord (position) // short (entity???) // byte (decal number???) // [optional] short (model index???) -#define TE_BUBBLES 113 // create alpha sprites inside of box, float upwards +#define TE_BUBBLES 113 // create alpha sprites inside of box, float upwards // coord, coord, coord (min start position) // coord, coord, coord (max start position) // coord (float height) @@ -446,7 +445,7 @@ // byte (count) // coord (speed) -#define TE_BUBBLETRAIL 114 // create alpha sprites along a line, float upwards +#define TE_BUBBLETRAIL 114 // create alpha sprites along a line, float upwards // coord, coord, coord (min start position) // coord, coord, coord (max start position) // coord (float height) @@ -454,34 +453,34 @@ // byte (count) // coord (speed) -#define TE_BLOODSPRITE 115 // spray of opaque sprite1's that fall, single sprite2 for 1..2 secs (this is a high-priority tent) +#define TE_BLOODSPRITE 115 // spray of opaque sprite1's that fall, single sprite2 for 1..2 secs (this is a high-priority tent) // coord, coord, coord (position) // short (sprite1 index) // short (sprite2 index) // byte (color) // byte (scale) -#define TE_WORLDDECAL 116 // Decal applied to the world brush +#define TE_WORLDDECAL 116 // Decal applied to the world brush // coord, coord, coord (x,y,z), decal position (center of texture in world) // byte (texture index of precached decal texture name) -#define TE_WORLDDECALHIGH 117 // Decal (with texture index > 256) applied to world brush +#define TE_WORLDDECALHIGH 117 // Decal (with texture index > 256) applied to world brush // coord, coord, coord (x,y,z), decal position (center of texture in world) // byte (texture index of precached decal texture name - 256) -#define TE_DECALHIGH 118 // Same as TE_DECAL, but the texture index was greater than 256 +#define TE_DECALHIGH 118 // Same as TE_DECAL, but the texture index was greater than 256 // coord, coord, coord (x,y,z), decal position (center of texture in world) // byte (texture index of precached decal texture name - 256) // short (entity index) -#define TE_PROJECTILE 119 // Makes a projectile (like a nail) (this is a high-priority tent) +#define TE_PROJECTILE 119 // Makes a projectile (like a nail) (this is a high-priority tent) // coord, coord, coord (position) // coord, coord, coord (velocity) // short (modelindex) // byte (life) // byte (owner) projectile won't collide with owner (if owner == 0, projectile will hit any client). -#define TE_SPRAY 120 // Throws a shower of sprites or models +#define TE_SPRAY 120 // Throws a shower of sprites or models // coord, coord, coord (position) // coord, coord, coord (direction) // short (modelindex) @@ -490,19 +489,19 @@ // byte (noise) // byte (rendermode) -#define TE_PLAYERSPRITES 121 // sprites emit from a player's bounding box (ONLY use for players!) +#define TE_PLAYERSPRITES 121 // sprites emit from a player's bounding box (ONLY use for players!) // byte (playernum) // short (sprite modelindex) // byte (count) // byte (variance) (0 = no variance in size) (10 = 10% variance in size) -#define TE_PARTICLEBURST 122 // very similar to lavasplash. +#define TE_PARTICLEBURST 122 // very similar to lavasplash. // coord (origin) // short (radius) // byte (particle color) // byte (duration * 10) (will be randomized a bit) -#define TE_FIREFIELD 123 // makes a field of fire. +#define TE_FIREFIELD 123 // makes a field of fire. // coord (origin) // short (radius) (fire is made in a square around origin. -radius, -radius to radius, radius) // short (modelindex) @@ -511,22 +510,22 @@ // byte (duration (in seconds) * 10) (will be randomized a bit) // // to keep network traffic low, this message has associated flags that fit into a byte: -#define TEFIRE_FLAG_ALLFLOAT 1 // all sprites will drift upwards as they animate -#define TEFIRE_FLAG_SOMEFLOAT 2 // some of the sprites will drift upwards. (50% chance) -#define TEFIRE_FLAG_LOOP 4 // if set, sprite plays at 15 fps, otherwise plays at whatever rate stretches the animation over the sprite's duration. -#define TEFIRE_FLAG_ALPHA 8 // if set, sprite is rendered alpha blended at 50% else, opaque -#define TEFIRE_FLAG_PLANAR 16 // if set, all fire sprites have same initial Z instead of randomly filling a cube. +#define TEFIRE_FLAG_ALLFLOAT 1 // all sprites will drift upwards as they animate +#define TEFIRE_FLAG_SOMEFLOAT 2 // some of the sprites will drift upwards. (50% chance) +#define TEFIRE_FLAG_LOOP 4 // if set, sprite plays at 15 fps, otherwise plays at whatever rate stretches the animation over the sprite's duration. +#define TEFIRE_FLAG_ALPHA 8 // if set, sprite is rendered alpha blended at 50% else, opaque +#define TEFIRE_FLAG_PLANAR 16 // if set, all fire sprites have same initial Z instead of randomly filling a cube. -#define TE_PLAYERATTACHMENT 124 // attaches a TENT to a player (this is a high-priority tent) +#define TE_PLAYERATTACHMENT 124 // attaches a TENT to a player (this is a high-priority tent) // byte (entity index of player) // coord (vertical offset) ( attachment origin.z = player origin.z + vertical offset ) // short (model index) // short (life * 10 ); -#define TE_KILLPLAYERATTACHMENTS 125 // will expire all TENTS attached to a player. +#define TE_KILLPLAYERATTACHMENTS 125 // will expire all TENTS attached to a player. // byte (entity index of player) -#define TE_MULTIGUNSHOT 126 // much more compact shotgun message +#define TE_MULTIGUNSHOT 126 // much more compact shotgun message // This message is used to make a client approximate a 'spray' of gunfire. // Any weapon that fires more than one bullet per frame and fires in a bit of a spread is // a good candidate for MULTIGUNSHOT use. (shotguns) @@ -545,7 +544,7 @@ // byte (count) // byte (bullethole decal texture index) -#define TE_USERTRACER 127 // larger message than the standard tracer, but allows some customization. +#define TE_USERTRACER 127 // larger message than the standard tracer, but allows some customization. // coord (origin) // coord (origin) // coord (origin) @@ -556,133 +555,129 @@ // byte ( color ) this is an index into an array of color vectors in the engine. (0 - ) // byte ( length * 10 ) - - -#define MSG_BROADCAST 0 // unreliable to all -#define MSG_ONE 1 // reliable to one (msg_entity) -#define MSG_ALL 2 // reliable to all -#define MSG_INIT 3 // write to the init string -#define MSG_PVS 4 // Ents in PVS of org -#define MSG_PAS 5 // Ents in PAS of org -#define MSG_PVS_R 6 // Reliable to PVS -#define MSG_PAS_R 7 // Reliable to PAS -#define MSG_ONE_UNRELIABLE 8 // Send to one client, but don't put in reliable stream, put in unreliable datagram ( could be dropped ) -#define MSG_SPEC 9 // Sends to all spectator proxies +#define MSG_BROADCAST 0 // unreliable to all +#define MSG_ONE 1 // reliable to one (msg_entity) +#define MSG_ALL 2 // reliable to all +#define MSG_INIT 3 // write to the init string +#define MSG_PVS 4 // Ents in PVS of org +#define MSG_PAS 5 // Ents in PAS of org +#define MSG_PVS_R 6 // Reliable to PVS +#define MSG_PAS_R 7 // Reliable to PAS +#define MSG_ONE_UNRELIABLE 8 // Send to one client, but don't put in reliable stream, put in unreliable datagram ( could be dropped ) +#define MSG_SPEC 9 // Sends to all spectator proxies // contents of a spot in the world -#define CONTENTS_EMPTY -1 -#define CONTENTS_SOLID -2 -#define CONTENTS_WATER -3 -#define CONTENTS_SLIME -4 -#define CONTENTS_LAVA -5 -#define CONTENTS_SKY -6 +#define CONTENTS_EMPTY -1 +#define CONTENTS_SOLID -2 +#define CONTENTS_WATER -3 +#define CONTENTS_SLIME -4 +#define CONTENTS_LAVA -5 +#define CONTENTS_SKY -6 -#define CONTENTS_LADDER -16 +#define CONTENTS_LADDER -16 -#define CONTENT_FLYFIELD -17 -#define CONTENT_GRAVITY_FLYFIELD -18 -#define CONTENT_FOG -19 +#define CONTENT_FLYFIELD -17 +#define CONTENT_GRAVITY_FLYFIELD -18 +#define CONTENT_FOG -19 -#define CONTENT_EMPTY -1 -#define CONTENT_SOLID -2 -#define CONTENT_WATER -3 -#define CONTENT_SLIME -4 -#define CONTENT_LAVA -5 -#define CONTENT_SKY -6 +#define CONTENT_EMPTY -1 +#define CONTENT_SOLID -2 +#define CONTENT_WATER -3 +#define CONTENT_SLIME -4 +#define CONTENT_LAVA -5 +#define CONTENT_SKY -6 // channels -#define CHAN_AUTO 0 -#define CHAN_WEAPON 1 -#define CHAN_VOICE 2 -#define CHAN_ITEM 3 -#define CHAN_BODY 4 -#define CHAN_STREAM 5 // allocate stream channel from the static or dynamic area -#define CHAN_STATIC 6 // allocate channel from the static area -#define CHAN_NETWORKVOICE_BASE 7 // voice data coming across the network -#define CHAN_NETWORKVOICE_END 500 // network voice data reserves slots (CHAN_NETWORKVOICE_BASE through CHAN_NETWORKVOICE_END). +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 +#define CHAN_STREAM 5 // allocate stream channel from the static or dynamic area +#define CHAN_STATIC 6 // allocate channel from the static area +#define CHAN_NETWORKVOICE_BASE 7 // voice data coming across the network +#define CHAN_NETWORKVOICE_END 500 // network voice data reserves slots (CHAN_NETWORKVOICE_BASE through CHAN_NETWORKVOICE_END). // attenuation values -#define ATTN_NONE 0.0f -#define ATTN_NORM 0.8f -#define ATTN_IDLE 2f -#define ATTN_STATIC 1.25f +#define ATTN_NONE 0.0f +#define ATTN_NORM 0.8f +#define ATTN_IDLE 2f +#define ATTN_STATIC 1.25f // pitch values -#define PITCH_NORM 100 // non-pitch shifted -#define PITCH_LOW 95 // other values are possible - 0-255, where 255 is very high -#define PITCH_HIGH 120 +#define PITCH_NORM 100 // non-pitch shifted +#define PITCH_LOW 95 // other values are possible - 0-255, where 255 is very high +#define PITCH_HIGH 120 // volume values -#define VOL_NORM 1.0 +#define VOL_NORM 1.0 // plats -#define PLAT_LOW_TRIGGER 1 +#define PLAT_LOW_TRIGGER 1 // Trains -#define SF_TRAIN_WAIT_RETRIGGER 1 -#define SF_TRAIN_START_ON 4 // Train is initially moving -#define SF_TRAIN_PASSABLE 8 // Train is not solid -- used to make water trains +#define SF_TRAIN_WAIT_RETRIGGER 1 +#define SF_TRAIN_START_ON 4 // Train is initially moving +#define SF_TRAIN_PASSABLE 8 // Train is not solid -- used to make water trains // buttons -#define IN_ATTACK (1 << 0) -#define IN_JUMP (1 << 1) -#define IN_DUCK (1 << 2) -#define IN_FORWARD (1 << 3) -#define IN_BACK (1 << 4) -#define IN_USE (1 << 5) -#define IN_CANCEL (1 << 6) -#define IN_LEFT (1 << 7) -#define IN_RIGHT (1 << 8) +#define IN_ATTACK (1 << 0) +#define IN_JUMP (1 << 1) +#define IN_DUCK (1 << 2) +#define IN_FORWARD (1 << 3) +#define IN_BACK (1 << 4) +#define IN_USE (1 << 5) +#define IN_CANCEL (1 << 6) +#define IN_LEFT (1 << 7) +#define IN_RIGHT (1 << 8) #define IN_MOVELEFT (1 << 9) #define IN_MOVERIGHT (1 << 10) -#define IN_ATTACK2 (1 << 11) -#define IN_RUN (1 << 12) -#define IN_RELOAD (1 << 13) -#define IN_ALT1 (1 << 14) -#define IN_SCORE (1 << 15) +#define IN_ATTACK2 (1 << 11) +#define IN_RUN (1 << 12) +#define IN_RELOAD (1 << 13) +#define IN_ALT1 (1 << 14) +#define IN_SCORE (1 << 15) // Break Model Defines -#define BREAK_TYPEMASK 0x4F -#define BREAK_GLASS 0x01 -#define BREAK_METAL 0x02 -#define BREAK_FLESH 0x04 -#define BREAK_WOOD 0x08 +#define BREAK_TYPEMASK 0x4F +#define BREAK_GLASS 0x01 +#define BREAK_METAL 0x02 +#define BREAK_FLESH 0x04 +#define BREAK_WOOD 0x08 -#define BREAK_SMOKE 0x10 -#define BREAK_TRANS 0x20 -#define BREAK_CONCRETE 0x40 -#define BREAK_2 0x80 +#define BREAK_SMOKE 0x10 +#define BREAK_TRANS 0x20 +#define BREAK_CONCRETE 0x40 +#define BREAK_2 0x80 // Colliding temp entity sounds -#define BOUNCE_GLASS BREAK_GLASS -#define BOUNCE_METAL BREAK_METAL -#define BOUNCE_FLESH BREAK_FLESH -#define BOUNCE_WOOD BREAK_WOOD -#define BOUNCE_SHRAP 0x10 -#define BOUNCE_SHELL 0x20 +#define BOUNCE_GLASS BREAK_GLASS +#define BOUNCE_METAL BREAK_METAL +#define BOUNCE_FLESH BREAK_FLESH +#define BOUNCE_WOOD BREAK_WOOD +#define BOUNCE_SHRAP 0x10 +#define BOUNCE_SHELL 0x20 #define BOUNCE_CONCRETE BREAK_CONCRETE #define BOUNCE_SHOTSHELL 0x80 // Temp entity bounce sound types -#define TE_BOUNCE_NULL 0 -#define TE_BOUNCE_SHELL 1 -#define TE_BOUNCE_SHOTSHELL 2 +#define TE_BOUNCE_NULL 0 +#define TE_BOUNCE_SHELL 1 +#define TE_BOUNCE_SHOTSHELL 2 // Rendering constants -enum -{ - kRenderNormal, // src - kRenderTransColor, // c*a+dest*(1-a) - kRenderTransTexture, // src*a+dest*(1-a) - kRenderGlow, // src*a+dest -- No Z buffer checks - kRenderTransAlpha, // src*srca+dest*(1-srca) - kRenderTransAdd // src*a+dest +enum { + kRenderNormal, // src + kRenderTransColor, // c*a+dest*(1-a) + kRenderTransTexture, // src*a+dest*(1-a) + kRenderGlow, // src*a+dest -- No Z buffer checks + kRenderTransAlpha, // src*srca+dest*(1-srca) + kRenderTransAdd // src*a+dest }; -enum -{ +enum { kRenderFxNone = 0, kRenderFxPulseSlow, kRenderFxPulseFast, @@ -698,41 +693,37 @@ enum kRenderFxFlickerSlow, kRenderFxFlickerFast, kRenderFxNoDissipation, - kRenderFxDistort, // Distort/scale/translate flicker - kRenderFxHologram, // kRenderFxDistort + distance fade - kRenderFxDeadPlayer, // kRenderAmt is the player index - kRenderFxExplode, // Scale up really big! - kRenderFxGlowShell, // Glowing Shell - kRenderFxClampMinScale // Keep this sprite from getting very small (SPRITES only!) + kRenderFxDistort, // Distort/scale/translate flicker + kRenderFxHologram, // kRenderFxDistort + distance fade + kRenderFxDeadPlayer, // kRenderAmt is the player index + kRenderFxExplode, // Scale up really big! + kRenderFxGlowShell, // Glowing Shell + kRenderFxClampMinScale // Keep this sprite from getting very small (SPRITES only!) }; - typedef int func_t; typedef int string_t; -typedef struct link_s -{ +typedef struct link_s { struct link_s *prev, *next; } link_t; typedef struct edict_s edict_t; -typedef struct -{ +typedef struct { vec3_t normal; float dist; } plane_t; -typedef struct -{ - int allsolid; // if true, plane is not valid - int startsolid; // if true, the initial point was in a solid area +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 + 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 7d7da4a..586ba5a 100644 --- a/include/engine/eiface.h +++ b/include/engine/eiface.h @@ -1,17 +1,17 @@ /*** -* -* Copyright (c) 1999-2005, Valve Corporation. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ + * + * Copyright (c) 1999-2005, Valve Corporation. All rights reserved. + * + * This product contains software technology licensed from Id + * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. + * All Rights Reserved. + * + * This source code contains proprietary and confidential information of + * Valve LLC and its suppliers. Access to this code is restricted to + * persons who have executed a written SDK license with Valve. Any access, + * use or distribution of this code by or to any unlicensed person is illegal. + * + ****/ #ifndef EIFACE_H #define EIFACE_H @@ -19,18 +19,17 @@ #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 +#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 -struct cvar_t -{ +struct cvar_t { char *name; char *string; int flags; @@ -50,337 +49,323 @@ struct cvar_t #ifdef _WIN32 #define DLLEXPORT __stdcall #else -#define DLLEXPORT /* */ +#define DLLEXPORT /* */ #endif -typedef enum -{ +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_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 ). + 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; +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 +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; // Returned by TraceLine -typedef struct -{ - int fAllSolid; // if true, plane is not valid - int fStartSolid; // if true, the initial point was in a solid area +typedef struct { + int fAllSolid; // if true, plane is not valid + int fStartSolid; // if true, the initial point was in a solid area int fInOpen; int fInWater; - float flFraction; // time completed, 1.0 = didn't hit anything - vec3_t vecEndPos; // final position + float flFraction; // time completed, 1.0 = didn't hit anything + vec3_t vecEndPos; // final position float flPlaneDist; - vec3_t vecPlaneNormal; // surface normal at impact - edict_t *pHit; // entity the surface is on - int iHitgroup; // 0 == generic, non zero is specific body part + vec3_t vecPlaneNormal; // surface normal at impact + edict_t *pHit; // entity the surface is on + int iHitgroup; // 0 == generic, non zero is specific body part } TraceResult; typedef uint32 CRC32_t; // Engine hands this to DLLs for functionality callbacks -typedef struct enginefuncs_s -{ - int (*pfnPrecacheModel) (char *s); - int (*pfnPrecacheSound) (char *s); - void (*pfnSetModel) (edict_t *e, const char *m); - int (*pfnModelIndex) (const char *m); - int (*pfnModelFrames) (int modelIndex); - void (*pfnSetSize) (edict_t *e, const float *rgflMin, const float *rgflMax); - void (*pfnChangeLevel) (char *s1, char *s2); - void (*pfnGetSpawnParms) (edict_t *ent); - void (*pfnSaveSpawnParms) (edict_t *ent); - float (*pfnVecToYaw) (const float *rgflVector); - void (*pfnVecToAngles) (const float *rgflVectorIn, float *rgflVectorOut); - void (*pfnMoveToOrigin) (edict_t *ent, const float *pflGoal, float dist, int iMoveType); - void (*pfnChangeYaw) (edict_t *ent); - void (*pfnChangePitch) (edict_t *ent); - edict_t *(*pfnFindEntityByString) (edict_t *pentEdictStartSearchAfter, const char *pszField, const char *pszValue); - int (*pfnGetEntityIllum) (edict_t *pEnt); - edict_t *(*pfnFindEntityInSphere) (edict_t *pentEdictStartSearchAfter, const float *org, float rad); - edict_t *(*pfnFindClientInPVS) (edict_t *ent); - edict_t *(*pfnEntitiesInPVS) (edict_t *pplayer); - void (*pfnMakeVectors) (const float *rgflVector); - void (*pfnAngleVectors) (const float *rgflVector, float *forward, float *right, float *up); - edict_t *(*pfnCreateEntity) (void); - void (*pfnRemoveEntity) (edict_t *e); - edict_t *(*pfnCreateNamedEntity) (int className); - void (*pfnMakeStatic) (edict_t *ent); - int (*pfnEntIsOnFloor) (edict_t *e); - int (*pfnDropToFloor) (edict_t *e); - int (*pfnWalkMove) (edict_t *ent, float yaw, float dist, int mode); - void (*pfnSetOrigin) (edict_t *e, const float *rgflOrigin); - void (*pfnEmitSound) (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch); - void (*pfnEmitAmbientSound) (edict_t *entity, float *pos, const char *samp, float vol, float attenuation, int fFlags, int pitch); - void (*pfnTraceLine) (const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnTraceToss) (edict_t *pent, edict_t *pentToIgnore, TraceResult *ptr); - int (*pfnTraceMonsterHull) (edict_t *ent, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnTraceHull) (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnTraceModel) (const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr); +typedef struct enginefuncs_s { + int (*pfnPrecacheModel) (char *s); + int (*pfnPrecacheSound) (char *s); + void (*pfnSetModel) (edict_t *e, const char *m); + int (*pfnModelIndex) (const char *m); + int (*pfnModelFrames) (int modelIndex); + void (*pfnSetSize) (edict_t *e, const float *rgflMin, const float *rgflMax); + void (*pfnChangeLevel) (char *s1, char *s2); + void (*pfnGetSpawnParms) (edict_t *ent); + void (*pfnSaveSpawnParms) (edict_t *ent); + float (*pfnVecToYaw) (const float *rgflVector); + void (*pfnVecToAngles) (const float *rgflVectorIn, float *rgflVectorOut); + void (*pfnMoveToOrigin) (edict_t *ent, const float *pflGoal, float dist, int iMoveType); + void (*pfnChangeYaw) (edict_t *ent); + void (*pfnChangePitch) (edict_t *ent); + edict_t *(*pfnFindEntityByString) (edict_t *pentEdictStartSearchAfter, const char *pszField, const char *pszValue); + int (*pfnGetEntityIllum) (edict_t *pEnt); + edict_t *(*pfnFindEntityInSphere) (edict_t *pentEdictStartSearchAfter, const float *org, float rad); + edict_t *(*pfnFindClientInPVS) (edict_t *ent); + edict_t *(*pfnEntitiesInPVS) (edict_t *pplayer); + void (*pfnMakeVectors) (const float *rgflVector); + void (*pfnAngleVectors) (const float *rgflVector, float *forward, float *right, float *up); + edict_t *(*pfnCreateEntity) (void); + void (*pfnRemoveEntity) (edict_t *e); + edict_t *(*pfnCreateNamedEntity) (int className); + void (*pfnMakeStatic) (edict_t *ent); + int (*pfnEntIsOnFloor) (edict_t *e); + int (*pfnDropToFloor) (edict_t *e); + int (*pfnWalkMove) (edict_t *ent, float yaw, float dist, int mode); + void (*pfnSetOrigin) (edict_t *e, const float *rgflOrigin); + void (*pfnEmitSound) (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch); + void (*pfnEmitAmbientSound) (edict_t *entity, float *pos, const char *samp, float vol, float attenuation, int fFlags, int pitch); + void (*pfnTraceLine) (const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnTraceToss) (edict_t *pent, edict_t *pentToIgnore, TraceResult *ptr); + int (*pfnTraceMonsterHull) (edict_t *ent, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnTraceHull) (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnTraceModel) (const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr); const char *(*pfnTraceTexture) (edict_t *pTextureEntity, const float *v1, const float *v2); - void (*pfnTraceSphere) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnGetAimVector) (edict_t *ent, float speed, float *rgflReturn); - void (*pfnServerCommand) (char *str); - void (*pfnServerExecute) (void); - void (*pfnClientCommand) (edict_t *ent, char const *szFmt, ...); - void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count); - void (*pfnLightStyle) (int style, char *val); - int (*pfnDecalIndex) (const char *name); - int (*pfnPointContents) (const float *rgflVector); - void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); - void (*pfnMessageEnd) (void); - void (*pfnWriteByte) (int value); - void (*pfnWriteChar) (int value); - void (*pfnWriteShort) (int value); - void (*pfnWriteLong) (int value); - void (*pfnWriteAngle) (float flValue); - void (*pfnWriteCoord) (float flValue); - void (*pfnWriteString) (const char *sz); - void (*pfnWriteEntity) (int value); - void (*pfnCVarRegister) (cvar_t *pCvar); - float (*pfnCVarGetFloat) (const char *szVarName); + void (*pfnTraceSphere) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnGetAimVector) (edict_t *ent, float speed, float *rgflReturn); + void (*pfnServerCommand) (char *str); + void (*pfnServerExecute) (void); + void (*pfnClientCommand) (edict_t *ent, char const *szFmt, ...); + void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count); + void (*pfnLightStyle) (int style, char *val); + int (*pfnDecalIndex) (const char *name); + int (*pfnPointContents) (const float *rgflVector); + void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + void (*pfnMessageEnd) (void); + void (*pfnWriteByte) (int value); + void (*pfnWriteChar) (int value); + void (*pfnWriteShort) (int value); + void (*pfnWriteLong) (int value); + void (*pfnWriteAngle) (float flValue); + void (*pfnWriteCoord) (float flValue); + void (*pfnWriteString) (const char *sz); + void (*pfnWriteEntity) (int value); + void (*pfnCVarRegister) (cvar_t *pCvar); + float (*pfnCVarGetFloat) (const char *szVarName); const char *(*pfnCVarGetString) (const char *szVarName); - void (*pfnCVarSetFloat) (const char *szVarName, float flValue); - void (*pfnCVarSetString) (const char *szVarName, const char *szValue); - void (*pfnAlertMessage) (ALERT_TYPE atype, char *szFmt, ...); - void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...); - void *(*pfnPvAllocEntPrivateData) (edict_t *ent, int32 cb); - void *(*pfnPvEntPrivateData) (edict_t *ent); - void (*pfnFreeEntPrivateData) (edict_t *ent); + void (*pfnCVarSetFloat) (const char *szVarName, float flValue); + void (*pfnCVarSetString) (const char *szVarName, const char *szValue); + void (*pfnAlertMessage) (ALERT_TYPE atype, char *szFmt, ...); + void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...); + void *(*pfnPvAllocEntPrivateData) (edict_t *ent, int32 cb); + void *(*pfnPvEntPrivateData) (edict_t *ent); + void (*pfnFreeEntPrivateData) (edict_t *ent); const char *(*pfnSzFromIndex) (int stingPtr); - int (*pfnAllostring) (const char *szValue); + int (*pfnAllocString) (const char *szValue); struct entvars_s *(*pfnGetVarsOfEnt) (edict_t *ent); - edict_t *(*pfnPEntityOfEntOffset) (int iEntOffset); - int (*pfnEntOffsetOfPEntity) (const edict_t *ent); - int (*pfnIndexOfEdict) (const edict_t *ent); - edict_t *(*pfnPEntityOfEntIndex) (int entIndex); - edict_t *(*pfnFindEntityByVars) (struct entvars_s *pvars); - void *(*pfnGetModelPtr) (edict_t *ent); - int (*pfnRegUserMsg) (const char *pszName, int iSize); - void (*pfnAnimationAutomove) (const edict_t *ent, float flTime); - void (*pfnGetBonePosition) (const edict_t *ent, int iBone, float *rgflOrigin, float *rgflAngles); - uint32 (*pfnFunctionFromName) (const char *pName); + edict_t *(*pfnPEntityOfEntOffset) (int iEntOffset); + int (*pfnEntOffsetOfPEntity) (const edict_t *ent); + int (*pfnIndexOfEdict) (const edict_t *ent); + edict_t *(*pfnPEntityOfEntIndex) (int entIndex); + edict_t *(*pfnFindEntityByVars) (struct entvars_s *pvars); + void *(*pfnGetModelPtr) (edict_t *ent); + int (*pfnRegUserMsg) (const char *pszName, int iSize); + void (*pfnAnimationAutomove) (const edict_t *ent, float flTime); + void (*pfnGetBonePosition) (const edict_t *ent, int iBone, float *rgflOrigin, float *rgflAngles); + uint32 (*pfnFunctionFromName) (const char *pName); const char *(*pfnNameForFunction) (uint32 function); - void (*pfnClientPrintf) (edict_t *ent, PRINT_TYPE ptype, const char *szMsg); // JOHN: engine callbacks so game DLL can print messages to individual clients - void (*pfnServerPrint) (const char *szMsg); - const char *(*pfnCmd_Args) (void); // these 3 added - const char *(*pfnCmd_Argv) (int argc); // so game DLL can easily - int (*pfnCmd_Argc) (void); // access client 'cmd' strings - void (*pfnGetAttachment) (const edict_t *ent, int iAttachment, float *rgflOrigin, float *rgflAngles); - void (*pfnCRC32_Init) (CRC32_t *pulCRC); - void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len); - void (*pfnCRC32_ProcessByte) (CRC32_t *pulCRC, uint8 ch); - CRC32_t (*pfnCRC32_Final) (CRC32_t pulCRC); - int32 (*pfnRandomLong) (int32 lLow, int32 lHigh); - float (*pfnRandomFloat) (float flLow, float flHigh); - void (*pfnSetView) (const edict_t *client, const edict_t *pViewent); - float (*pfnTime) (void); - void (*pfnCrosshairAngle) (const edict_t *client, float pitch, float yaw); - uint8 *(*pfnLoadFileForMe) (char const *szFilename, int *pLength); - void (*pfnFreeFile) (void *buffer); - void (*pfnEndSection) (const char *pszSectionName); // trigger_endsection - int (*pfnCompareFileTime) (char *filename1, char *filename2, int *compare); - void (*pfnGetGameDir) (char *szGetGameDir); - void (*pfnCvar_RegisterVariable) (cvar_t *variable); - void (*pfnFadeClientVolume) (const edict_t *ent, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds); - void (*pfnSetClientMaxspeed) (const edict_t *ent, float fNewMaxspeed); - edict_t *(*pfnCreateFakeClient) (const char *netname); // returns nullptr if fake client can't be created - void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, uint16 buttons, uint8 impulse, uint8 msec); - int (*pfnNumberOfEntities) (void); - char *(*pfnGetInfoKeyBuffer) (edict_t *e); // passing in nullptr gets the serverinfo - char *(*pfnInfoKeyValue) (char *infobuffer, char const *key); - void (*pfnSetKeyValue) (char *infobuffer, char *key, char *value); - void (*pfnSetClientKeyValue) (int clientIndex, char *infobuffer, char const *key, char const *value); - int (*pfnIsMapValid) (char *szFilename); - void (*pfnStaticDecal) (const float *origin, int decalIndex, int entityIndex, int modelIndex); - int (*pfnPrecacheGeneric) (char *s); - int (*pfnGetPlayerUserId) (edict_t *e); // returns the server assigned userid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients - void (*pfnBuildSoundMsg) (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); - int (*pfnIsDedicatedServer) (void); // is this a dedicated server? - cvar_t *(*pfnCVarGetPointer) (const char *szVarName); - unsigned int (*pfnGetPlayerWONId) (edict_t *e); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients - - void (*pfnInfo_RemoveKey) (char *s, const char *key); + void (*pfnClientPrintf) (edict_t *ent, PRINT_TYPE ptype, const char *szMsg); // JOHN: engine callbacks so game DLL can print messages to individual clients + void (*pfnServerPrint) (const char *szMsg); + const char *(*pfnCmd_Args) (void); // these 3 added + const char *(*pfnCmd_Argv) (int argc); // so game DLL can easily + int (*pfnCmd_Argc) (void); // access client 'cmd' strings + void (*pfnGetAttachment) (const edict_t *ent, int iAttachment, float *rgflOrigin, float *rgflAngles); + void (*pfnCRC32_Init) (CRC32_t *pulCRC); + void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len); + void (*pfnCRC32_ProcessByte) (CRC32_t *pulCRC, uint8 ch); + CRC32_t (*pfnCRC32_Final) (CRC32_t pulCRC); + int32 (*pfnRandomLong) (int32 lLow, int32 lHigh); + float (*pfnRandomFloat) (float flLow, float flHigh); + void (*pfnSetView) (const edict_t *client, const edict_t *pViewent); + float (*pfnTime) (void); + void (*pfnCrosshairAngle) (const edict_t *client, float pitch, float yaw); + uint8 *(*pfnLoadFileForMe) (char const *szFilename, int *pLength); + void (*pfnFreeFile) (void *buffer); + void (*pfnEndSection) (const char *pszSectionName); // trigger_endsection + int (*pfnCompareFileTime) (char *filename1, char *filename2, int *compare); + void (*pfnGetGameDir) (char *szGetGameDir); + void (*pfnCvar_RegisterVariable) (cvar_t *variable); + void (*pfnFadeClientVolume) (const edict_t *ent, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds); + void (*pfnSetClientMaxspeed) (const edict_t *ent, float fNewMaxspeed); + edict_t *(*pfnCreateFakeClient) (const char *netname); // returns nullptr if fake client can't be created + void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, uint16 buttons, uint8 impulse, uint8 msec); + int (*pfnNumberOfEntities) (void); + char *(*pfnGetInfoKeyBuffer) (edict_t *e); // passing in nullptr gets the serverinfo + char *(*pfnInfoKeyValue) (char *infobuffer, char const *key); + void (*pfnSetKeyValue) (char *infobuffer, char *key, char *value); + void (*pfnSetClientKeyValue) (int clientIndex, char *infobuffer, char const *key, char const *value); + int (*pfnIsMapValid) (char *szFilename); + void (*pfnStaticDecal) (const float *origin, int decalIndex, int entityIndex, int modelIndex); + int (*pfnPrecacheGeneric) (char *s); + int (*pfnGetPlayerUserId) (edict_t *e); // returns the server assigned userid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients + void (*pfnBuildSoundMsg) (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + int (*pfnIsDedicatedServer) (void); // is this a dedicated server? + cvar_t *(*pfnCVarGetPointer) (const char *szVarName); + unsigned int (*pfnGetPlayerWONId) (edict_t *e); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients + + void (*pfnInfo_RemoveKey) (char *s, const char *key); const char *(*pfnGetPhysicsKeyValue) (const edict_t *client, const char *key); - void (*pfnSetPhysicsKeyValue) (const edict_t *client, const char *key, const char *value); + void (*pfnSetPhysicsKeyValue) (const edict_t *client, const char *key, const char *value); const char *(*pfnGetPhysicsInfoString) (const edict_t *client); uint16 (*pfnPrecacheEvent) (int type, const char *psz); void (*pfnPlaybackEvent) (int flags, const edict_t *pInvoker, uint16 evIndexOfEntity, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2); uint8 *(*pfnSetFatPVS) (float *org); uint8 *(*pfnSetFatPAS) (float *org); - int (*pfnCheckVisibility) (const edict_t *entity, uint8 *pset); - void (*pfnDeltaSetField) (struct delta_s *pFields, const char *fieldname); - void (*pfnDeltaUnsetField) (struct delta_s *pFields, const char *fieldname); - void (*pfnDeltaAddEncoder) (char *name, void (*conditionalencode) (struct delta_s *pFields, const uint8 *from, const uint8 *to)); - int (*pfnGetCurrentPlayer) (void); - int (*pfnCanSkipPlayer) (const edict_t *player); - int (*pfnDeltaFindField) (struct delta_s *pFields, const char *fieldname); - void (*pfnDeltaSetFieldByIndex) (struct delta_s *pFields, int fieldNumber); - void (*pfnDeltaUnsetFieldByIndex) (struct delta_s *pFields, int fieldNumber); - void (*pfnSetGroupMask) (int mask, int op); - int (*pfnCreateInstancedBaseline) (int classname, struct entity_state_s *baseline); - void (*pfnCvar_DirectSet) (struct cvar_t *var, char *value); - void (*pfnForceUnmodified) (FORCE_TYPE type, float *mins, float *maxs, const char *szFilename); - void (*pfnGetPlayerStats) (const edict_t *client, int *ping, int *packet_loss); - void (*pfnAddServerCommand) (char *cmd_name, void (*function) (void)); - + int (*pfnCheckVisibility) (const edict_t *entity, uint8 *pset); + void (*pfnDeltaSetField) (struct delta_s *pFields, const char *fieldname); + void (*pfnDeltaUnsetField) (struct delta_s *pFields, const char *fieldname); + void (*pfnDeltaAddEncoder) (char *name, void (*conditionalencode) (struct delta_s *pFields, const uint8 *from, const uint8 *to)); + int (*pfnGetCurrentPlayer) (void); + int (*pfnCanSkipPlayer) (const edict_t *player); + int (*pfnDeltaFindField) (struct delta_s *pFields, const char *fieldname); + void (*pfnDeltaSetFieldByIndex) (struct delta_s *pFields, int fieldNumber); + void (*pfnDeltaUnsetFieldByIndex) (struct delta_s *pFields, int fieldNumber); + void (*pfnSetGroupMask) (int mask, int op); + int (*pfnCreateInstancedBaseline) (int classname, struct entity_state_s *baseline); + void (*pfnCvar_DirectSet) (struct cvar_t *var, char *value); + void (*pfnForceUnmodified) (FORCE_TYPE type, float *mins, float *maxs, const char *szFilename); + void (*pfnGetPlayerStats) (const edict_t *client, int *ping, int *packet_loss); + void (*pfnAddServerCommand) (char *cmd_name, void (*function) (void)); + int (*pfnVoice_GetClientListening) (int iReceiver, int iSender); int (*pfnVoice_SetClientListening) (int iReceiver, int iSender, int bListen); - + const char *(*pfnGetPlayerAuthId) (edict_t *e); - + struct sequenceEntry_s *(*pfnSequenceGet) (const char *fileName, const char *entryName); struct sentenceEntry_s *(*pfnSequencePickSentence) (const char *groupName, int pickMethod, int *picked); - - int (*pfnGetFileSize) (char *szFilename); + + int (*pfnGetFileSize) (char *szFilename); unsigned int (*pfnGetApproxWavePlayLen) (const char *filepath); - - int (*pfnIsCareerMatch) (void); - int (*pfnGetLocalizedStringLength) (const char *label); - void (*pfnRegisterTutorMessageShown) (int mid); - int (*pfnGetTimesTutorMessageShown) (int mid); - void (*pfnProcessTutorMessageDecayBuffer) (int *buffer, int bufferLength); - void (*pfnConstructTutorMessageDecayBuffer) (int *buffer, int bufferLength); - void (*pfnResetTutorMessageDecayData) (void); - - void (*pfnQueryClientCVarValue) (const edict_t *player, const char *cvarName); - void (*pfnQueryClientCVarValue2) (const edict_t *player, const char *cvarName, int requestID); - int (*pfnCheckParm)(const char *pchCmdLineToken, char **ppnext); + + int (*pfnIsCareerMatch) (void); + int (*pfnGetLocalizedStringLength) (const char *label); + void (*pfnRegisterTutorMessageShown) (int mid); + int (*pfnGetTimesTutorMessageShown) (int mid); + void (*pfnProcessTutorMessageDecayBuffer) (int *buffer, int bufferLength); + void (*pfnConstructTutorMessageDecayBuffer) (int *buffer, int bufferLength); + void (*pfnResetTutorMessageDecayData) (void); + + void (*pfnQueryClientCVarValue) (const edict_t *player, const char *cvarName); + void (*pfnQueryClientCVarValue2) (const edict_t *player, const char *cvarName, int requestID); + int (*pfnCheckParm) (const char *pchCmdLineToken, char **ppnext); } enginefuncs_t; // Passed to pfnKeyValue -typedef struct KeyValueData_s -{ +typedef struct KeyValueData_s { char *szClassName; // in: entity classname - char const *szKeyName; // in: name of key - char *szValue; // in: value of key - int32 fHandled; // out: DLL sets to true if key-value pair was understood + char const *szKeyName; // in: name of key + char *szValue; // in: value of key + int32 fHandled; // out: DLL sets to true if key-value pair was understood } KeyValueData; - -#define ARRAYSIZE_HLSDK(p) (int) (sizeof(p)/sizeof(p[0])) typedef struct customization_s customization_t; -typedef struct -{ +typedef struct { // Initialize/shutdown the game (one-time call after loading of game .dll ) - void (*pfnGameInit) (void); - int (*pfnSpawn) (edict_t *pent); - void (*pfnThink) (edict_t *pent); - void (*pfnUse) (edict_t *pentUsed, edict_t *pentOther); - void (*pfnTouch) (edict_t *pentTouched, edict_t *pentOther); - void (*pfnBlocked) (edict_t *pentBlocked, edict_t *pentOther); - void (*pfnKeyValue) (edict_t *pentKeyvalue, KeyValueData *pkvd); - void (*pfnSave) (edict_t *pent, struct SAVERESTOREDATA *pSaveData); - int (*pfnRestore) (edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity); - void (*pfnSetAbsBox) (edict_t *pent); + void (*pfnGameInit) (void); + int (*pfnSpawn) (edict_t *pent); + void (*pfnThink) (edict_t *pent); + void (*pfnUse) (edict_t *pentUsed, edict_t *pentOther); + void (*pfnTouch) (edict_t *pentTouched, edict_t *pentOther); + void (*pfnBlocked) (edict_t *pentBlocked, edict_t *pentOther); + void (*pfnKeyValue) (edict_t *pentKeyvalue, KeyValueData *pkvd); + void (*pfnSave) (edict_t *pent, struct SAVERESTOREDATA *pSaveData); + int (*pfnRestore) (edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity); + void (*pfnSetAbsBox) (edict_t *pent); - void (*pfnSaveWriteFields) (SAVERESTOREDATA *, const char *, void *, struct TYPEDESCRIPTION *, int); - void (*pfnSaveReadFields) (SAVERESTOREDATA *, const char *, void *, TYPEDESCRIPTION *, int); + void (*pfnSaveWriteFields) (SAVERESTOREDATA *, const char *, void *, struct TYPEDESCRIPTION *, int); + void (*pfnSaveReadFields) (SAVERESTOREDATA *, const char *, void *, TYPEDESCRIPTION *, int); - void (*pfnSaveGlobalState) (SAVERESTOREDATA *); - void (*pfnRestoreGlobalState) (SAVERESTOREDATA *); - void (*pfnResetGlobalState) (void); + void (*pfnSaveGlobalState) (SAVERESTOREDATA *); + void (*pfnRestoreGlobalState) (SAVERESTOREDATA *); + void (*pfnResetGlobalState) (void); - int (*pfnClientConnect) (edict_t *ent, const char *pszName, const char *pszAddress, char szRejectReason[128]); + int (*pfnClientConnect) (edict_t *ent, const char *pszName, const char *pszAddress, char szRejectReason[128]); - void (*pfnClientDisconnect) (edict_t *ent); - void (*pfnClientKill) (edict_t *ent); - void (*pfnClientPutInServer) (edict_t *ent); - void (*pfnClientCommand) (edict_t *ent); - void (*pfnClientUserInfoChanged) (edict_t *ent, char *infobuffer); + void (*pfnClientDisconnect) (edict_t *ent); + void (*pfnClientKill) (edict_t *ent); + void (*pfnClientPutInServer) (edict_t *ent); + void (*pfnClientCommand) (edict_t *ent); + void (*pfnClientUserInfoChanged) (edict_t *ent, char *infobuffer); - void (*pfnServerActivate) (edict_t *edictList, int edictCount, int clientMax); - void (*pfnServerDeactivate) (void); + void (*pfnServerActivate) (edict_t *edictList, int edictCount, int clientMax); + void (*pfnServerDeactivate) (void); - void (*pfnPlayerPreThink) (edict_t *ent); - void (*pfnPlayerPostThink) (edict_t *ent); + void (*pfnPlayerPreThink) (edict_t *ent); + void (*pfnPlayerPostThink) (edict_t *ent); - void (*pfnStartFrame) (void); - void (*pfnParmsNewLevel) (void); - void (*pfnParmsChangeLevel) (void); + void (*pfnStartFrame) (void); + void (*pfnParmsNewLevel) (void); + void (*pfnParmsChangeLevel) (void); // Returns string describing current .dll. E.g., TeamFotrress 2, Half-Life const char *(*pfnGetGameDescription) (void); // Notify dll about a player customization. - void (*pfnPlayerCustomization) (edict_t *ent, struct customization_s *pCustom); + void (*pfnPlayerCustomization) (edict_t *ent, struct customization_s *pCustom); // Spectator funcs - void (*pfnSpectatorConnect) (edict_t *ent); - void (*pfnSpectatorDisconnect) (edict_t *ent); - void (*pfnSpectatorThink) (edict_t *ent); + void (*pfnSpectatorConnect) (edict_t *ent); + void (*pfnSpectatorDisconnect) (edict_t *ent); + void (*pfnSpectatorThink) (edict_t *ent); // Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. - void (*pfnSys_Error) (const char *error_string); + void (*pfnSys_Error) (const char *error_string); - void (*pfnPM_Move) (struct playermove_s *ppmove, int server); - void (*pfnPM_Init) (struct playermove_s *ppmove); - char (*pfnPM_FindTextureType) (char *name); - void (*pfnSetupVisibility) (struct edict_s *pViewEntity, struct edict_s *client, uint8 **pvs, uint8 **pas); - void (*pfnUpdateClientData) (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd); - int (*pfnAddToFullPack) (struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, uint8 *pSet); - void (*pfnCreateBaseline) (int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, float* player_mins, float* player_maxs); - void (*pfnRegisterEncoders) (void); - int (*pfnGetWeaponData) (struct edict_s *player, struct weapon_data_s *info); + void (*pfnPM_Move) (struct playermove_s *ppmove, int server); + void (*pfnPM_Init) (struct playermove_s *ppmove); + char (*pfnPM_FindTextureType) (char *name); + void (*pfnSetupVisibility) (struct edict_s *pViewEntity, struct edict_s *client, uint8 **pvs, uint8 **pas); + void (*pfnUpdateClientData) (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd); + int (*pfnAddToFullPack) (struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, uint8 *pSet); + void (*pfnCreateBaseline) (int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, float *player_mins, float *player_maxs); + void (*pfnRegisterEncoders) (void); + int (*pfnGetWeaponData) (struct edict_s *player, struct weapon_data_s *info); - void (*pfnCmdStart) (const edict_t *player, const struct c *cmd, unsigned int random_seed); - void (*pfnCmdEnd) (const edict_t *player); + void (*pfnCmdStart) (const edict_t *player, const struct c *cmd, unsigned int random_seed); + void (*pfnCmdEnd) (const edict_t *player); // Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max // size of the response_buffer, so you must zero it out if you choose not to respond. - int (*pfnConnectionlessPacket) (const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size); + int (*pfnConnectionlessPacket) (const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size); // Enumerates player hulls. Returns 0 if the hull number doesn't exist, 1 otherwise - int (*pfnGetHullBounds) (int hullnumber, float *mins, float *maxs); + int (*pfnGetHullBounds) (int hullnumber, float *mins, float *maxs); // Create baselines for certain "unplaced" items. - void (*pfnCreateInstancedBaselines) (void); + void (*pfnCreateInstancedBaselines) (void); // One of the pfnForceUnmodified files failed the consistency check for the specified player // Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) - int (*pfnInconsistentFile) (const struct edict_s *player, const char *szFilename, char *disconnect_message); + int (*pfnInconsistentFile) (const struct edict_s *player, const char *szFilename, char *disconnect_message); // The game .dll should return 1 if lag compensation should be allowed ( could also just set // the sv_unlag cvar. // Most games right now should return 0, until client-side weapon prediction code is written // and tested for them. - int (*pfnAllowLagCompensation) (void); + int (*pfnAllowLagCompensation) (void); } gamefuncs_t; // Current version. -#define NEWGAMEDLLFUNCS_VERSION 1 +#define NEWGAMEDLLFUNCS_VERSION 1 -typedef struct -{ - // Called right before the object's memory is freed. +typedef struct { + // Called right before the object's memory is freed. // Calls its destructor. - void (*pfnOnFreeEntPrivateData) (edict_t *pEnt); - void (*pfnGameShutdown) (void); - int (*pfnShouldCollide) (edict_t *pentTouched, edict_t *pentOther); + void (*pfnOnFreeEntPrivateData) (edict_t *pEnt); + void (*pfnGameShutdown) (void); + int (*pfnShouldCollide) (edict_t *pentTouched, edict_t *pentOther); - void (*pfnCvarValue) (const edict_t *pEnt, const char *value); - void (*pfnCvarValue2) (const edict_t *pEnt, int requestID, const char *cvarName, const char *value); + 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 */ +#endif /* EIFACE_H */ diff --git a/include/engine/enginecallback.h b/include/engine/enginecallback.h deleted file mode 100644 index ad35a37..0000000 --- a/include/engine/enginecallback.h +++ /dev/null @@ -1,145 +0,0 @@ -/*** -* -* Copyright (c) 1999-2005, Valve Corporation. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef ENGINECALLBACK_H -#define ENGINECALLBACK_H -#pragma once - -// Must be provided by user of this code -extern enginefuncs_t g_engfuncs; - -// The actual engine callbacks -#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId) -#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel) -#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound) -#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric) -#define SET_MODEL (*g_engfuncs.pfnSetModel) -#define MODEL_INDEX (*g_engfuncs.pfnModelIndex) -#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames) -#define SET_SIZE (*g_engfuncs.pfnSetSize) -#define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel) -#define GET_INFOKEYBUFFER (*g_engfuncs.pfnGetInfoKeyBuffer) -#define INFOKEY_VALUE (*g_engfuncs.pfnInfoKeyValue) -#define SET_CLIENT_KEYVALUE (*g_engfuncs.pfnSetClientKeyValue) -#define REG_SVR_COMMAND (*g_engfuncs.pfnAddServerCommand) -#define SERVER_PRINT (*g_engfuncs.pfnServerPrint) -#define SET_SERVER_KEYVALUE (*g_engfuncs.pfnSetKeyValue) -#define GET_SPAWN_PARMS (*g_engfuncs.pfnGetSpawnParms) -#define SAVE_SPAWN_PARMS (*g_engfuncs.pfnSaveSpawnParms) -#define VEC_TO_YAW (*g_engfuncs.pfnVecToYaw) -#define VEC_TO_ANGLES (*g_engfuncs.pfnVecToAngles) -#define MOVE_TO_ORIGIN (*g_engfuncs.pfnMoveToOrigin) -#define oldCHANGE_YAW (*g_engfuncs.pfnChangeYaw) -#define CHANGE_PITCH (*g_engfuncs.pfnChangePitch) -#define MAKE_VECTORS (*g_engfuncs.pfnMakeVectors) -#define CREATE_ENTITY (*g_engfuncs.pfnCreateEntity) -#define REMOVE_ENTITY (*g_engfuncs.pfnRemoveEntity) -#define CREATE_NAMED_ENTITY (*g_engfuncs.pfnCreateNamedEntity) -#define MAKE_STATIC (*g_engfuncs.pfnMakeStatic) -#define ENT_IS_ON_FLOOR (*g_engfuncs.pfnEntIsOnFloor) -#define DROP_TO_FLOOR (*g_engfuncs.pfnDropToFloor) -#define WALK_MOVE (*g_engfuncs.pfnWalkMove) -#define SET_ORIGIN (*g_engfuncs.pfnSetOrigin) -#define EMIT_SOUND_DYN2 (*g_engfuncs.pfnEmitSound) -#define BUILD_SOUND_MSG (*g_engfuncs.pfnBuildSoundMsg) -#define TRACE_LINE (*g_engfuncs.pfnTraceLine) -#define TRACE_TOSS (*g_engfuncs.pfnTraceToss) -#define TRACE_MONSTER_HULL (*g_engfuncs.pfnTraceMonsterHull) -#define TRACE_HULL (*g_engfuncs.pfnTraceHull) -#define GET_AIM_VECTOR (*g_engfuncs.pfnGetAimVector) -#define SERVER_COMMAND (*g_engfuncs.pfnServerCommand) -#define SERVER_EXECUTE (*g_engfuncs.pfnServerExecute) -#define CLIENT_COMMAND (*g_engfuncs.pfnClientCommand) -#define PARTICLE_EFFECT (*g_engfuncs.pfnParticleEffect) -#define LIGHT_STYLE (*g_engfuncs.pfnLightStyle) -#define DECAL_INDEX (*g_engfuncs.pfnDecalIndex) -#define POINT_CONTENTS (*g_engfuncs.pfnPointContents) -#define CRC32_INIT (*g_engfuncs.pfnCRC32_Init) -#define CRC32_PROCESS_BUFFER (*g_engfuncs.pfnCRC32_ProcessBuffer) -#define CRC32_PROCESS_BYTE (*g_engfuncs.pfnCRC32_ProcessByte) -#define CRC32_FINAL (*g_engfuncs.pfnCRC32_Final) -#define RANDOM_LONG (*g_engfuncs.pfnRandomLong) -#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) -#define GETPLAYERAUTHID (*g_engfuncs.pfnGetPlayerAuthId) -static inline void MESSAGE_BEGIN (int msg_dest, int msg_type, const float *pOrigin = nullptr, edict_t *ed = nullptr) -{ - (*g_engfuncs.pfnMessageBegin) (msg_dest, msg_type, pOrigin, ed); -} - -#define MESSAGE_END (*g_engfuncs.pfnMessageEnd) -#define WRITE_BYTE (*g_engfuncs.pfnWriteByte) -#define WRITE_CHAR (*g_engfuncs.pfnWriteChar) -#define WRITE_SHORT (*g_engfuncs.pfnWriteShort) -#define WRITE_LONG (*g_engfuncs.pfnWriteLong) -#define WRITE_ANGLE (*g_engfuncs.pfnWriteAngle) -#define WRITE_COORD (*g_engfuncs.pfnWriteCoord) -#define WRITE_STRING (*g_engfuncs.pfnWriteString) -#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity) -#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister) -#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat) -#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString) -#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat) -#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString) -#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer) -#define ALERT (*g_engfuncs.pfnAlertMessage) -#define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf) -#define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) -#define GET_PRIVATE(pent) (pent ? (pent->pvPrivateData) : nullptr); -#define FREE_PRIVATE (*g_engfuncs.pfnFreeEntPrivateData) -#define ALLOC_STRING (*g_engfuncs.pfnAllostring) -#define FIND_ENTITY_BY_STRING (*g_engfuncs.pfnFindEntityByString) -#define GETENTITYILLUM (*g_engfuncs.pfnGetEntityIllum) -#define FIND_ENTITY_IN_SPHERE (*g_engfuncs.pfnFindEntityInSphere) -#define FIND_CLIENT_IN_PVS (*g_engfuncs.pfnFindClientInPVS) -#define EMIT_AMBIENT_SOUND (*g_engfuncs.pfnEmitAmbientSound) -#define GET_MODEL_PTR (*g_engfuncs.pfnGetModelPtr) -#define REG_USER_MSG (*g_engfuncs.pfnRegUserMsg) -#define GET_BONE_POSITION (*g_engfuncs.pfnGetBonePosition) -#define FUNCTION_FROM_NAME (*g_engfuncs.pfnFunctionFromName) -#define NAME_FOR_FUNCTION (*g_engfuncs.pfnNameForFunction) -#define TRACE_TEXTURE (*g_engfuncs.pfnTraceTexture) -#define CLIENT_PRINTF (*g_engfuncs.pfnClientPrintf) -#define CMD_ARGS (*g_engfuncs.pfnCmd_Args) -#define CMD_ARGC (*g_engfuncs.pfnCmd_Argc) -#define CMD_ARGV (*g_engfuncs.pfnCmd_Argv) -#define GET_ATTACHMENT (*g_engfuncs.pfnGetAttachment) -#define SET_VIEW (*g_engfuncs.pfnSetView) -#define SET_CROSSHAIRANGLE (*g_engfuncs.pfnCrosshairAngle) -#define LOAD_FILE_FOR_ME (*g_engfuncs.pfnLoadFileForMe) -#define FREE_FILE (*g_engfuncs.pfnFreeFile) -#define COMPARE_FILE_TIME (*g_engfuncs.pfnCompareFileTime) -#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir) -#define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid) -#define NUMBER_OF_ENTITIES (*g_engfuncs.pfnNumberOfEntities) -#define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer) -#define PRECACHE_EVENT (*g_engfuncs.pfnPrecacheEvent) -#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) -#define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) -#define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) -#define ENGINE_CHECK_VISIBILITY (*g_engfuncs.pfnCheckVisibility) -#define DELTA_SET (*g_engfuncs.pfnDeltaSetField) -#define DELTA_UNSET (*g_engfuncs.pfnDeltaUnsetField) -#define DELTA_ADDENCODER (*g_engfuncs.pfnDeltaAddEncoder) -#define ENGINE_CURRENT_PLAYER (*g_engfuncs.pfnGetCurrentPlayer) -#define ENGINE_CANSKIP (*g_engfuncs.pfnCanSkipPlayer) -#define DELTA_FINDFIELD (*g_engfuncs.pfnDeltaFindField) -#define DELTA_SETBYINDEX (*g_engfuncs.pfnDeltaSetFieldByIndex) -#define DELTA_UNSETBYINDEX (*g_engfuncs.pfnDeltaUnsetFieldByIndex) -#define ENGINE_GETPHYSINFO (*g_engfuncs.pfnGetPhysicsInfoString) -#define ENGINE_SETGROUPMASK (*g_engfuncs.pfnSetGroupMask) -#define ENGINE_INSTANCE_BASELINE (*g_engfuncs.pfnCreateInstancedBaseline) -#define ENGINE_FORCE_UNMODIFIED (*g_engfuncs.pfnForceUnmodified) -#define PLAYER_CNX_STATS (*g_engfuncs.pfnGetPlayerStats) - -#endif //ENGINECALLBACK_H diff --git a/include/engine/extdll.h b/include/engine/extdll.h index 08e9a09..4eef052 100644 --- a/include/engine/extdll.h +++ b/include/engine/extdll.h @@ -1,27 +1,27 @@ /*** -* -* Copyright (c) 1999-2005, Valve Corporation. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ + * + * Copyright (c) 1999-2005, Valve Corporation. All rights reserved. + * + * This product contains software technology licensed from Id + * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. + * All Rights Reserved. + * + * This source code contains proprietary and confidential information of + * Valve LLC and its suppliers. Access to this code is restricted to + * persons who have executed a written SDK license with Valve. Any access, + * use or distribution of this code by or to any unlicensed person is illegal. + * + ****/ #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 +/* 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 @@ -32,72 +32,70 @@ #define NOIME #include "windows.h" #include "winsock2.h" -#else // _WIN32 +#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)) +#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) +#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" -#include "math.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 int func_t; // +typedef int string_t; // from engine's pr_comp.h; +typedef float vec_t; // needed before including progdefs.h #include "corelib.h" -#define vec3_t Vector +typedef cr::classes::Vector vec3_t; +using namespace cr::types; #include "const.h" #include "progdefs.h" -#define MAX_ENT_LEAFS 48 +#define MAX_ENT_LEAFS 48 -struct edict_s -{ +struct edict_s { int free; int serialnumber; - link_t area; // linked to a division node or leaf - int headnode; // -1 to use normal leaf check + link_t area; // linked to a division node or leaf + int headnode; // -1 to use normal leaf check int num_leafs; short leafnums[MAX_ENT_LEAFS]; - float freetime; // sv.time when the object was freed - void *pvPrivateData; // Alloced and freed by engine, used by DLLs - entvars_t v; // C exported fields from progs + float freetime; // sv.time when the object was freed + void *pvPrivateData; // Alloced and freed by engine, used by DLLs + entvars_t v; // C exported fields from progs }; #include "eiface.h" -#define MAX_WEAPON_SLOTS 5 // hud item selection slots -#define MAX_ITEM_TYPES 6 // hud item selection slots +#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 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 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 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 HUD_PRINTNOTIFY 1 +#define HUD_PRINTCONSOLE 2 +#define HUD_PRINTTALK 3 +#define HUD_PRINTCENTER 4 -#define WEAPON_SUIT 31 -#define __USE_GNU 1 +#define WEAPON_SUIT 31 -#endif //EXTDLL_H +#endif // EXTDLL_H diff --git a/include/engine/meta_api.h b/include/engine/meta_api.h index bd07733..5bf7dc2 100644 --- a/include/engine/meta_api.h +++ b/include/engine/meta_api.h @@ -15,18 +15,9 @@ typedef int (*GET_ENGINE_FUNCTIONS_FN) (enginefuncs_t *pengfuncsFromEngine, int #define META_INTERFACE_VERSION "5:13" -typedef enum -{ - PT_NEVER = 0, - PT_STARTUP, - PT_CHANGELEVEL, - PT_ANYTIME, - PT_ANYPAUSE -} PLUG_LOADTIME; +typedef enum { PT_NEVER = 0, PT_STARTUP, PT_CHANGELEVEL, PT_ANYTIME, PT_ANYPAUSE } PLUG_LOADTIME; - -typedef struct -{ +typedef struct { char const *ifvers; char const *name; char const *version; @@ -41,33 +32,13 @@ extern plugin_info_t Plugin_info; typedef plugin_info_t *plid_t; -#define PLID &Plugin_info +#define PLID &Plugin_info +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 -{ - 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 -{ - MRES_UNSET = 0, - MRES_IGNORED, - MRES_HANDLED, - MRES_OVERRIDE, - MRES_SUPERCEDE -} META_RES; - -typedef struct meta_globals_s -{ +typedef struct meta_globals_s { META_RES mres; META_RES prev_mres; META_RES status; @@ -77,16 +48,23 @@ typedef struct meta_globals_s extern meta_globals_t *gpMetaGlobals; -#define SET_META_RESULT(result) gpMetaGlobals->mres=result -#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 SET_META_RESULT(result) gpMetaGlobals->mres = result +#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 +#define META_RESULT_OVERRIDE_RET(type) *(type *)gpMetaGlobals->override_ret -typedef struct -{ +typedef struct { GETENTITYAPI_FN pfnGetEntityAPI; GETENTITYAPI_FN pfnGetEntityAPI_Post; GETENTITYAPI2_FN pfnGetEntityAPI2; @@ -100,45 +78,34 @@ typedef struct #include "util.h" // max buffer size for printed messages -#define MAX_LOGMSG_LEN 1024 +#define MAX_LOGMSG_LEN 1024 // for getgameinfo: -typedef enum -{ - GINFO_NAME = 0, - GINFO_DESC, - GINFO_GAMEDIR, - GINFO_DLL_FULLPATH, - GINFO_DLL_FILENAME, - GINFO_REALDLL_FULLPATH -} ginfo_t; +typedef enum { GINFO_NAME = 0, GINFO_DESC, GINFO_GAMEDIR, GINFO_DLL_FULLPATH, GINFO_DLL_FILENAME, GINFO_REALDLL_FULLPATH } ginfo_t; // 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); +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); + 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 -{ +typedef struct { gamefuncs_t *dllapi_table; newgamefuncs_t *newapi_table; } gamedll_funcs_t; @@ -148,85 +115,85 @@ extern mutil_funcs_t *gpMetaUtilFuncs; extern meta_globals_t *gpMetaGlobals; extern metamod_funcs_t gMetaFunctionTable; -#define MDLL_FUNC gpGamedllFuncs->dllapi_table +#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 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 MNEW_FUNC gpGamedllFuncs->newapi_table +#define MNEW_FUNC gpGamedllFuncs->newapi_table -#define MNEW_OnFreeEntPrivateData MNEW_FUNC->pfnOnFreeEntPrivateData -#define MNEW_GameShutdown MNEW_FUNC->pfnGameShutdown -#define MNEW_ShouldCollide MNEW_FUNC->pfnShouldCollide -#define MNEW_CvarValue MNEW_FUNC->pfnCvarValue -#define MNEW_CvarValue2 MNEW_FUNC->pfnCvarValue2 +#define MNEW_OnFreeEntPrivateData MNEW_FUNC->pfnOnFreeEntPrivateData +#define MNEW_GameShutdown MNEW_FUNC->pfnGameShutdown +#define MNEW_ShouldCollide MNEW_FUNC->pfnShouldCollide +#define MNEW_CvarValue MNEW_FUNC->pfnCvarValue +#define MNEW_CvarValue2 MNEW_FUNC->pfnCvarValue2 // convenience macros for metautil functions -#define LOG_CONSOLE (*gpMetaUtilFuncs->pfnLogConsole) -#define LOG_MESSAGE (*gpMetaUtilFuncs->pfnLogMessage) -#define LOG_MMERROR (*gpMetaUtilFuncs->pfnLogError) -#define LOG_DEVELOPER (*gpMetaUtilFuncs->pfnLogDeveloper) -#define CENTER_SAY (*gpMetaUtilFuncs->pfnCenterSay) -#define CENTER_SAY_PARMS (*gpMetaUtilFuncs->pfnCenterSayParms) -#define CENTER_SAY_VARARGS (*gpMetaUtilFuncs->pfnCenterSayVarargs) -#define CALL_GAME_ENTITY (*gpMetaUtilFuncs->pfnCallGameEntity) -#define GET_USER_MSG_ID (*gpMetaUtilFuncs->pfnGetUserMsgID) -#define GET_USER_MSG_NAME (*gpMetaUtilFuncs->pfnGetUserMsgName) -#define GET_PLUGIN_PATH (*gpMetaUtilFuncs->pfnGetPluginPath) -#define GET_GAME_INFO (*gpMetaUtilFuncs->pfnGetGameInfo) -#define LOAD_PLUGIN (*gpMetaUtilFuncs->pfnLoadPlugin) -#define UNLOAD_PLUGIN (*gpMetaUtilFuncs->pfnUnloadPlugin) -#define UNLOAD_PLUGIN_BY_HANDLE (*gpMetaUtilFuncs->pfnUnloadPluginByHandle) +#define LOG_CONSOLE (*gpMetaUtilFuncs->pfnLogConsole) +#define LOG_MESSAGE (*gpMetaUtilFuncs->pfnLogMessage) +#define LOG_MMERROR (*gpMetaUtilFuncs->pfnLogError) +#define LOG_DEVELOPER (*gpMetaUtilFuncs->pfnLogDeveloper) +#define CENTER_SAY (*gpMetaUtilFuncs->pfnCenterSay) +#define CENTER_SAY_PARMS (*gpMetaUtilFuncs->pfnCenterSayParms) +#define CENTER_SAY_VARARGS (*gpMetaUtilFuncs->pfnCenterSayVarargs) +#define CALL_GAME_ENTITY (*gpMetaUtilFuncs->pfnCallGameEntity) +#define GET_USER_MSG_ID (*gpMetaUtilFuncs->pfnGetUserMsgID) +#define GET_USER_MSG_NAME (*gpMetaUtilFuncs->pfnGetUserMsgName) +#define GET_PLUGIN_PATH (*gpMetaUtilFuncs->pfnGetPluginPath) +#define GET_GAME_INFO (*gpMetaUtilFuncs->pfnGetGameInfo) +#define LOAD_PLUGIN (*gpMetaUtilFuncs->pfnLoadPlugin) +#define UNLOAD_PLUGIN (*gpMetaUtilFuncs->pfnUnloadPlugin) +#define UNLOAD_PLUGIN_BY_HANDLE (*gpMetaUtilFuncs->pfnUnloadPluginByHandle) #define IS_QUERYING_CLIENT_CVAR (*gpMetaUtilFuncs->pfnIsQueryingClienCVar_t) -#define MAKE_REQUESTID (*gpMetaUtilFuncs->pfnMakeRequestID) -#define GET_HOOK_TABLES (*gpMetaUtilFuncs->pfnGetHookTables) +#define MAKE_REQUESTID (*gpMetaUtilFuncs->pfnMakeRequestID) +#define GET_HOOK_TABLES (*gpMetaUtilFuncs->pfnGetHookTables) #endif diff --git a/include/engine/progdefs.h b/include/engine/progdefs.h index 71a02c0..5412d27 100644 --- a/include/engine/progdefs.h +++ b/include/engine/progdefs.h @@ -1,25 +1,24 @@ /*** -* -* Copyright (c) 1999-2005, Valve Corporation. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ + * + * Copyright (c) 1999-2005, Valve Corporation. All rights reserved. + * + * This product contains software technology licensed from Id + * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. + * All Rights Reserved. + * + * This source code contains proprietary and confidential information of + * Valve LLC and its suppliers. Access to this code is restricted to + * persons who have executed a written SDK license with Valve. Any access, + * 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 -{ +typedef struct { float time; float frametime; float force_retouch; @@ -53,9 +52,7 @@ typedef struct vec3_t vecLandmarkOffset; } globalvars_t; - -typedef struct entvars_s -{ +typedef struct entvars_s { string_t classname; string_t globalname; @@ -63,14 +60,14 @@ typedef struct entvars_s vec3_t oldorigin; vec3_t velocity; vec3_t basevelocity; - vec3_t clbasevelocity; // Base velocity that was passed in to server physics so + vec3_t clbasevelocity; // Base velocity that was passed in to server physics so // client can predict conveyors correctly. Server zeroes it, so we need to store here, too. vec3_t movedir; - vec3_t angles; // Model angles - vec3_t avelocity; // angle velocity (degrees per second) - vec3_t punchangle; // auto-decaying view angle adjustment - vec3_t v_angle; // Viewing angle (player only) + vec3_t angles; // Model angles + vec3_t avelocity; // angle velocity (degrees per second) + vec3_t punchangle; // auto-decaying view angle adjustment + vec3_t v_angle; // Viewing angle (player only) // For parametric entities vec3_t endpos; @@ -78,7 +75,7 @@ typedef struct entvars_s float impacttime; float starttime; - int fixangle; // 0:nothing, 1:force view angles, 2:add avelocity + int fixangle; // 0:nothing, 1:force view angles, 2:add avelocity float idealpitch; float pitch_speed; float ideal_yaw; @@ -87,14 +84,14 @@ typedef struct entvars_s int modelindex; string_t model; - int viewmodel; // player's viewmodel - int weaponmodel; // what other players see + int viewmodel; // player's viewmodel + int weaponmodel; // what other players see - vec3_t absmin; // BB max translated to world coord - vec3_t absmax; // BB max translated to world coord - vec3_t mins; // local BB min - vec3_t maxs; // local BB max - vec3_t size; // maxs - mins + vec3_t absmin; // BB max translated to world coord + vec3_t absmax; // BB max translated to world coord + vec3_t mins; // local BB min + vec3_t maxs; // local BB max + vec3_t size; // maxs - mins float ltime; float nextthink; @@ -103,23 +100,23 @@ typedef struct entvars_s int solid; int skin; - int body; // sub-model selection for studiomodels + int body; // sub-model selection for studiomodels int effects; - float gravity; // % of "normal" gravity - float friction; // inverse elasticity of MOVETYPE_BOUNCE + float gravity; // % of "normal" gravity + float friction; // inverse elasticity of MOVETYPE_BOUNCE int light_level; - int sequence; // animation sequence - int gaitsequence; // movement animation sequence for player (0 for none) - float frame; // % playback position in animation sequences (0..255) - float animtime; // world time when frame was set - float framerate; // animation playback rate (-8x to 8x) - uint8 controller[4]; // bone controller setting (0..255) - uint8 blending[2]; // blending amount between sub-sequences (0..255) + int sequence; // animation sequence + int gaitsequence; // movement animation sequence for player (0 for none) + float frame; // % playback position in animation sequences (0..255) + float animtime; // world time when frame was set + float framerate; // animation playback rate (-8x to 8x) + uint8 controller[4]; // bone controller setting (0..255) + uint8 blending[2]; // blending amount between sub-sequences (0..255) - float scale; // sprite rendering scale (0..255) + float scale; // sprite rendering scale (0..255) int rendermode; float renderamt; @@ -128,26 +125,26 @@ typedef struct entvars_s float health; float frags; - int weapons; // bit mask for available weapons + int weapons; // bit mask for available weapons float takedamage; int deadflag; - vec3_t view_ofs; // eye position + vec3_t view_ofs; // eye position int button; int impulse; - edict_t *chain; // Entity pointer when linked into a linked list + edict_t *chain; // Entity pointer when linked into a linked list edict_t *dmg_inflictor; edict_t *enemy; - edict_t *aiment; // entity pointer when MOVETYPE_FOLLOW + edict_t *aiment; // entity pointer when MOVETYPE_FOLLOW edict_t *owner; edict_t *groundentity; int spawnflags; int flags; - int colormap; // lowbyte topcolor, highbyte bottomcolor + int colormap; // lowbyte topcolor, highbyte bottomcolor int team; float max_health; diff --git a/include/engine/util.h b/include/engine/util.h index e0a387d..cc405f1 100644 --- a/include/engine/util.h +++ b/include/engine/util.h @@ -1,117 +1,50 @@ /*** -* -* Copyright (c) 1999-2005, Valve Corporation. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ + * + * Copyright (c) 1999-2005, Valve Corporation. All rights reserved. + * + * This product contains software technology licensed from Id + * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. + * All Rights Reserved. + * + * This source code contains proprietary and confidential information of + * Valve LLC and its suppliers. Access to this code is restricted to + * persons who have executed a written SDK license with Valve. Any access, + * use or distribution of this code by or to any unlicensed person is illegal. + * + ****/ #ifndef SDKUTIL_H #define SDKUTIL_H -#ifndef ENGINECALLBACK_H -#include "enginecallback.h" -#endif -static inline void MESSAGE_BEGIN (int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent); // implementation later in this file - extern globalvars_t *g_pGlobals; - -#define DLL_GLOBAL - -extern DLL_GLOBAL const Vector g_vZero; +extern enginefuncs_t g_engfuncs; // Use this instead of ALLOC_STRING on constant strings -#define STRING(offset) (const char *)(g_pGlobals->pStringBase + (int)offset) -#define MAKE_STRING(str) ((int)str - (int)STRING(0)) -#define ENGINE_STR(str) (const_cast (STRING (ALLOC_STRING (str)))) +#define STRING(offset) (const char *)(g_pGlobals->pStringBase + (int)offset) -static inline edict_t *FIND_ENTITY_BY_CLASSNAME (edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING (entStart, "classname", pszName); +// form fwgs-hlsdk +static inline int MAKE_STRING (const char *val) { + long long ptrdiff = val - STRING (0); + + if (ptrdiff > INT_MAX || ptrdiff < INT_MIN) { + return g_engfuncs.pfnAllocString (val); + } + return static_cast (ptrdiff); } -static inline edict_t *FIND_ENTITY_BY_TARGETNAME (edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING (entStart, "targetname", pszName); -} - -// for doing a reverse lookup. Say you have a door, and want to find its button. -static inline edict_t *FIND_ENTITY_BY_TARGET (edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING (entStart, "target", pszName); -} - -// Keeps clutter down a bit, when using a float as a bit-Vector -#define SetBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) | (bits)) -#define ClearBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) & ~(bits)) -#define FBitSet(flBitVector, bit) ((int)(flBitVector) & (bit)) - -// Makes these more explicit, and easier to find -#define FILE_GLOBAL static - -// Until we figure out why "const" gives the compiler problems, we'll just have to use -// this bogus "empty" define to mark things as constant. -#define CONSTANT - -// More explicit than "int" -typedef int EOFFSET; - -// In case it's not alread defined -#ifndef BOOL -typedef int BOOL; -#endif - -// In case this ever changes -#ifndef M_PI -#define M_PI 3.1415926 -#endif - -short FixedSigned16 (float value, float scale); -uint16 FixedUnsigned16 (float value, float scale); - -static inline void MESSAGE_BEGIN (int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent) -{ - (*g_engfuncs.pfnMessageBegin) (msg_dest, msg_type, pOrigin, ent->pContainingEntity); -} +#define ENGINE_STR(str) (const_cast (STRING (g_engfuncs.pfnAllocString (str)))) // 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 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 -// Misc useful -static inline BOOL FStrEq (const char *sz1, const char *sz2) -{ - return (strcmp (sz1, sz2) == 0); -} -static inline BOOL FClassnameIs (edict_t *pent, const char *szClassname) -{ - return FStrEq (STRING (pent->v.classname), szClassname); -} -static inline BOOL FClassnameIs (entvars_t *pev, const char *szClassname) -{ - return FStrEq (STRING (pev->classname), szClassname); -} +typedef enum { ignore_monsters = 1, dont_ignore_monsters = 0, missile = 2 } IGNORE_MONSTERS; +typedef enum { ignore_glass = 1, dont_ignore_glass = 0 } IGNORE_GLASS; +typedef enum { point_hull = 0, human_hull = 1, large_hull = 2, head_hull = 3 } HULL; -typedef enum -{ ignore_monsters = 1, dont_ignore_monsters = 0, missile = 2 } IGNORE_MONSTERS; -typedef enum -{ ignore_glass = 1, dont_ignore_glass = 0 } IGNORE_GLASS; -typedef enum -{ point_hull = 0, human_hull = 1, large_hull = 2, head_hull = 3 } HULL; -typedef int (*tMenuCallback) (edict_t *, int); - - -typedef struct hudtextparms_s -{ +typedef struct hudtextparms_s { float x; float y; int effect; @@ -124,124 +57,87 @@ typedef struct hudtextparms_s int channel; } hudtextparms_t; +#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 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 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 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 +#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_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_SMALLRADIUS 128 #define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 -#define SF_BRUSH_ROTATE_LARGERADIUS 512 +#define SF_BRUSH_ROTATE_LARGERADIUS 512 -#define PUSH_BLOCK_ONLY_X 1 -#define PUSH_BLOCK_ONLY_Y 2 +#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_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_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 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 +#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 +#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 +#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_PUSH_BREAKABLE 128 -#define SF_LIGHT_START_OFF 1 +#define SF_LIGHT_START_OFF 1 -#define SPAWNFLAG_NOMESSAGE 1 -#define SPAWNFLAG_NOTOUCH 1 -#define SPAWNFLAG_DROIDONLY 4 +#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 SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) -#define TELE_PLAYER_ONLY 1 -#define TELE_SILENT 2 +#define TELE_PLAYER_ONLY 1 +#define TELE_SILENT 2 -#define SF_TRIG_PUSH_ONCE 1 +#define SF_TRIG_PUSH_ONCE 1 -// NOTE: use EMIT_SOUND_DYN to set the pitch of a sound. Pitch of 100 -// is no pitch shift. Pitch > 100 up to 255 is a higher pitch, pitch < 100 -// down to 1 is a lower pitch. 150 to 70 is the realistic range. -// EMIT_SOUND_DYN with pitch != 100 should be used sparingly, as it's not quite as -// fast as EMIT_SOUND (the pitchshift mixer is not native coded). - -void EMIT_SOUND_DYN (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch); - - -static inline void EMIT_SOUND (edict_t *entity, int channel, const char *sample, float volume, float attenuation) -{ - EMIT_SOUND_DYN (entity, channel, sample, volume, attenuation, 0, PITCH_NORM); -} - -static inline void STOP_SOUND (edict_t *entity, int channel, const char *sample) -{ - EMIT_SOUND_DYN (entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM); -} - -// macro to handle memory allocation fails -#define TerminateOnMalloc() \ - AddLogEntry (true, LL_FATAL, "Memory Allocation Fail!\nFile: %s (Line: %d)", __FILE__, __LINE__) \ - -// internal assert function -#define InternalAssert(Expr) \ - if (!(Expr)) \ - { \ - AddLogEntry (true, LL_ERROR, "Assertion Fail! (Expression: %s, File: %s, Line: %d)", #Expr, __FILE__, __LINE__); \ - } \ - - -static inline void MakeVectors (const Vector &in) -{ - in.BuildVectors (&g_pGlobals->v_forward, &g_pGlobals->v_right, &g_pGlobals->v_up); -} #endif - diff --git a/include/globals.h b/include/globals.h index e5223ed..d4825a6 100644 --- a/include/globals.h +++ b/include/globals.h @@ -4,18 +4,18 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // #pragma once extern bool g_canSayBombPlanted; extern bool g_bombPlanted; -extern bool g_bombSayString; +extern bool g_bombSayString; extern bool g_roundEnded; extern bool g_waypointOn; extern bool g_autoWaypoint; -extern bool g_botsCanPause; +extern bool g_botsCanPause; extern bool g_editNoclip; extern bool g_gameWelcomeSent; @@ -27,10 +27,9 @@ extern float g_timeRoundEnd; extern float g_timeRoundMid; extern float g_timeRoundStart; extern float g_timePerSecondUpdate; -extern float g_lastRadioTime[2]; +extern float g_lastRadioTime[MAX_TEAM_COUNT]; -extern int g_mapType; -extern int g_numWaypoints; +extern int g_mapFlags; extern int g_gameFlags; extern int g_highestDamageCT; @@ -43,34 +42,32 @@ extern int g_carefulWeaponPrefs[NUM_WEAPONS]; extern int g_grenadeBuyPrecent[NUM_WEAPONS - 23]; extern int g_botBuyEconomyTable[NUM_WEAPONS - 15]; extern int g_radioSelect[MAX_ENGINE_PLAYERS]; -extern int g_lastRadio[2]; +extern int g_lastRadio[MAX_TEAM_COUNT]; extern int g_storeAddbotVars[4]; extern int *g_weaponPrefs[]; -extern Array > g_chatFactory; -extern Array > g_chatterFactory; -extern Array g_botNames; -extern Array g_replyFactory; -extern RandomSequenceOfUnique Random; +extern Array g_chatFactory; +extern Array> g_chatterFactory; +extern Array g_botNames; +extern Array g_replyFactory; extern WeaponSelect g_weaponSelect[NUM_WEAPONS + 1]; extern WeaponProperty g_weaponDefs[MAX_WEAPONS + 1]; extern Client g_clients[MAX_ENGINE_PLAYERS]; extern MenuText g_menus[BOT_MENU_TOTAL_MENUS]; -extern TaskItem g_taskFilters[]; +extern Task g_taskFilters[TASK_MAX]; extern Experience *g_experienceData; -extern edict_t *g_hostEntity; +extern edict_t *g_hostEntity; extern Library *g_gameLib; extern gamefuncs_t g_functionTable; -static inline bool IsNullString (const char *input) -{ - if (input == nullptr) +static inline bool isEmptyStr (const char *input) { + if (input == nullptr) { return true; - + } return *input == '\0'; } diff --git a/include/platform.h b/include/platform.h index 3a71f73..9aa128f 100644 --- a/include/platform.h +++ b/include/platform.h @@ -4,39 +4,39 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // #pragma once // detects the build platform -#if defined (__linux__) || defined (__debian__) || defined (__linux) - #define PLATFORM_LINUX 1 -#elif defined (__APPLE__) - #define PLATFORM_OSX 1 -#elif defined (_WIN32) - #define PLATFORM_WIN32 1 +#if defined(__linux__) + #define PLATFORM_LINUX +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(_WIN32) + #define PLATFORM_WIN32 #endif +// by default sse has everyone +#define PLATFORM_HAS_SSE2 + // detects the compiler -#if defined (_MSC_VER) - #define COMPILER_VISUALC _MSC_VER -#elif defined (__MINGW32_MAJOR_VERSION) - #define COMPILER_MINGW32 __MINGW32_MAJOR_VERSION +#if defined(_MSC_VER) + #define CXX_MSVC +#elif defined(__clang__) + #define CXX_CLANG #endif // configure export macros -#if defined (COMPILER_VISUALC) || defined (COMPILER_MINGW32) +#if defined(PLATFORM_WIN32) #define SHARED_LIBRARAY_EXPORT extern "C" __declspec (dllexport) -#elif defined (PLATFORM_LINUX) || defined (PLATFORM_OSX) - #define SHARED_LIBRARAY_EXPORT extern "C" __attribute__((visibility("default"))) +#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX) + #define SHARED_LIBRARAY_EXPORT extern "C" __attribute__ ((visibility ("default"))) #else #error "Can't configure export macros. Compiler unrecognized." #endif -// enable sse intrinsics -#define ENABLE_SSE_INTRINSICS 1 - // operating system specific macros, functions and typedefs #ifdef PLATFORM_WIN32 @@ -49,108 +49,46 @@ #define DLL_DETACHING (dwReason == DLL_PROCESS_DETACH) #define DLL_RETENTRY return TRUE - #if defined (COMPILER_VISUALC) + #if defined(CXX_MSVC) && !defined (_M_X64) #define DLL_GIVEFNPTRSTODLL extern "C" void STD_CALL - #elif defined (COMPILER_MINGW32) + #elif defined(CXX_CLANG) || defined (_M_X64) #define DLL_GIVEFNPTRSTODLL SHARED_LIBRARAY_EXPORT void STD_CALL #endif // specify export parameter - #if defined (COMPILER_VISUALC) && (COMPILER_VISUALC > 1000) - #pragma comment (linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1") - #pragma comment (linker, "/SECTION:.data,RW") + #if defined(CXX_MSVC) || defined (CXX_CLANG) + #if !defined (_M_X64) + #pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1") + #endif + #pragma comment(linker, "/SECTION:.data,RW") #endif -#elif defined (PLATFORM_LINUX) || defined (PLATFORM_OSX) - - #include +#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX) #include #include #include #include + #include - #include - #include - #include - #include #include + #include + #include + #include + #include - #define DLL_ENTRYPOINT __attribute__((destructor)) void _fini (void) + #define DLL_ENTRYPOINT __attribute__ ((destructor)) void _fini (void) #define DLL_DETACHING TRUE #define DLL_RETENTRY return - #define DLL_GIVEFNPTRSTODLL extern "C" void __attribute__((visibility("default"))) + #define DLL_GIVEFNPTRSTODLL extern "C" void __attribute__ ((visibility ("default"))) #define STD_CALL /* */ - #if defined (__ANDROID__) - #define PLATFORM_ANDROID 1 - #undef ENABLE_SSE_INTRINSICS + // android is a linux with a special cases + // @todo: sse should be working ok on x86 android? + #if defined(__ANDROID__) + #define PLATFORM_ANDROID + #undef PLATFORM_HAS_SSE2 #endif #else #error "Platform unrecognized." #endif - -// library wrapper -class Library -{ -private: - void *m_ptr; - -public: - - Library (const char *fileName) - { - m_ptr = nullptr; - - if (fileName == nullptr) - return; - - LoadLib (fileName); - } - - ~Library (void) - { - if (!IsLoaded ()) - return; - -#ifdef PLATFORM_WIN32 - FreeLibrary ((HMODULE) m_ptr); -#else - dlclose (m_ptr); -#endif - } - -public: - inline void *LoadLib (const char *fileName) - { -#ifdef PLATFORM_WIN32 - m_ptr = LoadLibrary (fileName); -#else - m_ptr = dlopen (fileName, RTLD_NOW); -#endif - - return m_ptr; - } - - template R GetFuncAddr (const char *function) - { - if (!IsLoaded ()) - return nullptr; - -#ifdef PLATFORM_WIN32 - return reinterpret_cast (GetProcAddress (static_cast (m_ptr), function)); -#else - return reinterpret_cast (dlsym (m_ptr, function)); -#endif - } - - template R GetHandle (void) - { - return (R) m_ptr; - } - - inline bool IsLoaded (void) const - { - return m_ptr != nullptr; - } -}; diff --git a/include/resource.h b/include/resource.h index ec0471c..8004b40 100644 --- a/include/resource.h +++ b/include/resource.h @@ -4,27 +4,30 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // #pragma once // general product information #define PRODUCT_NAME "Yet Another POD-Bot" -#define PRODUCT_VERSION "2.8" +#define PRODUCT_VERSION "2.9" #define PRODUCT_AUTHOR "YaPB Dev Team" -#define PRODUCT_URL "https://yapb.jeefo.net/" -#define PRODUCT_EMAIL "dmitry@jeefo.net" +#define PRODUCT_URL "https://yapb.ru/" +#define PRODUCT_EMAIL "d@entix.io" #define PRODUCT_LOGTAG "YAPB" -#define PRODUCT_DESCRIPTION PRODUCT_NAME " v" PRODUCT_VERSION " - The Counter-Strike Bot" -#define PRODUCT_COPYRIGHT "Copyright © 1999-2017, by " PRODUCT_AUTHOR +#define PRODUCT_END_YEAR "2018" +#define PRODUCT_DESCRIPTION PRODUCT_NAME " v" PRODUCT_VERSION " - The Counter-Strike Bot (" PRODUCT_COMMENTS ")" +#define PRODUCT_COPYRIGHT "Copyright © 1999-" PRODUCT_END_YEAR ", by " PRODUCT_AUTHOR #define PRODUCT_LEGAL "Half-Life, Counter-Strike, Counter-Strike: Condition Zero, Steam, Valve is a trademark of Valve Corporation" #define PRODUCT_ORIGINAL_NAME "yapb.dll" #define PRODUCT_INTERNAL_NAME "skybot" -#define PRODUCT_VERSION_DWORD_INTERNAL 2,8 -#define PRODUCT_VERSION_DWORD PRODUCT_VERSION_DWORD_INTERNAL,0 -#define PRODUCT_SUPPORT_VERSION "1.0 - CZ" -#define PRODUCT_COMMENTS "http://github.com/jeefo/yapb/" -#define PRODUCT_DATE __DATE__ #define PRODUCT_GIT_HASH "unspecified_hash" #define PRODUCT_GIT_COMMIT_AUTHOR "unspecified_author" +#define PRODUCT_GIT_COMMIT_ID 0000 +#define PRODUCT_VERSION_DWORD_INTERNAL 2, 9 +#define PRODUCT_VERSION_DWORD PRODUCT_VERSION_DWORD_INTERNAL, PRODUCT_GIT_COMMIT_ID +#define PRODUCT_SUPPORT_VERSION "Beta 6.6 - Condition Zero" +#define PRODUCT_COMMENTS "http://github.com/jeefo/yapb/" +#define PRODUCT_DATE __DATE__ + diff --git a/include/core.h b/include/yapb.h similarity index 51% rename from include/core.h rename to include/yapb.h index 2c3c5e7..6980f6a 100644 --- a/include/core.h +++ b/include/yapb.h @@ -4,33 +4,30 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // #pragma once #include -#include #include - +#include #include -using namespace Math; +using namespace cr::types; +using namespace cr::classes; #include #include #include -#include #include // defines bots tasks -enum TaskID -{ +enum TaskID { TASK_NORMAL, TASK_PAUSE, TASK_MOVETOPOSITION, TASK_FOLLOWUSER, - TASK_WAITFORGO, TASK_PICKUPITEM, TASK_CAMP, TASK_PLANTBOMB, @@ -51,12 +48,11 @@ enum TaskID }; // supported cs's -enum GameFlags -{ - GAME_CSTRIKE16 = (1 << 0), // Counter-Strike 1.6 and Above - GAME_XASH_ENGINE = (1 << 1), // Counter-Strike 1.6 under the xash engine (additional flag) - GAME_CZERO = (1 << 2), // Counter-Strike: Condition Zero - GAME_LEGACY = (1 << 3), // Counter-Strike 1.3-1.5 with/without Steam +enum GameFlags { + GAME_CSTRIKE16 = (1 << 0), // counter-strike 1.6 and above + GAME_XASH_ENGINE = (1 << 1), // counter-strike 1.6 under the xash engine (additional flag) + GAME_CZERO = (1 << 2), // counter-strike: condition zero + GAME_LEGACY = (1 << 3), // counter-strike 1.3-1.5 with/without steam GAME_MOBILITY = (1 << 4), // additional flag that bot is running on android (additional flag) GAME_OFFICIAL_CSBOT = (1 << 5), // additional flag that indicates official cs bots are in game GAME_METAMOD = (1 << 6), // game running under metamod @@ -67,8 +63,7 @@ enum GameFlags }; // bot menu ids -enum MenuId -{ +enum MenuId { BOT_MENU_INVALID = 0, BOT_MENU_MAIN, BOT_MENU_FEATURES, @@ -95,8 +90,7 @@ enum MenuId }; // log levels -enum LogLevel -{ +enum LogLevel { LL_DEFAULT = 1, // default log message LL_WARNING = 2, // warning log message LL_ERROR = 3, // error log message @@ -105,8 +99,7 @@ enum LogLevel }; // chat types id's -enum ChatType -{ +enum ChatType { CHAT_KILLING = 0, // id to kill chat array CHAT_DEAD, // id to dead chat array CHAT_BOMBPLANT, // id to bomb chat array @@ -118,22 +111,23 @@ enum ChatType }; // personalities defines -enum Personality -{ +enum Personality { PERSONALITY_NORMAL = 0, PERSONALITY_RUSHER, PERSONALITY_CAREFUL }; // bot difficulties -enum Difficulty -{ - +enum Difficulty : int { + DIFFICULTY_VERY_EASY, + DIFFICULTY_EASY, + DIFFICULTY_NORMAL, + DIFFICULTY_HARD, + DIFFICULTY_VERY_HARD }; // collision states -enum CollisionState -{ +enum CollisionState { COLLISION_NOTDECICED, COLLISION_PROBING, COLLISION_NOMOVE, @@ -144,25 +138,23 @@ enum CollisionState }; // counter-strike team id's -enum Team -{ - TERRORIST = 0, - CT, - SPECTATOR +enum Team { + TEAM_TERRORIST = 0, + TEAM_COUNTER, + TEAM_SPECTATOR, + TEAM_UNASSIGNED }; // client flags -enum ClientFlags -{ - CF_USED = (1 << 0), +enum ClientFlags { + CF_USED = (1 << 0), CF_ALIVE = (1 << 1), CF_ADMIN = (1 << 2), - CF_ICON = (1 << 3) + CF_ICON = (1 << 3) }; // bot create status -enum BotCreationResult -{ +enum BotCreationResult { BOT_RESULT_CREATED, BOT_RESULT_MAX_PLAYERS_REACHED, BOT_RESULT_NAV_ERROR, @@ -170,92 +162,88 @@ enum BotCreationResult }; // radio messages -enum RadioMessage_t -{ - Radio_CoverMe = 1, - Radio_YouTakePoint = 2, - Radio_HoldPosition = 3, - Radio_RegroupTeam = 4, - Radio_FollowMe = 5, - Radio_TakingFire = 6, - Radio_GoGoGo = 11, - Radio_Fallback = 12, - Radio_StickTogether = 13, - Radio_GetInPosition = 14, - Radio_StormTheFront = 15, - Radio_ReportTeam = 16, - Radio_Affirmative = 21, - Radio_EnemySpotted = 22, - Radio_NeedBackup = 23, - Radio_SectorClear = 24, - Radio_InPosition = 25, - Radio_ReportingIn = 26, - Radio_ShesGonnaBlow = 27, - Radio_Negative = 28, - Radio_EnemyDown = 29 +enum RadioMessage { + RADIO_COVER_ME = 1, + RADIO_YOU_TAKE_THE_POINT = 2, + RADIO_HOLD_THIS_POSITION = 3, + RADIO_REGROUP_TEAM = 4, + RADIO_FOLLOW_ME = 5, + RADIO_TAKING_FIRE = 6, + RADIO_GO_GO_GO = 11, + RADIO_TEAM_FALLBACK = 12, + RADIO_STICK_TOGETHER_TEAM = 13, + RADIO_GET_IN_POSITION = 14, + RADIO_STORM_THE_FRONT = 15, + RADIO_REPORT_TEAM = 16, + RADIO_AFFIRMATIVE = 21, + RADIO_ENEMY_SPOTTED = 22, + RADIO_NEED_BACKUP = 23, + RADIO_SECTOR_CLEAR = 24, + RADIO_IN_POSITION = 25, + RADIO_REPORTING_IN = 26, + RADIO_SHES_GONNA_BLOW = 27, + RADIO_NEGATIVE = 28, + RADIO_ENEMY_DOWN = 29 }; -// voice system (extending enum above, messages 30-39 is reserved) -enum ChatterMessage -{ - Chatter_SpotTheBomber = 40, - Chatter_FriendlyFire, - Chatter_DiePain, - Chatter_GotBlinded, - Chatter_GoingToPlantBomb, - Chatter_RescuingHostages, - Chatter_GoingToCamp, - Chatter_HearSomething, - Chatter_TeamKill, - Chatter_ReportingIn, - Chatter_GuardDroppedC4, - Chatter_Camp, - Chatter_PlantingC4, - Chatter_DefusingC4, - Chatter_InCombat, - Chatter_SeeksEnemy, - Chatter_Nothing, - Chatter_EnemyDown, - Chatter_UseHostage, - Chatter_FoundC4, - Chatter_WonTheRound, - Chatter_ScaredEmotion, - Chatter_HeardEnemy, - Chatter_SniperWarning, - Chatter_SniperKilled, - Chatter_VIPSpotted, - Chatter_GuardingVipSafety, - Chatter_GoingToGuardVIPSafety, - Chatter_QuicklyWonTheRound, - Chatter_OneEnemyLeft, - Chatter_TwoEnemiesLeft, - Chatter_ThreeEnemiesLeft, - Chatter_NoEnemiesLeft, - Chatter_FoundBombPlace, - Chatter_WhereIsTheBomb, - Chatter_DefendingBombSite, - Chatter_BarelyDefused, - Chatter_NiceshotCommander, - Chatter_NiceshotPall, - Chatter_GoingToGuardHostages, - Chatter_GoingToGuardDoppedBomb, - Chatter_OnMyWay, - Chatter_LeadOnSir, - Chatter_Pinned_Down, - Chatter_GottaFindTheBomb, - Chatter_You_Heard_The_Man, - Chatter_Lost_The_Commander, - Chatter_NewRound, - Chatter_CoverMe, - Chatter_BehindSmoke, - Chatter_BombSiteSecured, - Chatter_Total - +// chatter system (extending enum above, messages 30-39 is reserved) +enum ChatterMessage { + CHATTER_SPOT_THE_BOMBER = 40, + CHATTER_FRIENDLY_FIRE, + CHATTER_PAIN_DIED, + CHATTER_BLINDED, + CHATTER_GOING_TO_PLANT_BOMB, + CHATTER_RESCUING_HOSTAGES, + CHATTER_GOING_TO_CAMP, + CHATTER_HEARD_NOISE, + CHATTER_TEAM_ATTACK, + CHATTER_REPORTING_IN, + CHATTER_GUARDING_DROPPED_BOMB, + CHATTER_CAMP, + CHATTER_PLANTING_BOMB, + CHATTER_DEFUSING_BOMB, + CHATTER_IN_COMBAT, + CHATTER_SEEK_ENEMY, + CHATTER_NOTHING, + CHATTER_ENEMY_DOWN, + CHATTER_USING_HOSTAGES, + CHATTER_FOUND_BOMB, + CHATTER_WON_THE_ROUND, + CHATTER_SCARED_EMOTE, + CHATTER_HEARD_ENEMY, + CHATTER_SNIPER_WARNING, + CHATTER_SNIPER_KILLED, + CHATTER_VIP_SPOTTED, + CHATTER_GUARDING_VIP_SAFETY, + CHATTER_GOING_TO_GUARD_VIP_SAFETY, + CHATTER_QUICK_WON_ROUND, + CHATTER_ONE_ENEMY_LEFT, + CHATTER_TWO_ENEMIES_LEFT, + CHATTER_THREE_ENEMIES_LEFT, + CHATTER_NO_ENEMIES_LEFT, + CHATTER_FOUND_BOMB_PLACE, + CHATTER_WHERE_IS_THE_BOMB, + CHATTER_DEFENDING_BOMBSITE, + CHATTER_BARELY_DEFUSED, + CHATTER_NICESHOT_COMMANDER, + CHATTER_NICESHOT_PALL, + CHATTER_GOING_TO_GUARD_HOSTAGES, + CHATTER_GOING_TO_GUARD_DROPPED_BOMB, + CHATTER_ON_MY_WAY, + CHATTER_LEAD_ON_SIR, + CHATTER_PINNED_DOWN, + CHATTER_GOTTA_FIND_BOMB, + CHATTER_YOU_HEARD_THE_MAN, + CHATTER_LOST_COMMANDER, + CHATTER_NEW_ROUND, + CHATTER_COVER_ME, + CHATTER_BEHIND_SMOKE, + CHATTER_BOMB_SITE_SECURED, + CHATTER_MAX }; // counter-strike weapon id's -enum Weapon -{ +enum Weapon { WEAPON_P228 = 1, WEAPON_SHIELD = 2, WEAPON_SCOUT = 3, @@ -292,8 +280,7 @@ enum Weapon }; // buy counts -enum BuyState -{ +enum BuyState { BUYSTATE_PRIMARY_WEAPON = 0, BUYSTATE_ARMOR_VESTHELM, BUYSTATE_SECONDARY_WEAPON, @@ -304,8 +291,7 @@ enum BuyState }; // economics limits -enum EconomyLimit -{ +enum EconomyLimit { ECO_PRIMARY_GT = 0, ECO_SMG_GT_CT, ECO_SMG_GT_TE, @@ -320,8 +306,7 @@ enum EconomyLimit }; // defines for pickup items -enum PickupType -{ +enum PickupType { PICKUP_NONE, PICKUP_WEAPON, PICKUP_DROPPED_C4, @@ -333,48 +318,42 @@ enum PickupType }; // fight style type -enum FightStyle -{ +enum FightStyle { FIGHT_NONE, FIGHT_STRAFE, FIGHT_STAY }; // dodge type -enum StrafeDir -{ +enum StrafeDir { STRAFE_DIR_NONE, STRAFE_DIR_LEFT, STRAFE_DIR_RIGHT }; // reload state -enum ReloadState -{ +enum ReloadState { RELOAD_NONE = 0, // no reload state currently RELOAD_PRIMARY = 1, // primary weapon reload state RELOAD_SECONDARY = 2 // secondary weapon reload state }; // collision probes -enum CollisionProbe -{ +enum CollisionProbe { PROBE_JUMP = (1 << 0), // probe jump when colliding PROBE_DUCK = (1 << 1), // probe duck when colliding PROBE_STRAFE = (1 << 2) // probe strafing when colliding }; // vgui menus (since latest steam updates is obsolete, but left for old cs) -enum VGuiMenu -{ +enum VGuiMenu { VMS_TEAM = 2, // menu select team VMS_TF = 26, // terrorist select menu VMS_CT = 27 // ct select menu }; // lift usage states -enum LiftState -{ +enum LiftState { LIFT_NO_NEARBY = 0, LIFT_LOOKING_BUTTON_OUTSIDE, LIFT_WAITING_FOR, @@ -386,8 +365,7 @@ enum LiftState }; // wayponit auto-downloader -enum WaypointDownloadError -{ +enum WaypointDownloadError { WDE_SOCKET_ERROR, WDE_CONNECT_ERROR, WDE_NOTFOUND_ERROR, @@ -395,8 +373,7 @@ enum WaypointDownloadError }; // game start messages for counter-strike... -enum GameMessage -{ +enum GameMessage { GAME_MSG_NONE = 1, GAME_MSG_TEAM_SELECT = 2, GAME_MSG_CLASS_SELECT = 3, @@ -407,8 +384,7 @@ enum GameMessage }; // sensing states -enum SensingState -{ +enum SensingState { STATE_SEEING_ENEMY = (1 << 0), // seeing an enemy STATE_HEARING_ENEMY = (1 << 1), // hearing an enemy STATE_SUSPECT_ENEMY = (1 << 2), // suspect enemy behind obstacle @@ -419,8 +395,7 @@ enum SensingState }; // positions to aim at -enum AimPosition -{ +enum AimPosition { AIM_NAVPOINT = (1 << 0), // aim at nav point AIM_CAMP = (1 << 1), // aim at camp vector AIM_PREDICT_PATH = (1 << 2), // aim at predicted path @@ -432,34 +407,33 @@ enum AimPosition }; // famas/glock burst mode status + m4a1/usp silencer -enum BurstMode -{ - BM_ON = 1, +enum BurstMode { + BM_ON = 1, BM_OFF = 2 }; // visibility flags -enum Visibility -{ +enum Visibility { VISIBLE_HEAD = (1 << 1), VISIBLE_BODY = (1 << 2), VISIBLE_OTHER = (1 << 3) }; // defines map type -enum MapType -{ +enum MapFlags { MAP_AS = (1 << 0), MAP_CS = (1 << 1), MAP_DE = (1 << 2), MAP_ES = (1 << 3), MAP_KA = (1 << 4), - MAP_FY = (1 << 5) + MAP_FY = (1 << 5), + + // additional flags + MAP_HAS_DOORS = (1 << 6) }; // defines for waypoint flags field (32 bits are available) -enum WaypointFlag -{ +enum WaypointFlag { FLAG_LIFT = (1 << 1), // wait for lift to be down before approaching this waypoint FLAG_CROUCH = (1 << 2), // must crouch to reach this waypoint FLAG_CROSSING = (1 << 3), // a target waypoint @@ -475,92 +449,103 @@ enum WaypointFlag }; // defines for waypoint connection flags field (16 bits are available) -enum PathFlag -{ +enum PathFlag { PATHFLAG_JUMP = (1 << 0) // must jump for this connection }; // enum pathfind search type -enum SearchPathType -{ +enum SearchPathType { SEARCH_PATH_FASTEST = 0, SEARCH_PATH_SAFEST_FASTER, SEARCH_PATH_SAFEST }; // defines waypoint connection types -enum PathConnection -{ +enum PathConnection { CONNECTION_OUTGOING = 0, CONNECTION_INCOMING, CONNECTION_BOTHWAYS }; +// a* route state +enum RouteState { + ROUTE_OPEN, + ROUTE_CLOSED, + ROUTE_NEW +}; + // bot known file headers const char FH_WAYPOINT[] = "PODWAY!"; const char FH_EXPERIENCE[] = "PODEXP!"; const char FH_VISTABLE[] = "PODVIS!"; +const char FH_MATRIX[] = "PODMAT!"; -const int FV_WAYPOINT = 7; -const int FV_EXPERIENCE = 3; -const int FV_VISTABLE = 1; +constexpr int FV_WAYPOINT = 7; +constexpr int FV_EXPERIENCE = 3; +constexpr int FV_VISTABLE = 2; +constexpr int FV_MATRIX = 2; // some hardcoded desire defines used to override calculated ones -const float TASKPRI_NORMAL = 35.0f; -const float TASKPRI_PAUSE = 36.0f; -const float TASKPRI_CAMP = 37.0f; -const float TASKPRI_SPRAYLOGO = 38.0f; -const float TASKPRI_FOLLOWUSER = 39.0f; -const float TASKPRI_MOVETOPOSITION = 50.0f; -const float TASKPRI_DEFUSEBOMB = 89.0f; -const float TASKPRI_PLANTBOMB = 89.0f; -const float TASKPRI_ATTACK = 90.0f; -const float TASKPRI_SEEKCOVER = 91.0f; -const float TASKPRI_HIDE = 92.0f; -const float TASKPRI_THROWGRENADE = 99.0f; -const float TASKPRI_DOUBLEJUMP = 99.0f; -const float TASKPRI_BLINDED = 100.0f; -const float TASKPRI_SHOOTBREAKABLE = 100.0f; -const float TASKPRI_ESCAPEFROMBOMB = 100.0f; +constexpr float TASKPRI_NORMAL = 35.0f; +constexpr float TASKPRI_PAUSE = 36.0f; +constexpr float TASKPRI_CAMP = 37.0f; +constexpr float TASKPRI_SPRAYLOGO = 38.0f; +constexpr float TASKPRI_FOLLOWUSER = 39.0f; +constexpr float TASKPRI_MOVETOPOSITION = 50.0f; +constexpr float TASKPRI_DEFUSEBOMB = 89.0f; +constexpr float TASKPRI_PLANTBOMB = 89.0f; +constexpr float TASKPRI_ATTACK = 90.0f; +constexpr float TASKPRI_SEEKCOVER = 91.0f; +constexpr float TASKPRI_HIDE = 92.0f; +constexpr float TASKPRI_THROWGRENADE = 99.0f; +constexpr float TASKPRI_DOUBLEJUMP = 99.0f; +constexpr float TASKPRI_BLINDED = 100.0f; +constexpr float TASKPRI_SHOOTBREAKABLE = 100.0f; +constexpr float TASKPRI_ESCAPEFROMBOMB = 100.0f; -const float MAX_GRENADE_TIMER = 2.34f; -const float MAX_SPRAY_DISTANCE = 250.0f; -const float MAX_SPRAY_DISTANCE_X2 = MAX_SPRAY_DISTANCE * 2; +constexpr float MAX_GRENADE_TIMER = 2.15f; +constexpr float MAX_SPRAY_DISTANCE = 260.0f; +constexpr float MAX_SPRAY_DISTANCE_X2 = MAX_SPRAY_DISTANCE * 2; +constexpr float MAX_CHATTER_REPEAT = 99.0f; -const int MAX_HOSTAGES = 8; -const int MAX_PATH_INDEX = 8; -const int MAX_DAMAGE_VALUE = 2040; -const int MAX_GOAL_VALUE = 2040; -const int MAX_KILL_HISTORY = 16; -const int MAX_WAYPOINTS = 1024; -const int MAX_WEAPONS = 32; -const int NUM_WEAPONS = 26; -const int MAX_COLLIDE_MOVES = 3; -const int MAX_ENGINE_PLAYERS = 32; // we can have 64 players with xash -const int MAX_PRINT_BUFFER = 1024; +constexpr int MAX_PATH_INDEX = 8; +constexpr int MAX_DAMAGE_VALUE = 2040; +constexpr int MAX_GOAL_VALUE = 2040; +constexpr int MAX_KILL_HISTORY = 16; +constexpr int MAX_WAYPOINTS = 1024; +constexpr int MAX_ROUTE_LENGTH = MAX_WAYPOINTS / 2; +constexpr int MAX_WEAPONS = 32; +constexpr int NUM_WEAPONS = 26; +constexpr int MAX_COLLIDE_MOVES = 3; +constexpr int MAX_ENGINE_PLAYERS = 32; +constexpr int MAX_PRINT_BUFFER = 1024; +constexpr int MAX_TEAM_COUNT = 2; +constexpr int INVALID_WAYPOINT_INDEX = -1; + +constexpr int MAX_WAYPOINT_BUCKET_SIZE = static_cast (MAX_WAYPOINTS * 0.65); +constexpr int MAX_WAYPOINT_BUCKET_MAX = MAX_WAYPOINTS * 8 / MAX_WAYPOINT_BUCKET_SIZE + 1; +constexpr int MAX_WAYPOINT_BUCKET_WPTS = MAX_WAYPOINT_BUCKET_SIZE / MAX_WAYPOINT_BUCKET_MAX; // weapon masks -const int WEAPON_PRIMARY = ((1 << WEAPON_XM1014) | (1 < keywords; - Array replies; - Array usedReplies; +struct KeywordFactory { + StringArray keywords; + StringArray replies; + StringArray usedReplies; }; // tasks definition -struct TaskItem -{ +struct Task { TaskID id; // major task/action carried out float desire; // desire (filled in) for this task int data; // additional data (waypoint index) @@ -569,23 +554,20 @@ struct TaskItem }; // botname structure definition -struct BotName -{ +struct BotName { String steamId; String name; int usedBy; }; // voice config structure definition -struct ChatterItem -{ +struct ChatterItem { String name; float repeat; float duration; }; -struct WeaponSelect -{ +struct WeaponSelect { int id; // the weapon id value const char *weaponName; // name of the weapon when selecting it const char *modelName; // model name to separate cs weapons @@ -602,16 +584,14 @@ struct WeaponSelect }; // struct for menus -struct MenuText -{ +struct MenuText { MenuId id; // actual menu id int slots; // together bits for valid keys String text; // ptr to actual string }; // array of clients struct -struct Client -{ +struct Client { MenuId menu; // id to opened bot menu edict_t *ent; // pointer to actual edict Vector origin; // position in the world @@ -629,8 +609,7 @@ struct Client }; // experience data hold in memory while playing -struct Experience -{ +struct Experience { uint16 team0Damage; uint16 team1Damage; int16 team0DangerIndex; @@ -640,8 +619,7 @@ struct Experience }; // experience data when saving/loading -struct ExperienceSave -{ +struct ExperienceSave { uint8 team0Damage; uint8 team1Damage; int8 team0Value; @@ -649,9 +627,8 @@ struct ExperienceSave }; // bot creation tab -struct CreateQueue -{ - bool console; +struct CreateQueue { + bool manual; int difficulty; int team; int member; @@ -660,8 +637,7 @@ struct CreateQueue }; // weapon properties structure -struct WeaponProperty -{ +struct WeaponProperty { char className[64]; int ammo1; // ammo index for primary ammo int ammo1Max; // max primary ammo @@ -672,19 +648,17 @@ struct WeaponProperty }; // define chatting collection structure -struct ChatCollection -{ +struct ChatCollection { int chatProbability; float chatDelay; float timeNextChat; int entityIndex; - char sayText[512]; - Array lastUsedSentences; + String sayText; + StringArray lastUsedSentences; }; // general waypoint header information structure -struct WaypointHeader -{ +struct WaypointHeader { char header[8]; int32 fileVersion; int32 pointNumber; @@ -693,16 +667,14 @@ struct WaypointHeader }; // general experience & vistable header information structure -struct ExtensionHeader -{ +struct ExtensionHeader { char header[8]; int32 fileVersion; int32 pointNumber; }; // define general waypoint structure -struct Path -{ +struct Path { int32 pathNumber; int32 flags; Vector origin; @@ -718,12 +690,39 @@ struct Path Vector connectionVelocity[MAX_PATH_INDEX]; int32 distances[MAX_PATH_INDEX]; - struct Vis { uint16 stand, crouch; } vis; + struct Vis { + uint16 stand, crouch; + } vis; +}; + +// this structure links waypoints returned from pathfinder +class PathWalk : public IntArray { +public: + PathWalk (void) { + reserve (MAX_ROUTE_LENGTH); + clear (); + } + + ~PathWalk (void) = default; +public: + inline int &next (void) { + return at (1); + } + + inline int &first (void) { + return at (0); + } + + inline bool hasNext (void) const { + if (empty ()) { + return false; + } + return length () > 1; + } }; // main bot class -class Bot -{ +class Bot { friend class BotManager; private: @@ -737,20 +736,20 @@ private: bool m_isLeader; // bot is leader of his team bool m_checkTerrain; // check for terrain bool m_moveToC4; // ct is moving to bomb + bool m_grenadeRequested; // bot requested change to grenade float m_prevTime; // time previously checked movement speed float m_prevSpeed; // speed some frames before Vector m_prevOrigin; // origin some frames before int m_messageQueue[32]; // stack for messages - char m_tempStrings[160]; // space for strings (say text...) + String m_tempStrings; // space for strings (say text...) int m_radioSelect; // radio entry - float m_headedTime; + float m_headedTime; edict_t *m_avoid; // avoid players on our way float m_avoidTime; // time to avoid players around - float m_blindRecognizeTime; // time to recognize enemy float m_itemCheckTime; // time next search for items needs to be done PickupType m_pickupType; // type of entity which needs to be used/picked up Vector m_breakableOrigin; // origin of breakable @@ -779,8 +778,9 @@ private: unsigned int m_collStateIndex; // index into collide moves CollisionState m_collisionState; // collision State - PathNode *m_navNode; // pointer to current node from path - PathNode *m_navNodeStart; // pointer to start of path finding nodes + BinaryHeap m_routeQue; + Array m_routes; // pointer + PathWalk m_path; // pointer to current node from path Path *m_currentPath; // pointer to the current path waypoint SearchPathType m_pathType; // which pathfinder to use @@ -790,8 +790,9 @@ private: int m_travelStartIndex; // travel start index to double jump action int m_prevWptIndex[5]; // previous waypoint indices from waypoint find int m_waypointFlags; // current waypoint flags + int m_loosedBombWptIndex; // nearest to loosed bomb waypoint - int m_plantedBombWptIndex;// nearest to planted bomb waypoint + int m_plantedBombWptIndex; // nearest to planted bomb waypoint uint16 m_currentTravelFlags; // connection flags like jumping bool m_jumpFinished; // has bot finished jumping @@ -815,15 +816,17 @@ private: float m_followWaitTime; // wait to follow time edict_t *m_targetEntity; // the entity that the bot is trying to reach - edict_t *m_hostages[MAX_HOSTAGES]; // pointer to used hostage entities + Array m_hostages; // pointer to used hostage entities bool m_moveToGoal; // bot currently moving to goal?? bool m_isStuck; // bot is stuck bool m_isReloading; // bot is reloading a gun bool m_forceRadio; // should bot use radio anyway? + int m_oldButtons; // our old buttons int m_reloadState; // current reload state int m_voicePitch; // bot voice pitch + int m_rechoiceGoalCount; // multiple failed goals? bool m_duckDefuse; // should or not bot duck to defuse bomb float m_duckDefuseCheckTime; // time to check for ducking for defuse @@ -835,34 +838,35 @@ private: float m_zoomCheckTime; // time to check zoom again float m_shieldCheckTime; // time to check shiled drawing again float m_grenadeCheckTime; // time to check grenade usage + float m_sniperStopTime; // bot switched to other weapon? float m_lastEquipTime; // last time we equipped in buyzone bool m_checkKnifeSwitch; // is time to check switch to knife action bool m_checkWeaponSwitch; // is time to check weapon switch bool m_isUsingGrenade; // bot currently using grenade?? + bool m_bombSearchOverridden; // use normal waypoint if applicable when near the bomb StrafeDir m_combatStrafeDir; // direction to strafe FightStyle m_fightStyle; // combat style to use float m_lastFightStyleCheck; // time checked style float m_strafeSetTime; // time strafe direction was set - float m_maxThrowTimer; // time that completes the throw task float m_timeCamping; // time to camp int m_campDirection; // camp Facing direction float m_nextCampDirTime; // time next camp direction change int m_campButtons; // buttons to press while camping - int m_doorOpenAttempt; // attempt's to open the door + int m_tryOpenDoor; // attempt's to open the door int m_liftState; // state of lift handling float m_duckTime; // time to duck float m_jumpTime; // time last jump happened - float m_chatterTimes[Chatter_Total]; // voice command timers + float m_chatterTimes[CHATTER_MAX]; // voice command timers float m_soundUpdateTime; // time to update the sound float m_heardSoundTime; // last time noise is heard float m_buttonPushTime; // time to push the button float m_liftUsageTime; // time to use lift - int m_pingOffset[2]; + int m_pingOffset[2]; int m_ping[3]; // bots pings in scoreboard Vector m_liftTravelPos; // lift travel position @@ -872,189 +876,204 @@ private: float m_lookYawVel; // look yaw velocity float m_lookPitchVel; // look pitch velocity float m_lookUpdateTime; // lookangles update time + float m_aimErrorTime; // time to update error vector Vector m_randomizedIdealAngles; // angle wanted with noise Vector m_angularDeviation; // angular deviation from current to ideal angles Vector m_aimSpeed; // aim speed calculated + Vector m_aimLastError; // last calculated aim error float m_randomizeAnglesTime; // time last randomized location float m_playerTargetTime; // time last targeting - void InstantChatterMessage (int type); - void BotAI (void); - void CheckSpawnTimeConditions (void); - void PurchaseWeapons (void); + void instantChatter (int type); + void ai (void); + void checkSpawnConditions (void); + void buyStuff (void); - bool IsMorePowerfulWeaponCanBeBought (void); - int PickBestFromRandom (int *random, int count, int moneySave); + bool canReplaceWeapon (void); + int pickBestWeapon (int *vec, int count, int moneySave); - bool CanDuckUnder (const Vector &normal); - bool CanJumpUp (const Vector &normal); - bool FinishCanJumpUp (const Vector &normal); - bool CantMoveForward (const Vector &normal, TraceResult *tr); - - // split RunTask into RunTask_* functions - void RunTask_Normal (void); - void RunTask_Spray (void); - void RunTask_HuntEnemy (void); - void RunTask_SeekCover (void); - void RunTask_Attack (void); - void RunTask_Pause (void); - void RunTask_Blinded (void); - void RunTask_Camp (void); - void RunTask_Hide (void); - void RunTask_MoveToPos (void); - void RunTask_PlantBomb (void); - void RunTask_DefuseBomb (void); - void RunTask_FollowUser (void); - void RunTask_Throw_HE (void); - void RunTask_Throw_FL (void); - void RunTask_Throw_SG (void); - void RunTask_DoubleJump (void); - void RunTask_EscapeFromBomb (void); - void RunTask_PickupItem (void); - void RunTask_ShootBreakable (void); + bool canDuckUnder (const Vector &normal); + bool canJumpUp (const Vector &normal); + bool doneCanJumpUp (const Vector &normal); + bool cantMoveForward (const Vector &normal, TraceResult *tr); #ifdef DEAD_CODE - bool CanStrafeRight (TraceResult *tr); - bool CanStrafeLeft (TraceResult *tr); + bool canStrafeLeft (TraceResult *tr); + bool canStrafeRight (TraceResult *tr); - bool IsBlockedLeft (void); - bool IsBlockedRight (void); + bool isBlockedLeft (void); + bool isBlockedRight (void); - void ChangePitch (float speed); - void ChangeYaw (float speed); + void changePitch (float speed); + void changeYaw (float speed); #endif - void CheckMessageQueue (void); - void CheckRadioCommands (void); - void CheckReload (void); - void AvoidGrenades (void); - void CheckGrenadeThrow (void); - void CheckBurstMode (float distance); + void checkMsgQueue (void); + void checkRadioQueue (void); + void checkReload (void); + int getMaxClip (int id); + void avoidGrenades (void); + void checkGrenadesThrow (void); + void checkBurstMode (float distance); + void checkSilencer (void); + void updateAimDir (void); + bool checkWallOnLeft (void); + bool checkWallOnRight (void); + int getBombPoint (void); - void CheckSilencer (void); - bool CheckWallOnLeft (void); - bool CheckWallOnRight (void); - void ChooseAimDirection (void); - int ChooseBombWaypoint (void); + bool processNavigation (void); + bool isEnemyThreat (void); + void processLookAngles (void); + void processBodyAngles (void); + void updateLookAnglesNewbie (const Vector &direction, const float delta); + void setIdealReactionTimers (bool actual = false); + bool isWeaponRestricted (int weaponIndex); + bool isWeaponRestrictedAMX (int weaponIndex); - bool DoWaypointNav (void); - bool EnemyIsThreat (void); - void UpdateLookAngles (void); - void UpdateBodyAngles (void); - void UpdateLookAnglesLowSkill (const Vector &direction, const float delta); - void SetIdealReactionTimes (bool actual = false); - bool IsRestricted (int weaponIndex); - bool IsRestrictedAMX (int weaponIndex); + bool isInViewCone (const Vector &origin); + void processHearing (void); + bool checkBodyParts (edict_t *target, Vector *origin, uint8 *bodyPart); + bool seesEnemy (edict_t *player, bool ignoreFOV = false); - bool IsInViewCone (const Vector &origin); - void ReactOnSound (void); - bool CheckVisibility (edict_t *target, Vector *origin, uint8 *bodyPart); - bool IsEnemyViewable (edict_t *player); + edict_t *getNearestButton (const char *className); + edict_t *lookupBreakable (void); + int getCoverPoint (float maxDistance); + int getDefendPoint (const Vector &origin); + int searchGoal (void); + int getGoalProcess (int tactic, IntArray *defensive, IntArray *offsensive); + void filterGoals (const IntArray &goals, int *result); + void processPickups (void); + void checkTerrain (float movedDistance, const Vector &dirNormal); + bool doPlayerAvoidance (const Vector &normal); - edict_t *FindNearestButton (const char *className); - edict_t *FindBreakable (void); - int FindCoverWaypoint (float maxDistance); - int FindDefendWaypoint (const Vector &origin); - int FindGoal (void); - int FinishFindGoal (int tactic, Array *defensive, Array *offsensive); - void FilterGoals (const Array &goals, int *result); - void FindItem (void); - void CheckTerrain (float movedDistance, const Vector &dirNormal); - void CheckCloseAvoidance (const Vector &dirNormal); + void getCampDir (Vector *dest); + void collectGoalExperience (int damage, int team); + void collectDataExperience (edict_t *attacker, int damage); + int getMsgQueue (void); + bool hasActiveGoal (void); + bool advanceMovement (void); + float isInFOV (const Vector &dest); - void GetCampDirection (Vector *dest); - void CollectGoalExperience (int damage, int team); - void CollectExperienceData (edict_t *attacker, int damage); - int GetMessageQueue (void); - bool GoalIsValid (void); - bool HeadTowardWaypoint (void); - int InFieldOfView (const Vector &dest); + bool isBombDefusing (const Vector &bombOrigin); + bool isOccupiedPoint (int index); - bool IsBombDefusing (const Vector &bombOrigin); - bool IsPointOccupied (int index); + inline bool isOnLadder (void) { + return pev->movetype == MOVETYPE_FLY; + } - inline bool IsOnLadder (void) { return pev->movetype == MOVETYPE_FLY; } - inline bool IsOnFloor (void) { return (pev->flags & (FL_ONGROUND | FL_PARTIALGROUND)) != 0; } - inline bool IsInWater (void) { return pev->waterlevel >= 2; } + inline bool isOnFloor (void) { + return (pev->flags & (FL_ONGROUND | FL_PARTIALGROUND)) != 0; + } - float GetWalkSpeed (void); - - bool ItemIsVisible (const Vector &dest, const char *itemName); - bool LastEnemyShootable (void); - bool IsBehindSmokeClouds (edict_t *ent); - void RunTask (void); + inline bool isInWater (void) { + return pev->waterlevel >= 2; + } + float getShiftSpeed (void); - bool IsShootableBreakable (edict_t *ent); - bool RateGroundWeapon (edict_t *ent); - bool ReactOnEnemy (void); - void ResetCollideState (void); - void IgnoreCollisionShortly (void); - void SetConditions (void); - void SetConditionsOverride (void); - void UpdateEmotions (void); - void SetStrafeSpeed (const Vector &moveDir, float strafeSpeed); - void StartGame (void); - void TaskComplete (void); - bool GetBestNextWaypoint (void); - int GetBestWeaponCarried (void); - int GetBestSecondaryWeaponCarried (void); + bool seesItem (const Vector &dest, const char *itemName); + bool lastEnemyShootable (void); + void processTasks (void); - void RunPlayerMovement (void); - uint8 ThrottledMsec (void); - void GetValidWaypoint (void); - int ChangeWptIndex (int waypointIndex); - bool IsDeadlyDrop (const Vector &to); - bool OutOfBombTimer (void); + // split big task list into separate functions + void normal_ (void); + void spraypaint_ (void); + void huntEnemy_ (void); + void seekCover_ (void); + void attackEnemy_ (void); + void pause_ (void); + void blind_ (void); + void camp_ (void); + void hide_ (void); + void moveToPos_ (void); + void plantBomb_ (void); + void bombDefuse_ (void); + void followUser_ (void); + void throwExplosive_ (void); + void throwFlashbang_ (void); + void throwSmoke_ (void); + void doublejump_ (void); + void escapeFromBomb_ (void); + void pickupItem_ (void); + void shootBreakable_ (void); - Vector CheckThrow (const Vector &start, const Vector &stop); - Vector CheckToss (const Vector &start, const Vector &stop); - Vector CheckBombAudible (void); + bool isShootableBreakable (edict_t *ent); + bool rateGroundWeapon (edict_t *ent); + bool reactOnEnemy (void); + void resetCollision (void); + void ignoreCollision (void); + void setConditions (void); + void overrideConditions (void); + void updateEmotions (void); + void setStrafeSpeed (const Vector &moveDir, float strafeSpeed); + void processTeamJoin (void); + void completeTask (void); + bool getNextBestPoint (void); - const Vector &GetAimPosition (void); - float GetZOffset (float distance); + int bestPrimaryCarried (void); + int bestSecondaryCarried (void); + int bestGrenadeCarried (void); + int bestWeaponCarried (void); + bool hasAnyWeapons (void); - int CheckGrenades (void); - void CommandTeam (void); - void AttachToUser (void); - void CombatFight (void); - bool IsWeaponBadInDistance (int weaponIndex, float distance); - bool DoFirePause (float distance); - bool LookupEnemy (void); - bool IsEnemyHiddenByRendering (edict_t *enemy); - void FireWeapon (void); - void FinishWeaponSelection (float distance, int index, int id, int choosen); - void FocusEnemy (void); + void runMovement (void); + uint8 computeMsec (void); + void getValidPoint (void); - void SelectBestWeapon (void); - void SelectPistol (void); - bool IsFriendInLineOfFire (float distance); - bool IsGroupOfEnemies (const Vector &location, int numEnemies = 1, int radius = 256); + int changePointIndex (int index); + int getNearestPoint (void); + bool isDeadlyMove (const Vector &to); + bool isOutOfBombTimer (void); - bool IsShootableThruObstacle (const Vector &dest); - bool IsShootableThruObstacleEx (const Vector &dest); + edict_t *correctGrenadeVelocity (const char *model); + Vector calcThrow (const Vector &spot1, const Vector &spot2); + Vector calcToss (const Vector &spot1, const Vector &spot2); + Vector isBombAudible (void); - int GetNearbyEnemiesNearPosition (const Vector &origin, float radius); - int GetNearbyFriendsNearPosition (const Vector &origin, float radius); + const Vector &getEnemyBodyOffset (void); + Vector getBodyOffserError (float distance); + float getEnemyBodyOffsetCorrection (float distance); - void SelectWeaponByName (const char *name); - void SelectWeaponbyNumber (int num); - int GetHighestWeapon (void); + void processTeamCommands (void); + void decideFollowUser (void); + void attackMovement (void); + bool isWeaponBadAtDistance (int weaponIndex, float distance); + bool throttleFiring (float distance); + bool lookupEnemies (void); + bool isEnemyHidden (edict_t *enemy); + void fireWeapons (void); + void selectWeapons (float distance, int index, int id, int choosen); + void focusEnemy (void); - bool IsEnemyProtectedByShield (edict_t *enemy); - bool ParseChat (char *reply); - bool RepliesToPlayer (void); - float GetBombTimeleft (void); - float GetEstimatedReachTime (void); + void selectBestWeapon (void); + void selectSecondary (void); + bool isFriendInLineOfFire (float distance); + bool isGroupOfEnemies (const Vector &location, int numEnemies = 1, float radius = 256.0f); - int GetCampAimingWaypoint (void); - int GetAimingWaypoint (const Vector &to); + bool isPenetrableObstacle (const Vector &dest); + bool isPenetrableObstacle2 (const Vector &dest); - void FindShortestPath (int srcIndex, int destIndex); - void FindPath (int srcIndex, int destIndex, SearchPathType pathType = SEARCH_PATH_FASTEST); - void DebugMsg (const char *format, ...); - void PeriodicThink (void); + int numEnemiesNear (const Vector &origin, float radius); + int numFriendsNear (const Vector &origin, float radius); + + void selectWeaponByName (const char *name); + void selectWeaponById (int num); + + bool isEnemyBehindShield (edict_t *enemy); + bool processChatKeywords (char *reply); + bool isReplyingToChat (void); + float getBombTimeleft (void); + float getReachTime (void); + + int searchCampDir (void); + int searchAimingPoint (const Vector &to); + + void searchShortestPath (int srcIndex, int destIndex); + void searchPath (int srcIndex, int destIndex, SearchPathType pathType = SEARCH_PATH_FASTEST); + void clearRoute (void); + void sayDebug (const char *format, ...); + void frame (void); public: entvars_t *pev; @@ -1086,7 +1105,8 @@ public: int m_lastVoteKick; // last index int m_voteMap; // number of map to vote for int m_logotypeIndex; // index for logotype - + + bool m_ignoreBuyDelay; // when reaching buyzone in the middle of the round don't do pauses bool m_inBombZone; // bot in the bomb zone or not int m_buyState; // current Count in Buying float m_nextBuyTime; // next buy time @@ -1101,7 +1121,7 @@ public: bool m_jumpReady; // is double jump ready bool m_canChooseAimDirection; // can choose aiming direction float m_turnAwayFromFlashbang; // bot turned away from flashbang - + float m_blindTime; // time when bot is blinded float m_blindMoveSpeed; // mad speeds when bot is blind float m_blindSidemoveSpeed; // mad side move speeds when bot is blind @@ -1123,7 +1143,6 @@ public: int m_actMessageIndex; // current processed message int m_pushMessageIndex; // offset for next pushed message - int m_goalFailed; // if bot can't reach several times in a row int m_prevGoalIndex; // holds destination goal waypoint int m_chosenGoalIndex; // used for experience, same as above float m_goalValue; // ranking value for this waypoint @@ -1132,7 +1151,6 @@ public: Vector m_destOrigin; // origin of move destination Vector m_position; // position to move to in move to position task Vector m_doubleJumpOrigin; // origin of double jump - Vector m_lastBombPosition; // origin of last remembered bomb position float m_viewDistance; // current view distance float m_maxViewDistance; // maximum view distance @@ -1141,6 +1159,7 @@ public: BurstMode m_weaponBurstMode; // bot using burst mode? (famas/glock18, but also silencer mode) edict_t *m_enemy; // pointer to enemy entity + float m_retreatTime; // time to retreat? float m_enemyUpdateTime; // time to check for new enemies float m_enemyReachableTimer; // time to recheck if enemy reachable float m_enemyIgnoreTimer; // ignore enemy for some time @@ -1164,197 +1183,228 @@ public: int m_currentWeapon; // one current weapon for each bot int m_ammoInClip[MAX_WEAPONS]; // ammo in clip for each weapons int m_ammo[MAX_AMMO_SLOTS]; // total ammo amounts - - // a little optimization - int m_team; - - Array m_tasks; + int m_team; // bot team + Array m_tasks; Bot (edict_t *bot, int difficulty, int personality, int team, int member, const String &steamId); - ~Bot (void); + ~Bot (void); - int GetAmmo (void); - inline int GetAmmoInClip (void) { return m_ammoInClip[m_currentWeapon]; } + int ammo (void); + inline int ammoClip (void) { + return m_ammoInClip[m_currentWeapon]; + } - inline edict_t *GetEntity (void) { return pev->pContainingEntity; }; - int GetIndex (void); + inline edict_t *ent (void) { + return pev->pContainingEntity; + }; + int index (void); - inline Vector Center (void) { return (pev->absmax + pev->absmin) * 0.5; }; - inline Vector EyePosition (void) { return pev->origin + pev->view_ofs; }; + inline Vector centerPos (void) { + return (pev->absmax + pev->absmin) * 0.5; + }; + inline Vector eyePos (void) { + return pev->origin + pev->view_ofs; + }; - float GetThinkInterval (void); + float calcThinkInterval (void); // the main function that decides intervals of running bot ai - void Think (void); + void framePeriodic (void); /// the things that can be executed while skipping frames - void ThinkFrame (void); + void frameThink (void); - void GotBlind (int alpha); - void GetDamage (edict_t *inflictor, int damage, int armor, int bits); + void processBlind (int alpha); + void processDamage (edict_t *inflictor, int damage, int armor, int bits); - void DisplayDebugOverlay (void); - void NewRound (void); - void EquipInBuyzone (int buyState); - void PushMessageQueue (int message); - void PrepareChatMessage (char *text); - bool FindWaypoint (void); - bool EntityIsVisible (const Vector &dest, bool fromBody = false); + void showDebugOverlay (void); + void newRound (void); + void processBuyzoneEntering (int buyState); + void pushMsgQueue (int message); + void prepareChatMessage (char *text); + bool searchOptimalPoint (void); + bool seesEntity (const Vector &dest, bool fromBody = false); - void EnableChatterIcon (bool show); - void DeleteSearchNodes (void); - void VerifyBreakable (edict_t *touch); - void AvoidPlayersOnTheWay (edict_t *touch); + void showChaterIcon (bool show); + void clearSearchNodes (void); + void processBreakables (edict_t *touch); + void avoidIncomingPlayers (edict_t *touch); - void PushTask (TaskID id, float desire, int data, float time, bool canContinue); - void RemoveCertainTask (TaskID id); - void ApplyTaskFilters (void); - void ResetTasks (void); + void startTask (TaskID id, float desire, int data, float time, bool canContinue); + void clearTask (TaskID id); + void filterTasks (void); + void clearTasks (void); - TaskItem *GetTask (void); - inline TaskID GetTaskId (void) { return GetTask ()->id; }; + Task *task (void); - void DiscardWeaponForUser (edict_t *user, bool discardC4); - void ReleaseUsedName (void); + inline TaskID taskId (void) { + return task ()->id; + } - void SayText (const char *text); - void TeamSayText (const char *text); + void dropWeaponForUser (edict_t *user, bool discardC4); + void clearUsedName (void); - void ChatMessage (int type, bool isTeamSay = false); - void RadioMessage (int message); - void ChatterMessage (int message); - void HandleChatterMessage (const char *sz); - void TryHeadTowardRadioEntity (void); + void say (const char *text); + void sayTeam (const char *text); - void Kill (void); - void Kick (bool keepQuota = false); + void pushChatMessage (int type, bool isTeamSay = false); + void pushRadioMessage (int message); + void pushChatterMessage (int message); + void processChatterMessage (const char *sz); + void tryHeadTowardRadioMessage (void); - void ResetDoubleJumpState (void); - void StartDoubleJump (edict_t *ent); + void kill (void); + void kick (void); - int FindPlantedBomb(void); + void resetDoubleJump (void); + void startDoubleJump (edict_t *ent); - bool HasHostage (void); - bool UsesRifle (void); - bool UsesPistol (void); - bool UsesSniper (void); - bool UsesSubmachineGun (void); - bool UsesZoomableRifle (void); - bool UsesBadPrimary (void); - bool UsesCampGun (void); - bool HasPrimaryWeapon (void); - bool HasSecondaryWeapon(void); - bool HasShield (void); - bool IsShieldDrawn (void); + int locatePlantedC4 (void); + + bool hasHostage (void); + bool usesRifle (void); + bool usesPistol (void); + bool usesSniper (void); + bool usesSubmachine (void); + bool usesZoomableRifle (void); + bool usesBadWeapon (void); + bool usesCampGun (void); + bool hasPrimaryWeapon (void); + bool hasSecondaryWeapon (void); + bool hasShield (void); + bool isShieldDrawn (void); }; // manager class -class BotManager : public Singleton -{ +class BotManager : public Singleton { private: Array m_creationTab; // bot creation tab Bot *m_bots[MAX_ENGINE_PLAYERS]; // all available bots - float m_maintainTime; // time to maintain bot creation + float m_maintainTime; // time to maintain bot creation float m_quotaMaintainTime; // time to maintain bot quota float m_grenadeUpdateTime; // time to update active grenades + float m_entityUpdateTime; // time to update intresting entities int m_lastWinner; // the team who won previous round - int m_balanceCount; // limit of bots to add - bool m_leaderChoosen[SPECTATOR]; // is team leader choose theese round - bool m_economicsGood[SPECTATOR]; // is team able to buy anything + bool m_leaderChoosen[MAX_TEAM_COUNT]; // is team leader choose theese round + bool m_economicsGood[MAX_TEAM_COUNT]; // is team able to buy anything bool m_deathMsgSent; // for fake ping - Array m_activeGrenades; // holds currently active grenades in the map - Array m_trackedPlayers; // holds array of connected players, and waits the player joins team - + Array m_activeGrenades; // holds currently active grenades on the map + Array m_intrestingEntities; // holds currently intresting entities on the map edict_t *m_killerEntity; // killer entity for bots protected: - BotCreationResult CreateBot (const String &name, int difficulty, int personality, int team, int member, bool isConsoleCmd); + BotCreationResult create (const String &name, int difficulty, int personality, int team, int member); public: BotManager (void); - ~BotManager (void); + ~BotManager (void); - bool IsEcoValid (int team) { return m_economicsGood[team]; } - bool IsTeamStacked (int team); - - int GetLastWinner (void) const { return m_lastWinner; } - void SetLastWinner (int winner) { m_lastWinner = winner; } - - int GetIndex (edict_t *ent); - Bot *GetBot (int index); - Bot *GetBot (edict_t *ent); - Bot *GetAliveBot (void); - Bot *GetHighestFragsBot (int team); - - int GetHumansNum (void); - int GetHumansAliveNum(void); - int GetHumansJoinedTeam (void); - int GetBotsNum (void); - - void Think (void); - void PeriodicThink (void); - - void CreateKillerEntity (void); - void DestroyKillerEntity (void); - void TouchWithKillerEntity (Bot *bot); - - void Free (void); - void Free (int index); - - void AddRandom (bool isConsoleCmd = true) { AddBot ("", -1, -1, -1, -1, isConsoleCmd); } - void AddBot (const String &name, int difficulty, int personality, int team, int member, bool isConsoleCmd = true); - void AddBot (const String &name, const String &difficulty, const String &personality, const String &team, const String &member, bool isConsoleCmd = true); - void FillServer (int selection, int personality = PERSONALITY_NORMAL, int difficulty = -1, int numToAdd = -1); - - void RemoveAll (void); - void RemoveRandom (bool keepQuota = false); - void RemoveFromTeam (Team team, bool removeAll = false); - void RemoveMenu (edict_t *ent, int selection); - void KillAll (int team = -1); - void MaintainBotQuota (void); - void AdjustQuota (bool isPlayerConnection, edict_t *ent); - void InitQuota (void); - - void AddPlayerToCheckTeamQueue (edict_t *ent); - void VerifyPlayersHasJoinedTeam (int &desiredCount); - void SelectLeaderEachTeam (int team, bool reset); - - void ListBots (void); - void SetWeaponMode (int selection); - void CheckTeamEconomics (int team, bool setTrue = false); - - static void CallGameEntity (entvars_t *vars); - inline void SetDeathMsgState (bool sent) - { - m_deathMsgSent = sent; + inline bool checkTeamEco (int team) { + return m_economicsGood[team]; } - // grenades - void UpdateActiveGrenades (void); - const Array &GetActiveGrenades (void); + bool isTeamStacked (int team); - inline bool HasActiveGrenades (void) - { - return !m_activeGrenades.IsEmpty (); + inline int getLastWinner (void) const { + return m_lastWinner; + } + + inline void setLastWinner (int winner) { + m_lastWinner = winner; + } + void reset (void); + + int index (edict_t *ent); + Bot *getBot (int index); + Bot *getBot (edict_t *ent); + Bot *getAliveBot (void); + Bot *getHighfragBot (int team); + + int getHumansCount (bool ignoreSpectators = false); + int getAliveHumansCount (void); + int getBotCount (void); + + void framePeriodic (void); + void frame (void); + + void createKillerEntity (void); + void destroyKillerEntity (void); + void touchKillerEntity (Bot *bot); + + void destroy (void); + void destroy (int index); + + void createRandom (bool manual = false) { + addbot ("", -1, -1, -1, -1, manual); + } + void addbot (const String &name, int difficulty, int personality, int team, int member, bool manual); + void addbot (const String &name, const String &difficulty, const String &personality, const String &team, const String &member, bool manual); + void serverFill (int selection, int personality = PERSONALITY_NORMAL, int difficulty = -1, int numToAdd = -1); + + void kickEveryone (bool instant = false, bool zeroQuota = true); + void kickRandom (bool decQuota = true); + void kickBot (int index); + void kickFromTeam (Team team, bool removeAll = false); + + void kickBotByMenu (edict_t *ent, int selection); + void killAllBots (int team = -1); + void maintainQuota (void); + void initQuota (void); + void decrementQuota (int by = 1); + void selectLeaders (int team, bool reset); + + void listBots (void); + void setWeaponMode (int selection); + void updateTeamEconomics (int team, bool setTrue = false); + + static void execGameEntity (entvars_t *vars); + + // grenades + void updateActiveGrenade (void); + void updateIntrestingEntities (void); + + inline Array &searchActiveGrenades (void) { + return m_activeGrenades; + } + + inline Array &searchIntrestingEntities (void) { + return m_intrestingEntities; + } + + inline bool hasActiveGrenades (void) { + return !m_activeGrenades.empty (); + } + + inline bool hasIntrestingEntities (void) { + return !m_intrestingEntities.empty (); } public: - void CalculatePingOffsets (void); - void SendPingDataOffsets (edict_t *to); - void SendDeathMsgFix (void); + void calculatePingOffsets (void); + void sendPingOffsets (edict_t *to); + void sendDeathMsgFix (void); + + inline void updateDeathMsgState (bool sent) { + m_deathMsgSent = sent; + } }; // waypoint operation class -class Waypoint : public Singleton -{ +class Waypoint : public Singleton { + +public: friend class Bot; private: + struct Bucket { + int x, y, z; + }; + Path *m_paths[MAX_WAYPOINTS]; bool m_waypointPaths; @@ -1366,8 +1416,9 @@ private: Vector m_learnVelocity; Vector m_learnPosition; - Vector m_foundBombOrigin; + Vector m_bombPos; + int m_numWaypoints; int m_loadTries; int m_cacheWaypointIndex; int m_lastJumpWaypoint; @@ -1385,142 +1436,174 @@ private: int *m_distMatrix; int *m_pathMatrix; - Array m_terrorPoints; - Array m_ctPoints; - Array m_goalPoints; - Array m_campPoints; - Array m_sniperPoints; - Array m_rescuePoints; - Array m_visitedGoals; + IntArray m_terrorPoints; + IntArray m_ctPoints; + IntArray m_goalPoints; + IntArray m_campPoints; + IntArray m_sniperPoints; + IntArray m_rescuePoints; + IntArray m_visitedGoals; + + IntArray m_buckets[MAX_WAYPOINT_BUCKET_MAX][MAX_WAYPOINT_BUCKET_MAX][MAX_WAYPOINT_BUCKET_MAX]; public: bool m_redoneVisibility; Waypoint (void); - ~Waypoint (void); + ~Waypoint (void); - void Init (void); - void InitExperienceTab (void); - void InitVisibilityTab (void); + void init (void); + void initExperience (void); + void initVisibility (void); + void initTypes (void); - void InitTypes (void); - void AddPath (int addIndex, int pathIndex, float distance); + void addPath (int addIndex, int pathIndex, float distance); - int GetFacingIndex (void); - int FindFarest (const Vector &origin, float maxDistance = 32.0); - int FindNearest (const Vector &origin, float minDistance = 9999.0f, int flags = -1); - void FindInRadius (Array &holder, float radius, const Vector &origin, int maxCount = -1); + int getFacingIndex (void); + int getFarest (const Vector &origin, float maxDistance = 32.0); + int getNearest (const Vector &origin, float minDistance = 9999.0f, int flags = -1); + int getNearestNoBuckets (const Vector &origin, float minDistance = 9999.0f, int flags = -1); + int getEditorNeareset (void); + IntArray searchRadius (float radius, const Vector &origin, int maxCount = -1); - void Add (int flags, const Vector &waypointOrigin = Vector::GetZero ()); - void Delete (void); - void ToggleFlags (int toggleFlag); - void SetRadius (int radius); - bool IsConnected (int pointA, int pointB); - bool IsConnected (int num); - void InitializeVisibility (void); - void CreatePath (char dir); - void DeletePath (void); - void CacheWaypoint (void); + void push (int flags, const Vector &waypointOrigin = Vector::null ()); + void erase (int target); + void toggleFlags (int toggleFlag); + void setRadius (int radius); + bool isConnected (int pointA, int pointB); + bool isConnected (int num); + void rebuildVisibility (void); + void pathCreate (char dir); + void erasePath (void); + void cachePoint (void); - float GetTravelTime (float maxSpeed, const Vector &src, const Vector &origin); - bool IsVisible (int srcIndex, int destIndex); - bool IsStandVisible (int srcIndex, int destIndex); - bool IsDuckVisible (int srcIndex, int destIndex); - void CalculateWayzone (int index); + float calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin); + bool isVisible (int srcIndex, int destIndex); + bool isStandVisible (int srcIndex, int destIndex); + bool isDuckVisible (int srcIndex, int destIndex); + void calculatePathRadius (int index); - bool Load (void); - void Save (void); - void CleanupPathMemory (void); + bool load (void); + void save (void); + void cleanupPathMemory (void); - bool Reachable (Bot *bot, int index); - bool IsNodeReachable (const Vector &src, const Vector &destination); - void Think (void); - bool NodesValid (void); - void SaveExperienceTab (void); - void SaveVisibilityTab (void); - void CreateBasic (void); - void EraseFromHardDisk (void); + bool isReachable (Bot *bot, int index); + bool isNodeReacheable (const Vector &src, const Vector &destination); + void frame (void); + bool checkNodes (void); + void saveExperience (void); + void saveVisibility (void); + void addBasic (void); + void eraseFromDisk (void); - void InitPathMatrix (void); - void SavePathMatrix (void); - bool LoadPathMatrix (void); + void initPathMatrix (void); + void savePathMatrix (void); + bool loadPathMatrix (void); - int GetPathDistance (int srcIndex, int destIndex); - Path *GetPath (int id); - const char *GetWaypointInfo (int id); + int getPathDist (int srcIndex, int destIndex); + const char *getInformation (int id); - char *GetInfo (void) { return m_infoBuffer; } - bool HasChanged (void) { return m_waypointsChanged; } + char *getAuthor (void) { + return m_infoBuffer; + } + bool hasChanged (void) { + return m_waypointsChanged; + } - void SetFindIndex (int index); - void SetLearnJumpWaypoint (void); + void setSearchIndex (int index); + void startLearnJump (void); - bool IsGoalVisited (int index); - void SetGoalVisited (int index); - void ClearVisitedGoals (void); + bool isVisited (int index); + void setVisited (int index); + void clearVisited (void); - const char *GetDataDir (bool isMemoryFile = false); - const char *GetFileName (bool isMemoryFile = false); + const char *getDataDirectory (bool isMemoryFile = false); + const char *getWaypointFilename (bool isMemoryFile = false); - void SetBombPosition (bool shouldReset = false); - inline const Vector &GetBombPosition (void) { return m_foundBombOrigin; } + void setBombPos (bool reset = false, const Vector &pos = Vector::null ()); + + inline const Vector &getBombPos (void) { + return m_bombPos; + } + + // access paths + inline Path &operator [] (int index) { + return *m_paths[index]; + } + + // check waypoints range + inline bool exists (int index) const { + return index >= 0 && index < m_numWaypoints; + } + + // get real waypoint num + inline int length (void) const { + return m_numWaypoints; + } // free's socket handle - void CloseSocketHandle (int sock); + void closeSocket (int sock); // do actually downloading of waypoint file - WaypointDownloadError RequestWaypoint (void); -}; + WaypointDownloadError downloadWaypoint (void); -class BotMenu -{ + // initalize waypoint buckets + void initBuckets (void); + void addToBucket (const Vector &pos, int index); + void eraseFromBucket (const Vector &pos, int index); + + Bucket locateBucket (const Vector &pos); + IntArray &getWaypointsInBucket (const Vector &pos); }; #include // expose bot super-globals -#define waypoints Waypoint::GetReference () -#define engine Engine::GetReference () -#define bots BotManager::GetReference () +static auto &waypoints = Waypoint::ref (); +static auto &bots = BotManager::ref (); +static auto &engine = Engine::ref (); +static auto &rng = RandomSequence::ref (); // prototypes of bot functions... -extern int GetWeaponReturn (bool isString, const char *weaponAlias, int weaponIndex = -1); -extern int GetWeaponPenetrationPower (int id); -extern int GenerateBuildNumber (void); -extern float GetShootingConeDeviation (edict_t *ent, Vector *position); +extern int getWeaponData (bool isString, const char *weaponAlias, int weaponIndex = -1); +extern int getWeaponPenetrationPower (int id); +extern int buildNumber (void); +extern float getShootingConeDeviation (edict_t *ent, const Vector &position); -extern bool IsVisible (const Vector &origin, edict_t *ent); -extern bool IsAlive (edict_t *ent); -extern bool IsInViewCone (const Vector &origin, edict_t *ent); +extern bool isVisible (const Vector &origin, edict_t *ent); +extern bool isAlive (edict_t *ent); +extern bool isInViewCone (const Vector &origin, edict_t *ent); +extern bool isFakeClient (edict_t *ent); +extern bool isPlayer (edict_t *ent); +extern bool isPlayerVIP (edict_t *ent); +extern bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant = false); +extern bool findNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false, bool needBotWithC4 = false); -extern bool IsValidBot (edict_t *ent); -extern bool IsValidPlayer (edict_t *ent); -extern bool IsPlayerVIP (edict_t *ent); -extern bool OpenConfig (const char *fileName, const char *errorIfNotExists, MemoryFile *outFile, bool languageDependant = false); -extern bool FindNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false); +extern void cleanupGarbage (void); +extern void initRound (void); +extern void checkWelcome (void); +extern void logEntry (bool outputToConsole, int logLevel, const char *format, ...); +extern void showMenu (edict_t *ent, MenuId menu); +extern void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex); +extern void attachSoundsToClients (edict_t *ent, const char *sample, float volume); +extern void simulateSoundUpdates (int playerIndex); -extern void FreeLibraryMemory (void); -extern void RoundInit (void); -extern void CheckWelcomeMessage (void); -extern void AddLogEntry (bool outputToConsole, int logLevel, const char *format, ...); -extern void DisplayMenuToClient (edict_t *ent, MenuId menu); -extern void DecalTrace (entvars_t *pev, TraceResult *trace, int logotypeIndex); -extern void SoundAttachToClients (edict_t *ent, const char *sample, float volume); -extern void SoundSimulateUpdate (int playerIndex); - -extern const char *FormatBuffer (const char *format, ...); +extern const char *format (const char *format, ...); // very global convars extern ConVar yb_jasonmode; extern ConVar yb_communication_type; extern ConVar yb_ignore_enemies; -#include #include +#include #include -inline int Bot::GetIndex (void) -{ - return engine.IndexOfEntity (GetEntity ()); +inline int Bot::index (void) { + return engine.indexOfEntity (ent ()); } + +static inline void makeVectors (const Vector &in) { + in.makeVectors (&g_pGlobals->v_forward, &g_pGlobals->v_right, &g_pGlobals->v_up); +} \ No newline at end of file diff --git a/project/makefile b/project/makefile index fd85be5..8816545 100644 --- a/project/makefile +++ b/project/makefile @@ -4,27 +4,21 @@ # # This software is licensed under the BSD-style license. # Additional exceptions apply. For full license details, see LICENSE.txt or visit: -# https://yapb.jeefo.net/license +# https://yapb.ru/license # PROJECT = yapb SOURCES = ../source OBJECTS = $(wildcard $(SOURCES)/*.cpp) -COMPILER_FLAGS = -std=c++11 -m32 -Wall -Werror -Wextra -fno-exceptions -fno-rtti -LINKER_FLAGS = -m32 -lm -ldl - -ifeq "$(SSE_VERSION)" "" - COMPILER_SSE_VERSION = 2 -else - COMPILER_SSE_VERSION = $(SSE_VERSION) -endif +COMPILER_FLAGS = -mtune=generic -std=c++11 -m32 -Wall -Wextra -Werror -fno-exceptions -fno-rtti -DPOSIX +LINKER_FLAGS = -m32 ifeq "$(DEBUG)" "true" COMPILER_FLAGS += -D_DEBUG -DDEBUG -g3 BINARY_DIR = debug else - COMPILER_FLAGS += -DNDEBUG -pipe -O2 -msse$(COMPILER_SSE_VERSION) -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fvisibility-inlines-hidden + COMPILER_FLAGS += -DNDEBUG -pipe -O3 -msse2 -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fvisibility-inlines-hidden -nostdinc++ BINARY_DIR = release endif @@ -37,12 +31,12 @@ endif ifeq "$(OSX)" "true" LIBRARY_EXT = dylib - COMPILER_FLAGS += -DOSX -D_OSX -DPOSIX - LINKER_FLAGS += -dynamiclib -lstdc++ -mmacosx-version-min=10.12 -arch i386 + COMPILER_FLAGS += -DOSX -D_OSX -mmacosx-version-min=10.9 + LINKER_FLAGS += -dynamiclib -lstdc++ -mmacosx-version-min=10.9 -arch i386 else LIBRARY_EXT = so - COMPILER_FLAGS += -DLINUX -D_LINUX -DPOSIX - LINKER_FLAGS += -shared -lsupc++ + COMPILER_FLAGS += -DLINUX -D_LINUX + LINKER_FLAGS += -shared endif BINARY_OUTPUT = $(PROJECT).$(LIBRARY_EXT) @@ -54,13 +48,11 @@ ifeq ($(findstring clang,$(COMPILER)),clang) else ifeq ($(findstring gcc,$(COMPILER)),gcc) ifneq "$(OSX)" "false" LINKER_FLAGS += -static-libgcc - COMPILER_FLAGS += -funroll-all-loops + COMPILER_FLAGS += -funroll-all-loops -Wno-implicit-fallthrough endif -else ifeq ($(findstring icpc,$(COMPILER)),icpc) +else ifeq ($(findstring icc,$(COMPILER)),icc) COMPILER_FLAGS += -funroll-all-loops -no-prec-div -no-inline-min-size -no-inline-max-size -wd11076 -wd11074 - LINKER_FLAGS += -static-intel -no-intel-extensions -else - $(error Compiler unrecognized. Specify CC.) + LINKER_FLAGS += -cxxlib-nostd -static-intel -no-intel-extensions endif OBJECTS_BIN := $(OBJECTS:%.cpp=$(BINARY_DIR)/%.o) diff --git a/project/yapb.rc b/project/yapb.rc index b590ba2..3b25b68 100644 --- a/project/yapb.rc +++ b/project/yapb.rc @@ -27,7 +27,7 @@ FILETYPE 0x2 VALUE "LegalCopyright", PRODUCT_COPYRIGHT "\0" VALUE "LegalTrademarks", PRODUCT_LEGAL "\0" VALUE "ProductName", PRODUCT_NAME "\0" - VALUE "ProductVersion", PRODUCT_COMMENTS "\0" + VALUE "ProductVersion", PRODUCT_VERSION "\0" VALUE "InternalName", PRODUCT_INTERNAL_NAME "\0" } } diff --git a/project/yapb.sln b/project/yapb.sln index a118420..47365ca 100644 --- a/project/yapb.sln +++ b/project/yapb.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25029.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.28016.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yapb", "yapb.vcxproj", "{C232645A-3B99-48F4-A1F3-F20CF0A9568B}" EndProject @@ -19,4 +19,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {03C7A0BC-CD53-4141-879D-E7347565D90C} + EndGlobalSection EndGlobal diff --git a/project/yapb.vcxproj b/project/yapb.vcxproj index d3ba282..89e9a76 100644 --- a/project/yapb.vcxproj +++ b/project/yapb.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -12,12 +12,11 @@ - + - @@ -53,14 +52,14 @@ DynamicLibrary - v140_xp + v141 false true DynamicLibrary false - v140_xp + v141 diff --git a/project/yapb.vcxproj.filters b/project/yapb.vcxproj.filters index 5c6e46b..027bc0d 100644 --- a/project/yapb.vcxproj.filters +++ b/project/yapb.vcxproj.filters @@ -18,9 +18,6 @@ include - - include - include @@ -54,8 +51,8 @@ include - - include\engine + + include @@ -80,15 +77,15 @@ source - - source - source source + + source + diff --git a/source/basecode.cpp b/source/basecode.cpp index 6f5ad8a..8514c7d 100644 --- a/source/basecode.cpp +++ b/source/basecode.cpp @@ -7,7 +7,7 @@ // https://yapb.ru/license // -#include +#include ConVar yb_debug ("yb_debug", "0"); ConVar yb_debug_goal ("yb_debug_goal", "-1"); @@ -27,7 +27,7 @@ ConVar yb_botbuy ("yb_botbuy", "1"); ConVar yb_chatter_path ("yb_chatter_path", "sound/radio/bot"); ConVar yb_restricted_weapons ("yb_restricted_weapons", ""); -ConVar yb_best_weapon_picker_type ("yb_best_weapon_picker_type", "2"); +ConVar yb_best_weapon_picker_type ("yb_best_weapon_picker_type", "1"); // game console variables ConVar mp_c4timer ("mp_c4timer", nullptr, VT_NOREGISTER); @@ -35,37 +35,31 @@ ConVar mp_buytime ("mp_buytime", nullptr, VT_NOREGISTER, true, "1"); ConVar mp_footsteps ("mp_footsteps", nullptr, VT_NOREGISTER); ConVar sv_gravity ("sv_gravity", nullptr, VT_NOREGISTER); -int Bot::GetMessageQueue (void) -{ +int Bot::getMsgQueue (void) { // this function get the current message from the bots message queue int message = m_messageQueue[m_actMessageIndex++]; m_actMessageIndex &= 0x1f; // wraparound return message; -} +} -void Bot::PushMessageQueue (int message) -{ +void Bot::pushMsgQueue (int message) { // this function put a message into the bot message queue - if (message == GAME_MSG_SAY_CMD) - { + if (message == GAME_MSG_SAY_CMD) { // notify other bots of the spoken text otherwise, bots won't respond to other bots (network messages aren't sent from bots) - int entityIndex = GetIndex (); + int entityIndex = index (); - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *otherBot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *otherBot = bots.getBot (i); - if (otherBot != nullptr && otherBot->pev != pev) - { - if (m_notKilled == otherBot->m_notKilled) - { + if (otherBot != nullptr && otherBot->pev != pev) { + if (m_notKilled == otherBot->m_notKilled) { otherBot->m_sayTextBuffer.entityIndex = entityIndex; - strcpy (otherBot->m_sayTextBuffer.sayText, m_tempStrings); + otherBot->m_sayTextBuffer.sayText = m_tempStrings; } - otherBot->m_sayTextBuffer.timeNextChat = engine.Time () + otherBot->m_sayTextBuffer.chatDelay; + otherBot->m_sayTextBuffer.timeNextChat = engine.timebase () + otherBot->m_sayTextBuffer.chatDelay; } } } @@ -73,320 +67,323 @@ void Bot::PushMessageQueue (int message) m_pushMessageIndex &= 0x1f; // wraparound } -int Bot::InFieldOfView (const Vector &destination) -{ - float entityAngle = AngleMod (destination.ToYaw ()); // find yaw angle from source to destination... - float viewAngle = AngleMod (pev->v_angle.y); // get bot's current view angle... +float Bot::isInFOV (const Vector &destination) { + float entityAngle = cr::angleMod (destination.toYaw ()); // find yaw angle from source to destination... + float viewAngle = cr::angleMod (pev->v_angle.y); // get bot's current view angle... // return the absolute value of angle to destination entity // zero degrees means straight ahead, 45 degrees to the left or // 45 degrees to the right is the limit of the normal view angle - int absoluteAngle = abs (static_cast (viewAngle) - static_cast (entityAngle)); - - if (absoluteAngle > 180) - absoluteAngle = 360 - absoluteAngle; + float absoluteAngle = cr::abs (viewAngle - entityAngle); + if (absoluteAngle > 180.0f) { + absoluteAngle = 360.0f - absoluteAngle; + } return absoluteAngle; } -bool Bot::IsInViewCone (const Vector &origin) -{ +bool Bot::isInViewCone (const Vector &origin) { // this function returns true if the spatial vector location origin is located inside // the field of view cone of the bot entity, false otherwise. It is assumed that entities // have a human-like field of view, that is, about 90 degrees. - return ::IsInViewCone (origin, GetEntity ()); + return ::isInViewCone (origin, ent ()); } -bool Bot::ItemIsVisible (const Vector &destination, const char *itemName) -{ +bool Bot::seesItem (const Vector &destination, const char *itemName) { TraceResult tr; // trace a line from bot's eyes to destination.. - engine.TestLine (EyePosition (), destination, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (eyePos (), destination, TRACE_IGNORE_MONSTERS, ent (), &tr); // check if line of sight to object is not blocked (i.e. visible) - if (tr.flFraction != 1.0f) - { - auto classname = STRING (tr.pHit->v.classname); - - // check for standard items - if (strcmp (classname, itemName) == 0) - return true; - - if (tr.flFraction > 0.98f && ((g_gameFlags & GAME_CSDM) && strncmp (classname, "csdmw_", 6) == 0)) - return true; - - return false; + if (tr.flFraction != 1.0f) { + return strcmp (STRING (tr.pHit->v.classname), itemName) == 0; } return true; } -bool Bot::EntityIsVisible (const Vector &dest, bool fromBody) -{ +bool Bot::seesEntity (const Vector &dest, bool fromBody) { TraceResult tr; // trace a line from bot's eyes to destination... - engine.TestLine (fromBody ? pev->origin - Vector (0.0f, 0.0f, 1.0f) : EyePosition (), dest, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (fromBody ? pev->origin : eyePos (), dest, TRACE_IGNORE_EVERYTHING, ent (), &tr); // check if line of sight to object is not blocked (i.e. visible) return tr.flFraction >= 1.0f; } -void Bot::CheckGrenadeThrow (void) -{ +void Bot::checkGrenadesThrow (void) { + + // do not check cancel if we have grenade in out hands + bool checkTasks = taskId () == TASK_PLANTBOMB || taskId () == TASK_DEFUSEBOMB; + + auto clearThrowStates = [] (unsigned int &states) { + states &= ~(STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG); + }; + // check if throwing a grenade is a good thing to do... - if (m_lastEnemy == nullptr || yb_ignore_enemies.GetBool () || yb_jasonmode.GetBool () || m_grenadeCheckTime > engine.Time () || m_isUsingGrenade || GetTaskId () == TASK_PLANTBOMB || GetTaskId () == TASK_DEFUSEBOMB || m_isReloading || !IsAlive (m_lastEnemy)) - { - m_states &= ~(STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG); + if (checkTasks || yb_ignore_enemies.boolean () || m_isUsingGrenade || m_grenadeRequested || m_isReloading || yb_jasonmode.boolean () || m_grenadeCheckTime >= engine.timebase ()) { + clearThrowStates (m_states); return; } // check again in some seconds - m_grenadeCheckTime = engine.Time () + 0.5f; - - // check if we have grenades to throw - int grenadeToThrow = CheckGrenades (); - - // if we don't have grenades no need to check it this round again - if (grenadeToThrow == -1) - { - m_grenadeCheckTime = engine.Time () + 15.0f; // changed since, conzero can drop grens from dead players - m_states &= ~(STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG); + m_grenadeCheckTime = engine.timebase () + 0.5f; + if (!isAlive (m_lastEnemy) || !(m_states & (STATE_SUSPECT_ENEMY | STATE_HEARING_ENEMY))) { + clearThrowStates (m_states); return; } - // care about different types of grenades - if ((grenadeToThrow == WEAPON_EXPLOSIVE || grenadeToThrow == WEAPON_SMOKE) && Random.Int (0, 100) < 45 && !(m_states & (STATE_SEEING_ENEMY | STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG))) - { - float distance = (m_lastEnemy->v.origin - pev->origin).GetLength (); - // is enemy to high to throw - if ((m_lastEnemy->v.origin.z > (pev->origin.z + 650.0)) || !(m_lastEnemy->v.flags & (FL_ONGROUND | FL_DUCKING))) - distance = 99999.0f; // just some crazy value + // check if we have grenades to throw + int grenadeToThrow = bestGrenadeCarried (); - // enemy is within a good throwing distance ? - if (distance > (grenadeToThrow == WEAPON_SMOKE ? 400 : 600) && distance <= 1000) - { - // care about different types of grenades - if (grenadeToThrow == WEAPON_EXPLOSIVE) - { - bool allowThrowing = true; + // if we don't have grenades no need to check it this round again + if (grenadeToThrow == -1) { + m_grenadeCheckTime = engine.timebase () + 15.0f; // changed since, conzero can drop grens from dead players - // check for teammates - if (GetNearbyFriendsNearPosition (m_lastEnemy->v.origin, 256.0f) > 0) - allowThrowing = false; + clearThrowStates (m_states); + return; + } + else { + int cancelProb = 20; - if (allowThrowing && m_seeEnemyTime + 2.0 < engine.Time ()) - { - const Vector &enemyPredict = ((m_lastEnemy->v.velocity * 0.5f).Get2D () + m_lastEnemy->v.origin); - float searchRadius = m_lastEnemy->v.velocity.GetLength2D (); - - // check the search radius - if (searchRadius < 128.0f) - searchRadius = 128.0f; - - Array predictedPoints; - - // search waypoints - waypoints.FindInRadius (predictedPoints, searchRadius, enemyPredict, 5); - - FOR_EACH_AE (predictedPoints, it) - { - allowThrowing = true; - - // check the throwing - m_throw = waypoints.GetPath (predictedPoints[it])->origin; - Vector src = CheckThrow (EyePosition (), m_throw); - - if (src.GetLengthSquared () < 100.0f) - src = CheckToss (EyePosition (), m_throw); - - if (src.IsZero ()) - allowThrowing = false; - else - break; - } - } - - // start explosive grenade throwing? - if (allowThrowing) - m_states |= STATE_THROW_HE; - else - m_states &= ~STATE_THROW_HE; - } - else if (grenadeToThrow == WEAPON_SMOKE) - { - // start smoke grenade throwing? - if ((m_states & STATE_SEEING_ENEMY) && GetShootingConeDeviation (m_enemy, &pev->origin) >= 0.70f && m_seeEnemyTime + 2.0f < engine.Time ()) - m_states &= ~STATE_THROW_SG; - else - m_states |= STATE_THROW_SG; - } + if (grenadeToThrow == WEAPON_FLASHBANG) { + cancelProb = 10; + } + else if (grenadeToThrow == WEAPON_SMOKE) { + cancelProb = 5; + } + if (rng.getInt (0, 100) < cancelProb) { + clearThrowStates (m_states); + return; } } - else if (IsAlive (m_lastEnemy) && grenadeToThrow == WEAPON_FLASHBANG && (m_lastEnemy->v.origin - pev->origin).GetLength () < 800.0f && !(m_aimFlags & AIM_ENEMY) && Random.Int (0, 100) < 50) - { + float distance = (m_lastEnemyOrigin - pev->origin).length2D (); + + // don't throw grenades at anything that isn't on the ground! + if (!(m_lastEnemy->v.flags & FL_ONGROUND) && !m_lastEnemy->v.waterlevel && m_lastEnemyOrigin.z > pev->absmax.z) { + distance = 9999.0f; + } + + // too high to throw? + if (m_lastEnemy->v.origin.z > pev->origin.z + 500.0f) { + distance = 9999.0f; + } + + // enemy within a good throw distance? + if (!m_lastEnemyOrigin.empty () && distance > (grenadeToThrow == WEAPON_SMOKE ? 200.0f : 400.0f) && distance < 1200.0f) { bool allowThrowing = true; - Array inRadius; - waypoints.FindInRadius (inRadius, 256.0f, m_lastEnemy->v.origin + (m_lastEnemy->v.velocity * 0.5f).Get2D ()); + // care about different grenades + switch (grenadeToThrow) { + case WEAPON_EXPLOSIVE: + if (numFriendsNear (m_lastEnemy->v.origin, 256.0f) > 0) { + allowThrowing = false; + } + else { + float radius = m_lastEnemy->v.velocity.length2D (); + const Vector &pos = (m_lastEnemy->v.velocity * 0.5f).make2D () + m_lastEnemy->v.origin; - FOR_EACH_AE (inRadius, i) - { - Path *path = waypoints.GetPath (i); + if (radius < 164.0f) { + radius = 164.0f; + } + auto predicted = waypoints.searchRadius (radius, pos, 12); - int friendCount = GetNearbyFriendsNearPosition (path->origin, 256.0f); + if (predicted.empty ()) { + m_states &= ~STATE_THROW_HE; + break; + } - if (friendCount > 0 && m_difficulty < 4) - continue; + for (const auto predict : predicted) { + allowThrowing = true; - m_throw = path->origin; - Vector src = CheckThrow (EyePosition (), m_throw); + if (!waypoints.exists (predict)) { + allowThrowing = false; + continue; + } - if (src.GetLengthSquared () < 100.0f) - src = CheckToss (EyePosition (), m_throw); + m_throw = waypoints[predict].origin; - if (src.IsZero ()) - continue; + auto throwPos = calcThrow (eyePos (), m_throw); - allowThrowing = true; + if (throwPos.lengthSq () < 100.0f) { + throwPos = calcToss (eyePos (), m_throw); + } + + if (throwPos.empty ()) { + allowThrowing = false; + } + else { + m_throw.z += 110.0f; + break; + } + } + } + + if (allowThrowing) { + m_states |= STATE_THROW_HE; + } + else { + m_states &= ~STATE_THROW_HE; + } + break; + + case WEAPON_FLASHBANG: { + int nearest = waypoints.getNearest ((m_lastEnemy->v.velocity * 0.5f).make2D () + m_lastEnemy->v.origin); + + if (nearest != INVALID_WAYPOINT_INDEX) { + m_throw = waypoints[nearest].origin; + + if (numFriendsNear (m_throw, 256.0f) > 0) { + allowThrowing = false; + } + } + else { + allowThrowing = false; + } + + if (allowThrowing) { + auto throwPos = calcThrow (eyePos (), m_throw); + + if (throwPos.lengthSq () < 100.0f) { + throwPos = calcToss (eyePos (), m_throw); + } + + if (throwPos.empty ()) { + allowThrowing = false; + } + else { + m_throw.z += 110.0f; + } + } + + if (allowThrowing) { + m_states |= STATE_THROW_FB; + } + else { + m_states &= ~STATE_THROW_FB; + } break; } - // start concussion grenade throwing? - if (allowThrowing && m_seeEnemyTime + 2.0f < engine.Time ()) - m_states |= STATE_THROW_FB; - else - m_states &= ~STATE_THROW_FB; + case WEAPON_SMOKE: + if (allowThrowing && !engine.isNullEntity (m_lastEnemy)) { + if (getShootingConeDeviation (m_lastEnemy, pev->origin) >= 0.9f) { + allowThrowing = false; + } + } + + if (allowThrowing) { + m_states |= STATE_THROW_SG; + } + else { + m_states &= ~STATE_THROW_SG; + } + break; + } + const float MaxThrowTime = engine.timebase () + 0.3f; + + if (m_states & STATE_THROW_HE) { + startTask (TASK_THROWHEGRENADE, TASKPRI_THROWGRENADE, INVALID_WAYPOINT_INDEX, MaxThrowTime, false); + } + else if (m_states & STATE_THROW_FB) { + startTask (TASK_THROWFLASHBANG, TASKPRI_THROWGRENADE, INVALID_WAYPOINT_INDEX, MaxThrowTime, false); + } + else if (m_states & STATE_THROW_SG) { + startTask (TASK_THROWSMOKE, TASKPRI_THROWGRENADE, INVALID_WAYPOINT_INDEX, MaxThrowTime, false); + } } - - if (m_states & STATE_THROW_HE) - PushTask (TASK_THROWHEGRENADE, TASKPRI_THROWGRENADE, -1, engine.Time () + 3.0f, false); - else if (m_states & STATE_THROW_FB) - PushTask (TASK_THROWFLASHBANG, TASKPRI_THROWGRENADE, -1, engine.Time () + 3.0f, false); - else if (m_states & STATE_THROW_SG) - PushTask (TASK_THROWSMOKE, TASKPRI_THROWGRENADE, -1, engine.Time () + 3.0f, false); - - // delay next grenade throw - if (m_states & (STATE_THROW_HE | STATE_THROW_FB | STATE_THROW_SG)) - { - m_grenadeCheckTime = engine.Time () + MAX_GRENADE_TIMER; - m_maxThrowTimer = engine.Time () + MAX_GRENADE_TIMER * 2.0f; + else { + clearThrowStates (m_states); } } -void Bot::AvoidGrenades (void) -{ +void Bot::avoidGrenades (void) { // checks if bot 'sees' a grenade, and avoid it - if (!bots.HasActiveGrenades ()) + if (!bots.hasActiveGrenades ()) { return; + } - // check if old pointers to grenade is invalid - if (engine.IsNullEntity (m_avoidGrenade)) - { + // check if old pointers to grenade is invalid + if (engine.isNullEntity (m_avoidGrenade)) { m_avoidGrenade = nullptr; m_needAvoidGrenade = 0; } - else if ((m_avoidGrenade->v.flags & FL_ONGROUND) || (m_avoidGrenade->v.effects & EF_NODRAW)) - { + else if ((m_avoidGrenade->v.flags & FL_ONGROUND) || (m_avoidGrenade->v.effects & EF_NODRAW)) { m_avoidGrenade = nullptr; m_needAvoidGrenade = 0; } - Array activeGrenades = bots.GetActiveGrenades (); + auto &activeGrenades = bots.searchActiveGrenades (); // find all grenades on the map - FOR_EACH_AE (activeGrenades, it) - { - edict_t *ent = activeGrenades[it]; - - if (ent->v.effects & EF_NODRAW) + for (auto pent : activeGrenades) { + if (pent->v.effects & EF_NODRAW) { continue; + } // check if visible to the bot - if (!EntityIsVisible (ent->v.origin) && InFieldOfView (ent->v.origin - EyePosition ()) > pev->fov * 0.5f) + if (!seesEntity (pent->v.origin) && isInFOV (pent->v.origin - eyePos ()) > pev->fov * 0.5f) { continue; + } + auto model = STRING (pent->v.model) + 9; - if (m_turnAwayFromFlashbang < engine.Time () && m_personality == PERSONALITY_RUSHER && m_difficulty == 4 && strcmp (STRING (ent->v.model) + 9, "flashbang.mdl") == 0) - { + if (m_turnAwayFromFlashbang < engine.timebase () && m_personality == PERSONALITY_RUSHER && m_difficulty == 4 && strcmp (model, "flashbang.mdl") == 0) { // don't look at flash bang - if (!(m_states & STATE_SEEING_ENEMY)) - { - pev->v_angle.y = AngleNormalize ((engine.GetAbsOrigin (ent) - EyePosition ()).ToAngles ().y + 180.0f); + if (!(m_states & STATE_SEEING_ENEMY)) { + pev->v_angle.y = cr::angleNorm ((engine.getAbsPos (pent) - eyePos ()).toAngles ().y + 180.0f); m_canChooseAimDirection = false; - m_turnAwayFromFlashbang = engine.Time () + Random.Float (1.0f, 2.0f); + m_turnAwayFromFlashbang = engine.timebase () + rng.getFloat (1.0f, 2.0f); } } - else if (strcmp (STRING (ent->v.model) + 9, "hegrenade.mdl") == 0) - { - if (!engine.IsNullEntity (m_avoidGrenade)) + else if (strcmp (model, "hegrenade.mdl") == 0) { + if (!engine.isNullEntity (m_avoidGrenade)) { return; + } - if (engine.GetTeam (ent->v.owner) == m_team && ent->v.owner != GetEntity ()) + if (engine.getTeam (pent->v.owner) == m_team || pent->v.owner == ent ()) { return; + } - if ((ent->v.flags & FL_ONGROUND) == 0) - { - float distance = (ent->v.origin - pev->origin).GetLength (); - float distanceMoved = ((ent->v.origin + ent->v.velocity * GetThinkInterval ()) - pev->origin).GetLength (); + if (!(pent->v.flags & FL_ONGROUND)) { + float distance = (pent->v.origin - pev->origin).length (); + float distanceMoved = ((pent->v.origin + pent->v.velocity * calcThinkInterval ()) - pev->origin).length (); - if (distanceMoved < distance && distance < 500.0f) - { - MakeVectors (pev->v_angle); + if (distanceMoved < distance && distance < 500.0f) { + makeVectors (pev->v_angle); - const Vector &dirToPoint = (pev->origin - ent->v.origin).Normalize2D (); - const Vector &rightSide = g_pGlobals->v_right.Normalize2D (); + const Vector &dirToPoint = (pev->origin - pent->v.origin).normalize2D (); + const Vector &rightSide = g_pGlobals->v_right.normalize2D (); - if ((dirToPoint | rightSide) > 0.0f) + if ((dirToPoint | rightSide) > 0.0f) { m_needAvoidGrenade = -1; - else + } + else { m_needAvoidGrenade = 1; + } + m_avoidGrenade = pent; + } + } + } + else if ((pent->v.flags & FL_ONGROUND) == 0 && strcmp (model, "smokegrenade.mdl") == 0) { + float distance = (pent->v.origin - pev->origin).length (); - m_avoidGrenade = ent; + // shrink bot's viewing distance to smoke grenade's distance + if (m_viewDistance > distance) { + m_viewDistance = distance; + + if (rng.getInt (0, 100) < 45) { + pushChatterMessage (CHATTER_BEHIND_SMOKE); } } } } } -bool Bot::IsBehindSmokeClouds (edict_t *ent) -{ - if (!bots.HasActiveGrenades ()) - return false; - - const Vector &betweenUs = (ent->v.origin - pev->origin).Normalize (); - Array activeGrenades = bots.GetActiveGrenades (); - - // find all grenades on the map - FOR_EACH_AE (activeGrenades, it) - { - edict_t *grenade = activeGrenades[it]; - - if (grenade->v.effects & EF_NODRAW) - continue; - - // if grenade is invisible don't care for it - if (!(grenade->v.flags & (FL_ONGROUND | FL_PARTIALGROUND)) || strcmp (STRING (grenade->v.model) + 9, "smokegrenade.mdl")) - continue; - - // check if visible to the bot - if (!EntityIsVisible (ent->v.origin) && InFieldOfView (ent->v.origin - EyePosition ()) > pev->fov / 3.0f) - continue; - - const Vector &entityOrigin = engine.GetAbsOrigin (grenade); - const Vector &betweenNade = (entityOrigin - pev->origin).Normalize (); - const Vector &betweenResult = ((betweenNade.Get2D () * 150.0f + entityOrigin) - pev->origin).Normalize (); - - if ((betweenNade | betweenUs) > (betweenNade | betweenResult)) - return true; - } - return false; -} - -int Bot::GetBestWeaponCarried (void) -{ +int Bot::bestPrimaryCarried (void) { // this function returns the best weapon of this bot (based on personality prefs) int *ptr = g_weaponPrefs[m_personality]; @@ -396,21 +393,20 @@ int Bot::GetBestWeaponCarried (void) WeaponSelect *weaponTab = &g_weaponSelect[0]; // take the shield in account - if (HasShield ()) + if (hasShield ()) { weapons |= (1 << WEAPON_SHIELD); + } - for (int i = 0; i < NUM_WEAPONS; i++) - { - if (weapons & (1 << weaponTab[*ptr].id)) + for (int i = 0; i < NUM_WEAPONS; i++) { + if (weapons & (1 << weaponTab[*ptr].id)) { weaponIndex = i; - + } ptr++; } return weaponIndex; } -int Bot::GetBestSecondaryWeaponCarried (void) -{ +int Bot::bestSecondaryCarried (void) { // this function returns the best secondary weapon of this bot (based on personality prefs) int *ptr = g_weaponPrefs[m_personality]; @@ -418,17 +414,15 @@ int Bot::GetBestSecondaryWeaponCarried (void) int weapons = pev->weapons; // take the shield in account - if (HasShield ()) + if (hasShield ()) { weapons |= (1 << WEAPON_SHIELD); - + } WeaponSelect *weaponTab = &g_weaponSelect[0]; - for (int i = 0; i < NUM_WEAPONS; i++) - { + for (int i = 0; i < NUM_WEAPONS; i++) { int id = weaponTab[*ptr].id; - if ((weapons & (1 << weaponTab[*ptr].id)) && (id == WEAPON_USP || id == WEAPON_GLOCK || id == WEAPON_DEAGLE || id == WEAPON_P228 || id == WEAPON_ELITE || id == WEAPON_FIVESEVEN)) - { + if ((weapons & (1 << weaponTab[*ptr].id)) && (id == WEAPON_USP || id == WEAPON_GLOCK || id == WEAPON_DEAGLE || id == WEAPON_P228 || id == WEAPON_ELITE || id == WEAPON_FIVESEVEN)) { weaponIndex = i; break; } @@ -437,8 +431,7 @@ int Bot::GetBestSecondaryWeaponCarried (void) return weaponIndex; } -bool Bot::RateGroundWeapon (edict_t *ent) -{ +bool Bot::rateGroundWeapon (edict_t *ent) { // this function compares weapons on the ground to the one the bot is using int hasWeapon = 0; @@ -447,461 +440,407 @@ bool Bot::RateGroundWeapon (edict_t *ent) WeaponSelect *weaponTab = &g_weaponSelect[0]; - for (int i = 0; i < NUM_WEAPONS; i++) - { - if (strcmp (weaponTab[*ptr].modelName, STRING (ent->v.model) + 9) == 0) - { + for (int i = 0; i < NUM_WEAPONS; i++) { + if (strcmp (weaponTab[*ptr].modelName, STRING (ent->v.model) + 9) == 0) { groundIndex = i; break; } - ptr++; + ptr++; } - if (groundIndex < 7) - hasWeapon = GetBestSecondaryWeaponCarried (); - else - hasWeapon = GetBestWeaponCarried (); - + if (groundIndex < 7) { + hasWeapon = bestSecondaryCarried (); + } + else { + hasWeapon = bestPrimaryCarried (); + } return groundIndex > hasWeapon; } -void Bot::VerifyBreakable (edict_t *touch) -{ - if (!IsShootableBreakable (touch)) +void Bot::processBreakables (edict_t *touch) { + + if (!isShootableBreakable (touch)) { return; + } + m_breakableEntity = lookupBreakable (); - m_breakableEntity = FindBreakable (); - - if (engine.IsNullEntity (m_breakableEntity)) + if (engine.isNullEntity (m_breakableEntity)) { return; - + } m_campButtons = pev->button & IN_DUCK; - PushTask (TASK_SHOOTBREAKABLE, TASKPRI_SHOOTBREAKABLE, -1, 0.0f, false); + startTask (TASK_SHOOTBREAKABLE, TASKPRI_SHOOTBREAKABLE, INVALID_WAYPOINT_INDEX, 0.0f, false); } -void Bot::AvoidPlayersOnTheWay (edict_t *touch) -{ - auto task = GetTaskId (); - - if (task == TASK_PLANTBOMB || task == TASK_DEFUSEBOMB) - return; - - int ownId = GetIndex (); - int otherId = engine.IndexOfEntity (touch); - - if (ownId < otherId) - return; - - if (m_avoid != nullptr) - { - int currentId = engine.IndexOfEntity (m_avoid); - - if (currentId < otherId) - return; - } - m_avoid = touch; - m_avoidTime = engine.Time () + 0.6f; -} - -edict_t *Bot::FindBreakable (void) -{ +edict_t *Bot::lookupBreakable (void) { // this function checks if bot is blocked by a shoot able breakable in his moving direction TraceResult tr; - engine.TestLine (pev->origin, pev->origin + (m_destOrigin - pev->origin).Normalize () * 72.0f, TRACE_IGNORE_NONE, GetEntity (), &tr); + engine.testLine (pev->origin, pev->origin + (m_destOrigin - pev->origin).normalize () * 72.0f, TRACE_IGNORE_NONE, ent (), &tr); - if (tr.flFraction != 1.0f) - { + if (tr.flFraction != 1.0f) { edict_t *ent = tr.pHit; // check if this isn't a triggered (bomb) breakable and if it takes damage. if true, shoot the crap! - if (IsShootableBreakable (ent)) - { - m_breakableOrigin = engine.GetAbsOrigin (ent); + if (isShootableBreakable (ent)) { + m_breakableOrigin = engine.getAbsPos (ent); return ent; } } - engine.TestLine (EyePosition (), EyePosition () + (m_destOrigin - EyePosition ()).Normalize () * 72.0f, TRACE_IGNORE_NONE, GetEntity (), &tr); + engine.testLine (eyePos (), eyePos () + (m_destOrigin - eyePos ()).normalize () * 72.0f, TRACE_IGNORE_NONE, ent (), &tr); - if (tr.flFraction != 1.0f) - { + if (tr.flFraction != 1.0f) { edict_t *ent = tr.pHit; - if (IsShootableBreakable (ent)) - { - m_breakableOrigin = engine.GetAbsOrigin (ent); + if (isShootableBreakable (ent)) { + m_breakableOrigin = engine.getAbsPos (ent); return ent; } } m_breakableEntity = nullptr; - m_breakableOrigin.Zero (); + m_breakableOrigin.nullify (); return nullptr; } -void Bot::SetIdealReactionTimes (bool actual) -{ - float min = 0.0f; - float max = 0.01f; +void Bot::setIdealReactionTimers (bool actual) { + static struct ReactionTime { + float min; + float max; + } reactionTimers[] = {{0.8f, 1.0f}, {0.4f, 0.6f}, {0.2f, 0.4f}, {0.1f, 0.3f}, {0.0f, 0.1f}}; - switch (m_difficulty) - { - case 0: - min = 0.8f; max = 1.0f; - break; + const ReactionTime &reaction = reactionTimers[m_difficulty]; - case 1: - min = 0.4f; max = 0.6f; - break; - - case 2: - min = 0.2f; max = 0.4f; - break; - - case 3: - min = 0.0f; max = 0.1f; - break; - - case 4: - default: - min = 0.0f;max = 0.01f; - break; - } - - if (actual) - { - m_idealReactionTime = min; - m_actualReactionTime = min; + if (actual) { + m_idealReactionTime = reaction.min; + m_actualReactionTime = reaction.min; return; } - m_idealReactionTime = Random.Float (min, max); + m_idealReactionTime = rng.getFloat (reaction.min, reaction.max); } -void Bot::FindItem (void) -{ +void Bot::processPickups (void) { // this function finds Items to collect or use in the near of a bot // don't try to pickup anything while on ladder or trying to escape from bomb... - if (IsOnLadder () || GetTaskId () == TASK_ESCAPEFROMBOMB || yb_jasonmode.GetBool ()) - { + if (isOnLadder () || taskId () == TASK_ESCAPEFROMBOMB || yb_jasonmode.boolean () || !bots.hasIntrestingEntities ()) { m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; return; } + auto &intresting = bots.searchIntrestingEntities (); - edict_t *ent = nullptr, *pickupItem = nullptr; Bot *bot = nullptr; + constexpr float radius = cr::square (320.0f); - float distance, minDistance = 341.0f; - - const float searchRadius = 340.0f; - - if (!engine.IsNullEntity (m_pickupItem)) - { + if (!engine.isNullEntity (m_pickupItem)) { bool itemExists = false; - pickupItem = m_pickupItem; + auto pickupItem = m_pickupItem; - while (!engine.IsNullEntity (ent = FIND_ENTITY_IN_SPHERE (ent, pev->origin, searchRadius))) - { - if ((ent->v.effects & EF_NODRAW) || IsValidPlayer (ent->v.owner)) + for (auto ent : intresting) { + if (isPlayer (ent->v.owner)) { continue; // someone owns this weapon or it hasn't re spawned yet + } + const Vector &origin = engine.getAbsPos (ent); - if (ent == pickupItem) - { - if (ItemIsVisible (engine.GetAbsOrigin (ent), const_cast (STRING (ent->v.classname)))) + // too far from us ? + if ((pev->origin - origin).lengthSq () > radius) { + continue; + } + + if (ent == pickupItem) { + if (seesItem (origin, STRING (ent->v.classname))) { itemExists = true; - + } break; } } - if (itemExists) + if (itemExists) { return; - - else - { + } + else { m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; } } - ent = nullptr; - pickupItem = nullptr; - + edict_t *pickupItem = nullptr; PickupType pickupType = PICKUP_NONE; - - Vector pickupOrigin; - Vector entityOrigin; + Vector pickupPos = Vector::null (); m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; - while (!engine.IsNullEntity (ent = FIND_ENTITY_IN_SPHERE (ent, pev->origin, searchRadius))) - { - bool allowPickup = false; // assume can't use it until known otherwise + for (auto ent : intresting) { + bool allowPickup = false; // assume can't use it until known otherwise - if ((ent->v.effects & EF_NODRAW) || ent == m_itemIgnore) + if (ent == m_itemIgnore) { continue; // someone owns this weapon or it hasn't respawned yet + } + const Vector &origin = engine.getAbsPos (ent); + + // too far from us ? + if ((pev->origin - origin).lengthSq () > radius) { + continue; + } auto classname = STRING (ent->v.classname); auto model = STRING (ent->v.model) + 9; - entityOrigin = engine.GetAbsOrigin (ent); - // check if line of sight to object is not blocked (i.e. visible) - if (ItemIsVisible (entityOrigin, classname)) - { - if (strncmp ("hostage_entity", classname, 14) == 0) - { + if (seesItem (origin, classname)) { + if (strncmp ("hostage_entity", classname, 14) == 0) { allowPickup = true; pickupType = PICKUP_HOSTAGE; } - else if (strncmp ("weaponbox", classname, 9) == 0 && strcmp (model, "backpack.mdl") == 0) - { + else if (strncmp ("weaponbox", classname, 9) == 0 && strcmp (model, "backpack.mdl") == 0) { allowPickup = true; pickupType = PICKUP_DROPPED_C4; } - else if ((strncmp ("weaponbox", classname, 9) == 0 || strncmp ("armoury_entity", classname, 14) == 0 || strncmp ("csdm", classname, 4) == 0) && !m_isUsingGrenade) - { + else if ((strncmp ("weaponbox", classname, 9) == 0 || strncmp ("armoury_entity", classname, 14) == 0 || strncmp ("csdm", classname, 4) == 0) && !m_isUsingGrenade) { allowPickup = true; pickupType = PICKUP_WEAPON; } - else if (strncmp ("weapon_shield", classname, 13) == 0 && !m_isUsingGrenade) - { + else if (strncmp ("weapon_shield", classname, 13) == 0 && !m_isUsingGrenade) { allowPickup = true; pickupType = PICKUP_SHIELD; } - else if (strncmp ("item_thighpack", classname, 14) == 0 && m_team == CT && !m_hasDefuser) - { + else if (strncmp ("item_thighpack", classname, 14) == 0 && m_team == TEAM_COUNTER && !m_hasDefuser) { allowPickup = true; pickupType = PICKUP_DEFUSEKIT; } - else if (strncmp ("grenade", classname, 7) == 0 && strcmp (model, "c4.mdl") == 0) - { + else if (strncmp ("grenade", classname, 7) == 0 && strcmp (model, "c4.mdl") == 0) { allowPickup = true; pickupType = PICKUP_PLANTED_C4; } } - if (allowPickup) // if the bot found something it can pickup... - { - distance = (entityOrigin - pev->origin).GetLength (); - - // see if it's the closest item so far... - if (distance < minDistance) + // if the bot found something it can pickup... + if (allowPickup) { + if (pickupType == PICKUP_WEAPON) // found weapon on ground? { - if (pickupType == PICKUP_WEAPON) // found weapon on ground? - { - int weaponCarried = GetBestWeaponCarried (); - int secondaryWeaponCarried = GetBestSecondaryWeaponCarried (); + int weaponCarried = bestPrimaryCarried (); + int secondaryWeaponCarried = bestSecondaryCarried (); - if (secondaryWeaponCarried < 7 && (m_ammo[g_weaponSelect[secondaryWeaponCarried].id] > 0.3 * g_weaponDefs[g_weaponSelect[secondaryWeaponCarried].id].ammo1Max) && strcmp (model, "w_357ammobox.mdl") == 0) - allowPickup = false; - else if (!m_isVIP && weaponCarried >= 7 && (m_ammo[g_weaponSelect[weaponCarried].id] > 0.3 * g_weaponDefs[g_weaponSelect[weaponCarried].id].ammo1Max) && strncmp (model, "w_", 2) == 0) - { - if (strcmp (model, "w_9mmarclip.mdl") == 0 && !(weaponCarried == WEAPON_FAMAS || weaponCarried == WEAPON_AK47 || weaponCarried == WEAPON_M4A1 || weaponCarried == WEAPON_GALIL || weaponCarried == WEAPON_AUG || weaponCarried == WEAPON_SG552)) - allowPickup = false; - else if (strcmp (model, "w_shotbox.mdl") == 0 && !(weaponCarried == WEAPON_M3 || weaponCarried == WEAPON_XM1014)) - allowPickup = false; - else if (strcmp (model, "w_9mmclip.mdl") == 0 && !(weaponCarried == WEAPON_MP5 || weaponCarried == WEAPON_TMP || weaponCarried == WEAPON_P90 || weaponCarried == WEAPON_MAC10 || weaponCarried == WEAPON_UMP45)) - allowPickup = false; - else if (strcmp (model, "w_crossbow_clip.mdl") == 0 && !(weaponCarried == WEAPON_AWP || weaponCarried == WEAPON_G3SG1 || weaponCarried == WEAPON_SCOUT || weaponCarried == WEAPON_SG550)) - allowPickup = false; - else if (strcmp (model, "w_chainammo.mdl") == 0 && weaponCarried == WEAPON_M249) - allowPickup = false; - } - else if (m_isVIP || !RateGroundWeapon (ent)) - allowPickup = false; - else if (strcmp (model, "medkit.mdl") == 0 && pev->health >= 100.0f) - allowPickup = false; - else if ((strcmp (model, "kevlar.mdl") == 0 || strcmp (model, "battery.mdl") == 0) && pev->armorvalue >= 100.0f) // armor vest - allowPickup = false; - else if (strcmp (model, "flashbang.mdl") == 0 && (pev->weapons & (1 << WEAPON_FLASHBANG))) // concussion grenade - allowPickup = false; - else if (strcmp (model, "hegrenade.mdl") == 0 && (pev->weapons & (1 << WEAPON_EXPLOSIVE))) // explosive grenade - allowPickup = false; - else if (strcmp (model, "smokegrenade.mdl") == 0 && (pev->weapons & (1 << WEAPON_SMOKE))) // smoke grenade - allowPickup = false; + if (secondaryWeaponCarried < 7 && (m_ammo[g_weaponSelect[secondaryWeaponCarried].id] > 0.3 * g_weaponDefs[g_weaponSelect[secondaryWeaponCarried].id].ammo1Max) && strcmp (model, "w_357ammobox.mdl") == 0) { + allowPickup = false; } - else if (pickupType == PICKUP_SHIELD) // found a shield on ground? - { - if ((pev->weapons & (1 << WEAPON_ELITE)) || HasShield () || m_isVIP || (HasPrimaryWeapon () && !RateGroundWeapon (ent))) - allowPickup = false; - } - else if (m_team == TERRORIST) // terrorist team specific - { - if (pickupType == PICKUP_DROPPED_C4) - { - allowPickup = true; - m_destOrigin = entityOrigin; // ensure we reached dropped bomb + else if (!m_isVIP && weaponCarried >= 7 && (m_ammo[g_weaponSelect[weaponCarried].id] > 0.3 * g_weaponDefs[g_weaponSelect[weaponCarried].id].ammo1Max) && strncmp (model, "w_", 2) == 0) { + bool isSniperRifle = weaponCarried == WEAPON_AWP || weaponCarried == WEAPON_G3SG1 || weaponCarried == WEAPON_SG550; + bool isSubmachine = weaponCarried == WEAPON_MP5 || weaponCarried == WEAPON_TMP || weaponCarried == WEAPON_P90 || weaponCarried == WEAPON_MAC10 || weaponCarried == WEAPON_UMP45; + bool isShotgun = weaponCarried == WEAPON_M3; + bool isRifle = weaponCarried == WEAPON_FAMAS || weaponCarried == WEAPON_AK47 || weaponCarried == WEAPON_M4A1 || weaponCarried == WEAPON_GALIL || weaponCarried == WEAPON_AUG || weaponCarried == WEAPON_SG552; - ChatterMessage (Chatter_FoundC4); // play info about that - DeleteSearchNodes (); + if (strcmp (model, "w_9mmarclip.mdl") == 0 && !isRifle) { + allowPickup = false; } - else if (pickupType == PICKUP_HOSTAGE) - { - m_itemIgnore = ent; + else if (strcmp (model, "w_shotbox.mdl") == 0 && !isShotgun) { allowPickup = false; + } + else if (strcmp (model, "w_9mmclip.mdl") == 0 && !isSubmachine) { + allowPickup = false; + } + else if (strcmp (model, "w_crossbow_clip.mdl") == 0 && !isSniperRifle) { + allowPickup = false; + } + else if (strcmp (model, "w_chainammo.mdl") == 0 && weaponCarried != WEAPON_M249) { + allowPickup = false; + } + } + else if (m_isVIP || !rateGroundWeapon (ent)) { + allowPickup = false; + } + else if (strcmp (model, "medkit.mdl") == 0 && pev->health >= 100.0f) { + allowPickup = false; + } + else if ((strcmp (model, "kevlar.mdl") == 0 || strcmp (model, "battery.mdl") == 0) && pev->armorvalue >= 100.0f) { + allowPickup = false; + } + else if (strcmp (model, "flashbang.mdl") == 0 && (pev->weapons & (1 << WEAPON_FLASHBANG))) { + allowPickup = false; + } + else if (strcmp (model, "hegrenade.mdl") == 0 && (pev->weapons & (1 << WEAPON_EXPLOSIVE))) { + allowPickup = false; + } + else if (strcmp (model, "smokegrenade.mdl") == 0 && (pev->weapons & (1 << WEAPON_SMOKE))) { + allowPickup = false; + } + } + else if (pickupType == PICKUP_SHIELD) // found a shield on ground? + { + if ((pev->weapons & (1 << WEAPON_ELITE)) || hasShield () || m_isVIP || (hasPrimaryWeapon () && !rateGroundWeapon (ent))) { + allowPickup = false; + } + } + else if (m_team == TEAM_TERRORIST) // terrorist team specific + { + if (pickupType == PICKUP_DROPPED_C4) { + allowPickup = true; + m_destOrigin = origin; // ensure we reached dropped bomb - if (!m_defendHostage && m_difficulty >= 3 && Random.Int (0, 100) < 30 && m_timeCamping + 15.0f < engine.Time ()) - { - int index = FindDefendWaypoint (entityOrigin); + pushChatterMessage (CHATTER_FOUND_BOMB); // play info about that + clearSearchNodes (); + } + else if (pickupType == PICKUP_HOSTAGE) { + m_itemIgnore = ent; + allowPickup = false; - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (30.0f, 60.0f), true); // push camp task on to stack - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.Time () + Random.Float (3.0f, 6.0f), true); // push move command + if (!m_defendHostage && m_difficulty > 2 && rng.getInt (0, 100) < 30 && m_timeCamping + 15.0f < engine.timebase ()) { + int index = getDefendPoint (origin); - if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand) + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (30.0f, 60.0f), true); // push camp task on to stack + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.timebase () + rng.getFloat (3.0f, 6.0f), true); // push move command + + if (waypoints[index].vis.crouch <= waypoints[index].vis.stand) { + m_campButtons |= IN_DUCK; + } + else { + m_campButtons &= ~IN_DUCK; + } + m_defendHostage = true; + + pushChatterMessage (CHATTER_GOING_TO_GUARD_HOSTAGES); // play info about that + return; + } + } + else if (pickupType == PICKUP_PLANTED_C4) { + allowPickup = false; + + if (!m_defendedBomb) { + m_defendedBomb = true; + + int index = getDefendPoint (origin); + Path &path = waypoints[index]; + + float bombTimer = mp_c4timer.flt (); + float timeMidBlowup = g_timeBombPlanted + (bombTimer * 0.5f + bombTimer * 0.25f) - waypoints.calculateTravelTime (pev->maxspeed, pev->origin, path.origin); + + if (timeMidBlowup > engine.timebase ()) { + clearTask (TASK_MOVETOPOSITION); // remove any move tasks + + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, timeMidBlowup, true); // push camp task on to stack + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, timeMidBlowup, true); // push move command + + if (path.vis.crouch <= path.vis.stand) { m_campButtons |= IN_DUCK; - else - m_campButtons &= ~IN_DUCK; - - m_defendHostage = true; - - ChatterMessage (Chatter_GoingToGuardHostages); // play info about that - return; - } - } - else if (pickupType == PICKUP_PLANTED_C4) - { - allowPickup = false; - - if (!m_defendedBomb) - { - m_defendedBomb = true; - - int index = FindDefendWaypoint (entityOrigin); - Path *path = waypoints.GetPath (index); - - float bombTimer = mp_c4timer.GetFloat (); - float timeMidBlowup = g_timeBombPlanted + (bombTimer * 0.5f + bombTimer * 0.25f) - waypoints.GetTravelTime (pev->maxspeed, pev->origin, path->origin); - - if (timeMidBlowup > engine.Time ()) - { - RemoveCertainTask (TASK_MOVETOPOSITION); // remove any move tasks - - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, timeMidBlowup, true); // push camp task on to stack - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, timeMidBlowup, true); // push move command - - if (path->vis.crouch <= path->vis.stand) - m_campButtons |= IN_DUCK; - else - m_campButtons &= ~IN_DUCK; - - if (Random.Int (0, 100) < 90) - ChatterMessage (Chatter_DefendingBombSite); } - else - RadioMessage (Radio_ShesGonnaBlow); // issue an additional radio message + else { + m_campButtons &= ~IN_DUCK; + } + if (rng.getInt (0, 100) < 90) { + pushChatterMessage (CHATTER_DEFENDING_BOMBSITE); + } + } + else { + pushRadioMessage (RADIO_SHES_GONNA_BLOW); // issue an additional radio message } } } - else if (m_team == CT) - { - if (pickupType == PICKUP_HOSTAGE) - { - if (engine.IsNullEntity (ent) || ent->v.health <= 0) - allowPickup = false; // never pickup dead hostage - else for (int i = 0; i < engine.MaxClients (); i++) - { - if ((bot = bots.GetBot (i)) != nullptr && bot->m_notKilled) - { - for (int j = 0; j < MAX_HOSTAGES; j++) - { - if (bot->m_hostages[j] == ent) - { + } + else if (m_team == TEAM_COUNTER) { + if (pickupType == PICKUP_HOSTAGE) { + if (engine.isNullEntity (ent) || ent->v.health <= 0) { + allowPickup = false; // never pickup dead hostage + } + else + for (int i = 0; i < engine.maxClients (); i++) { + if ((bot = bots.getBot (i)) != nullptr && bot->m_notKilled) { + for (auto hostage : bot->m_hostages) { + if (hostage == ent) { allowPickup = false; break; } } } } - } - else if (pickupType == PICKUP_PLANTED_C4 && !OutOfBombTimer ()) - { - if (IsValidPlayer (m_enemy)) - { - allowPickup = false; - return; - } - - if (Random.Int (0, 100) < 90) - ChatterMessage (Chatter_FoundBombPlace); - - allowPickup = !IsBombDefusing (waypoints.GetBombPosition ()) || m_hasProgressBar; - pickupType = PICKUP_PLANTED_C4; - - if (!m_defendedBomb && !allowPickup) - { - m_defendedBomb = true; - - int index = FindDefendWaypoint (entityOrigin); - Path *path = waypoints.GetPath (index); - - float timeToExplode = g_timeBombPlanted + mp_c4timer.GetFloat () - waypoints.GetTravelTime (pev->maxspeed, pev->origin, path->origin); - - RemoveCertainTask (TASK_MOVETOPOSITION); // remove any move tasks - - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, timeToExplode, true); // push camp task on to stack - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, timeToExplode, true); // push move command - - if (path->vis.crouch <= path->vis.stand) - m_campButtons |= IN_DUCK; - else - m_campButtons &= ~IN_DUCK; - - if (Random.Int (0, 100) < 90) - ChatterMessage (Chatter_DefendingBombSite); - } - } - else if (pickupType == PICKUP_DROPPED_C4) - { - m_itemIgnore = ent; + } + else if (pickupType == PICKUP_PLANTED_C4) { + if (isPlayer (m_enemy)) { allowPickup = false; + return; + } - if (!m_defendedBomb && m_difficulty >= 2 && Random.Int (0, 100) < 75 && pev->health < 80) - { - int index = FindDefendWaypoint (entityOrigin); + if (isOutOfBombTimer ()) { + allowPickup = false; + return; + } - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (30.0f, 70.0f), true); // push camp task on to stack - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.Time () + Random.Float (10.0f, 30.0f), true); // push move command + if (rng.getInt (0, 100) < 90) { + pushChatterMessage (CHATTER_FOUND_BOMB_PLACE); + } - if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand) - m_campButtons |= IN_DUCK; - else - m_campButtons &= ~IN_DUCK; + allowPickup = !isBombDefusing (origin) || m_hasProgressBar; + pickupType = PICKUP_PLANTED_C4; - m_defendedBomb = true; + if (!m_defendedBomb && !allowPickup) { + m_defendedBomb = true; - ChatterMessage (Chatter_GoingToGuardDoppedBomb); // play info about that - return; + int index = getDefendPoint (origin); + Path &path = waypoints[index]; + + float timeToExplode = g_timeBombPlanted + mp_c4timer.flt () - waypoints.calculateTravelTime (pev->maxspeed, pev->origin, path.origin); + + clearTask (TASK_MOVETOPOSITION); // remove any move tasks + + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, timeToExplode, true); // push camp task on to stack + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, timeToExplode, true); // push move command + + if (path.vis.crouch <= path.vis.stand) { + m_campButtons |= IN_DUCK; + } + else { + m_campButtons &= ~IN_DUCK; + } + + if (rng.getInt (0, 100) < 90) { + pushChatterMessage (CHATTER_DEFENDING_BOMBSITE); } } } + else if (pickupType == PICKUP_DROPPED_C4) { + m_itemIgnore = ent; + allowPickup = false; - // if condition valid - if (allowPickup) - { - minDistance = distance; // update the minimum distance - pickupOrigin = entityOrigin; // remember location of entity - pickupItem = ent; // remember this entity + if (!m_defendedBomb && m_difficulty > 2 && rng.getInt (0, 100) < 75 && pev->health < 80) { + int index = getDefendPoint (origin); - m_pickupType = pickupType; + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (30.0f, 70.0f), true); // push camp task on to stack + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.timebase () + rng.getFloat (10.0f, 30.0f), true); // push move command + + if (waypoints[index].vis.crouch <= waypoints[index].vis.stand) { + m_campButtons |= IN_DUCK; + } + else { + m_campButtons &= ~IN_DUCK; + } + m_defendedBomb = true; + + pushChatterMessage (CHATTER_GOING_TO_GUARD_DROPPED_BOMB); // play info about that + return; + } } - else - pickupType = PICKUP_NONE; + } + + // if condition valid + if (allowPickup) { + pickupPos = origin; // remember location of entity + pickupItem = ent; // remember this entity + + m_pickupType = pickupType; + break; + } + else { + pickupType = PICKUP_NONE; } } } // end of the while loop - if (!engine.IsNullEntity (pickupItem)) - { - for (int i = 0; i < engine.MaxClients (); i++) - { - if ((bot = bots.GetBot (i)) != nullptr && bot->m_notKilled && bot->m_pickupItem == pickupItem) - { + if (!engine.isNullEntity (pickupItem)) { + for (int i = 0; i < engine.maxClients (); i++) { + if ((bot = bots.getBot (i)) != nullptr && bot->m_notKilled && bot->m_pickupItem == pickupItem) { m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; @@ -909,8 +848,8 @@ void Bot::FindItem (void) } } - if (pickupOrigin.z > EyePosition ().z + (m_pickupType == PICKUP_HOSTAGE ? 40.0f : 15.0f) || IsDeadlyDrop (pickupOrigin)) // check if item is too high to reach, check if getting the item would hurt bot - { + // check if item is too high to reach, check if getting the item would hurt bot + if (pickupPos.z > eyePos ().z + (m_pickupType == PICKUP_HOSTAGE ? 50.0f : 20.0f) || isDeadlyMove (pickupPos)) { m_itemIgnore = m_pickupItem; m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; @@ -921,228 +860,197 @@ void Bot::FindItem (void) } } -void Bot::GetCampDirection (Vector *dest) -{ +void Bot::getCampDir (Vector *dest) { // this function check if view on last enemy position is blocked - replace with better vector then // mostly used for getting a good camping direction vector if not camping on a camp waypoint TraceResult tr; - const Vector &src = EyePosition (); + const Vector &src = eyePos (); - engine.TestLine (src, *dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, *dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // check if the trace hit something... - if (tr.flFraction < 1.0f) - { - float length = (tr.vecEndPos - src).GetLengthSquared (); + if (tr.flFraction < 1.0f) { + float length = (tr.vecEndPos - src).lengthSq (); - if (length > 10000.0f) + if (length > 10000.0f) { return; + } + int enemyIndex = waypoints.getNearest (*dest); + int tempIndex = waypoints.getNearest (pev->origin); + + if (tempIndex == INVALID_WAYPOINT_INDEX || enemyIndex == INVALID_WAYPOINT_INDEX) { + return; + } float minDistance = 99999.0f; - float maxDistance = 99999.0f; - int enemyIndex = -1, tempIndex = -1; + int lookAtWaypoint = INVALID_WAYPOINT_INDEX; + Path &path = waypoints[tempIndex]; - // find nearest waypoint to bot and position - for (int i = 0; i < g_numWaypoints; i++) - { - float distance = (waypoints.GetPath (i)->origin - pev->origin).GetLengthSquared (); - - if (distance < minDistance) - { - minDistance = distance; - tempIndex = i; - } - distance = (waypoints.GetPath (i)->origin - *dest).GetLengthSquared (); - - if (distance < maxDistance) - { - maxDistance = distance; - enemyIndex = i; - } - } - - if (tempIndex == -1 || enemyIndex == -1) - return; - - minDistance = 99999.0f; - - int lookAtWaypoint = -1; - Path *path = waypoints.GetPath (tempIndex); - - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (path->index[i] == -1) + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (path.index[i] == INVALID_WAYPOINT_INDEX) { continue; + } + float distance = static_cast (waypoints.getPathDist (path.index[i], enemyIndex)); - float distance = static_cast (waypoints.GetPathDistance (path->index[i], enemyIndex)); - - if (distance < minDistance) - { + if (distance < minDistance) { minDistance = distance; - lookAtWaypoint = path->index[i]; + lookAtWaypoint = path.index[i]; } } - if (lookAtWaypoint != -1 && lookAtWaypoint < g_numWaypoints) - *dest = waypoints.GetPath (lookAtWaypoint)->origin; + + if (waypoints.exists (lookAtWaypoint)) { + *dest = waypoints[lookAtWaypoint].origin; + } } } -void Bot::EnableChatterIcon (bool show) -{ +void Bot::showChaterIcon (bool show) { // this function depending on show boolen, shows/remove chatter, icon, on the head of bot. - if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || yb_communication_type.GetInt () != 2) + if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || yb_communication_type.integer () != 2) { return; + } - auto SendBotVoiceMsg = [] (bool show, edict_t *ent, int ownId) - { - MESSAGE_BEGIN (MSG_ONE, engine.FindMessageId (NETMSG_BOTVOICE), nullptr, ent); // begin message - WRITE_BYTE (show); // switch on/off - WRITE_BYTE (ownId); - MESSAGE_END (); - + auto sendBotVoice = [](bool show, edict_t *ent, int ownId) { + MessageWriter (MSG_ONE, engine.getMessageId (NETMSG_BOTVOICE), Vector::null (), ent) // begin message + .writeByte (show) // switch on/off + .writeByte (ownId); }; - int ownId = GetIndex (); + int ownId = index (); - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) + if (!(client.flags & CF_USED) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) { continue; + } - if (!show && (client.iconFlags[ownId] & CF_ICON) && client.iconTimestamp[ownId] < engine.Time ()) - { - SendBotVoiceMsg (false, client.ent, ownId); + if (!show && (client.iconFlags[ownId] & CF_ICON) && client.iconTimestamp[ownId] < engine.timebase ()) { + sendBotVoice (false, client.ent, ownId); client.iconTimestamp[ownId] = 0.0f; client.iconFlags[ownId] &= ~CF_ICON; } - else if (show && !(client.iconFlags[ownId] & CF_ICON)) - SendBotVoiceMsg (true, client.ent, ownId); + else if (show && !(client.iconFlags[ownId] & CF_ICON)) { + sendBotVoice (true, client.ent, ownId); + } } } -void Bot::InstantChatterMessage (int type) -{ +void Bot::instantChatter (int type) { // this function sends instant chatter messages. - if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || yb_communication_type.GetInt () != 2 || g_chatterFactory[type].IsEmpty ()) + if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || yb_communication_type.integer () != 2 || g_chatterFactory[type].empty ()) { return; + } // delay only report team - if (type == Radio_ReportTeam) - { - if (m_timeRepotingInDelay < engine.Time ()) + if (type == RADIO_REPORT_TEAM) { + if (m_timeRepotingInDelay < engine.timebase ()) { return; - - m_timeRepotingInDelay = engine.Time () + Random.Float (30.0f, 60.0f); + } + m_timeRepotingInDelay = engine.timebase () + rng.getFloat (30.0f, 60.0f); } - auto playbackSound = g_chatterFactory[type].GetRandomElement (); - auto painSound = g_chatterFactory[Chatter_DiePain].GetRandomElement (); + auto playbackSound = g_chatterFactory[type].random (); + auto painSound = g_chatterFactory[CHATTER_PAIN_DIED].random (); - if (m_notKilled) - EnableChatterIcon (true); + if (m_notKilled) { + showChaterIcon (true); + } + MessageWriter msg; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) + if (!(client.flags & CF_USED) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) { continue; + } + msg.start (MSG_ONE, engine.getMessageId (NETMSG_SENDAUDIO), Vector::null (), client.ent) // begin message + .writeByte (index ()); - MESSAGE_BEGIN (MSG_ONE, engine.FindMessageId (NETMSG_SENDAUDIO), nullptr, client.ent); // begin message - WRITE_BYTE (GetIndex ()); - - if (pev->deadflag & DEAD_DYING) - { - client.iconTimestamp[GetIndex ()] = engine.Time () + painSound.duration; - WRITE_STRING (FormatBuffer ("%s/%s.wav", yb_chatter_path.GetString (), painSound.name.GetBuffer ())); - } - else if (!(pev->deadflag & DEAD_DEAD)) - { - client.iconTimestamp[GetIndex ()] = engine.Time () + playbackSound.duration; - WRITE_STRING (FormatBuffer ("%s/%s.wav", yb_chatter_path.GetString (), playbackSound.name.GetBuffer ())); - } - WRITE_SHORT (m_voicePitch); - MESSAGE_END (); - - client.iconFlags[GetIndex ()] |= CF_ICON; + if (pev->deadflag & DEAD_DYING) { + client.iconTimestamp[index ()] = engine.timebase () + painSound.duration; + msg.writeString (format ("%s/%s.wav", yb_chatter_path.str (), painSound.name.chars ())); + } + else if (!(pev->deadflag & DEAD_DEAD)) { + client.iconTimestamp[index ()] = engine.timebase () + playbackSound.duration; + msg.writeString (format ("%s/%s.wav", yb_chatter_path.str (), playbackSound.name.chars ())); + } + msg.writeShort (m_voicePitch).end (); + client.iconFlags[index ()] |= CF_ICON; } } -void Bot::RadioMessage (int message) -{ +void Bot::pushRadioMessage (int message) { // this function inserts the radio message into the message queue - if (yb_communication_type.GetInt () == 0 || m_numFriendsLeft == 0) + if (yb_communication_type.integer () == 0 || m_numFriendsLeft == 0) { return; - - if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || g_chatterFactory[message].IsEmpty () || yb_communication_type.GetInt () != 2) + } + if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || g_chatterFactory[message].empty () || yb_communication_type.integer () != 2) { m_forceRadio = true; // use radio instead voice - else + } + else { m_forceRadio = false; - + } m_radioSelect = message; - PushMessageQueue (GAME_MSG_RADIO); + pushMsgQueue (GAME_MSG_RADIO); } -void Bot::ChatterMessage (int message) -{ +void Bot::pushChatterMessage (int message) { // this function inserts the voice message into the message queue (mostly same as above) - if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || yb_communication_type.GetInt () != 2 || g_chatterFactory[message].IsEmpty () || m_numFriendsLeft == 0) + if (!(g_gameFlags & GAME_SUPPORT_BOT_VOICE) || yb_communication_type.integer () != 2 || g_chatterFactory[message].empty () || m_numFriendsLeft == 0) { return; + } + bool sendMessage = false; - bool shouldExecute = false; + float &messageTimer = m_chatterTimes[message]; + float &messageRepeat = g_chatterFactory[message][0].repeat; - if (m_chatterTimes[message] < engine.Time () || m_chatterTimes[message] == 99999.0f) - { - if (m_chatterTimes[message] != 99999.0f) - m_chatterTimes[message] = engine.Time () + g_chatterFactory[message][0].repeat; - - shouldExecute = true; + if (messageTimer < engine.timebase () || cr::fequal (messageTimer, MAX_CHATTER_REPEAT)) { + if (!cr::fequal (messageTimer, MAX_CHATTER_REPEAT) && !cr::fequal (messageRepeat, MAX_CHATTER_REPEAT)) { + messageTimer = engine.timebase () + messageRepeat; + } + sendMessage = true; } - if (!shouldExecute) + if (!sendMessage) { return; - + } m_radioSelect = message; - PushMessageQueue (GAME_MSG_RADIO); + pushMsgQueue (GAME_MSG_RADIO); } -void Bot::CheckMessageQueue (void) -{ +void Bot::checkMsgQueue (void) { // this function checks and executes pending messages // no new message? - if (m_actMessageIndex == m_pushMessageIndex) + if (m_actMessageIndex == m_pushMessageIndex) { return; - + } // get message from stack - int state = GetMessageQueue (); + int state = getMsgQueue (); // nothing to do? - if (state == GAME_MSG_NONE || (state == GAME_MSG_RADIO && (g_gameFlags & GAME_CSDM_FFA))) + if (state == GAME_MSG_NONE || (state == GAME_MSG_RADIO && (g_gameFlags & GAME_CSDM_FFA))) { return; + } - switch (state) - { + switch (state) { case GAME_MSG_PURCHASE: // general buy message // buy weapon - if (m_nextBuyTime > engine.Time ()) - { + if (m_nextBuyTime > engine.timebase ()) { // keep sending message - PushMessageQueue (GAME_MSG_PURCHASE); + pushMsgQueue (GAME_MSG_PURCHASE); return; } - if (!m_inBuyZone || (g_gameFlags & GAME_CSDM)) - { + if (!m_inBuyZone || (g_gameFlags & GAME_CSDM)) { m_buyPending = true; m_buyingFinished = true; @@ -1150,187 +1058,188 @@ void Bot::CheckMessageQueue (void) } m_buyPending = false; - m_nextBuyTime = engine.Time () + Random.Float (0.5f, 1.3f); + m_nextBuyTime = engine.timebase () + rng.getFloat (0.5f, 1.3f); // if bot buying is off then no need to buy - if (!yb_botbuy.GetBool ()) + if (!yb_botbuy.boolean ()) { m_buyState = BUYSTATE_FINISHED; + } // if fun-mode no need to buy - if (yb_jasonmode.GetBool ()) - { + if (yb_jasonmode.boolean ()) { m_buyState = BUYSTATE_FINISHED; - SelectWeaponByName ("weapon_knife"); + selectWeaponByName ("weapon_knife"); } // prevent vip from buying - if (IsPlayerVIP (GetEntity ())) - { - m_isVIP = true; + if (m_isVIP) { m_buyState = BUYSTATE_FINISHED; m_pathType = SEARCH_PATH_FASTEST; } // prevent terrorists from buying on es maps - if ((g_mapType & MAP_ES) && m_team == TERRORIST) + if ((g_mapFlags & MAP_ES) && m_team == TEAM_TERRORIST) { m_buyState = 6; - - // prevent teams from buying on fun maps - if (g_mapType & (MAP_KA | MAP_FY)) - { - m_buyState = BUYSTATE_FINISHED; - - if (g_mapType & MAP_KA) - yb_jasonmode.SetInt (1); } - if (m_buyState > BUYSTATE_FINISHED - 1) - { + // prevent teams from buying on fun maps + if (g_mapFlags & (MAP_KA | MAP_FY)) { + m_buyState = BUYSTATE_FINISHED; + + if (g_mapFlags & MAP_KA) { + yb_jasonmode.set (1); + } + } + + if (m_buyState > BUYSTATE_FINISHED - 1) { m_buyingFinished = true; return; } - PushMessageQueue (GAME_MSG_NONE); - PurchaseWeapons (); + pushMsgQueue (GAME_MSG_NONE); + buyStuff (); break; - case GAME_MSG_RADIO: // general radio message issued - // if last bot radio command (global) happened just a second ago, delay response - if (g_lastRadioTime[m_team] + 1.0f < engine.Time ()) - { + case GAME_MSG_RADIO: + // if last bot radio command (global) happened just a 3 seconds ago, delay response + if (g_lastRadioTime[m_team] + 3.0f < engine.timebase ()) { // if same message like previous just do a yes/no - if (m_radioSelect != Radio_Affirmative && m_radioSelect != Radio_Negative) - { - if (m_radioSelect == g_lastRadio[m_team] && g_lastRadioTime[m_team] + 1.5f > engine.Time ()) + if (m_radioSelect != RADIO_AFFIRMATIVE && m_radioSelect != RADIO_NEGATIVE) { + if (m_radioSelect == g_lastRadio[m_team] && g_lastRadioTime[m_team] + 1.5f > engine.timebase ()) m_radioSelect = -1; - else - { - if (m_radioSelect != Radio_ReportingIn) + else { + if (m_radioSelect != RADIO_REPORTING_IN) { g_lastRadio[m_team] = m_radioSelect; - else + } + else { g_lastRadio[m_team] = -1; + } - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot != nullptr) - { - if (pev != bot->pev && bot->m_team == m_team) - { + if (bot != nullptr) { + if (pev != bot->pev && bot->m_team == m_team) { bot->m_radioOrder = m_radioSelect; - bot->m_radioEntity = GetEntity (); + bot->m_radioEntity = ent (); } } } } } - if (m_radioSelect == Radio_ReportingIn) - { - switch (GetTaskId ()) - { + if (m_radioSelect == RADIO_REPORTING_IN) { + switch (taskId ()) { case TASK_NORMAL: - if (GetTask ()->data != -1 && Random.Int (0, 100) < 70) - { - Path *path = waypoints.GetPath (GetTask ()->data); + if (task ()->data != INVALID_WAYPOINT_INDEX && rng.getInt (0, 100) < 70) { + Path &path = waypoints[task ()->data]; - if (path->flags & FLAG_GOAL) - { - if ((g_mapType & MAP_DE) && m_team == TERRORIST && m_hasC4) - InstantChatterMessage (Chatter_GoingToPlantBomb); - else - InstantChatterMessage (Chatter_Nothing); + if (path.flags & FLAG_GOAL) { + if ((g_mapFlags & MAP_DE) && m_team == TEAM_TERRORIST && m_hasC4) { + instantChatter (CHATTER_GOING_TO_PLANT_BOMB); + } + else { + instantChatter (CHATTER_NOTHING); + } + } + else if (path.flags & FLAG_RESCUE) { + instantChatter (CHATTER_RESCUING_HOSTAGES); + } + else if ((path.flags & FLAG_CAMP) && rng.getInt (0, 100) > 15) { + instantChatter (CHATTER_GOING_TO_CAMP); + } + else { + instantChatter (CHATTER_HEARD_NOISE); } - else if (path->flags & FLAG_RESCUE) - InstantChatterMessage (Chatter_RescuingHostages); - else if ((path->flags & FLAG_CAMP) && Random.Int (0, 100) > 15) - InstantChatterMessage (Chatter_GoingToCamp); - else - InstantChatterMessage (Chatter_HearSomething); } - else if (Random.Int (0, 100) < 40) - InstantChatterMessage (Chatter_ReportingIn); - + else if (rng.getInt (0, 100) < 30) { + instantChatter (CHATTER_REPORTING_IN); + } break; case TASK_MOVETOPOSITION: - InstantChatterMessage (Chatter_GoingToCamp); + if (rng.getInt (0, 100) < 20) { + instantChatter (CHATTER_GOING_TO_CAMP); + } break; case TASK_CAMP: - if (Random.Int (0, 100) < 40) - { - - if (g_bombPlanted && m_team == TERRORIST) - InstantChatterMessage (Chatter_GuardDroppedC4); - else if (m_inVIPZone && m_team == TERRORIST) - InstantChatterMessage (Chatter_GuardingVipSafety); - else - InstantChatterMessage (Chatter_Camp); + if (rng.getInt (0, 100) < 40) { + if (g_bombPlanted && m_team == TEAM_TERRORIST) { + instantChatter (CHATTER_GUARDING_DROPPED_BOMB); + } + else if (m_inVIPZone && m_team == TEAM_TERRORIST) { + instantChatter (CHATTER_GUARDING_VIP_SAFETY); + } + else { + instantChatter (CHATTER_CAMP); + } } break; case TASK_PLANTBOMB: - InstantChatterMessage (Chatter_PlantingC4); + instantChatter (CHATTER_PLANTING_BOMB); break; case TASK_DEFUSEBOMB: - InstantChatterMessage (Chatter_DefusingC4); + instantChatter (CHATTER_DEFUSING_BOMB); break; case TASK_ATTACK: - InstantChatterMessage (Chatter_InCombat); + instantChatter (CHATTER_IN_COMBAT); break; case TASK_HIDE: case TASK_SEEKCOVER: - InstantChatterMessage (Chatter_SeeksEnemy); + instantChatter (CHATTER_SEEK_ENEMY); break; default: - InstantChatterMessage (Chatter_Nothing); + if (rng.getInt (0, 100) < 50) { + instantChatter (CHATTER_NOTHING); + } break; } } - if ((m_radioSelect != Radio_ReportingIn && m_forceRadio) || yb_communication_type.GetInt () != 2 || g_chatterFactory[m_radioSelect].IsEmpty () || !(g_gameFlags & GAME_SUPPORT_BOT_VOICE)) - { - if (m_radioSelect < Radio_GoGoGo) - engine.IssueBotCommand (GetEntity (), "radio1"); - else if (m_radioSelect < Radio_Affirmative) - { - m_radioSelect -= Radio_GoGoGo - 1; - engine.IssueBotCommand (GetEntity (), "radio2"); - } - else - { - m_radioSelect -= Radio_Affirmative - 1; - engine.IssueBotCommand (GetEntity (), "radio3"); - } + if (m_radioSelect != -1) { + if ((m_radioSelect != RADIO_REPORTING_IN && m_forceRadio) || yb_communication_type.integer () != 2 || g_chatterFactory[m_radioSelect].empty () || !(g_gameFlags & GAME_SUPPORT_BOT_VOICE)) { + if (m_radioSelect < RADIO_GO_GO_GO) { + engine.execBotCmd (ent (), "radio1"); + } + else if (m_radioSelect < RADIO_AFFIRMATIVE) { + m_radioSelect -= RADIO_GO_GO_GO - 1; + engine.execBotCmd (ent (), "radio2"); + } + else { + m_radioSelect -= RADIO_AFFIRMATIVE - 1; + engine.execBotCmd (ent (), "radio3"); + } - // select correct menu item for this radio message - engine.IssueBotCommand (GetEntity (), "menuselect %d", m_radioSelect); + // select correct menu item for this radio message + engine.execBotCmd (ent (), "menuselect %d", m_radioSelect); + } + else if (m_radioSelect != RADIO_REPORTING_IN) { + instantChatter (m_radioSelect); + } } - else if (m_radioSelect != -1 && m_radioSelect != Radio_ReportingIn) - InstantChatterMessage (m_radioSelect); - m_forceRadio = false; // reset radio to voice - g_lastRadioTime[m_team] = engine.Time (); // store last radio usage + g_lastRadioTime[m_team] = engine.timebase (); // store last radio usage + } + else { + pushMsgQueue (GAME_MSG_RADIO); } - else - PushMessageQueue (GAME_MSG_RADIO); break; // team independent saytext case GAME_MSG_SAY_CMD: - SayText (m_tempStrings); + say (m_tempStrings.chars ()); break; // team dependent saytext case GAME_MSG_SAY_TEAM_MSG: - TeamSayText (m_tempStrings); + sayTeam (m_tempStrings.chars ()); break; default: @@ -1338,207 +1247,211 @@ void Bot::CheckMessageQueue (void) } } -bool Bot::IsRestricted (int weaponIndex) -{ +bool Bot::isWeaponRestricted (int weaponIndex) { // this function checks for weapon restrictions. - if (IsNullString (yb_restricted_weapons.GetString ())) - return IsRestrictedAMX (weaponIndex); // no banned weapons + if (isEmptyStr (yb_restricted_weapons.str ())) { + return isWeaponRestrictedAMX (weaponIndex); // no banned weapons + } + auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";"); - Array bannedWeapons = String (yb_restricted_weapons.GetString ()).Split (';'); - - FOR_EACH_AE (bannedWeapons, i) - { - const char *banned = STRING (GetWeaponReturn (true, nullptr, weaponIndex)); + for (auto &ban : bannedWeapons) { + const char *banned = STRING (getWeaponData (true, nullptr, weaponIndex)); // check is this weapon is banned - if (strncmp (bannedWeapons[i].GetBuffer (), banned, bannedWeapons[i].GetLength ()) == 0) + if (strncmp (ban.chars (), banned, ban.length ()) == 0) { return true; + } } - return IsRestrictedAMX (weaponIndex); + return isWeaponRestrictedAMX (weaponIndex); } -bool Bot::IsRestrictedAMX (int weaponIndex) -{ +bool Bot::isWeaponRestrictedAMX (int weaponIndex) { // this function checks restriction set by AMX Mod, this function code is courtesy of KWo. // check for weapon restrictions - if ((1 << weaponIndex) & (WEAPON_PRIMARY | WEAPON_SECONDARY | WEAPON_SHIELD)) - { - const char *restrictedWeapons = CVAR_GET_STRING ("amx_restrweapons"); + if ((1 << weaponIndex) & (WEAPON_PRIMARY | WEAPON_SECONDARY | WEAPON_SHIELD)) { + const char *restrictedWeapons = g_engfuncs.pfnCVarGetString ("amx_restrweapons"); - if (IsNullString (restrictedWeapons)) + if (isEmptyStr (restrictedWeapons)) { return false; - + } int indices[] = {4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11}; // find the weapon index int index = indices[weaponIndex - 1]; // validate index range - if (index < 0 || index >= static_cast (strlen (restrictedWeapons))) + if (index < 0 || index >= static_cast (strlen (restrictedWeapons))) { return false; - + } return restrictedWeapons[index] != '0'; } - else // check for equipment restrictions - { - const char *restrictedEquipment = CVAR_GET_STRING ("amx_restrequipammo"); - if (IsNullString (restrictedEquipment)) + // check for equipment restrictions + else { + const char *restrictedEquipment = g_engfuncs.pfnCVarGetString ("amx_restrequipammo"); + + if (isEmptyStr (restrictedEquipment)) { return false; - + } int indices[] = {-1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 0, 1, 5}; // find the weapon index int index = indices[weaponIndex - 1]; // validate index range - if (index < 0 || index >= static_cast (strlen (restrictedEquipment))) + if (index < 0 || index >= static_cast (strlen (restrictedEquipment))) { return false; - + } return restrictedEquipment[index] != '0'; } } -bool Bot::IsMorePowerfulWeaponCanBeBought (void) -{ +bool Bot::canReplaceWeapon (void) { // this function determines currently owned primary weapon, and checks if bot has // enough money to buy more powerful weapon. // if bot is not rich enough or non-standard weapon mode enabled return false - if (g_weaponSelect[25].teamStandard != 1 || m_moneyAmount < 4000) + if (g_weaponSelect[25].teamStandard != 1 || m_moneyAmount < 4000) { return false; + } - if (!IsNullString (yb_restricted_weapons.GetString ())) - { - Array bannedWeapons = String (yb_restricted_weapons.GetString ()).Split (';'); + if (!isEmptyStr (yb_restricted_weapons.str ())) { + auto bannedWeapons = String (yb_restricted_weapons.str ()).split (";"); // check if its banned - FOR_EACH_AE (bannedWeapons, i) - { - if (m_currentWeapon == GetWeaponReturn (false, bannedWeapons[i].GetBuffer ())) + for (auto &ban : bannedWeapons) { + if (m_currentWeapon == getWeaponData (false, ban.chars ())) { return true; + } } } - if (m_currentWeapon == WEAPON_SCOUT && m_moneyAmount > 5000) + if (m_currentWeapon == WEAPON_SCOUT && m_moneyAmount > 5000) { return true; - else if (m_currentWeapon == WEAPON_MP5 && m_moneyAmount > 6000) + } + else if (m_currentWeapon == WEAPON_MP5 && m_moneyAmount > 6000) { return true; - else if ((m_currentWeapon == WEAPON_M3 || m_currentWeapon == WEAPON_XM1014) && m_moneyAmount > 4000) + } + else if ((m_currentWeapon == WEAPON_M3 || m_currentWeapon == WEAPON_XM1014) && m_moneyAmount > 4000) { return true; - + } return false; } -int Bot::PickBestFromRandom(int *random, int count, int moneySave) -{ +int Bot::pickBestWeapon (int *vec, int count, int moneySave) { // this function picks best available weapon from random choice with money save - if (yb_best_weapon_picker_type.GetInt () == 1) - { + if (yb_best_weapon_picker_type.integer () == 1) { + + auto pick = [] (const float factor) -> float { + return (static_cast (((unsigned int &) factor >> 23) & 0xff) - 127) * 0.3010299956639812f; + }; + float buyFactor = (m_moneyAmount - static_cast (moneySave)) / (16000.0f - static_cast (moneySave)) * 3.0f; - if (buyFactor < 1.0f) + if (buyFactor < 1.0f) { buyFactor = 1.0f; + } // swap array values - for (int *begin = random, *end = random + count - 1; begin < end; ++begin, --end) - { - int swap = *end; - - *end = *begin; - *begin = swap; + for (int *begin = vec, *end = vec + count - 1; begin < end; ++begin, --end) { + cr::swap (*end, *begin); } - return random[static_cast (static_cast (count - 1) * log10f (Random.Float (1, powf (10.0f, buyFactor))) / buyFactor + 0.5f)]; + return vec[static_cast (static_cast (count - 1) * pick (rng.getFloat (1.0f, cr::powf (10.0f, buyFactor))) / buyFactor + 0.5f)]; } int chance = 95; // high skilled bots almost always prefer best weapon - if (m_difficulty < 4) - { - if (m_personality == PERSONALITY_NORMAL) + if (m_difficulty < 4) { + if (m_personality == PERSONALITY_NORMAL) { chance = 50; - else if (m_personality == PERSONALITY_CAREFUL) + } + else if (m_personality == PERSONALITY_CAREFUL) { chance = 75; + } } - for (int i = 0; i < count; i++) - { - auto weapon = &g_weaponSelect[random[i]]; + for (int i = 0; i < count; i++) { + auto weapon = &g_weaponSelect[vec[i]]; // if wea have enough money for weapon buy it - if (weapon->price + moneySave < m_moneyAmount + Random.Int (50, 200) && Random.Int (0, 100) < chance) - return random[i]; + if (weapon->price + moneySave < m_moneyAmount + rng.getInt (50, 200) && rng.getInt (0, 100) < chance) { + return vec[i]; + } } - return random[Random.Int (0, count - 1)]; + return vec[rng.getInt (0, count - 1)]; } -void Bot::PurchaseWeapons (void) -{ +void Bot::buyStuff (void) { // this function does all the work in selecting correct buy menus for most weapons/items WeaponSelect *selectedWeapon = nullptr; - m_nextBuyTime = engine.Time () + Random.Float (0.3f, 0.5f); + m_nextBuyTime = engine.timebase (); - int count = 0, foundWeapons = 0; + if (!m_ignoreBuyDelay) { + m_nextBuyTime += rng.getFloat (0.3f, 0.5f); + } + + int count = 0, weaponCount = 0; int choices[NUM_WEAPONS]; // select the priority tab for this personality int *ptr = g_weaponPrefs[m_personality] + NUM_WEAPONS; bool isPistolMode = g_weaponSelect[25].teamStandard == -1 && g_weaponSelect[3].teamStandard == 2; - bool teamEcoValid = bots.IsEcoValid (m_team); + bool teamEcoValid = bots.checkTeamEco (m_team); // do this, because xash engine is not capable to run all the features goldsrc, but we have cs 1.6 on it, so buy table must be the same bool isOldGame = (g_gameFlags & GAME_LEGACY) && !(g_gameFlags & GAME_XASH_ENGINE); - switch (m_buyState) - { + switch (m_buyState) { case BUYSTATE_PRIMARY_WEAPON: // if no primary weapon and bot has some money, buy a primary weapon - if ((!HasShield () && !HasPrimaryWeapon () && teamEcoValid) || (teamEcoValid && IsMorePowerfulWeaponCanBeBought ())) - { + if ((!hasShield () && !hasPrimaryWeapon () && teamEcoValid) || (teamEcoValid && canReplaceWeapon ())) { int moneySave = 0; - do - { + do { bool ignoreWeapon = false; ptr--; - InternalAssert (*ptr > -1); - InternalAssert (*ptr < NUM_WEAPONS); + assert (*ptr > -1); + assert (*ptr < NUM_WEAPONS); selectedWeapon = &g_weaponSelect[*ptr]; count++; - if (selectedWeapon->buyGroup == 1) + if (selectedWeapon->buyGroup == 1) { continue; + } // weapon available for every team? - if ((g_mapType & MAP_AS) && selectedWeapon->teamAS != 2 && selectedWeapon->teamAS != m_team) + if ((g_mapFlags & MAP_AS) && selectedWeapon->teamAS != 2 && selectedWeapon->teamAS != m_team) { continue; + } // ignore weapon if this weapon not supported by currently running cs version... - if (isOldGame && selectedWeapon->buySelect == -1) + if (isOldGame && selectedWeapon->buySelect == -1) { continue; + } // ignore weapon if this weapon is not targeted to out team.... - if (selectedWeapon->teamStandard != 2 && selectedWeapon->teamStandard != m_team) + if (selectedWeapon->teamStandard != 2 && selectedWeapon->teamStandard != m_team) { continue; + } // ignore weapon if this weapon is restricted - if (IsRestricted (selectedWeapon->id)) + if (isWeaponRestricted (selectedWeapon->id)) { continue; + } int *limit = g_botBuyEconomyTable; int prostock = 0; // filter out weapons with bot economics - switch (m_personality) - { + switch (m_personality) { case PERSONALITY_RUSHER: prostock = limit[ECO_PROSTOCK_RUSHER]; break; @@ -1552,496 +1465,473 @@ void Bot::PurchaseWeapons (void) break; } - if (m_team == CT) - { - switch (selectedWeapon->id) - { + if (m_team == TEAM_COUNTER) { + switch (selectedWeapon->id) { case WEAPON_TMP: case WEAPON_UMP45: case WEAPON_P90: case WEAPON_MP5: - if (m_moneyAmount > limit[ECO_SMG_GT_CT] + prostock) + if (m_moneyAmount > limit[ECO_SMG_GT_CT] + prostock) { ignoreWeapon = true; + } break; } - if (selectedWeapon->id == WEAPON_SHIELD && m_moneyAmount > limit[ECO_SHIELDGUN_GT]) + if (selectedWeapon->id == WEAPON_SHIELD && m_moneyAmount > limit[ECO_SHIELDGUN_GT]) { ignoreWeapon = true; + } } - else if (m_team == TERRORIST) - { - switch (selectedWeapon->id) - { + else if (m_team == TEAM_TERRORIST) { + switch (selectedWeapon->id) { case WEAPON_UMP45: case WEAPON_MAC10: case WEAPON_P90: case WEAPON_MP5: case WEAPON_SCOUT: - if (m_moneyAmount > limit[ECO_SMG_GT_TE] + prostock) + if (m_moneyAmount > limit[ECO_SMG_GT_TE] + prostock) { ignoreWeapon = true; + } break; } } - switch (selectedWeapon->id) - { + switch (selectedWeapon->id) { case WEAPON_XM1014: case WEAPON_M3: - if (m_moneyAmount < limit[ECO_SHOTGUN_LT]) + if (m_moneyAmount < limit[ECO_SHOTGUN_LT]) { ignoreWeapon = true; + } - if (m_moneyAmount >= limit[ECO_SHOTGUN_GT]) + if (m_moneyAmount >= limit[ECO_SHOTGUN_GT]) { ignoreWeapon = false; + } break; } - switch (selectedWeapon->id) - { + switch (selectedWeapon->id) { case WEAPON_SG550: case WEAPON_G3SG1: - case WEAPON_AWP: + case WEAPON_AWP: case WEAPON_M249: - if (m_moneyAmount < limit[ECO_HEAVY_LT]) + if (m_moneyAmount < limit[ECO_HEAVY_LT]) { ignoreWeapon = true; - if (m_moneyAmount >= limit[ECO_HEAVY_GT]) - ignoreWeapon = false; + } + if (m_moneyAmount >= limit[ECO_HEAVY_GT]) { + ignoreWeapon = false; + } break; } - if (ignoreWeapon && g_weaponSelect[25].teamStandard == 1 && yb_economics_rounds.GetBool ()) + if (ignoreWeapon && g_weaponSelect[25].teamStandard == 1 && yb_economics_rounds.boolean ()) { continue; + } // save money for grenade for example? - moneySave = Random.Int (500, 1000); + moneySave = rng.getInt (500, 1000); - if (bots.GetLastWinner () == m_team) + if (bots.getLastWinner () == m_team) { moneySave = 0; + } - if (selectedWeapon->price <= (m_moneyAmount - moneySave)) - choices[foundWeapons++] = *ptr; + if (selectedWeapon->price <= (m_moneyAmount - moneySave)) { + choices[weaponCount++] = *ptr; + } - } while (count < NUM_WEAPONS && foundWeapons < 4); + } while (count < NUM_WEAPONS && weaponCount < 4); // found a desired weapon? - if (foundWeapons > 0) - { + if (weaponCount > 0) { int chosenWeapon; // choose randomly from the best ones... - if (foundWeapons > 1) - chosenWeapon = PickBestFromRandom (choices, foundWeapons, moneySave); - else - chosenWeapon = choices[foundWeapons - 1]; - + if (weaponCount > 1) { + chosenWeapon = pickBestWeapon (choices, weaponCount, moneySave); + } + else { + chosenWeapon = choices[weaponCount - 1]; + } selectedWeapon = &g_weaponSelect[chosenWeapon]; } - else + else { selectedWeapon = nullptr; + } - if (selectedWeapon != nullptr) - { - engine.IssueBotCommand (GetEntity (), "buy;menuselect %d", selectedWeapon->buyGroup); + if (selectedWeapon != nullptr) { + engine.execBotCmd (ent (), "buy;menuselect %d", selectedWeapon->buyGroup); - if (isOldGame) - engine.IssueBotCommand(GetEntity (), "menuselect %d", selectedWeapon->buySelect); - else // SteamCS buy menu is different from the old one - { - if (m_team == TERRORIST) - engine.IssueBotCommand(GetEntity (), "menuselect %d", selectedWeapon->newBuySelectT); - else - engine.IssueBotCommand (GetEntity (), "menuselect %d", selectedWeapon->newBuySelectCT); + if (isOldGame) { + engine.execBotCmd (ent (), "menuselect %d", selectedWeapon->buySelect); + } + else { + if (m_team == TEAM_TERRORIST) { + engine.execBotCmd (ent (), "menuselect %d", selectedWeapon->newBuySelectT); + } + else { + engine.execBotCmd (ent (), "menuselect %d", selectedWeapon->newBuySelectCT); + } } } } - else if (HasPrimaryWeapon () && !HasShield ()) - { + else if (hasPrimaryWeapon () && !hasShield ()) { m_reloadState = RELOAD_PRIMARY; break; - } - else if ((HasSecondaryWeapon () && !HasShield ()) || HasShield()) - { + } + else if ((hasSecondaryWeapon () && !hasShield ()) || hasShield ()) { m_reloadState = RELOAD_SECONDARY; break; - } + } + break; case BUYSTATE_ARMOR_VESTHELM: // if armor is damaged and bot has some money, buy some armor - if (pev->armorvalue < Random.Int (50, 80) && (isPistolMode || (teamEcoValid && HasPrimaryWeapon ()))) - { + if (pev->armorvalue < rng.getInt (50, 80) && (isPistolMode || (teamEcoValid && hasPrimaryWeapon ()))) { // if bot is rich, buy kevlar + helmet, else buy a single kevlar - if (m_moneyAmount > 1500 && !IsRestricted (WEAPON_ARMORHELM)) - engine.IssueBotCommand (GetEntity (), "buyequip;menuselect 2"); - else if (!IsRestricted (WEAPON_ARMOR)) - engine.IssueBotCommand (GetEntity (), "buyequip;menuselect 1"); + if (m_moneyAmount > 1500 && !isWeaponRestricted (WEAPON_ARMORHELM)) { + engine.execBotCmd (ent (), "buyequip;menuselect 2"); + } + else if (!isWeaponRestricted (WEAPON_ARMOR)) { + engine.execBotCmd (ent (), "buyequip;menuselect 1"); + } } break; case BUYSTATE_SECONDARY_WEAPON: // if bot has still some money, buy a better secondary weapon - if (isPistolMode || (HasPrimaryWeapon () && (pev->weapons & ((1 << WEAPON_USP) | (1 << WEAPON_GLOCK))) && m_moneyAmount > Random.Int (7500, 9000))) - { - do - { + if (isPistolMode || (hasPrimaryWeapon () && (pev->weapons & ((1 << WEAPON_USP) | (1 << WEAPON_GLOCK))) && m_moneyAmount > rng.getInt (7500, 9000))) { + do { ptr--; - InternalAssert (*ptr > -1); - InternalAssert (*ptr < NUM_WEAPONS); + assert (*ptr > -1); + assert (*ptr < NUM_WEAPONS); selectedWeapon = &g_weaponSelect[*ptr]; count++; - if (selectedWeapon->buyGroup != 1) + if (selectedWeapon->buyGroup != 1) { continue; + } // ignore weapon if this weapon is restricted - if (IsRestricted (selectedWeapon->id)) + if (isWeaponRestricted (selectedWeapon->id)) { continue; + } // weapon available for every team? - if ((g_mapType & MAP_AS) && selectedWeapon->teamAS != 2 && selectedWeapon->teamAS != m_team) + if ((g_mapFlags & MAP_AS) && selectedWeapon->teamAS != 2 && selectedWeapon->teamAS != m_team) { continue; + } - if (isOldGame && selectedWeapon->buySelect == -1) + if (isOldGame && selectedWeapon->buySelect == -1) { continue; + } - if (selectedWeapon->teamStandard != 2 && selectedWeapon->teamStandard != m_team) + if (selectedWeapon->teamStandard != 2 && selectedWeapon->teamStandard != m_team) { continue; + } - if (selectedWeapon->price <= (m_moneyAmount - Random.Int (100, 200))) - choices[foundWeapons++] = *ptr; + if (selectedWeapon->price <= (m_moneyAmount - rng.getInt (100, 200))) { + choices[weaponCount++] = *ptr; + } - } while (count < NUM_WEAPONS && foundWeapons < 4); + } while (count < NUM_WEAPONS && weaponCount < 4); // found a desired weapon? - if (foundWeapons > 0) - { + if (weaponCount > 0) { int chosenWeapon; // choose randomly from the best ones... - if (foundWeapons > 1) - chosenWeapon = PickBestFromRandom (choices, foundWeapons, Random.Int (100, 200)); - else - chosenWeapon = choices[foundWeapons - 1]; - + if (weaponCount > 1) { + chosenWeapon = pickBestWeapon (choices, weaponCount, rng.getInt (100, 200)); + } + else { + chosenWeapon = choices[weaponCount - 1]; + } selectedWeapon = &g_weaponSelect[chosenWeapon]; } - else + else { selectedWeapon = nullptr; + } - if (selectedWeapon != nullptr) - { - engine.IssueBotCommand (GetEntity (), "buy;menuselect %d", selectedWeapon->buyGroup); + if (selectedWeapon != nullptr) { + engine.execBotCmd (ent (), "buy;menuselect %d", selectedWeapon->buyGroup); - if (isOldGame) - engine.IssueBotCommand (GetEntity (), "menuselect %d", selectedWeapon->buySelect); - - else // steam cs buy menu is different from old one - { - if (m_team == TERRORIST) - engine.IssueBotCommand (GetEntity (), "menuselect %d", selectedWeapon->newBuySelectT); - else - engine.IssueBotCommand (GetEntity (), "menuselect %d", selectedWeapon->newBuySelectCT); + if (isOldGame) { + engine.execBotCmd (ent (), "menuselect %d", selectedWeapon->buySelect); + } + else { + if (m_team == TEAM_TERRORIST) { + engine.execBotCmd (ent (), "menuselect %d", selectedWeapon->newBuySelectT); + } + else { + engine.execBotCmd (ent (), "menuselect %d", selectedWeapon->newBuySelectCT); + } } } } break; case BUYSTATE_GRENADES: // if bot has still some money, choose if bot should buy a grenade or not - if (Random.Int (1, 100) < g_grenadeBuyPrecent[0] && m_moneyAmount >= 400 && !IsRestricted (WEAPON_EXPLOSIVE)) - { + if (rng.getInt (1, 100) < g_grenadeBuyPrecent[0] && m_moneyAmount >= 400 && !isWeaponRestricted (WEAPON_EXPLOSIVE)) { // buy a he grenade - engine.IssueBotCommand (GetEntity (), "buyequip"); - engine.IssueBotCommand (GetEntity (), "menuselect 4"); + engine.execBotCmd (ent (), "buyequip"); + engine.execBotCmd (ent (), "menuselect 4"); } - if (Random.Int (1, 100) < g_grenadeBuyPrecent[1] && m_moneyAmount >= 300 && teamEcoValid && !IsRestricted (WEAPON_FLASHBANG)) - { + if (rng.getInt (1, 100) < g_grenadeBuyPrecent[1] && m_moneyAmount >= 300 && teamEcoValid && !isWeaponRestricted (WEAPON_FLASHBANG)) { // buy a concussion grenade, i.e., 'flashbang' - engine.IssueBotCommand (GetEntity (), "buyequip"); - engine.IssueBotCommand (GetEntity (), "menuselect 3"); + engine.execBotCmd (ent (), "buyequip"); + engine.execBotCmd (ent (), "menuselect 3"); } - if (Random.Int (1, 100) < g_grenadeBuyPrecent[2] && m_moneyAmount >= 400 && teamEcoValid && !IsRestricted (WEAPON_SMOKE)) - { + if (rng.getInt (1, 100) < g_grenadeBuyPrecent[2] && m_moneyAmount >= 400 && teamEcoValid && !isWeaponRestricted (WEAPON_SMOKE)) { // buy a smoke grenade - engine.IssueBotCommand (GetEntity (), "buyequip"); - engine.IssueBotCommand (GetEntity (), "menuselect 5"); + engine.execBotCmd (ent (), "buyequip"); + engine.execBotCmd (ent (), "menuselect 5"); } break; case BUYSTATE_DEFUSER: // if bot is CT and we're on a bomb map, randomly buy the defuse kit - if ((g_mapType & MAP_DE) && m_team == CT && Random.Int (1, 100) < 80 && m_moneyAmount > 200 && !IsRestricted (WEAPON_DEFUSER)) - { - if (isOldGame) - engine.IssueBotCommand (GetEntity (), "buyequip;menuselect 6"); - else - engine.IssueBotCommand (GetEntity (), "defuser"); // use alias in SteamCS + if ((g_mapFlags & MAP_DE) && m_team == TEAM_COUNTER && rng.getInt (1, 100) < 80 && m_moneyAmount > 200 && !isWeaponRestricted (WEAPON_DEFUSER)) { + if (isOldGame) { + engine.execBotCmd (ent (), "buyequip;menuselect 6"); + } + else { + engine.execBotCmd (ent (), "defuser"); // use alias in steamcs + } } break; case BUYSTATE_AMMO: // buy enough primary & secondary ammo (do not check for money here) - for (int i = 0; i <= 5; i++) - engine.IssueBotCommand (GetEntity (), "buyammo%d", Random.Int (1, 2)); // simulate human + for (int i = 0; i <= 5; i++) { + engine.execBotCmd (ent (), "buyammo%d", rng.getInt (1, 2)); // simulate human + } // buy enough secondary ammo - if (HasPrimaryWeapon ()) - engine.IssueBotCommand (GetEntity (), "buy;menuselect 7"); + if (hasPrimaryWeapon ()) { + engine.execBotCmd (ent (), "buy;menuselect 7"); + } // buy enough primary ammo - engine.IssueBotCommand (GetEntity (), "buy;menuselect 6"); + engine.execBotCmd (ent (), "buy;menuselect 6"); // try to reload secondary weapon - if (m_reloadState != RELOAD_PRIMARY) + if (m_reloadState != RELOAD_PRIMARY) { m_reloadState = RELOAD_SECONDARY; - + } + m_ignoreBuyDelay = false; break; } m_buyState++; - PushMessageQueue (GAME_MSG_PURCHASE); + pushMsgQueue (GAME_MSG_PURCHASE); } -TaskItem *MaxDesire (TaskItem *first, TaskItem *second) -{ - // this function returns the behavior having the higher activation level. - - if (first->desire > second->desire) - return first; - - return second; -} - -TaskItem *SubsumeDesire (TaskItem *first, TaskItem *second) -{ - // this function returns the first behavior if its activation level is anything higher than zero. - - if (first->desire > 0) - return first; - - return second; -} - -TaskItem *ThresholdDesire (TaskItem *first, float threshold, float desire) -{ - // this function returns the input behavior if it's activation level exceeds the threshold, or some default - // behavior otherwise. - - if (first->desire < threshold) - first->desire = desire; - - return first; -} - -float HysteresisDesire (float cur, float min, float max, float old) -{ - // this function clamp the inputs to be the last known value outside the [min, max] range. - - if (cur <= min || cur >= max) - old = cur; - - return old; -} - -void Bot::UpdateEmotions (void) -{ +void Bot::updateEmotions (void) { // slowly increase/decrease dynamic emotions back to their base level - if (m_nextEmotionUpdate > engine.Time ()) + if (m_nextEmotionUpdate > engine.timebase ()) { return; + } - if (m_agressionLevel > m_baseAgressionLevel) + if (m_agressionLevel > m_baseAgressionLevel) { m_agressionLevel -= 0.10f; - else + } + else { m_agressionLevel += 0.10f; + } - if (m_fearLevel > m_baseFearLevel) + if (m_fearLevel > m_baseFearLevel) { m_fearLevel -= 0.05f; - else + } + else { m_fearLevel += 0.05f; + } - if (m_agressionLevel < 0.0f) + if (m_agressionLevel < 0.0f) { m_agressionLevel = 0.0f; + } - if (m_fearLevel < 0.0f) + if (m_fearLevel < 0.0f) { m_fearLevel = 0.0f; - - m_nextEmotionUpdate = engine.Time () + 1.0f; + } + m_nextEmotionUpdate = engine.timebase () + 1.0f; } -void Bot::SetConditionsOverride (void) -{ - if (m_currentWeapon != WEAPON_KNIFE && m_difficulty > 3 && ((m_aimFlags & AIM_ENEMY) || (m_states & (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY)) || (GetTaskId () == TASK_SEEKCOVER && (m_isReloading || m_isVIP))) && !yb_jasonmode.GetBool () && GetTaskId () != TASK_CAMP && !IsOnLadder ()) - { - m_moveToGoal = false; // don't move to goal - m_navTimeset = engine.Time (); +void Bot::overrideConditions (void) { - if (IsValidPlayer (m_enemy)) - CombatFight (); + if (m_currentWeapon != WEAPON_KNIFE && m_difficulty > 2 && ((m_aimFlags & AIM_ENEMY) || (m_states & STATE_SEEING_ENEMY)) && !yb_jasonmode.boolean () && taskId () != TASK_CAMP && taskId () != TASK_SEEKCOVER && !isOnLadder ()) { + m_moveToGoal = false; // don't move to goal + m_navTimeset = engine.timebase (); + + if (isPlayer (m_enemy)) { + attackMovement (); + } } // check if we need to escape from bomb - if ((g_mapType & MAP_DE) && g_bombPlanted && m_notKilled && GetTaskId () != TASK_ESCAPEFROMBOMB && GetTaskId () != TASK_CAMP && OutOfBombTimer ()) - { - TaskComplete (); // complete current task + if ((g_mapFlags & MAP_DE) && g_bombPlanted && m_notKilled && taskId () != TASK_ESCAPEFROMBOMB && taskId () != TASK_CAMP && isOutOfBombTimer ()) { + completeTask (); // complete current task - // then start escape from bomb immidiate - PushTask (TASK_ESCAPEFROMBOMB, TASKPRI_ESCAPEFROMBOMB, -1, 0.0f, true); + // then start escape from bomb immediate + startTask (TASK_ESCAPEFROMBOMB, TASKPRI_ESCAPEFROMBOMB, INVALID_WAYPOINT_INDEX, 0.0f, true); } // special handling, if we have a knife in our hands - if (m_currentWeapon == WEAPON_KNIFE && IsValidPlayer (m_enemy) && (GetTaskId () != TASK_MOVETOPOSITION || GetTask ()->desire != TASKPRI_HIDE)) - { - float length = (pev->origin - m_enemy->v.origin).GetLength2D (); + if ((g_timeRoundStart + 6.0f > engine.timebase () || !hasAnyWeapons ()) && m_currentWeapon == WEAPON_KNIFE && isPlayer (m_enemy) && (taskId () != TASK_MOVETOPOSITION || task ()->desire != TASKPRI_HIDE)) { + float length = (pev->origin - m_enemy->v.origin).length2D (); - // do waypoint movement if enemy is not reacheable with a knife - if (length > 100.0f && (m_states & STATE_SEEING_ENEMY)) - { - int nearestToEnemyPoint = waypoints.FindNearest (m_enemy->v.origin); + // do waypoint movement if enemy is not reachable with a knife + if (length > 100.0f && (m_states & STATE_SEEING_ENEMY)) { + int nearestToEnemyPoint = waypoints.getNearest (m_enemy->v.origin); - if (nearestToEnemyPoint != -1 && nearestToEnemyPoint != m_currentWaypointIndex && fabsf (waypoints.GetPath (nearestToEnemyPoint)->origin.z - m_enemy->v.origin.z) < 16.0f) - { - PushTask (TASK_MOVETOPOSITION, TASKPRI_HIDE, nearestToEnemyPoint, engine.Time () + Random.Float (5.0f, 10.0f), true); + if (nearestToEnemyPoint != INVALID_WAYPOINT_INDEX && nearestToEnemyPoint != m_currentWaypointIndex && cr::abs (waypoints[nearestToEnemyPoint].origin.z - m_enemy->v.origin.z) < 16.0f) { + startTask (TASK_MOVETOPOSITION, TASKPRI_HIDE, nearestToEnemyPoint, engine.timebase () + rng.getFloat (5.0f, 10.0f), true); m_isEnemyReachable = false; m_enemy = nullptr; - m_enemyIgnoreTimer = engine.Time () + ((length / pev->maxspeed) * 0.5f); + m_enemyIgnoreTimer = engine.timebase () + length / pev->maxspeed * 0.5f; } } } + + // special handling for sniping + if (usesSniper () && (m_states & (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY)) && m_sniperStopTime > engine.timebase () && taskId () != TASK_SEEKCOVER) { + m_moveSpeed = 0.0f; + m_strafeSpeed = 0.0f; + m_navTimeset = engine.timebase (); + } } -void Bot::SetConditions (void) -{ +void Bot::setConditions (void) { // this function carried out each frame. does all of the sensing, calculates emotions and finally sets the desired // action after applying all of the Filters m_aimFlags = 0; - UpdateEmotions (); + updateEmotions (); // does bot see an enemy? - if (LookupEnemy ()) + if (lookupEnemies ()) { m_states |= STATE_SEEING_ENEMY; - else - { + } + else { m_states &= ~STATE_SEEING_ENEMY; m_enemy = nullptr; } // did bot just kill an enemy? - if (!engine.IsNullEntity (m_lastVictim)) - { - if (engine.GetTeam (m_lastVictim) != m_team) - { + if (!engine.isNullEntity (m_lastVictim)) { + if (engine.getTeam (m_lastVictim) != m_team) { // add some aggression because we just killed somebody m_agressionLevel += 0.1f; - if (m_agressionLevel > 1.0f) + if (m_agressionLevel > 1.0f) { m_agressionLevel = 1.0f; + } - if (Random.Int (1, 100) < 10) - ChatMessage (CHAT_KILLING); + if (rng.getInt (1, 100) < 10) { + pushChatMessage (CHAT_KILLING); + } - if (Random.Int (1, 100) < 10) - RadioMessage (Radio_EnemyDown); - else - { - if ((m_lastVictim->v.weapons & (1 << WEAPON_AWP)) || (m_lastVictim->v.weapons & (1 << WEAPON_SCOUT)) || (m_lastVictim->v.weapons & (1 << WEAPON_G3SG1)) || (m_lastVictim->v.weapons & (1 << WEAPON_SG550))) - ChatterMessage (Chatter_SniperKilled); - else - { - switch (GetNearbyEnemiesNearPosition (pev->origin, 99999.0f)) - { + if (rng.getInt (1, 100) < 10) { + pushRadioMessage (RADIO_ENEMY_DOWN); + } + else { + if ((m_lastVictim->v.weapons & (1 << WEAPON_AWP)) || (m_lastVictim->v.weapons & (1 << WEAPON_SCOUT)) || (m_lastVictim->v.weapons & (1 << WEAPON_G3SG1)) || (m_lastVictim->v.weapons & (1 << WEAPON_SG550))) { + pushChatterMessage (CHATTER_SNIPER_KILLED); + } + else { + switch (numEnemiesNear (pev->origin, 99999.0f)) { case 0: - if (Random.Int (0, 100) < 50) - ChatterMessage (Chatter_NoEnemiesLeft); - else - ChatterMessage (Chatter_EnemyDown); + if (rng.getInt (0, 100) < 50) { + pushChatterMessage (CHATTER_NO_ENEMIES_LEFT); + } + else { + pushChatterMessage (CHATTER_ENEMY_DOWN); + } break; case 1: - ChatterMessage (Chatter_OneEnemyLeft); + pushChatterMessage (CHATTER_ONE_ENEMY_LEFT); break; case 2: - ChatterMessage (Chatter_TwoEnemiesLeft); + pushChatterMessage (CHATTER_TWO_ENEMIES_LEFT); break; case 3: - ChatterMessage (Chatter_ThreeEnemiesLeft); + pushChatterMessage (CHATTER_THREE_ENEMIES_LEFT); break; default: - ChatterMessage (Chatter_EnemyDown); + pushChatterMessage (CHATTER_ENEMY_DOWN); } } } // if no more enemies found AND bomb planted, switch to knife to get to bombplace faster - if (m_team == CT && m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0 && g_bombPlanted) - { - SelectWeaponByName ("weapon_knife"); - m_plantedBombWptIndex = FindPlantedBomb (); + if (m_team == TEAM_COUNTER && m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0 && g_bombPlanted) { + selectWeaponByName ("weapon_knife"); + m_plantedBombWptIndex = locatePlantedC4 (); - if (IsPointOccupied (m_plantedBombWptIndex)) - InstantChatterMessage (Chatter_BombSiteSecured); + if (isOccupiedPoint (m_plantedBombWptIndex)) { + instantChatter (CHATTER_BOMB_SITE_SECURED); + } } } - else - { - ChatMessage (CHAT_TEAMKILL, true); - ChatterMessage (Chatter_TeamKill); + else { + pushChatMessage (CHAT_TEAMKILL, true); + pushChatterMessage (CHATTER_TEAM_ATTACK); } m_lastVictim = nullptr; } // check if our current enemy is still valid - if (!engine.IsNullEntity (m_lastEnemy)) - { - if (!IsAlive (m_lastEnemy) && m_shootAtDeadTime < engine.Time ()) - { - m_lastEnemyOrigin.Zero (); + if (!engine.isNullEntity (m_lastEnemy)) { + if (!isAlive (m_lastEnemy) && m_shootAtDeadTime < engine.timebase ()) { + m_lastEnemyOrigin.nullify (); m_lastEnemy = nullptr; } } - else - { - m_lastEnemyOrigin.Zero (); + else { + m_lastEnemyOrigin.nullify (); m_lastEnemy = nullptr; } // don't listen if seeing enemy, just checked for sounds or being blinded (because its inhuman) - if (!yb_ignore_enemies.GetBool () && m_soundUpdateTime < engine.Time () && m_blindTime < engine.Time () && m_seeEnemyTime + 1.0f < engine.Time ()) - { - ReactOnSound (); - m_soundUpdateTime = engine.Time () + 0.25f; + if (!yb_ignore_enemies.boolean () && m_soundUpdateTime < engine.timebase () && m_blindTime < engine.timebase () && m_seeEnemyTime + 1.0f < engine.timebase ()) { + processHearing (); + m_soundUpdateTime = engine.timebase () + 0.25f; } - else if (m_heardSoundTime < engine.Time ()) + else if (m_heardSoundTime < engine.timebase ()) { m_states &= ~STATE_HEARING_ENEMY; + } - if (engine.IsNullEntity (m_enemy) && !engine.IsNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.IsZero ()) - { + if (engine.isNullEntity (m_enemy) && !engine.isNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.empty ()) { m_aimFlags |= AIM_PREDICT_PATH; - if (EntityIsVisible (m_lastEnemyOrigin)) + if (seesEntity (m_lastEnemyOrigin, true)) { m_aimFlags |= AIM_LAST_ENEMY; + } + } + + // check for grenades depending on difficulty + if (rng.getInt (0, 100) < m_difficulty * 25) { + checkGrenadesThrow (); } - CheckGrenadeThrow (); // check if there are items needing to be used/collected - if (m_itemCheckTime < engine.Time () || !engine.IsNullEntity (m_pickupItem)) - { - m_itemCheckTime = engine.Time () + 0.4f; - FindItem (); + if (m_itemCheckTime < engine.timebase () || !engine.isNullEntity (m_pickupItem)) { + m_itemCheckTime = engine.timebase () + 0.5f; + processPickups (); } - ApplyTaskFilters (); + filterTasks (); } -void Bot::ApplyTaskFilters (void) -{ +void Bot::filterTasks (void) { // initialize & calculate the desire for all actions based on distances, emotions and other stuff - GetTask (); + task (); float tempFear = m_fearLevel; float tempAgression = m_agressionLevel; @@ -2049,106 +1939,113 @@ void Bot::ApplyTaskFilters (void) // decrease fear if teammates near int friendlyNum = 0; - if (!m_lastEnemyOrigin.IsZero ()) - friendlyNum = GetNearbyFriendsNearPosition (pev->origin, 500.0f) - GetNearbyEnemiesNearPosition (m_lastEnemyOrigin, 500.0f); + if (!m_lastEnemyOrigin.empty ()) { + friendlyNum = numFriendsNear (pev->origin, 500.0f) - numEnemiesNear (m_lastEnemyOrigin, 500.0f); + } - if (friendlyNum > 0) + if (friendlyNum > 0) { tempFear = tempFear * 0.5f; + } // increase/decrease fear/aggression if bot uses a sniping weapon to be more careful - if (UsesSniper ()) - { - tempFear = tempFear * 1.5f; - tempAgression = tempAgression * 0.5f; + if (usesSniper ()) { + tempFear = tempFear * 1.2f; + tempAgression = tempAgression * 0.6f; } // bot found some item to use? - if (!engine.IsNullEntity (m_pickupItem) && GetTaskId () != TASK_ESCAPEFROMBOMB) - { + if (!engine.isNullEntity (m_pickupItem) && taskId () != TASK_ESCAPEFROMBOMB) { m_states |= STATE_PICKUP_ITEM; - if (m_pickupType == PICKUP_BUTTON) + if (m_pickupType == PICKUP_BUTTON) { g_taskFilters[TASK_PICKUPITEM].desire = 50.0f; // always pickup button - else - { - float distance = (500.0f - (engine.GetAbsOrigin (m_pickupItem) - pev->origin).GetLength ()) * 0.2f; + } + else { + float distance = (500.0f - (engine.getAbsPos (m_pickupItem) - pev->origin).length ()) * 0.2f; - if (distance > 50.0f) + if (distance > 50.0f) { distance = 50.0f; - + } g_taskFilters[TASK_PICKUPITEM].desire = distance; } } - else - { + else { m_states &= ~STATE_PICKUP_ITEM; g_taskFilters[TASK_PICKUPITEM].desire = 0.0f; } // calculate desire to attack - if ((m_states & STATE_SEEING_ENEMY) && ReactOnEnemy ()) + if ((m_states & STATE_SEEING_ENEMY) && reactOnEnemy ()) { g_taskFilters[TASK_ATTACK].desire = TASKPRI_ATTACK; - else + } + else { g_taskFilters[TASK_ATTACK].desire = 0.0f; + } + float &seekCoverDesire = g_taskFilters[TASK_SEEKCOVER].desire; + float &huntEnemyDesire = g_taskFilters[TASK_HUNTENEMY].desire; + float &blindedDesire = g_taskFilters[TASK_BLINDED].desire; // calculate desires to seek cover or hunt - if (IsValidPlayer (m_lastEnemy) && !m_lastEnemyOrigin.IsZero () && !m_hasC4) - { - float distance = (m_lastEnemyOrigin - pev->origin).GetLength (); + if (isPlayer (m_lastEnemy) && !m_lastEnemyOrigin.empty () && !m_hasC4) { + float retreatLevel = (100.0f - (pev->health > 50.0f ? 100.0f : pev->health)) * tempFear; // retreat level depends on bot health - // retreat level depends on bot health - float retreatLevel = (100.0f - (pev->health > 100.0f ? 100.0f : pev->health)) * tempFear; - float timeSeen = m_seeEnemyTime - engine.Time (); - float timeHeard = m_heardSoundTime - engine.Time (); - float ratio = 0.0f; + if (m_numEnemiesLeft > m_numFriendsLeft * 0.5f && m_retreatTime < engine.timebase () && m_seeEnemyTime - rng.getFloat (2.0f, 4.0f) < engine.timebase ()) { - if (timeSeen > timeHeard) - { - timeSeen += 10.0f; - ratio = timeSeen * 0.1f; + float timeSeen = m_seeEnemyTime - engine.timebase (); + float timeHeard = m_heardSoundTime - engine.timebase (); + float ratio = 0.0f; + + m_retreatTime = engine.timebase () + rng.getFloat (3.0f, 15.0f); + + if (timeSeen > timeHeard) { + timeSeen += 10.0f; + ratio = timeSeen * 0.1f; + } + else { + timeHeard += 10.0f; + ratio = timeHeard * 0.1f; + } + bool lowAmmo = m_ammoInClip[m_currentWeapon] < getMaxClip (m_currentWeapon) * 0.18f; + + if (g_bombPlanted || m_isStuck || m_currentWeapon == WEAPON_KNIFE) { + ratio /= 3.0f; // reduce the seek cover desire if bomb is planted + } + else if (m_isVIP || m_isReloading || (lowAmmo && usesSniper ())) { + ratio *= 2.0f; // triple the seek cover desire if bot is VIP or reloading + } + else { + ratio /= 2.0f; // reduce seek cover otherwise + } + seekCoverDesire = retreatLevel * ratio; } - else - { - timeHeard += 10.0f; - ratio = timeHeard * 0.1f; + else { + seekCoverDesire = 0.0f; } - - if (g_bombPlanted || m_isStuck || m_currentWeapon == WEAPON_KNIFE) - ratio /= 3.0f; // reduce the seek cover desire if bomb is planted - else if (m_isVIP || m_isReloading) - ratio *= 3.0f; // triple the seek cover desire if bot is VIP or reloading - - if (distance > 500.0f) - g_taskFilters[TASK_SEEKCOVER].desire = retreatLevel * ratio; - + // if half of the round is over, allow hunting - // FIXME: it probably should be also team/map dependant - if (GetTaskId () != TASK_ESCAPEFROMBOMB && engine.IsNullEntity (m_enemy) && g_timeRoundMid < engine.Time () && !m_isUsingGrenade && m_currentWaypointIndex != waypoints.FindNearest (m_lastEnemyOrigin) && m_personality != PERSONALITY_CAREFUL) - { - float desireLevel = 4096.0f - ((1.0f - tempAgression) * distance); + if (taskId () != TASK_ESCAPEFROMBOMB && engine.isNullEntity (m_enemy) && g_timeRoundMid < engine.timebase () && !m_isUsingGrenade && m_currentWaypointIndex != waypoints.getNearest (m_lastEnemyOrigin) && m_personality != PERSONALITY_CAREFUL && !yb_ignore_enemies.boolean ()) { + float desireLevel = 4096.0f - ((1.0f - tempAgression) * (m_lastEnemyOrigin - pev->origin).length ()); desireLevel = (100.0f * desireLevel) / 4096.0f; desireLevel -= retreatLevel; - if (desireLevel > 89.0f) + if (desireLevel > 89.0f) { desireLevel = 89.0f; - - g_taskFilters[TASK_HUNTENEMY].desire = desireLevel; + } + huntEnemyDesire = desireLevel; + } + else { + huntEnemyDesire = 0.0f; } - else - g_taskFilters[TASK_HUNTENEMY].desire = 0.0f; } - else - { - g_taskFilters[TASK_SEEKCOVER].desire = 0.0f; - g_taskFilters[TASK_HUNTENEMY].desire = 0.0f; + else { + huntEnemyDesire = 0.0f; + seekCoverDesire = 0.0f; } // blinded behavior - if (m_blindTime > engine.Time ()) - g_taskFilters[TASK_BLINDED].desire = TASKPRI_BLINDED; - else - g_taskFilters[TASK_BLINDED].desire = 0.0f; + blindedDesire = m_blindTime > engine.timebase () ? TASKPRI_BLINDED : 0.0f; + // now we've initialized all the desires go through the hard work // of filtering all actions against each other to pick the most @@ -2162,282 +2059,288 @@ void Bot::ApplyTaskFilters (void) // utility i wrote so there could still be some weird behaviors, it's // hard to check them all out. - m_oldCombatDesire = HysteresisDesire (g_taskFilters[TASK_ATTACK].desire, 40.0f, 90.0f, m_oldCombatDesire); + // this function returns the behavior having the higher activation level + auto maxDesire = [] (Task *first, Task *second) { + if (first->desire > second->desire) { + return first; + } + return second; + }; + + // this function returns the first behavior if its activation level is anything higher than zero + auto subsumeDesire = [] (Task *first, Task *second) { + if (first->desire > 0) { + return first; + } + return second; + }; + + // this function returns the input behavior if it's activation level exceeds the threshold, or some default behavior otherwise + auto thresholdDesire = [] (Task *first, float threshold, float desire) { + if (first->desire < threshold) { + first->desire = desire; + } + return first; + }; + + // this function clamp the inputs to be the last known value outside the [min, max] range. + auto hysteresisDesire = [] (float cur, float min, float max, float old) { + if (cur <= min || cur >= max) { + old = cur; + } + return old; + }; + + m_oldCombatDesire = hysteresisDesire (g_taskFilters[TASK_ATTACK].desire, 40.0f, 90.0f, m_oldCombatDesire); g_taskFilters[TASK_ATTACK].desire = m_oldCombatDesire; - TaskItem *taskOffensive = &g_taskFilters[TASK_ATTACK]; - TaskItem *taskPickup = &g_taskFilters[TASK_PICKUPITEM]; + auto offensive = &g_taskFilters[TASK_ATTACK]; + auto pickup = &g_taskFilters[TASK_PICKUPITEM]; // calc survive (cover/hide) - TaskItem *taskSurvive = ThresholdDesire (&g_taskFilters[TASK_SEEKCOVER], 40.0f, 0.0f); - taskSurvive = SubsumeDesire (&g_taskFilters[TASK_HIDE], taskSurvive); + auto survive = thresholdDesire (&g_taskFilters[TASK_SEEKCOVER], 40.0f, 0.0f); + survive = subsumeDesire (&g_taskFilters[TASK_HIDE], survive); - TaskItem *def = ThresholdDesire (&g_taskFilters[TASK_HUNTENEMY], 41.0f, 0.0f); // don't allow hunting if desires 60< - taskOffensive = SubsumeDesire (taskOffensive, taskPickup); // if offensive task, don't allow picking up stuff + auto def = thresholdDesire (&g_taskFilters[TASK_HUNTENEMY], 41.0f, 0.0f); // don't allow hunting if desires 60< + offensive = subsumeDesire (offensive, pickup); // if offensive task, don't allow picking up stuff - TaskItem *taskSub = MaxDesire (taskOffensive, def); // default normal & careful tasks against offensive actions - TaskItem *final = SubsumeDesire (&g_taskFilters[TASK_BLINDED], MaxDesire (taskSurvive, taskSub)); // reason about fleeing instead + auto sub = maxDesire (offensive, def); // default normal & careful tasks against offensive actions + auto final = subsumeDesire (&g_taskFilters[TASK_BLINDED], maxDesire (survive, sub)); // reason about fleeing instead - if (!m_tasks.IsEmpty ()) - { - final = MaxDesire (final, GetTask ()); - PushTask (final->id, final->desire, final->data, final->time, final->resume); // push the final behavior in our task stack to carry out + if (!m_tasks.empty ()) { + final = maxDesire (final, task ()); + startTask (final->id, final->desire, final->data, final->time, final->resume); // push the final behavior in our task stack to carry out } } -void Bot::ResetTasks (void) -{ +void Bot::clearTasks (void) { // this function resets bot tasks stack, by removing all entries from the stack. - m_tasks.RemoveAll (); + m_tasks.clear (); } -void Bot::PushTask (TaskID id, float desire, int data, float time, bool resume) -{ - if (!m_tasks.IsEmpty ()) - { - TaskItem &item = m_tasks.Last (); - - if (item.id == id) - { - item.desire = desire; +void Bot::startTask (TaskID id, float desire, int data, float time, bool resume) { + for (auto &task : m_tasks) { + if (task.id == id) { + if (!cr::fequal (task.desire, desire)) { + task.desire = desire; + } return; } } - TaskItem item; + m_tasks.push ({ id, desire, data, time, resume }); - item.id = id; - item.desire = desire; - item.data = data; - item.time = time; - item.resume = resume; + clearSearchNodes (); + ignoreCollision (); - m_tasks.Push (item); - - DeleteSearchNodes (); - IgnoreCollisionShortly (); - - int taskId = GetTaskId (); + int tid = taskId (); // leader bot? - if (m_isLeader && taskId == TASK_SEEKCOVER) - CommandTeam (); // reorganize team if fleeing + if (m_isLeader && tid == TASK_SEEKCOVER) { + processTeamCommands (); // reorganize team if fleeing + } - if (taskId == TASK_CAMP) - SelectBestWeapon (); + if (tid == TASK_CAMP) { + selectBestWeapon (); + } // this is best place to handle some voice commands report team some info - if (Random.Int (0, 100) < 95) - { - if (taskId == TASK_BLINDED) - InstantChatterMessage (Chatter_GotBlinded); - else if (taskId == TASK_PLANTBOMB) - InstantChatterMessage (Chatter_PlantingC4); + if (rng.getInt (0, 100) < 95) { + if (tid == TASK_BLINDED) { + instantChatter (CHATTER_BLINDED); + } + else if (tid == TASK_PLANTBOMB) { + instantChatter (CHATTER_PLANTING_BOMB); + } } - if (Random.Int (0, 100) < 80 && taskId == TASK_CAMP) - { - if ((g_mapType & MAP_DE) && g_bombPlanted) - ChatterMessage (Chatter_GuardDroppedC4); - else - ChatterMessage (Chatter_GoingToCamp); + if (rng.getInt (0, 100) < 80 && tid == TASK_CAMP) { + if ((g_mapFlags & MAP_DE) && g_bombPlanted) { + pushChatterMessage (CHATTER_GUARDING_DROPPED_BOMB); + } + else { + pushChatterMessage (CHATTER_GOING_TO_CAMP); + } } - if (yb_debug_goal.GetInt () != -1) - m_chosenGoalIndex = yb_debug_goal.GetInt (); - else - m_chosenGoalIndex = GetTask ()->data; + if (yb_debug_goal.integer () != INVALID_WAYPOINT_INDEX) { + m_chosenGoalIndex = yb_debug_goal.integer (); + } + else { + m_chosenGoalIndex = task ()->data; + } - if (Random.Int (0, 100) < 80 && GetTaskId () == TASK_CAMP && m_team == TERRORIST && m_inVIPZone) - ChatterMessage (Chatter_GoingToGuardVIPSafety); + if (rng.getInt (0, 100) < 80 && tid == TASK_CAMP && m_team == TEAM_TERRORIST && m_inVIPZone) { + pushChatterMessage (CHATTER_GOING_TO_GUARD_VIP_SAFETY); + } } -TaskItem *Bot::GetTask (void) -{ - if (m_tasks.IsEmpty ()) - { - m_tasks.RemoveAll (); - - TaskItem task; - - task.id = TASK_NORMAL; - task.desire = TASKPRI_NORMAL; - task.data = -1; - task.time = 0.0f; - task.resume = true; - - m_tasks.Push (task); +Task *Bot::task (void) { + if (m_tasks.empty ()) { + m_tasks.push ({ TASK_NORMAL, TASKPRI_NORMAL, INVALID_WAYPOINT_INDEX, 0.0f, true }); } - return &m_tasks.Last (); + return &m_tasks.back (); } -void Bot::RemoveCertainTask (TaskID id) -{ +void Bot::clearTask (TaskID id) { // this function removes one task from the bot task stack. - if (m_tasks.IsEmpty () || (!m_tasks.IsEmpty () && GetTaskId () == TASK_NORMAL)) + if (m_tasks.empty () || taskId () == TASK_NORMAL) { return; // since normal task can be only once on the stack, don't remove it... + } - if (GetTaskId () == id) - { - DeleteSearchNodes (); - m_tasks.Pop (); + if (taskId () == id) { + clearSearchNodes (); + ignoreCollision (); + m_tasks.pop (); return; } - FOR_EACH_AE (m_tasks, i) - { - if (m_tasks[i].id == id) - m_tasks.RemoveAt (i); + for (auto &task : m_tasks) { + if (task.id == id) { + m_tasks.erase (task); + } } - DeleteSearchNodes (); + + ignoreCollision (); + clearSearchNodes (); } -void Bot::TaskComplete (void) -{ +void Bot::completeTask (void) { // this function called whenever a task is completed. - if (m_tasks.IsEmpty ()) + ignoreCollision (); + + if (m_tasks.empty ()) { return; + } - do - { - m_tasks.Pop (); + do { + m_tasks.pop (); + } while (!m_tasks.empty () && !m_tasks.back ().resume); - } while (!m_tasks.IsEmpty () && !m_tasks.Last ().resume); - - DeleteSearchNodes (); + clearSearchNodes (); } -bool Bot::EnemyIsThreat (void) -{ - if (engine.IsNullEntity (m_enemy) || GetTaskId () == TASK_SEEKCOVER) +bool Bot::isEnemyThreat (void) { + if (engine.isNullEntity (m_enemy) || taskId () == TASK_SEEKCOVER) { return false; + } // if bot is camping, he should be firing anyway and not leaving his position - if (GetTaskId () == TASK_CAMP) + if (taskId () == TASK_CAMP) { return false; + } // if enemy is near or facing us directly - if ((m_enemy->v.origin - pev->origin).GetLength () < 256.0f || IsInViewCone (m_enemy->v.origin)) + if ((m_enemy->v.origin - pev->origin).lengthSq () < cr::square (256.0f) || isInViewCone (m_enemy->v.origin)) { return true; - + } return false; } -bool Bot::ReactOnEnemy (void) -{ +bool Bot::reactOnEnemy (void) { // the purpose of this function is check if task has to be interrupted because an enemy is near (run attack actions then) - if (!EnemyIsThreat ()) + if (!isEnemyThreat ()) { return false; - - if (m_enemyReachableTimer < engine.Time ()) - { - int i = waypoints.FindNearest (pev->origin); - int enemyIndex = waypoints.FindNearest (m_enemy->v.origin); - - float lineDist = (m_enemy->v.origin - pev->origin).GetLength (); - float pathDist = static_cast (waypoints.GetPathDistance (i, enemyIndex)); - - if (pathDist - lineDist > 112.0f) - m_isEnemyReachable = false; - else - m_isEnemyReachable = true; - - m_enemyReachableTimer = engine.Time () + 1.0f; } - if (m_isEnemyReachable) - { - m_navTimeset = engine.Time (); // override existing movement by attack movement + if (m_enemyReachableTimer < engine.timebase ()) { + int ownIndex = m_currentWaypointIndex; + + if (ownIndex == INVALID_WAYPOINT_INDEX) { + ownIndex = getNearestPoint (); + } + int enemyIndex = waypoints.getNearest (m_enemy->v.origin); + + float lineDist = (m_enemy->v.origin - pev->origin).length (); + float pathDist = static_cast (waypoints.getPathDist (ownIndex, enemyIndex)); + + if (pathDist - lineDist > 112.0f) { + m_isEnemyReachable = false; + } + else { + m_isEnemyReachable = true; + } + m_enemyReachableTimer = engine.timebase () + 1.0f; + } + + if (m_isEnemyReachable) { + m_navTimeset = engine.timebase (); // override existing movement by attack movement return true; } return false; } -bool Bot::LastEnemyShootable (void) -{ +bool Bot::lastEnemyShootable (void) { // don't allow shooting through walls - if (!(m_aimFlags & AIM_LAST_ENEMY) || m_lastEnemyOrigin.IsZero () || engine.IsNullEntity (m_lastEnemy)) + if (!(m_aimFlags & AIM_LAST_ENEMY) || m_lastEnemyOrigin.empty () || engine.isNullEntity (m_lastEnemy)) { return false; - - return GetShootingConeDeviation (GetEntity (), &m_lastEnemyOrigin) >= 0.90f && IsShootableThruObstacle (m_lastEnemyOrigin); + } + return getShootingConeDeviation (ent (), m_lastEnemyOrigin) >= 0.90f && isPenetrableObstacle (m_lastEnemyOrigin); } -void Bot::CheckRadioCommands (void) -{ +void Bot::checkRadioQueue (void) { // this function handling radio and reacting to it - float distance = (m_radioEntity->v.origin - pev->origin).GetLength (); + float distance = (m_radioEntity->v.origin - pev->origin).length (); // don't allow bot listen you if bot is busy - if ((GetTaskId () == TASK_DEFUSEBOMB || GetTaskId () == TASK_PLANTBOMB || HasHostage () || m_hasC4) && m_radioOrder != Radio_ReportTeam) - { + if ((taskId () == TASK_DEFUSEBOMB || taskId () == TASK_PLANTBOMB || hasHostage () || m_hasC4) && m_radioOrder != RADIO_REPORT_TEAM) { m_radioOrder = 0; return; } - switch (m_radioOrder) - { - case Radio_CoverMe: - case Radio_FollowMe: - case Radio_StickTogether: - case Chatter_GoingToPlantBomb: - case Chatter_CoverMe: + switch (m_radioOrder) { + case RADIO_COVER_ME: + case RADIO_FOLLOW_ME: + case RADIO_STICK_TOGETHER_TEAM: + case CHATTER_GOING_TO_PLANT_BOMB: + case CHATTER_COVER_ME: // check if line of sight to object is not blocked (i.e. visible) - if ((EntityIsVisible (m_radioEntity->v.origin)) || (m_radioOrder == Radio_StickTogether)) - { - if (engine.IsNullEntity (m_targetEntity) && engine.IsNullEntity (m_enemy) && Random.Int (0, 100) < (m_personality == PERSONALITY_CAREFUL ? 80 : 20)) - { + if ((seesEntity (m_radioEntity->v.origin)) || (m_radioOrder == RADIO_STICK_TOGETHER_TEAM)) { + if (engine.isNullEntity (m_targetEntity) && engine.isNullEntity (m_enemy) && rng.getInt (0, 100) < (m_personality == PERSONALITY_CAREFUL ? 80 : 20)) { int numFollowers = 0; // Check if no more followers are allowed - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot != nullptr) - { - if (bot->m_notKilled) - { - if (bot->m_targetEntity == m_radioEntity) + if (bot != nullptr) { + if (bot->m_notKilled) { + if (bot->m_targetEntity == m_radioEntity) { numFollowers++; + } } } } + int allowedFollowers = yb_user_max_followers.integer (); - int allowedFollowers = yb_user_max_followers.GetInt (); - - if (m_radioEntity->v.weapons & (1 << WEAPON_C4)) + if (m_radioEntity->v.weapons & (1 << WEAPON_C4)) { allowedFollowers = 1; + } - if (numFollowers < allowedFollowers) - { - RadioMessage (Radio_Affirmative); + if (numFollowers < allowedFollowers) { + pushRadioMessage (RADIO_AFFIRMATIVE); m_targetEntity = m_radioEntity; // don't pause/camp/follow anymore - TaskID taskID = GetTaskId (); + TaskID taskID = taskId (); - if (taskID == TASK_PAUSE || taskID == TASK_CAMP) - GetTask ()->time = engine.Time (); - - PushTask (TASK_FOLLOWUSER, TASKPRI_FOLLOWUSER, -1, 0.0f, true); + if (taskID == TASK_PAUSE || taskID == TASK_CAMP) { + task ()->time = engine.timebase (); + } + startTask (TASK_FOLLOWUSER, TASKPRI_FOLLOWUSER, INVALID_WAYPOINT_INDEX, 0.0f, true); } - else if (numFollowers > allowedFollowers) - { - for (int i = 0; (i < engine.MaxClients () && numFollowers > allowedFollowers); i++) - { - Bot *bot = bots.GetBot (i); + else if (numFollowers > allowedFollowers) { + for (int i = 0; (i < engine.maxClients () && numFollowers > allowedFollowers); i++) { + Bot *bot = bots.getBot (i); - if (bot != nullptr) - { - if (bot->m_notKilled) - { - if (bot->m_targetEntity == m_radioEntity) - { + if (bot != nullptr) { + if (bot->m_notKilled) { + if (bot->m_targetEntity == m_radioEntity) { bot->m_targetEntity = nullptr; numFollowers--; } @@ -2445,237 +2348,235 @@ void Bot::CheckRadioCommands (void) } } } - else if (m_radioOrder != Chatter_GoingToPlantBomb && Random.Int (0, 100) < 50) - RadioMessage (Radio_Negative); + else if (m_radioOrder != CHATTER_GOING_TO_PLANT_BOMB && rng.getInt (0, 100) < 15) { + pushRadioMessage (RADIO_NEGATIVE); + } + } + else if (m_radioOrder != CHATTER_GOING_TO_PLANT_BOMB && rng.getInt (0, 100) < 25) { + pushRadioMessage (RADIO_NEGATIVE); } - else if (m_radioOrder != Chatter_GoingToPlantBomb && Random.Int (0, 100) < 50) - RadioMessage (Radio_Negative); } break; - case Radio_HoldPosition: - if (!engine.IsNullEntity (m_targetEntity)) - { - if (m_targetEntity == m_radioEntity) - { + case RADIO_HOLD_THIS_POSITION: + if (!engine.isNullEntity (m_targetEntity)) { + if (m_targetEntity == m_radioEntity) { m_targetEntity = nullptr; - RadioMessage (Radio_Affirmative); + pushRadioMessage (RADIO_AFFIRMATIVE); m_campButtons = 0; - PushTask (TASK_PAUSE, TASKPRI_PAUSE, -1, engine.Time () + Random.Float (30.0f, 60.0f), false); + startTask (TASK_PAUSE, TASKPRI_PAUSE, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (30.0f, 60.0f), false); } } break; - case Chatter_NewRound: - ChatterMessage (Chatter_You_Heard_The_Man); - break; + case CHATTER_NEW_ROUND: + pushChatterMessage (CHATTER_YOU_HEARD_THE_MAN); + break; - case Radio_TakingFire: - if (engine.IsNullEntity (m_targetEntity)) - { - if (engine.IsNullEntity (m_enemy) && m_seeEnemyTime + 4.0f < engine.Time ()) - { + case RADIO_TAKING_FIRE: + if (engine.isNullEntity (m_targetEntity)) { + if (engine.isNullEntity (m_enemy) && m_seeEnemyTime + 4.0f < engine.timebase ()) { // decrease fear levels to lower probability of bot seeking cover again m_fearLevel -= 0.2f; - if (m_fearLevel < 0.0f) + if (m_fearLevel < 0.0f) { m_fearLevel = 0.0f; + } - if (Random.Int (0, 100) < 45 && yb_communication_type.GetInt () == 2) - ChatterMessage (Chatter_OnMyWay); - else if (m_radioOrder == Radio_NeedBackup && yb_communication_type.GetInt () != 2) - RadioMessage (Radio_Affirmative); - - TryHeadTowardRadioEntity (); + if (rng.getInt (0, 100) < 45 && yb_communication_type.integer () == 2) { + pushChatterMessage (CHATTER_ON_MY_WAY); + } + else if (m_radioOrder == RADIO_NEED_BACKUP && yb_communication_type.integer () != 2) { + pushRadioMessage (RADIO_AFFIRMATIVE); + } + tryHeadTowardRadioMessage (); + } + else if (rng.getInt (0, 100) < 25) { + pushRadioMessage (RADIO_NEGATIVE); } - else if (Random.Int (0, 100) < 70) - RadioMessage (Radio_Negative); } break; - case Radio_YouTakePoint: - if (EntityIsVisible (m_radioEntity->v.origin) && m_isLeader) - RadioMessage (Radio_Affirmative); + case RADIO_YOU_TAKE_THE_POINT: + if (seesEntity (m_radioEntity->v.origin) && m_isLeader) { + pushRadioMessage (RADIO_AFFIRMATIVE); + } break; - case Radio_EnemySpotted: - case Radio_NeedBackup: - case Chatter_ScaredEmotion: - case Chatter_Pinned_Down: - if (((engine.IsNullEntity (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 2048.0f || !m_moveToC4) && Random.Int (0, 100) > 50 && m_seeEnemyTime + 4.0f < engine.Time ()) - { + case RADIO_ENEMY_SPOTTED: + case RADIO_NEED_BACKUP: + case CHATTER_SCARED_EMOTE: + case CHATTER_PINNED_DOWN: + if (((engine.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distance < 2048.0f || !m_moveToC4) && rng.getInt (0, 100) > 50 && m_seeEnemyTime + 4.0f < engine.timebase ()) { m_fearLevel -= 0.1f; - if (m_fearLevel < 0.0f) + if (m_fearLevel < 0.0f) { m_fearLevel = 0.0f; + } - if (Random.Int (0, 100) < 45 && yb_communication_type.GetInt () == 2) - ChatterMessage (Chatter_OnMyWay); - else if (m_radioOrder == Radio_NeedBackup && yb_communication_type.GetInt () != 2) - RadioMessage (Radio_Affirmative); - - TryHeadTowardRadioEntity (); + if (rng.getInt (0, 100) < 45 && yb_communication_type.integer () == 2) { + pushChatterMessage (CHATTER_ON_MY_WAY); + } + else if (m_radioOrder == RADIO_NEED_BACKUP && yb_communication_type.integer () != 2) { + pushRadioMessage (RADIO_AFFIRMATIVE); + } + tryHeadTowardRadioMessage (); + } + else if (rng.getInt (0, 100) < 30 && m_radioOrder == RADIO_NEED_BACKUP) { + pushRadioMessage (RADIO_NEGATIVE); } - else if (Random.Int (0, 100) < 60 && m_radioOrder == Radio_NeedBackup) - RadioMessage (Radio_Negative); break; - case Radio_GoGoGo: - if (m_radioEntity == m_targetEntity) - { - if (Random.Int (0, 100) < 45 && yb_communication_type.GetInt () == 2) - RadioMessage (Radio_Affirmative); - else if (m_radioOrder == Radio_NeedBackup && yb_communication_type.GetInt () != 2) - RadioMessage (Radio_Affirmative); + case RADIO_GO_GO_GO: + if (m_radioEntity == m_targetEntity) { + if (rng.getInt (0, 100) < 45 && yb_communication_type.integer () == 2) { + pushRadioMessage (RADIO_AFFIRMATIVE); + } + else if (m_radioOrder == RADIO_NEED_BACKUP && yb_communication_type.integer () != 2) { + pushRadioMessage (RADIO_AFFIRMATIVE); + } m_targetEntity = nullptr; m_fearLevel -= 0.2f; - if (m_fearLevel < 0.0f) + if (m_fearLevel < 0.0f) { m_fearLevel = 0.0f; - } - else if ((engine.IsNullEntity (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 2048.0f) - { - TaskID taskID = GetTaskId (); - - if (taskID == TASK_PAUSE || taskID == TASK_CAMP) - { - m_fearLevel -= 0.2f; - - if (m_fearLevel < 0.0f) - m_fearLevel = 0.0f; - - RadioMessage (Radio_Affirmative); - // don't pause/camp anymore - GetTask ()->time = engine.Time (); - - m_targetEntity = nullptr; - MakeVectors (m_radioEntity->v.v_angle); - - m_position = m_radioEntity->v.origin + g_pGlobals->v_forward * Random.Float (1024.0f, 2048.0f); - - DeleteSearchNodes (); - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true); } } - else if (!engine.IsNullEntity (m_doubleJumpEntity)) - { - RadioMessage (Radio_Affirmative); - ResetDoubleJumpState (); - } - else - RadioMessage (Radio_Negative); + else if ((engine.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distance < 2048.0f) { + TaskID taskID = taskId (); + if (taskID == TASK_PAUSE || taskID == TASK_CAMP) { + m_fearLevel -= 0.2f; + + if (m_fearLevel < 0.0f) { + m_fearLevel = 0.0f; + } + + pushRadioMessage (RADIO_AFFIRMATIVE); + // don't pause/camp anymore + task ()->time = engine.timebase (); + + m_targetEntity = nullptr; + makeVectors (m_radioEntity->v.v_angle); + + m_position = m_radioEntity->v.origin + g_pGlobals->v_forward * rng.getFloat (1024.0f, 2048.0f); + + clearSearchNodes (); + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, INVALID_WAYPOINT_INDEX, 0.0f, true); + } + } + else if (!engine.isNullEntity (m_doubleJumpEntity)) { + pushRadioMessage (RADIO_AFFIRMATIVE); + resetDoubleJump (); + } + else if (rng.getInt (0, 100) < 35) { + pushRadioMessage (RADIO_NEGATIVE); + } break; - case Radio_ShesGonnaBlow: - if (engine.IsNullEntity (m_enemy) && distance < 2048.0f && g_bombPlanted && m_team == TERRORIST) - { - RadioMessage (Radio_Affirmative); - - if (GetTaskId () == TASK_CAMP) - RemoveCertainTask (TASK_CAMP); + case RADIO_SHES_GONNA_BLOW: + if (engine.isNullEntity (m_enemy) && distance < 2048.0f && g_bombPlanted && m_team == TEAM_TERRORIST) { + pushRadioMessage (RADIO_AFFIRMATIVE); + if (taskId () == TASK_CAMP) { + clearTask (TASK_CAMP); + } m_targetEntity = nullptr; - PushTask (TASK_ESCAPEFROMBOMB, TASKPRI_ESCAPEFROMBOMB, -1, 0.0f, true); + startTask (TASK_ESCAPEFROMBOMB, TASKPRI_ESCAPEFROMBOMB, INVALID_WAYPOINT_INDEX, 0.0f, true); + } + else if (rng.getInt (0, 100) < 35) { + pushRadioMessage (RADIO_NEGATIVE); } - else - RadioMessage (Radio_Negative); - break; - case Radio_RegroupTeam: + case RADIO_REGROUP_TEAM: // if no more enemies found AND bomb planted, switch to knife to get to bombplace faster - if (m_team == CT && m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0 && g_bombPlanted && GetTaskId () != TASK_DEFUSEBOMB) - { - SelectWeaponByName ("weapon_knife"); + if (m_team == TEAM_COUNTER && m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0 && g_bombPlanted && taskId () != TASK_DEFUSEBOMB) { + selectWeaponByName ("weapon_knife"); - DeleteSearchNodes (); + clearSearchNodes (); - m_position = waypoints.GetBombPosition (); - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true); + m_position = waypoints.getBombPos (); + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, INVALID_WAYPOINT_INDEX, 0.0f, true); - RadioMessage (Radio_Affirmative); + pushRadioMessage (RADIO_AFFIRMATIVE); } break; - case Radio_StormTheFront: - if (((engine.IsNullEntity (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 1024.0f) && Random.Int (0, 100) > 50) - { - RadioMessage (Radio_Affirmative); + case RADIO_STORM_THE_FRONT: + if (((engine.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distance < 1024.0f) && rng.getInt (0, 100) > 50) { + pushRadioMessage (RADIO_AFFIRMATIVE); // don't pause/camp anymore - TaskID taskID = GetTaskId (); - - if (taskID == TASK_PAUSE || taskID == TASK_CAMP) - GetTask ()->time = engine.Time (); + TaskID taskID = taskId (); + if (taskID == TASK_PAUSE || taskID == TASK_CAMP) { + task ()->time = engine.timebase (); + } m_targetEntity = nullptr; - MakeVectors (m_radioEntity->v.v_angle); - m_position = m_radioEntity->v.origin + g_pGlobals->v_forward * Random.Float (1024.0f, 2048.0f); + makeVectors (m_radioEntity->v.v_angle); + m_position = m_radioEntity->v.origin + g_pGlobals->v_forward * rng.getFloat (1024.0f, 2048.0f); - DeleteSearchNodes (); - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true); + clearSearchNodes (); + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, INVALID_WAYPOINT_INDEX, 0.0f, true); m_fearLevel -= 0.3f; - if (m_fearLevel < 0.0f) + if (m_fearLevel < 0.0f) { m_fearLevel = 0.0f; - + } m_agressionLevel += 0.3f; - if (m_agressionLevel > 1.0f) + if (m_agressionLevel > 1.0f) { m_agressionLevel = 1.0f; + } } break; - case Radio_Fallback: - if ((engine.IsNullEntity (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 1024.0f) - { + case RADIO_TEAM_FALLBACK: + if ((engine.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distance < 1024.0f) { m_fearLevel += 0.5f; - if (m_fearLevel > 1.0f) + if (m_fearLevel > 1.0f) { m_fearLevel = 1.0f; - + } m_agressionLevel -= 0.5f; - if (m_agressionLevel < 0.0f) + if (m_agressionLevel < 0.0f) { m_agressionLevel = 0.0f; - - if (GetTaskId () == TASK_CAMP) - GetTask ()->time += Random.Float (10.0f, 15.0f); - else - { + } + if (taskId () == TASK_CAMP) { + task ()->time += rng.getFloat (10.0f, 15.0f); + } + else { // don't pause/camp anymore - TaskID taskID = GetTaskId (); - - if (taskID == TASK_PAUSE) - GetTask ()->time = engine.Time (); + TaskID taskID = taskId (); + if (taskID == TASK_PAUSE) { + task ()->time = engine.timebase (); + } m_targetEntity = nullptr; - m_seeEnemyTime = engine.Time (); + m_seeEnemyTime = engine.timebase (); // if bot has no enemy - if (m_lastEnemyOrigin.IsZero ()) - { + if (m_lastEnemyOrigin.empty ()) { float nearestDistance = 99999.0f; // take nearest enemy to ordering player - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team) { continue; - + } edict_t *enemy = client.ent; - float curDist = (m_radioEntity->v.origin - enemy->v.origin).GetLengthSquared (); + float curDist = (m_radioEntity->v.origin - enemy->v.origin).lengthSq (); - if (curDist < nearestDistance) - { + if (curDist < nearestDistance) { nearestDistance = curDist; m_lastEnemy = enemy; @@ -2683,114 +2584,108 @@ void Bot::CheckRadioCommands (void) } } } - DeleteSearchNodes (); + clearSearchNodes (); } } break; - case Radio_ReportTeam: - if (Random.Int (0, 100) < 30) - RadioMessage ((GetNearbyEnemiesNearPosition (pev->origin, 400.0f) == 0 && yb_communication_type.GetInt () != 2) ? Radio_SectorClear : Radio_ReportingIn); + case RADIO_REPORT_TEAM: + if (rng.getInt (0, 100) < 30) { + pushRadioMessage ((numEnemiesNear (pev->origin, 400.0f) == 0 && yb_communication_type.integer () != 2) ? RADIO_SECTOR_CLEAR : RADIO_REPORTING_IN); + } break; - case Radio_SectorClear: + case RADIO_SECTOR_CLEAR: // is bomb planted and it's a ct - if (!g_bombPlanted) + if (!g_bombPlanted) { break; + } // check if it's a ct command - if (engine.GetTeam (m_radioEntity) == CT && m_team == CT && IsValidBot (m_radioEntity) && g_timeNextBombUpdate < engine.Time ()) - { + if (engine.getTeam (m_radioEntity) == TEAM_COUNTER && m_team == TEAM_COUNTER && isFakeClient (m_radioEntity) && g_timeNextBombUpdate < engine.timebase ()) { float minDistance = 99999.0f; - int bombPoint = -1; + int bombPoint = INVALID_WAYPOINT_INDEX; // find nearest bomb waypoint to player - FOR_EACH_AE (waypoints.m_goalPoints, i) - { - distance = (waypoints.GetPath (waypoints.m_goalPoints[i])->origin - m_radioEntity->v.origin).GetLengthSquared (); + for (auto &point : waypoints.m_goalPoints) { + distance = (waypoints[point].origin - m_radioEntity->v.origin).lengthSq (); - if (distance < minDistance) - { + if (distance < minDistance) { minDistance = distance; - bombPoint = waypoints.m_goalPoints[i]; + bombPoint = point; } } // mark this waypoint as restricted point - if (bombPoint != -1 && !waypoints.IsGoalVisited (bombPoint)) - { + if (bombPoint != INVALID_WAYPOINT_INDEX && !waypoints.isVisited (bombPoint)) { // does this bot want to defuse? - if (GetTaskId () == TASK_NORMAL) - { + if (taskId () == TASK_NORMAL) { // is he approaching this goal? - if (GetTask ()->data == bombPoint) - { - GetTask ()->data = -1; - RadioMessage (Radio_Affirmative); - + if (task ()->data == bombPoint) { + task ()->data = INVALID_WAYPOINT_INDEX; + pushRadioMessage (RADIO_AFFIRMATIVE); } } - waypoints.SetGoalVisited (bombPoint); + waypoints.setVisited (bombPoint); } - g_timeNextBombUpdate = engine.Time () + 0.5f; + g_timeNextBombUpdate = engine.timebase () + 0.5f; } break; - case Radio_GetInPosition: - if ((engine.IsNullEntity (m_enemy) && EntityIsVisible (m_radioEntity->v.origin)) || distance < 1024.0f) - { - RadioMessage (Radio_Affirmative); + case RADIO_GET_IN_POSITION: + if ((engine.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distance < 1024.0f) { + pushRadioMessage (RADIO_AFFIRMATIVE); - if (GetTaskId () == TASK_CAMP) - GetTask ()->time = engine.Time () + Random.Float (30.0f, 60.0f); - else - { + if (taskId () == TASK_CAMP) { + task ()->time = engine.timebase () + rng.getFloat (30.0f, 60.0f); + } + else { // don't pause anymore - TaskID taskID = GetTaskId (); + TaskID taskID = taskId (); - if (taskID == TASK_PAUSE) - GetTask ()->time = engine.Time (); + if (taskID == TASK_PAUSE) { + task ()->time = engine.timebase (); + } m_targetEntity = nullptr; - m_seeEnemyTime = engine.Time (); + m_seeEnemyTime = engine.timebase (); // if bot has no enemy - if (m_lastEnemyOrigin.IsZero ()) - { + if (m_lastEnemyOrigin.empty ()) { float nearestDistance = 99999.0f; // take nearest enemy to ordering player - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team) continue; edict_t *enemy = client.ent; - float dist = (m_radioEntity->v.origin - enemy->v.origin).GetLengthSquared (); + float dist = (m_radioEntity->v.origin - enemy->v.origin).lengthSq (); - if (dist < nearestDistance) - { + if (dist < nearestDistance) { nearestDistance = dist; m_lastEnemy = enemy; m_lastEnemyOrigin = enemy->v.origin; } } } - DeleteSearchNodes (); + clearSearchNodes (); - int index = FindDefendWaypoint (m_radioEntity->v.origin); + int index = getDefendPoint (m_radioEntity->v.origin); // push camp task on to stack - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (30.0f, 60.0f), true); + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (30.0f, 60.0f), true); // push move command - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.Time () + Random.Float (30.0f, 60.0f), true); + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.timebase () + rng.getFloat (30.0f, 60.0f), true); - if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand) + if (waypoints[index].vis.crouch <= waypoints[index].vis.stand) { m_campButtons |= IN_DUCK; - else + } + else { m_campButtons &= ~IN_DUCK; + } } } break; @@ -2798,341 +2693,388 @@ void Bot::CheckRadioCommands (void) m_radioOrder = 0; // radio command has been handled, reset } -void Bot::TryHeadTowardRadioEntity (void) -{ - TaskID taskID = GetTaskId (); +void Bot::tryHeadTowardRadioMessage (void) { + TaskID taskID = taskId (); - if (taskID == TASK_MOVETOPOSITION || m_headedTime + 15.0f < engine.Time () || !IsAlive (m_radioEntity) || m_hasC4) + if (taskID == TASK_MOVETOPOSITION || m_headedTime + 15.0f < engine.timebase () || !isAlive (m_radioEntity) || m_hasC4) return; - if ((IsValidBot (m_radioEntity) && Random.Int (0, 100) < 25 && m_personality == PERSONALITY_NORMAL) || !(m_radioEntity->v.flags & FL_FAKECLIENT)) - { - if (taskID == TASK_PAUSE || taskID == TASK_CAMP) - GetTask ()->time = engine.Time (); - - m_headedTime = engine.Time (); + if ((isFakeClient (m_radioEntity) && rng.getInt (0, 100) < 25 && m_personality == PERSONALITY_NORMAL) || !(m_radioEntity->v.flags & FL_FAKECLIENT)) { + if (taskID == TASK_PAUSE || taskID == TASK_CAMP) { + task ()->time = engine.timebase (); + } + m_headedTime = engine.timebase (); m_position = m_radioEntity->v.origin; - DeleteSearchNodes (); - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true); + clearSearchNodes (); + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, INVALID_WAYPOINT_INDEX, 0.0f, true); } } -void Bot::ChooseAimDirection (void) -{ +void Bot::updateAimDir (void) { unsigned int flags = m_aimFlags; // don't allow bot to look at danger positions under certain circumstances - if (!(flags & (AIM_GRENADE | AIM_ENEMY | AIM_ENTITY))) - { - if (IsOnLadder () || IsInWater () || (m_waypointFlags & FLAG_LADDER) || (m_currentTravelFlags & PATHFLAG_JUMP)) - { + if (!(flags & (AIM_GRENADE | AIM_ENEMY | AIM_ENTITY))) { + if (isOnLadder () || isInWater () || (m_waypointFlags & FLAG_LADDER) || (m_currentTravelFlags & PATHFLAG_JUMP)) { flags &= ~(AIM_LAST_ENEMY | AIM_PREDICT_PATH); m_canChooseAimDirection = false; } } - if (flags & AIM_OVERRIDE) + if (flags & AIM_OVERRIDE) { m_lookAt = m_camp; - else if (flags & AIM_GRENADE) - m_lookAt = m_throw + Vector (0.0f, 0.0f, 1.0f * m_grenade.z); - else if (flags & AIM_ENEMY) - FocusEnemy (); - else if (flags & AIM_ENTITY) + } + else if (flags & AIM_GRENADE) { + m_lookAt = m_throw; + + float throwDistance = (m_throw - pev->origin).length (); + float coordCorrection = 0.0f; + float angleCorrection = 0.0f; + + if (throwDistance > 100.0f && throwDistance < 800.0f) { + angleCorrection = 0.0f; + coordCorrection = 0.25f * (m_throw.z - pev->origin.z); + } + else if (throwDistance >= 800.0f) { + angleCorrection = 37.0f * (throwDistance - 800.0f) / 800.0f; + + if (angleCorrection > 45.0f) { + angleCorrection = 45.0f; + } + coordCorrection = throwDistance * cr::tanf (cr::deg2rad (angleCorrection)) + 0.25f * (m_throw.z - pev->origin.z); + } + m_lookAt.z += coordCorrection * 0.5f; + } + else if (flags & AIM_ENEMY) { + focusEnemy (); + } + else if (flags & AIM_ENTITY) { m_lookAt = m_entity; - else if (flags & AIM_LAST_ENEMY) - { + } + else if (flags & AIM_LAST_ENEMY) { m_lookAt = m_lastEnemyOrigin; // did bot just see enemy and is quite aggressive? - if (m_seeEnemyTime + 2.0f - m_actualReactionTime + m_baseAgressionLevel > engine.Time ()) - { + if (m_seeEnemyTime + 1.0f - m_actualReactionTime + m_baseAgressionLevel > engine.timebase ()) { + // feel free to fire if shootable - if (!UsesSniper () && LastEnemyShootable ()) + if (!usesSniper () && lastEnemyShootable ()) { m_wantsToFire = true; + } } } - else if (flags & AIM_PREDICT_PATH) - { + else if (flags & AIM_PREDICT_PATH) { bool changePredictedEnemy = true; - - if (m_trackingEdict == m_lastEnemy && m_timeNextTracking < engine.Time ()) + + if (m_timeNextTracking > engine.timebase () && m_trackingEdict == m_lastEnemy && isAlive (m_lastEnemy)) { changePredictedEnemy = false; - - if (changePredictedEnemy) - { - m_lookAt = waypoints.GetPath (GetAimingWaypoint (m_lastEnemyOrigin))->origin; - m_camp = m_lookAt; - - m_timeNextTracking = engine.Time () + 1.25f; - m_trackingEdict = m_lastEnemy; } - else + + if (changePredictedEnemy) { + int aimPoint = searchAimingPoint (m_lastEnemyOrigin); + + if (aimPoint != INVALID_WAYPOINT_INDEX) { + m_lookAt = waypoints[aimPoint].origin; + m_camp = m_lookAt; + + m_timeNextTracking = engine.timebase () + 0.5f; + m_trackingEdict = m_lastEnemy; + } + else { + m_aimFlags &= ~AIM_PREDICT_PATH; + + if (!m_camp.empty ()) { + m_lookAt = m_camp; + } + } + } + else { m_lookAt = m_camp; + } } - else if (flags & AIM_CAMP) + else if (flags & AIM_CAMP) { m_lookAt = m_camp; - else if (flags & AIM_NAVPOINT) - { + } + else if (flags & AIM_NAVPOINT) { m_lookAt = m_destOrigin; - if (m_canChooseAimDirection && m_currentWaypointIndex != -1 && !(m_currentPath->flags & FLAG_LADDER)) - { - int index = m_currentWaypointIndex; + if (m_canChooseAimDirection && m_currentWaypointIndex != INVALID_WAYPOINT_INDEX && !(m_currentPath->flags & FLAG_LADDER)) { + auto experience = (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + m_currentWaypointIndex); - if (m_team == TERRORIST) - { - if ((g_experienceData + (index * g_numWaypoints) + index)->team0DangerIndex != -1) - m_lookAt = waypoints.GetPath ((g_experienceData + (index * g_numWaypoints) + index)->team0DangerIndex)->origin; + if (m_team == TEAM_TERRORIST) { + if (experience->team0DangerIndex != INVALID_WAYPOINT_INDEX && waypoints.isVisible (m_currentWaypointIndex, experience->team0DangerIndex)) { + m_lookAt = waypoints[experience->team0DangerIndex].origin; + } } - else - { - if ((g_experienceData + (index * g_numWaypoints) + index)->team1DangerIndex != -1) - m_lookAt = waypoints.GetPath ((g_experienceData + (index * g_numWaypoints) + index)->team1DangerIndex)->origin; + else { + if (experience->team1DangerIndex != INVALID_WAYPOINT_INDEX && waypoints.isVisible (m_currentWaypointIndex, experience->team1DangerIndex)) { + m_lookAt = waypoints[experience->team1DangerIndex].origin; + } } } } - if (m_lookAt.IsZero ()) + if (m_lookAt.empty ()) { m_lookAt = m_destOrigin; + } } -void Bot::Think (void) -{ - if (m_thinkFps <= engine.Time ()) - { +void Bot::framePeriodic (void) { + if (m_thinkFps <= engine.timebase ()) { // execute delayed think - ThinkFrame (); + frameThink (); // skip some frames - m_thinkFps = engine.Time () + m_thinkInterval; + m_thinkFps = engine.timebase () + m_thinkInterval; + } + else { + processLookAngles (); } - else - UpdateLookAngles (); } -void Bot::ThinkFrame (void) -{ +void Bot::frameThink (void) { pev->button = 0; pev->flags |= FL_FAKECLIENT; // restore fake client bit, if it were removed by some evil action =) m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_moveAngles.Zero (); + m_moveAngles.nullify (); m_canChooseAimDirection = true; - m_notKilled = IsAlive (GetEntity ()); - m_team = engine.GetTeam (GetEntity ()); + m_notKilled = isAlive (ent ()); + m_team = engine.getTeam (ent ()); - if (m_team == TERRORIST && (g_mapType & MAP_DE)) + if ((g_mapFlags & MAP_AS) && !m_isVIP) { + m_isVIP = isPlayerVIP (ent ()); + } + + if (m_team == TEAM_TERRORIST && (g_mapFlags & MAP_DE)) { m_hasC4 = !!(pev->weapons & (1 << WEAPON_C4)); + } // is bot movement enabled bool botMovement = false; - if (m_notStarted) // if the bot hasn't selected stuff to start the game yet, go do that... - StartGame (); // select team & class - else if (!m_notKilled) - { - // no movement allowed in - if (m_voteKickIndex != m_lastVoteKick && yb_tkpunish.GetBool ()) // we got a teamkiller? vote him away... - { - engine.IssueBotCommand (GetEntity (), "vote %d", m_voteKickIndex); + // if the bot hasn't selected stuff to start the game yet, go do that... + if (m_notStarted) { + processTeamJoin (); // select team & class + } + else if (!m_notKilled) { + + // we got a teamkiller? vote him away... + if (m_voteKickIndex != m_lastVoteKick && yb_tkpunish.boolean ()) { + engine.execBotCmd (ent (), "vote %d", m_voteKickIndex); m_lastVoteKick = m_voteKickIndex; // if bot tk punishment is enabled slay the tk - if (yb_tkpunish.GetInt () != 2 || IsValidBot (engine.EntityOfIndex (m_voteKickIndex))) + if (yb_tkpunish.integer () != 2 || isFakeClient (engine.entityOfIndex (m_voteKickIndex))) { return; - - edict_t *killer = engine.EntityOfIndex (m_lastVoteKick); + } + edict_t *killer = engine.entityOfIndex (m_lastVoteKick); killer->v.frags++; MDLL_ClientKill (killer); } - else if (m_voteMap != 0) // host wants the bots to vote for a map? - { - engine.IssueBotCommand (GetEntity (), "votemap %d", m_voteMap); + + // host wants us to kick someone + else if (m_voteMap != 0) { + engine.execBotCmd (ent (), "votemap %d", m_voteMap); m_voteMap = 0; } } - else if (m_notKilled && m_buyingFinished && !(pev->maxspeed < 10.0f && GetTaskId () != TASK_PLANTBOMB && GetTaskId () != TASK_DEFUSEBOMB) && !yb_freeze_bots.GetBool () && !waypoints.HasChanged ()) + else if (m_notKilled && m_buyingFinished && !(pev->maxspeed < 10.0f && taskId () != TASK_PLANTBOMB && taskId () != TASK_DEFUSEBOMB) && !yb_freeze_bots.boolean () && !waypoints.hasChanged ()) { botMovement = true; + } + checkMsgQueue (); // check for pending messages - CheckMessageQueue (); // check for pending messages - - if (botMovement) - BotAI (); // execute main code - - RunPlayerMovement (); // run the player movement + if (botMovement) { + ai (); // execute main code + } + runMovement (); // run the player movement } -void Bot::PeriodicThink (void) -{ - if (m_timePeriodicUpdate > engine.Time ()) +void Bot::frame (void) { + if (m_timePeriodicUpdate > engine.timebase ()) { return; - - // this function is called from main think function - - m_numFriendsLeft = GetNearbyFriendsNearPosition (pev->origin, 99999.0f); - m_numEnemiesLeft = GetNearbyEnemiesNearPosition (pev->origin, 99999.0f); - - if (g_bombPlanted && m_team == CT) - { - const Vector &bombPosition = waypoints.GetBombPosition (); - - if (!m_hasProgressBar && GetTaskId () != TASK_ESCAPEFROMBOMB && (pev->origin - bombPosition).GetLength () < 700 && !IsBombDefusing (bombPosition)) - ResetTasks (); } - CheckSpawnTimeConditions (); + + m_numFriendsLeft = numFriendsNear (pev->origin, 99999.0f); + m_numEnemiesLeft = numEnemiesNear (pev->origin, 99999.0f); + + if (g_bombPlanted && m_team == TEAM_COUNTER) { + const Vector &bombPosition = waypoints.getBombPos (); + + if (!m_hasProgressBar && taskId () != TASK_ESCAPEFROMBOMB && (pev->origin - bombPosition).length () < 700.0f && !isBombDefusing (bombPosition)) { + clearTasks (); + } + } + checkSpawnConditions (); extern ConVar yb_chat; - if (!m_notKilled && yb_chat.GetBool () && m_lastChatTime + 10.0 < engine.Time () && g_lastChatTime + 5.0f < engine.Time () && !RepliesToPlayer ()) // bot chatting turned on? - { + // bot chatting turned on? + if (!m_notKilled && yb_chat.boolean () && m_lastChatTime + 10.0 < engine.timebase () && g_lastChatTime + 5.0f < engine.timebase () && !isReplyingToChat ()) { // say a text every now and then - if (Random.Int (1, 1500) < 2) - { - m_lastChatTime = engine.Time (); - g_lastChatTime = engine.Time (); + if (rng.getInt (1, 1500) < 50) { + m_lastChatTime = engine.timebase (); + g_lastChatTime = engine.timebase (); - auto pickedPhrase = g_chatFactory[CHAT_DEAD].GetRandomElement ().GetBuffer (); - bool sayBufferExists = false; + if (!g_chatFactory[CHAT_DEAD].empty ()) { + const String &phrase = g_chatFactory[CHAT_DEAD].random (); + bool sayBufferExists = false; - // search for last messages, sayed - FOR_EACH_AE (m_sayTextBuffer.lastUsedSentences, i) - { - if (strncmp (m_sayTextBuffer.lastUsedSentences[i].GetBuffer (), pickedPhrase, m_sayTextBuffer.lastUsedSentences[i].GetLength ()) == 0) - sayBufferExists = true; - } + // search for last messages, sayed + for (auto &sentence : m_sayTextBuffer.lastUsedSentences) { + if (strncmp (sentence.chars (), phrase.chars (), sentence.length ()) == 0) { + sayBufferExists = true; + break; + } + } - if (!sayBufferExists) - { - PrepareChatMessage (const_cast (pickedPhrase)); - PushMessageQueue (GAME_MSG_SAY_CMD); + if (!sayBufferExists) { + prepareChatMessage (const_cast (phrase.chars ())); + pushMsgQueue (GAME_MSG_SAY_CMD); - // add to ignore list - m_sayTextBuffer.lastUsedSentences.Push (pickedPhrase); + // add to ignore list + m_sayTextBuffer.lastUsedSentences.push (phrase); + } } // clear the used line buffer every now and then - if (m_sayTextBuffer.lastUsedSentences.GetElementNumber () > Random.Int (4, 6)) - m_sayTextBuffer.lastUsedSentences.RemoveAll (); + if (static_cast (m_sayTextBuffer.lastUsedSentences.length ()) > rng.getInt (4, 6)) { + m_sayTextBuffer.lastUsedSentences.clear (); + } } } - if (g_gameFlags & GAME_SUPPORT_BOT_VOICE) - EnableChatterIcon (false); // end voice feedback + if (g_gameFlags & GAME_SUPPORT_BOT_VOICE) { + showChaterIcon (false); // end voice feedback + } // clear enemy far away - if (!m_lastEnemyOrigin.IsZero () && !engine.IsNullEntity (m_lastEnemy) && (pev->origin - m_lastEnemyOrigin).GetLength () >= 1600.0f) - { + if (!m_lastEnemyOrigin.empty () && !engine.isNullEntity (m_lastEnemy) && (pev->origin - m_lastEnemyOrigin).lengthSq () >= cr::square (1600.0f)) { m_lastEnemy = nullptr; - m_lastEnemyOrigin.Zero (); + m_lastEnemyOrigin.nullify (); } - m_timePeriodicUpdate = engine.Time () + 0.5f; + m_timePeriodicUpdate = engine.timebase () + 0.5f; } -void Bot::RunTask_Normal (void) -{ +void Bot::normal_ (void) { m_aimFlags |= AIM_NAVPOINT; + int debugGoal = yb_debug_goal.integer (); + // user forced a waypoint as a goal? - if (yb_debug_goal.GetInt () != -1 && GetTask ()->data != yb_debug_goal.GetInt ()) - { - DeleteSearchNodes (); - GetTask ()->data = yb_debug_goal.GetInt (); + if (debugGoal != INVALID_WAYPOINT_INDEX && task ()->data != debugGoal) { + clearSearchNodes (); + task ()->data = debugGoal; + } + + // stand still if reached debug goal + else if (m_currentWaypointIndex == debugGoal) { + pev->button = 0; + ignoreCollision (); + + m_moveSpeed = 0.0; + m_strafeSpeed = 0.0f; + + return; } // bots rushing with knife, when have no enemy (thanks for idea to nicebot project) - if (m_currentWeapon == WEAPON_KNIFE && (engine.IsNullEntity (m_lastEnemy) || !IsAlive (m_lastEnemy)) && engine.IsNullEntity (m_enemy) && m_knifeAttackTime < engine.Time () && !HasShield () && GetNearbyFriendsNearPosition (pev->origin, 96.0f) == 0) - { - if (Random.Int (0, 100) < 40) + if (m_currentWeapon == WEAPON_KNIFE && (engine.isNullEntity (m_lastEnemy) || !isAlive (m_lastEnemy)) && engine.isNullEntity (m_enemy) && m_knifeAttackTime < engine.timebase () && !hasShield () && numFriendsNear (pev->origin, 96.0f) == 0) { + if (rng.getInt (0, 100) < 40) { pev->button |= IN_ATTACK; - else + } + else { pev->button |= IN_ATTACK2; - - m_knifeAttackTime = engine.Time () + Random.Float (2.5f, 6.0f); + } + m_knifeAttackTime = engine.timebase () + rng.getFloat (2.5f, 6.0f); } - if (m_reloadState == RELOAD_NONE && GetAmmo () != 0 && GetAmmoInClip () < 5 && g_weaponDefs[m_currentWeapon].ammo1 != -1) + if (m_reloadState == RELOAD_NONE && ammo () != 0 && ammoClip () < 5 && g_weaponDefs[m_currentWeapon].ammo1 != -1) { m_reloadState = RELOAD_PRIMARY; + } // if bomb planted and it's a CT calculate new path to bomb point if he's not already heading for - if (g_bombPlanted && m_team == CT && GetTask ()->data != -1 && !(waypoints.GetPath (GetTask ()->data)->flags & FLAG_GOAL) && GetTaskId () != TASK_ESCAPEFROMBOMB) - { - DeleteSearchNodes (); - GetTask ()->data = -1; + if (!m_bombSearchOverridden && g_bombPlanted && m_team == TEAM_COUNTER && task ()->data != INVALID_WAYPOINT_INDEX && !(waypoints[task ()->data].flags & FLAG_GOAL) && taskId () != TASK_ESCAPEFROMBOMB) { + clearSearchNodes (); + task ()->data = INVALID_WAYPOINT_INDEX; } - if (!g_bombPlanted && m_currentWaypointIndex != -1 && (m_currentPath->flags & FLAG_GOAL) && Random.Int (0, 100) < 80 && GetNearbyEnemiesNearPosition (pev->origin, 650.0f) == 0) - RadioMessage (Radio_SectorClear); + if (!g_bombPlanted && m_currentWaypointIndex != INVALID_WAYPOINT_INDEX && (m_currentPath->flags & FLAG_GOAL) && rng.getInt (0, 100) < 50 && numEnemiesNear (pev->origin, 650.0f) == 0) { + pushRadioMessage (RADIO_SECTOR_CLEAR); + } // reached the destination (goal) waypoint? - if (DoWaypointNav ()) - { - TaskComplete (); - m_prevGoalIndex = -1; - + if (processNavigation ()) { + + completeTask (); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + // spray logo sometimes if allowed to do so - if (m_timeLogoSpray < engine.Time () && yb_spraypaints.GetBool () && Random.Int (1, 100) < 60 && m_moveSpeed > GetWalkSpeed () && engine.IsNullEntity (m_pickupItem)) - { - if (!((g_mapType & MAP_DE) && g_bombPlanted && m_team == CT)) - PushTask (TASK_SPRAY, TASKPRI_SPRAYLOGO, -1, engine.Time () + 1.0f, false); + if (m_timeLogoSpray < engine.timebase () && yb_spraypaints.boolean () && rng.getInt (1, 100) < 60 && m_moveSpeed > getShiftSpeed () && engine.isNullEntity (m_pickupItem)) { + if (!((g_mapFlags & MAP_DE) && g_bombPlanted && m_team == TEAM_COUNTER)) { + startTask (TASK_SPRAY, TASKPRI_SPRAYLOGO, INVALID_WAYPOINT_INDEX, engine.timebase () + 1.0f, false); + } } // reached waypoint is a camp waypoint - if ((m_currentPath->flags & FLAG_CAMP) && !(g_gameFlags & GAME_CSDM) && yb_camping_allowed.GetBool ()) - { + if ((m_currentPath->flags & FLAG_CAMP) && !(g_gameFlags & GAME_CSDM) && yb_camping_allowed.boolean ()) { + // check if bot has got a primary weapon and hasn't camped before - if (HasPrimaryWeapon () && m_timeCamping + 10.0f < engine.Time () && !HasHostage ()) - { + if (hasPrimaryWeapon () && m_timeCamping + 10.0f < engine.timebase () && !hasHostage ()) { bool campingAllowed = true; // Check if it's not allowed for this team to camp here - if (m_team == TERRORIST) - { - if (m_currentPath->flags & FLAG_CF_ONLY) + if (m_team == TEAM_TERRORIST) { + if (m_currentPath->flags & FLAG_CF_ONLY) { campingAllowed = false; + } } - else - { - if (m_currentPath->flags & FLAG_TF_ONLY) + else { + if (m_currentPath->flags & FLAG_TF_ONLY) { campingAllowed = false; + } } // don't allow vip on as_ maps to camp + don't allow terrorist carrying c4 to camp - if (campingAllowed && (IsPlayerVIP (GetEntity ()) || ((g_mapType & MAP_DE) && m_team == TERRORIST && !g_bombPlanted && m_hasC4))) + if (campingAllowed && (m_isVIP || ((g_mapFlags & MAP_DE) && m_team == TEAM_TERRORIST && !g_bombPlanted && m_hasC4))) { campingAllowed = false; + } // check if another bot is already camping here - if (campingAllowed && IsPointOccupied (m_currentWaypointIndex)) + if (campingAllowed && isOccupiedPoint (m_currentWaypointIndex)) { campingAllowed = false; + } - if (campingAllowed) - { + if (campingAllowed) { // crouched camping here? - if (m_currentPath->flags & FLAG_CROUCH) + if (m_currentPath->flags & FLAG_CROUCH) { m_campButtons = IN_DUCK; - else + } + else { m_campButtons = 0; + } + selectBestWeapon (); - SelectBestWeapon (); - - if (!(m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) && !m_reloadState) + if (!(m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) && !m_reloadState) { m_reloadState = RELOAD_PRIMARY; + } + makeVectors (pev->v_angle); - MakeVectors (pev->v_angle); - - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (20.0f, 40.0f), true); + m_timeCamping = engine.timebase () + rng.getFloat (10.0f, 25.0f); + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, m_timeCamping, true); m_camp = Vector (m_currentPath->campStartX, m_currentPath->campStartY, 0.0f); m_aimFlags |= AIM_CAMP; m_campDirection = 0; // tell the world we're camping - if (Random.Int (0, 100) < 60) - RadioMessage (Radio_InPosition); - + if (rng.getInt (0, 100) < 40) { + pushRadioMessage (RADIO_IN_POSITION); + } m_moveToGoal = false; m_checkTerrain = false; @@ -3141,132 +3083,124 @@ void Bot::RunTask_Normal (void) } } } - else - { + else { // some goal waypoints are map dependant so check it out... - if (g_mapType & MAP_CS) - { + if (g_mapFlags & MAP_CS) { // CT Bot has some hostages following? - if (m_team == CT && HasHostage ()) - { + if (m_team == TEAM_COUNTER && hasHostage ()) { // and reached a Rescue Point? - if (m_currentPath->flags & FLAG_RESCUE) - { - for (int i = 0; i < MAX_HOSTAGES; i++) - m_hostages[i] = nullptr; // clear array of hostage pointers + if (m_currentPath->flags & FLAG_RESCUE) { + m_hostages.clear (); } } - else if (m_team == TERRORIST && Random.Int (0, 100) < 75) - { - int index = FindDefendWaypoint (m_currentPath->origin); + else if (m_team == TEAM_TERRORIST && rng.getInt (0, 100) < 75) { + int index = getDefendPoint (m_currentPath->origin); - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (60.0f, 120.0f), true); // push camp task on to stack - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.Time () + Random.Float (5.0f, 10.0f), true); // push move command + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (60.0f, 120.0f), true); // push camp task on to stack + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.timebase () + rng.getFloat (5.0f, 10.0f), true); // push move command - auto path = waypoints.GetPath (index); + auto &path = waypoints[index]; // decide to duck or not to duck - if (path->vis.crouch <= path->vis.stand) + if (path.vis.crouch <= path.vis.stand) { m_campButtons |= IN_DUCK; - else + } + else { m_campButtons &= ~IN_DUCK; - - ChatterMessage (Chatter_GoingToGuardVIPSafety); // play info about that + } + pushChatterMessage (CHATTER_GOING_TO_GUARD_VIP_SAFETY); // play info about that } } - else if ((g_mapType & MAP_DE) && ((m_currentPath->flags & FLAG_GOAL) || m_inBombZone)) - { + else if ((g_mapFlags & MAP_DE) && ((m_currentPath->flags & FLAG_GOAL) || m_inBombZone)) { // is it a terrorist carrying the bomb? - if (m_hasC4) - { - if ((m_states & STATE_SEEING_ENEMY) && GetNearbyFriendsNearPosition (pev->origin, 768.0f) == 0) - { + if (m_hasC4) { + if ((m_states & STATE_SEEING_ENEMY) && numFriendsNear (pev->origin, 768.0f) == 0) { // request an help also - RadioMessage (Radio_NeedBackup); - InstantChatterMessage (Chatter_ScaredEmotion); + pushRadioMessage (RADIO_NEED_BACKUP); + instantChatter (CHATTER_SCARED_EMOTE); - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (4.0f, 8.0f), true); + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (4.0f, 8.0f), true); + } + else { + startTask (TASK_PLANTBOMB, TASKPRI_PLANTBOMB, INVALID_WAYPOINT_INDEX, 0.0f, false); } - else - PushTask (TASK_PLANTBOMB, TASKPRI_PLANTBOMB, -1, 0.0f, false); } - else if (m_team == CT) - { - if (!g_bombPlanted && GetNearbyFriendsNearPosition (pev->origin, 210.0f) < 4) - { - int index = FindDefendWaypoint (m_currentPath->origin); + else if (m_team == TEAM_COUNTER) { + if (!g_bombPlanted && numFriendsNear (pev->origin, 210.0f) < 4) { + int index = getDefendPoint (m_currentPath->origin); - float campTime = Random.Float (25.0f, 40.f); + float campTime = rng.getFloat (25.0f, 40.f); // rusher bots don't like to camp too much - if (m_personality == PERSONALITY_RUSHER) + if (m_personality == PERSONALITY_RUSHER) { campTime *= 0.5f; + } + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + campTime, true); // push camp task on to stack + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.timebase () + rng.getFloat (5.0f, 11.0f), true); // push move command - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + campTime, true); // push camp task on to stack - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.Time () + Random.Float (5.0f, 11.0f), true); // push move command - - auto path = waypoints.GetPath (index); + auto &path = waypoints[index]; // decide to duck or not to duck - if (path->vis.crouch <= path->vis.stand) + if (path.vis.crouch <= path.vis.stand) { m_campButtons |= IN_DUCK; - else + } + else { m_campButtons &= ~IN_DUCK; - - ChatterMessage (Chatter_DefendingBombSite); // play info about that + } + pushChatterMessage (CHATTER_DEFENDING_BOMBSITE); // play info about that } } } } } // no more nodes to follow - search new ones (or we have a bomb) - else if (!GoalIsValid ()) - { - m_moveSpeed = 0.0f; - DeleteSearchNodes (); + else if (!hasActiveGoal ()) { + m_moveSpeed = pev->maxspeed; + + clearSearchNodes (); + ignoreCollision (); // did we already decide about a goal before? - int destIndex = GetTask ()->data != -1 ? GetTask ()->data : FindGoal (); + int destIndex = task ()->data != INVALID_WAYPOINT_INDEX ? task ()->data : searchGoal (); m_prevGoalIndex = destIndex; - m_chosenGoalIndex = destIndex; // remember index - GetTask ()->data = destIndex; + task ()->data = destIndex; // do pathfinding if it's not the current waypoint - if (destIndex != m_currentWaypointIndex) - FindPath (m_currentWaypointIndex, destIndex, m_pathType); + if (destIndex != m_currentWaypointIndex) { + searchPath (m_currentWaypointIndex, destIndex, m_pathType); + } } - else - { - if (!(pev->flags & FL_DUCKING) && m_minSpeed != pev->maxspeed) + else { + if (!(pev->flags & FL_DUCKING) && m_minSpeed != pev->maxspeed && m_minSpeed > 1.0f) { m_moveSpeed = m_minSpeed; + } } + float shiftSpeed = getShiftSpeed (); - if ((yb_walking_allowed.GetBool () && mp_footsteps.GetBool ()) && m_difficulty >= 2 && !(m_aimFlags & AIM_ENEMY) && (m_heardSoundTime + 8.0f >= engine.Time () || (m_states & (STATE_HEARING_ENEMY | STATE_SUSPECT_ENEMY))) && GetNearbyEnemiesNearPosition (pev->origin, 1024.0f) >= 1 && !yb_jasonmode.GetBool () && !g_bombPlanted) - m_moveSpeed = GetWalkSpeed (); + if ((!cr::fzero (m_moveSpeed) && m_moveSpeed > shiftSpeed) && (yb_walking_allowed.boolean () && mp_footsteps.boolean ()) && m_difficulty > 2 && !(m_aimFlags & AIM_ENEMY) && (m_heardSoundTime + 6.0f >= engine.timebase () || (m_states & STATE_SUSPECT_ENEMY)) && numEnemiesNear (pev->origin, 768.0f) >= 1 && !yb_jasonmode.boolean () && !g_bombPlanted) { + m_moveSpeed = shiftSpeed; + } // bot hasn't seen anything in a long time and is asking his teammates to report in - if (m_seeEnemyTime + Random.Float (45.0f, 80.0f) < engine.Time () && Random.Int (0, 100) < 30 && g_timeRoundStart + 20.0f < engine.Time () && m_askCheckTime < engine.Time ()) - { - m_askCheckTime = engine.Time () + Random.Float (45.0f, 80.0f); - RadioMessage (Radio_ReportTeam); + if (m_seeEnemyTime + rng.getFloat (45.0f, 80.0f) < engine.timebase () && rng.getInt (0, 100) < 30 && g_timeRoundStart + 20.0f < engine.timebase () && m_askCheckTime < engine.timebase ()) { + m_askCheckTime = engine.timebase () + rng.getFloat (45.0f, 80.0f); + pushRadioMessage (RADIO_REPORT_TEAM); } } -void Bot::RunTask_Spray (void) -{ +void Bot::spraypaint_ (void) { m_aimFlags |= AIM_ENTITY; // bot didn't spray this round? - if (m_timeLogoSpray < engine.Time () && GetTask ()->time > engine.Time ()) - { - MakeVectors (pev->v_angle); - Vector sprayOrigin = EyePosition () + g_pGlobals->v_forward * 128.0f; + if (m_timeLogoSpray < engine.timebase () && task ()->time > engine.timebase ()) { + makeVectors (pev->v_angle); + Vector sprayOrigin = eyePos () + g_pGlobals->v_forward * 128.0f; TraceResult tr; - engine.TestLine (EyePosition (), sprayOrigin, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (eyePos (), sprayOrigin, TRACE_IGNORE_MONSTERS, ent (), &tr); // no wall in front? if (tr.flFraction >= 1.0f) @@ -3274,255 +3208,253 @@ void Bot::RunTask_Spray (void) m_entity = sprayOrigin; - if (GetTask ()->time - 0.5f < engine.Time ()) - { + if (task ()->time - 0.5f < engine.timebase ()) { // emit spraycan sound - EMIT_SOUND_DYN2 (GetEntity (), CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100); - engine.TestLine (EyePosition (), EyePosition () + g_pGlobals->v_forward * 128.0f, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + g_engfuncs.pfnEmitSound (ent (), CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100); + engine.testLine (eyePos (), eyePos () + g_pGlobals->v_forward * 128.0f, TRACE_IGNORE_MONSTERS, ent (), &tr); // paint the actual logo decal - DecalTrace (pev, &tr, m_logotypeIndex); - m_timeLogoSpray = engine.Time () + Random.Float (60.0f, 90.0f); + traceDecals (pev, &tr, m_logotypeIndex); + m_timeLogoSpray = engine.timebase () + rng.getFloat (60.0f, 90.0f); } } - else - TaskComplete (); - + else { + completeTask (); + } m_moveToGoal = false; m_checkTerrain = false; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - IgnoreCollisionShortly (); + ignoreCollision (); } -void Bot::RunTask_HuntEnemy (void) -{ +void Bot::huntEnemy_ (void) { m_aimFlags |= AIM_NAVPOINT; - m_checkTerrain = true; - + // if we've got new enemy... - if (!engine.IsNullEntity (m_enemy) || engine.IsNullEntity (m_lastEnemy)) - { - // forget about it... - TaskComplete (); - m_prevGoalIndex = -1; + if (!engine.isNullEntity (m_enemy) || engine.isNullEntity (m_lastEnemy)) { - m_lastEnemy = nullptr; - m_lastEnemyOrigin.Zero (); + // forget about it... + clearTask (TASK_HUNTENEMY); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; } - else if (engine.GetTeam (m_lastEnemy) == m_team) - { + else if (engine.getTeam (m_lastEnemy) == m_team) { + // don't hunt down our teammate... - RemoveCertainTask (TASK_HUNTENEMY); - m_prevGoalIndex = -1; + clearTask (TASK_HUNTENEMY); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + m_lastEnemy = nullptr; } - else if (DoWaypointNav ()) // reached last enemy pos? + else if (processNavigation ()) // reached last enemy pos? { // forget about it... - TaskComplete (); - m_prevGoalIndex = -1; + completeTask (); - m_lastEnemy = nullptr; - m_lastEnemyOrigin.Zero (); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + m_lastEnemyOrigin.nullify (); } - else if (!GoalIsValid ()) // do we need to calculate a new path? + else if (!hasActiveGoal ()) // do we need to calculate a new path? { - DeleteSearchNodes (); + clearSearchNodes (); - int destIndex = -1; + int destIndex = INVALID_WAYPOINT_INDEX; + int goal = task ()->data; // is there a remembered index? - if (GetTask ()->data != -1 && GetTask ()->data < g_numWaypoints) - destIndex = GetTask ()->data; - else // no. we need to find a new one - destIndex = waypoints.FindNearest (m_lastEnemyOrigin); + if (waypoints.exists (goal)) { + destIndex = goal; + } + + // find new one instead + else { + destIndex = waypoints.getNearest (m_lastEnemyOrigin); + } // remember index m_prevGoalIndex = destIndex; - GetTask ()->data = destIndex; + task ()->data = destIndex; - if (destIndex != m_currentWaypointIndex) - FindPath (m_currentWaypointIndex, destIndex, m_pathType); + if (destIndex != m_currentWaypointIndex) { + searchPath (m_currentWaypointIndex, destIndex, m_pathType); + } } // bots skill higher than 60? - if (yb_walking_allowed.GetBool () && mp_footsteps.GetBool () && m_difficulty >= 1 && !yb_jasonmode.GetBool ()) - { + if (yb_walking_allowed.boolean () && mp_footsteps.boolean () && m_difficulty > 1 && !yb_jasonmode.boolean ()) { // then make him move slow if near enemy - if (!(m_currentTravelFlags & PATHFLAG_JUMP)) - { - if (m_currentWaypointIndex != -1) - { - if (m_currentPath->radius < 32 && !IsOnLadder () && !IsInWater () && m_seeEnemyTime + 4.0f > engine.Time () && m_difficulty < 2) + if (!(m_currentTravelFlags & PATHFLAG_JUMP)) { + if (m_currentWaypointIndex != INVALID_WAYPOINT_INDEX) { + if (m_currentPath->radius < 32.0f && !isOnLadder () && !isInWater () && m_seeEnemyTime + 4.0f > engine.timebase () && m_difficulty < 3) { pev->button |= IN_DUCK; + } } - if ((m_lastEnemyOrigin - pev->origin).GetLength () < 512.0f) - m_moveSpeed = GetWalkSpeed (); + if ((m_lastEnemyOrigin - pev->origin).lengthSq () < cr::square (512.0f)) { + m_moveSpeed = getShiftSpeed (); + } } } } -void Bot::RunTask_SeekCover (void) -{ +void Bot::seekCover_ (void) { m_aimFlags |= AIM_NAVPOINT; - if (engine.IsNullEntity (m_lastEnemy) || !IsAlive (m_lastEnemy)) - { - TaskComplete (); - m_prevGoalIndex = -1; + if (!isAlive (m_lastEnemy)) { + completeTask (); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; } - else if (DoWaypointNav ()) // reached final cover waypoint? - { - // yep. activate hide behaviour - TaskComplete (); - m_prevGoalIndex = -1; - m_pathType = SEARCH_PATH_FASTEST; + // reached final waypoint? + else if (processNavigation ()) { + // yep. activate hide behaviour + completeTask (); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; // start hide task - PushTask (TASK_HIDE, TASKPRI_HIDE, -1, engine.Time () + Random.Float (5.0f, 15.0f), false); + startTask (TASK_HIDE, TASKPRI_HIDE, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (3.0f, 12.0f), false); Vector dest = m_lastEnemyOrigin; // get a valid look direction - GetCampDirection (&dest); + getCampDir (&dest); m_aimFlags |= AIM_CAMP; m_camp = dest; m_campDirection = 0; // chosen waypoint is a camp waypoint? - if (m_currentPath->flags & FLAG_CAMP) - { + if (m_currentPath->flags & FLAG_CAMP) { // use the existing camp wpt prefs - if (m_currentPath->flags & FLAG_CROUCH) + if (m_currentPath->flags & FLAG_CROUCH) { m_campButtons = IN_DUCK; - else + } + else { m_campButtons = 0; + } } - else - { + else { // choose a crouch or stand pos - if (m_currentPath->vis.crouch <= m_currentPath->vis.stand) + if (m_currentPath->vis.crouch <= m_currentPath->vis.stand) { m_campButtons = IN_DUCK; - else + } + else { m_campButtons = 0; + } // enter look direction from previously calculated positions m_currentPath->campStartX = dest.x; m_currentPath->campStartY = dest.y; - m_currentPath->campStartX = dest.x; + m_currentPath->campEndX = dest.x; m_currentPath->campEndY = dest.y; } - if (m_reloadState == RELOAD_NONE && GetAmmoInClip () < 8 && GetAmmo () != 0) + if (m_reloadState == RELOAD_NONE && ammoClip () < 5 && ammo () != 0) { m_reloadState = RELOAD_PRIMARY; - + } m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; m_moveToGoal = false; - m_checkTerrain = true; + m_checkTerrain = false; } - else if (!GoalIsValid ()) // we didn't choose a cover waypoint yet or lost it due to an attack? + else if (!hasActiveGoal ()) // we didn't choose a cover waypoint yet or lost it due to an attack? { - DeleteSearchNodes (); + clearSearchNodes (); + int destIndex = INVALID_WAYPOINT_INDEX; - int destIndex = -1; + if (task ()->data != INVALID_WAYPOINT_INDEX) { + destIndex = task ()->data; + } + else { + destIndex = getCoverPoint (usesSniper () ? 256.0f : 512.0f); - if (GetTask ()->data != -1) - destIndex = GetTask ()->data; - else - { - destIndex = FindCoverWaypoint (1024.0f); + if (destIndex == INVALID_WAYPOINT_INDEX) { + m_retreatTime = engine.timebase () + rng.getFloat (5.0f, 10.0f); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; - if (destIndex == -1) - destIndex = waypoints.FindNearest (pev->origin, 512.0f); + completeTask (); + return; + } } m_campDirection = 0; m_prevGoalIndex = destIndex; - GetTask ()->data = destIndex; + task ()->data = destIndex; - if (destIndex != m_currentWaypointIndex) - FindPath (m_currentWaypointIndex, destIndex, SEARCH_PATH_FASTEST); + if (destIndex != m_currentWaypointIndex) { + searchPath (m_currentWaypointIndex, destIndex, SEARCH_PATH_FASTEST); + } } } -void Bot::RunTask_Attack (void) -{ +void Bot::attackEnemy_ (void) { m_moveToGoal = false; m_checkTerrain = false; - if (!engine.IsNullEntity (m_enemy)) - { - IgnoreCollisionShortly (); + if (!engine.isNullEntity (m_enemy)) { + ignoreCollision (); - if (IsOnLadder ()) - { + if (isOnLadder ()) { pev->button |= IN_JUMP; - DeleteSearchNodes (); + clearSearchNodes (); } - CombatFight (); + attackMovement (); - if (m_currentWeapon == WEAPON_KNIFE && !m_lastEnemyOrigin.IsZero ()) + if (m_currentWeapon == WEAPON_KNIFE && !m_lastEnemyOrigin.empty ()) { m_destOrigin = m_lastEnemyOrigin; + } } - else - { - TaskComplete (); + else { + completeTask (); m_destOrigin = m_lastEnemyOrigin; } - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); } -void Bot::RunTask_Pause (void) -{ +void Bot::pause_ (void) { m_moveToGoal = false; m_checkTerrain = false; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; m_aimFlags |= AIM_NAVPOINT; // is bot blinded and above average difficulty? - if (m_viewDistance < 500.0f && m_difficulty >= 2) - { + if (m_viewDistance < 500.0f && m_difficulty >= 2) { // go mad! - m_moveSpeed = -fabsf ((m_viewDistance - 500.0f) * 0.5f); + m_moveSpeed = -cr::abs ((m_viewDistance - 500.0f) * 0.5f); - if (m_moveSpeed < -pev->maxspeed) + if (m_moveSpeed < -pev->maxspeed) { m_moveSpeed = -pev->maxspeed; - - MakeVectors (pev->v_angle); - m_camp = EyePosition () + g_pGlobals->v_forward * 500.0f; + } + makeVectors (pev->v_angle); + m_camp = eyePos () + g_pGlobals->v_forward * 500.0f; m_aimFlags |= AIM_OVERRIDE; m_wantsToFire = true; } - else + else { pev->button |= m_campButtons; + } // stop camping if time over or gets hurt by something else than bullets - if (GetTask ()->time < engine.Time () || m_lastDamageType > 0) - TaskComplete (); + if (task ()->time < engine.timebase () || m_lastDamageType > 0) { + completeTask (); + } } -void Bot::RunTask_Blinded (void) -{ +void Bot::blind_ (void) { m_moveToGoal = false; m_checkTerrain = false; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); // if bot remembers last enemy position - if (m_difficulty >= 2 && !m_lastEnemyOrigin.IsZero () && IsValidPlayer (m_lastEnemy) && !UsesSniper ()) - { + if (m_difficulty >= 2 && !m_lastEnemyOrigin.empty () && isPlayer (m_lastEnemy) && !usesSniper ()) { m_lookAt = m_lastEnemyOrigin; // face last enemy m_wantsToFire = true; // and shoot it } @@ -3531,15 +3463,14 @@ void Bot::RunTask_Blinded (void) m_strafeSpeed = m_blindSidemoveSpeed; pev->button |= m_blindButton; - if (m_blindTime < engine.Time ()) - TaskComplete (); + if (m_blindTime < engine.timebase ()) { + completeTask (); + } } -void Bot::RunTask_Camp (void) -{ - if (!yb_camping_allowed.GetBool ()) - { - TaskComplete (); +void Bot::camp_ (void) { + if (!yb_camping_allowed.boolean ()) { + completeTask (); return; } @@ -3547,42 +3478,38 @@ void Bot::RunTask_Camp (void) m_checkTerrain = false; m_moveToGoal = false; - if (m_team == CT && g_bombPlanted && m_defendedBomb && !IsBombDefusing (waypoints.GetBombPosition ()) && !OutOfBombTimer ()) - { + if (m_team == TEAM_COUNTER && g_bombPlanted && m_defendedBomb && !isBombDefusing (waypoints.getBombPos ()) && !isOutOfBombTimer ()) { m_defendedBomb = false; - TaskComplete (); + completeTask (); } + ignoreCollision (); // half the reaction time if camping because you're more aware of enemies if camping - SetIdealReactionTimes (); + setIdealReactionTimers (); m_idealReactionTime *= 0.5f; - m_navTimeset = engine.Time (); - m_timeCamping = engine.Time (); + m_navTimeset = engine.timebase (); + m_timeCamping = engine.timebase (); m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - GetValidWaypoint (); + getValidPoint (); - if (m_nextCampDirTime < engine.Time ()) - { - m_nextCampDirTime = engine.Time () + Random.Float (2.0f, 5.0f); + if (m_nextCampDirTime < engine.timebase ()) { + m_nextCampDirTime = engine.timebase () + rng.getFloat (2.0f, 5.0f); - if (m_currentPath->flags & FLAG_CAMP) - { + if (m_currentPath->flags & FLAG_CAMP) { Vector dest; // switch from 1 direction to the other - if (m_campDirection < 1) - { + if (m_campDirection < 1) { dest.x = m_currentPath->campStartX; dest.y = m_currentPath->campStartY; m_campDirection ^= 1; } - else - { + else { dest.x = m_currentPath->campEndX; dest.y = m_currentPath->campEndY; m_campDirection ^= 1; @@ -3592,29 +3519,25 @@ void Bot::RunTask_Camp (void) // find a visible waypoint to this direction... // i know this is ugly hack, but i just don't want to break compatibility :) int numFoundPoints = 0; + int campPoints[3] = { 0, }; int distances[3] = { 0, }; - const Vector &dotA = (dest - pev->origin).Normalize2D (); + const Vector &dotA = (dest - pev->origin).normalize2D (); - for (int i = 0; i < g_numWaypoints; i++) - { + for (int i = 0; i < waypoints.length (); i++) { // skip invisible waypoints or current waypoint - if (!waypoints.IsVisible (m_currentWaypointIndex, i) || (i == m_currentWaypointIndex)) + if (!waypoints.isVisible (m_currentWaypointIndex, i) || (i == m_currentWaypointIndex)) { continue; + } + const Vector &dotB = (waypoints[i].origin - pev->origin).normalize2D (); - const Vector &dotB = (waypoints.GetPath (i)->origin - pev->origin).Normalize2D (); + if ((dotA | dotB) > 0.9f) { + int distance = static_cast ((pev->origin - waypoints[i].origin).length ()); - if ((dotA | dotB) > 0.9f) - { - int distance = static_cast ((pev->origin - waypoints.GetPath (i)->origin).GetLength ()); - - if (numFoundPoints >= 3) - { - for (int j = 0; j < 3; j++) - { - if (distance > distances[j]) - { + if (numFoundPoints >= 3) { + for (int j = 0; j < 3; j++) { + if (distance > distances[j]) { distances[j] = distance; campPoints[j] = i; @@ -3622,8 +3545,7 @@ void Bot::RunTask_Camp (void) } } } - else - { + else { campPoints[numFoundPoints] = i; distances[numFoundPoints] = distance; @@ -3632,258 +3554,273 @@ void Bot::RunTask_Camp (void) } } - if (--numFoundPoints >= 0) - m_camp = waypoints.GetPath (campPoints[Random.Int (0, numFoundPoints)])->origin; - else - m_camp = waypoints.GetPath (GetCampAimingWaypoint ())->origin; + if (--numFoundPoints >= 0) { + m_camp = waypoints[campPoints[rng.getInt (0, numFoundPoints)]].origin; + } + else { + m_camp = waypoints[searchCampDir ()].origin; + } } else - m_camp = waypoints.GetPath (GetCampAimingWaypoint ())->origin; + m_camp = waypoints[searchCampDir ()].origin; } // press remembered crouch button pev->button |= m_campButtons; // stop camping if time over or gets hurt by something else than bullets - if (GetTask ()->time < engine.Time () || m_lastDamageType > 0) - TaskComplete (); + if (task ()->time < engine.timebase () || m_lastDamageType > 0) { + completeTask (); + } } -void Bot::RunTask_Hide (void) -{ +void Bot::hide_ (void) { m_aimFlags |= AIM_CAMP; m_checkTerrain = false; m_moveToGoal = false; // half the reaction time if camping - SetIdealReactionTimes (); + setIdealReactionTimers (); m_idealReactionTime *= 0.5f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - GetValidWaypoint (); + getValidPoint (); - if (HasShield () && !m_isReloading) - { - if (!IsShieldDrawn ()) + if (hasShield () && !m_isReloading) { + if (!isShieldDrawn ()) { pev->button |= IN_ATTACK2; // draw the shield! - else + } + else { pev->button |= IN_DUCK; // duck under if the shield is already drawn + } } // if we see an enemy and aren't at a good camping point leave the spot - if ((m_states & STATE_SEEING_ENEMY) || m_inBombZone) - { - if (!(m_currentPath->flags & FLAG_CAMP)) - { - TaskComplete (); + if ((m_states & STATE_SEEING_ENEMY) || m_inBombZone) { + if (!(m_currentPath->flags & FLAG_CAMP)) { + completeTask (); m_campButtons = 0; - m_prevGoalIndex = -1; - - if (!engine.IsNullEntity (m_enemy)) - CombatFight (); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + if (!engine.isNullEntity (m_enemy)) { + attackMovement (); + } return; } } - else if (m_lastEnemyOrigin.IsZero ()) // If we don't have an enemy we're also free to leave - { - TaskComplete (); + + // if we don't have an enemy we're also free to leave + else if (m_lastEnemyOrigin.empty ()) { + completeTask (); m_campButtons = 0; - m_prevGoalIndex = -1; - - if (GetTaskId () == TASK_HIDE) - TaskComplete (); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + if (taskId () == TASK_HIDE) { + completeTask (); + } return; } pev->button |= m_campButtons; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); // stop camping if time over or gets hurt by something else than bullets - if (GetTask ()->time < engine.Time () || m_lastDamageType > 0) - TaskComplete (); + if (task ()->time < engine.timebase () || m_lastDamageType > 0) { + completeTask (); + } } -void Bot::RunTask_MoveToPos (void) -{ +void Bot::moveToPos_ (void) { m_aimFlags |= AIM_NAVPOINT; - if (IsShieldDrawn ()) + if (isShieldDrawn ()) { pev->button |= IN_ATTACK2; - - if (DoWaypointNav ()) // reached destination? - { - TaskComplete (); // we're done - - m_prevGoalIndex = -1; - m_position.Zero (); } - else if (!GoalIsValid ()) // didn't choose goal waypoint yet? - { - DeleteSearchNodes (); - int destIndex = -1; + // reached destination? + if (processNavigation ()) { + completeTask (); // we're done - if (GetTask ()->data != -1 && GetTask ()->data < g_numWaypoints) - destIndex = GetTask ()->data; - else - destIndex = waypoints.FindNearest (m_position); + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + m_position.nullify (); + } - if (destIndex >= 0 && destIndex < g_numWaypoints) - { - m_prevGoalIndex = destIndex; - GetTask ()->data = destIndex; + // didn't choose goal waypoint yet? + else if (!hasActiveGoal ()) { + clearSearchNodes (); - FindPath (m_currentWaypointIndex, destIndex, m_pathType); + int destIndex = INVALID_WAYPOINT_INDEX; + int goal = task ()->data; + + if (waypoints.exists (goal)) { + destIndex = goal; + } + else { + destIndex = waypoints.getNearest (m_position); + } + if (waypoints.exists (destIndex)) { + m_prevGoalIndex = destIndex; + task ()->data = destIndex; + + searchPath (m_currentWaypointIndex, destIndex, m_pathType); + } + else { + completeTask (); } - else - TaskComplete (); } } -void Bot::RunTask_PlantBomb (void) -{ +void Bot::plantBomb_ (void) { m_aimFlags |= AIM_CAMP; - if (m_hasC4) // we're still got the C4? - { - SelectWeaponByName ("weapon_c4"); + // we're still got the C4? + if (m_hasC4) { + selectWeaponByName ("weapon_c4"); - if (IsAlive (m_enemy) || !m_inBombZone) - TaskComplete (); - else - { + if (isAlive (m_enemy) || !m_inBombZone) { + completeTask (); + } + else { m_moveToGoal = false; m_checkTerrain = false; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); - if (m_currentPath->flags & FLAG_CROUCH) + if (m_currentPath->flags & FLAG_CROUCH) { pev->button |= (IN_ATTACK | IN_DUCK); - else + } + else { pev->button |= IN_ATTACK; - + } m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; } } - else // done with planting - { - TaskComplete (); + + // done with planting + else { + completeTask (); // tell teammates to move over here... - if (GetNearbyFriendsNearPosition (pev->origin, 1200.0f) != 0) - RadioMessage (Radio_NeedBackup); + if (numFriendsNear (pev->origin, 1200.0f) != 0) { + pushRadioMessage (RADIO_NEED_BACKUP); + } + clearSearchNodes (); + int index = getDefendPoint (pev->origin); - DeleteSearchNodes (); - int index = FindDefendWaypoint (pev->origin); - - float guardTime = mp_c4timer.GetFloat () * 0.5f + mp_c4timer.GetFloat () * 0.25f; + float guardTime = mp_c4timer.flt () * 0.5f + mp_c4timer.flt () * 0.25f; // push camp task on to stack - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + guardTime, true); - // push move command - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.Time () + guardTime, true); + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + guardTime, true); - if (waypoints.GetPath (index)->vis.crouch <= waypoints.GetPath (index)->vis.stand) + // push move command + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, engine.timebase () + guardTime, true); + + if (waypoints[index].vis.crouch <= waypoints[index].vis.stand) { m_campButtons |= IN_DUCK; - else + } + else { m_campButtons &= ~IN_DUCK; + } } } -void Bot::RunTask_DefuseBomb (void) -{ +void Bot::bombDefuse_ (void) { float fullDefuseTime = m_hasDefuser ? 7.0f : 12.0f; - float timeToBlowUp = GetBombTimeleft (); + float timeToBlowUp = getBombTimeleft (); float defuseRemainingTime = fullDefuseTime; - if (m_hasProgressBar /*&& IsOnFloor ()*/) - defuseRemainingTime = fullDefuseTime - engine.Time (); + if (m_hasProgressBar /*&& isOnFloor ()*/) { + defuseRemainingTime = fullDefuseTime - engine.timebase (); + } + bool pickupExists = !engine.isNullEntity (m_pickupItem); + const Vector &bombPos = pickupExists ? m_pickupItem->v.origin : waypoints.getBombPos (); + + if (pickupExists) { + if (waypoints.getBombPos () != bombPos) { + waypoints.setBombPos (bombPos); + } + } bool defuseError = false; // exception: bomb has been defused - if (waypoints.GetBombPosition ().IsZero ()) - { + if (bombPos.empty ()) { defuseError = true; g_bombPlanted = false; - if (Random.Int (0, 100) < 50 && m_numFriendsLeft != 0) - { - if (timeToBlowUp <= 3.0) - { - if (yb_communication_type.GetInt () == 2) - InstantChatterMessage (Chatter_BarelyDefused); - else if (yb_communication_type.GetInt () == 1) - RadioMessage (Radio_SectorClear); + if (rng.getInt (0, 100) < 50 && m_numFriendsLeft != 0) { + if (timeToBlowUp <= 3.0) { + if (yb_communication_type.integer () == 2) { + instantChatter (CHATTER_BARELY_DEFUSED); + } + else if (yb_communication_type.integer () == 1) { + pushRadioMessage (RADIO_SECTOR_CLEAR); + } + } + else { + pushRadioMessage (RADIO_SECTOR_CLEAR); } - else - RadioMessage (Radio_SectorClear); } } - else if (defuseRemainingTime > timeToBlowUp) // exception: not time left for defusing + else if (defuseRemainingTime > timeToBlowUp) { defuseError = true; - else if (IsValidPlayer (m_enemy)) - { - int friends = GetNearbyFriendsNearPosition (pev->origin, 768.0f); + } + else if (m_states & STATE_SEEING_ENEMY) { + int friends = numFriendsNear (pev->origin, 768.0f); - if (friends < 2 && defuseRemainingTime < timeToBlowUp) - { + if (friends < 2 && defuseRemainingTime < timeToBlowUp) { defuseError = true; - if (defuseRemainingTime + 2.0f > timeToBlowUp) + if (defuseRemainingTime + 2.0f > timeToBlowUp) { defuseError = false; + } - if (m_numFriendsLeft > friends) - RadioMessage (Radio_NeedBackup); + if (m_numFriendsLeft > friends) { + pushRadioMessage (RADIO_NEED_BACKUP); + } } } // one of exceptions is thrown. finish task. - if (defuseError) - { + if (defuseError) { m_checkTerrain = true; m_moveToGoal = true; - m_destOrigin.Zero (); - m_entity.Zero (); + m_destOrigin.nullify (); + m_entity.nullify (); m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; - TaskComplete (); + completeTask (); return; } - // to revert from pause after reload waiting && just to be sure + // to revert from pause after reload ting && just to be sure m_moveToGoal = false; - m_checkTerrain = true; + m_checkTerrain = false; m_moveSpeed = pev->maxspeed; m_strafeSpeed = 0.0f; // bot is reloading and we close enough to start defusing - if (m_isReloading && (waypoints.GetBombPosition () - pev->origin).GetLength2D () < 80.0f) - { - if (m_numEnemiesLeft == 0 || timeToBlowUp < fullDefuseTime + 7.0f || ((GetAmmoInClip () > 8 && m_reloadState == RELOAD_PRIMARY) || (GetAmmoInClip () > 5 && m_reloadState == RELOAD_SECONDARY))) - { - int weaponIndex = GetHighestWeapon (); + if (m_isReloading && (bombPos - pev->origin).length2D () < 80.0f) { + if (m_numEnemiesLeft == 0 || timeToBlowUp < fullDefuseTime + 7.0f || ((ammoClip () > 8 && m_reloadState == RELOAD_PRIMARY) || (ammoClip () > 5 && m_reloadState == RELOAD_SECONDARY))) { + int weaponIndex = bestWeaponCarried (); // just select knife and then select weapon - SelectWeaponByName ("weapon_knife"); - - if (weaponIndex > 0 && weaponIndex < NUM_WEAPONS) - SelectWeaponbyNumber (weaponIndex); + selectWeaponByName ("weapon_knife"); + if (weaponIndex > 0 && weaponIndex < NUM_WEAPONS) { + selectWeaponById (weaponIndex); + } m_isReloading = false; } - else // just wait here - { + else { m_moveToGoal = false; m_checkTerrain = false; @@ -3895,56 +3832,55 @@ void Bot::RunTask_DefuseBomb (void) // head to bomb and press use button m_aimFlags |= AIM_ENTITY; - m_destOrigin = waypoints.GetBombPosition (); - m_entity = waypoints.GetBombPosition (); + m_destOrigin = bombPos; + m_entity = bombPos; pev->button |= IN_USE; // if defusing is not already started, maybe crouch before - if (!m_hasProgressBar && m_duckDefuseCheckTime < engine.Time ()) - { - if (m_difficulty >= 2 && m_numEnemiesLeft != 0) + if (!m_hasProgressBar && m_duckDefuseCheckTime < engine.timebase ()) { + if (m_difficulty >= 2 && m_numEnemiesLeft != 0) { m_duckDefuse = true; - + } Vector botDuckOrigin, botStandOrigin; - if (pev->button & IN_DUCK) - { + if (pev->button & IN_DUCK) { botDuckOrigin = pev->origin; botStandOrigin = pev->origin + Vector (0.0f, 0.0f, 18.0f); } - else - { + else { botDuckOrigin = pev->origin - Vector (0.0f, 0.0f, 18.0f); botStandOrigin = pev->origin; } - float duckLength = (m_entity - botDuckOrigin).GetLengthSquared (); - float standLength = (m_entity - botStandOrigin).GetLengthSquared (); + float duckLength = (m_entity - botDuckOrigin).lengthSq (); + float standLength = (m_entity - botStandOrigin).lengthSq (); - if (duckLength > 5625.0f || standLength > 5625.0f) - { - if (standLength < duckLength) + if (duckLength > 5625.0f || standLength > 5625.0f) { + if (standLength < duckLength) { m_duckDefuse = false; // stand - else + } + else { m_duckDefuse = true; // duck + } } - m_duckDefuseCheckTime = engine.Time () + 1.5f; + m_duckDefuseCheckTime = engine.timebase () + 1.5f; } // press duck button - if (m_duckDefuse || (pev->oldbuttons & IN_DUCK)) + if (m_duckDefuse || (m_oldButtons & IN_DUCK)) { pev->button |= IN_DUCK; - else + } + else { pev->button &= ~IN_DUCK; + } // we are defusing bomb - if (m_hasProgressBar) - { + if (m_hasProgressBar || pickupExists || (m_oldButtons & IN_USE)) { pev->button |= IN_USE; m_reloadState = RELOAD_NONE; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); // don't move when defusing m_moveToGoal = false; @@ -3954,67 +3890,66 @@ void Bot::RunTask_DefuseBomb (void) m_strafeSpeed = 0.0f; // notify team - if (m_numFriendsLeft != 0) - { - ChatterMessage (Chatter_DefusingC4); + if (m_numFriendsLeft != 0) { + pushChatterMessage (CHATTER_DEFUSING_BOMB); - if (GetNearbyFriendsNearPosition (pev->origin, 512.0f) < 2) - RadioMessage (Radio_NeedBackup); + if (numFriendsNear (pev->origin, 512.0f) < 2) { + pushRadioMessage (RADIO_NEED_BACKUP); + } } } + else + completeTask (); } -void Bot::RunTask_FollowUser (void) -{ - if (engine.IsNullEntity (m_targetEntity) || !IsAlive (m_targetEntity)) - { +void Bot::followUser_ (void) { + if (engine.isNullEntity (m_targetEntity) || !isAlive (m_targetEntity)) { m_targetEntity = nullptr; - TaskComplete (); + completeTask (); return; } - if (m_targetEntity->v.button & IN_ATTACK) - { - MakeVectors (m_targetEntity->v.v_angle); + if (m_targetEntity->v.button & IN_ATTACK) { + makeVectors (m_targetEntity->v.v_angle); TraceResult tr; - engine.TestLine (m_targetEntity->v.origin + m_targetEntity->v.view_ofs, g_pGlobals->v_forward * 500.0f, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (m_targetEntity->v.origin + m_targetEntity->v.view_ofs, g_pGlobals->v_forward * 500.0f, TRACE_IGNORE_EVERYTHING, ent (), &tr); - if (!engine.IsNullEntity (tr.pHit) && IsValidPlayer (tr.pHit) && engine.GetTeam (tr.pHit) != m_team) - { + if (!engine.isNullEntity (tr.pHit) && isPlayer (tr.pHit) && engine.getTeam (tr.pHit) != m_team) { m_targetEntity = nullptr; m_lastEnemy = tr.pHit; m_lastEnemyOrigin = tr.pHit->v.origin; - TaskComplete (); + completeTask (); return; } } - if (m_targetEntity->v.maxspeed != 0 && m_targetEntity->v.maxspeed < pev->maxspeed) + if (m_targetEntity->v.maxspeed != 0 && m_targetEntity->v.maxspeed < pev->maxspeed) { m_moveSpeed = m_targetEntity->v.maxspeed; + } - if (m_reloadState == RELOAD_NONE && GetAmmo () != 0) + if (m_reloadState == RELOAD_NONE && ammo () != 0) { m_reloadState = RELOAD_PRIMARY; + } - if ((m_targetEntity->v.origin - pev->origin).GetLength () > 130) + if ((m_targetEntity->v.origin - pev->origin).lengthSq () > cr::square (130.0f)) { m_followWaitTime = 0.0f; - else - { + } + else { m_moveSpeed = 0.0f; - if (m_followWaitTime == 0.0f) - m_followWaitTime = engine.Time (); - else - { - if (m_followWaitTime + 3.0f < engine.Time ()) - { + if (m_followWaitTime == 0.0f) { + m_followWaitTime = engine.timebase (); + } + else { + if (m_followWaitTime + 3.0f < engine.timebase ()) { // stop following if we have been waiting too long m_targetEntity = nullptr; - RadioMessage (Radio_YouTakePoint); - TaskComplete (); + pushRadioMessage (RADIO_YOU_TAKE_THE_POINT); + completeTask (); return; } @@ -4022,429 +3957,414 @@ void Bot::RunTask_FollowUser (void) } m_aimFlags |= AIM_NAVPOINT; - if (yb_walking_allowed.GetBool () && m_targetEntity->v.maxspeed < m_moveSpeed && !yb_jasonmode.GetBool ()) - m_moveSpeed = GetWalkSpeed (); + if (yb_walking_allowed.boolean () && m_targetEntity->v.maxspeed < m_moveSpeed && !yb_jasonmode.boolean ()) { + m_moveSpeed = getShiftSpeed (); + } - if (IsShieldDrawn ()) + if (isShieldDrawn ()) { pev->button |= IN_ATTACK2; + } - if (DoWaypointNav ()) // reached destination? - GetTask ()->data = -1; + // reached destination? + if (processNavigation ()) { + task ()->data = INVALID_WAYPOINT_INDEX; + } - if (!GoalIsValid ()) // didn't choose goal waypoint yet? - { - DeleteSearchNodes (); + // didn't choose goal waypoint yet? + if (!hasActiveGoal ()) { + clearSearchNodes (); - int destIndex = waypoints.FindNearest (m_targetEntity->v.origin); - - Array points; - waypoints.FindInRadius (points, 200.0f, m_targetEntity->v.origin); - - while (!points.IsEmpty ()) - { - int newIndex = points.Pop (); + int destIndex = waypoints.getNearest (m_targetEntity->v.origin); + IntArray points = waypoints.searchRadius (200.0f, m_targetEntity->v.origin); + for (auto &newIndex : points) { // if waypoint not yet used, assign it as dest - if (!IsPointOccupied (newIndex) && newIndex != m_currentWaypointIndex) + if (newIndex != m_currentWaypointIndex && !isOccupiedPoint (newIndex)) { destIndex = newIndex; + } } - if (destIndex >= 0 && destIndex < g_numWaypoints && destIndex != m_currentWaypointIndex && m_currentWaypointIndex >= 0 && m_currentWaypointIndex < g_numWaypoints) - { + if (waypoints.exists (destIndex) && waypoints.exists (m_currentWaypointIndex)) { m_prevGoalIndex = destIndex; - GetTask ()->data = destIndex; + task ()->data = destIndex; // always take the shortest path - FindShortestPath (m_currentWaypointIndex, destIndex); + searchShortestPath (m_currentWaypointIndex, destIndex); } - else - { + else { m_targetEntity = nullptr; - TaskComplete (); + completeTask (); } } } -void Bot::RunTask_Throw_HE (void) -{ +void Bot::throwExplosive_ (void) { m_aimFlags |= AIM_GRENADE; Vector dest = m_throw; - if (!(m_states & STATE_SEEING_ENEMY)) - { + if (!(m_states & STATE_SEEING_ENEMY)) { m_strafeSpeed = 0.0f; m_moveSpeed = 0.0f; m_moveToGoal = false; } - else if (!(m_states & STATE_SUSPECT_ENEMY) && !engine.IsNullEntity (m_enemy)) - dest = m_enemy->v.origin + m_enemy->v.velocity.Get2D () * 0.5f; - + else if (!(m_states & STATE_SUSPECT_ENEMY) && !engine.isNullEntity (m_enemy)) { + dest = m_enemy->v.origin + m_enemy->v.velocity.make2D () * 0.55f; + } m_isUsingGrenade = true; m_checkTerrain = false; - IgnoreCollisionShortly (); + ignoreCollision (); - if (m_maxThrowTimer < engine.Time () || (pev->origin - dest).GetLengthSquared () < GET_SQUARE (400.0f)) - { + if ((pev->origin - dest).lengthSq () < cr::square (400.0f)) { // heck, I don't wanna blow up myself - m_grenadeCheckTime = engine.Time () + MAX_GRENADE_TIMER; + m_grenadeCheckTime = engine.timebase () + MAX_GRENADE_TIMER; - SelectBestWeapon (); - TaskComplete (); + selectBestWeapon (); + completeTask (); return; } - m_grenade = CheckThrow (EyePosition (), dest); + m_grenade = calcThrow (eyePos (), dest); - if (m_grenade.GetLengthSquared () < 100.0f) - m_grenade = CheckToss (EyePosition (), dest); - - if (m_grenade.GetLengthSquared () <= 100.0f) - { - m_grenadeCheckTime = engine.Time () + MAX_GRENADE_TIMER; - m_grenade = m_lookAt; - - SelectBestWeapon (); - TaskComplete (); + if (m_grenade.lengthSq () < 100.0f) { + m_grenade = calcToss (pev->origin, dest); } - else - { - edict_t *ent = nullptr; - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "grenade"))) - { - if (ent->v.owner == GetEntity () && strcmp (STRING (ent->v.model) + 9, "hegrenade.mdl") == 0) - { - // set the correct velocity for the grenade - if (m_grenade.GetLengthSquared () > 100.0f) - ent->v.velocity = m_grenade; + if (m_grenade.lengthSq () <= 100.0f) { + m_grenadeCheckTime = engine.timebase () + MAX_GRENADE_TIMER; - m_grenadeCheckTime = engine.Time () + MAX_GRENADE_TIMER; + selectBestWeapon (); + completeTask (); + } + else { + auto grenade = correctGrenadeVelocity ("hegrenade.mdl"); - SelectBestWeapon (); - TaskComplete (); + if (engine.isNullEntity (grenade)) { + if (m_currentWeapon != WEAPON_EXPLOSIVE && !m_grenadeRequested) { + if (pev->weapons & (1 << WEAPON_EXPLOSIVE)) { + m_grenadeRequested = true; + selectWeaponByName ("weapon_hegrenade"); + } + else { + m_grenadeRequested = false; - break; + selectBestWeapon (); + completeTask (); + + return; + } } - } - - if (engine.IsNullEntity (ent)) - { - if (m_currentWeapon != WEAPON_EXPLOSIVE) - { - if (pev->weapons & (1 << WEAPON_EXPLOSIVE)) - SelectWeaponByName ("weapon_hegrenade"); - } - else if (!(pev->oldbuttons & IN_ATTACK)) + else if (!(m_oldButtons & IN_ATTACK)) { pev->button |= IN_ATTACK; + m_grenadeRequested = false; + } } } pev->button |= m_campButtons; } -void Bot::RunTask_Throw_FL (void) -{ +void Bot::throwFlashbang_ (void) { m_aimFlags |= AIM_GRENADE; Vector dest = m_throw; - if (!(m_states & STATE_SEEING_ENEMY)) - { + if (!(m_states & STATE_SEEING_ENEMY)) { m_strafeSpeed = 0.0f; m_moveSpeed = 0.0f; + m_moveToGoal = false; + } + else if (!(m_states & STATE_SUSPECT_ENEMY) && !engine.isNullEntity (m_enemy)) { + dest = m_enemy->v.origin + m_enemy->v.velocity.make2D () * 0.55f; } - else if (!(m_states & STATE_SUSPECT_ENEMY) && !engine.IsNullEntity (m_enemy)) - dest = m_enemy->v.origin + m_enemy->v.velocity.Get2D () * 0.5; m_isUsingGrenade = true; m_checkTerrain = false; - IgnoreCollisionShortly (); + ignoreCollision (); - m_grenade = CheckThrow (EyePosition (), dest); + if ((pev->origin - dest).lengthSq () < cr::square (400.0f)) { + // heck, I don't wanna blow up myself + m_grenadeCheckTime = engine.timebase () + MAX_GRENADE_TIMER; - if (m_grenade.GetLengthSquared () < 100.0f) - m_grenade = CheckToss (pev->origin, dest); + selectBestWeapon (); + completeTask (); - if (m_maxThrowTimer < engine.Time () || m_grenade.GetLengthSquared () <= 100.0f) - { - m_grenadeCheckTime = engine.Time () + MAX_GRENADE_TIMER; - m_grenade = m_lookAt; - - SelectBestWeapon (); - TaskComplete (); + return; } - else - { - edict_t *ent = nullptr; - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "grenade"))) - { - if (ent->v.owner == GetEntity () && strcmp (STRING (ent->v.model) + 9, "flashbang.mdl") == 0) - { - // set the correct velocity for the grenade - if (m_grenade.GetLengthSquared () > 100.0f) - ent->v.velocity = m_grenade; + m_grenade = calcThrow (eyePos (), dest); - m_grenadeCheckTime = engine.Time () + MAX_GRENADE_TIMER; + if (m_grenade.lengthSq () < 100.0f) { + m_grenade = calcToss (pev->origin, dest); + } - SelectBestWeapon (); - TaskComplete (); - break; + if (m_grenade.lengthSq () <= 100.0f) { + m_grenadeCheckTime = engine.timebase () + MAX_GRENADE_TIMER; + + selectBestWeapon (); + completeTask (); + } + else { + auto grenade = correctGrenadeVelocity ("flashbang.mdl"); + + if (engine.isNullEntity (grenade)) { + if (m_currentWeapon != WEAPON_FLASHBANG && !m_grenadeRequested) { + if (pev->weapons & (1 << WEAPON_FLASHBANG)) { + m_grenadeRequested = true; + selectWeaponByName ("weapon_flashbang"); + } + else { + m_grenadeRequested = false; + + selectBestWeapon (); + completeTask (); + + return; + } } - } - - if (engine.IsNullEntity (ent)) - { - if (m_currentWeapon != WEAPON_FLASHBANG) - { - if (pev->weapons & (1 << WEAPON_FLASHBANG)) - SelectWeaponByName ("weapon_flashbang"); - } - else if (!(pev->oldbuttons & IN_ATTACK)) + else if (!(m_oldButtons & IN_ATTACK)) { pev->button |= IN_ATTACK; + m_grenadeRequested = false; + } } } pev->button |= m_campButtons; } -void Bot::RunTask_Throw_SG (void) -{ +void Bot::throwSmoke_ (void) { m_aimFlags |= AIM_GRENADE; - if (!(m_states & STATE_SEEING_ENEMY)) - { + if (!(m_states & STATE_SEEING_ENEMY)) { m_strafeSpeed = 0.0f; m_moveSpeed = 0.0f; + m_moveToGoal = false; } m_checkTerrain = false; m_isUsingGrenade = true; - IgnoreCollisionShortly (); + ignoreCollision (); Vector src = m_lastEnemyOrigin - pev->velocity; // predict where the enemy is in 0.5 secs - if (!engine.IsNullEntity (m_enemy)) + if (!engine.isNullEntity (m_enemy)) src = src + m_enemy->v.velocity * 0.5f; - m_grenade = (src - EyePosition ()).Normalize (); + m_grenade = (src - eyePos ()).normalize (); - if (m_maxThrowTimer < engine.Time () || GetTask ()->time < engine.Time () + 0.5f) - { - m_aimFlags &= ~AIM_GRENADE; - m_states &= ~STATE_THROW_SG; - - TaskComplete (); + if (task ()->time < engine.timebase ()) { + completeTask (); return; } - if (m_currentWeapon != WEAPON_SMOKE) - { - if (pev->weapons & (1 << WEAPON_SMOKE)) - { - SelectWeaponByName ("weapon_smokegrenade"); - GetTask ()->time = engine.Time () + MAX_GRENADE_TIMER; + if (m_currentWeapon != WEAPON_SMOKE && !m_grenadeRequested) { + if (pev->weapons & (1 << WEAPON_SMOKE)) { + m_grenadeRequested = true; + + selectWeaponByName ("weapon_smokegrenade"); + task ()->time = engine.timebase () + 1.2f; + } + else { + m_grenadeRequested = false; + + selectBestWeapon (); + completeTask (); + + return; } - else - GetTask ()->time = engine.Time () + 0.1f; } - else if (!(pev->oldbuttons & IN_ATTACK)) + else if (!(m_oldButtons & IN_ATTACK)) { pev->button |= IN_ATTACK; + m_grenadeRequested = false; + } + pev->button |= m_campButtons; } -void Bot::RunTask_DoubleJump (void) -{ - if (!IsAlive (m_doubleJumpEntity) || (m_aimFlags & AIM_ENEMY) || (m_travelStartIndex != -1 && GetTask ()->time + (waypoints.GetTravelTime (pev->maxspeed, waypoints.GetPath (m_travelStartIndex)->origin, m_doubleJumpOrigin) + 11.0f) < engine.Time ())) - { - ResetDoubleJumpState (); +void Bot::doublejump_ (void) { + if (!isAlive (m_doubleJumpEntity) || (m_aimFlags & AIM_ENEMY) || (m_travelStartIndex != INVALID_WAYPOINT_INDEX && task ()->time + (waypoints.calculateTravelTime (pev->maxspeed, waypoints[m_travelStartIndex].origin, m_doubleJumpOrigin) + 11.0f) < engine.timebase ())) { + resetDoubleJump (); return; } m_aimFlags |= AIM_NAVPOINT; - if (m_jumpReady) - { + if (m_jumpReady) { m_moveToGoal = false; m_checkTerrain = false; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; bool inJump = (m_doubleJumpEntity->v.button & IN_JUMP) || (m_doubleJumpEntity->v.oldbuttons & IN_JUMP); - if (m_duckForJump < engine.Time ()) + if (m_duckForJump < engine.timebase ()) { pev->button |= IN_DUCK; - else if (inJump && !(pev->oldbuttons & IN_JUMP)) + } + else if (inJump && !(m_oldButtons & IN_JUMP)) { pev->button |= IN_JUMP; - - MakeVectors (Vector (0.0f, pev->angles.y, 0.0f)); + } + makeVectors (Vector (0.0f, pev->angles.y, 0.0f)); Vector src = pev->origin + Vector (0.0f, 0.0f, 45.0f); Vector dest = src + g_pGlobals->v_up * 256.0f; TraceResult tr; - engine.TestLine (src, dest, TRACE_IGNORE_NONE, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_NONE, ent (), &tr); - if (tr.flFraction < 1.0f && tr.pHit == m_doubleJumpEntity && inJump) - { - m_duckForJump = engine.Time () + Random.Float (3.0f, 5.0f); - GetTask ()->time = engine.Time (); + if (tr.flFraction < 1.0f && tr.pHit == m_doubleJumpEntity && inJump) { + m_duckForJump = engine.timebase () + rng.getFloat (3.0f, 5.0f); + task ()->time = engine.timebase (); } return; } - if (m_currentWaypointIndex == m_prevGoalIndex) - { + if (m_currentWaypointIndex == m_prevGoalIndex) { m_waypointOrigin = m_doubleJumpOrigin; m_destOrigin = m_doubleJumpOrigin; } - if (DoWaypointNav ()) // reached destination? - GetTask ()->data = -1; + if (processNavigation ()) { + task ()->data = INVALID_WAYPOINT_INDEX; + } - if (!GoalIsValid ()) // didn't choose goal waypoint yet? - { - DeleteSearchNodes (); + // didn't choose goal waypoint yet? + if (!hasActiveGoal ()) { + clearSearchNodes (); - int destIndex = waypoints.FindNearest (m_doubleJumpOrigin); + int destIndex = waypoints.getNearest (m_doubleJumpOrigin); - if (destIndex >= 0 && destIndex < g_numWaypoints) - { + if (waypoints.exists (destIndex)) { m_prevGoalIndex = destIndex; - GetTask ()->data = destIndex; + task ()->data = destIndex; m_travelStartIndex = m_currentWaypointIndex; // always take the shortest path - FindShortestPath (m_currentWaypointIndex, destIndex); + searchShortestPath (m_currentWaypointIndex, destIndex); - if (m_currentWaypointIndex == destIndex) + if (m_currentWaypointIndex == destIndex) { m_jumpReady = true; + } + } + else { + resetDoubleJump (); } - else - ResetDoubleJumpState (); } } -void Bot::RunTask_EscapeFromBomb (void) -{ +void Bot::escapeFromBomb_ (void) { m_aimFlags |= AIM_NAVPOINT; - if (!g_bombPlanted) - TaskComplete (); + if (!g_bombPlanted) { + completeTask (); + } - if (IsShieldDrawn ()) + if (isShieldDrawn ()) { pev->button |= IN_ATTACK2; + } - if (m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0) - SelectWeaponByName ("weapon_knife"); + if (m_currentWeapon != WEAPON_KNIFE && m_numEnemiesLeft == 0) { + selectWeaponByName ("weapon_knife"); + } - if (DoWaypointNav ()) // reached destination? - { - TaskComplete (); // we're done + // reached destination? + if (processNavigation ()) { + completeTask (); // we're done // press duck button if we still have some enemies - if (GetNearbyEnemiesNearPosition (pev->origin, 2048.0f)) + if (numEnemiesNear (pev->origin, 2048.0f)) { m_campButtons = IN_DUCK; + } // we're reached destination point so just sit down and camp - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + 10.0f, true); + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + 10.0f, true); } - else if (!GoalIsValid ()) // didn't choose goal waypoint yet? - { - DeleteSearchNodes (); - int lastSelectedGoal = -1, minPathDistance = 99999; - float safeRadius = Random.Float (1248.0f, 2048.0f); + // didn't choose goal waypoint yet? + else if (!hasActiveGoal ()) { + clearSearchNodes (); - for (int i = 0; i < g_numWaypoints; i++) - { - if ((waypoints.GetPath (i)->origin - waypoints.GetBombPosition ()).GetLength () < safeRadius || IsPointOccupied (i)) + int lastSelectedGoal = INVALID_WAYPOINT_INDEX, minPathDistance = 99999; + float safeRadius = rng.getFloat (1248.0f, 2048.0f); + + for (int i = 0; i < waypoints.length (); i++) { + if ((waypoints[i].origin - waypoints.getBombPos ()).length () < safeRadius || isOccupiedPoint (i)) { continue; + } + int pathDistance = waypoints.getPathDist (m_currentWaypointIndex, i); - int pathDistance = waypoints.GetPathDistance (m_currentWaypointIndex, i); - - if (minPathDistance > pathDistance) - { + if (minPathDistance > pathDistance) { minPathDistance = pathDistance; lastSelectedGoal = i; } } - if (lastSelectedGoal < 0) - lastSelectedGoal = waypoints.FindFarest (pev->origin, safeRadius); + if (lastSelectedGoal < 0) { + lastSelectedGoal = waypoints.getFarest (pev->origin, safeRadius); + } // still no luck? - if (lastSelectedGoal < 0) - { - TaskComplete (); // we're done + if (lastSelectedGoal < 0) { + completeTask (); // we're done // we have no destination point, so just sit down and camp - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + 10.0f, true); + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + 10.0f, true); return; } m_prevGoalIndex = lastSelectedGoal; - GetTask ()->data = lastSelectedGoal; + task ()->data = lastSelectedGoal; - FindShortestPath (m_currentWaypointIndex, lastSelectedGoal); + searchShortestPath (m_currentWaypointIndex, lastSelectedGoal); } } -void Bot::RunTask_ShootBreakable (void) -{ +void Bot::shootBreakable_ (void) { m_aimFlags |= AIM_OVERRIDE; // Breakable destroyed? - if (engine.IsNullEntity (FindBreakable ())) - { - TaskComplete (); + if (engine.isNullEntity (lookupBreakable ())) { + completeTask (); return; } pev->button |= m_campButtons; m_checkTerrain = false; m_moveToGoal = false; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); Vector src = m_breakableOrigin; m_camp = src; // is bot facing the breakable? - if (GetShootingConeDeviation (GetEntity (), &src) >= 0.90f) - { + if (getShootingConeDeviation (ent (), src) >= 0.90f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - if (m_currentWeapon == WEAPON_KNIFE) - SelectBestWeapon (); - + if (m_currentWeapon == WEAPON_KNIFE) { + selectBestWeapon (); + } m_wantsToFire = true; } - else - { + else { m_checkTerrain = true; m_moveToGoal = true; } } -void Bot::RunTask_PickupItem () -{ - if (engine.IsNullEntity (m_pickupItem)) - { +void Bot::pickupItem_ () { + if (engine.isNullEntity (m_pickupItem)) { m_pickupItem = nullptr; - TaskComplete (); + completeTask (); return; } - Vector dest = engine.GetAbsOrigin (m_pickupItem); + Vector dest = engine.getAbsPos (m_pickupItem); m_destOrigin = dest; m_entity = dest; // find the distance to the item - float itemDistance = (dest - pev->origin).GetLength (); + float itemDistance = (dest - pev->origin).length (); - switch (m_pickupType) - { + switch (m_pickupType) { case PICKUP_DROPPED_C4: case PICKUP_NONE: break; @@ -4453,70 +4373,73 @@ void Bot::RunTask_PickupItem () m_aimFlags |= AIM_NAVPOINT; // near to weapon? - if (itemDistance < 50.0f) - { + if (itemDistance < 50.0f) { int id = 0; - - for (id = 0; id < 7; id++) - { - if (strcmp (g_weaponSelect[id].modelName, STRING (m_pickupItem->v.model) + 9) == 0) + + for (id = 0; id < 7; id++) { + if (strcmp (g_weaponSelect[id].modelName, STRING (m_pickupItem->v.model) + 9) == 0) { break; + } } - if (id < 7) - { + if (id < 7) { // secondary weapon. i.e., pistol int wid = 0; - for (id = 0; id < 7; id++) - { - if (pev->weapons & (1 << g_weaponSelect[id].id)) + for (id = 0; id < 7; id++) { + if (pev->weapons & (1 << g_weaponSelect[id].id)) { wid = id; + } } - if (wid > 0) - { - SelectWeaponbyNumber (wid); - engine.IssueBotCommand (GetEntity (), "drop"); + if (wid > 0) { + selectWeaponById (wid); + engine.execBotCmd (ent (), "drop"); - if (HasShield ()) // If we have the shield... - engine.IssueBotCommand (GetEntity (), "drop"); // discard both shield and pistol + if (hasShield ()) { + engine.execBotCmd (ent (), "drop"); // discard both shield and pistol + } } - EquipInBuyzone (BUYSTATE_PRIMARY_WEAPON); + processBuyzoneEntering (BUYSTATE_PRIMARY_WEAPON); } - else - { + else { // primary weapon - int wid = GetHighestWeapon (); - - if (wid > 6 || HasShield ()) - { - SelectWeaponbyNumber (wid); - engine.IssueBotCommand (GetEntity (), "drop"); + int wid = bestWeaponCarried (); + + if (wid == WEAPON_SHIELD || wid > 6 || hasShield ()) { + selectWeaponById (wid); + engine.execBotCmd (ent (), "drop"); } - EquipInBuyzone (BUYSTATE_PRIMARY_WEAPON); + + if (!wid) { + m_itemIgnore = m_pickupItem; + m_pickupItem = nullptr; + m_pickupType = PICKUP_NONE; + + break; + } + processBuyzoneEntering (BUYSTATE_PRIMARY_WEAPON); } - CheckSilencer (); // check the silencer + checkSilencer (); // check the silencer } break; case PICKUP_SHIELD: m_aimFlags |= AIM_NAVPOINT; - if (HasShield ()) - { + if (hasShield ()) { m_pickupItem = nullptr; break; } - else if (itemDistance < 50.0f) // near to shield? - { - // get current best weapon to check if it's a primary in need to be dropped - int wid = GetHighestWeapon (); - if (wid > 6) - { - SelectWeaponbyNumber (wid); - engine.IssueBotCommand (GetEntity (), "drop"); + // near to shield? + else if (itemDistance < 50.0f) { + // get current best weapon to check if it's a primary in need to be dropped + int wid = bestWeaponCarried (); + + if (wid > 6) { + selectWeaponById (wid); + engine.execBotCmd (ent (), "drop"); } } break; @@ -4524,68 +4447,56 @@ void Bot::RunTask_PickupItem () case PICKUP_PLANTED_C4: m_aimFlags |= AIM_ENTITY; - if (m_team == CT && itemDistance < 80.0f) - { - ChatterMessage (Chatter_DefusingC4); + if (m_team == TEAM_COUNTER && itemDistance < 80.0f) { + pushChatterMessage (CHATTER_DEFUSING_BOMB); // notify team of defusing - if (m_numFriendsLeft < 3) - RadioMessage (Radio_NeedBackup); - + if (m_numFriendsLeft < 3) { + pushRadioMessage (RADIO_NEED_BACKUP); + } m_moveToGoal = false; m_checkTerrain = false; m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - PushTask (TASK_DEFUSEBOMB, TASKPRI_DEFUSEBOMB, -1, 0.0f, false); + startTask (TASK_DEFUSEBOMB, TASKPRI_DEFUSEBOMB, INVALID_WAYPOINT_INDEX, 0.0f, false); } break; case PICKUP_HOSTAGE: m_aimFlags |= AIM_ENTITY; - if (!IsAlive (m_pickupItem)) - { + if (!isAlive (m_pickupItem)) { // don't pickup dead hostages m_pickupItem = nullptr; - TaskComplete (); + completeTask (); break; } - if (itemDistance < 50.0f) - { - int angleToEntity = InFieldOfView (dest - EyePosition ()); + if (itemDistance < 50.0f) { + float angleToEntity = isInFOV (dest - eyePos ()); - if (angleToEntity <= 10) // bot faces hostage? - { + // bot faces hostage? + if (angleToEntity <= 10.0f) { // use game dll function to make sure the hostage is correctly 'used' - MDLL_Use (m_pickupItem, GetEntity ()); + MDLL_Use (m_pickupItem, ent ()); - if (Random.Int (0, 100) < 80) - ChatterMessage (Chatter_UseHostage); - - for (int i = 0; i < MAX_HOSTAGES; i++) - { - if (engine.IsNullEntity (m_hostages[i])) // store pointer to hostage so other bots don't steal from this one or bot tries to reuse it - { - m_hostages[i] = m_pickupItem; - m_pickupItem = nullptr; - - break; - } + if (rng.getInt (0, 100) < 80) { + pushChatterMessage (CHATTER_USING_HOSTAGES); } + m_hostages.push (m_pickupItem); + m_pickupItem = nullptr; } - IgnoreCollisionShortly (); // also don't consider being stuck + ignoreCollision (); // also don't consider being stuck } break; case PICKUP_DEFUSEKIT: m_aimFlags |= AIM_NAVPOINT; - if (m_hasDefuser) - { + if (m_hasDefuser) { m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; } @@ -4594,193 +4505,188 @@ void Bot::RunTask_PickupItem () case PICKUP_BUTTON: m_aimFlags |= AIM_ENTITY; - if (engine.IsNullEntity (m_pickupItem) || m_buttonPushTime < engine.Time ()) // it's safer... - { - TaskComplete (); + if (engine.isNullEntity (m_pickupItem) || m_buttonPushTime < engine.timebase ()) { + completeTask (); m_pickupType = PICKUP_NONE; break; } // find angles from bot origin to entity... - int angleToEntity = InFieldOfView (dest - EyePosition ()); + float angleToEntity = isInFOV (dest - eyePos ()); - if (itemDistance < 90.0f) // near to the button? - { + // near to the button? + if (itemDistance < 90.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; m_moveToGoal = false; m_checkTerrain = false; - if (angleToEntity <= 10) // facing it directly? - { - MDLL_Use (m_pickupItem, GetEntity ()); + // facing it directly? + if (angleToEntity <= 10.0f) { + MDLL_Use (m_pickupItem, ent ()); m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; - m_buttonPushTime = engine.Time () + 3.0f; + m_buttonPushTime = engine.timebase () + 3.0f; - TaskComplete (); + completeTask (); } } break; } } -void Bot::RunTask (void) -{ +void Bot::processTasks (void) { // this is core function that handle task execution - switch (GetTaskId ()) - { + switch (taskId ()) { // normal task default: case TASK_NORMAL: - RunTask_Normal (); + normal_ (); break; // bot sprays messy logos all over the place... case TASK_SPRAY: - RunTask_Spray (); + spraypaint_ (); break; // hunt down enemy case TASK_HUNTENEMY: - RunTask_HuntEnemy (); + huntEnemy_ (); break; // bot seeks cover from enemy case TASK_SEEKCOVER: - RunTask_SeekCover (); + seekCover_ (); break; // plain attacking case TASK_ATTACK: - RunTask_Attack (); + attackEnemy_ (); break; // Bot is pausing case TASK_PAUSE: - RunTask_Pause (); + pause_ (); break; // blinded (flashbanged) behaviour case TASK_BLINDED: - RunTask_Blinded (); + blind_ (); break; // camping behaviour case TASK_CAMP: - RunTask_Camp (); + camp_ (); break; // hiding behaviour case TASK_HIDE: - RunTask_Hide (); + hide_ (); break; // moves to a position specified in position has a higher priority than task_normal case TASK_MOVETOPOSITION: - RunTask_MoveToPos (); + moveToPos_ (); break; // planting the bomb right now case TASK_PLANTBOMB: - RunTask_PlantBomb (); + plantBomb_ (); break; // bomb defusing behaviour case TASK_DEFUSEBOMB: - RunTask_DefuseBomb (); + bombDefuse_ (); break; - + // follow user behaviour case TASK_FOLLOWUSER: - RunTask_FollowUser (); + followUser_ (); break; // HE grenade throw behaviour case TASK_THROWHEGRENADE: - RunTask_Throw_HE (); + throwExplosive_ (); break; // flashbang throw behavior (basically the same code like for HE's) case TASK_THROWFLASHBANG: - RunTask_Throw_FL (); + throwFlashbang_ (); break; // smoke grenade throw behavior // a bit different to the others because it mostly tries to throw the sg on the ground case TASK_THROWSMOKE: - RunTask_Throw_SG (); + throwSmoke_ (); break; // bot helps human player (or other bot) to get somewhere case TASK_DOUBLEJUMP: - RunTask_DoubleJump (); + doublejump_ (); break; // escape from bomb behaviour case TASK_ESCAPEFROMBOMB: - RunTask_EscapeFromBomb (); + escapeFromBomb_ (); break; // shooting breakables in the way action case TASK_SHOOTBREAKABLE: - RunTask_ShootBreakable (); + shootBreakable_ (); break; // picking up items and stuff behaviour case TASK_PICKUPITEM: - RunTask_PickupItem (); + pickupItem_ (); break; } } -void Bot::CheckSpawnTimeConditions (void) -{ - // this function is called instead of BotAI when buying finished, but freezetime is not yet left. +void Bot::checkSpawnConditions (void) { + // this function is called instead of ai when buying finished, but freezetime is not yet left. // switch to knife if time to do this - if (m_checkKnifeSwitch && !m_checkWeaponSwitch && m_buyingFinished && m_spawnTime + Random.Float (4.0f, 6.5f) < engine.Time ()) - { - if (Random.Int (1, 100) < 2 && yb_spraypaints.GetBool ()) - PushTask (TASK_SPRAY, TASKPRI_SPRAYLOGO, -1, engine.Time () + 1.0f, false); + if (m_checkKnifeSwitch && !m_checkWeaponSwitch && m_buyingFinished && m_spawnTime + rng.getFloat (4.0f, 6.5f) < engine.timebase ()) { + if (rng.getInt (1, 100) < 2 && yb_spraypaints.boolean ()) { + startTask (TASK_SPRAY, TASKPRI_SPRAYLOGO, INVALID_WAYPOINT_INDEX, engine.timebase () + 1.0f, false); + } - if (m_difficulty >= 2 && Random.Int (0, 100) < (m_personality == PERSONALITY_RUSHER ? 99 : 50) && !m_isReloading && (g_mapType & (MAP_CS | MAP_DE | MAP_ES | MAP_AS))) - { - if (yb_jasonmode.GetBool ()) - { - SelectPistol (); - engine.IssueBotCommand (GetEntity (), "drop"); + if (m_difficulty >= 2 && rng.getInt (0, 100) < (m_personality == PERSONALITY_RUSHER ? 99 : 50) && !m_isReloading && (g_mapFlags & (MAP_CS | MAP_DE | MAP_ES | MAP_AS))) { + if (yb_jasonmode.boolean ()) { + selectSecondary (); + engine.execBotCmd (ent (), "drop"); + } + else { + selectWeaponByName ("weapon_knife"); } - else - SelectWeaponByName ("weapon_knife"); } m_checkKnifeSwitch = false; - if (Random.Int (0, 100) < yb_user_follow_percent.GetInt () && engine.IsNullEntity (m_targetEntity) && !m_isLeader && !m_hasC4) - AttachToUser (); + if (rng.getInt (0, 100) < yb_user_follow_percent.integer () && engine.isNullEntity (m_targetEntity) && !m_isLeader && !m_hasC4 && rng.getInt (0, 100) > 50) { + decideFollowUser (); + } } // check if we already switched weapon mode - if (m_checkWeaponSwitch && m_buyingFinished && m_spawnTime + Random.Float (2.0f, 3.5f) < engine.Time ()) - { - if (HasShield () && IsShieldDrawn ()) + if (m_checkWeaponSwitch && m_buyingFinished && m_spawnTime + rng.getFloat (2.0f, 3.5f) < engine.timebase ()) { + if (hasShield () && isShieldDrawn ()) { pev->button |= IN_ATTACK2; - else - { - switch (m_currentWeapon) - { + } + else { + switch (m_currentWeapon) { case WEAPON_M4A1: case WEAPON_USP: - CheckSilencer (); + checkSilencer (); break; case WEAPON_FAMAS: case WEAPON_GLOCK: - if (Random.Int (0, 100) < 50) + if (rng.getInt (0, 100) < 50) { pev->button |= IN_ATTACK2; + } break; } } @@ -4788,8 +4694,7 @@ void Bot::CheckSpawnTimeConditions (void) } } -void Bot::BotAI (void) -{ +void Bot::ai (void) { // this function gets called each frame and is the core of all bot ai. from here all other subroutines are called float movedDistance = 2.0f; // length of different vector (distance bot moved) @@ -4797,66 +4702,67 @@ void Bot::BotAI (void) // increase reaction time m_actualReactionTime += 0.3f; - if (m_actualReactionTime > m_idealReactionTime) + if (m_actualReactionTime > m_idealReactionTime) { m_actualReactionTime = m_idealReactionTime; + } // bot could be blinded by flashbang or smoke, recover from it m_viewDistance += 3.0f; - if (m_viewDistance > m_maxViewDistance) + if (m_viewDistance > m_maxViewDistance) { m_viewDistance = m_maxViewDistance; + } - if (m_blindTime > engine.Time ()) - m_maxViewDistance = 4096.0f; - + if (m_blindTime > engine.timebase ()) { + m_maxViewDistance = 4096.0f; + } m_moveSpeed = pev->maxspeed; - if (m_prevTime <= engine.Time ()) - { + if (m_prevTime <= engine.timebase ()) { // see how far bot has moved since the previous position... - movedDistance = (m_prevOrigin - pev->origin).GetLength (); + movedDistance = (m_prevOrigin - pev->origin).length (); // save current position as previous m_prevOrigin = pev->origin; - m_prevTime = engine.Time () + 0.2f; + m_prevTime = engine.timebase () + 0.2f; } // if there's some radio message to respond, check it - if (m_radioOrder != 0) - CheckRadioCommands (); + if (m_radioOrder != 0) { + checkRadioQueue (); + } // do all sensing, calculate/filter all actions here - SetConditions (); + setConditions (); // some stuff required by by chatter engine - if (yb_communication_type.GetInt () == 2) - { - if ((m_states & STATE_SEEING_ENEMY) && !engine.IsNullEntity (m_enemy)) - { - int hasFriendNearby = GetNearbyFriendsNearPosition (pev->origin, 512.0f); + if (yb_communication_type.integer () == 2) { + if ((m_states & STATE_SEEING_ENEMY) && !engine.isNullEntity (m_enemy)) { + int hasFriendNearby = numFriendsNear (pev->origin, 512.0f); - if (!hasFriendNearby && Random.Int (0, 100) < 45 && (m_enemy->v.weapons & (1 << WEAPON_C4))) - ChatterMessage (Chatter_SpotTheBomber); - - else if (!hasFriendNearby && Random.Int (0, 100) < 45 && m_team == TERRORIST && IsPlayerVIP (m_enemy)) - ChatterMessage (Chatter_VIPSpotted); - - else if (!hasFriendNearby && Random.Int (0, 100) < 50 && engine.GetTeam (m_enemy) != m_team && IsGroupOfEnemies (m_enemy->v.origin, 2, 384)) - ChatterMessage (Chatter_ScaredEmotion); - - else if (!hasFriendNearby && Random.Int (0, 100) < 40 && ((m_enemy->v.weapons & (1 << WEAPON_AWP)) || (m_enemy->v.weapons & (1 << WEAPON_SCOUT)) || (m_enemy->v.weapons & (1 << WEAPON_G3SG1)) || (m_enemy->v.weapons & (1 << WEAPON_SG550)))) - ChatterMessage (Chatter_SniperWarning); + if (!hasFriendNearby && rng.getInt (0, 100) < 45 && (m_enemy->v.weapons & (1 << WEAPON_C4))) { + pushChatterMessage (CHATTER_SPOT_THE_BOMBER); + } + else if (!hasFriendNearby && rng.getInt (0, 100) < 45 && m_team == TEAM_TERRORIST && isPlayerVIP (m_enemy)) { + pushChatterMessage (CHATTER_VIP_SPOTTED); + } + else if (!hasFriendNearby && rng.getInt (0, 100) < 50 && engine.getTeam (m_enemy) != m_team && isGroupOfEnemies (m_enemy->v.origin, 2, 384)) { + pushChatterMessage (CHATTER_SCARED_EMOTE); + } + else if (!hasFriendNearby && rng.getInt (0, 100) < 40 && ((m_enemy->v.weapons & (1 << WEAPON_AWP)) || (m_enemy->v.weapons & (1 << WEAPON_SCOUT)) || (m_enemy->v.weapons & (1 << WEAPON_G3SG1)) || (m_enemy->v.weapons & (1 << WEAPON_SG550)))) { + pushChatterMessage (CHATTER_SNIPER_WARNING); + } // if bot is trapped under shield yell for help ! - if (GetTaskId () == TASK_CAMP && HasShield () && IsShieldDrawn () && hasFriendNearby >= 2 && IsEnemyViewable (m_enemy)) - InstantChatterMessage (Chatter_Pinned_Down); + if (taskId () == TASK_CAMP && hasShield () && isShieldDrawn () && hasFriendNearby >= 2 && seesEnemy (m_enemy)) { + instantChatter (CHATTER_PINNED_DOWN); + } } // if bomb planted warn teammates ! - if (g_canSayBombPlanted && g_bombPlanted && m_team == CT) - { + if (g_canSayBombPlanted && g_bombPlanted && m_team == TEAM_COUNTER) { g_canSayBombPlanted = false; - ChatterMessage (Chatter_GottaFindTheBomb); + pushChatterMessage (CHATTER_GOTTA_FIND_BOMB); } } Vector src, destination; @@ -4865,68 +4771,74 @@ void Bot::BotAI (void) m_moveToGoal = true; m_wantsToFire = false; - AvoidGrenades (); // avoid flyings grenades + avoidGrenades (); // avoid flyings grenades m_isUsingGrenade = false; - RunTask (); // execute current task - ChooseAimDirection (); // choose aim direction - UpdateLookAngles (); // and turn to chosen aim direction + processTasks (); // execute current task + updateAimDir (); // choose aim direction + processLookAngles (); // and turn to chosen aim direction // the bots wants to fire at something? - if (m_wantsToFire && !m_isUsingGrenade && m_shootTime <= engine.Time ()) - FireWeapon (); // if bot didn't fire a bullet try again next frame + if (m_wantsToFire && !m_isUsingGrenade && m_shootTime <= engine.timebase ()) { + fireWeapons (); // if bot didn't fire a bullet try again next frame + } // check for reloading - if (m_reloadCheckTime <= engine.Time ()) - CheckReload (); + if (m_reloadCheckTime <= engine.timebase ()) { + checkReload (); + } // set the reaction time (surprise momentum) different each frame according to skill - SetIdealReactionTimes (); + setIdealReactionTimers (); // calculate 2 direction vectors, 1 without the up/down component - const Vector &dirOld = m_destOrigin - (pev->origin + pev->velocity * GetThinkInterval ()); - const Vector &dirNormal = dirOld.Normalize2D (); + const Vector &dirOld = m_destOrigin - (pev->origin + pev->velocity * calcThinkInterval ()); + const Vector &dirNormal = dirOld.normalize2D (); - m_moveAngles = dirOld.ToAngles (); - - m_moveAngles.ClampAngles (); + m_moveAngles = dirOld.toAngles (); + m_moveAngles.clampAngles (); m_moveAngles.x *= -1.0f; // invert for engine - SetConditionsOverride (); + // do some overriding for special cases + overrideConditions (); // allowed to move to a destination position? - if (m_moveToGoal) - { - GetValidWaypoint (); + if (m_moveToGoal) { + getValidPoint (); - // Press duck button if we need to - if ((m_currentPath->flags & FLAG_CROUCH) && !(m_currentPath->flags & FLAG_CAMP)) + // press duck button if we need to + if ((m_currentPath->flags & FLAG_CROUCH) && !(m_currentPath->flags & (FLAG_CAMP | FLAG_GOAL))) { pev->button |= IN_DUCK; + } + m_timeWaypointMove = engine.timebase (); - m_timeWaypointMove = engine.Time (); - - if (IsInWater ()) // special movement for swimming here - { + // special movement for swimming here + if (isInWater ()) { // check if we need to go forward or back press the correct buttons - if (InFieldOfView (m_destOrigin - EyePosition ()) > 90.0f) + if (isInFOV (m_destOrigin - eyePos ()) > 90.0f) { pev->button |= IN_BACK; - else + } + else { pev->button |= IN_FORWARD; + } - if (m_moveAngles.x > 60.0f) + if (m_moveAngles.x > 60.0f) { pev->button |= IN_DUCK; - else if (m_moveAngles.x < -60.0f) + } + else if (m_moveAngles.x < -60.0f) { pev->button |= IN_JUMP; + } } } - if (m_checkTerrain) // are we allowed to check blocking terrain (and react to it)? - CheckTerrain (movedDistance, dirNormal); + // are we allowed to check blocking terrain (and react to it)? + if (m_checkTerrain) { + checkTerrain (movedDistance, dirNormal); + } // must avoid a grenade? - if (m_needAvoidGrenade != 0) - { - // Don't duck to get away faster + if (m_needAvoidGrenade != 0) { + // don't duck to get away faster pev->button &= ~IN_DUCK; m_moveSpeed = -pev->maxspeed; @@ -4934,317 +4846,208 @@ void Bot::BotAI (void) } // time to reach waypoint - if (m_navTimeset + GetEstimatedReachTime () < engine.Time () && engine.IsNullEntity (m_enemy)) - { - GetValidWaypoint (); + if (m_navTimeset + getReachTime () < engine.timebase () && engine.isNullEntity (m_enemy)) { + getValidPoint (); // clear these pointers, bot mingh be stuck getting to them - if (!engine.IsNullEntity (m_pickupItem) && !m_hasProgressBar) + if (!engine.isNullEntity (m_pickupItem) && !m_hasProgressBar) { m_itemIgnore = m_pickupItem; + } m_pickupItem = nullptr; m_breakableEntity = nullptr; - m_itemCheckTime = engine.Time () + 5.0f; + m_itemCheckTime = engine.timebase () + 5.0f; m_pickupType = PICKUP_NONE; } - if (m_duckTime >= engine.Time ()) + if (m_duckTime >= engine.timebase ()) { pev->button |= IN_DUCK; + } - if (pev->button & IN_JUMP) - m_jumpTime = engine.Time (); + if (pev->button & IN_JUMP) { + m_jumpTime = engine.timebase (); + } - if (m_jumpTime + 0.85f > engine.Time ()) - { - if (!IsOnFloor () && !IsInWater ()) + if (m_jumpTime + 0.85f > engine.timebase ()) { + if (!isOnFloor () && !isInWater ()) { pev->button |= IN_DUCK; + } } - if (!(pev->button & (IN_FORWARD | IN_BACK))) - { - if (m_moveSpeed > 0.0f) + if (!(pev->button & (IN_FORWARD | IN_BACK))) { + if (m_moveSpeed > 0.0f) { pev->button |= IN_FORWARD; - else if (m_moveSpeed < 0.0f) + } + else if (m_moveSpeed < 0.0f) { pev->button |= IN_BACK; + } } - if (!(pev->button & (IN_MOVELEFT | IN_MOVERIGHT))) - { - if (m_strafeSpeed > 0.0f) + if (!(pev->button & (IN_MOVELEFT | IN_MOVERIGHT))) { + if (m_strafeSpeed > 0.0f) { pev->button |= IN_MOVERIGHT; - else if (m_strafeSpeed < 0.0f) + } + else if (m_strafeSpeed < 0.0f) { pev->button |= IN_MOVELEFT; + } } // display some debugging thingy to host entity - if (!engine.IsNullEntity (g_hostEntity) && yb_debug.GetInt () >= 1) - DisplayDebugOverlay (); - + if (!engine.isNullEntity (g_hostEntity) && yb_debug.integer () >= 1) { + showDebugOverlay (); + } + // save the previous speed (for checking if stuck) - m_prevSpeed = fabsf (m_moveSpeed); + m_prevSpeed = cr::abs (m_moveSpeed); m_lastDamageType = -1; // reset damage } -void Bot::DisplayDebugOverlay (void) -{ +void Bot::showDebugOverlay (void) { bool displayDebugOverlay = false; - if (g_hostEntity->v.iuser2 == GetIndex ()) + if (g_hostEntity->v.iuser2 == index ()) { displayDebugOverlay = true; - - if (!displayDebugOverlay && yb_debug.GetInt () >= 2) - { - Bot *nearest = nullptr; - - if (FindNearestPlayer (reinterpret_cast (&nearest), g_hostEntity, 128.0f, true, true, true, true) && nearest == this) - displayDebugOverlay = true; } - if (displayDebugOverlay) - { + if (!displayDebugOverlay && yb_debug.integer () >= 2) { + Bot *nearest = nullptr; + + if (findNearestPlayer (reinterpret_cast (&nearest), g_hostEntity, 128.0f, false, true, true, true) && nearest == this) { + displayDebugOverlay = true; + } + } + + if (displayDebugOverlay) { + static bool s_mapsFilled = false; + static float timeDebugUpdate = 0.0f; static int index, goal, taskID; - if (!m_tasks.IsEmpty ()) - { - if (taskID != GetTaskId () || index != m_currentWaypointIndex || goal != GetTask ()->data || timeDebugUpdate < engine.Time ()) - { - taskID = GetTaskId (); + static HashMap > tasks; + static HashMap > personalities; + static HashMap > flags; + + if (!s_mapsFilled) { + tasks.put (TASK_NORMAL, "Normal"); + tasks.put (TASK_PAUSE, "Pause"); + tasks.put (TASK_MOVETOPOSITION, "Move"); + tasks.put (TASK_FOLLOWUSER, "Follow"); + tasks.put (TASK_PICKUPITEM, "Pickup"); + tasks.put (TASK_CAMP, "Camp"); + tasks.put (TASK_PLANTBOMB, "PlantBomb"); + tasks.put (TASK_DEFUSEBOMB, "DefuseBomb"); + tasks.put (TASK_ATTACK, "Attack"); + tasks.put (TASK_HUNTENEMY, "Hunt"); + tasks.put (TASK_SEEKCOVER, "SeekCover"); + tasks.put (TASK_THROWHEGRENADE, "ThrowHE"); + tasks.put (TASK_THROWFLASHBANG, "ThrowFL"); + tasks.put (TASK_THROWSMOKE, "ThrowSG"); + tasks.put (TASK_DOUBLEJUMP, "DoubleJump"); + tasks.put (TASK_ESCAPEFROMBOMB, "EscapeFromBomb"); + tasks.put (TASK_SHOOTBREAKABLE, "DestroyBreakable"); + tasks.put (TASK_HIDE, "Hide"); + tasks.put (TASK_BLINDED, "Blind"); + tasks.put (TASK_SPRAY, "Spray"); + + personalities.put (PERSONALITY_RUSHER, "Rusher"); + personalities.put (PERSONALITY_NORMAL, "Normal"); + personalities.put (PERSONALITY_CAREFUL, "Careful"); + + flags.put (AIM_NAVPOINT, "Nav"); + flags.put (AIM_CAMP, "Camp"); + flags.put (AIM_PREDICT_PATH, "Predict"); + flags.put (AIM_LAST_ENEMY, "LastEnemy"); + flags.put (AIM_ENTITY, "Entity"); + flags.put (AIM_ENEMY, "Enemy"); + flags.put (AIM_GRENADE, "Grenade"); + flags.put (AIM_OVERRIDE, "Override"); + + s_mapsFilled = true; + } + + if (!m_tasks.empty ()) { + if (taskID != taskId () || index != m_currentWaypointIndex || goal != task ()->data || timeDebugUpdate < engine.timebase ()) { + taskID = taskId (); index = m_currentWaypointIndex; - goal = GetTask ()->data; + goal = task ()->data; - char taskName[80]; - memset (taskName, 0, sizeof (taskName)); + String enemy = "(none)"; - switch (taskID) - { - case TASK_NORMAL: - sprintf (taskName, "Normal"); - break; - - case TASK_PAUSE: - sprintf (taskName, "Pause"); - break; - - case TASK_MOVETOPOSITION: - sprintf (taskName, "MoveToPosition"); - break; - - case TASK_FOLLOWUSER: - sprintf (taskName, "FollowUser"); - break; - - case TASK_WAITFORGO: - sprintf (taskName, "WaitForGo"); - break; - - case TASK_PICKUPITEM: - sprintf (taskName, "PickupItem"); - break; - - case TASK_CAMP: - sprintf (taskName, "Camp"); - break; - - case TASK_PLANTBOMB: - sprintf (taskName, "PlantBomb"); - break; - - case TASK_DEFUSEBOMB: - sprintf (taskName, "DefuseBomb"); - break; - - case TASK_ATTACK: - sprintf (taskName, "AttackEnemy"); - break; - - case TASK_HUNTENEMY: - sprintf (taskName, "HuntEnemy"); - break; - - case TASK_SEEKCOVER: - sprintf (taskName, "SeekCover"); - break; - - case TASK_THROWHEGRENADE: - sprintf (taskName, "ThrowExpGrenade"); - break; - - case TASK_THROWFLASHBANG: - sprintf (taskName, "ThrowFlashGrenade"); - break; - - case TASK_THROWSMOKE: - sprintf (taskName, "ThrowSmokeGrenade"); - break; - - case TASK_DOUBLEJUMP: - sprintf (taskName, "PerformDoubleJump"); - break; - - case TASK_ESCAPEFROMBOMB: - sprintf (taskName, "EscapeFromBomb"); - break; - - case TASK_SHOOTBREAKABLE: - sprintf (taskName, "ShootBreakable"); - break; - - case TASK_HIDE: - sprintf (taskName, "Hide"); - break; - - case TASK_BLINDED: - sprintf (taskName, "Blinded"); - break; - - case TASK_SPRAY: - sprintf (taskName, "SprayLogo"); - break; + if (!engine.isNullEntity (m_enemy)) { + enemy = STRING (m_enemy->v.netname); } - - char enemyName[80], weaponName[80], aimFlags[64], botType[32]; - - if (!engine.IsNullEntity (m_enemy)) - strncpy (enemyName, STRING (m_enemy->v.netname), SIZEOF_CHAR (enemyName)); - else if (!engine.IsNullEntity (m_lastEnemy)) - { - strcpy (enemyName, " (L)"); - strncat (enemyName, STRING (m_lastEnemy->v.netname), SIZEOF_CHAR (enemyName)); + else if (!engine.isNullEntity (m_lastEnemy)) { + enemy.format ("%s (L)", STRING (m_lastEnemy->v.netname)); } - else - strcpy (enemyName, " (null)"); + String pickup = "(none)"; - char pickupName[80]; - memset (pickupName, 0, sizeof (pickupName)); - - if (!engine.IsNullEntity (m_pickupItem)) - strncpy (pickupName, STRING (m_pickupItem->v.classname), SIZEOF_CHAR (pickupName)); - else - strcpy (pickupName, " (null)"); - - WeaponSelect *tab = &g_weaponSelect[0]; - char weaponCount = 0; - - while (m_currentWeapon != tab->id && weaponCount < NUM_WEAPONS) - { - tab++; - weaponCount++; + if (!engine.isNullEntity (m_pickupItem)) { + pickup = STRING (m_pickupItem->v.netname); } - memset (aimFlags, 0, sizeof (aimFlags)); + String aimFlags; - // set the aim flags - sprintf (aimFlags, "%s%s%s%s%s%s%s%s", - (m_aimFlags & AIM_NAVPOINT) ? " NavPoint" : "", - (m_aimFlags & AIM_CAMP) ? " CampPoint" : "", - (m_aimFlags & AIM_PREDICT_PATH) ? " PredictPath" : "", - (m_aimFlags & AIM_LAST_ENEMY) ? " LastEnemy" : "", - (m_aimFlags & AIM_ENTITY) ? " Entity" : "", - (m_aimFlags & AIM_ENEMY) ? " Enemy" : "", - (m_aimFlags & AIM_GRENADE) ? " Grenade" : "", - (m_aimFlags & AIM_OVERRIDE) ? " Override" : ""); + for (int i = 0; i < 8; i++) { + bool hasFlag = m_aimFlags & (1 << i); - // set the bot type - sprintf (botType, "%s%s%s", m_personality == PERSONALITY_RUSHER ? " Rusher" : "", - m_personality == PERSONALITY_CAREFUL ? " Careful" : "", - m_personality == PERSONALITY_NORMAL ? " Normal" : ""); - - if (weaponCount >= NUM_WEAPONS) - { - // prevent printing unknown message from known weapons - switch (m_currentWeapon) - { - case WEAPON_EXPLOSIVE: - strcpy (weaponName, "weapon_hegrenade"); - break; - - case WEAPON_FLASHBANG: - strcpy (weaponName, "weapon_flashbang"); - break; - - case WEAPON_SMOKE: - strcpy (weaponName, "weapon_smokegrenade"); - break; - - case WEAPON_C4: - strcpy (weaponName, "weapon_c4"); - break; - - default: - sprintf (weaponName, "Unknown! (%d)", m_currentWeapon); + if (hasFlag) { + aimFlags.formatAppend (" %s", flags[1 << i].chars ()); } } - else - strncpy (weaponName, tab->weaponName, SIZEOF_CHAR (weaponName)); + String weapon = STRING (getWeaponData (true, nullptr, m_currentWeapon)); - char outputBuffer[512]; - memset (outputBuffer, 0, sizeof (outputBuffer)); + String debugData; + debugData.format ("\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 (), task ()->desire, weapon.chars (), ammoClip (), ammo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim ().chars (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - engine.timebase (), pev->movetype, enemy.chars (), pickup.chars (), personalities[m_personality].chars ()); - sprintf (outputBuffer, "\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, taskName, GetTask ()->desire, &weaponName[7], GetAmmoInClip (), GetAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags, m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - engine.Time (), pev->movetype, enemyName, pickupName, botType); + MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, Vector::null (), g_hostEntity) + .writeByte (TE_TEXTMESSAGE) + .writeByte (1) + .writeShort (MessageWriter::fs16 (-1, 1 << 13)) + .writeShort (MessageWriter::fs16 (0, 1 << 13)) + .writeByte (0) + .writeByte (m_team == TEAM_COUNTER ? 0 : 255) + .writeByte (100) + .writeByte (m_team != TEAM_COUNTER ? 0 : 255) + .writeByte (0) + .writeByte (255) + .writeByte (255) + .writeByte (255) + .writeByte (0) + .writeShort (MessageWriter::fu16 (0, 1 << 8)) + .writeShort (MessageWriter::fu16 (0, 1 << 8)) + .writeShort (MessageWriter::fu16 (1.0, 1 << 8)) + .writeString (debugData.chars ()); - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, g_hostEntity); - WRITE_BYTE (TE_TEXTMESSAGE); - WRITE_BYTE (1); - WRITE_SHORT (FixedSigned16 (-1, 1 << 13)); - WRITE_SHORT (FixedSigned16 (0, 1 << 13)); - WRITE_BYTE (0); - WRITE_BYTE (m_team == CT ? 0 : 255); - WRITE_BYTE (100); - WRITE_BYTE (m_team != CT ? 0 : 255); - WRITE_BYTE (0); - WRITE_BYTE (255); - WRITE_BYTE (255); - WRITE_BYTE (255); - WRITE_BYTE (0); - WRITE_SHORT (FixedUnsigned16 (0, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (0, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (1.0, 1 << 8)); - WRITE_STRING (const_cast (&outputBuffer[0])); - MESSAGE_END (); - - timeDebugUpdate = engine.Time () + 1.0f; + timeDebugUpdate = engine.timebase () + 1.0f; } // green = destination origin // blue = ideal angles // red = view angles - engine.DrawLine (g_hostEntity, EyePosition (), m_destOrigin, 10, 0, 0, 255, 0, 250, 5, 1, DRAW_ARROW); + engine.drawLine (g_hostEntity, eyePos (), m_destOrigin, 10, 0, 0, 255, 0, 250, 5, 1, DRAW_ARROW); - MakeVectors (m_idealAngles); - engine.DrawLine (g_hostEntity, EyePosition () - Vector (0.0f, 0.0f, 16.0f), EyePosition () + g_pGlobals->v_forward * 300.0f, 10, 0, 0, 0, 255, 250, 5, 1, DRAW_ARROW); + makeVectors (m_idealAngles); + engine.drawLine (g_hostEntity, eyePos () - Vector (0.0f, 0.0f, 16.0f), eyePos () + g_pGlobals->v_forward * 300.0f, 10, 0, 0, 0, 255, 250, 5, 1, DRAW_ARROW); - MakeVectors (pev->v_angle); - engine.DrawLine (g_hostEntity, EyePosition () - Vector (0.0f, 0.0f, 32.0f), EyePosition () + g_pGlobals->v_forward * 300.0f, 10, 0, 255, 0, 0, 250, 5, 1, DRAW_ARROW); + makeVectors (pev->v_angle); + engine.drawLine (g_hostEntity, eyePos () - Vector (0.0f, 0.0f, 32.0f), eyePos () + g_pGlobals->v_forward * 300.0f, 10, 0, 255, 0, 0, 250, 5, 1, DRAW_ARROW); // now draw line from source to destination - PathNode *node = &m_navNode[0]; - - while (node != nullptr) - { - const Vector &srcPath = waypoints.GetPath (node->index)->origin; - node = node->next; - - if (node != nullptr) - { - const Vector &dstPath = waypoints.GetPath (node->index)->origin; - engine.DrawLine (g_hostEntity, srcPath, dstPath, 15, 0, 255, 100, 55, 200, 5, 1, DRAW_ARROW); - } + + for (size_t i = 0; i < m_path.length () && i + 1 < m_path.length (); i++) { + engine.drawLine (g_hostEntity, waypoints[m_path[i]].origin, waypoints[m_path[i + 1]].origin, 15, 0, 255, 100, 55, 200, 5, 1, DRAW_ARROW); } } } } -bool Bot::HasHostage (void) -{ - for (int i = 0; i < MAX_HOSTAGES; i++) - { - if (!engine.IsNullEntity (m_hostages[i])) - { +bool Bot::hasHostage (void) { + for (auto hostage : m_hostages) { + if (!engine.isNullEntity (hostage)) { + // don't care about dead hostages - if (m_hostages[i]->v.health <= 0.0f || (pev->origin - m_hostages[i]->v.origin).GetLength () > 600.0f) - { - m_hostages[i] = nullptr; + if (hostage->v.health <= 0.0f || (pev->origin - hostage->v.origin).lengthSq () > cr::square (600.0f)) { + hostage = nullptr; continue; } return true; @@ -5253,97 +5056,89 @@ bool Bot::HasHostage (void) return false; } -int Bot::GetAmmo (void) -{ - if (g_weaponDefs[m_currentWeapon].ammo1 == -1 || g_weaponDefs[m_currentWeapon].ammo1 > 31) +int Bot::ammo (void) { + if (g_weaponDefs[m_currentWeapon].ammo1 == -1 || g_weaponDefs[m_currentWeapon].ammo1 > MAX_WEAPONS - 1) { return 0; - + } return m_ammo[g_weaponDefs[m_currentWeapon].ammo1]; } -void Bot::GetDamage (edict_t *inflictor, int damage, int armor, int bits) -{ +void Bot::processDamage (edict_t *inflictor, int damage, int armor, int bits) { // this function gets called from the network message handler, when bot's gets hurt from any // other player. m_lastDamageType = bits; - CollectGoalExperience (damage, m_team); + collectGoalExperience (damage, m_team); - if (IsValidPlayer (inflictor)) - { - if (yb_tkpunish.GetBool () && engine.GetTeam (inflictor) == m_team && !IsValidBot (inflictor)) - { + if (isPlayer (inflictor)) { + if (yb_tkpunish.boolean () && engine.getTeam (inflictor) == m_team && !isFakeClient (inflictor)) { // alright, die you teamkiller!!! m_actualReactionTime = 0.0f; - m_seeEnemyTime = engine.Time(); + m_seeEnemyTime = engine.timebase (); m_enemy = inflictor; m_lastEnemy = m_enemy; m_lastEnemyOrigin = m_enemy->v.origin; m_enemyOrigin = m_enemy->v.origin; - ChatMessage(CHAT_TEAMATTACK); - HandleChatterMessage("#Bot_TeamAttack"); - ChatterMessage(Chatter_FriendlyFire); + pushChatMessage (CHAT_TEAMATTACK); + processChatterMessage ("#Bot_TeamAttack"); + pushChatterMessage (CHATTER_FRIENDLY_FIRE); } - else - { + else { // attacked by an enemy - if (pev->health > 60.0f) - { + if (pev->health > 60.0f) { m_agressionLevel += 0.1f; - if (m_agressionLevel > 1.0f) + if (m_agressionLevel > 1.0f) { m_agressionLevel += 1.0f; + } } - else - { + else { m_fearLevel += 0.03f; - if (m_fearLevel > 1.0f) + if (m_fearLevel > 1.0f) { m_fearLevel += 1.0f; + } } - RemoveCertainTask (TASK_CAMP); + clearTask (TASK_CAMP); - if (engine.IsNullEntity (m_enemy) && m_team != engine.GetTeam (inflictor)) - { + if (engine.isNullEntity (m_enemy) && m_team != engine.getTeam (inflictor)) { m_lastEnemy = inflictor; m_lastEnemyOrigin = inflictor->v.origin; // FIXME - Bot doesn't necessary sees this enemy - m_seeEnemyTime = engine.Time (); + m_seeEnemyTime = engine.timebase (); } - if (!(g_gameFlags & GAME_CSDM)) - CollectExperienceData (inflictor, armor + damage); + if (!(g_gameFlags & GAME_CSDM)) { + collectDataExperience (inflictor, armor + damage); + } } } - else // hurt by unusual damage like drowning or gas - { + // hurt by unusual damage like drowning or gas + else { // leave the camping/hiding position - if (!waypoints.Reachable (this, waypoints.FindNearest (m_destOrigin))) - { - DeleteSearchNodes (); - FindWaypoint (); + if (!waypoints.isReachable (this, waypoints.getNearest (m_destOrigin))) { + clearSearchNodes (); + searchOptimalPoint (); } } } -void Bot::GotBlind (int alpha) -{ +void Bot::processBlind (int alpha) { // this function gets called by network message handler, when screenfade message get's send // it's used to make bot blind from the grenade. - m_maxViewDistance = Random.Float (10.0f, 20.0f); - m_blindTime = engine.Time () + static_cast (alpha - 200) / 16.0f; + m_maxViewDistance = rng.getFloat (10.0f, 20.0f); + m_blindTime = engine.timebase () + static_cast (alpha - 200) / 16.0f; - if (m_blindTime < engine.Time ()) + if (m_blindTime < engine.timebase ()) { return; - + } m_enemy = nullptr; - if (m_difficulty <= 2) - { + if (m_difficulty <= 2) { m_blindMoveSpeed = 0.0f; m_blindSidemoveSpeed = 0.0f; m_blindButton = IN_DUCK; @@ -5354,398 +5149,421 @@ void Bot::GotBlind (int alpha) m_blindMoveSpeed = -pev->maxspeed; m_blindSidemoveSpeed = 0.0f; - if (Random.Int (0, 100) > 50) + if (rng.getInt (0, 100) > 50) { m_blindSidemoveSpeed = pev->maxspeed; - else + } + else { m_blindSidemoveSpeed = -pev->maxspeed; + } - if (pev->health < 85.0f) + if (pev->health < 85.0f) { m_blindMoveSpeed = -pev->maxspeed; - else if (m_personality == PERSONALITY_CAREFUL) - { + } + else if (m_personality == PERSONALITY_CAREFUL) { m_blindMoveSpeed = 0.0f; m_blindButton = IN_DUCK; } - else + else { m_blindMoveSpeed = pev->maxspeed; + } } -void Bot::CollectGoalExperience (int damage, int team) -{ +void Bot::collectGoalExperience (int damage, int team) { // gets called each time a bot gets damaged by some enemy. tries to achieve a statistic about most/less dangerous // waypoints for a destination goal used for pathfinding - if (g_numWaypoints < 1 || waypoints.HasChanged () || m_chosenGoalIndex < 0 || m_prevGoalIndex < 0) + if (waypoints.length () < 1 || waypoints.hasChanged () || m_chosenGoalIndex < 0 || m_prevGoalIndex < 0) { return; + } // only rate goal waypoint if bot died because of the damage // FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons - if (pev->health - damage <= 0) - { - if (team == TERRORIST) - { - int value = (g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team0Value; + if (pev->health - damage <= 0) { + if (team == TEAM_TERRORIST) { + int value = (g_experienceData + (m_chosenGoalIndex * waypoints.length ()) + m_prevGoalIndex)->team0Value; value -= static_cast (pev->health / 20); - if (value < -MAX_GOAL_VALUE) + if (value < -MAX_GOAL_VALUE) { value = -MAX_GOAL_VALUE; - - else if (value > MAX_GOAL_VALUE) + } + else if (value > MAX_GOAL_VALUE) { value = MAX_GOAL_VALUE; - - (g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team0Value = static_cast (value); + } + (g_experienceData + (m_chosenGoalIndex * waypoints.length ()) + m_prevGoalIndex)->team0Value = static_cast (value); } - else - { - int value = (g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team1Value; + else { + int value = (g_experienceData + (m_chosenGoalIndex * waypoints.length ()) + m_prevGoalIndex)->team1Value; value -= static_cast (pev->health / 20); - if (value < -MAX_GOAL_VALUE) + if (value < -MAX_GOAL_VALUE) { value = -MAX_GOAL_VALUE; - - else if (value > MAX_GOAL_VALUE) + } + else if (value > MAX_GOAL_VALUE) { value = MAX_GOAL_VALUE; - - (g_experienceData + (m_chosenGoalIndex * g_numWaypoints) + m_prevGoalIndex)->team1Value = static_cast (value); + } + (g_experienceData + (m_chosenGoalIndex * waypoints.length ()) + m_prevGoalIndex)->team1Value = static_cast (value); } } } -void Bot::CollectExperienceData (edict_t *attacker, int damage) -{ +void Bot::collectDataExperience (edict_t *attacker, int damage) { // this function gets called each time a bot gets damaged by some enemy. sotores the damage (teamspecific) done by victim. - if (!IsValidPlayer (attacker)) + if (!isPlayer (attacker)) { return; + } - int attackerTeam = engine.GetTeam (attacker); + int attackerTeam = engine.getTeam (attacker); int victimTeam = m_team; - if (attackerTeam == victimTeam ) + if (attackerTeam == victimTeam) { return; + } // if these are bots also remember damage to rank destination of the bot m_goalValue -= static_cast (damage); - if (bots.GetBot (attacker) != nullptr) - bots.GetBot (attacker)->m_goalValue += static_cast (damage); - - if (damage < 20) - return; // do not collect damage less than 20 - - int attackerIndex = waypoints.FindNearest (attacker->v.origin); - int victimIndex = waypoints.FindNearest (pev->origin); - - if (pev->health > 20.0f) - { - if (victimTeam == TERRORIST) - (g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team0Damage++; - else - (g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team1Damage++; - - if ((g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team0Damage > MAX_DAMAGE_VALUE) - (g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team0Damage = MAX_DAMAGE_VALUE; - - if ((g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team1Damage > MAX_DAMAGE_VALUE) - (g_experienceData + (victimIndex * g_numWaypoints) + victimIndex)->team1Damage = MAX_DAMAGE_VALUE; + if (bots.getBot (attacker) != nullptr) { + bots.getBot (attacker)->m_goalValue += static_cast (damage); } - float updateDamage = IsValidBot (attacker) ? 10.0f : 7.0f; + if (damage < 20) { + return; // do not collect damage less than 20 + } + + int attackerIndex = waypoints.getNearest (attacker->v.origin); + int victimIndex = m_currentWaypointIndex; + + if (victimIndex == INVALID_WAYPOINT_INDEX) { + victimIndex = getNearestPoint (); + } + + if (pev->health > 20.0f) { + if (victimTeam == TEAM_TERRORIST) { + (g_experienceData + (victimIndex * waypoints.length ()) + victimIndex)->team0Damage++; + } + else { + (g_experienceData + (victimIndex * waypoints.length ()) + victimIndex)->team1Damage++; + } + + if ((g_experienceData + (victimIndex * waypoints.length ()) + victimIndex)->team0Damage > MAX_DAMAGE_VALUE) { + (g_experienceData + (victimIndex * waypoints.length ()) + victimIndex)->team0Damage = MAX_DAMAGE_VALUE; + } + + if ((g_experienceData + (victimIndex * waypoints.length ()) + victimIndex)->team1Damage > MAX_DAMAGE_VALUE) { + (g_experienceData + (victimIndex * waypoints.length ()) + victimIndex)->team1Damage = MAX_DAMAGE_VALUE; + } + } + float updateDamage = isFakeClient (attacker) ? 10.0f : 7.0f; // store away the damage done - if (victimTeam == TERRORIST) - { - int value = (g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team0Damage; + if (victimTeam == TEAM_TERRORIST) { + int value = (g_experienceData + (victimIndex * waypoints.length ()) + attackerIndex)->team0Damage; value += static_cast (damage / updateDamage); - if (value > MAX_DAMAGE_VALUE) + if (value > MAX_DAMAGE_VALUE) { value = MAX_DAMAGE_VALUE; + } - if (value > g_highestDamageT) + if (value > g_highestDamageT) { g_highestDamageT = value; - - (g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team0Damage = static_cast (value); + } + (g_experienceData + (victimIndex * waypoints.length ()) + attackerIndex)->team0Damage = static_cast (value); } - else - { - int value = (g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team1Damage; + else { + int value = (g_experienceData + (victimIndex * waypoints.length ()) + attackerIndex)->team1Damage; value += static_cast (damage / updateDamage); - if (value > MAX_DAMAGE_VALUE) + if (value > MAX_DAMAGE_VALUE) { value = MAX_DAMAGE_VALUE; + } - if (value > g_highestDamageCT) + if (value > g_highestDamageCT) { g_highestDamageCT = value; - - (g_experienceData + (victimIndex * g_numWaypoints) + attackerIndex)->team1Damage = static_cast (value); + } + (g_experienceData + (victimIndex * waypoints.length ()) + attackerIndex)->team1Damage = static_cast (value); } } -void Bot::HandleChatterMessage (const char *tempMessage) -{ +void Bot::processChatterMessage (const char *tempMessage) { // this function is added to prevent engine crashes with: 'Message XX started, before message XX ended', or something. - if ((m_team == CT && strcmp (tempMessage, "#CTs_Win") == 0) || (m_team == TERRORIST && strcmp (tempMessage, "#Terrorists_Win") == 0)) - { - if (g_timeRoundMid > engine.Time ()) - ChatterMessage (Chatter_QuicklyWonTheRound); - else - ChatterMessage (Chatter_WonTheRound); + if ((m_team == TEAM_COUNTER && strcmp (tempMessage, "#CTs_Win") == 0) || (m_team == TEAM_TERRORIST && strcmp (tempMessage, "#Terrorists_Win") == 0)) { + if (g_timeRoundMid > engine.timebase ()) { + pushChatterMessage (CHATTER_QUICK_WON_ROUND); + } + else { + pushChatterMessage (CHATTER_WON_THE_ROUND); + } } - else if (strcmp (tempMessage, "#Bot_TeamAttack") == 0) - ChatterMessage (Chatter_FriendlyFire); - - else if (strcmp (tempMessage, "#Bot_NiceShotCommander") == 0) - ChatterMessage (Chatter_NiceshotCommander); - - else if (strcmp (tempMessage, "#Bot_NiceShotPall") == 0) - ChatterMessage (Chatter_NiceshotPall); + else if (strcmp (tempMessage, "#Bot_TeamAttack") == 0) { + pushChatterMessage (CHATTER_FRIENDLY_FIRE); + } + else if (strcmp (tempMessage, "#Bot_NiceShotCommander") == 0) { + pushChatterMessage (CHATTER_NICESHOT_COMMANDER); + } + else if (strcmp (tempMessage, "#Bot_NiceShotPall") == 0) { + pushChatterMessage (CHATTER_NICESHOT_PALL); + } } -void Bot::ChatMessage (int type, bool isTeamSay) -{ +void Bot::pushChatMessage (int type, bool isTeamSay) { extern ConVar yb_chat; - if (g_chatFactory[type].IsEmpty () || !yb_chat.GetBool ()) + if (g_chatFactory[type].empty () || !yb_chat.boolean ()) { return; + } + const char *pickedPhrase = g_chatFactory[type].random ().chars (); - const char *pickedPhrase = g_chatFactory[type].GetRandomElement ().GetBuffer (); - - if (IsNullString (pickedPhrase)) + if (isEmptyStr (pickedPhrase)) { return; + } - PrepareChatMessage (const_cast (pickedPhrase)); - PushMessageQueue (isTeamSay ? GAME_MSG_SAY_TEAM_MSG : GAME_MSG_SAY_CMD); + prepareChatMessage (const_cast (pickedPhrase)); + pushMsgQueue (isTeamSay ? GAME_MSG_SAY_TEAM_MSG : GAME_MSG_SAY_CMD); } -void Bot::DiscardWeaponForUser (edict_t *user, bool discardC4) -{ +void Bot::dropWeaponForUser (edict_t *user, bool discardC4) { // this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop* // command, very useful, when i'm don't have money to buy anything... ) - if (IsAlive (user) && m_moneyAmount >= 2000 && HasPrimaryWeapon () && (user->v.origin - pev->origin).GetLength () <= 450.0f) - { + if (isAlive (user) && m_moneyAmount >= 2000 && hasPrimaryWeapon () && (user->v.origin - pev->origin).length () <= 450.0f) { m_aimFlags |= AIM_ENTITY; m_lookAt = user->v.origin; - if (discardC4) - { - SelectWeaponByName ("weapon_c4"); - engine.IssueBotCommand (GetEntity (), "drop"); + if (discardC4) { + selectWeaponByName ("weapon_c4"); + engine.execBotCmd (ent (), "drop"); } - else - { - SelectBestWeapon (); - engine.IssueBotCommand (GetEntity (), "drop"); + else { + selectBestWeapon (); + engine.execBotCmd (ent (), "drop"); } m_pickupItem = nullptr; m_pickupType = PICKUP_NONE; - m_itemCheckTime = engine.Time () + 5.0f; + m_itemCheckTime = engine.timebase () + 5.0f; - if (m_inBuyZone) - { + if (m_inBuyZone) { + m_ignoreBuyDelay = true; m_buyingFinished = false; m_buyState = BUYSTATE_PRIMARY_WEAPON; - PushMessageQueue (GAME_MSG_PURCHASE); - m_nextBuyTime = engine.Time (); + pushMsgQueue (GAME_MSG_PURCHASE); + m_nextBuyTime = engine.timebase (); } } } -void Bot::StartDoubleJump (edict_t *ent) -{ - ResetDoubleJumpState (); +void Bot::startDoubleJump (edict_t *ent) { + resetDoubleJump (); m_doubleJumpOrigin = ent->v.origin; m_doubleJumpEntity = ent; - PushTask (TASK_DOUBLEJUMP, TASKPRI_DOUBLEJUMP, -1, engine.Time (), true); - TeamSayText (FormatBuffer ("Ok %s, i will help you!", STRING (ent->v.netname))); + startTask (TASK_DOUBLEJUMP, TASKPRI_DOUBLEJUMP, INVALID_WAYPOINT_INDEX, engine.timebase (), true); + sayTeam (format ("Ok %s, i will help you!", STRING (ent->v.netname))); } -void Bot::ResetDoubleJumpState (void) -{ - TaskComplete (); +void Bot::resetDoubleJump (void) { + completeTask (); m_doubleJumpEntity = nullptr; m_duckForJump = 0.0f; - m_doubleJumpOrigin.Zero (); - m_travelStartIndex = -1; + m_doubleJumpOrigin.nullify (); + m_travelStartIndex = INVALID_WAYPOINT_INDEX; m_jumpReady = false; } -void Bot::DebugMsg (const char *format, ...) -{ - int level = yb_debug.GetInt (); +void Bot::sayDebug (const char *format, ...) { + int level = yb_debug.integer (); - if (level <= 2) + if (level <= 2) { return; - + } va_list ap; char buffer[MAX_PRINT_BUFFER]; va_start (ap, format); - vsnprintf (buffer, SIZEOF_CHAR (buffer), format, ap); + vsnprintf (buffer, cr::bufsize (buffer), format, ap); va_end (ap); - char printBuf[MAX_PRINT_BUFFER]; - sprintf (printBuf, "%s: %s", STRING (pev->netname), buffer); + String printBuf; + printBuf.format ("%s: %s", STRING (pev->netname), buffer); bool playMessage = false; - if (level == 3 && !engine.IsNullEntity (g_hostEntity) && g_hostEntity->v.iuser2 == GetIndex ()) + if (level == 3 && !engine.isNullEntity (g_hostEntity) && g_hostEntity->v.iuser2 == index ()) { playMessage = true; - else if (level != 3) + } + else if (level != 3) { playMessage = true; - - if (playMessage && level > 3) - AddLogEntry (false, LL_DEFAULT, printBuf); - - if (playMessage) - { - engine.Printf (printBuf); - SayText (printBuf); + } + if (playMessage && level > 3) { + logEntry (false, LL_DEFAULT, printBuf.chars ()); + } + if (playMessage) { + engine.print (printBuf.chars ()); + say (printBuf.chars ()); } } -Vector Bot::CheckToss(const Vector &start, const Vector &stop) -{ +Vector Bot::calcToss (const Vector &start, const Vector &stop) { // this function returns the velocity at which an object should looped from start to land near end. // returns null vector if toss is not feasible. TraceResult tr; - float gravity = sv_gravity.GetFloat () * 0.55f; + float gravity = sv_gravity.flt () * 0.55f; Vector end = stop - pev->velocity; end.z -= 15.0f; - if (fabsf (end.z - start.z) > 500.0f) - return Vector::GetZero (); - + if (cr::abs (end.z - start.z) > 500.0f) { + return Vector::null (); + } Vector midPoint = start + (end - start) * 0.5f; - engine.TestHull (midPoint, midPoint + Vector (0.0f, 0.0f, 500.0f), TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); + engine.testHull (midPoint, midPoint + Vector (0.0f, 0.0f, 500.0f), TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); - if (tr.flFraction < 1.0f) - { + if (tr.flFraction < 1.0f) { midPoint = tr.vecEndPos; midPoint.z = tr.pHit->v.absmin.z - 1.0f; } - if ((midPoint.z < start.z) || (midPoint.z < end.z)) - return Vector::GetZero (); + if (midPoint.z < start.z || midPoint.z < end.z) { + return Vector::null (); + } + float timeOne = cr::sqrtf ((midPoint.z - start.z) / (0.5f * gravity)); + float timeTwo = cr::sqrtf ((midPoint.z - end.z) / (0.5f * gravity)); - float timeOne = A_sqrtf ((midPoint.z - start.z) / (0.5f * gravity)); - float timeTwo = A_sqrtf ((midPoint.z - end.z) / (0.5f * gravity)); + if (timeOne < 0.1f) { + return Vector::null (); + } + Vector velocity = (end - start) / (timeOne + timeTwo); + velocity.z = gravity * timeOne; - if (timeOne < 0.1f) - return Vector::GetZero (); - - Vector nadeVelocity = (end - start) / (timeOne + timeTwo); - nadeVelocity.z = gravity * timeOne; - - Vector apex = start + nadeVelocity * timeOne; + Vector apex = start + velocity * timeOne; apex.z = midPoint.z; - engine.TestHull (start, apex, TRACE_IGNORE_NONE, head_hull, GetEntity (), &tr); + engine.testHull (start, apex, TRACE_IGNORE_NONE, head_hull, ent (), &tr); - if (tr.flFraction < 1.0f || tr.fAllSolid) - return Vector::GetZero (); - - engine.TestHull (end, apex, TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); - - if (tr.flFraction != 1.0f) - { - float dot = -(tr.vecPlaneNormal | (apex - end).Normalize ()); - - if (dot > 0.7f || tr.flFraction < 0.8f) // 60 degrees - return Vector::GetZero (); + if (tr.flFraction < 1.0f || tr.fAllSolid) { + return Vector::null (); } - return nadeVelocity * 0.777f; + engine.testHull (end, apex, TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); + + if (tr.flFraction != 1.0f) { + float dot = -(tr.vecPlaneNormal | (apex - end).normalize ()); + + if (dot > 0.7f || tr.flFraction < 0.8f) { + return Vector::null (); + } + } + return velocity * 0.777f; } -Vector Bot::CheckThrow(const Vector &start, const Vector &stop) -{ +Vector Bot::calcThrow (const Vector &start, const Vector &stop) { // this function returns the velocity vector at which an object should be thrown from start to hit end. // returns null vector if throw is not feasible. - Vector nadeVelocity = (stop - start); + Vector velocity = stop - start; TraceResult tr; - float gravity = sv_gravity.GetFloat () * 0.55f; - float time = nadeVelocity.GetLength () / 195.0f; + float gravity = sv_gravity.flt () * 0.55f; + float time = velocity.length () / 195.0f; - if (time < 0.01f) - return Vector::GetZero (); - else if (time > 2.0f) + if (time < 0.01f) { + return Vector::null (); + } + else if (time > 2.0f) { time = 1.2f; - - nadeVelocity = nadeVelocity * (1.0f / time); - nadeVelocity.z += gravity * time * 0.5f; + } + velocity = velocity * (1.0f / time); + velocity.z += gravity * time * 0.5f; Vector apex = start + (stop - start) * 0.5f; apex.z += 0.5f * gravity * (time * 0.5f) * (time * 0.5f); - engine.TestHull (start, apex, TRACE_IGNORE_NONE, head_hull, GetEntity (), &tr); + engine.testHull (start, apex, TRACE_IGNORE_NONE, head_hull, ent (), &tr); - if (tr.flFraction != 1.0f) - return Vector::GetZero (); - - engine.TestHull (stop, apex, TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); - - if (tr.flFraction != 1.0 || tr.fAllSolid) - { - float dot = -(tr.vecPlaneNormal | (apex - stop).Normalize ()); - - if (dot > 0.7f || tr.flFraction < 0.8f) - return Vector::GetZero (); + if (tr.flFraction != 1.0f) { + return Vector::null (); } - return nadeVelocity * 0.7793f; + engine.testHull (stop, apex, TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); + + if (tr.flFraction != 1.0 || tr.fAllSolid) { + float dot = -(tr.vecPlaneNormal | (apex - stop).normalize ()); + + if (dot > 0.7f || tr.flFraction < 0.8f) { + return Vector::null (); + } + } + return velocity * 0.7793f; } -Vector Bot::CheckBombAudible (void) -{ +edict_t *Bot::correctGrenadeVelocity (const char *model) { + edict_t *pent = nullptr; + + while (!engine.isNullEntity (pent = g_engfuncs.pfnFindEntityByString (pent, "classname", "grenade"))) { + if (pent->v.owner == ent () && strcmp (STRING (pent->v.model) + 9, model) == 0) { + // set the correct velocity for the grenade + if (m_grenade.lengthSq () > 100.0f) { + pent->v.velocity = m_grenade; + } + m_grenadeCheckTime = engine.timebase () + MAX_GRENADE_TIMER; + + selectBestWeapon (); + completeTask (); + + break; + } + } + return pent; +} + +Vector Bot::isBombAudible (void) { // this function checks if bomb is can be heard by the bot, calculations done by manual testing. - if (!g_bombPlanted || GetTaskId () == TASK_ESCAPEFROMBOMB) - return Vector::GetZero (); // reliability check + if (!g_bombPlanted || taskId () == TASK_ESCAPEFROMBOMB) { + return Vector::null (); // reliability check + } - if (m_difficulty >= 3) - return waypoints.GetBombPosition(); + if (m_difficulty > 2) { + return waypoints.getBombPos (); + } + const Vector &bombOrigin = waypoints.getBombPos (); - const Vector &bombOrigin = waypoints.GetBombPosition (); - - float timeElapsed = ((engine.Time () - g_timeBombPlanted) / mp_c4timer.GetFloat ()) * 100.0f; + float timeElapsed = ((engine.timebase () - g_timeBombPlanted) / mp_c4timer.flt ()) * 100.0f; float desiredRadius = 768.0f; // start the manual calculations - if (timeElapsed > 85.0f) + if (timeElapsed > 85.0f) { desiredRadius = 4096.0f; - else if (timeElapsed > 68.0f) + } + else if (timeElapsed > 68.0f) { desiredRadius = 2048.0f; - else if (timeElapsed > 52.0f) + } + else if (timeElapsed > 52.0f) { desiredRadius = 1280.0f; - else if (timeElapsed > 28.0f) + } + else if (timeElapsed > 28.0f) { desiredRadius = 1024.0f; + } // we hear bomb if length greater than radius - if (desiredRadius < (pev->origin - bombOrigin).GetLength2D ()) + if (desiredRadius < (pev->origin - bombOrigin).length2D ()) { return bombOrigin; - - return Vector::GetZero (); + } + return Vector::null (); } -uint8 Bot::ThrottledMsec (void) -{ +uint8 Bot::computeMsec (void) { // estimate msec to use for this command based on time passed from the previous command - return static_cast ((engine.Time () - m_lastCommandTime) * 1000.0f); + return static_cast ((engine.timebase () - m_lastCommandTime) * 1000.0f); } -void Bot::RunPlayerMovement (void) -{ +void Bot::runMovement (void) { // the purpose of this function is to compute, according to the specified computation // method, the msec value which will be passed as an argument of pfnRunPlayerMove. This // function is called every frame for every bot, since the RunPlayerMove is the function @@ -5760,278 +5578,250 @@ void Bot::RunPlayerMovement (void) // elapses, that bot will behave like a ghost : no movement, but bullets and players can // pass through it. Then, when the next frame will begin, the stucking problem will arise ! - m_frameInterval = engine.Time () - m_lastCommandTime; + m_frameInterval = engine.timebase () - m_lastCommandTime; - uint8 msecVal = ThrottledMsec (); - m_lastCommandTime = engine.Time (); + uint8 msecVal = computeMsec (); + m_lastCommandTime = engine.timebase (); g_engfuncs.pfnRunPlayerMove (pev->pContainingEntity, m_moveAngles, m_moveSpeed, m_strafeSpeed, 0.0f, static_cast (pev->button), static_cast (pev->impulse), msecVal); + + // save our own copy of old buttons, since bot ai code is not running every frame now + m_oldButtons = pev->button; } -void Bot::CheckBurstMode (float distance) -{ +void Bot::checkBurstMode (float distance) { // this function checks burst mode, and switch it depending distance to to enemy. - if (HasShield ()) - return; // no checking when shiled is active + if (hasShield ()) { + return; // no checking when shield is active + } // if current weapon is glock, disable burstmode on long distances, enable it else - if (m_currentWeapon == WEAPON_GLOCK && distance < 300.0f && m_weaponBurstMode == BM_OFF) + if (m_currentWeapon == WEAPON_GLOCK && distance < 300.0f && m_weaponBurstMode == BM_OFF) { pev->button |= IN_ATTACK2; - else if (m_currentWeapon == WEAPON_GLOCK && distance >= 300.0f && m_weaponBurstMode == BM_ON) + } + else if (m_currentWeapon == WEAPON_GLOCK && distance >= 300.0f && m_weaponBurstMode == BM_ON) { pev->button |= IN_ATTACK2; + } // if current weapon is famas, disable burstmode on short distances, enable it else - if (m_currentWeapon == WEAPON_FAMAS && distance > 400.0f && m_weaponBurstMode == BM_OFF) + if (m_currentWeapon == WEAPON_FAMAS && distance > 400.0f && m_weaponBurstMode == BM_OFF) { pev->button |= IN_ATTACK2; - else if (m_currentWeapon == WEAPON_FAMAS && distance <= 400.0f && m_weaponBurstMode == BM_ON) + } + else if (m_currentWeapon == WEAPON_FAMAS && distance <= 400.0f && m_weaponBurstMode == BM_ON) { pev->button |= IN_ATTACK2; + } } -void Bot::CheckSilencer (void) -{ - if (((m_currentWeapon == WEAPON_USP && m_difficulty < 2) || m_currentWeapon == WEAPON_M4A1) && !HasShield()) - { - int random = (m_personality == PERSONALITY_RUSHER ? 35 : 65); +void Bot::checkSilencer (void) { + if (((m_currentWeapon == WEAPON_USP && m_difficulty < 2) || m_currentWeapon == WEAPON_M4A1) && !hasShield ()) { + int prob = (m_personality == PERSONALITY_RUSHER ? 35 : 65); // aggressive bots don't like the silencer - if (Random.Int (1, 100) <= (m_currentWeapon == WEAPON_USP ? random / 3 : random)) - { - if (pev->weaponanim > 6) // is the silencer not attached... + if (rng.getInt (1, 100) <= (m_currentWeapon == WEAPON_USP ? prob / 3 : prob)) { + + // is the silencer not attached... + if (pev->weaponanim > 6) { pev->button |= IN_ATTACK2; // attach the silencer + } } - else - { - if (pev->weaponanim <= 6) // is the silencer attached... + else { + + // is the silencer attached... + if (pev->weaponanim <= 6) { pev->button |= IN_ATTACK2; // detach the silencer + } } } } -float Bot::GetBombTimeleft (void) -{ - if (!g_bombPlanted) +float Bot::getBombTimeleft (void) { + if (!g_bombPlanted) { return 0.0f; + } + float timeLeft = ((g_timeBombPlanted + mp_c4timer.flt ()) - engine.timebase ()); - float timeLeft = ((g_timeBombPlanted + mp_c4timer.GetFloat ()) - engine.Time ()); - - if (timeLeft < 0.0f) + if (timeLeft < 0.0f) { return 0.0f; - + } return timeLeft; } -float Bot::GetEstimatedReachTime (void) -{ - float estimatedTime = 2.0f; // time to reach next waypoint - - // calculate 'real' time that we need to get from one waypoint to another - if (m_currentWaypointIndex >= 0 && m_currentWaypointIndex < g_numWaypoints && m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints) - { - float distance = (waypoints.GetPath (m_prevWptIndex[0])->origin - m_currentPath->origin).GetLength (); - - // caclulate estimated time - if (pev->maxspeed <= 0.0f) - estimatedTime = 4.0f * distance / 240.0f; - else - estimatedTime = 4.0f * distance / pev->maxspeed; - - bool longTermReachability = (m_currentPath->flags & FLAG_CROUCH) || (m_currentPath->flags & FLAG_LADDER) || (pev->button & IN_DUCK); - - // check for special waypoints, that can slowdown our movement - if (longTermReachability) - estimatedTime *= 3.0f; - - // check for too low values - if (estimatedTime < 1.0f) - estimatedTime = 1.0f; - - const float maxReachTime = longTermReachability ? 10.0f : 5.0f; - - // check for too high values - if (estimatedTime > maxReachTime) - estimatedTime = maxReachTime; +bool Bot::isOutOfBombTimer (void) { + if (!(g_mapFlags & MAP_DE)) { + return false; } - return estimatedTime; -} -bool Bot::OutOfBombTimer (void) -{ - if (m_currentWaypointIndex == -1 || ((g_mapType & MAP_DE) && (m_hasProgressBar || GetTaskId () == TASK_ESCAPEFROMBOMB))) + if (m_currentWaypointIndex == INVALID_WAYPOINT_INDEX || (m_hasProgressBar || taskId () == TASK_ESCAPEFROMBOMB)) { return false; // if CT bot already start defusing, or already escaping, return false + } // calculate left time - float timeLeft = GetBombTimeleft (); + float timeLeft = getBombTimeleft (); // if time left greater than 13, no need to do other checks - if (timeLeft > 13.0f) + if (timeLeft > 13.0f) { return false; - - const Vector &bombOrigin = waypoints.GetBombPosition (); + } + const Vector &bombOrigin = waypoints.getBombPos (); // for terrorist, if timer is lower than 13 seconds, return true - if (static_cast (timeLeft) < 13 && m_team == TERRORIST && (bombOrigin - pev->origin).GetLength () < 1000.0f) + if (timeLeft < 13.0f && m_team == TEAM_TERRORIST && (bombOrigin - pev->origin).lengthSq () < cr::square (964.0f)) { return true; - + } bool hasTeammatesWithDefuserKit = false; // check if our teammates has defusal kit - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = nullptr; // temporaly pointer to bot + for (int i = 0; i < engine.maxClients (); i++) { + auto *bot = bots.getBot (i); // search players with defuse kit - if ((bot = bots.GetBot (i)) != nullptr && bot->m_team == CT && bot->m_hasDefuser && (bombOrigin - bot->pev->origin).GetLength () < 500.0f) - { + if (bot != nullptr && bot != this && bot->m_team == TEAM_COUNTER && bot->m_hasDefuser && (bombOrigin - bot->pev->origin).lengthSq () < cr::square (512.0f)) { hasTeammatesWithDefuserKit = true; break; } } // add reach time to left time - float reachTime = waypoints.GetTravelTime (pev->maxspeed, m_currentPath->origin, bombOrigin); + float reachTime = waypoints.calculateTravelTime (pev->maxspeed, m_currentPath->origin, bombOrigin); // for counter-terrorist check alos is we have time to reach position plus average defuse time - if ((timeLeft < reachTime + 6.0f && !m_hasDefuser && !hasTeammatesWithDefuserKit) || (timeLeft < reachTime + 2.0f && m_hasDefuser)) + if ((timeLeft < reachTime + 8.0f && !m_hasDefuser && !hasTeammatesWithDefuserKit) || (timeLeft < reachTime + 4.0f && m_hasDefuser)) { return true; + } - if (m_hasProgressBar && IsOnFloor () && ((m_hasDefuser ? 10.0f : 15.0f) > GetBombTimeleft ())) + if (m_hasProgressBar && isOnFloor () && ((m_hasDefuser ? 10.0f : 15.0f) > getBombTimeleft ())) { return true; - + } return false; // return false otherwise } -void Bot::ReactOnSound (void) -{ - int hearEnemyIndex = -1; +void Bot::processHearing (void) { + int hearEnemyIndex = INVALID_WAYPOINT_INDEX; float minDistance = 99999.0f; // loop through all enemy clients to check for hearable stuff - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == GetEntity () || client.team == m_team || client.timeSoundLasting < engine.Time ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == ent () || client.team == m_team || client.timeSoundLasting < engine.timebase ()) { continue; + } + float distance = (client.soundPos - pev->origin).length (); - float distance = (client.soundPos - pev->origin).GetLength (); - - if (distance > client.hearingDistance) + if (distance > client.hearingDistance) { continue; + } - if (distance < minDistance) - { + if (distance < minDistance) { hearEnemyIndex = i; minDistance = distance; } } edict_t *player = nullptr; - if (hearEnemyIndex >= 0 && g_clients[hearEnemyIndex].team != m_team && !(g_gameFlags & GAME_CSDM_FFA)) + if (hearEnemyIndex >= 0 && g_clients[hearEnemyIndex].team != m_team && !(g_gameFlags & GAME_CSDM_FFA)) { player = g_clients[hearEnemyIndex].ent; + } // did the bot hear someone ? - if (IsValidPlayer (player)) - { + if (player != nullptr && isPlayer (player)) { // change to best weapon if heard something - if (m_shootTime < engine.Time () - 5.0f && IsOnFloor () && m_currentWeapon != WEAPON_C4 && m_currentWeapon != WEAPON_EXPLOSIVE && m_currentWeapon != WEAPON_SMOKE && m_currentWeapon != WEAPON_FLASHBANG && !yb_jasonmode.GetBool ()) - SelectBestWeapon (); + if (m_shootTime < engine.timebase () - 5.0f && isOnFloor () && m_currentWeapon != WEAPON_C4 && m_currentWeapon != WEAPON_EXPLOSIVE && m_currentWeapon != WEAPON_SMOKE && m_currentWeapon != WEAPON_FLASHBANG && !yb_jasonmode.boolean ()) { + selectBestWeapon (); + } - m_heardSoundTime = engine.Time (); + m_heardSoundTime = engine.timebase (); m_states |= STATE_HEARING_ENEMY; - if ((Random.Int (0, 100) < 15) && engine.IsNullEntity (m_enemy) && engine.IsNullEntity (m_lastEnemy) && m_seeEnemyTime + 7.0f < engine.Time ()) - ChatterMessage (Chatter_HeardEnemy); + if (rng.getInt (0, 100) < 15 && engine.isNullEntity (m_enemy) && engine.isNullEntity (m_lastEnemy) && m_seeEnemyTime + 7.0f < engine.timebase ()) { + pushChatterMessage (CHATTER_HEARD_ENEMY); + } // didn't bot already have an enemy ? take this one... - if (m_lastEnemyOrigin.IsZero () || m_lastEnemy == nullptr) - { + if (m_lastEnemyOrigin.empty () || m_lastEnemy == nullptr) { m_lastEnemy = player; m_lastEnemyOrigin = player->v.origin; } - else // bot had an enemy, check if it's the heard one - { - if (player == m_lastEnemy) - { - // bot sees enemy ? then bail out ! - if (m_states & STATE_SEEING_ENEMY) - return; + // bot had an enemy, check if it's the heard one + else { + if (player == m_lastEnemy) { + // bot sees enemy ? then bail out ! + if (m_states & STATE_SEEING_ENEMY) { + return; + } m_lastEnemyOrigin = player->v.origin; } - else - { + else { // if bot had an enemy but the heard one is nearer, take it instead - float distance = (m_lastEnemyOrigin - pev->origin).GetLengthSquared (); + float distance = (m_lastEnemyOrigin - pev->origin).lengthSq (); - if (distance > (player->v.origin - pev->origin).GetLengthSquared () && m_seeEnemyTime + 2.0f < engine.Time ()) - { + if (distance > (player->v.origin - pev->origin).lengthSq () && m_seeEnemyTime + 2.0f < engine.timebase ()) { m_lastEnemy = player; m_lastEnemyOrigin = player->v.origin; } - else + else { return; + } } } extern ConVar yb_shoots_thru_walls; // check if heard enemy can be seen - if (CheckVisibility (player, &m_lastEnemyOrigin, &m_visibility)) - { + if (checkBodyParts (player, &m_enemyOrigin, &m_visibility)) { m_enemy = player; m_lastEnemy = player; - m_enemyOrigin = m_lastEnemyOrigin; + m_lastEnemyOrigin = m_enemyOrigin; m_states |= STATE_SEEING_ENEMY; - m_seeEnemyTime = engine.Time (); + m_seeEnemyTime = engine.timebase (); } - else // check if heard enemy can be shoot through some obstacle - { - if (m_difficulty > 2 && m_lastEnemy == player && m_seeEnemyTime + 3.0 > engine.Time () && yb_shoots_thru_walls.GetBool () && IsShootableThruObstacle (player->v.origin + player->v.view_ofs)) - { + + // check if heard enemy can be shoot through some obstacle + else { + if (m_difficulty > 2 && m_lastEnemy == player && m_seeEnemyTime + 3.0f > engine.timebase () && yb_shoots_thru_walls.boolean () && isPenetrableObstacle (player->v.origin)) { m_enemy = player; m_lastEnemy = player; m_enemyOrigin = player->v.origin; m_lastEnemyOrigin = player->v.origin; m_states |= (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY); - m_seeEnemyTime = engine.Time (); - + m_seeEnemyTime = engine.timebase (); } } } } -bool Bot::IsShootableBreakable (edict_t *ent) -{ +bool Bot::isShootableBreakable (edict_t *ent) { // this function is checking that pointed by ent pointer obstacle, can be destroyed. - if (FClassnameIs (ent, "func_breakable") || (FClassnameIs (ent, "func_pushable") && (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; + auto classname = STRING (ent->v.classname); + if (strcmp (classname, "func_breakable") == 0 || (strcmp (classname, "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; } -void Bot::EquipInBuyzone (int buyState) -{ +void Bot::processBuyzoneEntering (int buyState) { // this function is gets called when bot enters a buyzone, to allow bot to buy some stuff // if bot is in buy zone, try to buy ammo for this weapon... - if (m_seeEnemyTime + 5.0f < engine.Time () && m_lastEquipTime + 15.0f < engine.Time () && m_inBuyZone && (g_timeRoundStart + Random.Float (10.0f, 20.0f) + mp_buytime.GetFloat () < engine.Time ()) && !g_bombPlanted && m_moneyAmount > g_botBuyEconomyTable[0]) - { + if (m_seeEnemyTime + 12.0f < engine.timebase () && m_lastEquipTime + 15.0f < engine.timebase () && m_inBuyZone && (g_timeRoundStart + rng.getFloat (10.0f, 20.0f) + mp_buytime.flt () < engine.timebase ()) && !g_bombPlanted && m_moneyAmount > g_botBuyEconomyTable[0]) { + m_ignoreBuyDelay = true; m_buyingFinished = false; m_buyState = buyState; // push buy message - PushMessageQueue (GAME_MSG_PURCHASE); + pushMsgQueue (GAME_MSG_PURCHASE); - m_nextBuyTime = engine.Time (); - m_lastEquipTime = engine.Time (); + m_nextBuyTime = engine.timebase (); + m_lastEquipTime = engine.timebase (); } } -bool Bot::IsBombDefusing (const Vector &bombOrigin) -{ +bool Bot::isBombDefusing (const Vector &bombOrigin) { // this function finds if somebody currently defusing the bomb. if (!g_bombPlanted) @@ -6039,29 +5829,29 @@ bool Bot::IsBombDefusing (const Vector &bombOrigin) bool defusingInProgress = false; - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot == nullptr || bot == this) + if (bot == nullptr || bot == this) { continue; // skip invalid bots + } - if (m_team != bot->m_team || bot->GetTaskId () == TASK_ESCAPEFROMBOMB) + if (m_team != bot->m_team || bot->taskId () == TASK_ESCAPEFROMBOMB) { continue; // skip other mess + } - if ((bot->pev->origin - bombOrigin).GetLength () < 140.0f && (bot->GetTaskId () == TASK_DEFUSEBOMB || bot->m_hasProgressBar)) - { + if ((bot->pev->origin - bombOrigin).length () < 140.0f && (bot->taskId () == TASK_DEFUSEBOMB || bot->m_hasProgressBar)) { defusingInProgress = true; break; } const Client &client = g_clients[i]; // take in account peoples too - if (defusingInProgress || !(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || IsValidBot (client.ent)) + if (defusingInProgress || !(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || isFakeClient (client.ent)) { continue; + } - if ((client.ent->v.origin - bombOrigin).GetLength () < 140.0f && ((client.ent->v.button | client.ent->v.oldbuttons) & IN_USE)) - { + if ((client.ent->v.origin - bombOrigin).length () < 140.0f && ((client.ent->v.button | client.ent->v.oldbuttons) & IN_USE)) { defusingInProgress = true; break; } @@ -6069,10 +5859,9 @@ bool Bot::IsBombDefusing (const Vector &bombOrigin) return defusingInProgress; } -float Bot::GetWalkSpeed (void) -{ - if (GetTaskId () == TASK_SEEKCOVER || (pev->flags & FL_DUCKING) || (pev->button & IN_DUCK) || (pev->oldbuttons & IN_DUCK) || (m_currentTravelFlags & PATHFLAG_JUMP) || (m_currentPath != nullptr && m_currentPath->flags & FLAG_LADDER) || IsOnLadder () || IsInWater ()) +float Bot::getShiftSpeed (void) { + if (taskId () == TASK_SEEKCOVER || (pev->flags & FL_DUCKING) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PATHFLAG_JUMP) || (m_currentPath != nullptr && m_currentPath->flags & FLAG_LADDER) || isOnLadder () || isInWater () || m_isStuck) { return pev->maxspeed; - + } return static_cast (pev->maxspeed * 0.4f); } diff --git a/source/chatlib.cpp b/source/chatlib.cpp index 4ba2a1c..c6c7d82 100644 --- a/source/chatlib.cpp +++ b/source/chatlib.cpp @@ -4,142 +4,141 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include ConVar yb_chat ("yb_chat", "1"); -void StripTags (char *buffer) -{ +void stripClanTags (char *buffer) { // this function strips 'clan' tags specified below in given string buffer - const char *tagOpen[] = {"-=", "-[", "-]", "-}", "-{", "<[", "<]", "[-", "]-", "{-", "}-", "[[", "[", "{", "]", "}", "<", ">", "-", "|", "=", "+", "(", ")"}; - const char *tagClose[] = {"=-", "]-", "[-", "{-", "}-", "]>", "[>", "-]", "-[", "-}", "-{", "]]", "]", "}", "[", "{", ">", "<", "-", "|", "=", "+", ")", "("}; + if (isEmptyStr (buffer)) { + return; + } + size_t nameLength = strlen (buffer); - int index, fieldStart, fieldStop, i; - int length = strlen (buffer); // get length of string + if (nameLength < 4) { + return; + } - // foreach known tag... - for (index = 0; index < ARRAYSIZE_HLSDK (tagOpen); index++) - { - fieldStart = strstr (buffer, tagOpen[index]) - buffer; // look for a tag start + constexpr const char *open[] = { "-=", "-[", "-]", "-}", "-{", "<[", "<]", "[-", "]-", "{-", "}-", "[[", "[", "{", "]", "}", "<", ">", "-", "|", "=", "+", "(", ")" }; + constexpr const char *close[] = { "=-", "]-", "[-", "{-", "}-", "]>", "[>", "-]", "-[", "-}", "-{", "]]", "]", "}", "[", "{", ">", "<", "-", "|", "=", "+", ")", "(" }; + + // for each known tag... + for (size_t i = 0; i < cr::arrsize (open); i++) { + size_t start = strstr (buffer, open[i]) - buffer; // look for a tag start // have we found a tag start? - if (fieldStart >= 0 && fieldStart < 32) - { - fieldStop = strstr (buffer, tagClose[index]) - buffer; // look for a tag stop + if (start < 32) { + size_t stop = strstr (buffer, close[i]) - buffer; // look for a tag stop // have we found a tag stop? - if (fieldStop > fieldStart && fieldStop < 32) - { - int tagLength = strlen (tagClose[index]); + if (stop > start && stop < 32) { + size_t tag = strlen (close[i]); + size_t j = start; - for (i = fieldStart; i < length - (fieldStop + tagLength - fieldStart); i++) - buffer[i] = buffer[i + (fieldStop + tagLength - fieldStart)]; // overwrite the buffer with the stripped string - - buffer[i] = 0x0; // terminate the string + for (; j < nameLength - (stop + tag - start); j++) { + buffer[j] = buffer[j + (stop + tag - start)]; // overwrite the buffer with the stripped string + } + buffer[j] = '\0'; // terminate the string } } } // have we stripped too much (all the stuff)? - if (buffer[0] != '\0') - { - String::TrimExternalBuffer (buffer); // if so, string is just a tag + if (isEmptyStr (buffer)) { + String::trimChars (buffer); + return; + } + String::trimChars (buffer); // if so, string is just a tag - int tagLength = 0; + // strip just the tag part... + for (size_t i = 0; i < cr::arrsize (open); i++) { + size_t start = strstr (buffer, open[i]) - buffer; // look for a tag start - // strip just the tag part... - for (index = 0; index < ARRAYSIZE_HLSDK (tagOpen); index++) - { - fieldStart = strstr (buffer, tagOpen[index]) - buffer; // look for a tag start + // have we found a tag start? + if (start < 32) { + size_t tag = strlen (open[i]); + size_t j = start; - // have we found a tag start? - if (fieldStart >= 0 && fieldStart < 32) - { - tagLength = strlen (tagOpen[index]); + for (; j < nameLength - tag; j++) { + buffer[j] = buffer[j + tag]; // overwrite the buffer with the stripped string + } + buffer[j] = '\0'; // terminate the string + start = strstr (buffer, close[i]) - buffer; // look for a tag stop - for (i = fieldStart; i < length - tagLength; i++) - buffer[i] = buffer[i + tagLength]; // overwrite the buffer with the stripped string + // have we found a tag stop ? + if (start < 32) { + tag = strlen (close[i]); + j = start; - buffer[i] = 0x0; // terminate the string - - fieldStart = strstr (buffer, tagClose[index]) - buffer; // look for a tag stop - - // have we found a tag stop ? - if (fieldStart >= 0 && fieldStart < 32) - { - tagLength = strlen (tagClose[index]); - - for (i = fieldStart; i < length - tagLength; i++) - buffer[i] = buffer[i + tagLength]; // overwrite the buffer with the stripped string - - buffer[i] = 0; // terminate the string + for (; j < nameLength - tag; j++) { + buffer[j] = buffer[j + tag]; // overwrite the buffer with the stripped string } + buffer[j] = 0; // terminate the string } } } - String::TrimExternalBuffer (buffer); // to finish, strip eventual blanks after and before the tag marks + String::trimChars (buffer); // to finish, strip eventual blanks after and before the tag marks } -char *HumanizeName (char *name) -{ +char *humanizeName (char *name) { // this function humanize player name (i.e. trim clan and switch to lower case (sometimes)) static char outputName[64]; // create return name buffer - strncpy (outputName, name, SIZEOF_CHAR (outputName)); // copy name to new buffer + strncpy (outputName, name, cr::bufsize (outputName)); // copy name to new buffer // drop tag marks, 80 percent of time - if (Random.Int (1, 100) < 80) - StripTags (outputName); - else - String::TrimExternalBuffer (outputName); + if (rng.getInt (1, 100) < 80) { + stripClanTags (outputName); + } + else { + String::trimChars (outputName); + } // sometimes switch name to lower characters // note: since we're using russian names written in english, we reduce this shit to 6 percent - if (Random.Int (1, 100) <= 6) - { - for (int i = 0; i < static_cast (strlen (outputName)); i++) + if (rng.getInt (1, 100) <= 6) { + for (size_t i = 0; i < strlen (outputName); i++) { outputName[i] = static_cast (tolower (static_cast (outputName[i]))); // to lower case + } } return &outputName[0]; // return terminated string } -void HumanizeChat (char *buffer) -{ +void addChatErrors (char *buffer) { // this function humanize chat string to be more handwritten - int length = strlen (buffer); // get length of string - int i = 0; + size_t length = strlen (buffer); // get length of string + size_t i = 0; // sometimes switch text to lowercase - // note: since we're using russian chat written in english, we reduce this shit to 4 percent - if (Random.Int (1, 100) <= 4) - { - for (i = 0; i < length; i++) - buffer[i] = static_cast (tolower (static_cast (buffer[i])));; // switch to lowercase + // note: since we're using russian chat written in English, we reduce this shit to 4 percent + if (rng.getInt (1, 100) <= 4) { + for (i = 0; i < length; i++) { + buffer[i] = static_cast (tolower (static_cast (buffer[i]))); // switch to lowercase + } } - if (length > 15) - { + if (length > 15) { + size_t percentile = length / 2; + // "length / 2" percent of time drop a character - if (Random.Int (1, 100) < (length / 2)) - { - int pos = Random.Int ((length / 8), length - (length / 8)); // chose random position in string + if (rng.getInt (1u, 100u) < percentile) { + size_t pos = rng.getInt (length / 8, length - length / 8); // chose random position in string - for (i = pos; i < length - 1; i++) + for (i = pos; i < length - 1; i++) { buffer[i] = buffer[i + 1]; // overwrite the buffer with stripped string - - buffer[i] = 0x0; // terminate string; + } + buffer[i] = '\0'; // terminate string; length--; // update new string length } // "length" / 4 precent of time swap character - if (Random.Int (1, 100) < (length / 4)) - { - int pos = Random.Int ((length / 8), ((3 * length) / 8)); // choose random position in string + if (rng.getInt (1u, 100u) < percentile / 2) { + size_t pos = rng.getInt (length / 8, 3 * length / 8); // choose random position in string char ch = buffer[pos]; // swap characters buffer[pos] = buffer[pos + 1]; @@ -149,304 +148,286 @@ void HumanizeChat (char *buffer) buffer[length] = 0; // terminate string } -void Bot::PrepareChatMessage (char *text) -{ +void Bot::prepareChatMessage (char *text) { // this function parses messages from the botchat, replaces keywords and converts names into a more human style - if (!yb_chat.GetBool () || IsNullString (text)) + if (!yb_chat.boolean () || isEmptyStr (text)) { return; - - #define ASSIGN_TALK_ENTITY() if (!engine.IsNullEntity (talkEntity)) strncat (m_tempStrings, HumanizeName (const_cast (STRING (talkEntity->v.netname))), SIZEOF_CHAR (m_tempStrings)) - - memset (&m_tempStrings, 0, sizeof (m_tempStrings)); + } + m_tempStrings.clear (); char *textStart = text; char *pattern = text; edict_t *talkEntity = nullptr; - while (pattern != nullptr) - { + auto getHumanizedName = [] (edict_t *ent) { + if (!engine.isNullEntity (ent)) { + return const_cast (humanizeName (const_cast (STRING (ent->v.netname)))); + } + return "unknown"; + }; + + while (pattern != nullptr) { // all replacement placeholders start with a % - pattern = strstr (textStart, "%"); + pattern = strchr (textStart, '%'); - if (pattern != nullptr) - { - int length = pattern - textStart; - - if (length > 0) - strncpy (m_tempStrings, textStart, length); + if (pattern != nullptr) { + size_t length = pattern - textStart; + if (length > 0) { + m_tempStrings = String (textStart, length); + } pattern++; // player with most frags? - if (*pattern == 'f') - { + if (*pattern == 'f') { int highestFrags = -9000; // just pick some start value int index = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || client.ent == ent ()) { continue; - + } int frags = static_cast (client.ent->v.frags); - if (frags > highestFrags) - { + if (frags > highestFrags) { highestFrags = frags; index = i; } } talkEntity = g_clients[index].ent; - - ASSIGN_TALK_ENTITY (); + m_tempStrings += getHumanizedName (talkEntity); } // mapname? - else if (*pattern == 'm') - strncat (m_tempStrings, engine.GetMapName (), SIZEOF_CHAR (m_tempStrings)); + else if (*pattern == 'm') { + m_tempStrings += engine.getMapName (); + } // roundtime? - else if (*pattern == 'r') - { - int time = static_cast (g_timeRoundEnd - engine.Time ()); - strncat (m_tempStrings, FormatBuffer ("%02d:%02d", time / 60, time % 60), SIZEOF_CHAR (m_tempStrings)); + else if (*pattern == 'r') { + int time = static_cast (g_timeRoundEnd - engine.timebase ()); + m_tempStrings.formatAppend ("%02d:%02d", time / 60, time % 60); } // chat reply? - else if (*pattern == 's') - { - talkEntity = engine.EntityOfIndex (m_sayTextBuffer.entityIndex); - ASSIGN_TALK_ENTITY (); + else if (*pattern == 's') { + talkEntity = engine.entityOfIndex (m_sayTextBuffer.entityIndex); + m_tempStrings += getHumanizedName (talkEntity); } // teammate alive? - else if (*pattern == 't') - { + else if (*pattern == 't') { int i; - for (i = 0; i < engine.MaxClients (); i++) - { + for (i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) { continue; - + } break; } - if (i < engine.MaxClients ()) - { - if (!engine.IsNullEntity (pev->dmg_inflictor) && m_team == engine.GetTeam (pev->dmg_inflictor)) + if (i < engine.maxClients ()) { + if (isPlayer (pev->dmg_inflictor) && m_team == engine.getTeam (pev->dmg_inflictor)) { talkEntity = pev->dmg_inflictor; - else + } + else { talkEntity = g_clients[i].ent; - - ASSIGN_TALK_ENTITY (); + } + m_tempStrings += getHumanizedName (talkEntity); } else // no teammates alive... { - for (i = 0; i < engine.MaxClients (); i++) - { + for (i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || client.team != m_team || client.ent == ent ()) { continue; - + } break; } - if (i < engine.MaxClients ()) - { + if (i < engine.maxClients ()) { talkEntity = g_clients[i].ent; - - ASSIGN_TALK_ENTITY (); + m_tempStrings += getHumanizedName (talkEntity); } } } - else if (*pattern == 'e') - { + else if (*pattern == 'e') { int i; - for (i = 0; i < engine.MaxClients (); i++) - { + for (i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team || client.ent == ent ()) { continue; - + } break; } - if (i < engine.MaxClients ()) - { + if (i < engine.maxClients ()) { talkEntity = g_clients[i].ent; - ASSIGN_TALK_ENTITY (); + m_tempStrings += getHumanizedName (talkEntity); } - else // no teammates alive... - { - for (i = 0; i < engine.MaxClients (); i++) - { + + // no teammates alive ? + else { + for (i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || client.team == m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || client.team == m_team || client.ent == ent ()) { continue; - + } break; } - if (i < engine.MaxClients ()) - { + if (i < engine.maxClients ()) { talkEntity = g_clients[i].ent; - ASSIGN_TALK_ENTITY (); + m_tempStrings += getHumanizedName (talkEntity); } } } - else if (*pattern == 'd') - { - if (g_gameFlags & GAME_CZERO) - { - if (Random.Int (1, 100) < 30) - strcat (m_tempStrings, "CZ"); - else - strcat (m_tempStrings, "Condition Zero"); + else if (*pattern == 'd') { + if (g_gameFlags & GAME_CZERO) { + if (rng.getInt (1, 100) < 30) { + m_tempStrings += "CZ"; + } + else { + m_tempStrings += "Condition Zero"; + } } - else if ((g_gameFlags & GAME_CSTRIKE16) || (g_gameFlags & GAME_LEGACY)) - { - if (Random.Int (1, 100) < 30) - strcat (m_tempStrings, "CS"); - else - strcat (m_tempStrings, "Counter-Strike"); + else if ((g_gameFlags & GAME_CSTRIKE16) || (g_gameFlags & GAME_LEGACY)) { + if (rng.getInt (1, 100) < 30) { + m_tempStrings += "CS"; + } + else { + m_tempStrings += "Counter-Strike"; + } } } - else if (*pattern == 'v') - { + else if (*pattern == 'v') { talkEntity = m_lastVictim; - ASSIGN_TALK_ENTITY (); + m_tempStrings += getHumanizedName (talkEntity); } pattern++; textStart = pattern; } } - if (textStart != nullptr) - { + if (textStart != nullptr) { // let the bots make some mistakes... char tempString[160]; - strncpy (tempString, textStart, SIZEOF_CHAR (tempString)); + strncpy (tempString, textStart, cr::bufsize (tempString)); - HumanizeChat (tempString); - strncat (m_tempStrings, tempString, SIZEOF_CHAR (m_tempStrings)); + addChatErrors (tempString); + m_tempStrings += tempString; } } -bool CheckKeywords (char *tempMessage, char *reply) -{ - // this function checks is string contain keyword, and generates relpy to it +bool checkForKeywords (char *message, char *reply) { + // this function checks is string contain keyword, and generates reply to it - if (!yb_chat.GetBool () || IsNullString (tempMessage)) + if (!yb_chat.boolean () || isEmptyStr (message)) { return false; + } + + for (auto &factory : g_replyFactory) { + for (auto &keyword : factory.keywords) { - FOR_EACH_AE (g_replyFactory, i) - { - FOR_EACH_AE (g_replyFactory[i].keywords, j) - { // check is keyword has occurred in message - if (strstr (tempMessage, g_replyFactory[i].keywords[j].GetBuffer ()) != nullptr) - { - Array &replies = g_replyFactory[i].usedReplies; + if (strstr (message, keyword.chars ()) != nullptr) { + StringArray &replies = factory.usedReplies; - if (replies.GetElementNumber () >= g_replyFactory[i].replies.GetElementNumber () / 2) - replies.RemoveAll (); - - bool replyUsed = false; - const char *generatedReply = g_replyFactory[i].replies.GetRandomElement (); - - // don't say this twice - FOR_EACH_AE (replies, k) - { - if (strstr (replies[k].GetBuffer (), generatedReply) != nullptr) - replyUsed = true; + if (replies.length () >= factory.replies.length () / 2) { + replies.clear (); } + else if (!replies.empty ()) { + bool replyUsed = false; + const String &choosenReply = factory.replies.random (); - // reply not used, so use it - if (!replyUsed) - { - strcpy (reply, generatedReply); // update final buffer - replies.Push (generatedReply); //add to ignore list + // don't say this twice + for (auto &used : replies) { + if (used.contains (choosenReply)) { + replyUsed = true; + break; + } + } - return true; + // reply not used, so use it + if (!replyUsed) { + strcpy (reply, choosenReply.chars ()); // update final buffer + replies.push (choosenReply); // add to ignore list + + return true; + } } } } } // didn't find a keyword? 70% of the time use some universal reply - if (Random.Int (1, 100) < 70 && !g_chatFactory[CHAT_NOKW].IsEmpty ()) - { - strcpy (reply, g_chatFactory[CHAT_NOKW].GetRandomElement ().GetBuffer ()); + if (rng.getInt (1, 100) < 70 && !g_chatFactory[CHAT_NOKW].empty ()) { + strcpy (reply, g_chatFactory[CHAT_NOKW].random ().chars ()); return true; } return false; } -bool Bot::ParseChat (char *reply) -{ +bool Bot::processChatKeywords (char *reply) { // this function parse chat buffer, and prepare buffer to keyword searching char tempMessage[512]; - strcpy (tempMessage, m_sayTextBuffer.sayText); // copy to safe place + size_t maxLength = cr::bufsize (tempMessage); + + strncpy (tempMessage, m_sayTextBuffer.sayText.chars (), maxLength); // copy to safe place // text to uppercase for keyword parsing - for (int i = 0; i < static_cast (strlen (tempMessage)); i++) - tempMessage[i] = static_cast (tolower (static_cast (tempMessage[i]))); - - return CheckKeywords (tempMessage, reply); + for (size_t i = 0; i < maxLength; i++) { + tempMessage[i] = static_cast (toupper (static_cast (tempMessage[i]))); + } + return checkForKeywords (tempMessage, reply); } -bool Bot::RepliesToPlayer (void) -{ +bool Bot::isReplyingToChat (void) { // this function sends reply to a player - if (m_sayTextBuffer.entityIndex != -1 && !IsNullString (m_sayTextBuffer.sayText)) - { + if (m_sayTextBuffer.entityIndex != -1 && !m_sayTextBuffer.sayText.empty ()) { char text[256]; // check is time to chat is good - if (m_sayTextBuffer.timeNextChat < engine.Time ()) - { - if (Random.Int (1, 100) < m_sayTextBuffer.chatProbability + Random.Int (2, 10) && ParseChat (reinterpret_cast (&text))) - { - PrepareChatMessage (text); - PushMessageQueue (GAME_MSG_SAY_CMD); + if (m_sayTextBuffer.timeNextChat < engine.timebase ()) { + if (rng.getInt (1, 100) < m_sayTextBuffer.chatProbability + rng.getInt (15, 35) && processChatKeywords (reinterpret_cast (&text))) { + prepareChatMessage (text); + pushMsgQueue (GAME_MSG_SAY_CMD); + m_sayTextBuffer.entityIndex = -1; - m_sayTextBuffer.sayText[0] = 0x0; - m_sayTextBuffer.timeNextChat = engine.Time () + m_sayTextBuffer.chatDelay; + m_sayTextBuffer.timeNextChat = engine.timebase () + m_sayTextBuffer.chatDelay; + m_sayTextBuffer.sayText.clear (); return true; } m_sayTextBuffer.entityIndex = -1; - m_sayTextBuffer.sayText[0] = 0x0; + m_sayTextBuffer.sayText.clear (); } } return false; } -void Bot::SayText (const char *text) -{ +void Bot::say (const char *text) { // this function prints saytext message to all players - if (IsNullString (text)) + if (isEmptyStr (text)) { return; - - engine.IssueBotCommand (GetEntity (), "say \"%s\"", text); + } + engine.execBotCmd (ent (), "say \"%s\"", text); } -void Bot::TeamSayText (const char *text) -{ +void Bot::sayTeam (const char *text) { // this function prints saytext message only for teammates - if (IsNullString (text)) + if (isEmptyStr (text)) { return; - - engine.IssueBotCommand (GetEntity (), "say_team \"%s\"", text); + } + engine.execBotCmd (ent (), "say_team \"%s\"", text); } diff --git a/source/combat.cpp b/source/combat.cpp index 8fca03e..074bcee 100644 --- a/source/combat.cpp +++ b/source/combat.cpp @@ -1,13 +1,13 @@ -// +// // Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd"). // Copyright (c) YaPB Development Team. // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include ConVar yb_shoots_thru_walls ("yb_shoots_thru_walls", "2"); ConVar yb_ignore_enemies ("yb_ignore_enemies", "0"); @@ -15,140 +15,130 @@ ConVar yb_check_enemy_rendering ("yb_check_enemy_rendering", "0"); ConVar mp_friendlyfire ("mp_friendlyfire", nullptr, VT_NOREGISTER); -int Bot::GetNearbyFriendsNearPosition(const Vector &origin, float radius) -{ +int Bot::numFriendsNear (const Vector &origin, float radius) { int count = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) { continue; + } - if ((client.origin - origin).GetLengthSquared () < GET_SQUARE (radius)) + if ((client.origin - origin).lengthSq () < cr::square (radius)) { count++; + } } return count; } -int Bot::GetNearbyEnemiesNearPosition(const Vector &origin, float radius) -{ +int Bot::numEnemiesNear (const Vector &origin, float radius) { int count = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team) { continue; + } - if ((client.origin - origin).GetLengthSquared () < GET_SQUARE (radius)) + if ((client.origin - origin).lengthSq () < cr::square (radius)) { count++; + } } return count; } -bool Bot::IsEnemyHiddenByRendering (edict_t *enemy) -{ - if (!yb_check_enemy_rendering.GetBool () || engine.IsNullEntity (enemy)) +bool Bot::isEnemyHidden (edict_t *enemy) { + if (!yb_check_enemy_rendering.boolean () || engine.isNullEntity (enemy)) { return false; - + } entvars_t &v = enemy->v; bool enemyHasGun = (v.weapons & WEAPON_PRIMARY) || (v.weapons & WEAPON_SECONDARY); bool enemyGunfire = (v.button & IN_ATTACK) || (v.oldbuttons & IN_ATTACK); - if ((v.renderfx == kRenderFxExplode || (v.effects & EF_NODRAW)) && (!enemyGunfire || !enemyHasGun)) + if ((v.renderfx == kRenderFxExplode || (v.effects & EF_NODRAW)) && (!enemyGunfire || !enemyHasGun)) { return true; + } - if ((v.renderfx == kRenderFxExplode || (v.effects & EF_NODRAW)) && enemyGunfire && enemyHasGun) + if ((v.renderfx == kRenderFxExplode || (v.effects & EF_NODRAW)) && enemyGunfire && enemyHasGun) { return false; + } - if (v.renderfx != kRenderFxHologram && v.renderfx != kRenderFxExplode && v.rendermode != kRenderNormal) - { - if (v.renderfx == kRenderFxGlowShell) - { - if (v.renderamt <= 20.0f && v.rendercolor.x <= 20.0f && v.rendercolor.y <= 20.0f && v.rendercolor.z <= 20.0f) - { - if (!enemyGunfire || !enemyHasGun) + if (v.renderfx != kRenderFxHologram && v.renderfx != kRenderFxExplode && v.rendermode != kRenderNormal) { + if (v.renderfx == kRenderFxGlowShell) { + if (v.renderamt <= 20.0f && v.rendercolor.x <= 20.0f && v.rendercolor.y <= 20.0f && v.rendercolor.z <= 20.0f) { + if (!enemyGunfire || !enemyHasGun) { return true; - + } return false; } - else if (!enemyGunfire && v.renderamt <= 60.0f && v.rendercolor.x <= 60.f && v.rendercolor.y <= 60.0f && v.rendercolor.z <= 60.0f) + else if (!enemyGunfire && v.renderamt <= 60.0f && v.rendercolor.x <= 60.f && v.rendercolor.y <= 60.0f && v.rendercolor.z <= 60.0f) { return true; + } } - else if (v.renderamt <= 20.0f) - { - if (!enemyGunfire || !enemyHasGun) + else if (v.renderamt <= 20.0f) { + if (!enemyGunfire || !enemyHasGun) { return true; - + } return false; } - else if (!enemyGunfire && v.renderamt <= 60.0f) + else if (!enemyGunfire && v.renderamt <= 60.0f) { return true; + } } return false; } -bool Bot::CheckVisibility (edict_t *target, Vector *origin, uint8 *bodyPart) -{ +bool Bot::checkBodyParts (edict_t *target, Vector *origin, uint8 *bodyPart) { // this function checks visibility of a bot target. - if (IsEnemyHiddenByRendering (target)) - { + if (isEnemyHidden (target)) { *bodyPart = 0; - origin->Zero (); + origin->nullify (); return false; } TraceResult result; - Vector eyes = EyePosition (); + Vector eyes = eyePos (); Vector spot = target->v.origin; - + *bodyPart = 0; - engine.TestLine (eyes, spot, TRACE_IGNORE_EVERYTHING, pev->pContainingEntity, &result); + engine.testLine (eyes, spot, TRACE_IGNORE_EVERYTHING, ent (), &result); - if (result.flFraction >= 1.0f) - { + if (result.flFraction >= 1.0f) { *bodyPart |= VISIBLE_BODY; *origin = result.vecEndPos; - - if (m_difficulty > 3) - origin->z += 3.0f; } // check top of head - spot = spot + Vector (0, 0, 25.0f); - engine.TestLine (eyes, spot, TRACE_IGNORE_EVERYTHING, pev->pContainingEntity, &result); + spot.z += 25.0f; + engine.testLine (eyes, spot, TRACE_IGNORE_EVERYTHING, ent (), &result); - if (result.flFraction >= 1.0f) - { + if (result.flFraction >= 1.0f) { *bodyPart |= VISIBLE_HEAD; *origin = result.vecEndPos; - - if (m_difficulty > 3) - origin->z += 1.0f; } - if (*bodyPart != 0) + if (*bodyPart != 0) { return true; + } const float standFeet = 34.0f; const float crouchFeet = 14.0f; - if (target->v.flags & FL_DUCKING) + if (target->v.flags & FL_DUCKING) { spot.z = target->v.origin.z - crouchFeet; - else + } + else { spot.z = target->v.origin.z - standFeet; + } + engine.testLine (eyes, spot, TRACE_IGNORE_EVERYTHING, ent (), &result); - engine.TestLine (eyes, spot, TRACE_IGNORE_EVERYTHING, pev->pContainingEntity, &result); - - if (result.flFraction >= 1.0f) - { + if (result.flFraction >= 1.0f) { *bodyPart |= VISIBLE_OTHER; *origin = result.vecEndPos; @@ -156,15 +146,14 @@ bool Bot::CheckVisibility (edict_t *target, Vector *origin, uint8 *bodyPart) } const float edgeOffset = 13.0f; - Vector dir = (target->v.origin - pev->origin).Normalize2D (); + Vector dir = (target->v.origin - pev->origin).normalize2D (); Vector perp (-dir.y, dir.x, 0.0f); spot = target->v.origin + Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0); - engine.TestLine (eyes, spot, TRACE_IGNORE_EVERYTHING, pev->pContainingEntity, &result); + engine.testLine (eyes, spot, TRACE_IGNORE_EVERYTHING, ent (), &result); - if (result.flFraction >= 1.0f) - { + if (result.flFraction >= 1.0f) { *bodyPart |= VISIBLE_OTHER; *origin = result.vecEndPos; @@ -172,10 +161,9 @@ bool Bot::CheckVisibility (edict_t *target, Vector *origin, uint8 *bodyPart) } spot = target->v.origin - Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0); - engine.TestLine (eyes, spot, TRACE_IGNORE_EVERYTHING, pev->pContainingEntity, &result); + engine.testLine (eyes, spot, TRACE_IGNORE_EVERYTHING, ent (), &result); - if (result.flFraction >= 1.0f) - { + if (result.flFraction >= 1.0f) { *bodyPart |= VISIBLE_OTHER; *origin = result.vecEndPos; @@ -184,119 +172,109 @@ bool Bot::CheckVisibility (edict_t *target, Vector *origin, uint8 *bodyPart) return false; } -bool Bot::IsEnemyViewable (edict_t *player) -{ - if (engine.IsNullEntity (player)) +bool Bot::seesEnemy (edict_t *player, bool ignoreFOV) { + if (engine.isNullEntity (player)) { return false; + } - bool forceTrueIfVisible = false; + if (isPlayer (pev->dmg_inflictor) && engine.getTeam (pev->dmg_inflictor) != m_team) { + ignoreFOV = true; + } - if (IsValidPlayer (pev->dmg_inflictor) && engine.GetTeam (pev->dmg_inflictor) != m_team) - forceTrueIfVisible = true; - - if ((IsInViewCone (player->v.origin + pev->view_ofs) || forceTrueIfVisible) && CheckVisibility (player, &m_enemyOrigin, &m_visibility)) - { - m_seeEnemyTime = engine.Time (); + if ((ignoreFOV || isInViewCone (player->v.origin)) && checkBodyParts (player, &m_enemyOrigin, &m_visibility)) { + m_seeEnemyTime = engine.timebase (); m_lastEnemy = player; - m_lastEnemyOrigin = player->v.origin; + m_lastEnemyOrigin = m_enemyOrigin; return true; } return false; } -bool Bot::LookupEnemy (void) -{ +bool Bot::lookupEnemies (void) { // this function tries to find the best suitable enemy for the bot // do not search for enemies while we're blinded, or shooting disabled by user - if (m_enemyIgnoreTimer > engine.Time () || m_blindTime > engine.Time () || yb_ignore_enemies.GetBool ()) + if (m_enemyIgnoreTimer > engine.timebase () || m_blindTime > engine.timebase () || yb_ignore_enemies.boolean ()) { return false; - + } edict_t *player, *newEnemy = nullptr; - - float nearestDistance = m_viewDistance; + float nearestDistance = cr::square (m_viewDistance); // clear suspected flag - if (m_seeEnemyTime + 3.0f < engine.Time ()) + if (!engine.isNullEntity (m_enemy) && (m_states & STATE_SEEING_ENEMY)) { m_states &= ~STATE_SUSPECT_ENEMY; + } + else if (engine.isNullEntity (m_enemy) && m_seeEnemyTime + 1.0f > engine.timebase () && isAlive (m_lastEnemy)) { + m_states |= STATE_SUSPECT_ENEMY; + m_aimFlags |= AIM_LAST_ENEMY; + } + m_visibility = 0; + m_enemyOrigin.nullify (); - if (!engine.IsNullEntity (m_enemy) && m_enemyUpdateTime > engine.Time ()) - { + if (!engine.isNullEntity (m_enemy)) { player = m_enemy; // is player is alive - if (IsAlive (player) && IsEnemyViewable (player)) + if (m_enemyUpdateTime > engine.timebase () && (m_enemy->v.origin - pev->origin).lengthSq () < nearestDistance && isAlive (player) && seesEnemy (player)) { newEnemy = player; + } } // the old enemy is no longer visible or - if (engine.IsNullEntity (newEnemy)) - { - m_enemyUpdateTime = engine.Time () + 0.5f; + if (engine.isNullEntity (newEnemy)) { // ignore shielded enemies, while we have real one edict_t *shieldEnemy = nullptr; // search the world for players... - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team || client.ent == ent ()) { continue; - + } player = client.ent; - // do some blind by smoke grenade - if (m_blindRecognizeTime < engine.Time () && IsBehindSmokeClouds (player)) - { - m_blindRecognizeTime = engine.Time () + Random.Float (1.0f, 2.0f); - - if (Random.Int (0, 100) < 50) - ChatterMessage (Chatter_BehindSmoke); + if ((player->v.button & (IN_ATTACK | IN_ATTACK2)) && m_viewDistance < m_maxViewDistance) { + nearestDistance = cr::square (m_maxViewDistance); } - if (player->v.button & (IN_ATTACK | IN_ATTACK2)) - m_blindRecognizeTime = engine.Time () - 0.1f; - // see if bot can see the player... - if (m_blindRecognizeTime < engine.Time () && IsEnemyViewable (player)) - { - if (IsEnemyProtectedByShield (player)) - { + if (seesEnemy (player)) { + if (isEnemyBehindShield (player)) { shieldEnemy = player; continue; } - float distance = (player->v.origin - pev->origin).GetLength (); + float distance = (player->v.origin - pev->origin).lengthSq (); - if (distance < nearestDistance) - { + if (distance * 0.7f < nearestDistance) { nearestDistance = distance; newEnemy = player; // aim VIP first on AS maps... - if (IsPlayerVIP (newEnemy)) + if (isPlayerVIP (newEnemy)) { break; + } } } } + m_enemyUpdateTime = engine.timebase () + calcThinkInterval () * 30.0f; - if (engine.IsNullEntity (newEnemy) && !engine.IsNullEntity (shieldEnemy)) + if (engine.isNullEntity (newEnemy) && !engine.isNullEntity (shieldEnemy)) { newEnemy = shieldEnemy; + } } - if (IsValidPlayer (newEnemy)) - { + if (isPlayer (newEnemy)) { g_botsCanPause = true; m_aimFlags |= AIM_ENEMY; m_states |= STATE_SEEING_ENEMY; - if (newEnemy == m_enemy) - { + if (newEnemy == m_enemy) { // if enemy is still visible and in field of view, keep it keep track of when we last saw an enemy - m_seeEnemyTime = engine.Time (); + m_seeEnemyTime = engine.timebase (); // zero out reaction time m_actualReactionTime = 0.0f; @@ -305,17 +283,23 @@ bool Bot::LookupEnemy (void) return true; } - else - { - if (m_seeEnemyTime + 3.0 < engine.Time () && (m_hasC4 || HasHostage () || !engine.IsNullEntity (m_targetEntity))) - RadioMessage (Radio_EnemySpotted); - + else { + if (m_seeEnemyTime + 3.0 < engine.timebase () && (m_hasC4 || hasHostage () || !engine.isNullEntity (m_targetEntity))) { + pushRadioMessage (RADIO_ENEMY_SPOTTED); + } m_targetEntity = nullptr; // stop following when we see an enemy... - if (Random.Int (0, 100) < m_difficulty * 25) - m_enemySurpriseTime = engine.Time () + m_actualReactionTime * 0.5f; - else - m_enemySurpriseTime = engine.Time () + m_actualReactionTime; + if (rng.getInt (0, 100) < m_difficulty * 25) { + m_enemySurpriseTime = m_actualReactionTime * 0.5f; + } + else { + m_enemySurpriseTime = m_actualReactionTime; + } + + if (usesSniper ()) { + m_enemySurpriseTime *= 0.5f; + } + m_enemySurpriseTime += engine.timebase (); // zero out reaction time m_actualReactionTime = 0.0f; @@ -325,26 +309,25 @@ bool Bot::LookupEnemy (void) m_enemyReachableTimer = 0.0f; // keep track of when we last saw an enemy - m_seeEnemyTime = engine.Time (); + m_seeEnemyTime = engine.timebase (); - if (!(pev->oldbuttons & IN_ATTACK)) + if (!(m_oldButtons & IN_ATTACK)) { return true; + } // now alarm all teammates who see this bot & don't have an actual enemy of the bots enemy should simulate human players seeing a teammate firing - for (int j = 0; j < engine.MaxClients (); j++) - { + for (int j = 0; j < engine.maxClients (); j++) { const Client &client = g_clients[j]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) { continue; + } + Bot *other = bots.getBot (client.ent); - Bot *other = bots.GetBot (client.ent); - - if (other != nullptr && other->m_seeEnemyTime + 2.0f < engine.Time () && engine.IsNullEntity (other->m_lastEnemy) && IsVisible (pev->origin, other->GetEntity ()) && other->IsInViewCone (pev->origin)) - { + if (other != nullptr && other->m_seeEnemyTime + 2.0f < engine.timebase () && engine.isNullEntity (other->m_lastEnemy) && isVisible (pev->origin, other->ent ()) && other->isInViewCone (pev->origin)) { other->m_lastEnemy = newEnemy; other->m_lastEnemyOrigin = m_lastEnemyOrigin; - other->m_seeEnemyTime = engine.Time (); + other->m_seeEnemyTime = engine.timebase (); other->m_states |= (STATE_SUSPECT_ENEMY | STATE_HEARING_ENEMY); other->m_aimFlags |= AIM_LAST_ENEMY; } @@ -352,21 +335,17 @@ bool Bot::LookupEnemy (void) return true; } } - else if (!engine.IsNullEntity (m_enemy)) - { + else if (!engine.isNullEntity (m_enemy)) { newEnemy = m_enemy; m_lastEnemy = newEnemy; - if (!IsAlive (newEnemy)) - { + if (!isAlive (newEnemy)) { m_enemy = nullptr; - + // shoot at dying players if no new enemy to give some more human-like illusion - if (m_seeEnemyTime + 0.1f > engine.Time ()) - { - if (!UsesSniper ()) - { - m_shootAtDeadTime = engine.Time () + 0.4f; + if (m_seeEnemyTime + 0.3f > engine.timebase ()) { + if (!usesSniper ()) { + m_shootAtDeadTime = engine.timebase () + 0.4f; m_actualReactionTime = 0.0f; m_states |= STATE_SUSPECT_ENEMY; @@ -374,8 +353,7 @@ bool Bot::LookupEnemy (void) } return false; } - else if (m_shootAtDeadTime > engine.Time ()) - { + else if (m_shootAtDeadTime > engine.timebase ()) { m_actualReactionTime = 0.0f; m_states |= STATE_SUSPECT_ENEMY; @@ -385,9 +363,8 @@ bool Bot::LookupEnemy (void) } // if no enemy visible check if last one shoot able through wall - if (yb_shoots_thru_walls.GetBool () && m_difficulty >= 2 && IsShootableThruObstacle (newEnemy->v.origin)) - { - m_seeEnemyTime = engine.Time (); + if (yb_shoots_thru_walls.boolean () && m_difficulty >= 2 && isPenetrableObstacle (newEnemy->v.origin)) { + m_seeEnemyTime = engine.timebase (); m_states |= STATE_SUSPECT_ENEMY; m_aimFlags |= AIM_LAST_ENEMY; @@ -401,230 +378,223 @@ bool Bot::LookupEnemy (void) } // check if bots should reload... - if ((m_aimFlags <= AIM_PREDICT_PATH && m_seeEnemyTime + 3.0f < engine.Time () && !(m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) && engine.IsNullEntity (m_lastEnemy) && engine.IsNullEntity (m_enemy) && GetTaskId () != TASK_SHOOTBREAKABLE && GetTaskId () != TASK_PLANTBOMB && GetTaskId () != TASK_DEFUSEBOMB) || g_roundEnded) - { - if (!m_reloadState) + if ((m_aimFlags <= AIM_PREDICT_PATH && m_seeEnemyTime + 3.0f < engine.timebase () && !(m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) && engine.isNullEntity (m_lastEnemy) && engine.isNullEntity (m_enemy) && taskId () != TASK_SHOOTBREAKABLE && taskId () != TASK_PLANTBOMB && taskId () != TASK_DEFUSEBOMB) || g_roundEnded) { + if (!m_reloadState) { m_reloadState = RELOAD_PRIMARY; + } } // is the bot using a sniper rifle or a zoomable rifle? - if ((UsesSniper () || UsesZoomableRifle ()) && m_zoomCheckTime + 1.0f < engine.Time ()) - { - if (pev->fov < 90.0f) // let the bot zoom out + if ((usesSniper () || usesZoomableRifle ()) && m_zoomCheckTime + 1.0f < engine.timebase ()) { + if (pev->fov < 90.0f) { pev->button |= IN_ATTACK2; - else + } + else { m_zoomCheckTime = 0.0f; + } } return false; } -const Vector &Bot::GetAimPosition (void) -{ +Vector Bot::getBodyOffserError (float distance) { + if (engine.isNullEntity (m_enemy)) { + return Vector::null (); + } + + if (m_aimErrorTime < engine.timebase ()) { + const float error = distance / (cr::clamp (m_difficulty, 1, 4) * 1000.0f); + Vector &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins; + + m_aimLastError = Vector (rng.getFloat (mins.x * error, maxs.x * error), rng.getFloat (mins.y * error, maxs.y * error), rng.getFloat (mins.z * error, maxs.z * error)); + m_aimErrorTime = engine.timebase () + rng.getFloat (0.5f, 1.0f); + } + return m_aimLastError; +} + +const Vector &Bot::getEnemyBodyOffset (void) { // the purpose of this function, is to make bot aiming not so ideal. it's mutate m_enemyOrigin enemy vector // returned from visibility check function. - float distance = (m_enemy->v.origin - pev->origin).GetLength (); + auto headOffset = [] (edict_t *e) { + return e->v.absmin.z + e->v.size.z * 0.81f; + }; - // get enemy position initially - Vector targetOrigin = m_enemy->v.origin; - Vector randomize; - - const Vector &adjust = Vector (Random.Float (m_enemy->v.mins.x * 0.5f, m_enemy->v.maxs.x * 0.5f), Random.Float (m_enemy->v.mins.y * 0.5f, m_enemy->v.maxs.y * 0.5f), Random.Float (m_enemy->v.mins.z * 0.5f, m_enemy->v.maxs.z * 0.5f)); + // if no visibility data, use last one + if (!m_visibility) { + return m_enemyOrigin; + } + float distance = (m_enemy->v.origin - pev->origin).length (); // do not aim at head, at long distance (only if not using sniper weapon) - if ((m_visibility & VISIBLE_BODY) && !UsesSniper () && !UsesPistol () && (distance > (m_difficulty == 4 ? 2400.0 : 1200.0))) + if ((m_visibility & VISIBLE_BODY) && !usesSniper () && distance > (m_difficulty > 2 ? 2000.0f : 1000.0f)) { m_visibility &= ~VISIBLE_HEAD; + } + Vector aimPos = m_enemy->v.origin; + + if (m_difficulty > 2 && !(m_visibility & VISIBLE_OTHER)) { + aimPos = (m_enemy->v.velocity - pev->velocity) * calcThinkInterval () + aimPos; + } // if we only suspect an enemy behind a wall take the worst skill - if ((m_states & STATE_SUSPECT_ENEMY) && !(m_states & STATE_SEEING_ENEMY)) - targetOrigin = targetOrigin + adjust; - else - { + if (!m_visibility && (m_states & STATE_SUSPECT_ENEMY)) { + aimPos += getBodyOffserError (distance); + } + else { // now take in account different parts of enemy body - if (m_visibility & (VISIBLE_HEAD | VISIBLE_BODY)) // visible head & body - { + if (m_visibility & (VISIBLE_HEAD | VISIBLE_BODY)) { int headshotFreq[5] = { 20, 40, 60, 80, 100 }; // now check is our skill match to aim at head, else aim at enemy body - if ((Random.Int (1, 100) < headshotFreq[m_difficulty]) || UsesPistol ()) - targetOrigin = targetOrigin + m_enemy->v.view_ofs + Vector (0.0f, 0.0f, GetZOffset (distance)); - else - targetOrigin = targetOrigin + Vector (0.0f, 0.0f, GetZOffset (distance)); + if ((rng.getInt (1, 100) < headshotFreq[m_difficulty]) || usesPistol ()) { + aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance); + } + else { + aimPos.z += getEnemyBodyOffsetCorrection (distance); + } } - else if (m_visibility & VISIBLE_BODY) // visible only body - targetOrigin = targetOrigin + Vector (0.0f, 0.0f, GetZOffset (distance)); - else if (m_visibility & VISIBLE_OTHER) // random part of body is visible - targetOrigin = m_enemyOrigin; - else if (m_visibility & VISIBLE_HEAD) // visible only head - targetOrigin = targetOrigin + m_enemy->v.view_ofs + Vector (0.0f, 0.0f, GetZOffset (distance)); - else // something goes wrong, use last enemy origin - { - targetOrigin = m_lastEnemyOrigin; - - if (m_difficulty < 3) - randomize = adjust; + else if (m_visibility & VISIBLE_BODY) { + aimPos.z += getEnemyBodyOffsetCorrection (distance); + } + else if (m_visibility & VISIBLE_OTHER) { + aimPos = m_enemyOrigin; + } + else if (m_visibility & VISIBLE_HEAD) { + aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance); } - m_lastEnemyOrigin = targetOrigin; } - const Vector &velocity = UsesSniper () ? Vector::GetZero () : 1.0f * GetThinkInterval () * (m_enemy->v.velocity - pev->velocity); - if (m_difficulty < 3 && !randomize.IsZero ()) - { - float divOffs = (m_enemyOrigin - pev->origin).GetLength (); + m_enemyOrigin = aimPos; + m_lastEnemyOrigin = aimPos; - if (pev->fov < 40) - divOffs = divOffs / 2000; - else if (pev->fov < 90) - divOffs = divOffs / 1000; - else - divOffs = divOffs / 500; - - // randomize the target position - m_enemyOrigin = divOffs * randomize; + // add some error to unskilled bots + if (m_difficulty < 3) { + m_enemyOrigin += getBodyOffserError (distance); } - else - m_enemyOrigin = targetOrigin; - - if (distance >= 256.0f && m_difficulty < 4) - m_enemyOrigin += velocity; - return m_enemyOrigin; } -float Bot::GetZOffset (float distance) -{ - if (m_difficulty < 3) - return 0.0f; +float Bot::getEnemyBodyOffsetCorrection (float distance) { + bool sniper = usesSniper (); + bool pistol = usesPistol (); + bool rifle = usesRifle (); - bool sniper = UsesSniper (); - bool pistol = UsesPistol (); - bool rifle = UsesRifle (); - - bool zoomableRifle = UsesZoomableRifle (); - bool submachine = UsesSubmachineGun (); + bool zoomableRifle = usesZoomableRifle (); + bool submachine = usesSubmachine (); bool shotgun = (m_currentWeapon == WEAPON_XM1014 || m_currentWeapon == WEAPON_M3); bool m249 = m_currentWeapon == WEAPON_M249; - float result = 3.5f; + float result = -2.0f; - if (distance < 2800.0f && distance > MAX_SPRAY_DISTANCE_X2) - { - if (sniper) result = 1.5f; - else if (zoomableRifle) result = 4.5f; - else if (pistol) result = 6.5f; - else if (submachine) result = 5.5f; - else if (rifle) result = 5.5f; - else if (m249) result = 2.5f; - else if (shotgun) result = 10.5f; + if (distance < MAX_SPRAY_DISTANCE) { + return -9.0f; } - else if (distance > MAX_SPRAY_DISTANCE && distance <= MAX_SPRAY_DISTANCE_X2) - { - if (sniper) result = 2.5f; - else if (zoomableRifle) result = 3.5f; - else if (pistol) result = 6.5f; - else if (submachine) result = 3.5f; - else if (rifle) result = 1.6f; - else if (m249) result = -1.0f; - else if (shotgun) result = 10.0f; - } - else if (distance < MAX_SPRAY_DISTANCE) - { - if (sniper) result = 4.5f; - else if (zoomableRifle) result = -5.0f; - else if (pistol) result = 4.5f; - else if (submachine) result = -4.5f; - else if (rifle) result = -4.5f; - else if (m249) result = -6.0f; - else if (shotgun) result = -5.0f; + else if (distance >= MAX_SPRAY_DISTANCE_X2) { + if (sniper) { + result = 0.18f; + } + else if (zoomableRifle) { + result = 1.5f; + } + else if (pistol) { + result = 2.5f; + } + else if (submachine) { + result = 1.5f; + } + else if (rifle) { + result = 1.0f; + } + else if (m249) { + result = -5.5f; + } + else if (shotgun) { + result = -4.5f; + } } return result; } -bool Bot::IsFriendInLineOfFire (float distance) -{ +bool Bot::isFriendInLineOfFire (float distance) { // bot can't hurt teammates, if friendly fire is not enabled... - if (!mp_friendlyfire.GetBool () || (g_gameFlags & GAME_CSDM)) + if (!mp_friendlyfire.boolean () || (g_gameFlags & GAME_CSDM)) { return false; - - MakeVectors (pev->v_angle); + } + makeVectors (pev->v_angle); TraceResult tr; - engine.TestLine (EyePosition (), EyePosition () + 10000.0f * pev->v_angle, TRACE_IGNORE_NONE, GetEntity (), &tr); + engine.testLine (eyePos (), eyePos () + distance * pev->v_angle, TRACE_IGNORE_NONE, ent (), &tr); // check if we hit something - if (!engine.IsNullEntity (tr.pHit)) - { - int playerIndex = engine.IndexOfEntity (tr.pHit) - 1; + if (isPlayer (tr.pHit) && tr.pHit != ent ()) { + auto hit = tr.pHit; // check valid range - if (playerIndex >= 0 && playerIndex < engine.MaxClients () && g_clients[playerIndex].team == m_team && (g_clients[playerIndex].flags & CF_ALIVE)) + if (isAlive (hit) && engine.getTeam (hit) == m_team) { return true; + } } // search the world for players - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) { continue; + } + edict_t *pent = client.ent; - edict_t *ent = client.ent; + float friendDistance = (pent->v.origin - pev->origin).length (); + float squareDistance = cr::sqrtf (1089.0f + cr::square (friendDistance)); - float friendDistance = (ent->v.origin - pev->origin).GetLength (); - float squareDistance = A_sqrtf (1089.0f + (friendDistance * friendDistance)); - - if (GetShootingConeDeviation (GetEntity (), &ent->v.origin) > (friendDistance * friendDistance) / (squareDistance * squareDistance) && friendDistance <= distance) + if (friendDistance <= distance && getShootingConeDeviation (ent (), pent->v.origin) > cr::square (friendDistance) / cr::square (squareDistance)) { return true; + } } return false; } -bool Bot::IsShootableThruObstacle (const Vector &dest) -{ +bool Bot::isPenetrableObstacle (const Vector &dest) { // this function returns true if enemy can be shoot through some obstacle, false otherwise. // credits goes to Immortal_BLG - if (yb_shoots_thru_walls.GetInt () == 2) - return IsShootableThruObstacleEx (dest); + if (yb_shoots_thru_walls.integer () == 2) { + return isPenetrableObstacle2 (dest); + } - if (m_difficulty < 2) + if (m_difficulty < 2) { return false; + } + int penetratePower = getWeaponPenetrationPower (m_currentWeapon); - int penetratePower = GetWeaponPenetrationPower (m_currentWeapon); - - if (penetratePower == 0) + if (penetratePower == 0) { return false; - + } TraceResult tr; float obstacleDistance = 0.0f; - engine.TestLine (EyePosition (), dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (eyePos (), dest, TRACE_IGNORE_MONSTERS, ent (), &tr); - if (tr.fStartSolid) - { + if (tr.fStartSolid) { const Vector &source = tr.vecEndPos; - engine.TestLine (dest, source, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (dest, source, TRACE_IGNORE_MONSTERS, ent (), &tr); - if (tr.flFraction != 1.0f) - { - if ((tr.vecEndPos - dest).GetLengthSquared () > GET_SQUARE (800.0f)) + if (tr.flFraction != 1.0f) { + if ((tr.vecEndPos - dest).lengthSq () > cr::square (800.0f)) { return false; + } - if (tr.vecEndPos.z >= dest.z + 200.0f) + if (tr.vecEndPos.z >= dest.z + 200.0f) { return false; - - obstacleDistance = (tr.vecEndPos - source).GetLength (); + } + obstacleDistance = (tr.vecEndPos - source).lengthSq (); } } + constexpr float distance = cr::square (75.0f); - if (obstacleDistance > 0.0f) - { - while (penetratePower > 0) - { - if (obstacleDistance > 75.0f) - { - obstacleDistance -= 75.0f; + if (obstacleDistance > 0.0f) { + while (penetratePower > 0) { + if (obstacleDistance > distance) { + obstacleDistance -= distance; penetratePower--; continue; @@ -635,101 +605,102 @@ bool Bot::IsShootableThruObstacle (const Vector &dest) return false; } -bool Bot::IsShootableThruObstacleEx (const Vector &dest) -{ +bool Bot::isPenetrableObstacle2 (const Vector &dest) { // this function returns if enemy can be shoot through some obstacle - if (m_difficulty < 2 || GetWeaponPenetrationPower (m_currentWeapon) == 0) + if (m_difficulty < 2 || getWeaponPenetrationPower (m_currentWeapon) == 0) { return false; + } - Vector source = EyePosition (); - Vector direction = (dest - source).Normalize (); // 1 unit long - Vector point; + const Vector &source = eyePos (); + const Vector &direction = (dest - source).normalize (); // 1 unit long int thikness = 0; int numHits = 0; + Vector point; TraceResult tr; - engine.TestLine (source, dest, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); - while (tr.flFraction != 1.0f && numHits < 3) - { + engine.testLine (source, dest, TRACE_IGNORE_EVERYTHING, ent (), &tr); + + while (tr.flFraction != 1.0f && numHits < 3) { numHits++; thikness++; point = tr.vecEndPos + direction; - while (POINT_CONTENTS (point) == CONTENTS_SOLID && thikness < 98) - { + while (g_engfuncs.pfnPointContents (point) == CONTENTS_SOLID && thikness < 98) { point = point + direction; thikness++; } - engine.TestLine (point, dest, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (point, dest, TRACE_IGNORE_EVERYTHING, ent (), &tr); } - if (numHits < 3 && thikness < 98) - { - if ((dest - point).GetLengthSquared () < 13143) + if (numHits < 3 && thikness < 98) { + if ((dest - point).lengthSq () < 13143.0f) { return true; + } } return false; } -bool Bot::DoFirePause (float distance) -{ +bool Bot::throttleFiring (float distance) { // returns true if bot needs to pause between firing to compensate for punchangle & weapon spread - if (m_firePause > engine.Time ()) + if (usesSniper ()) { + return false; + } + + if (m_firePause > engine.timebase ()) return true; - if ((m_aimFlags & AIM_ENEMY) && !m_enemyOrigin.IsZero ()) - { - if (GetShootingConeDeviation (GetEntity (), &m_enemyOrigin) > 0.92f && IsEnemyProtectedByShield (m_enemy)) + if ((m_aimFlags & AIM_ENEMY) && !m_enemyOrigin.empty ()) { + if (getShootingConeDeviation (ent (), m_enemyOrigin) > 0.92f && isEnemyBehindShield (m_enemy)) { return true; + } } - float offset = 0.0f; + float offset = 5.0f; - if (distance < MAX_SPRAY_DISTANCE) + if (distance < MAX_SPRAY_DISTANCE * 0.5f) { return false; - else if (distance < MAX_SPRAY_DISTANCE_X2) + } + else if (distance < MAX_SPRAY_DISTANCE) { + offset = 12.0f; + } + else if (distance < MAX_SPRAY_DISTANCE_X2) { offset = 10.0f; - else - offset = 5.0f; + } + const float xPunch = cr::deg2rad (pev->punchangle.x); + const float yPunch = cr::deg2rad (pev->punchangle.y); - const float xPunch = DegreeToRadian (pev->punchangle.x); - const float yPunch = DegreeToRadian (pev->punchangle.y); - - float interval = GetThinkInterval (); + float interval = calcThinkInterval (); + float tolerance = (100.0f - m_difficulty * 25.0f) / 99.0f; // check if we need to compensate recoil - if (tanf (A_sqrtf (fabsf (xPunch * xPunch) + fabsf (yPunch * yPunch))) * distance > offset + 30.0f + ((100 - (m_difficulty * 25)) / 100.f)) - { - if (m_firePause < engine.Time ()) - m_firePause = Random.Float (0.5f, 0.5f + 0.3f * ((100.0f - (m_difficulty * 25)) / 100.f)); - + if (cr::tanf (cr::sqrtf (cr::abs (xPunch * xPunch) + cr::abs (yPunch * yPunch))) * distance > offset + 30.0f + tolerance) { + if (m_firePause < engine.timebase ()) { + m_firePause = rng.getFloat (0.5f, 0.5f + 0.3f * tolerance); + } m_firePause -= interval; - m_firePause += engine.Time (); + m_firePause += engine.timebase (); return true; } return false; } -void Bot::FinishWeaponSelection (float distance, int index, int id, int choosen) -{ +void Bot::selectWeapons (float distance, int index, int id, int choosen) { WeaponSelect *tab = &g_weaponSelect[0]; // we want to fire weapon, don't reload now - if (!m_isReloading) - { + if (!m_isReloading) { m_reloadState = RELOAD_NONE; - m_reloadCheckTime = engine.Time () + 3.0f; + m_reloadCheckTime = engine.timebase () + 3.0f; } // select this weapon if it isn't already selected - if (m_currentWeapon != id) - { - SelectWeaponByName (g_weaponDefs[id].className); + if (m_currentWeapon != id) { + selectWeaponByName (g_weaponDefs[id].className); // reset burst fire variables m_firePause = 0.0f; @@ -738,137 +709,145 @@ void Bot::FinishWeaponSelection (float distance, int index, int id, int choosen) return; } - if (tab[choosen].id != id) - { + if (tab[choosen].id != id) { choosen = 0; // loop through all the weapons until terminator is found... - while (tab[choosen].id) - { - if (tab[choosen].id == id) + while (tab[choosen].id) { + if (tab[choosen].id == id) { break; - + } choosen++; } } // if we're have a glock or famas vary burst fire mode - CheckBurstMode (distance); + checkBurstMode (distance); - if (HasShield () && m_shieldCheckTime < engine.Time () && GetTaskId () != TASK_CAMP) // better shield gun usage + if (hasShield () && m_shieldCheckTime < engine.timebase () && taskId () != TASK_CAMP) // better shield gun usage { - if (distance >= 750.0f && !IsShieldDrawn ()) + if (distance >= 750.0f && !isShieldDrawn ()) { pev->button |= IN_ATTACK2; // draw the shield - else if (IsShieldDrawn () || (!engine.IsNullEntity (m_enemy) && ((m_enemy->v.button & IN_RELOAD) || !IsEnemyViewable (m_enemy)))) - pev->button |= IN_ATTACK2; // draw out the shield - - m_shieldCheckTime = engine.Time () + 1.0f; - } - - if (UsesSniper () && m_zoomCheckTime + 1.0f < engine.Time ()) // is the bot holding a sniper rifle? - { - if (distance > 1500.0f && pev->fov >= 40.0f) // should the bot switch to the long-range zoom? - pev->button |= IN_ATTACK2; - - else if (distance > 150.0f && pev->fov >= 90.0f) // else should the bot switch to the close-range zoom ? - pev->button |= IN_ATTACK2; - - else if (distance <= 150.0f && pev->fov < 90.0f) // else should the bot restore the normal view ? - pev->button |= IN_ATTACK2; - - m_zoomCheckTime = engine.Time (); - - if (!engine.IsNullEntity (m_enemy) && (m_states & STATE_SEEING_ENEMY)) - { - m_moveSpeed = 0.0f; - m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); } + else if (isShieldDrawn () || (!engine.isNullEntity (m_enemy) && ((m_enemy->v.button & IN_RELOAD) || !seesEnemy (m_enemy)))) { + pev->button |= IN_ATTACK2; // draw out the shield + } + m_shieldCheckTime = engine.timebase () + 1.0f; } - else if (m_difficulty < 4 && UsesZoomableRifle ()) // else is the bot holding a zoomable rifle? - { - if (distance > 800.0f && pev->fov >= 90.0f) // should the bot switch to zoomed mode? - pev->button |= IN_ATTACK2; - else if (distance <= 800.0f && pev->fov < 90.0f) // else should the bot restore the normal view? + // is the bot holding a sniper rifle? + if (usesSniper () && m_zoomCheckTime < engine.timebase ()) { + // should the bot switch to the long-range zoom? + if (distance > 1500.0f && pev->fov >= 40.0f) { pev->button |= IN_ATTACK2; + } - m_zoomCheckTime = engine.Time (); + // else should the bot switch to the close-range zoom ? + else if (distance > 150.0f && pev->fov >= 90.0f) { + pev->button |= IN_ATTACK2; + } + + // else should the bot restore the normal view ? + else if (distance <= 150.0f && pev->fov < 90.0f) { + pev->button |= IN_ATTACK2; + } + m_zoomCheckTime = engine.timebase () + 0.25f; + } + + // else is the bot holding a zoomable rifle? + else if (m_difficulty < 3 && usesZoomableRifle () && m_zoomCheckTime < engine.timebase ()) { + // should the bot switch to zoomed mode? + if (distance > 800.0f && pev->fov >= 90.0f) { + pev->button |= IN_ATTACK2; + } + + // else should the bot restore the normal view? + else if (distance <= 800.0f && pev->fov < 90.0f) { + pev->button |= IN_ATTACK2; + } + m_zoomCheckTime = engine.timebase () + 0.5f; + } + + // we're should stand still before firing sniper weapons, else sniping is useless.. + if (usesSniper () && (m_states & (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY)) && !m_isReloading && pev->velocity.lengthSq () > 0.0f) { + m_moveSpeed = 0.0f; + m_strafeSpeed = 0.0f; + m_navTimeset = engine.timebase (); + + if (cr::abs (pev->velocity.x) > 5.0f || cr::abs (pev->velocity.y) > 5.0f || cr::abs (pev->velocity.z) > 5.0f) { + m_sniperStopTime = engine.timebase () + 2.5f; + return; + } } // need to care for burst fire? - if (distance < MAX_SPRAY_DISTANCE || m_blindTime > engine.Time ()) - { - if (id == WEAPON_KNIFE) - { - if (distance < 64.0f) - { - if (Random.Int (1, 100) < 30 || HasShield ()) + if (distance < MAX_SPRAY_DISTANCE || m_blindTime > engine.timebase ()) { + if (id == WEAPON_KNIFE) { + if (distance < 64.0f) { + if (rng.getInt (1, 100) < 30 || hasShield ()) { pev->button |= IN_ATTACK; // use primary attack - else + } + else { pev->button |= IN_ATTACK2; // use secondary attack + } } } - else - { - if (tab[choosen].primaryFireHold && m_ammo[g_weaponDefs[tab[index].id].ammo1] > tab[index].minPrimaryAmmo) // if automatic weapon, just press attack + else { + + // if automatic weapon press attack + if (tab[choosen].primaryFireHold && m_ammo[g_weaponDefs[tab[index].id].ammo1] > tab[index].minPrimaryAmmo) { pev->button |= IN_ATTACK; - else // if not, toggle buttons - { - if ((pev->oldbuttons & IN_ATTACK) == 0) + } + + // if not, toggle + else { + if ((m_oldButtons & IN_ATTACK) == 0) { pev->button |= IN_ATTACK; + } } } - m_shootTime = engine.Time (); + m_shootTime = engine.timebase (); } - else - { - if (DoFirePause (distance)) + else { + if (throttleFiring (distance)) { return; + } // don't attack with knife over long distance - if (id == WEAPON_KNIFE) - { - m_shootTime = engine.Time (); + if (id == WEAPON_KNIFE) { + m_shootTime = engine.timebase (); return; } - if (tab[choosen].primaryFireHold) - { - m_shootTime = engine.Time (); - m_zoomCheckTime = engine.Time (); + if (tab[choosen].primaryFireHold) { + m_shootTime = engine.timebase (); + m_zoomCheckTime = engine.timebase (); - pev->button |= IN_ATTACK; // use primary attack + pev->button |= IN_ATTACK; // use primary attack } - else - { + else { pev->button |= IN_ATTACK; - m_shootTime = engine.Time () + Random.Float (0.15f, 0.35f); - m_zoomCheckTime = engine.Time () - 0.09f; + const float minDelay[] = { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.6f }; + const float maxDelay[] = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.7f }; + + const int offset = cr::abs (m_difficulty * 25 / 20 - 5); + + m_shootTime = engine.timebase () + 0.1f + rng.getFloat (minDelay[offset], maxDelay[offset]); + m_zoomCheckTime = engine.timebase (); } } } -void Bot::FireWeapon (void) -{ +void Bot::fireWeapons (void) { // this function will return true if weapon was fired, false otherwise - float distance = (m_lookAt - EyePosition ()).GetLength (); // how far away is the enemy? - - // if using grenade stop this - if (m_isUsingGrenade) - { - m_shootTime = engine.Time () + 0.1f; - return; - } + float distance = (m_lookAt - eyePos ()).length (); // how far away is the enemy? // or if friend in line of fire, stop this too but do not update shoot time - if (!engine.IsNullEntity (m_enemy)) - { - if (IsFriendInLineOfFire (distance)) - { + if (!engine.isNullEntity (m_enemy)) { + if (isFriendInLineOfFire (distance)) { m_fightStyle = FIGHT_STRAFE; - m_lastFightStyleCheck = engine.Time (); + m_lastFightStyleCheck = engine.timebase (); return; } @@ -881,56 +860,49 @@ void Bot::FireWeapon (void) int weapons = pev->weapons; // if jason mode use knife only - if (yb_jasonmode.GetBool ()) - { - FinishWeaponSelection (distance, selectIndex, selectId, choosenWeapon); + if (yb_jasonmode.boolean ()) { + selectWeapons (distance, selectIndex, selectId, choosenWeapon); return; } // use knife if near and good difficulty (l33t dude!) - if (m_difficulty >= 3 && pev->health > 80.0f && !engine.IsNullEntity (enemy) && pev->health >= enemy->v.health && distance < 100.0f && !IsOnLadder () && !IsGroupOfEnemies (pev->origin)) - { - FinishWeaponSelection (distance, selectIndex, selectId, choosenWeapon); + if (m_difficulty >= 3 && pev->health > 80.0f && !engine.isNullEntity (enemy) && pev->health >= enemy->v.health && distance < 100.0f && !isOnLadder () && !isGroupOfEnemies (pev->origin)) { + selectWeapons (distance, selectIndex, selectId, choosenWeapon); return; } // loop through all the weapons until terminator is found... - while (tab[selectIndex].id) - { + while (tab[selectIndex].id) { // is the bot carrying this weapon? - if (weapons & (1 << tab[selectIndex].id)) - { + if (weapons & (1 << tab[selectIndex].id)) { + // is enough ammo available to fire AND check is better to use pistol in our current situation... - if (m_ammoInClip[tab[selectIndex].id] > 0 && !IsWeaponBadInDistance (selectIndex, distance)) + if (m_ammoInClip[tab[selectIndex].id] > 0 && !isWeaponBadAtDistance (selectIndex, distance)) { choosenWeapon = selectIndex; + } } selectIndex++; } selectId = tab[choosenWeapon].id; // if no available weapon... - if (choosenWeapon == 0) - { + if (choosenWeapon == 0) { selectIndex = 0; // loop through all the weapons until terminator is found... - while (tab[selectIndex].id) - { + while (tab[selectIndex].id) { int id = tab[selectIndex].id; // is the bot carrying this weapon? - if (weapons & (1 << id)) - { - if ( g_weaponDefs[id].ammo1 != -1 && g_weaponDefs[id].ammo1 < 32 && m_ammo[g_weaponDefs[id].ammo1] >= tab[selectIndex].minPrimaryAmmo) - { + if (weapons & (1 << id)) { + if (g_weaponDefs[id].ammo1 != -1 && g_weaponDefs[id].ammo1 < 32 && m_ammo[g_weaponDefs[id].ammo1] >= tab[selectIndex].minPrimaryAmmo) { // available ammo found, reload weapon - if (m_reloadState == RELOAD_NONE || m_reloadCheckTime > engine.Time ()) - { + if (m_reloadState == RELOAD_NONE || m_reloadCheckTime > engine.timebase ()) { m_isReloading = true; m_reloadState = RELOAD_PRIMARY; - m_reloadCheckTime = engine.Time (); + m_reloadCheckTime = engine.timebase (); - RadioMessage (Radio_NeedBackup); + pushRadioMessage (RADIO_NEED_BACKUP); } return; } @@ -939,416 +911,406 @@ void Bot::FireWeapon (void) } selectId = WEAPON_KNIFE; // no available ammo, use knife! } - FinishWeaponSelection (distance, selectIndex, selectId, choosenWeapon); + selectWeapons (distance, selectIndex, selectId, choosenWeapon); } -bool Bot::IsWeaponBadInDistance (int weaponIndex, float distance) -{ +bool Bot::isWeaponBadAtDistance (int weaponIndex, float distance) { // this function checks, is it better to use pistol instead of current primary weapon // to attack our enemy, since current weapon is not very good in this situation. - if (m_difficulty < 2) + if (m_difficulty < 2) { return false; - + } int wid = g_weaponSelect[weaponIndex].id; - if (wid == WEAPON_KNIFE) + if (wid == WEAPON_KNIFE) { return false; + } // check is ammo available for secondary weapon - if (m_ammoInClip[g_weaponSelect[GetBestSecondaryWeaponCarried ()].id] >= 1) + if (m_ammoInClip[g_weaponSelect[bestSecondaryCarried ()].id] >= 1) { return false; + } // better use pistol in short range distances, when using sniper weapons - if ((wid == WEAPON_SCOUT || wid == WEAPON_AWP || wid == WEAPON_G3SG1 || wid == WEAPON_SG550) && distance < 500.0f) + if ((wid == WEAPON_SCOUT || wid == WEAPON_AWP || wid == WEAPON_G3SG1 || wid == WEAPON_SG550) && distance < 450.0f) { return true; + } // shotguns is too inaccurate at long distances, so weapon is bad - if ((wid == WEAPON_M3 || wid == WEAPON_XM1014) && distance > 750.0f) + if ((wid == WEAPON_M3 || wid == WEAPON_XM1014) && distance > 750.0f) { return true; - + } return false; } -void Bot::FocusEnemy (void) -{ +void Bot::focusEnemy (void) { // aim for the head and/or body - m_lookAt = GetAimPosition (); + m_lookAt = getEnemyBodyOffset (); - if (m_enemySurpriseTime > engine.Time ()) + if (m_enemySurpriseTime > engine.timebase () || engine.isNullEntity (m_enemy)) { return; + } + float distance = (m_lookAt - eyePos ()).length2D (); // how far away is the enemy scum? - float distance = (m_lookAt - EyePosition ()).GetLength2D (); // how far away is the enemy scum? - - if (distance < 128.0f) - { - if (m_currentWeapon == WEAPON_KNIFE) - { - if (distance < 80.0f) + if (distance < 128.0f && !usesSniper ()) { + if (m_currentWeapon == WEAPON_KNIFE) { + if (distance < 80.0f) { m_wantsToFire = true; + } + else if (distance > 120.0f) { + m_wantsToFire = false; + } } - else + else { m_wantsToFire = true; + } } - else - { - float dot = GetShootingConeDeviation (GetEntity (), &m_enemyOrigin); + else { + float dot = getShootingConeDeviation (ent (), m_enemyOrigin); - if (dot < 0.90f) + if (dot < 0.90f) { m_wantsToFire = false; - else - { - float enemyDot = GetShootingConeDeviation (m_enemy, &pev->origin); + } + else { + float enemyDot = getShootingConeDeviation (m_enemy, pev->origin); // enemy faces bot? - if (enemyDot >= 0.90f) + if (enemyDot >= 0.90f) { m_wantsToFire = true; - else - { - if (dot > 0.99f) - m_wantsToFire = true; - else - m_wantsToFire = false; } - + else { + if (dot > 0.99f) { + m_wantsToFire = true; + } + else { + m_wantsToFire = false; + } + } } } } -void Bot::CombatFight (void) -{ +void Bot::attackMovement (void) { // no enemy? no need to do strafing - if (engine.IsNullEntity (m_enemy)) + if (engine.isNullEntity (m_enemy)) { return; + } + float distance = (m_lookAt - eyePos ()).length2D (); // how far away is the enemy scum? - float distance = (m_lookAt - EyePosition ()).GetLength2D (); // how far away is the enemy scum? - - if (m_timeWaypointMove < engine.Time ()) - { + if (m_timeWaypointMove + calcThinkInterval () + 0.5f < engine.timebase ()) { int approach; - if (m_currentWeapon == WEAPON_KNIFE) // knife? + if (m_currentWeapon == WEAPON_KNIFE) { approach = 100; - else if ((m_states & STATE_SUSPECT_ENEMY) && !(m_states & STATE_SEEING_ENEMY)) // if suspecting enemy stand still + } + else if ((m_states & STATE_SUSPECT_ENEMY) && !(m_states & STATE_SEEING_ENEMY)) { approach = 49; - else if (m_isReloading || m_isVIP) // if reloading or vip back off + } + else if (m_isReloading || m_isVIP) { approach = 29; - else - { + } + else { approach = static_cast (pev->health * m_agressionLevel); - if (UsesSniper () && approach > 49) + if (usesSniper () && approach > 49) { approach = 49; + } } // only take cover when bomb is not planted and enemy can see the bot or the bot is VIP - if (approach < 30 && !g_bombPlanted && (IsInViewCone (m_enemy->v.origin) || m_isVIP)) - { + if ((m_states & STATE_SEEING_ENEMY) && approach < 30 && !g_bombPlanted && (isInViewCone (m_enemy->v.origin) || m_isVIP)) { m_moveSpeed = -pev->maxspeed; - - TaskItem *task = GetTask (); - - task->id = TASK_SEEKCOVER; - task->resume = true; - task->desire = TASKPRI_ATTACK + 1.0f; + startTask (TASK_SEEKCOVER, TASKPRI_SEEKCOVER, INVALID_WAYPOINT_INDEX, 0.0f, true); } - else if (approach < 50) + else if (approach < 50) { m_moveSpeed = 0.0f; - else + } + else { m_moveSpeed = pev->maxspeed; + } - if (distance < 96.0f && m_currentWeapon != WEAPON_KNIFE) + if (distance < 96.0f && m_currentWeapon != WEAPON_KNIFE) { m_moveSpeed = -pev->maxspeed; + } - if (UsesSniper ()) - { + if (usesSniper () || !(m_visibility & (VISIBLE_BODY | VISIBLE_HEAD))) { m_fightStyle = FIGHT_STAY; - m_lastFightStyleCheck = engine.Time (); + m_lastFightStyleCheck = engine.timebase (); } - else if (UsesRifle () || UsesSubmachineGun ()) - { - if (m_lastFightStyleCheck + 3.0f < engine.Time ()) - { - int rand = Random.Int (1, 100); + else if (usesRifle () || usesSubmachine ()) { + if (m_lastFightStyleCheck + 3.0f < engine.timebase ()) { + int rand = rng.getInt (1, 100); - if (distance < 450.0f) + if (distance < 450.0f) { m_fightStyle = FIGHT_STRAFE; - else if (distance < 1024.0f) - { - if (rand < (UsesSubmachineGun () ? 50 : 30)) - m_fightStyle = FIGHT_STRAFE; - else - m_fightStyle = FIGHT_STAY; } - else - { - if (rand < (UsesSubmachineGun () ? 80 : 93)) - m_fightStyle = FIGHT_STAY; - else + else if (distance < 1024.0f) { + if (rand < (usesSubmachine () ? 50 : 30)) { m_fightStyle = FIGHT_STRAFE; + } + else { + m_fightStyle = FIGHT_STAY; + } } - m_lastFightStyleCheck = engine.Time (); + else { + if (rand < (usesSubmachine () ? 80 : 93)) { + m_fightStyle = FIGHT_STAY; + } + else { + m_fightStyle = FIGHT_STRAFE; + } + } + m_lastFightStyleCheck = engine.timebase (); } } - else - { - if (m_lastFightStyleCheck + 3.0f < engine.Time ()) - { - if (Random.Int (0, 100) < 50) + else { + if (m_lastFightStyleCheck + 3.0f < engine.timebase ()) { + if (rng.getInt (0, 100) < 50) { m_fightStyle = FIGHT_STRAFE; - else + } + else { m_fightStyle = FIGHT_STAY; - - m_lastFightStyleCheck = engine.Time (); + } + m_lastFightStyleCheck = engine.timebase (); } } - if (m_fightStyle == FIGHT_STRAFE || ((pev->button & IN_RELOAD) || m_isReloading) || (UsesPistol () && distance < 400.0f) || m_currentWeapon == WEAPON_KNIFE) - { - if (m_strafeSetTime < engine.Time ()) - { + if (m_fightStyle == FIGHT_STRAFE || ((pev->button & IN_RELOAD) || m_isReloading) || (usesPistol () && distance < 400.0f) || m_currentWeapon == WEAPON_KNIFE) { + if (m_strafeSetTime < engine.timebase ()) { // to start strafing, we have to first figure out if the target is on the left side or right side - MakeVectors (m_enemy->v.v_angle); + makeVectors (m_enemy->v.v_angle); - const Vector &dirToPoint = (pev->origin - m_enemy->v.origin).Normalize2D (); - const Vector &rightSide = g_pGlobals->v_right.Normalize2D (); + const Vector &dirToPoint = (pev->origin - m_enemy->v.origin).normalize2D (); + const Vector &rightSide = g_pGlobals->v_right.normalize2D (); - if ((dirToPoint | rightSide) < 0) + if ((dirToPoint | rightSide) < 0) { m_combatStrafeDir = STRAFE_DIR_LEFT; - else + } + else { m_combatStrafeDir = STRAFE_DIR_RIGHT; + } - if (Random.Int (1, 100) < 30) + if (rng.getInt (1, 100) < 30) { m_combatStrafeDir = (m_combatStrafeDir == STRAFE_DIR_LEFT ? STRAFE_DIR_RIGHT : STRAFE_DIR_LEFT); - - m_strafeSetTime = engine.Time () + Random.Float (0.5f, 3.0f); + } + m_strafeSetTime = engine.timebase () + rng.getFloat (0.5f, 3.0f); } - if (m_combatStrafeDir == STRAFE_DIR_RIGHT) - { - if (!CheckWallOnLeft ()) + if (m_combatStrafeDir == STRAFE_DIR_RIGHT) { + if (!checkWallOnLeft ()) { m_strafeSpeed = -pev->maxspeed; - else - { + } + else { m_combatStrafeDir = STRAFE_DIR_LEFT; - m_strafeSetTime = engine.Time () + 1.5f; + m_strafeSetTime = engine.timebase () + rng.getFloat (0.8f, 1.3f); } } - else - { - if (!CheckWallOnRight ()) + else { + if (!checkWallOnRight ()) { m_strafeSpeed = pev->maxspeed; - else - { + } + else { m_combatStrafeDir = STRAFE_DIR_RIGHT; - m_strafeSetTime = engine.Time () + 1.5f; + m_strafeSetTime = engine.timebase () + rng.getFloat (0.8f, 1.3f); } } - if (m_difficulty >= 3 && (m_jumpTime + 5.0f < engine.Time () && IsOnFloor () && Random.Int (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.GetLength2D () > 150.0f) && !UsesSniper ()) + if (m_difficulty >= 3 && (m_jumpTime + 5.0f < engine.timebase () && isOnFloor () && rng.getInt (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.length2D () > 120.0f) && !usesSniper ()) { pev->button |= IN_JUMP; + } - if (m_moveSpeed > 0.0f && distance > 100.0f && m_currentWeapon != WEAPON_KNIFE) + if (m_moveSpeed > 0.0f && distance > 100.0f && m_currentWeapon != WEAPON_KNIFE) { m_moveSpeed = 0.0f; + } - if (m_currentWeapon == WEAPON_KNIFE) + if (m_currentWeapon == WEAPON_KNIFE) { m_strafeSpeed = 0.0f; + } } - else if (m_fightStyle == FIGHT_STAY) - { - if ((m_visibility & (VISIBLE_HEAD | VISIBLE_BODY)) && GetTaskId () != TASK_SEEKCOVER && GetTaskId () != TASK_HUNTENEMY && waypoints.IsDuckVisible (m_currentWaypointIndex, waypoints.FindNearest (m_enemy->v.origin))) - m_duckTime = engine.Time () + 0.5f; + else if (m_fightStyle == FIGHT_STAY) { + if ((m_visibility & (VISIBLE_HEAD | VISIBLE_BODY)) && !(m_visibility & VISIBLE_OTHER) && taskId () != TASK_SEEKCOVER && taskId () != TASK_HUNTENEMY) { + int enemyNearestIndex = waypoints.getNearest (m_enemy->v.origin); + if (waypoints.isDuckVisible (m_currentWaypointIndex, enemyNearestIndex) && waypoints.isDuckVisible (enemyNearestIndex, m_currentWaypointIndex)) { + m_duckTime = engine.timebase () + 1.0f; + } + } m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); } } - if (m_duckTime > engine.Time ()) - { + if (m_duckTime > engine.timebase ()) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; } - if (m_moveSpeed > 0.0f && m_currentWeapon != WEAPON_KNIFE) - m_moveSpeed = GetWalkSpeed (); - - if (m_isReloading) - { - m_moveSpeed = -pev->maxspeed; - m_duckTime = engine.Time () - 1.0f; + if (m_moveSpeed > 0.0f && m_currentWeapon != WEAPON_KNIFE) { + m_moveSpeed = getShiftSpeed (); } - if (!IsInWater () && !IsOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) - { - MakeVectors (pev->v_angle); + if (m_isReloading) { + m_moveSpeed = -pev->maxspeed; + m_duckTime = engine.timebase () - 1.0f; + } - if (IsDeadlyDrop (pev->origin + (g_pGlobals->v_forward * m_moveSpeed * 0.2f) + (g_pGlobals->v_right * m_strafeSpeed * 0.2f) + (pev->velocity * GetThinkInterval ()))) - { + if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) { + makeVectors (pev->v_angle); + + if (isDeadlyMove (pev->origin + (g_pGlobals->v_forward * m_moveSpeed * 0.2f) + (g_pGlobals->v_right * m_strafeSpeed * 0.2f) + (pev->velocity * calcThinkInterval ()))) { m_strafeSpeed = -m_strafeSpeed; m_moveSpeed = -m_moveSpeed; pev->button &= ~IN_JUMP; } } - IgnoreCollisionShortly (); + ignoreCollision (); } -bool Bot::HasPrimaryWeapon (void) -{ +bool Bot::hasPrimaryWeapon (void) { // this function returns returns true, if bot has a primary weapon - return (pev->weapons & WEAPON_PRIMARY) != 0; + return (pev->weapons & WEAPON_PRIMARY) != 0; } -bool Bot::HasSecondaryWeapon (void) -{ +bool Bot::hasSecondaryWeapon (void) { // this function returns returns true, if bot has a secondary weapon return (pev->weapons & WEAPON_SECONDARY) != 0; } -bool Bot::HasShield (void) -{ +bool Bot::hasShield (void) { // this function returns true, if bot has a tactical shield return strncmp (STRING (pev->viewmodel), "models/shield/v_shield_", 23) == 0; } -bool Bot::IsShieldDrawn (void) -{ +bool Bot::isShieldDrawn (void) { // this function returns true, is the tactical shield is drawn - if (!HasShield ()) + if (!hasShield ()) { return false; - + } return pev->weaponanim == 6 || pev->weaponanim == 7; } -bool Bot::IsEnemyProtectedByShield (edict_t *enemy) -{ +bool Bot::isEnemyBehindShield (edict_t *enemy) { // this function returns true, if enemy protected by the shield - if (engine.IsNullEntity (enemy) || IsShieldDrawn ()) + if (engine.isNullEntity (enemy) || isShieldDrawn ()) { return false; + } // check if enemy has shield and this shield is drawn - if (strncmp (STRING (enemy->v.viewmodel), "models/shield/v_shield_", 23) == 0 && (enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7)) - { - if (::IsInViewCone (pev->origin, enemy)) + if (strncmp (STRING (enemy->v.viewmodel), "models/shield/v_shield_", 23) == 0 && (enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7)) { + if (::isInViewCone (pev->origin, enemy)) { return true; + } } return false; } -bool Bot::UsesSniper (void) -{ +bool Bot::usesSniper (void) { // this function returns true, if returns if bot is using a sniper rifle return m_currentWeapon == WEAPON_AWP || m_currentWeapon == WEAPON_G3SG1 || m_currentWeapon == WEAPON_SCOUT || m_currentWeapon == WEAPON_SG550; } -bool Bot::UsesRifle (void) -{ +bool Bot::usesRifle (void) { WeaponSelect *tab = &g_weaponSelect[0]; int count = 0; - while (tab->id) - { - if (m_currentWeapon == tab->id) + while (tab->id) { + if (m_currentWeapon == tab->id) { break; - + } tab++; count++; } - if (tab->id && count > 13) + if (tab->id && count > 13) { return true; - + } return false; } -bool Bot::UsesPistol (void) -{ +bool Bot::usesPistol (void) { WeaponSelect *tab = &g_weaponSelect[0]; int count = 0; // loop through all the weapons until terminator is found - while (tab->id) - { - if (m_currentWeapon == tab->id) + while (tab->id) { + if (m_currentWeapon == tab->id) { break; - + } tab++; count++; } - if (tab->id && count < 7) + if (tab->id && count < 7) { return true; - + } return false; } -bool Bot::UsesCampGun (void) -{ - return UsesSubmachineGun () || UsesRifle () || UsesSniper (); +bool Bot::usesCampGun (void) { + return usesSubmachine () || usesRifle () || usesSniper (); } -bool Bot::UsesSubmachineGun (void) -{ +bool Bot::usesSubmachine (void) { return m_currentWeapon == WEAPON_MP5 || m_currentWeapon == WEAPON_TMP || m_currentWeapon == WEAPON_P90 || m_currentWeapon == WEAPON_MAC10 || m_currentWeapon == WEAPON_UMP45; } -bool Bot::UsesZoomableRifle (void) -{ +bool Bot::usesZoomableRifle (void) { return m_currentWeapon == WEAPON_AUG || m_currentWeapon == WEAPON_SG552; } -bool Bot::UsesBadPrimary (void) -{ +bool Bot::usesBadWeapon (void) { return m_currentWeapon == WEAPON_XM1014 || m_currentWeapon == WEAPON_M3 || m_currentWeapon == WEAPON_UMP45 || m_currentWeapon == WEAPON_MAC10 || m_currentWeapon == WEAPON_TMP || m_currentWeapon == WEAPON_P90; } -int Bot::CheckGrenades (void) -{ - if (pev->weapons & (1 << WEAPON_EXPLOSIVE)) +int Bot::bestGrenadeCarried (void) { + if (pev->weapons & (1 << WEAPON_EXPLOSIVE)) { return WEAPON_EXPLOSIVE; - else if (pev->weapons & (1 << WEAPON_FLASHBANG)) - return WEAPON_FLASHBANG; - else if (pev->weapons & (1 << WEAPON_SMOKE)) + } + else if (pev->weapons & (1 << WEAPON_SMOKE)) { return WEAPON_SMOKE; - + } + else if (pev->weapons & (1 << WEAPON_FLASHBANG)) { + return WEAPON_FLASHBANG; + } return -1; } -void Bot::SelectBestWeapon (void) -{ +bool Bot::hasAnyWeapons (void) { + return (pev->weapons & (WEAPON_PRIMARY | WEAPON_SECONDARY)); +} + +void Bot::selectBestWeapon (void) { // this function chooses best weapon, from weapons that bot currently own, and change // current weapon to best one. - if (yb_jasonmode.GetBool ()) - { + if (yb_jasonmode.boolean ()) { // if knife mode activated, force bot to use knife - SelectWeaponByName ("weapon_knife"); + selectWeaponByName ("weapon_knife"); return; } - if (m_isReloading) + if (m_isReloading) { return; - + } WeaponSelect *tab = &g_weaponSelect[0]; int selectIndex = 0; int chosenWeaponIndex = 0; // loop through all the weapons until terminator is found... - while (tab[selectIndex].id) - { + while (tab[selectIndex].id) { // is the bot NOT carrying this weapon? - if (!(pev->weapons & (1 << tab[selectIndex].id))) - { - selectIndex++; // skip to next weapon + if (!(pev->weapons & (1 << tab[selectIndex].id))) { + selectIndex++; // skip to next weapon continue; } @@ -1356,16 +1318,18 @@ void Bot::SelectBestWeapon (void) bool ammoLeft = false; // is the bot already holding this weapon and there is still ammo in clip? - if (tab[selectIndex].id == m_currentWeapon && (GetAmmoInClip () < 0 || GetAmmoInClip () >= tab[selectIndex].minPrimaryAmmo)) + if (tab[selectIndex].id == m_currentWeapon && (ammoClip () < 0 || ammoClip () >= tab[selectIndex].minPrimaryAmmo)) { ammoLeft = true; + } // is no ammo required for this weapon OR enough ammo available to fire - if (g_weaponDefs[id].ammo1 < 0 || (g_weaponDefs[id].ammo1 < 32 && m_ammo[g_weaponDefs[id].ammo1] >= tab[selectIndex].minPrimaryAmmo)) + if (g_weaponDefs[id].ammo1 < 0 || (g_weaponDefs[id].ammo1 < 32 && m_ammo[g_weaponDefs[id].ammo1] >= tab[selectIndex].minPrimaryAmmo)) { ammoLeft = true; + } - if (ammoLeft) + if (ammoLeft) { chosenWeaponIndex = selectIndex; - + } selectIndex++; } @@ -1375,25 +1339,23 @@ void Bot::SelectBestWeapon (void) int id = tab[selectIndex].id; // select this weapon if it isn't already selected - if (m_currentWeapon != id) - SelectWeaponByName (tab[selectIndex].weaponName); - + if (m_currentWeapon != id) { + selectWeaponByName (tab[selectIndex].weaponName); + } m_isReloading = false; m_reloadState = RELOAD_NONE; } -void Bot::SelectPistol (void) -{ +void Bot::selectSecondary (void) { int oldWeapons = pev->weapons; pev->weapons &= ~WEAPON_PRIMARY; - SelectBestWeapon (); + selectBestWeapon (); pev->weapons = oldWeapons; } -int Bot::GetHighestWeapon (void) -{ +int Bot::bestWeaponCarried (void) { WeaponSelect *tab = &g_weaponSelect[0]; int weapons = pev->weapons; @@ -1401,248 +1363,243 @@ int Bot::GetHighestWeapon (void) int i = 0; // loop through all the weapons until terminator is found... - while (tab->id) - { + while (tab->id) { // is the bot carrying this weapon? - if (weapons & (1 << tab->id)) + if (weapons & (1 << tab->id)) { num = i; - + } i++; tab++; } return num; } -void Bot::SelectWeaponByName (const char *name) -{ - engine.IssueBotCommand (GetEntity (), name); +void Bot::selectWeaponByName (const char *name) { + engine.execBotCmd (ent (), name); } -void Bot::SelectWeaponbyNumber (int num) -{ - engine.IssueBotCommand (GetEntity (), g_weaponSelect[num].weaponName); +void Bot::selectWeaponById (int num) { + engine.execBotCmd (ent (), g_weaponSelect[num].weaponName); } -void Bot::AttachToUser (void) -{ +void Bot::decideFollowUser (void) { // this function forces bot to follow user - Array users; + Array users; // search friends near us - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) { continue; + } - if (EntityIsVisible (client.origin) && !IsValidBot (client.ent)) - users.Push (client.ent); + if (seesEntity (client.origin) && !isFakeClient (client.ent)) { + users.push (client.ent); + } } - if (users.IsEmpty ()) + if (users.empty ()) { return; + } + m_targetEntity = users.random (); - m_targetEntity = users.GetRandomElement (); - - ChatterMessage (Chatter_LeadOnSir); - PushTask (TASK_FOLLOWUSER, TASKPRI_FOLLOWUSER, -1, 0.0f, true); + pushChatterMessage (CHATTER_LEAD_ON_SIR); + startTask (TASK_FOLLOWUSER, TASKPRI_FOLLOWUSER, INVALID_WAYPOINT_INDEX, 0.0f, true); } -void Bot::CommandTeam (void) -{ +void Bot::processTeamCommands (void) { // prevent spamming - if (m_timeTeamOrder > engine.Time () + 2.0f || (g_gameFlags & GAME_CSDM_FFA) || !yb_communication_type.GetInt ()) + if (m_timeTeamOrder > engine.timebase () + 2.0f || (g_gameFlags & GAME_CSDM_FFA) || !yb_communication_type.integer ()) { return; + } bool memberNear = false; bool memberExists = false; // search teammates seen by this bot - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) { continue; - + } memberExists = true; - if (EntityIsVisible (client.origin)) - { + if (seesEntity (client.origin)) { memberNear = true; break; } } - if (memberNear) // has teammates ? - { - if (m_personality == PERSONALITY_RUSHER && yb_communication_type.GetInt () == 2) - RadioMessage (Radio_StormTheFront); - else if (m_personality != PERSONALITY_RUSHER && yb_communication_type.GetInt () == 2) - RadioMessage (Radio_Fallback); + // has teammates? + if (memberNear) { + if (m_personality == PERSONALITY_RUSHER && yb_communication_type.integer () == 2) { + pushRadioMessage (RADIO_STORM_THE_FRONT); + } + else if (m_personality != PERSONALITY_RUSHER && yb_communication_type.integer () == 2) { + pushRadioMessage (RADIO_TEAM_FALLBACK); + } } - else if (memberExists && yb_communication_type.GetInt () == 1) - RadioMessage (Radio_TakingFire); - else if (memberExists && yb_communication_type.GetInt () == 2) - ChatterMessage(Chatter_ScaredEmotion); - - m_timeTeamOrder = engine.Time () + Random.Float (5.0f, 30.0f); + else if (memberExists && yb_communication_type.integer () == 1) { + pushRadioMessage (RADIO_TAKING_FIRE); + } + else if (memberExists && yb_communication_type.integer () == 2) { + pushChatterMessage (CHATTER_SCARED_EMOTE); + } + m_timeTeamOrder = engine.timebase () + rng.getFloat (15.0f, 30.0f); } -bool Bot::IsGroupOfEnemies (const Vector &location, int numEnemies, int radius) -{ +bool Bot::isGroupOfEnemies (const Vector &location, int numEnemies, float radius) { int numPlayers = 0; // search the world for enemy players... - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == GetEntity ()) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == ent ()) { continue; + } - if ((client.ent->v.origin - location).GetLengthSquared () < GET_SQUARE (radius)) - { + if ((client.ent->v.origin - location).lengthSq () < cr::square (radius)) { // don't target our teammates... - if (client.team == m_team) + if (client.team == m_team) { return false; + } - if (numPlayers++ > numEnemies) + if (numPlayers++ > numEnemies) { return true; + } } } return false; } -void Bot::CheckReload (void) -{ +void Bot::checkReload (void) { // check the reload state - if (GetTaskId () == TASK_PLANTBOMB || GetTaskId () == TASK_DEFUSEBOMB || GetTaskId () == TASK_PICKUPITEM || GetTaskId () == TASK_THROWFLASHBANG || GetTaskId () == TASK_THROWSMOKE || m_isUsingGrenade) - { + if (taskId () == TASK_PLANTBOMB || taskId () == TASK_DEFUSEBOMB || taskId () == TASK_PICKUPITEM || taskId () == TASK_THROWFLASHBANG || taskId () == TASK_THROWSMOKE || m_isUsingGrenade) { m_reloadState = RELOAD_NONE; return; } m_isReloading = false; // update reloading status - m_reloadCheckTime = engine.Time () + 3.0f; + m_reloadCheckTime = engine.timebase () + 3.0f; - if (m_reloadState != RELOAD_NONE) - { + if (m_reloadState != RELOAD_NONE) { int weaponIndex = 0, maxClip = 0; int weapons = pev->weapons; - if (m_reloadState == RELOAD_PRIMARY) + if (m_reloadState == RELOAD_PRIMARY) { weapons &= WEAPON_PRIMARY; - else if (m_reloadState == RELOAD_SECONDARY) + } + else if (m_reloadState == RELOAD_SECONDARY) { weapons &= WEAPON_SECONDARY; + } - if (weapons == 0) - { + if (weapons == 0) { m_reloadState++; - if (m_reloadState > RELOAD_SECONDARY) + if (m_reloadState > RELOAD_SECONDARY) { m_reloadState = RELOAD_NONE; - + } return; } - for (int i = 1; i < MAX_WEAPONS; i++) - { - if (weapons & (1 << i)) - { + for (int i = 1; i < MAX_WEAPONS; i++) { + if (weapons & (1 << i)) { weaponIndex = i; break; } } - InternalAssert (weaponIndex); + maxClip = getMaxClip (weaponIndex); - switch (weaponIndex) - { - case WEAPON_M249: - maxClip = 100; - break; + if (m_ammoInClip[weaponIndex] < maxClip * 0.8f && g_weaponDefs[weaponIndex].ammo1 != -1 && g_weaponDefs[weaponIndex].ammo1 < 32 && m_ammo[g_weaponDefs[weaponIndex].ammo1] > 0) { + if (m_currentWeapon != weaponIndex) { + selectWeaponByName (g_weaponDefs[weaponIndex].className); + } + pev->button &= ~IN_ATTACK; - case WEAPON_P90: - maxClip = 50; - break; - - case WEAPON_GALIL: - maxClip = 35; - break; - - case WEAPON_ELITE: - case WEAPON_MP5: - case WEAPON_TMP: - case WEAPON_MAC10: - case WEAPON_M4A1: - case WEAPON_AK47: - case WEAPON_SG552: - case WEAPON_AUG: - case WEAPON_SG550: - maxClip = 30; - break; - - case WEAPON_UMP45: - case WEAPON_FAMAS: - maxClip = 25; - break; - - case WEAPON_GLOCK: - case WEAPON_FIVESEVEN: - case WEAPON_G3SG1: - maxClip = 20; - break; - - case WEAPON_P228: - maxClip = 13; - break; - - case WEAPON_USP: - maxClip = 12; - break; - - case WEAPON_AWP: - case WEAPON_SCOUT: - maxClip = 10; - break; - - case WEAPON_M3: - maxClip = 8; - break; - - case WEAPON_DEAGLE: - case WEAPON_XM1014: - maxClip = 7; - break; - } - - if (m_ammoInClip[weaponIndex] < maxClip * 0.8f && g_weaponDefs[weaponIndex].ammo1 != -1 && g_weaponDefs[weaponIndex].ammo1 < 32 && m_ammo[g_weaponDefs[weaponIndex].ammo1] > 0) - { - if (m_currentWeapon != weaponIndex) - SelectWeaponByName (g_weaponDefs[weaponIndex].className); - - pev->button &= ~IN_ATTACK; - - if ((pev->oldbuttons & IN_RELOAD) == RELOAD_NONE) + if ((m_oldButtons & IN_RELOAD) == RELOAD_NONE) { pev->button |= IN_RELOAD; // press reload button - + } m_isReloading = true; } - else - { + else { // if we have enemy don't reload next weapon - if ((m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) || m_seeEnemyTime + 5.0f > engine.Time ()) - { + if ((m_states & (STATE_SEEING_ENEMY | STATE_HEARING_ENEMY)) || m_seeEnemyTime + 5.0f > engine.timebase ()) { m_reloadState = RELOAD_NONE; return; } m_reloadState++; - if (m_reloadState > RELOAD_SECONDARY) + if (m_reloadState > RELOAD_SECONDARY) { m_reloadState = RELOAD_NONE; - + } return; } } } + +int Bot::getMaxClip (int id) { + int maxClip = 0; + + switch (id) { + case WEAPON_M249: + maxClip = 100; + break; + + case WEAPON_P90: + maxClip = 50; + break; + + case WEAPON_GALIL: + maxClip = 35; + break; + + case WEAPON_ELITE: + case WEAPON_MP5: + case WEAPON_TMP: + case WEAPON_MAC10: + case WEAPON_M4A1: + case WEAPON_AK47: + case WEAPON_SG552: + case WEAPON_AUG: + case WEAPON_SG550: + maxClip = 30; + break; + + case WEAPON_UMP45: + case WEAPON_FAMAS: + maxClip = 25; + break; + + case WEAPON_GLOCK: + case WEAPON_FIVESEVEN: + case WEAPON_G3SG1: + maxClip = 20; + break; + + case WEAPON_P228: + maxClip = 13; + break; + + case WEAPON_USP: + maxClip = 12; + break; + + case WEAPON_AWP: + case WEAPON_SCOUT: + maxClip = 10; + break; + + case WEAPON_M3: + maxClip = 8; + break; + + case WEAPON_DEAGLE: + case WEAPON_XM1014: + maxClip = 7; + break; + } + return maxClip; +} diff --git a/source/engine.cpp b/source/engine.cpp index 485250e..ca80a0d 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -4,162 +4,240 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include -Engine::Engine (void) -{ +Engine::Engine (void) { m_startEntity = nullptr; m_localEntity = nullptr; - m_language.RemoveAll (); - ResetMessageCapture (); + resetMessages (); - for (int i = 0; i < NETMSG_NUM; i++) + for (int i = 0; i < NETMSG_NUM; i++) { m_msgBlock.regMsgs[i] = NETMSG_UNDEFINED; - + } + m_precached = false; m_isBotCommand = false; m_argumentCount = 0; + memset (m_arguments, 0, sizeof (m_arguments)); + memset (m_drawModels, 0, sizeof (m_drawModels)); - m_cvars.RemoveAll (); - m_language.RemoveAll (); + m_cvars.clear (); } -Engine::~Engine (void) -{ - TerminateTranslator (); - ResetMessageCapture (); +Engine::~Engine (void) { + resetMessages (); - for (int i = 0; i < NETMSG_NUM; i++) + for (int i = 0; i < NETMSG_NUM; i++) { m_msgBlock.regMsgs[i] = NETMSG_UNDEFINED; + } } -void Engine::Precache (edict_t *startEntity) -{ +void Engine::precache (void) { + if (m_precached) { + return; + } + m_precached = true; + + m_drawModels[DRAW_SIMPLE] = g_engfuncs.pfnPrecacheModel (ENGINE_STR ("sprites/laserbeam.spr")); + m_drawModels[DRAW_ARROW] = g_engfuncs.pfnPrecacheModel (ENGINE_STR ("sprites/arrow1.spr")); + + g_engfuncs.pfnPrecacheSound (ENGINE_STR ("weapons/xbow_hit1.wav")); // waypoint add + g_engfuncs.pfnPrecacheSound (ENGINE_STR ("weapons/mine_activate.wav")); // waypoint delete + g_engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_hudoff.wav")); // path add/delete start + g_engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_hudon.wav")); // path add/delete done + g_engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_moveselect.wav")); // path add/delete cancel + g_engfuncs.pfnPrecacheSound (ENGINE_STR ("common/wpn_denyselect.wav")); // path add/delete error + + g_mapFlags = 0; // reset map type as worldspawn is the first entity spawned + + // detect official csbots here, as they causing crash in linkent code when active for some reason + if (!(g_gameFlags & GAME_LEGACY) && g_engfuncs.pfnCVarGetPointer ("bot_stop") != nullptr) { + g_gameFlags |= GAME_OFFICIAL_CSBOT; + } + pushRegStackToEngine (true); +} + +void Engine::levelInitialize (void) { // this function precaches needed models and initialize class variables - m_drawModels[DRAW_SIMPLE] = PRECACHE_MODEL (ENGINE_STR ("sprites/laserbeam.spr")); - m_drawModels[DRAW_ARROW] = PRECACHE_MODEL (ENGINE_STR ("sprites/arrow1.spr")); - m_localEntity = nullptr; - m_startEntity = startEntity; + + // go thru the all entities on map, and do whatever we're want + for (int i = 0; i < g_pGlobals->maxEntities; i++) { + + auto ent = g_engfuncs.pfnPEntityOfEntIndex (i); + + // only valid entities + if (engine.isNullEntity (ent) || ent->free || ent->v.classname == 0) { + continue; + } + auto classname = STRING (ent->v.classname); + + if (strcmp (classname, "worldspawn") == 0) { + m_startEntity = ent; + + // initialize some structures + initRound (); + } + else if (strcmp (classname, "player_weaponstrip") == 0) { + if ((g_gameFlags & GAME_LEGACY) && (STRING (ent->v.target))[0] == '\0') { + ent->v.target = ent->v.targetname = g_engfuncs.pfnAllocString ("fake"); + } + else { + g_engfuncs.pfnRemoveEntity (ent); + } + } + else if (strcmp (classname, "info_player_start") == 0) { + g_engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/urban/urban.mdl")); + + ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency + ent->v.renderamt = 127; // set its transparency amount + ent->v.effects |= EF_NODRAW; + } + else if (strcmp (classname, "info_player_deathmatch") == 0) { + g_engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/terror/terror.mdl")); + + ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency + ent->v.renderamt = 127; // set its transparency amount + ent->v.effects |= EF_NODRAW; + } + + else if (strcmp (classname, "info_vip_start") == 0) { + g_engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/vip/vip.mdl")); + + ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency + ent->v.renderamt = 127; // set its transparency amount + ent->v.effects |= EF_NODRAW; + } + else if (strcmp (classname, "func_vip_safetyzone") == 0 || strcmp (classname, "info_vip_safetyzone") == 0) { + g_mapFlags |= MAP_AS; // assassination map + } + else if (strcmp (classname, "hostage_entity") == 0) { + g_mapFlags |= MAP_CS; // rescue map + } + else if (strcmp (classname, "func_bomb_target") == 0 || strcmp (classname, "info_bomb_target") == 0) { + g_mapFlags |= MAP_DE; // defusion map + } + else if (strcmp (classname, "func_escapezone") == 0) { + g_mapFlags |= MAP_ES; + } + else if (strncmp (classname, "func_door", 9) == 0) { + g_mapFlags |= MAP_HAS_DOORS; + } + } + + // next maps doesn't have map-specific entities, so determine it by name + if (strncmp (engine.getMapName (), "fy_", 3) == 0) { + g_mapFlags |= MAP_FY; + } + else if (strncmp (engine.getMapName (), "ka_", 3) == 0) { + g_mapFlags |= MAP_KA; + } } -void Engine::Printf (const char *fmt, ...) -{ +void Engine::print (const char *fmt, ...) { // this function outputs string into server console va_list ap; char string[MAX_PRINT_BUFFER]; va_start (ap, fmt); - vsnprintf (string, SIZEOF_CHAR (string), TraslateMessage (fmt), ap); + vsnprintf (string, cr::bufsize (string), translate (fmt), ap); va_end (ap); strcat (string, "\n"); g_engfuncs.pfnServerPrint (string); } -void Engine::ChatPrintf (const char *fmt, ...) -{ +void Engine::chatPrint (const char *fmt, ...) { va_list ap; char string[MAX_PRINT_BUFFER]; va_start (ap, fmt); - vsnprintf (string, SIZEOF_CHAR (string), TraslateMessage (fmt), ap); + vsnprintf (string, cr::bufsize (string), translate (fmt), ap); va_end (ap); - if (IsDedicatedServer ()) - { - Printf (string); + if (isDedicated ()) { + print (string); return; } strcat (string, "\n"); - MESSAGE_BEGIN (MSG_BROADCAST, FindMessageId (NETMSG_TEXTMSG)); - WRITE_BYTE (HUD_PRINTTALK); - WRITE_STRING ("%s"); - WRITE_STRING (string); - MESSAGE_END (); + MessageWriter (MSG_BROADCAST, getMessageId (NETMSG_TEXTMSG)) + .writeByte (HUD_PRINTTALK) + .writeString (string); } -void Engine::CenterPrintf (const char *fmt, ...) -{ +void Engine::centerPrint (const char *fmt, ...) { va_list ap; char string[MAX_PRINT_BUFFER]; va_start (ap, fmt); - vsnprintf (string, SIZEOF_CHAR (string), TraslateMessage (fmt), ap); + vsnprintf (string, cr::bufsize (string), translate (fmt), ap); va_end (ap); - if (IsDedicatedServer ()) - { - Printf (string); + if (isDedicated ()) { + print (string); return; } strcat (string, "\n"); - MESSAGE_BEGIN (MSG_BROADCAST, SVC_CENTERPRINT); - WRITE_STRING (string); - MESSAGE_END (); + MessageWriter (MSG_BROADCAST, getMessageId (NETMSG_TEXTMSG)) + .writeByte (HUD_PRINTCENTER) + .writeString (string); } -void Engine::ClientPrintf (edict_t *ent, const char *fmt, ...) -{ +void Engine::clientPrint (edict_t *ent, const char *fmt, ...) { va_list ap; char string[MAX_PRINT_BUFFER]; va_start (ap, fmt); - vsnprintf (string, SIZEOF_CHAR (string), TraslateMessage (fmt), ap); + vsnprintf (string, cr::bufsize (string), translate (fmt), ap); va_end (ap); - if (IsDedicatedServer () || IsNullEntity (ent) || ent == g_hostEntity) - { - Printf (string); + if (isNullEntity (ent)) { + print (string); return; } strcat (string, "\n"); g_engfuncs.pfnClientPrintf (ent, print_console, string); } -void Engine::DrawLine (edict_t * ent, const Vector &start, const Vector &end, int width, int noise, int red, int green, int blue, int brightness, int speed, int life, DrawLineType type) -{ +void Engine::drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, int red, int green, int blue, int brightness, int speed, int life, DrawLineType type) { // this function draws a arrow visible from the client side of the player whose player entity // is pointed to by ent, from the vector location start to the vector location end, // which is supposed to last life tenths seconds, and having the color defined by RGB. - if (!IsValidPlayer (ent)) + if (!isPlayer (ent)) { return; // reliability check + } - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, ent); - WRITE_BYTE (TE_BEAMPOINTS); - WRITE_COORD (end.x); - WRITE_COORD (end.y); - WRITE_COORD (end.z); - WRITE_COORD (start.x); - WRITE_COORD (start.y); - WRITE_COORD (start.z); - WRITE_SHORT (m_drawModels[type]); - WRITE_BYTE (0); // framestart - WRITE_BYTE (10); // framerate - WRITE_BYTE (life); // life in 0.1's - WRITE_BYTE (width); // width - WRITE_BYTE (noise); // noise - - WRITE_BYTE (red); // r, g, b - WRITE_BYTE (green); // r, g, b - WRITE_BYTE (blue); // r, g, b - - WRITE_BYTE (brightness); // brightness - WRITE_BYTE (speed); // speed - MESSAGE_END (); + MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, Vector::null (), ent) + .writeByte (TE_BEAMPOINTS) + .writeCoord (end.x) + .writeCoord (end.y) + .writeCoord (end.z) + .writeCoord (start.x) + .writeCoord (start.y) + .writeCoord (start.z) + .writeShort (m_drawModels[type]) + .writeByte (0) // framestart + .writeByte (10) // framerate + .writeByte (life) // life in 0.1's + .writeByte (width) // width + .writeByte (noise) // noise + .writeByte (red) // r, g, b + .writeByte (green) // r, g, b + .writeByte (blue) // r, g, b + .writeByte (brightness) // brightness + .writeByte (speed); // speed } -void Engine::TestLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr) -{ +void Engine::testLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr) { // this function traces a line dot by dot, starting from vecStart in the direction of vecEnd, // ignoring or not monsters (depending on the value of IGNORE_MONSTERS, true or false), and stops // at the first obstacle encountered, returning the results of the trace in the TraceResult structure @@ -170,17 +248,17 @@ void Engine::TestLine (const Vector &start, const Vector &end, int ignoreFlags, int engineFlags = 0; - if (ignoreFlags & TRACE_IGNORE_MONSTERS) + if (ignoreFlags & TRACE_IGNORE_MONSTERS) { engineFlags = 1; + } - if (ignoreFlags & TRACE_IGNORE_GLASS) + if (ignoreFlags & TRACE_IGNORE_GLASS) { engineFlags |= 0x100; - + } g_engfuncs.pfnTraceLine (start, end, engineFlags, ignoreEntity, ptr); } -void Engine::TestHull (const Vector &start, const Vector &end, int ignoreFlags, int hullNumber, edict_t *ignoreEntity, TraceResult *ptr) -{ +void Engine::testHull (const Vector &start, const Vector &end, int ignoreFlags, int hullNumber, edict_t *ignoreEntity, TraceResult *ptr) { // this function traces a hull dot by dot, starting from vecStart in the direction of vecEnd, // ignoring or not monsters (depending on the value of IGNORE_MONSTERS, true or // false), and stops at the first obstacle encountered, returning the results @@ -192,30 +270,27 @@ void Engine::TestHull (const Vector &start, const Vector &end, int ignoreFlags, // function allows to specify whether the trace starts "inside" an entity's polygonal model, // and if so, to specify that entity in ignoreEntity in order to ignore it as an obstacle. - (*g_engfuncs.pfnTraceHull) (start, end, (ignoreFlags & TRACE_IGNORE_MONSTERS), hullNumber, ignoreEntity, ptr); + g_engfuncs.pfnTraceHull (start, end, !!(ignoreFlags & TRACE_IGNORE_MONSTERS), hullNumber, ignoreEntity, ptr); } -float Engine::GetWaveLength (const char *fileName) -{ +float Engine::getWaveLen (const char *fileName) { extern ConVar yb_chatter_path; - const char *filePath = FormatBuffer ("%s/%s/%s.wav", GetModName (), yb_chatter_path.GetString (), fileName); + const char *filePath = format ("%s/%s/%s.wav", getModName (), yb_chatter_path.str (), fileName); File fp (filePath, "rb"); // we're got valid handle? - if (!fp.IsValid ()) + if (!fp.isValid ()) { return 0.0f; - + } // check if we have engine function for this - if (!(g_gameFlags & GAME_XASH_ENGINE) && g_engfuncs.pfnGetApproxWavePlayLen != nullptr) - { - fp.Close (); + if (!(g_gameFlags & GAME_XASH_ENGINE) && g_engfuncs.pfnGetApproxWavePlayLen != nullptr) { + fp.close (); return g_engfuncs.pfnGetApproxWavePlayLen (filePath) / 1000.0f; } // else fuck with manual search - struct WavHeader - { + struct WavHeader { char riffChunkId[4]; unsigned long packageSize; char chunkID[4]; @@ -233,174 +308,169 @@ float Engine::GetWaveLength (const char *fileName) memset (&waveHdr, 0, sizeof (waveHdr)); - if (fp.Read (&waveHdr, sizeof (WavHeader)) == 0) - { - AddLogEntry (true, LL_ERROR, "Wave File %s - has wrong or unsupported format", filePath); + if (fp.read (&waveHdr, sizeof (WavHeader)) == 0) { + logEntry (true, LL_ERROR, "Wave File %s - has wrong or unsupported format", filePath); return 0.0f; } - if (strncmp (waveHdr.chunkID, "WAVE", 4) != 0) - { - AddLogEntry (true, LL_ERROR, "Wave File %s - has wrong wave chunk id", filePath); + if (strncmp (waveHdr.chunkID, "WAVE", 4) != 0) { + logEntry (true, LL_ERROR, "Wave File %s - has wrong wave chunk id", filePath); return 0.0f; } - fp.Close (); + fp.close (); - if (waveHdr.dataChunkLength == 0) - { - AddLogEntry (true, LL_ERROR, "Wave File %s - has zero length!", filePath); + if (waveHdr.dataChunkLength == 0) { + logEntry (true, LL_ERROR, "Wave File %s - has zero length!", filePath); return 0.0f; } return static_cast (waveHdr.dataChunkLength) / static_cast (waveHdr.bytesPerSecond); } -bool Engine::IsDedicatedServer (void) -{ +bool Engine::isDedicated (void) { // return true if server is dedicated server, false otherwise static bool dedicated = g_engfuncs.pfnIsDedicatedServer () > 0; return dedicated; } -const char *Engine::GetModName (void) -{ +const char *Engine::getModName (void) { // this function returns mod name without path - static char engineModName[256]; + static char modname[256]; - g_engfuncs.pfnGetGameDir (engineModName); - int length = strlen (engineModName); + g_engfuncs.pfnGetGameDir (modname); + size_t length = strlen (modname); - int stop = length - 1; - while ((engineModName[stop] == '\\' || engineModName[stop] == '/') && stop > 0) + size_t stop = length - 1; + while ((modname[stop] == '\\' || modname[stop] == '/') && stop > 0) { stop--; + } - int start = stop; - while (engineModName[start] != '\\' && engineModName[start] != '/' && start > 0) + size_t start = stop; + while (modname[start] != '\\' && modname[start] != '/' && start > 0) { start--; + } - if (engineModName[start] == '\\' || engineModName[start] == '/') + if (modname[start] == '\\' || modname[start] == '/') { start++; + } - for (length = start; length <= stop; length++) - engineModName[length - start] = engineModName[length]; - - engineModName[length - start] = 0; // terminate the string - - return &engineModName[0]; + for (length = start; length <= stop; length++) { + modname[length - start] = modname[length]; + } + modname[length - start] = 0; // terminate the string + return &modname[0]; } -const char *Engine::GetMapName (void) -{ +const char *Engine::getMapName (void) { // this function gets the map name and store it in the map_name global string variable. static char engineMap[256]; - strncpy (engineMap, const_cast (g_pGlobals->pStringBase + static_cast (g_pGlobals->mapname)), SIZEOF_CHAR (engineMap)); + strncpy (engineMap, STRING (g_pGlobals->mapname), cr::bufsize (engineMap)); return &engineMap[0]; } -Vector Engine::GetAbsOrigin (edict_t *ent) -{ +Vector Engine::getAbsPos (edict_t *ent) { // this expanded function returns the vector origin of a bounded entity, assuming that any // entity that has a bounding box has its center at the center of the bounding box itself. - if (IsNullEntity (ent)) - return Vector::GetZero (); + if (isNullEntity (ent)) { + return Vector::null (); + } - if (ent->v.origin.IsZero ()) + if (ent->v.origin.empty ()) { return ent->v.absmin + ent->v.size * 0.5f; - + } return ent->v.origin; } -void Engine::RegisterCmd (const char * command, void func (void)) -{ +void Engine::registerCmd (const char *command, void func (void)) { // this function tells the engine that a new server command is being declared, in addition // to the standard ones, whose name is command_name. The engine is thus supposed to be aware // that for every "command_name" server command it receives, it should call the function // pointed to by "function" in order to handle it. // check for hl pre 1.1.0.4, as it's doesn't have pfnAddServerCommand - if (!A_IsValidCodePointer (reinterpret_cast (g_engfuncs.pfnAddServerCommand))) - AddLogEntry (true, LL_FATAL, "YaPB's minimum HL engine version is 1.1.0.4 and minimum Counter-Strike Beta 6.6. Please update your engine version."); - + if (!cr::checkptr (reinterpret_cast (g_engfuncs.pfnAddServerCommand))) { + logEntry (true, LL_FATAL, "YaPB's minimum HL engine version is 1.1.0.4 and minimum Counter-Strike is Beta 6.6. Please update your engine version."); + } g_engfuncs.pfnAddServerCommand (const_cast (command), func); } -void Engine::EmitSound (edict_t *ent, const char *sound) -{ +void Engine::playSound (edict_t *ent, const char *sound) { g_engfuncs.pfnEmitSound (ent, CHAN_WEAPON, sound, 1.0f, ATTN_NORM, 0, 100); } -void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...) -{ +void Engine::execBotCmd (edict_t *ent, const char *fmt, ...) { // the purpose of this function is to provide fakeclients (bots) with the same client // command-scripting advantages (putting multiple commands in one line between semicolons) // as real players. It is an improved version of botman's FakeClientCommand, in which you // supply directly the whole string as if you were typing it in the bot's "console". It // is supposed to work exactly like the pfnClientCommand (server-sided client command). - if (IsNullEntity (ent)) - return; - + if (!isFakeClient (ent)) { + return; + } va_list ap; char string[256]; va_start (ap, fmt); - vsnprintf (string, SIZEOF_CHAR (string), fmt, ap); + vsnprintf (string, cr::bufsize (string), fmt, ap); va_end (ap); - if (IsNullString (string)) + if (isEmptyStr (string)) { return; - + } m_arguments[0] = '\0'; m_argumentCount = 0; m_isBotCommand = true; - int i, pos = 0; - int length = strlen (string); + size_t i, pos = 0; + size_t length = strlen (string); - while (pos < length) - { - int start = pos; - int stop = pos; + while (pos < length) { + size_t start = pos; + size_t stop = pos; - while (pos < length && string[pos] != ';') + while (pos < length && string[pos] != ';') { pos++; + } - if (string[pos - 1] == '\n') + if (pos > 1 && string[pos - 1] == '\n') { stop = pos - 2; - else - stop = pos - 1; + } + else { + stop = pos - 1; + } - for (i = start; i <= stop; i++) + for (i = start; i <= stop; i++) { m_arguments[i - start] = string[i]; - + } m_arguments[i - start] = 0; pos++; - int index = 0; + size_t index = 0; m_argumentCount = 0; - while (index < i - start) - { - while (index < i - start && m_arguments[index] == ' ') - index++; - - if (m_arguments[index] == '"') - { - index++; - - while (index < i - start && m_arguments[index] != '"') - index++; + while (index < i - start) { + while (index < i - start && m_arguments[index] == ' ') { index++; } - else - while (index < i - start && m_arguments[index] != ' ') - index++; + if (m_arguments[index] == '"') { + index++; + while (index < i - start && m_arguments[index] != '"') { + index++; + } + index++; + } + else { + while (index < i - start && m_arguments[index] != ' ') { + index++; + } + } m_argumentCount++; } MDLL_ClientCommand (ent); @@ -411,69 +481,64 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...) m_argumentCount = 0; } -const char *Engine::ExtractSingleField (const char *string, int id) -{ +const char *Engine::getField (const char *string, size_t id) { // this function gets and returns a particular field in a string where several strings are concatenated const int IterBufMax = 4; - static char arg[IterBufMax][256]; + static char arg[IterBufMax][512]; static int iter = -1; - if (iter > IterBufMax - 1) + if (iter > IterBufMax - 1) { iter = 0; + } - char *ptr = arg[++iter]; + char *ptr = arg[cr::clamp (iter++, 0, IterBufMax - 1)]; ptr[0] = 0; - int pos = 0, count = 0, start = 0, stop = 0; - int length = strlen (string); + size_t pos = 0, count = 0, start = 0, stop = 0; + size_t length = strlen (string); - while (pos < length && count <= id) - { - while (pos < length && (string[pos] == ' ' || string[pos] == '\t')) + while (pos < length && count <= id) { + while (pos < length && (string[pos] == ' ' || string[pos] == '\t')) { pos++; - - if (string[pos] == '"') - { + } + if (string[pos] == '"') { pos++; start = pos; - while (pos < length && string[pos] != '"') + while (pos < length && string[pos] != '"') { pos++; - + } stop = pos - 1; pos++; } - else - { + else { start = pos; - while (pos < length && string[pos] != ' ' && string[pos] != '\t') + while (pos < length && string[pos] != ' ' && string[pos] != '\t') { pos++; - + } stop = pos - 1; } - if (count == id) - { - int i = start; + if (count == id) { + size_t i = start; - for (; i <= stop; i++) + for (; i <= stop; i++) { ptr[i - start] = string[i]; - + } ptr[i - start] = 0; break; } count++; // we have parsed one field more } - String::TrimExternalBuffer (ptr); + String::trimChars (ptr); return ptr; } -void Engine::IssueCmd (const char *fmt, ...) -{ +void Engine::execCmd (const char *fmt, ...) { // this function asks the engine to execute a server command va_list ap; @@ -481,15 +546,14 @@ void Engine::IssueCmd (const char *fmt, ...) // concatenate all the arguments in one string va_start (ap, fmt); - vsnprintf (string, SIZEOF_CHAR (string), fmt, ap); + vsnprintf (string, cr::bufsize (string), fmt, ap); va_end (ap); strcat (string, "\n"); g_engfuncs.pfnServerCommand (string); } -void Engine::PushVariableToStack (const char *variable, const char *value, VarType varType, bool regMissing, const char *regVal, ConVar *self) -{ +void Engine::pushVarToRegStack (const char *variable, const char *value, VarType varType, bool regMissing, const char *regVal, ConVar *self) { // this function adds globally defined variable to registration stack VarPair pair; @@ -502,109 +566,75 @@ void Engine::PushVariableToStack (const char *variable, const char *value, VarTy int engineFlags = FCVAR_EXTDLL; - if (varType == VT_NORMAL) + if (varType == VT_NORMAL) { engineFlags |= FCVAR_SERVER; - else if (varType == VT_READONLY) + } + else if (varType == VT_READONLY) { engineFlags |= FCVAR_SERVER | FCVAR_SPONLY | FCVAR_PRINTABLEONLY; - else if (varType == VT_PASSWORD) + } + else if (varType == VT_PASSWORD) { engineFlags |= FCVAR_PROTECTED; + } pair.reg.flags = engineFlags; pair.self = self; pair.type = varType; - m_cvars.Push (pair); + m_cvars.push (pair); } -void Engine::PushRegisteredConVarsToEngine (bool gameVars) -{ +void Engine::pushRegStackToEngine (bool gameVars) { // this function pushes all added global variables to engine registration - FOR_EACH_AE (m_cvars, i) - { - VarPair *ptr = &m_cvars[i]; + for (auto &var : m_cvars) { + ConVar &self = *var.self; + cvar_t ® = var.reg; - if (ptr->type != VT_NOREGISTER) - { - ptr->self->m_eptr = g_engfuncs.pfnCVarGetPointer (ptr->reg.name); + if (var.type != VT_NOREGISTER) { + self.m_eptr = g_engfuncs.pfnCVarGetPointer (reg.name); - if (ptr->self->m_eptr == nullptr) - { - g_engfuncs.pfnCVarRegister (&ptr->reg); - ptr->self->m_eptr = g_engfuncs.pfnCVarGetPointer (ptr->reg.name); + if (self.m_eptr == nullptr) { + g_engfuncs.pfnCVarRegister (&var.reg); + self.m_eptr = g_engfuncs.pfnCVarGetPointer (reg.name); } } - else if (gameVars && ptr->type == VT_NOREGISTER) - { - ptr->self->m_eptr = g_engfuncs.pfnCVarGetPointer (ptr->reg.name); + else if (gameVars) { + self.m_eptr = g_engfuncs.pfnCVarGetPointer (reg.name); - if (ptr->regMissing && ptr->self->m_eptr == nullptr) - { - if (ptr->reg.string == nullptr && ptr->regVal != nullptr) - { - ptr->reg.string = const_cast (ptr->regVal); - ptr->reg.flags |= FCVAR_SERVER; + if (var.regMissing && self.m_eptr == nullptr) { + if (reg.string == nullptr && var.regVal != nullptr) { + reg.string = const_cast (var.regVal); + reg.flags |= FCVAR_SERVER; } - g_engfuncs.pfnCVarRegister (&ptr->reg); - ptr->self->m_eptr = g_engfuncs.pfnCVarGetPointer (ptr->reg.name); + g_engfuncs.pfnCVarRegister (&var.reg); + self.m_eptr = g_engfuncs.pfnCVarGetPointer (reg.name); + } + + if (!self.m_eptr) { + print ("Got nullptr on cvar %s!", reg.name); } - - if (!ptr->self->m_eptr) - engine.Printf ("Got nullptr on cvar %s!", ptr->reg.name); } } } -char *Engine::TraslateMessage (const char *input) -{ +const char *Engine::translate (const char *input) { // this function translate input string into needed language - if (IsDedicatedServer ()) - return const_cast (&input[0]); - - static char string[MAX_PRINT_BUFFER] = { 0, }; - const char *ptr = input + strlen (input) - 1; - - while (ptr > input && *ptr == '\n') - ptr--; - - if (ptr != input) - ptr++; - - strncpy (string, input, SIZEOF_CHAR (string)); - String::TrimExternalBuffer (string); - - FOR_EACH_AE (m_language, i) - { - if (strcmp (string, m_language[i].original) == 0) - { - strncpy (string, m_language[i].translated, SIZEOF_CHAR (string)); - - if (ptr != input) - strncat (string, ptr, MAX_PRINT_BUFFER - 1 - strlen (string)); - - return &string[0]; - } + if (isDedicated ()) { + return input; } - return const_cast (&input[0]); // nothing found + static String result; + + if (m_language.get (input, result)) { + return result.chars (); + } + return input; // nothing found } -void Engine::TerminateTranslator (void) -{ - // this function terminates language translator and frees all memory - - FOR_EACH_AE (m_language, it) - { - delete[] m_language[it].original; - delete[] m_language[it].translated; - } - m_language.RemoveAll (); -} - -void Engine::ProcessMessageCapture (void *ptr) -{ - if (m_msgBlock.msg == NETMSG_UNDEFINED) +void Engine::processMessages (void *ptr) { + if (m_msgBlock.msg == NETMSG_UNDEFINED) { return; + } // some needed variables static uint8 r, g, b; @@ -619,22 +649,19 @@ void Engine::ProcessMessageCapture (void *ptr) static WeaponProperty weaponProp; // some widely used stuff - Bot *bot = bots.GetBot (m_msgBlock.bot); + Bot *bot = bots.getBot (m_msgBlock.bot); char *strVal = reinterpret_cast (ptr); int intVal = *reinterpret_cast (ptr); uint8 byteVal = *reinterpret_cast (ptr); // now starts of network message execution - switch (m_msgBlock.msg) - { + switch (m_msgBlock.msg) { case NETMSG_VGUI: // this message is sent when a VGUI menu is displayed. - if (m_msgBlock.state == 0) - { - switch (intVal) - { + if (bot != nullptr && m_msgBlock.state == 0) { + switch (intVal) { case VMS_TEAM: bot->m_startAction = GAME_MSG_TEAM_SELECT; break; @@ -650,35 +677,43 @@ void Engine::ProcessMessageCapture (void *ptr) case NETMSG_SHOWMENU: // this message is sent when a text menu is displayed. - if (m_msgBlock.state < 3) // ignore first 3 fields of message + // ignore first 3 fields of message + if (m_msgBlock.state < 3 || bot == nullptr) { break; + } - if (strcmp (strVal, "#Team_Select") == 0) // team select menu? + if (strcmp (strVal, "#Team_Select") == 0) { bot->m_startAction = GAME_MSG_TEAM_SELECT; - else if (strcmp (strVal, "#Team_Select_Spect") == 0) // team select menu? + } + else if (strcmp (strVal, "#Team_Select_Spect") == 0) { bot->m_startAction = GAME_MSG_TEAM_SELECT; - else if (strcmp (strVal, "#IG_Team_Select_Spect") == 0) // team select menu? + } + else if (strcmp (strVal, "#IG_Team_Select_Spect") == 0) { bot->m_startAction = GAME_MSG_TEAM_SELECT; - else if (strcmp (strVal, "#IG_Team_Select") == 0) // team select menu? + } + else if (strcmp (strVal, "#IG_Team_Select") == 0) { bot->m_startAction = GAME_MSG_TEAM_SELECT; - else if (strcmp (strVal, "#IG_VIP_Team_Select") == 0) // team select menu? + } + else if (strcmp (strVal, "#IG_VIP_Team_Select") == 0) { bot->m_startAction = GAME_MSG_TEAM_SELECT; - else if (strcmp (strVal, "#IG_VIP_Team_Select_Spect") == 0) // team select menu? + } + else if (strcmp (strVal, "#IG_VIP_Team_Select_Spect") == 0) { bot->m_startAction = GAME_MSG_TEAM_SELECT; - else if (strcmp (strVal, "#Terrorist_Select") == 0) // T model select? + } + else if (strcmp (strVal, "#Terrorist_Select") == 0) { bot->m_startAction = GAME_MSG_CLASS_SELECT; - else if (strcmp (strVal, "#CT_Select") == 0) // CT model select menu? + } + else if (strcmp (strVal, "#CT_Select") == 0) { bot->m_startAction = GAME_MSG_CLASS_SELECT; - + } break; case NETMSG_WEAPONLIST: // this message is sent when a client joins the game. All of the weapons are sent with the weapon ID and information about what ammo is used. - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: - strncpy (weaponProp.className, strVal, SIZEOF_CHAR (weaponProp.className)); + strncpy (weaponProp.className, strVal, cr::bufsize (weaponProp.className)); break; case 1: @@ -711,8 +746,7 @@ void Engine::ProcessMessageCapture (void *ptr) case NETMSG_CURWEAPON: // this message is sent when a weapon is selected (either by the bot chosing a weapon or by the server auto assigning the bot a weapon). In CS it's also called when Ammo is increased/decreased - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: state = intVal; // state of the current weapon (WTF???) break; @@ -724,15 +758,15 @@ void Engine::ProcessMessageCapture (void *ptr) case 2: clip = intVal; // ammo currently in the clip for this weapon - if (id <= 31) - { - if (state != 0) + if (bot != nullptr && id <= 31) { + if (state != 0) { bot->m_currentWeapon = id; + } // ammo amount decreased ? must have fired a bullet... - if (id == bot->m_currentWeapon && bot->m_ammoInClip[id] > clip) - bot->m_timeLastFired = Time (); // remember the last bullet time - + if (id == bot->m_currentWeapon && bot->m_ammoInClip[id] > clip) { + bot->m_timeLastFired = timebase (); // remember the last bullet time + } bot->m_ammoInClip[id] = clip; } break; @@ -742,14 +776,15 @@ void Engine::ProcessMessageCapture (void *ptr) case NETMSG_AMMOX: // this message is sent whenever ammo amounts are adjusted (up or down). NOTE: Logging reveals that CS uses it very unreliable! - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: index = intVal; // ammo index (for type of ammo) break; case 1: - bot->m_ammo[index] = intVal; // store it away + if (bot != nullptr) { + bot->m_ammo[index] = intVal; // store it away + } break; } break; @@ -759,14 +794,15 @@ void Engine::ProcessMessageCapture (void *ptr) // not really necessary except it allows the HUD to draw pictures of ammo that have been picked up. The bots // don't really need pictures since they don't have any eyes anyway. - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: index = intVal; break; case 1: - bot->m_ammo[index] = intVal; + if (bot != nullptr) { + bot->m_ammo[index] = intVal; + } break; } break; @@ -774,8 +810,7 @@ void Engine::ProcessMessageCapture (void *ptr) case NETMSG_DAMAGE: // this message gets sent when the bots are getting damaged. - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: damageArmor = intVal; break; @@ -787,8 +822,9 @@ void Engine::ProcessMessageCapture (void *ptr) case 2: damageBits = intVal; - if (bot != nullptr && (damageArmor > 0 || damageTaken > 0)) - bot->GetDamage (bot->pev->dmg_inflictor, damageTaken, damageArmor, damageBits); + if (bot != nullptr && (damageArmor > 0 || damageTaken > 0)) { + bot->processDamage (bot->pev->dmg_inflictor, damageTaken, damageArmor, damageBits); + } break; } break; @@ -796,39 +832,41 @@ void Engine::ProcessMessageCapture (void *ptr) case NETMSG_MONEY: // this message gets sent when the bots money amount changes - if (m_msgBlock.state == 0) + if (bot != nullptr && m_msgBlock.state == 0) { bot->m_moneyAmount = intVal; // amount of money + } break; case NETMSG_STATUSICON: - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: enabled = byteVal; break; case 1: - if (strcmp (strVal, "defuser") == 0) - bot->m_hasDefuser = (enabled != 0); - else if (strcmp (strVal, "buyzone") == 0) - { - bot->m_inBuyZone = (enabled != 0); + if (bot != nullptr) { + if (strcmp (strVal, "defuser") == 0) { + bot->m_hasDefuser = (enabled != 0); + } + else if (strcmp (strVal, "buyzone") == 0) { + bot->m_inBuyZone = (enabled != 0); - // try to equip in buyzone - bot->EquipInBuyzone (BUYSTATE_PRIMARY_WEAPON); + // try to equip in buyzone + bot->processBuyzoneEntering (BUYSTATE_PRIMARY_WEAPON); + } + else if (strcmp (strVal, "vipsafety") == 0) { + bot->m_inVIPZone = (enabled != 0); + } + else if (strcmp (strVal, "c4") == 0) { + bot->m_inBombZone = (enabled == 2); + } } - else if (strcmp (strVal, "vipsafety") == 0) - bot->m_inVIPZone = (enabled != 0); - else if (strcmp (strVal, "c4") == 0) - bot->m_inBombZone = (enabled == 2); - break; } break; case NETMSG_DEATH: // this message sends on death - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: killerIndex = intVal; break; @@ -838,65 +876,60 @@ void Engine::ProcessMessageCapture (void *ptr) break; case 2: - bots.SetDeathMsgState (true); + bots.updateDeathMsgState (true); - if (killerIndex != 0 && killerIndex != victimIndex) - { - edict_t *killer = EntityOfIndex (killerIndex); - edict_t *victim = EntityOfIndex (victimIndex); + if (killerIndex != 0 && killerIndex != victimIndex) { + edict_t *killer = entityOfIndex (killerIndex); + edict_t *victim = entityOfIndex (victimIndex); - if (IsNullEntity (killer) || IsNullEntity (victim)) + if (isNullEntity (killer) || isNullEntity (victim)) { break; + } - if (yb_communication_type.GetInt () == 2) - { + if (yb_communication_type.integer () == 2) { // need to send congrats on well placed shot - for (int i = 0; i < MaxClients (); i++) - { - Bot *notify = bots.GetBot (i); - - if (notify != nullptr && notify->m_notKilled && killer != notify->GetEntity () && notify->EntityIsVisible (victim->v.origin) && GetTeam (killer) == notify->m_team && GetTeam (killer) != GetTeam (victim)) - { - if (killer == g_hostEntity) - notify->HandleChatterMessage ("#Bot_NiceShotCommander"); - else - notify->HandleChatterMessage ("#Bot_NiceShotPall"); + for (int i = 0; i < maxClients (); i++) { + Bot *notify = bots.getBot (i); + if (notify != nullptr && notify->m_notKilled && killer != notify->ent () && notify->seesEntity (victim->v.origin) && getTeam (killer) == notify->m_team && getTeam (killer) != getTeam (victim)) { + if (killer == g_hostEntity) { + notify->processChatterMessage ("#Bot_NiceShotCommander"); + } + else { + notify->processChatterMessage ("#Bot_NiceShotPall"); + } break; } } } // notice nearby to victim teammates, that attacker is near - for (int i = 0; i < MaxClients (); i++) - { - Bot *notify = bots.GetBot (i); + for (int i = 0; i < maxClients (); i++) { + Bot *notify = bots.getBot (i); - if (notify != nullptr && notify->m_seeEnemyTime + 2.0f < Time () && notify->m_notKilled && notify->m_team == GetTeam (victim) && IsVisible (killer->v.origin, notify->GetEntity ()) && IsNullEntity (notify->m_enemy) && GetTeam (killer) != GetTeam (victim)) - { + if (notify != nullptr && notify->m_seeEnemyTime + 2.0f < timebase () && notify->m_notKilled && notify->m_team == getTeam (victim) && isVisible (killer->v.origin, notify->ent ()) && isNullEntity (notify->m_enemy) && getTeam (killer) != getTeam (victim)) { notify->m_actualReactionTime = 0.0f; - notify->m_seeEnemyTime = Time (); + notify->m_seeEnemyTime = timebase (); notify->m_enemy = killer; notify->m_lastEnemy = killer; notify->m_lastEnemyOrigin = killer->v.origin; } } - Bot *notify = bots.GetBot (killer); + Bot *notify = bots.getBot (killer); // is this message about a bot who killed somebody? - if (notify != nullptr) + if (notify != nullptr) { notify->m_lastVictim = victim; - + } else // did a human kill a bot on his team? { - Bot *target = bots.GetBot (victim); + Bot *target = bots.getBot (victim); - if (target != nullptr) - { - if (GetTeam (killer) == GetTeam (victim)) + if (target != nullptr) { + if (getTeam (killer) == getTeam (victim)) { target->m_voteKickIndex = killerIndex; - + } target->m_notKilled = false; } } @@ -906,8 +939,7 @@ void Engine::ProcessMessageCapture (void *ptr) break; case NETMSG_SCREENFADE: // this message gets sent when the screen fades (flashbang) - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 3: r = byteVal; break; @@ -921,159 +953,153 @@ void Engine::ProcessMessageCapture (void *ptr) break; case 6: - if (bot != nullptr && r >= 255 && g >= 255 && b >= 255 && byteVal > 170) - bot->GotBlind (byteVal); + if (bot != nullptr && r >= 255 && g >= 255 && b >= 255 && byteVal > 170) { + bot->processBlind (byteVal); + } break; } break; case NETMSG_HLTV: // round restart in steam cs - switch (m_msgBlock.state) - { + switch (m_msgBlock.state) { case 0: numPlayers = intVal; break; case 1: - if (numPlayers == 0 && intVal == 0) - RoundInit (); + if (numPlayers == 0 && intVal == 0) { + initRound (); + } break; } break; - case NETMSG_TEXTMSG: - if (m_msgBlock.state == 1) - { - if (FStrEq (strVal, "#CTs_Win") || - FStrEq (strVal, "#Bomb_Defused") || - FStrEq (strVal, "#Terrorists_Win") || - FStrEq (strVal, "#Round_Draw") || - FStrEq (strVal, "#All_Hostages_Rescued") || - FStrEq (strVal, "#Target_Saved") || - FStrEq (strVal, "#Hostages_Not_Rescued") || - FStrEq (strVal, "#Terrorists_Not_Escaped") || - FStrEq (strVal, "#VIP_Not_Escaped") || - FStrEq (strVal, "#Escaping_Terrorists_Neutralized") || - FStrEq (strVal, "#VIP_Assassinated") || - FStrEq (strVal, "#VIP_Escaped") || - FStrEq (strVal, "#Terrorists_Escaped") || - FStrEq (strVal, "#CTs_PreventEscape") || - FStrEq (strVal, "#Target_Bombed") || - FStrEq (strVal, "#Game_Commencing") || - FStrEq (strVal, "#Game_will_restart_in")) - { + if (m_msgBlock.state == 1) { + if (strcmp (strVal, "#CTs_Win") == 0 || + strcmp (strVal, "#Bomb_Defused") == 0 || + strcmp (strVal, "#Terrorists_Win") == 0 || + strcmp (strVal, "#Round_Draw") == 0 || + strcmp (strVal, "#All_Hostages_Rescued") == 0 || + strcmp (strVal, "#Target_Saved") == 0 || + strcmp (strVal, "#Hostages_Not_Rescued") == 0 || + strcmp (strVal, "#Terrorists_Not_Escaped") == 0 || + strcmp (strVal, "#VIP_Not_Escaped") == 0 || + strcmp (strVal, "#Escaping_Terrorists_Neutralized") == 0 || + strcmp (strVal, "#VIP_Assassinated") == 0 || + strcmp (strVal, "#VIP_Escaped") == 0 || + strcmp (strVal, "#Terrorists_Escaped") == 0 || + strcmp (strVal, "#CTs_PreventEscape") == 0 || + strcmp (strVal, "#Target_Bombed") == 0 || + strcmp (strVal, "#Game_Commencing") == 0 || + strcmp (strVal, "#Game_will_restart_in") == 0) { g_roundEnded = true; - if (FStrEq (strVal, "#Game_Commencing")) + if (strcmp (strVal, "#Game_Commencing") == 0) { g_gameWelcomeSent = true; + } - if (FStrEq (strVal, "#CTs_Win")) - { - bots.SetLastWinner (CT); // update last winner for economics + if (strcmp (strVal, "#CTs_Win") == 0) { + bots.setLastWinner (TEAM_COUNTER); // update last winner for economics - if (yb_communication_type.GetInt () == 2) - { - Bot *notify = bots.GetAliveBot (); + if (yb_communication_type.integer () == 2) { + Bot *notify = bots.getAliveBot (); - if (notify != nullptr && notify->m_notKilled) - notify->HandleChatterMessage (strVal); + if (notify != nullptr && notify->m_notKilled) { + notify->processChatterMessage (strVal); + } } } - if (FStrEq (strVal, "#Game_will_restart_in")) - { - bots.CheckTeamEconomics (CT, true); - bots.CheckTeamEconomics (TERRORIST, true); + if (strcmp (strVal, "#Game_will_restart_in") == 0) { + bots.updateTeamEconomics (TEAM_COUNTER, true); + bots.updateTeamEconomics (TEAM_TERRORIST, true); } - if (FStrEq (strVal, "#Terrorists_Win")) - { - bots.SetLastWinner (TERRORIST); // update last winner for economics + if (strcmp (strVal, "#Terrorists_Win") == 0) { + bots.setLastWinner (TEAM_TERRORIST); // update last winner for economics - if (yb_communication_type.GetInt () == 2) - { - Bot *notify = bots.GetAliveBot (); + if (yb_communication_type.integer () == 2) { + Bot *notify = bots.getAliveBot (); - if (notify != nullptr && notify->m_notKilled) - notify->HandleChatterMessage (strVal); + if (notify != nullptr && notify->m_notKilled) { + notify->processChatterMessage (strVal); + } } } - waypoints.SetBombPosition (true); + waypoints.setBombPos (true); } - else if (!g_bombPlanted && FStrEq (strVal, "#Bomb_Planted")) - { + else if (!g_bombPlanted && strcmp (strVal, "#Bomb_Planted") == 0) { g_bombPlanted = g_bombSayString = true; - g_timeBombPlanted = Time (); + g_timeBombPlanted = timebase (); - for (int i = 0; i < MaxClients (); i++) - { - Bot *notify = bots.GetBot (i); + for (int i = 0; i < maxClients (); i++) { + Bot *notify = bots.getBot (i); - if (notify != nullptr && notify->m_notKilled) - { - notify->DeleteSearchNodes (); - notify->ResetTasks (); + if (notify != nullptr && notify->m_notKilled) { + notify->clearSearchNodes (); + notify->clearTasks (); - if (yb_communication_type.GetInt () == 2 && Random.Int (0, 100) < 75 && notify->m_team == CT) - notify->ChatterMessage (Chatter_WhereIsTheBomb); + if (yb_communication_type.integer () == 2 && rng.getInt (0, 100) < 55 && notify->m_team == TEAM_COUNTER) { + notify->pushChatterMessage (CHATTER_WHERE_IS_THE_BOMB); + } } } - waypoints.SetBombPosition (); + waypoints.setBombPos (); } - else if (bot != nullptr && FStrEq (strVal, "#Switch_To_BurstFire")) + else if (bot != nullptr && strcmp (strVal, "#Switch_To_BurstFire") == 0) { bot->m_weaponBurstMode = BM_ON; - else if (bot != nullptr && FStrEq (strVal, "#Switch_To_SemiAuto")) + } + else if (bot != nullptr && strcmp (strVal, "#Switch_To_SemiAuto") == 0) { bot->m_weaponBurstMode = BM_OFF; + } } break; - case NETMSG_SCOREINFO: - switch (m_msgBlock.state) - { + case NETMSG_TEAMINFO: + switch (m_msgBlock.state) { case 0: playerIndex = intVal; break; - case 4: - if (playerIndex >= 0 && playerIndex <= MaxClients ()) - { - Client &client = g_clients[playerIndex - 1]; + case 1: + if (playerIndex >= 0 && playerIndex <= maxClients ()) { + int team = TEAM_UNASSIGNED; - if (intVal == 1) - client.team2 = TERRORIST; - else if (intVal == 2) - client.team2 = CT; - else - client.team2 = SPECTATOR; + if (strVal[0] == 'U' && strVal[1] == 'N') { + team = TEAM_UNASSIGNED; + } + else if (strVal[0] == 'T' && strVal[1] == 'E') { + team = TEAM_TERRORIST; + } + else if (strVal[0] == 'C' && strVal[1] == 'T') { + team = TEAM_COUNTER; + } + else if (strVal[0] == 'S' && strVal[1] == 'P') { + team = TEAM_SPECTATOR; + } + auto &client = g_clients[playerIndex - 1]; - if (g_gameFlags & GAME_CSDM_FFA) - client.team = playerIndex; - else - client.team = client.team2; + client.team2 = team; + client.team = (g_gameFlags & GAME_CSDM_FFA) ? playerIndex : team; } break; } break; case NETMSG_BARTIME: - if (m_msgBlock.state == 0) - { - if (intVal > 0) + if (bot != nullptr && m_msgBlock.state == 0) { + if (intVal > 0) { bot->m_hasProgressBar = true; // the progress bar on a hud - else if (intVal == 0) + } + else if (intVal == 0) { bot->m_hasProgressBar = false; // no progress bar or disappeared + } } break; default: - AddLogEntry (true, LL_FATAL, "Network message handler error. Call to unrecognized message id (%d).\n", m_msgBlock.msg); + logEntry (true, LL_FATAL, "Network message handler error. Call to unrecognized message id (%d).\n", m_msgBlock.msg); } m_msgBlock.state++; // and finally update network message state -} - -// console var registrator -ConVar::ConVar (const char *name, const char *initval, VarType type, bool regMissing, const char *regVal) : m_eptr (nullptr) -{ - engine.PushVariableToStack (name, initval, type, regMissing, regVal, this); -} +} \ No newline at end of file diff --git a/source/globals.cpp b/source/globals.cpp index 236f039..e87fd8c 100644 --- a/source/globals.cpp +++ b/source/globals.cpp @@ -4,10 +4,10 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include bool g_canSayBombPlanted = true; bool g_roundEnded = true; @@ -27,25 +27,23 @@ float g_timeRoundMid = 0.0f; float g_timeNextBombUpdate = 0.0f; float g_timeBombPlanted = 0.0f; float g_timePerSecondUpdate = 0.0f; -float g_lastRadioTime[2] = {0.0f, 0.0f}; +float g_lastRadioTime[MAX_TEAM_COUNT] = { 0.0f, }; float g_autoPathDistance = 250.0f; -int g_lastRadio[2]; +int g_lastRadio[MAX_TEAM_COUNT]; int g_storeAddbotVars[4]; int g_radioSelect[MAX_ENGINE_PLAYERS]; int g_gameFlags = 0; -int g_numWaypoints = 0; -int g_mapType = 0; +int g_mapFlags = 0; int g_highestDamageCT = 1; int g_highestDamageT = 1; int g_highestKills = 1; -Array > g_chatFactory; -Array > g_chatterFactory; -Array g_botNames; -Array g_replyFactory; -RandomSequenceOfUnique Random; +Array g_chatFactory; +Array> g_chatterFactory; +Array g_botNames; +Array g_replyFactory; Library *g_gameLib = nullptr; meta_globals_t *gpMetaGlobals = nullptr; @@ -63,31 +61,15 @@ globalvars_t *g_pGlobals = nullptr; Experience *g_experienceData = nullptr; // default tables for personality weapon preferences, overridden by weapons.cfg -int g_normalWeaponPrefs[NUM_WEAPONS] = - {0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16}; - -int g_rusherWeaponPrefs[NUM_WEAPONS] = - {0, 2, 1, 4, 5, 6, 3, 24, 19, 22, 23, 20, 21, 10, 12, 13, 7, 8, 11, 9, 18, 17, 19, 25, 15, 16}; - -int g_carefulWeaponPrefs[NUM_WEAPONS] = - {0, 2, 1, 4, 25, 6, 3, 7, 8, 12, 10, 13, 11, 9, 24, 18, 14, 17, 16, 15, 19, 20, 21, 22, 23, 5}; - -int g_grenadeBuyPrecent[NUM_WEAPONS - 23] = - {95, 85, 60}; - -int g_botBuyEconomyTable[NUM_WEAPONS - 15] = - {1900, 2100, 2100, 4000, 6000, 7000, 16000, 1200, 800, 1000, 3000}; - -int *g_weaponPrefs[] = -{ - g_normalWeaponPrefs, - g_rusherWeaponPrefs, - g_carefulWeaponPrefs -}; +int g_normalWeaponPrefs[NUM_WEAPONS] = {0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16}; +int g_rusherWeaponPrefs[NUM_WEAPONS] = {0, 2, 1, 4, 5, 6, 3, 24, 19, 22, 23, 20, 21, 10, 12, 13, 7, 8, 11, 9, 18, 17, 19, 25, 15, 16}; +int g_carefulWeaponPrefs[NUM_WEAPONS] = {0, 2, 1, 4, 25, 6, 3, 7, 8, 12, 10, 13, 11, 9, 24, 18, 14, 17, 16, 15, 19, 20, 21, 22, 23, 5}; +int g_grenadeBuyPrecent[NUM_WEAPONS - 23] = {95, 85, 60}; +int g_botBuyEconomyTable[NUM_WEAPONS - 15] = {1900, 2100, 2100, 4000, 6000, 7000, 16000, 1200, 800, 1000, 3000}; +int *g_weaponPrefs[] = {g_normalWeaponPrefs, g_rusherWeaponPrefs, g_carefulWeaponPrefs}; // metamod plugin information -plugin_info_t Plugin_info = -{ +plugin_info_t Plugin_info = { META_INTERFACE_VERSION, // interface version PRODUCT_NAME, // plugin name PRODUCT_VERSION, // plugin version @@ -99,304 +81,291 @@ plugin_info_t Plugin_info = PT_ANYTIME, // when unloadable }; -// table with all available actions for the bots (filtered in & out in Bot::SetConditions) some of them have subactions included -TaskItem g_taskFilters[] = -{ - {TASK_NORMAL, 0, -1, 0.0f, true}, - {TASK_PAUSE, 0, -1, 0.0f, false}, - {TASK_MOVETOPOSITION, 0, -1, 0.0f, true}, - {TASK_FOLLOWUSER, 0, -1, 0.0f, true}, - {TASK_WAITFORGO, 0, -1, 0.0f, true}, - {TASK_PICKUPITEM, 0, -1, 0.0f, true}, - {TASK_CAMP, 0, -1, 0.0f, true}, - {TASK_PLANTBOMB, 0, -1, 0.0f, false}, - {TASK_DEFUSEBOMB, 0, -1, 0.0f, false}, - {TASK_ATTACK, 0, -1, 0.0f, false}, - {TASK_HUNTENEMY, 0, -1, 0.0f, false}, - {TASK_SEEKCOVER, 0, -1, 0.0f, false}, - {TASK_THROWHEGRENADE, 0, -1, 0.0f, false}, - {TASK_THROWFLASHBANG, 0, -1, 0.0f, false}, - {TASK_THROWSMOKE, 0, -1, 0.0f, false}, - {TASK_DOUBLEJUMP, 0, -1, 0.0f, false}, - {TASK_ESCAPEFROMBOMB, 0, -1, 0.0f, false}, - {TASK_SHOOTBREAKABLE, 0, -1, 0.0f, false}, - {TASK_HIDE, 0, -1, 0.0f, false}, - {TASK_BLINDED, 0, -1, 0.0f, false}, - {TASK_SPRAY, 0, -1, 0.0f, false} +// table with all available actions for the bots (filtered in & out in Bot::setConditions) some of them have subactions included +Task g_taskFilters[TASK_MAX] = { + { TASK_NORMAL, 0, INVALID_WAYPOINT_INDEX, 0.0f, true }, + { TASK_PAUSE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_MOVETOPOSITION, 0, INVALID_WAYPOINT_INDEX, 0.0f, true }, + { TASK_FOLLOWUSER, 0, INVALID_WAYPOINT_INDEX, 0.0f, true }, + { TASK_PICKUPITEM, 0, INVALID_WAYPOINT_INDEX, 0.0f, true }, + { TASK_CAMP, 0, INVALID_WAYPOINT_INDEX, 0.0f, true }, + { TASK_PLANTBOMB, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_DEFUSEBOMB, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_ATTACK, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_HUNTENEMY, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_SEEKCOVER, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_THROWHEGRENADE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_THROWFLASHBANG, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_THROWSMOKE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_DOUBLEJUMP, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_ESCAPEFROMBOMB, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_SHOOTBREAKABLE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_HIDE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_BLINDED, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }, + { TASK_SPRAY, 0, INVALID_WAYPOINT_INDEX, 0.0f, false } }; // weapons and their specifications -WeaponSelect g_weaponSelect[NUM_WEAPONS + 1] = -{ - {WEAPON_KNIFE, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, true }, - {WEAPON_USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, false}, - {WEAPON_GLOCK, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, false}, - {WEAPON_DEAGLE, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, false}, - {WEAPON_P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0, false}, - {WEAPON_ELITE, "weapon_elite", "elite.mdl", 1000, 1, 0, 0, 1, 5, 5, 5, 0, false}, - {WEAPON_FIVESEVEN, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, false}, - {WEAPON_M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, false}, - {WEAPON_XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, false}, - {WEAPON_MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, true }, - {WEAPON_TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, true }, - {WEAPON_P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, true }, - {WEAPON_MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, true }, - {WEAPON_UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, true }, - {WEAPON_AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, true }, - {WEAPON_SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, true }, - {WEAPON_M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, true }, - {WEAPON_GALIL, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, true }, - {WEAPON_FAMAS, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, true }, - {WEAPON_AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, true }, - {WEAPON_SCOUT, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, false}, - {WEAPON_AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, false}, - {WEAPON_G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, false}, - {WEAPON_SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, false}, - {WEAPON_M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, true }, - {WEAPON_SHIELD, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, false}, - {0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, false} +WeaponSelect g_weaponSelect[NUM_WEAPONS + 1] = { + { WEAPON_KNIFE, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, true }, + { WEAPON_USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, false }, + { WEAPON_GLOCK, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, false }, + { WEAPON_DEAGLE, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, false }, + { WEAPON_P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0 , false }, + { WEAPON_ELITE, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, false }, + { WEAPON_FIVESEVEN, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, false }, + { WEAPON_M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, false }, + { WEAPON_XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, false }, + { WEAPON_MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, true }, + { WEAPON_TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, true }, + { WEAPON_P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, true }, + { WEAPON_MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, true }, + { WEAPON_UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, true }, + { WEAPON_AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, true }, + { WEAPON_SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, true }, + { WEAPON_M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, true }, + { WEAPON_GALIL, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, true }, + { WEAPON_FAMAS, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, true }, + { WEAPON_AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, true }, + { WEAPON_SCOUT, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, false }, + { WEAPON_AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, false }, + { WEAPON_G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, false }, + { WEAPON_SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, false }, + { WEAPON_M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, true }, + { WEAPON_SHIELD, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, false }, + { 0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, false } }; -void SetupBotMenus (void) -{ +void setupBotMenus (void) { int counter = 0; + auto buildKeys = [](int numKeys) { + int keys = 0; + + for (int i = 0; i < numKeys; i++) { + keys |= (1 << i); + } + keys |= (1 << 9); + + return keys; + }; + // bots main menu - g_menus[counter] = - { - BOT_MENU_MAIN, 0x2ff, - "\\yMain Menu\\w\v\v" - "1. Control Bots\v" - "2. Features\v\v" - "3. Fill Server\v" - "4. End Round\v\v" + g_menus[counter] = { + BOT_MENU_MAIN, buildKeys (4), + "\\yMain Menu\\w\n\n" + "1. Control Bots\n" + "2. Features\n\n" + "3. Fill Server\n" + "4. End Round\n\n" "0. Exit" }; // bots features menu - g_menus[++counter] = - { - BOT_MENU_FEATURES, 0x25f, - "\\yBots Features\\w\v\v" - "1. Weapon Mode Menu\v" - "2. Waypoint Menu\v" - "3. Select Personality\v\v" - "4. Toggle Debug Mode\v" - "5. Command Menu\v\v" + g_menus[++counter] = { + BOT_MENU_FEATURES, buildKeys (5), + "\\yBots Features\\w\n\n" + "1. Weapon Mode Menu\n" + "2. Waypoint Menu\n" + "3. Select Personality\n\n" + "4. Toggle Debug Mode\n" + "5. Command Menu\n\n" "0. Exit" }; // bot control menu - g_menus[++counter] = - { - BOT_MENU_CONTROL, 0x2ff, - "\\yBots Control Menu\\w\v\v" - "1. Add a Bot, Quick\v" - "2. Add a Bot, Specified\v\v" - "3. Remove Random Bot\v" - "4. Remove All Bots\v\v" - "5. Remove Bot Menu\v\v" + g_menus[++counter] = { + BOT_MENU_CONTROL, buildKeys (5), + "\\yBots Control Menu\\w\n\n" + "1. Add a Bot, Quick\n" + "2. Add a Bot, Specified\n\n" + "3. Remove Random Bot\n" + "4. Remove All Bots\n\n" + "5. Remove Bot Menu\n\n" "0. Exit" }; // weapon mode select menu - g_menus[++counter] = - { - BOT_MENU_WEAPON_MODE, 0x27f, - "\\yBots Weapon Mode\\w\v\v" - "1. Knives only\v" - "2. Pistols only\v" - "3. Shotguns only\v" - "4. Machine Guns only\v" - "5. Rifles only\v" - "6. Sniper Weapons only\v" - "7. All Weapons\v\v" + g_menus[++counter] = { + BOT_MENU_WEAPON_MODE, buildKeys (7), + "\\yBots Weapon Mode\\w\n\n" + "1. Knives only\n" + "2. Pistols only\n" + "3. Shotguns only\n" + "4. Machine Guns only\n" + "5. Rifles only\n" + "6. Sniper Weapons only\n" + "7. All Weapons\n\n" "0. Exit" }; // personality select menu - g_menus[++counter] = - { - BOT_MENU_PERSONALITY, 0x20f, - "\\yBots Personality\\w\v\v" - "1. Random\v" - "2. Normal\v" - "3. Aggressive\v" - "4. Careful\v\v" + g_menus[++counter] = { + BOT_MENU_PERSONALITY, buildKeys (4), + "\\yBots Personality\\w\n\n" + "1. Random\n" + "2. Normal\n" + "3. Aggressive\n" + "4. Careful\n\n" "0. Exit" }; // difficulty select menu - g_menus[++counter] = - { - BOT_MENU_DIFFICULTY, 0x23f, - "\\yBots Difficulty Level\\w\v\v" - "1. Newbie\v" - "2. Average\v" - "3. Normal\v" - "4. Professional\v" - "5. Godlike\v\v" + g_menus[++counter] = { + BOT_MENU_DIFFICULTY, buildKeys (5), + "\\yBots Difficulty Level\\w\n\n" + "1. Newbie\n" + "2. Average\n" + "3. Normal\n" + "4. Professional\n" + "5. Godlike\n\n" "0. Exit" }; // team select menu - g_menus[++counter] = - { - BOT_MENU_TEAM_SELECT, 0x213, - "\\ySelect a team\\w\v\v" - "1. Terrorist Force\v" - "2. Counter-Terrorist Force\v\v" - "5. Auto-select\v\v" + g_menus[++counter] = { + BOT_MENU_TEAM_SELECT, buildKeys (5), + "\\ySelect a team\\w\n\n" + "1. Terrorist Force\n" + "2. Counter-Terrorist Force\n\n" + "5. Auto-select\n\n" "0. Exit" }; // terrorist model select menu - g_menus[++counter] = - { - BOT_MENU_TERRORIST_SELECT, 0x21f, - "\\ySelect an appearance\\w\v\v" - "1. Phoenix Connexion\v" - "2. L337 Krew\v" - "3. Arctic Avengers\v" - "4. Guerilla Warfare\v\v" - "5. Auto-select\v\v" + g_menus[++counter] = { + BOT_MENU_TERRORIST_SELECT, buildKeys (5), + "\\ySelect an appearance\\w\n\n" + "1. Phoenix Connexion\n" + "2. L337 Krew\n" + "3. Arctic Avengers\n" + "4. Guerilla Warfare\n\n" + "5. Auto-select\n\n" "0. Exit" }; // counter-terrorist model select menu - g_menus[++counter] = - { - BOT_MENU_CT_SELECT, 0x21f, - "\\ySelect an appearance\\w\v\v" - "1. Seal Team 6 (DEVGRU)\v" - "2. German GSG-9\v" - "3. UK SAS\v" - "4. French GIGN\v\v" - "5. Auto-select\v\v" + g_menus[++counter] = { + BOT_MENU_CT_SELECT, buildKeys (5), + "\\ySelect an appearance\\w\n\n" + "1. Seal Team 6 (DEVGRU)\n" + "2. German GSG-9\n" + "3. UK SAS\n" + "4. French GIGN\n\n" + "5. Auto-select\n\n" "0. Exit" }; // command menu - g_menus[++counter] = - { - BOT_MENU_COMMANDS, 0x23f, - "\\yBot Command Menu\\w\v\v" - "1. Make Double Jump\v" - "2. Finish Double Jump\v\v" - "3. Drop the C4 Bomb\v" - "4. Drop the Weapon\v\v" + g_menus[++counter] = { + BOT_MENU_COMMANDS, buildKeys (4), + "\\yBot Command Menu\\w\n\n" + "1. Make Double Jump\n" + "2. Finish Double Jump\n\n" + "3. Drop the C4 Bomb\n" + "4. Drop the Weapon\n\n" "0. Exit" }; // main waypoint menu - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_MAIN_PAGE1, 0x3ff, - "\\yWaypoint Operations (Page 1)\\w\v\v" - "1. Show/Hide waypoints\v" - "2. Cache waypoint\v" - "3. Create path\v" - "4. Delete path\v" - "5. Add waypoint\v" - "6. Delete waypoint\v" - "7. Set Autopath Distance\v" - "8. Set Radius\v\v" - "9. Next...\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_MAIN_PAGE1, buildKeys (9), + "\\yWaypoint Operations (Page 1)\\w\n\n" + "1. Show/Hide waypoints\n" + "2. Cache waypoint\n" + "3. Create path\n" + "4. Delete path\n" + "5. Add waypoint\n" + "6. Delete waypoint\n" + "7. Set Autopath Distance\n" + "8. Set Radius\n\n" + "9. Next...\n\n" "0. Exit" }; // main waypoint menu (page 2) - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_MAIN_PAGE2, 0x3ff, - "\\yWaypoint Operations (Page 2)\\w\v\v" - "1. Waypoint stats\v" - "2. Autowaypoint on/off\v" - "3. Set flags\v" - "4. Save waypoints\v" - "5. Save without checking\v" - "6. Load waypoints\v" - "7. Check waypoints\v" - "8. Noclip cheat on/off\v\v" - "9. Previous...\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_MAIN_PAGE2, buildKeys (9), + "\\yWaypoint Operations (Page 2)\\w\n\n" + "1. Waypoint stats\n" + "2. Autowaypoint on/off\n" + "3. Set flags\n" + "4. Save waypoints\n" + "5. Save without checking\n" + "6. Load waypoints\n" + "7. Check waypoints\n" + "8. Noclip cheat on/off\n\n" + "9. Previous...\n\n" "0. Exit" }; // select waypoint radius menu - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_RADIUS, 0x3ff, - "\\yWaypoint Radius\\w\v\v" - "1. SetRadius 0\v" - "2. SetRadius 8\v" - "3. SetRadius 16\v" - "4. SetRadius 32\v" - "5. SetRadius 48\v" - "6. SetRadius 64\v" - "7. SetRadius 80\v" - "8. SetRadius 96\v" - "9. SetRadius 128\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_RADIUS, buildKeys (9), + "\\yWaypoint Radius\\w\n\n" + "1. SetRadius 0\n" + "2. SetRadius 8\n" + "3. SetRadius 16\n" + "4. SetRadius 32\n" + "5. SetRadius 48\n" + "6. SetRadius 64\n" + "7. SetRadius 80\n" + "8. SetRadius 96\n" + "9. SetRadius 128\n\n" "0. Exit" }; // waypoint add menu - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_TYPE, 0x3ff, - "\\yWaypoint Type\\w\v\v" - "1. Normal\v" - "\\r2. Terrorist Important\v" - "3. Counter-Terrorist Important\v" - "\\w4. Block with hostage / Ladder\v" - "\\y5. Rescue Zone\v" - "\\w6. Camping\v" - "7. Camp End\v" - "\\r8. Map Goal\v" - "\\w9. Jump\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_TYPE, buildKeys (9), + "\\yWaypoint Type\\w\n\n" + "1. Normal\n" + "\\r2. Terrorist Important\n" + "3. Counter-Terrorist Important\n" + "\\w4. Block with hostage / Ladder\n" + "\\y5. Rescue Zone\n" + "\\w6. Camping\n" + "7. Camp End\n" + "\\r8. Map Goal\n" + "\\w9. Jump\n\n" "0. Exit" }; // set waypoint flag menu - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_FLAG, 0x2ff, - "\\yToggle Waypoint Flags\\w\v\v" - "1. Block with Hostage\v" - "2. Terrorists Specific\v" - "3. CTs Specific\v" - "4. Use Elevator\v" - "5. Sniper Point (\\yFor Camp Points Only!\\w)\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_FLAG, buildKeys (5), + "\\yToggle Waypoint Flags\\w\n\n" + "1. Block with Hostage\n" + "2. Terrorists Specific\n" + "3. CTs Specific\n" + "4. Use Elevator\n" + "5. Sniper Point (\\yFor Camp Points Only!\\w)\n\n" "0. Exit" }; // auto-path max distance - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_AUTOPATH, - 0x27f, - "\\yAutoPath Distance\\w\v\v" - "1. Distance 0\v" - "2. Distance 100\v" - "3. Distance 130\v" - "4. Distance 160\v" - "5. Distance 190\v" - "6. Distance 220\v" - "7. Distance 250 (Default)\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_AUTOPATH, buildKeys (7), + "\\yAutoPath Distance\\w\n\n" + "1. Distance 0\n" + "2. Distance 100\n" + "3. Distance 130\n" + "4. Distance 160\n" + "5. Distance 190\n" + "6. Distance 220\n" + "7. Distance 250 (Default)\n\n" "0. Exit" }; // path connections - g_menus[++counter] = - { - BOT_MENU_WAYPOINT_PATH, - 0x207, - "\\yCreate Path (Choose Direction)\\w\v\v" - "1. Outgoing Path\v" - "2. Incoming Path\v" - "3. Bidirectional (Both Ways)\v\v" + g_menus[++counter] = { + BOT_MENU_WAYPOINT_PATH, buildKeys (3), + "\\yCreate Path (Choose Direction)\\w\n\n" + "1. Outgoing Path\n" + "2. Incoming Path\n" + "3. Bidirectional (Both Ways)\n\n" "0. Exit" }; - const String &empty = ""; // kick menus @@ -407,4 +376,4 @@ void SetupBotMenus (void) } // bot menus -MenuText g_menus[BOT_MENU_TOTAL_MENUS]; +MenuText g_menus[BOT_MENU_TOTAL_MENUS]; \ No newline at end of file diff --git a/source/interface.cpp b/source/interface.cpp index 35888cf..55b47ac 100644 --- a/source/interface.cpp +++ b/source/interface.cpp @@ -4,12 +4,13 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include // console vars +ConVar yb_ignore_cvars_on_changelevel ("yb_ignore_cvars_on_changelevel", "yb_quota,yb_autovacate"); ConVar yb_password ("yb_password", "", VT_PASSWORD); ConVar yb_password_key ("yb_password_key", "_ybpw"); ConVar yb_language ("yb_language", "en"); @@ -17,879 +18,927 @@ ConVar yb_version ("yb_version", PRODUCT_VERSION, VT_READONLY); ConVar mp_startmoney ("mp_startmoney", nullptr, VT_NOREGISTER, true, "800"); -int BotCommandHandler (edict_t *ent, const char *arg0, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *self) -{ +int handleBotCommands (edict_t *ent, const char *arg0, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *self) { // adding one bot with random parameters to random team - if (A_stricmp (arg0, "addbot") == 0 || A_stricmp (arg0, "add") == 0) - bots.AddBot (arg4, arg1, arg2, arg3, arg5); + if (stricmp (arg0, "addbot") == 0 || stricmp (arg0, "add") == 0) { + bots.addbot (arg4, arg1, arg2, arg3, arg5, true); + } // adding one bot with high difficulty parameters to random team - else if (A_stricmp (arg0, "addbot_hs") == 0 || A_stricmp (arg0, "addhs") == 0) - bots.AddBot (arg4, "4", "1", arg3, arg5); + else if (stricmp (arg0, "addbot_hs") == 0 || stricmp (arg0, "addhs") == 0) { + bots.addbot (arg4, "4", "1", arg3, arg5, true); + } // adding one bot with random parameters to terrorist team - else if (A_stricmp (arg0, "addbot_t") == 0 || A_stricmp (arg0, "add_t") == 0) - bots.AddBot (arg4, arg1, arg2, "1", arg5); + else if (stricmp (arg0, "addbot_t") == 0 || stricmp (arg0, "add_t") == 0) { + bots.addbot (arg4, arg1, arg2, "1", arg5, true); + } // adding one bot with random parameters to counter-terrorist team - else if (A_stricmp (arg0, "addbot_ct") == 0 || A_stricmp (arg0, "add_ct") == 0) - bots.AddBot (arg4, arg1, arg2, "2", arg5); - + else if (stricmp (arg0, "addbot_ct") == 0 || stricmp (arg0, "add_ct") == 0) { + bots.addbot (arg4, arg1, arg2, "2", arg5, true); + } + // kicking off one bot from the terrorist team - else if (A_stricmp (arg0, "kickbot_t") == 0 || A_stricmp (arg0, "kick_t") == 0) - bots.RemoveFromTeam (TERRORIST); + else if (stricmp (arg0, "kickbot_t") == 0 || stricmp (arg0, "kick_t") == 0) { + bots.kickFromTeam (TEAM_TERRORIST); + } // kicking off one bot from the counter-terrorist team - else if (A_stricmp (arg0, "kickbot_ct") == 0 || A_stricmp (arg0, "kick_ct") == 0) - bots.RemoveFromTeam (CT); - + else if (stricmp (arg0, "kickbot_ct") == 0 || stricmp (arg0, "kick_ct") == 0) { + bots.kickFromTeam (TEAM_COUNTER); + } // kills all bots on the terrorist team - else if (A_stricmp (arg0, "killbots_t") == 0 || A_stricmp (arg0, "kill_t") == 0) - bots.KillAll (TERRORIST); + else if (stricmp (arg0, "killbots_t") == 0 || stricmp (arg0, "kill_t") == 0) { + bots.killAllBots (TEAM_TERRORIST); + } // kills all bots on the counter-terrorist team - else if (A_stricmp (arg0, "killbots_ct") == 0 || A_stricmp (arg0, "kill_ct") == 0) - bots.KillAll (CT); + else if (stricmp (arg0, "killbots_ct") == 0 || stricmp (arg0, "kill_ct") == 0) { + bots.killAllBots (TEAM_COUNTER); + } // list all bots playeing on the server - else if (A_stricmp (arg0, "listbots") == 0 || A_stricmp (arg0, "list") == 0) - bots.ListBots (); + else if (stricmp (arg0, "listbots") == 0 || stricmp (arg0, "list") == 0) { + bots.listBots (); + } // kick off all bots from the played server - else if (A_stricmp (arg0, "kickbots") == 0 || A_stricmp (arg0, "kickall") == 0) - bots.RemoveAll (); + else if (stricmp (arg0, "kickbots") == 0 || stricmp (arg0, "kickall") == 0) { + bots.kickEveryone (); + } // kill all bots on the played server - else if (A_stricmp (arg0, "killbots") == 0 || A_stricmp (arg0, "killall") == 0) - bots.KillAll (); + else if (stricmp (arg0, "killbots") == 0 || stricmp (arg0, "killall") == 0) { + bots.killAllBots (); + } // kick off one random bot from the played server - else if (A_stricmp (arg0, "kickone") == 0 || A_stricmp (arg0, "kick") == 0) - bots.RemoveRandom (); + else if (stricmp (arg0, "kickone") == 0 || stricmp (arg0, "kick") == 0) { + bots.kickRandom (); + } // fill played server with bots - else if (A_stricmp (arg0, "fillserver") == 0 || A_stricmp (arg0, "fill") == 0) - bots.FillServer (atoi (arg1), IsNullString (arg2) ? -1 : atoi (arg2), IsNullString (arg3) ? -1 : atoi (arg3), IsNullString (arg4) ? -1 : atoi (arg4)); + else if (stricmp (arg0, "fillserver") == 0 || stricmp (arg0, "fill") == 0) { + bots.serverFill (atoi (arg1), isEmptyStr (arg2) ? -1 : atoi (arg2), isEmptyStr (arg3) ? -1 : atoi (arg3), isEmptyStr (arg4) ? -1 : atoi (arg4)); + } // select the weapon mode for bots - else if (A_stricmp (arg0, "weaponmode") == 0 || A_stricmp (arg0, "wmode") == 0) - { + else if (stricmp (arg0, "weaponmode") == 0 || stricmp (arg0, "wmode") == 0) { int selection = atoi (arg1); // check is selected range valid if (selection >= 1 && selection <= 7) - bots.SetWeaponMode (selection); + bots.setWeaponMode (selection); else - engine.ClientPrintf (ent, "Choose weapon from 1 to 7 range"); + engine.clientPrint (ent, "Choose weapon from 1 to 7 range"); } // force all bots to vote to specified map - else if (A_stricmp (arg0, "votemap") == 0) - { - if (!IsNullString (arg1)) - { + else if (stricmp (arg0, "votemap") == 0) { + if (!isEmptyStr (arg1)) { int nominatedMap = atoi (arg1); // loop through all players - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); if (bot != nullptr) bot->m_voteMap = nominatedMap; } - engine.ClientPrintf (ent, "All dead bots will vote for map #%d", nominatedMap); + engine.clientPrint (ent, "All dead bots will vote for map #%d", nominatedMap); } } // displays version information - else if (A_stricmp (arg0, "version") == 0 || A_stricmp (arg0, "ver") == 0) - { - char versionData[] = - "------------------------------------------------\n" - "Name: %s\n" - "Version: %s (Build: %u)\n" - "Compiled: %s, %s\n" - "Git Hash: %s\n" - "Git Commit Author: %s\n" - "------------------------------------------------"; + else if (stricmp (arg0, "version") == 0 || stricmp (arg0, "ver") == 0) { + char versionData[] = "------------------------------------------------\n" + "Name: %s\n" + "Version: %s (Build: %u)\n" + "Compiled: %s, %s\n" + "Git Hash: %s\n" + "Git Commit Author: %s\n" + "------------------------------------------------"; - engine.ClientPrintf (ent, versionData, PRODUCT_NAME, PRODUCT_VERSION, GenerateBuildNumber (), __DATE__, __TIME__, PRODUCT_GIT_HASH, PRODUCT_GIT_COMMIT_AUTHOR); + engine.clientPrint (ent, versionData, PRODUCT_NAME, PRODUCT_VERSION, buildNumber (), __DATE__, __TIME__, PRODUCT_GIT_HASH, PRODUCT_GIT_COMMIT_AUTHOR); } // display some sort of help information - else if (strcmp (arg0, "?") == 0 || strcmp (arg0, "help") == 0) - { - engine.ClientPrintf (ent, "Bot Commands:"); - engine.ClientPrintf (ent, "%s version\t - display version information.", self); - engine.ClientPrintf (ent, "%s add\t - create a bot in current game.", self); - engine.ClientPrintf (ent, "%s fill\t - fill the server with random bots.", self); - engine.ClientPrintf (ent, "%s kickall\t - disconnects all bots from current game.", self); - engine.ClientPrintf (ent, "%s killbots\t - kills all bots in current game.", self); - engine.ClientPrintf (ent, "%s kick\t - disconnect one random bot from game.", self); - engine.ClientPrintf (ent, "%s weaponmode\t - select bot weapon mode.", self); - engine.ClientPrintf (ent, "%s votemap\t - allows dead bots to vote for specific map.", self); - engine.ClientPrintf (ent, "%s cmenu\t - displaying bots command menu.", self); + else if (strcmp (arg0, "?") == 0 || strcmp (arg0, "help") == 0) { + engine.clientPrint (ent, "Bot Commands:"); + engine.clientPrint (ent, "%s version\t - display version information.", self); + engine.clientPrint (ent, "%s add\t - create a bot in current game.", self); + engine.clientPrint (ent, "%s fill\t - fill the server with random bots.", self); + engine.clientPrint (ent, "%s kickall\t - disconnects all bots from current game.", self); + engine.clientPrint (ent, "%s killbots\t - kills all bots in current game.", self); + engine.clientPrint (ent, "%s kick\t - disconnect one random bot from game.", self); + engine.clientPrint (ent, "%s weaponmode\t - select bot weapon mode.", self); + engine.clientPrint (ent, "%s votemap\t - allows dead bots to vote for specific map.", self); + engine.clientPrint (ent, "%s cmenu\t - displaying bots command menu.", self); - if (strcmp (arg1, "full") == 0 || strcmp (arg1, "?") == 0) - { - engine.ClientPrintf (ent, "%s add_t\t - creates one random bot to terrorist team.", self); - engine.ClientPrintf (ent, "%s add_ct\t - creates one random bot to ct team.", self); - engine.ClientPrintf (ent, "%s kick_t\t - disconnect one random bot from terrorist team.", self); - engine.ClientPrintf (ent, "%s kick_ct\t - disconnect one random bot from ct team.", self); - engine.ClientPrintf (ent, "%s kill_t\t - kills all bots on terrorist team.", self); - engine.ClientPrintf (ent, "%s kill_ct\t - kills all bots on ct team.", self); - engine.ClientPrintf (ent, "%s list\t - display list of bots currently playing.", self); - engine.ClientPrintf (ent, "%s deletewp\t - erase waypoint file from hard disk (permanently).", self); + if (strcmp (arg1, "full") == 0 || strcmp (arg1, "?") == 0) { + engine.clientPrint (ent, "%s add_t\t - creates one random bot to terrorist team.", self); + engine.clientPrint (ent, "%s add_ct\t - creates one random bot to ct team.", self); + engine.clientPrint (ent, "%s kick_t\t - disconnect one random bot from terrorist team.", self); + engine.clientPrint (ent, "%s kick_ct\t - disconnect one random bot from ct team.", self); + engine.clientPrint (ent, "%s kill_t\t - kills all bots on terrorist team.", self); + engine.clientPrint (ent, "%s kill_ct\t - kills all bots on ct team.", self); + engine.clientPrint (ent, "%s list\t - display list of bots currently playing.", self); + engine.clientPrint (ent, "%s deletewp\t - erase waypoint file from hard disk (permanently).", self); - if (!engine.IsDedicatedServer ()) - { - engine.Printf ("%s autowp\t - toggle autowaypointing.", self); - engine.Printf ("%s wp\t - toggle waypoint showing.", self); - engine.Printf ("%s wp on noclip\t - enable noclip cheat", self); - engine.Printf ("%s wp save nocheck\t - save waypoints without checking.", self); - engine.Printf ("%s wp add\t - open menu for waypoint creation.", self); - engine.Printf ("%s wp delete\t - delete waypoint nearest to host edict.", self); - engine.Printf ("%s wp menu\t - open main waypoint menu.", self); - engine.Printf ("%s wp addbasic\t - creates basic waypoints on map.", self); - engine.Printf ("%s wp find\t - show direction to specified waypoint.", self); - engine.Printf ("%s wp load\t - load the waypoint file from hard disk.", self); - engine.Printf ("%s wp check\t - checks if all waypoints connections are valid.", self); - engine.Printf ("%s wp cache\t - cache nearest waypoint.", self); - engine.Printf ("%s wp teleport\t - teleport hostile to specified waypoint.", self); - engine.Printf ("%s wp setradius\t - manually sets the wayzone radius for this waypoint.", self); - engine.Printf ("%s path autodistance - opens menu for setting autopath maximum distance.", self); - engine.Printf ("%s path cache\t - remember the nearest to player waypoint.", self); - engine.Printf ("%s path create\t - opens menu for path creation.", self); - engine.Printf ("%s path delete\t - delete path from cached to nearest waypoint.", self); - engine.Printf ("%s path create_in\t - creating incoming path connection.", self); - engine.Printf ("%s path create_out\t - creating outgoing path connection.", self); - engine.Printf ("%s path create_both\t - creating both-ways path connection.", self); - engine.Printf ("%s exp save\t - save the experience data.", self); - } + if (!engine.isDedicated ()) { + engine.print ("%s autowp\t - toggle autowaypointing.", self); + engine.print ("%s wp\t - toggle waypoint showing.", self); + engine.print ("%s wp on noclip\t - enable noclip cheat", self); + engine.print ("%s wp save nocheck\t - save waypoints without checking.", self); + engine.print ("%s wp add\t - open menu for waypoint creation.", self); + engine.print ("%s wp delete\t - delete waypoint nearest to host edict.", self); + engine.print ("%s wp menu\t - open main waypoint menu.", self); + engine.print ("%s wp addbasic\t - creates basic waypoints on map.", self); + engine.print ("%s wp find\t - show direction to specified waypoint.", self); + engine.print ("%s wp load\t - load the waypoint file from hard disk.", self); + engine.print ("%s wp check\t - checks if all waypoints connections are valid.", self); + engine.print ("%s wp cache\t - cache nearest waypoint.", self); + engine.print ("%s wp teleport\t - teleport hostile to specified waypoint.", self); + engine.print ("%s wp setradius\t - manually sets the wayzone radius for this waypoint.", self); + engine.print ("%s path autodistance - opens menu for setting autopath maximum distance.", self); + engine.print ("%s path cache\t - remember the nearest to player waypoint.", self); + engine.print ("%s path create\t - opens menu for path creation.", self); + engine.print ("%s path delete\t - delete path from cached to nearest waypoint.", self); + engine.print ("%s path create_in\t - creating incoming path connection.", self); + engine.print ("%s path create_out\t - creating outgoing path connection.", self); + engine.print ("%s path create_both\t - creating both-ways path connection.", self); + engine.print ("%s exp save\t - save the experience data.", self); + } } } - else if (A_stricmp (arg0, "bot_takedamage") == 0 && !IsNullString (arg1)) - { + else if (stricmp (arg0, "bot_takedamage") == 0 && !isEmptyStr (arg1)) { bool isOn = !!(atoi (arg1) == 1); - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot != nullptr) - { + if (bot != nullptr) { bot->pev->takedamage = isOn ? 0.0f : 1.0f; } } } // displays main bot menu - else if (A_stricmp (arg0, "botmenu") == 0 || A_stricmp (arg0, "menu") == 0) - DisplayMenuToClient (ent, BOT_MENU_MAIN); + else if (stricmp (arg0, "botmenu") == 0 || stricmp (arg0, "menu") == 0) { + showMenu (ent, BOT_MENU_MAIN); + } // display command menu - else if (A_stricmp (arg0, "cmdmenu") == 0 || A_stricmp (arg0, "cmenu") == 0) - { - if (IsAlive (ent)) - DisplayMenuToClient (ent, BOT_MENU_COMMANDS); - else - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display - engine.CenterPrintf ("You're dead, and have no access to this menu"); + else if (stricmp (arg0, "cmdmenu") == 0 || stricmp (arg0, "cmenu") == 0) { + if (isAlive (ent)) { + showMenu (ent, BOT_MENU_COMMANDS); + } + else { + showMenu (ent, BOT_MENU_INVALID); // reset menu display + engine.centerPrint ("You're dead, and have no access to this menu"); } } // waypoint manimupulation (really obsolete, can be edited through menu) (supported only on listen server) - else if (A_stricmp (arg0, "waypoint") == 0 || A_stricmp (arg0, "wp") == 0 || A_stricmp (arg0, "wpt") == 0) - { - if (engine.IsDedicatedServer () || engine.IsNullEntity (g_hostEntity)) + else if (stricmp (arg0, "waypoint") == 0 || stricmp (arg0, "wp") == 0 || stricmp (arg0, "wpt") == 0) { + if (engine.isDedicated () || engine.isNullEntity (g_hostEntity)) { return 2; + } // enables or disable waypoint displaying - if (A_stricmp (arg1, "on") == 0) - { + if (stricmp (arg1, "on") == 0) { g_waypointOn = true; - engine.Printf ("Waypoint Editing Enabled"); + engine.print ("Waypoint Editing Enabled"); // enables noclip cheat - if (A_stricmp (arg2, "noclip") == 0) - { - if (g_editNoclip) - { + if (!isEmptyStr (arg2) && stricmp (arg2, "noclip") == 0) { + if (g_editNoclip) { g_hostEntity->v.movetype = MOVETYPE_WALK; - engine.Printf ("Noclip Cheat Disabled"); + engine.print ("Noclip Cheat Disabled"); + + g_editNoclip = false; } - else - { + else { g_hostEntity->v.movetype = MOVETYPE_NOCLIP; - engine.Printf ("Noclip Cheat Enabled"); + engine.print ("Noclip Cheat Enabled"); + + g_editNoclip = true; } - g_editNoclip = !g_editNoclip; // switch on/off (XOR it!) } - engine.IssueCmd ("yapb wp mdl on"); + engine.execCmd ("yapb wp mdl on"); } // switching waypoint editing off - else if (A_stricmp (arg1, "off") == 0) - { + else if (stricmp (arg1, "off") == 0) { g_waypointOn = false; g_editNoclip = false; g_hostEntity->v.movetype = MOVETYPE_WALK; - engine.Printf ("Waypoint Editing Disabled"); - engine.IssueCmd ("yapb wp mdl off"); + engine.print ("Waypoint Editing Disabled"); + engine.execCmd ("yapb wp mdl off"); } // toggles displaying player models on spawn spots - else if (A_stricmp (arg1, "mdl") == 0 || A_stricmp (arg1, "models") == 0) - { - edict_t *spawnEntity = nullptr; + else if (stricmp (arg1, "mdl") == 0 || stricmp (arg1, "models") == 0) { - if (A_stricmp (arg2, "on") == 0) - { - while (!engine.IsNullEntity (spawnEntity = FIND_ENTITY_BY_CLASSNAME (spawnEntity, "info_player_start"))) - spawnEntity->v.effects &= ~EF_NODRAW; - while (!engine.IsNullEntity (spawnEntity = FIND_ENTITY_BY_CLASSNAME (spawnEntity, "info_player_deathmatch"))) - spawnEntity->v.effects &= ~EF_NODRAW; - while (!engine.IsNullEntity (spawnEntity = FIND_ENTITY_BY_CLASSNAME (spawnEntity, "info_vip_start"))) - spawnEntity->v.effects &= ~EF_NODRAW; + auto toggleDraw = [] (bool draw, const String &stuff) { + edict_t *ent = nullptr; - engine.IssueCmd ("mp_roundtime 9"); // reset round time to maximum - engine.IssueCmd ("mp_timelimit 0"); // disable the time limit - engine.IssueCmd ("mp_freezetime 0"); // disable freezetime + while (!engine.isNullEntity (ent = g_engfuncs.pfnFindEntityByString (ent, "classname", stuff.chars ()))) { + if (draw) { + ent->v.effects &= ~EF_NODRAW; + } + else { + ent->v.effects |= EF_NODRAW; + } + } + }; + StringArray entities; + + entities.push ("info_player_start"); + entities.push ("info_player_deathmatch"); + entities.push ("info_vip_start"); + + if (stricmp (arg2, "on") == 0) { + for (auto &type : entities) { + toggleDraw (true, type); + } + + engine.execCmd ("mp_roundtime 9"); // reset round time to maximum + engine.execCmd ("mp_timelimit 0"); // disable the time limit + engine.execCmd ("mp_freezetime 0"); // disable freezetime } - else if (A_stricmp (arg2, "off") == 0) - { - while (!engine.IsNullEntity (spawnEntity = FIND_ENTITY_BY_CLASSNAME (spawnEntity, "info_player_start"))) - spawnEntity->v.effects |= EF_NODRAW; - while (!engine.IsNullEntity (spawnEntity = FIND_ENTITY_BY_CLASSNAME (spawnEntity, "info_player_deathmatch"))) - spawnEntity->v.effects |= EF_NODRAW; - while (!engine.IsNullEntity (spawnEntity = FIND_ENTITY_BY_CLASSNAME (spawnEntity, "info_vip_start"))) - spawnEntity->v.effects |= EF_NODRAW; + else if (stricmp (arg2, "off") == 0) { + for (auto &type : entities) { + toggleDraw (false, type); + } } } // show direction to specified waypoint - else if (A_stricmp (arg1, "find") == 0) - waypoints.SetFindIndex (atoi (arg2)); + else if (stricmp (arg1, "find") == 0) { + waypoints.setSearchIndex (atoi (arg2)); + } // opens adding waypoint menu - else if (A_stricmp (arg1, "add") == 0) - { - g_waypointOn = true; // turn waypoints on - DisplayMenuToClient (g_hostEntity, BOT_MENU_WAYPOINT_TYPE); + else if (stricmp (arg1, "add") == 0) { + g_waypointOn = true; // turn waypoints on + showMenu (g_hostEntity, BOT_MENU_WAYPOINT_TYPE); } // creates basic waypoints on the map (ladder/spawn points/goals) - else if (A_stricmp (arg1, "addbasic") == 0) - { - waypoints.CreateBasic (); - engine.CenterPrintf ("Basic waypoints was Created"); + else if (stricmp (arg1, "addbasic") == 0) { + waypoints.addBasic (); + engine.centerPrint ("Basic waypoints was Created"); } // delete nearest to host edict waypoint - else if (A_stricmp (arg1, "delete") == 0) - { + else if (stricmp (arg1, "delete") == 0) { g_waypointOn = true; // turn waypoints on - waypoints.Delete (); + + if (!isEmptyStr (arg2)) { + waypoints.erase (atoi (arg2)); + } + else { + waypoints.erase (INVALID_WAYPOINT_INDEX); + } } // save waypoint data into file on hard disk - else if (A_stricmp (arg1, "save") == 0) - { - char *waypointSaveMessage = engine.TraslateMessage ("Waypoints Saved"); + else if (stricmp (arg1, "save") == 0) { + const char *waypointSaveMessage = "Waypoints Saved"; - if (FStrEq (arg2, "nocheck")) - { - waypoints.Save (); - engine.Printf (waypointSaveMessage); + if (strcmp (arg2, "nocheck") == 0) { + waypoints.save (); + engine.print (waypointSaveMessage); } - else if (waypoints.NodesValid ()) - { - waypoints.Save (); - engine.Printf (waypointSaveMessage); + else if (waypoints.checkNodes ()) { + waypoints.save (); + engine.print (waypointSaveMessage); } } // remove waypoint and all corresponding files from hard disk - else if (A_stricmp (arg1, "erase") == 0) - waypoints.EraseFromHardDisk (); + else if (stricmp (arg1, "erase") == 0) { + waypoints.eraseFromDisk (); + } // load all waypoints again (overrides all changes, that wasn't saved) - else if (A_stricmp (arg1, "load") == 0) - { - if (waypoints.Load ()) - engine.Printf ("Waypoints loaded"); + else if (stricmp (arg1, "load") == 0) { + if (waypoints.load ()) + engine.print ("Waypoints loaded"); } // check all nodes for validation - else if (A_stricmp (arg1, "check") == 0) - { - if (waypoints.NodesValid ()) - engine.CenterPrintf ("Nodes work Fine"); + else if (stricmp (arg1, "check") == 0) { + if (waypoints.checkNodes ()) + engine.centerPrint ("Nodes work Fine"); } // opens menu for setting (removing) waypoint flags - else if (A_stricmp (arg1, "flags") == 0) - DisplayMenuToClient (g_hostEntity, BOT_MENU_WAYPOINT_FLAG); + else if (stricmp (arg1, "flags") == 0) { + showMenu (g_hostEntity, BOT_MENU_WAYPOINT_FLAG); + } // setting waypoint radius - else if (A_stricmp (arg1, "setradius") == 0) - waypoints.SetRadius (atoi (arg2)); + else if (stricmp (arg1, "setradius") == 0) { + waypoints.setRadius (atoi (arg2)); + } // remembers nearest waypoint - else if (A_stricmp (arg1, "cache") == 0) - waypoints.CacheWaypoint (); + else if (stricmp (arg1, "cache") == 0) { + waypoints.cachePoint (); + } // teleport player to specified waypoint - else if (A_stricmp (arg1, "teleport") == 0) - { + else if (stricmp (arg1, "teleport") == 0) { int teleportPoint = atoi (arg2); - if (teleportPoint < g_numWaypoints) - { - Path *path = waypoints.GetPath (teleportPoint); + if (waypoints.exists (teleportPoint)) { + Path &path = waypoints[teleportPoint]; - (*g_engfuncs.pfnSetOrigin) (g_hostEntity, path->origin); + g_engfuncs.pfnSetOrigin (g_hostEntity, path.origin); g_waypointOn = true; - engine.Printf ("Player '%s' teleported to waypoint #%d (x:%.1f, y:%.1f, z:%.1f)", STRING (g_hostEntity->v.netname), teleportPoint, path->origin.x, path->origin.y, path->origin.z); //-V807 + engine.print ("Player '%s' teleported to waypoint #%d (x:%.1f, y:%.1f, z:%.1f)", STRING (g_hostEntity->v.netname), teleportPoint, path.origin.x, path.origin.y, path.origin.z); //-V807 g_editNoclip = true; } } // displays waypoint menu - else if (A_stricmp (arg1, "menu") == 0) - DisplayMenuToClient (g_hostEntity, BOT_MENU_WAYPOINT_MAIN_PAGE1); + else if (stricmp (arg1, "menu") == 0) { + showMenu (g_hostEntity, BOT_MENU_WAYPOINT_MAIN_PAGE1); + } // otherwise display waypoint current status - else - engine.Printf ("Waypoints are %s", g_waypointOn == true ? "Enabled" : "Disabled"); + else { + engine.print ("Waypoints are %s", g_waypointOn == true ? "Enabled" : "Disabled"); + } } // path waypoint editing system (supported only on listen server) - else if (A_stricmp (arg0, "pathwaypoint") == 0 || A_stricmp (arg0, "path") == 0 || A_stricmp (arg0, "pwp") == 0) - { - if (engine.IsDedicatedServer () || engine.IsNullEntity (g_hostEntity)) + else if (stricmp (arg0, "pathwaypoint") == 0 || stricmp (arg0, "path") == 0 || stricmp (arg0, "pwp") == 0) { + if (engine.isDedicated () || engine.isNullEntity (g_hostEntity)) { return 2; + } // opens path creation menu - if (A_stricmp (arg1, "create") == 0) - DisplayMenuToClient (g_hostEntity, BOT_MENU_WAYPOINT_PATH); + if (stricmp (arg1, "create") == 0) { + showMenu (g_hostEntity, BOT_MENU_WAYPOINT_PATH); + } // creates incoming path from the cached waypoint - else if (A_stricmp (arg1, "create_in") == 0) - waypoints.CreatePath (CONNECTION_INCOMING); + else if (stricmp (arg1, "create_in") == 0) { + waypoints.pathCreate (CONNECTION_INCOMING); + } // creates outgoing path from current waypoint - else if (A_stricmp (arg1, "create_out") == 0) - waypoints.CreatePath (CONNECTION_OUTGOING); + else if (stricmp (arg1, "create_out") == 0) { + waypoints.pathCreate (CONNECTION_OUTGOING); + } - // creates bidirectional path from cahed to current waypoint - else if (A_stricmp (arg1, "create_both") == 0) - waypoints.CreatePath (CONNECTION_BOTHWAYS); + // creates bidirectional path from cached to current waypoint + else if (stricmp (arg1, "create_both") == 0) { + waypoints.pathCreate (CONNECTION_BOTHWAYS); + } // delete special path - else if (A_stricmp (arg1, "delete") == 0) - waypoints.DeletePath (); + else if (stricmp (arg1, "delete") == 0) { + waypoints.erasePath (); + } // sets auto path maximum distance - else if (A_stricmp (arg1, "autodistance") == 0) - DisplayMenuToClient (g_hostEntity, BOT_MENU_WAYPOINT_AUTOPATH); + else if (stricmp (arg1, "autodistance") == 0) { + showMenu (g_hostEntity, BOT_MENU_WAYPOINT_AUTOPATH); + } } // automatic waypoint handling (supported only on listen server) - else if (A_stricmp (arg0, "autowaypoint") == 0 || A_stricmp (arg0, "autowp") == 0) - { - if (engine.IsDedicatedServer () || engine.IsNullEntity (g_hostEntity)) + else if (stricmp (arg0, "autowaypoint") == 0 || stricmp (arg0, "autowp") == 0) { + if (engine.isDedicated () || engine.isNullEntity (g_hostEntity)) { return 2; + } // enable autowaypointing - if (A_stricmp (arg1, "on") == 0) - { + if (stricmp (arg1, "on") == 0) { g_autoWaypoint = true; g_waypointOn = true; // turn this on just in case } // disable autowaypointing - else if (A_stricmp (arg1, "off") == 0) + else if (stricmp (arg1, "off") == 0) { g_autoWaypoint = false; + } // display status - engine.Printf ("Auto-Waypoint %s", g_autoWaypoint ? "Enabled" : "Disabled"); + engine.print ("Auto-Waypoint %s", g_autoWaypoint ? "Enabled" : "Disabled"); } // experience system handling (supported only on listen server) - else if (A_stricmp (arg0, "experience") == 0 || A_stricmp (arg0, "exp") == 0) - { - if (engine.IsDedicatedServer () || engine.IsNullEntity (g_hostEntity)) + else if (stricmp (arg0, "experience") == 0 || stricmp (arg0, "exp") == 0) { + if (engine.isDedicated () || engine.isNullEntity (g_hostEntity)) { return 2; + } // write experience table (and visibility table) to hard disk - if (A_stricmp (arg1, "save") == 0) - { - waypoints.SaveExperienceTab (); - waypoints.SaveVisibilityTab (); + if (stricmp (arg1, "save") == 0) { + waypoints.saveExperience (); + waypoints.saveVisibility (); - engine.Printf ("Experience tab saved"); + engine.print ("Experience tab saved"); } } - else + else { return 0; // command is not handled by bot - + } return 1; // command was handled by bot } +void execBotConfigs (bool onlyMain) { + static bool setMemoryPointers = true; -// forwards for MemoryFile -MemoryFile::MF_Loader MemoryFile::Loader = nullptr; -MemoryFile::MF_Unloader MemoryFile::Unloader = nullptr; - -void InitConfig (void) -{ - if (!MemoryFile::Loader && !MemoryFile::Unloader) - { - MemoryFile::Loader = reinterpret_cast (g_engfuncs.pfnLoadFileForMe); - MemoryFile::Unloader = reinterpret_cast (g_engfuncs.pfnFreeFile); + if (setMemoryPointers) { + MemoryLoader::ref ().setup (g_engfuncs.pfnLoadFileForMe, g_engfuncs.pfnFreeFile); + setMemoryPointers = true; } - MemoryFile fp; - char line[512]; + auto isCommentLine = [] (const char *line) { + char ch = *line; + return ch == '#' || ch == '/' || ch == '\r' || ch == ';' || ch == 0 || ch == ' '; + }; - KeywordFactory replyKey; + MemFile fp; + char lineBuffer[512]; - // fixes for crashing if configs couldn't be accessed - g_chatFactory.SetSize (CHAT_TOTAL); - g_chatterFactory.SetSize (Chatter_Total); + // this is does the same as exec of engine, but not overwriting values of cvars spcified in yb_ignore_cvars_on_changelevel + if (onlyMain) { + static bool firstLoad = true; - #define SKIP_COMMENTS() if (line[0] == '/' || line[0] == '\r' || line[0] == '\n' || line[0] == 0 || line[0] == ' ' || line[0] == '\t' || line[0] == ';') continue + auto needsToIgnoreVar = [](StringArray &list, const char *needle) { + for (auto &var : list) { + if (var == needle) { + return true; + } + } + return false; + }; + + if (openConfig ("yapb.cfg", "YaPB main config file is not found.", &fp, false)) { + while (fp.gets (lineBuffer, 255)) { + if (isCommentLine (lineBuffer)) { + continue; + } + if (firstLoad) { + engine.execCmd (lineBuffer); + continue; + } + auto keyval = String (lineBuffer).split (" "); + + if (keyval.length () > 1) { + auto ignore = String (yb_ignore_cvars_on_changelevel.str ()).split (","); + + auto key = keyval[0].trim ().chars (); + auto cvar = g_engfuncs.pfnCVarGetPointer (key); + + if (cvar != nullptr) { + auto value = const_cast (keyval[1].trim ().trim ("\"").trim ().chars ()); + + if (needsToIgnoreVar (ignore, key)) { + if (!!stricmp (value, cvar->string)) { + engine.print ("Bot CVAR '%s' is differs from stored in config (%s/%s). Ignoring.", cvar->name, cvar->string, value); + + // ensure cvar will have old value + g_engfuncs.pfnCvar_DirectSet (cvar, cvar->string); + } + else { + g_engfuncs.pfnCvar_DirectSet (cvar, value); + } + } + } + else + engine.execCmd (lineBuffer); + } + } + fp.close (); + } + firstLoad = false; + return; + } + KeywordFactory replies; + + // reserve some space for chat + g_chatFactory.reserve (CHAT_TOTAL); + g_chatterFactory.reserve (CHATTER_MAX); + g_botNames.reserve (CHATTER_MAX); // NAMING SYSTEM INITIALIZATION - if (OpenConfig ("names.cfg", "Name configuration file not found.", &fp , true)) - { - g_botNames.RemoveAll (); + if (openConfig ("names.cfg", "Name configuration file not found.", &fp, true)) { + g_botNames.clear (); - while (fp.GetBuffer (line, 255)) - { - SKIP_COMMENTS (); + while (fp.gets (lineBuffer, 255)) { + if (isCommentLine (lineBuffer)) { + continue; + } + StringArray pair = String (lineBuffer).split ("\t\t"); - Array pair = String (line).Split ("\t\t"); + if (pair.length () > 1) { + strncpy (lineBuffer, pair[0].trim ().chars (), cr::bufsize (lineBuffer)); + } - if (pair.GetElementNumber () > 1) - strncpy (line, pair[0].Trim ().GetBuffer (), SIZEOF_CHAR (line)); - - String::TrimExternalBuffer (line); - line[32] = 0; + String::trimChars (lineBuffer); + lineBuffer[32] = 0; BotName item; - memset (&item, 0, sizeof (item)); - - item.name = line; + item.name = lineBuffer; item.usedBy = 0; - if (pair.GetElementNumber () > 1) - item.steamId = pair[1].Trim (); - - g_botNames.Push (item); + if (pair.length () > 1) { + item.steamId = pair[1].trim (); + } + g_botNames.push (cr::move (item)); } - fp.Close (); + fp.close (); } - engine.Printf ("INITING CHAT.CfG"); - // CHAT SYSTEM CONFIG INITIALIZATION - if (OpenConfig ("chat.cfg", "Chat file not found.", &fp, true)) - { - char section[80]; + if (openConfig ("chat.cfg", "Chat file not found.", &fp, true)) { + int chatType = -1; - while (fp.GetBuffer (line, 255)) - { - SKIP_COMMENTS (); + while (fp.gets (lineBuffer, 255)) { + if (isCommentLine (lineBuffer)) { + continue; + } + String section (lineBuffer, strlen (lineBuffer) - 1); + section.trim (); - String::TrimExternalBuffer (line); - strncpy (section, line, SIZEOF_CHAR (section)); - - if (strcmp (section, "[KILLED]") == 0) - { + if (section == "[KILLED]") { chatType = 0; continue; } - else if (strcmp (section, "[BOMBPLANT]") == 0) - { + else if (section == "[BOMBPLANT]") { chatType = 1; continue; } - else if (strcmp (section, "[DEADCHAT]") == 0) - { + else if (section == "[DEADCHAT]") { chatType = 2; continue; } - else if (strcmp (section, "[REPLIES]") == 0) - { + else if (section == "[REPLIES]") { chatType = 3; continue; } - else if (strcmp (section, "[UNKNOWN]") == 0) - { + else if (section == "[UNKNOWN]") { chatType = 4; continue; } - else if (strcmp (section, "[TEAMATTACK]") == 0) - { + else if (section == "[TEAMATTACK]") { chatType = 5; continue; } - else if (strcmp (section, "[WELCOME]") == 0) - { + else if (section == "[WELCOME]") { chatType = 6; continue; } - else if (strcmp (section, "[TEAMKILL]") == 0) - { + else if (section == "[TEAMKILL]") { chatType = 7; continue; } - if (chatType != 3) - line[79] = 0; + if (chatType != 3) { + lineBuffer[79] = 0; + } - switch (chatType) - { + switch (chatType) { case 0: - g_chatFactory[CHAT_KILLING].Push (line); + g_chatFactory[CHAT_KILLING].push (lineBuffer); break; case 1: - g_chatFactory[CHAT_BOMBPLANT].Push (line); + g_chatFactory[CHAT_BOMBPLANT].push (lineBuffer); break; case 2: - g_chatFactory[CHAT_DEAD].Push (line); + g_chatFactory[CHAT_DEAD].push (lineBuffer); break; case 3: - if (strstr (line, "@KEY") != nullptr) - { - if (!replyKey.keywords.IsEmpty () && !replyKey.replies.IsEmpty ()) - { - g_replyFactory.Push (replyKey); - replyKey.replies.RemoveAll (); + if (strstr (lineBuffer, "@KEY") != nullptr) { + if (!replies.keywords.empty () && !replies.replies.empty ()) { + g_replyFactory.push (cr::forward (replies)); + replies.replies.clear (); } - replyKey.keywords.RemoveAll (); - replyKey.keywords = String (&line[4]).Split (','); + replies.keywords.clear (); + replies.keywords = String (&lineBuffer[4]).split (","); - FOR_EACH_AE (replyKey.keywords, i) - replyKey.keywords[i].Trim ().TrimQuotes (); + for (auto &keywords : replies.keywords) { + keywords.trim ().trim ("\""); + } + } + else if (!replies.keywords.empty ()) { + replies.replies.push (lineBuffer); } - else if (!replyKey.keywords.IsEmpty ()) - replyKey.replies.Push (line); - break; case 4: - g_chatFactory[CHAT_NOKW].Push (line); + g_chatFactory[CHAT_NOKW].push (lineBuffer); break; case 5: - g_chatFactory[CHAT_TEAMATTACK].Push (line); + g_chatFactory[CHAT_TEAMATTACK].push (lineBuffer); break; case 6: - g_chatFactory[CHAT_WELCOME].Push (line); + g_chatFactory[CHAT_WELCOME].push (lineBuffer); break; case 7: - g_chatFactory[CHAT_TEAMKILL].Push (line); + g_chatFactory[CHAT_TEAMKILL].push (lineBuffer); break; } } - fp.Close (); + fp.close (); } - else - { + else { extern ConVar yb_chat; - yb_chat.SetInt (0); + yb_chat.set (0); } - + // GENERAL DATA INITIALIZATION - if (OpenConfig ("general.cfg", "General configuration file not found. Loading defaults", &fp)) - { - while (fp.GetBuffer (line, 255)) - { - SKIP_COMMENTS (); - - auto pair = String (line).Split ('='); - - if (pair.GetElementNumber () != 2) + if (openConfig ("general.cfg", "General configuration file not found. Loading defaults", &fp)) { + while (fp.gets (lineBuffer, 255)) { + if (isCommentLine (lineBuffer)) { continue; - - pair[0].Trim ().Trim (); - pair[1].Trim ().Trim (); - - auto splitted = pair[1].Split (','); - - if (pair[0] == "MapStandard") - { - if (splitted.GetElementNumber () != NUM_WEAPONS) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); - - for (int i = 0; i < NUM_WEAPONS; i++) - g_weaponSelect[i].teamStandard = splitted[i].ToInt (); } - else if (pair[0] == "MapAS") - { - if (splitted.GetElementNumber () != NUM_WEAPONS) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); + auto pair = String (lineBuffer).split ("="); - for (int i = 0; i < NUM_WEAPONS; i++) - g_weaponSelect[i].teamAS = splitted[i].ToInt (); + if (pair.length () != 2) { + continue; } - else if (pair[0] == "GrenadePercent") - { - if (splitted.GetElementNumber () != 3) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); - for (int i = 0; i < 3; i++) - g_grenadeBuyPrecent[i] = splitted[i].ToInt (); + for (auto &trim : pair) { + trim.trim (); } - else if (pair[0] == "Economics") - { - if (splitted.GetElementNumber () != 11) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); + auto splitted = pair[1].split (","); - for (int i = 0; i < 11; i++) - g_botBuyEconomyTable[i] = splitted[i].ToInt (); + if (pair[0] == "MapStandard") { + if (splitted.length () != NUM_WEAPONS) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } + + for (int i = 0; i < NUM_WEAPONS; i++) { + g_weaponSelect[i].teamStandard = splitted[i].toInt32 (); + } } - else if (pair[0] == "PersonalityNormal") - { - if (splitted.GetElementNumber () != NUM_WEAPONS) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); + else if (pair[0] == "MapAS") { + if (splitted.length () != NUM_WEAPONS) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } - for (int i = 0; i < NUM_WEAPONS; i++) - g_normalWeaponPrefs[i] = splitted[i].ToInt (); + for (int i = 0; i < NUM_WEAPONS; i++) { + g_weaponSelect[i].teamAS = splitted[i].toInt32 (); + } } - else if (pair[0] == "PersonalityRusher") - { - if (splitted.GetElementNumber () != NUM_WEAPONS) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); + else if (pair[0] == "GrenadePercent") { + if (splitted.length () != 3) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } - for (int i = 0; i < NUM_WEAPONS; i++) - g_rusherWeaponPrefs[i] = splitted[i].ToInt (); + for (int i = 0; i < 3; i++) { + g_grenadeBuyPrecent[i] = splitted[i].toInt32 (); + } } - else if (pair[0] == "PersonalityCareful") - { - if (splitted.GetElementNumber () != NUM_WEAPONS) - AddLogEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].GetBuffer ()); + else if (pair[0] == "Economics") { + if (splitted.length () != 11) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } - for (int i = 0; i < NUM_WEAPONS; i++) - g_carefulWeaponPrefs[i] = splitted[i].ToInt (); + for (int i = 0; i < 11; i++) { + g_botBuyEconomyTable[i] = splitted[i].toInt32 (); + } + } + else if (pair[0] == "PersonalityNormal") { + if (splitted.length () != NUM_WEAPONS) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } + + for (int i = 0; i < NUM_WEAPONS; i++) { + g_normalWeaponPrefs[i] = splitted[i].toInt32 (); + } + } + else if (pair[0] == "PersonalityRusher") { + if (splitted.length () != NUM_WEAPONS) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } + + for (int i = 0; i < NUM_WEAPONS; i++) { + g_rusherWeaponPrefs[i] = splitted[i].toInt32 (); + } + } + else if (pair[0] == "PersonalityCareful") { + if (splitted.length () != NUM_WEAPONS) { + logEntry (true, LL_FATAL, "%s entry in general config is not valid.", pair[0].chars ()); + } + + for (int i = 0; i < NUM_WEAPONS; i++) { + g_carefulWeaponPrefs[i] = splitted[i].toInt32 (); + } } } - fp.Close (); + fp.close (); } // CHATTER SYSTEM INITIALIZATION - if ((g_gameFlags & GAME_SUPPORT_BOT_VOICE) && yb_communication_type.GetInt () == 2 && OpenConfig ("chatter.cfg", "Couldn't open chatter system configuration", &fp)) - { - const float ChatterInfinity = 99999.0f; - - // for faster loading - struct EventMap - { + if ((g_gameFlags & GAME_SUPPORT_BOT_VOICE) && yb_communication_type.integer () == 2 && openConfig ("chatter.cfg", "Couldn't open chatter system configuration", &fp)) { + struct EventMap { const char *str; int code; float repeat; - } chatterEventMap[] = { - { STRINGIFY (Radio_CoverMe), Radio_CoverMe, ChatterInfinity }, - { STRINGIFY (Radio_YouTakePoint), Radio_YouTakePoint, ChatterInfinity }, - { STRINGIFY (Radio_HoldPosition), Radio_HoldPosition, ChatterInfinity }, - { STRINGIFY (Radio_RegroupTeam), Radio_RegroupTeam, ChatterInfinity }, - { STRINGIFY (Radio_FollowMe), Radio_FollowMe, ChatterInfinity }, - { STRINGIFY (Radio_TakingFire), Radio_TakingFire, ChatterInfinity }, - { STRINGIFY (Radio_GoGoGo), Radio_GoGoGo, ChatterInfinity }, - { STRINGIFY (Radio_Fallback), Radio_Fallback, ChatterInfinity }, - { STRINGIFY (Radio_StickTogether), Radio_StickTogether, ChatterInfinity }, - { STRINGIFY (Radio_GetInPosition), Radio_GetInPosition, ChatterInfinity }, - { STRINGIFY (Radio_StormTheFront), Radio_StormTheFront, ChatterInfinity }, - { STRINGIFY (Radio_ReportTeam), Radio_ReportTeam, ChatterInfinity }, - { STRINGIFY (Radio_Affirmative), Radio_Affirmative, ChatterInfinity }, - { STRINGIFY (Radio_EnemySpotted), Radio_EnemySpotted, ChatterInfinity }, - { STRINGIFY (Radio_NeedBackup), Radio_NeedBackup, ChatterInfinity }, - { STRINGIFY (Radio_SectorClear), Radio_SectorClear, ChatterInfinity }, - { STRINGIFY (Radio_InPosition), Radio_InPosition, ChatterInfinity }, - { STRINGIFY (Radio_ReportingIn), Radio_ReportingIn, ChatterInfinity }, - { STRINGIFY (Radio_ShesGonnaBlow), Radio_ShesGonnaBlow, ChatterInfinity }, - { STRINGIFY (Radio_Negative), Radio_Negative, ChatterInfinity }, - { STRINGIFY (Radio_EnemyDown), Radio_EnemyDown, ChatterInfinity }, - { STRINGIFY (Chatter_DiePain), Chatter_DiePain, ChatterInfinity }, - { STRINGIFY (Chatter_GoingToPlantBomb), Chatter_GoingToPlantBomb, ChatterInfinity }, - { STRINGIFY (Chatter_GoingToGuardVIPSafety), Chatter_GoingToGuardVIPSafety, ChatterInfinity }, - { STRINGIFY (Chatter_RescuingHostages), Chatter_RescuingHostages, ChatterInfinity }, - { STRINGIFY (Chatter_TeamKill), Chatter_TeamKill, ChatterInfinity }, - { STRINGIFY (Chatter_GuardingVipSafety), Chatter_GuardingVipSafety, ChatterInfinity }, - { STRINGIFY (Chatter_PlantingC4), Chatter_PlantingC4, ChatterInfinity }, - { STRINGIFY (Chatter_InCombat), Chatter_InCombat, ChatterInfinity }, - { STRINGIFY (Chatter_SeeksEnemy), Chatter_SeeksEnemy, ChatterInfinity }, - { STRINGIFY (Chatter_Nothing), Chatter_Nothing, ChatterInfinity }, - { STRINGIFY (Chatter_EnemyDown), Chatter_EnemyDown, ChatterInfinity }, - { STRINGIFY (Chatter_UseHostage), Chatter_UseHostage, ChatterInfinity }, - { STRINGIFY (Chatter_WonTheRound), Chatter_WonTheRound, ChatterInfinity }, - { STRINGIFY (Chatter_QuicklyWonTheRound), Chatter_QuicklyWonTheRound, ChatterInfinity }, - { STRINGIFY (Chatter_NoEnemiesLeft), Chatter_NoEnemiesLeft, ChatterInfinity }, - { STRINGIFY (Chatter_FoundBombPlace), Chatter_FoundBombPlace, ChatterInfinity }, - { STRINGIFY (Chatter_WhereIsTheBomb), Chatter_WhereIsTheBomb, ChatterInfinity }, - { STRINGIFY (Chatter_DefendingBombSite), Chatter_DefendingBombSite, ChatterInfinity }, - { STRINGIFY (Chatter_BarelyDefused), Chatter_BarelyDefused, ChatterInfinity }, - { STRINGIFY (Chatter_NiceshotCommander), Chatter_NiceshotCommander, ChatterInfinity }, - { STRINGIFY (Chatter_ReportingIn), Chatter_ReportingIn, 10.0f }, - { STRINGIFY (Chatter_SpotTheBomber), Chatter_SpotTheBomber, 4.3f }, - { STRINGIFY (Chatter_VIPSpotted), Chatter_VIPSpotted, 5.3f }, - { STRINGIFY (Chatter_FriendlyFire), Chatter_FriendlyFire, 2.1f }, - { STRINGIFY (Chatter_GotBlinded), Chatter_GotBlinded, 5.0f }, - { STRINGIFY (Chatter_GuardDroppedC4), Chatter_GuardDroppedC4, 3.0f }, - { STRINGIFY (Chatter_DefusingC4), Chatter_DefusingC4, 3.0f }, - { STRINGIFY (Chatter_FoundC4), Chatter_FoundC4, 5.5f }, - { STRINGIFY (Chatter_ScaredEmotion), Chatter_ScaredEmotion, 6.1f }, - { STRINGIFY (Chatter_HeardEnemy), Chatter_ScaredEmotion, 12.8f }, - { STRINGIFY (Chatter_SniperWarning), Chatter_SniperWarning, 4.3f }, - { STRINGIFY (Chatter_SniperKilled), Chatter_SniperKilled, 2.1f }, - { STRINGIFY (Chatter_OneEnemyLeft), Chatter_OneEnemyLeft, 2.5f }, - { STRINGIFY (Chatter_TwoEnemiesLeft), Chatter_TwoEnemiesLeft, 2.5f }, - { STRINGIFY (Chatter_ThreeEnemiesLeft), Chatter_ThreeEnemiesLeft, 2.5f }, - { STRINGIFY (Chatter_NiceshotPall), Chatter_NiceshotPall, 2.0f }, - { STRINGIFY (Chatter_GoingToGuardHostages), Chatter_GoingToGuardHostages, 3.0f }, - { STRINGIFY (Chatter_GoingToGuardDoppedBomb), Chatter_GoingToGuardDoppedBomb, 3.0f }, - { STRINGIFY (Chatter_OnMyWay), Chatter_OnMyWay, 1.5f }, - { STRINGIFY (Chatter_LeadOnSir), Chatter_LeadOnSir, 5.0f }, - { STRINGIFY (Chatter_Pinned_Down), Chatter_Pinned_Down, 5.0f }, - { STRINGIFY (Chatter_GottaFindTheBomb), Chatter_GottaFindTheBomb, 3.0f }, - { STRINGIFY (Chatter_You_Heard_The_Man), Chatter_You_Heard_The_Man, 3.0f }, - { STRINGIFY (Chatter_Lost_The_Commander), Chatter_Lost_The_Commander, 4.5f }, - { STRINGIFY (Chatter_NewRound), Chatter_NewRound, 3.5f }, - { STRINGIFY (Chatter_CoverMe), Chatter_CoverMe, 3.5f }, - { STRINGIFY (Chatter_BehindSmoke), Chatter_BehindSmoke, 3.5f }, - { STRINGIFY (Chatter_BombSiteSecured), Chatter_BombSiteSecured, 3.5f }, - { STRINGIFY (Chatter_GoingToCamp), Chatter_GoingToCamp, 5.0f }, - { STRINGIFY (Chatter_Camp), Chatter_Camp, 5.0f }, + { "Radio_CoverMe", RADIO_COVER_ME, MAX_CHATTER_REPEAT }, + { "Radio_YouTakePoint", RADIO_YOU_TAKE_THE_POINT, MAX_CHATTER_REPEAT }, + { "Radio_HoldPosition", RADIO_HOLD_THIS_POSITION, MAX_CHATTER_REPEAT }, + { "Radio_RegroupTeam", RADIO_REGROUP_TEAM, MAX_CHATTER_REPEAT }, + { "Radio_FollowMe", RADIO_FOLLOW_ME, MAX_CHATTER_REPEAT }, + { "Radio_TakingFire", RADIO_TAKING_FIRE, MAX_CHATTER_REPEAT }, + { "Radio_GoGoGo", RADIO_GO_GO_GO, MAX_CHATTER_REPEAT }, + { "Radio_Fallback", RADIO_TEAM_FALLBACK, MAX_CHATTER_REPEAT }, + { "Radio_StickTogether", RADIO_STICK_TOGETHER_TEAM, MAX_CHATTER_REPEAT }, + { "Radio_GetInPosition", RADIO_GET_IN_POSITION, MAX_CHATTER_REPEAT }, + { "Radio_StormTheFront", RADIO_STORM_THE_FRONT, MAX_CHATTER_REPEAT }, + { "Radio_ReportTeam", RADIO_REPORT_TEAM, MAX_CHATTER_REPEAT }, + { "Radio_Affirmative", RADIO_AFFIRMATIVE, MAX_CHATTER_REPEAT }, + { "Radio_EnemySpotted", RADIO_ENEMY_SPOTTED, MAX_CHATTER_REPEAT }, + { "Radio_NeedBackup", RADIO_NEED_BACKUP, MAX_CHATTER_REPEAT }, + { "Radio_SectorClear", RADIO_SECTOR_CLEAR, MAX_CHATTER_REPEAT }, + { "Radio_InPosition", RADIO_IN_POSITION, MAX_CHATTER_REPEAT }, + { "Radio_ReportingIn", RADIO_REPORTING_IN, MAX_CHATTER_REPEAT }, + { "Radio_ShesGonnaBlow", RADIO_SHES_GONNA_BLOW, MAX_CHATTER_REPEAT }, + { "Radio_Negative", RADIO_NEGATIVE, MAX_CHATTER_REPEAT }, + { "Radio_EnemyDown", RADIO_ENEMY_DOWN, MAX_CHATTER_REPEAT }, + { "Chatter_DiePain", CHATTER_PAIN_DIED, MAX_CHATTER_REPEAT }, + { "Chatter_GoingToPlantBomb", CHATTER_GOING_TO_PLANT_BOMB, MAX_CHATTER_REPEAT }, + { "Chatter_GoingToGuardVIPSafety", CHATTER_GOING_TO_GUARD_VIP_SAFETY, MAX_CHATTER_REPEAT }, + { "Chatter_RescuingHostages", CHATTER_RESCUING_HOSTAGES, MAX_CHATTER_REPEAT }, + { "Chatter_TeamKill", CHATTER_TEAM_ATTACK, MAX_CHATTER_REPEAT }, + { "Chatter_GuardingVipSafety", CHATTER_GUARDING_VIP_SAFETY, MAX_CHATTER_REPEAT }, + { "Chatter_PlantingC4", CHATTER_PLANTING_BOMB, MAX_CHATTER_REPEAT }, + { "Chatter_InCombat", CHATTER_IN_COMBAT, MAX_CHATTER_REPEAT }, + { "Chatter_SeeksEnemy", CHATTER_SEEK_ENEMY, MAX_CHATTER_REPEAT }, + { "Chatter_Nothing", CHATTER_NOTHING, MAX_CHATTER_REPEAT }, + { "Chatter_EnemyDown", CHATTER_ENEMY_DOWN, MAX_CHATTER_REPEAT }, + { "Chatter_UseHostage", CHATTER_USING_HOSTAGES, MAX_CHATTER_REPEAT }, + { "Chatter_WonTheRound", CHATTER_WON_THE_ROUND, MAX_CHATTER_REPEAT }, + { "Chatter_QuicklyWonTheRound", CHATTER_QUICK_WON_ROUND, MAX_CHATTER_REPEAT }, + { "Chatter_NoEnemiesLeft", CHATTER_NO_ENEMIES_LEFT, MAX_CHATTER_REPEAT }, + { "Chatter_FoundBombPlace", CHATTER_FOUND_BOMB_PLACE, MAX_CHATTER_REPEAT }, + { "Chatter_WhereIsTheBomb", CHATTER_WHERE_IS_THE_BOMB, MAX_CHATTER_REPEAT }, + { "Chatter_DefendingBombSite", CHATTER_DEFENDING_BOMBSITE, MAX_CHATTER_REPEAT }, + { "Chatter_BarelyDefused", CHATTER_BARELY_DEFUSED, MAX_CHATTER_REPEAT }, + { "Chatter_NiceshotCommander", CHATTER_NICESHOT_COMMANDER, MAX_CHATTER_REPEAT }, + { "Chatter_ReportingIn", CHATTER_REPORTING_IN, 10.0f }, + { "Chatter_SpotTheBomber", CHATTER_SPOT_THE_BOMBER, 4.3f }, + { "Chatter_VIPSpotted", CHATTER_VIP_SPOTTED, 5.3f }, + { "Chatter_FriendlyFire", CHATTER_FRIENDLY_FIRE, 2.1f }, + { "Chatter_GotBlinded", CHATTER_BLINDED, 5.0f }, + { "Chatter_GuardDroppedC4", CHATTER_GUARDING_DROPPED_BOMB, 3.0f }, + { "Chatter_DefusingC4", CHATTER_DEFUSING_BOMB, 3.0f }, + { "Chatter_FoundC4", CHATTER_FOUND_BOMB, 5.5f }, + { "Chatter_ScaredEmotion", CHATTER_SCARED_EMOTE, 6.1f }, + { "Chatter_HeardEnemy", CHATTER_SCARED_EMOTE, 12.8f }, + { "Chatter_SniperWarning", CHATTER_SNIPER_WARNING, 14.3f }, + { "Chatter_SniperKilled", CHATTER_SNIPER_KILLED, 2.1f }, + { "Chatter_OneEnemyLeft", CHATTER_ONE_ENEMY_LEFT, 2.5f }, + { "Chatter_TwoEnemiesLeft", CHATTER_TWO_ENEMIES_LEFT, 2.5f }, + { "Chatter_ThreeEnemiesLeft", CHATTER_THREE_ENEMIES_LEFT, 2.5f }, + { "Chatter_NiceshotPall", CHATTER_NICESHOT_PALL, 2.0f }, + { "Chatter_GoingToGuardHostages", CHATTER_GOING_TO_GUARD_HOSTAGES, 3.0f }, + { "Chatter_GoingToGuardDoppedBomb", CHATTER_GOING_TO_GUARD_DROPPED_BOMB, 3.0f }, + { "Chatter_OnMyWay", CHATTER_ON_MY_WAY, 1.5f }, + { "Chatter_LeadOnSir", CHATTER_LEAD_ON_SIR, 5.0f }, + { "Chatter_Pinned_Down", CHATTER_PINNED_DOWN, 5.0f }, + { "Chatter_GottaFindTheBomb", CHATTER_GOTTA_FIND_BOMB, 3.0f }, + { "Chatter_You_Heard_The_Man", CHATTER_YOU_HEARD_THE_MAN, 3.0f }, + { "Chatter_Lost_The_Commander", CHATTER_LOST_COMMANDER, 4.5f }, + { "Chatter_NewRound", CHATTER_NEW_ROUND, 3.5f }, + { "Chatter_CoverMe", CHATTER_COVER_ME, 3.5f }, + { "Chatter_BehindSmoke", CHATTER_BEHIND_SMOKE, 3.5f }, + { "Chatter_BombSiteSecured", CHATTER_BOMB_SITE_SECURED, 3.5f }, + { "Chatter_GoingToCamp", CHATTER_GOING_TO_CAMP, 25.0f }, + { "Chatter_Camp", CHATTER_CAMP, 25.0f }, }; - while (fp.GetBuffer (line, 511)) - { - SKIP_COMMENTS (); - - if (strncmp (line, "RewritePath", 11) == 0) - { - extern ConVar yb_chatter_path; - yb_chatter_path.SetString (String (&line[12]).Trim ()); + while (fp.gets (lineBuffer, 511)) { + if (isCommentLine (lineBuffer)) { + continue; } - else if (strncmp (line, "Event", 5) == 0) - { - auto items = String (&line[6]).Split ('='); - if (items.GetElementNumber () != 2) - { - AddLogEntry (true, LL_ERROR, "Error in chatter config file syntax... Please correct all errors."); + if (strncmp (lineBuffer, "RewritePath", 11) == 0) { + extern ConVar yb_chatter_path; + yb_chatter_path.set (String (&lineBuffer[12]).trim ().chars ()); + } + else if (strncmp (lineBuffer, "Event", 5) == 0) { + auto items = String (&lineBuffer[6]).split ("="); + + if (items.length () != 2) { + logEntry (true, LL_ERROR, "Error in chatter config file syntax... Please correct all errors."); continue; } - FOR_EACH_AE (items, i) - items[i].Trim ().Trim (); // double trim + for (auto &item : items) { + item.trim (); + } + items[1].trim ("(;)"); - // just to be more unique :) - items[1].TrimLeft ('('); - items[1].TrimRight (';'); - items[1].TrimRight (')'); - - for (int i = 0; i < ARRAYSIZE_HLSDK (chatterEventMap); i++) - { + for (size_t i = 0; i < cr::arrsize (chatterEventMap); i++) { auto event = &chatterEventMap[i]; - if (A_stricmp (event->str, items[0].GetBuffer ()) == 0) - { + if (stricmp (event->str, items[0].chars ()) == 0) { // this does common work of parsing comma-separated chatter line - auto sounds = items[1].Split (','); + auto sounds = items[1].split (","); - FOR_EACH_AE (sounds, j) - { - sounds[j].Trim ().TrimQuotes (); + for (auto &sound : sounds) { + sound.trim ().trim ("\""); + float duration = engine.getWaveLen (sound.chars ()); - float duration = engine.GetWaveLength (sounds[j]); - - if (duration > 0.0f) - g_chatterFactory[event->code].Push ({ sounds[j], event->repeat, duration }, true); + if (duration > 0.0f) { + g_chatterFactory[event->code].push ({ sound, event->repeat, duration }); + } } - sounds.RemoveAll (); + sounds.clear (); } } } } - fp.Close (); + fp.close (); } - else - { - yb_communication_type.SetInt (1); - AddLogEntry (true, LL_DEFAULT, "Chatter Communication disabled."); + else { + yb_communication_type.set (1); + logEntry (true, LL_DEFAULT, "Chatter Communication disabled."); } // LOCALIZER INITITALIZATION - if (OpenConfig ("lang.cfg", "Specified language not found", &fp, true) && !(g_gameFlags & GAME_LEGACY)) - { - if (engine.IsDedicatedServer ()) + if (!(g_gameFlags & GAME_LEGACY) && openConfig ("lang.cfg", "Specified language not found", &fp, true)) { + if (engine.isDedicated ()) { return; // dedicated server will use only english translation + } + enum Lang { LANG_ORIGINAL, LANG_TRANSLATED, LANG_UNDEFINED } langState = static_cast (LANG_UNDEFINED); - enum Lang { Lang_Original, Lang_Translate } langState = static_cast (2); + char buffer[1024] = { 0, }; + Pair lang; - char buffer[1024]; - TranslatorPair temp = {"", ""}; + while (fp.gets (lineBuffer, 255)) { + if (isCommentLine (lineBuffer)) { + continue; + } - while (fp.GetBuffer (line, 255)) - { - if (strncmp (line, "[ORIGINAL]", 10) == 0) - { - langState = Lang_Original; + if (strncmp (lineBuffer, "[ORIGINAL]", 10) == 0) { + langState = LANG_ORIGINAL; - if (!IsNullString (buffer)) - { - String::TrimExternalBuffer (buffer); - temp.translated = A_strdup (buffer); - buffer[0] = 0x0; + if (buffer[0] != 0) { + lang.second = buffer; + lang.second.trim (); + + buffer[0] = 0; } - if (!IsNullString (temp.translated) && !IsNullString (temp.original)) - engine.PushTranslationPair (temp); + if (!lang.second.empty () && !lang.first.empty ()) { + engine.addTranslation (lang.first, lang.second); + } } - else if (strncmp (line, "[TRANSLATED]", 12) == 0) - { - String::TrimExternalBuffer (buffer); - temp.original = A_strdup (buffer); - buffer[0] = 0x0; + else if (strncmp (lineBuffer, "[TRANSLATED]", 12) == 0) { - langState = Lang_Translate; + lang.first = buffer; + lang.first.trim (); + + langState = LANG_TRANSLATED; + buffer[0] = 0; } - else - { - switch (langState) - { - case Lang_Original: - strncat (buffer, line, 1024 - 1 - strlen (buffer)); + else { + switch (langState) { + case LANG_ORIGINAL: + strncat (buffer, lineBuffer, 1024 - 1 - strlen (buffer)); break; - case Lang_Translate: - strncat (buffer, line, 1024 - 1 - strlen (buffer)); + case LANG_TRANSLATED: + strncat (buffer, lineBuffer, 1024 - 1 - strlen (buffer)); + break; + + case LANG_UNDEFINED: break; } } } - fp.Close (); + fp.close (); + } + else if (g_gameFlags & GAME_LEGACY) { + logEntry (true, LL_DEFAULT, "Multilingual system disabled, due to your Counter-Strike Version!"); + } + else if (strcmp (yb_language.str (), "en") != 0) { + logEntry (true, LL_ERROR, "Couldn't load language configuration"); } - else if (g_gameFlags & GAME_LEGACY) - AddLogEntry (true, LL_DEFAULT, "Multilingual system disabled, due to your Counter-Strike Version!"); - else if (strcmp (yb_language.GetString (), "en") != 0) - AddLogEntry (true, LL_ERROR, "Couldn't load language configuration"); // set personality weapon pointers here g_weaponPrefs[PERSONALITY_NORMAL] = reinterpret_cast (&g_normalWeaponPrefs); @@ -899,8 +948,7 @@ void InitConfig (void) g_timePerSecondUpdate = 0.0f; } -void GameDLLInit (void) -{ +void GameDLLInit (void) { // this function is a one-time call, and appears to be the second function called in the // DLL after GiveFntprsToDll() has been called. Its purpose is to tell the MOD DLL to // initialize the game before the engine actually hooks into it with its video frames and @@ -909,39 +957,46 @@ void GameDLLInit (void) // server is enabled. Here is a good place to do our own game session initialization, and // to register by the engine side the server commands we need to administrate our bots. - auto CommandHandler = [] (void) - { - if (BotCommandHandler (g_hostEntity, IsNullString (CMD_ARGV (1)) ? "help" : CMD_ARGV (1), CMD_ARGV (2), CMD_ARGV (3), CMD_ARGV (4), CMD_ARGV (5), CMD_ARGV (6), CMD_ARGV (0)) == 0) - engine.Printf ("Unknown command: %s", CMD_ARGV (1)); + auto commandHandler = [](void) { + if (handleBotCommands (g_hostEntity, isEmptyStr (g_engfuncs.pfnCmd_Argv (1)) ? "help" : g_engfuncs.pfnCmd_Argv (1), g_engfuncs.pfnCmd_Argv (2), g_engfuncs.pfnCmd_Argv (3), g_engfuncs.pfnCmd_Argv (4), g_engfuncs.pfnCmd_Argv (5), g_engfuncs.pfnCmd_Argv (6), g_engfuncs.pfnCmd_Argv (0)) == 0) { + engine.print ("Unknown command: %s", g_engfuncs.pfnCmd_Argv (1)); + } }; // register server command(s) - engine.RegisterCmd ("yapb", CommandHandler); - engine.RegisterCmd ("yb", CommandHandler); - - // execute main config - engine.IssueCmd ("exec addons/yapb/conf/yapb.cfg"); + engine.registerCmd ("yapb", commandHandler); + engine.registerCmd ("yb", commandHandler); // set correct version string - yb_version.SetString (FormatBuffer ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, GenerateBuildNumber ())); + yb_version.set (format ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, buildNumber ())); + + // execute main config + execBotConfigs (true); // register fake metamod command handler if we not! under mm - if (!(g_gameFlags & GAME_METAMOD)) - { - engine.RegisterCmd ("meta", [] (void) - { - engine.Printf ("You're launched standalone version of yapb. Metamod is not installed or not enabled!"); - }); + if (!(g_gameFlags & GAME_METAMOD)) { + engine.registerCmd ("meta", [](void) { engine.print ("You're launched standalone version of yapb. Metamod is not installed or not enabled!"); }); } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_IGNORED); + // elite price is 1000$ on older versions of cs... + if (g_gameFlags & GAME_LEGACY) { + for (int i = 0; i < NUM_WEAPONS; i++) { + auto &weapon = g_weaponSelect[i]; - (*g_functionTable.pfnGameInit) (); + if (weapon.id == WEAPON_ELITE) { + weapon.price = 1000; + break; + } + } + } + + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_IGNORED); + } + g_functionTable.pfnGameInit (); } -void Touch (edict_t *pentTouched, edict_t *pentOther) -{ +void Touch (edict_t *pentTouched, edict_t *pentOther) { // this function is called when two entities' bounding boxes enter in collision. For example, // when a player walks upon a gun, the player entity bounding box collides to the gun entity // bounding box, and the result is that this function is called. It is used by the game for @@ -953,136 +1008,59 @@ void Touch (edict_t *pentTouched, edict_t *pentOther) // the two entities both have velocities, for example two players colliding, this function // is called twice, once for each entity moving. - if (!engine.IsNullEntity (pentOther) && (pentOther->v.flags & FL_FAKECLIENT)) - { - Bot *bot = bots.GetBot (pentOther); + if (!engine.isNullEntity (pentTouched) && (pentTouched->v.flags & FL_FAKECLIENT) && pentOther != engine.getStartEntity ()) { + Bot *bot = bots.getBot (pentTouched); - if (bot != nullptr) - { - if (IsValidPlayer (pentTouched)) - bot->AvoidPlayersOnTheWay (pentTouched); - else - bot->VerifyBreakable (pentTouched); + if (bot != nullptr && pentOther != bot->ent ()) { + + if (isPlayer (pentOther) && isAlive (pentOther)) { + bot->avoidIncomingPlayers (pentOther); + } + else { + bot->processBreakables (pentOther); + } } } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_IGNORED); - (*g_functionTable.pfnTouch) (pentTouched, pentOther); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_IGNORED); + } + g_functionTable.pfnTouch (pentTouched, pentOther); } -int Spawn (edict_t *ent) -{ +int Spawn (edict_t *ent) { // this function asks the game DLL to spawn (i.e, give a physical existence in the virtual // world, in other words to 'display') the entity pointed to by ent in the game. The // Spawn() function is one of the functions any entity is supposed to have in the game DLL, // and any MOD is supposed to implement one for each of its entities. - // for faster access - const char *entityClassname = STRING (ent->v.classname); + engine.precache (); - if (strcmp (entityClassname, "worldspawn") == 0) - { - engine.Precache (ent); - engine.PushRegisteredConVarsToEngine (true); - - PRECACHE_SOUND (ENGINE_STR ("weapons/xbow_hit1.wav")); // waypoint add - PRECACHE_SOUND (ENGINE_STR ("weapons/mine_activate.wav")); // waypoint delete - PRECACHE_SOUND (ENGINE_STR ("common/wpn_hudoff.wav")); // path add/delete start - PRECACHE_SOUND (ENGINE_STR ("common/wpn_hudon.wav")); // path add/delete done - PRECACHE_SOUND (ENGINE_STR ("common/wpn_moveselect.wav")); // path add/delete cancel - PRECACHE_SOUND (ENGINE_STR ("common/wpn_denyselect.wav")); // path add/delete error - - RoundInit (); - g_mapType = 0; // reset map type as worldspawn is the first entity spawned - - // detect official csbots here, as they causing crash in linkent code when active for some reason - if (!(g_gameFlags & GAME_LEGACY) && g_engfuncs.pfnCVarGetPointer ("bot_stop") != nullptr) - g_gameFlags |= GAME_OFFICIAL_CSBOT; - } - else if (strcmp (entityClassname, "player_weaponstrip") == 0) - { - if ((g_gameFlags & GAME_LEGACY) && (STRING (ent->v.target))[0] == '0') - ent->v.target = ent->v.targetname = ALLOC_STRING ("fake"); - else - { - REMOVE_ENTITY (ent); - - if (g_gameFlags & GAME_METAMOD) - RETURN_META_VALUE (MRES_SUPERCEDE, 0); - - return 0; - } - } - else if (strcmp (entityClassname, "info_player_start") == 0) - { - SET_MODEL (ent, ENGINE_STR ("models/player/urban/urban.mdl")); - - ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency - ent->v.renderamt = 127; // set its transparency amount - ent->v.effects |= EF_NODRAW; - } - else if (strcmp (entityClassname, "info_player_deathmatch") == 0) - { - SET_MODEL (ent, ENGINE_STR ("models/player/terror/terror.mdl")); - - ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency - ent->v.renderamt = 127; // set its transparency amount - ent->v.effects |= EF_NODRAW; - } - - else if (strcmp (entityClassname, "info_vip_start") == 0) - { - SET_MODEL (ent, ENGINE_STR ("models/player/vip/vip.mdl")); - - ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency - ent->v.renderamt = 127; // set its transparency amount - ent->v.effects |= EF_NODRAW; - } - else if (strcmp (entityClassname, "func_vip_safetyzone") == 0 || strcmp (STRING (ent->v.classname), "info_vip_safetyzone") == 0) - g_mapType |= MAP_AS; // assassination map - - else if (strcmp (entityClassname, "hostage_entity") == 0) - g_mapType |= MAP_CS; // rescue map - - else if (strcmp (entityClassname, "func_bomb_target") == 0 || strcmp (STRING (ent->v.classname), "info_bomb_target") == 0) - g_mapType |= MAP_DE; // defusion map - - else if (strcmp (entityClassname, "func_escapezone") == 0) - g_mapType |= MAP_ES; - - // next maps doesn't have map-specific entities, so determine it by name - else if (strncmp (engine.GetMapName (), "fy_", 3) == 0) // fun map - g_mapType |= MAP_FY; - else if (strncmp (engine.GetMapName (), "ka_", 3) == 0) // knife arena map - g_mapType |= MAP_KA; - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META_VALUE (MRES_IGNORED, 0); + } + int result = g_functionTable.pfnSpawn (ent); // get result - int result = (*g_functionTable.pfnSpawn) (ent); // get result - - if (ent->v.rendermode == kRenderTransTexture) + if (ent->v.rendermode == kRenderTransTexture) { ent->v.flags &= ~FL_WORLDBRUSH; // clear the FL_WORLDBRUSH flag out of transparent ents - + } return result; } -void UpdateClientData (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd) -{ +void UpdateClientData (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd) { extern ConVar yb_latency_display; - if ((g_gameFlags & GAME_SUPPORT_SVC_PINGS) && yb_latency_display.GetInt () == 2) - bots.SendPingDataOffsets (const_cast (ent)); + if ((g_gameFlags & GAME_SUPPORT_SVC_PINGS) && yb_latency_display.integer () == 2) { + bots.sendPingOffsets (const_cast (ent)); + } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_functionTable.pfnUpdateClientData) (ent, sendweapons, cd); + } + g_functionTable.pfnUpdateClientData (ent, sendweapons, cd); } -int ClientConnect (edict_t *ent, const char *name, const char *addr, char rejectReason[128]) -{ +int ClientConnect (edict_t *ent, const char *name, const char *addr, char rejectReason[128]) { // this function is called in order to tell the MOD DLL that a client attempts to connect the // game. The entity pointer of this client is ent, the name under which he connects is // pointed to by the pszName pointer, and its IP address string is pointed by the pszAddress @@ -1104,19 +1082,17 @@ int ClientConnect (edict_t *ent, const char *name, const char *addr, char reject // the server for incoming clients. // check if this client is the listen server client - if (strcmp (addr, "loopback") == 0) + if (strcmp (addr, "loopback") == 0) { g_hostEntity = ent; // save the edict of the listen server client... + } - bots.AdjustQuota (true, ent); - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META_VALUE (MRES_IGNORED, 0); - - return (*g_functionTable.pfnClientConnect) (ent, name, addr, rejectReason); + } + return g_functionTable.pfnClientConnect (ent, name, addr, rejectReason); } -void ClientDisconnect (edict_t *ent) -{ +void ClientDisconnect (edict_t *ent) { // this function is called whenever a client is VOLUNTARILY disconnected from the server, // either because the client dropped the connection, or because the server dropped him from // the game (latency timeout). The effect is the freeing of a client slot on the server. Note @@ -1128,58 +1104,52 @@ void ClientDisconnect (edict_t *ent) // to reset his entity pointer for safety. There are still a few server frames to go once a // listen server client disconnects, and we don't want to send him any sort of message then. - int index = engine.IndexOfEntity (ent) - 1; + int index = engine.indexOfEntity (ent) - 1; - InternalAssert (index >= 0 && index < MAX_ENGINE_PLAYERS); + if (index >= 0 && index < MAX_ENGINE_PLAYERS) { + auto bot = bots.getBot (index); - Bot *bot = bots.GetBot (index); - - // check if its a bot - if (bot != nullptr && bot->pev == &ent->v) - { - bot->EnableChatterIcon (false); - bots.Free (index); + // check if its a bot + if (bot != nullptr && bot->pev == &ent->v) { + bot->showChaterIcon (false); + bots.destroy (index); + } } - - bots.AdjustQuota (false, ent); - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_functionTable.pfnClientDisconnect) (ent); + } + g_functionTable.pfnClientDisconnect (ent); } -void ClientUserInfoChanged (edict_t *ent, char *infobuffer) -{ +void ClientUserInfoChanged (edict_t *ent, char *infobuffer) { // this function is called when a player changes model, or changes team. Occasionally it // enforces rules on these changes (for example, some MODs don't want to allow players to // change their player model). But most commonly, this function is in charge of handling // team changes, recounting the teams population, etc... - if (engine.IsDedicatedServer () && !IsValidBot (ent)) - { - const char *passwordField = yb_password_key.GetString (); - const char *password = yb_password.GetString (); + if (engine.isDedicated () && !isFakeClient (ent)) { + const String &key = yb_password_key.str (); + const String &password = yb_password.str (); - if (!IsNullString (passwordField) || !IsNullString (password)) - { - int clientIndex = engine.IndexOfEntity (ent) - 1; + if (!key.empty () && !password.empty ()) { + int clientIndex = engine.indexOfEntity (ent) - 1; - if (strcmp (password, INFOKEY_VALUE (infobuffer, const_cast (passwordField))) == 0) + if (password == g_engfuncs.pfnInfoKeyValue (infobuffer, key.chars ())) { g_clients[clientIndex].flags |= CF_ADMIN; - else + } + else { g_clients[clientIndex].flags &= ~CF_ADMIN; + } } } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_functionTable.pfnClientUserInfoChanged) (ent, infobuffer); + } + g_functionTable.pfnClientUserInfoChanged (ent, infobuffer); } -void ClientCommand (edict_t *ent) -{ +void ClientCommand (edict_t *ent) { // this function is called whenever the client whose player entity is ent issues a client // command. How it works is that clients all have a global string in their client DLL that // stores the command string; if ever that string is filled with characters, the client DLL @@ -1193,46 +1163,41 @@ void ClientCommand (edict_t *ent) // so as this is just a bot DLL, not a MOD. The purpose is not to add functionality to // clients. Hence it can lack of commenting a bit, since this code is very subject to change. - const char *command = CMD_ARGV (0); - const char *arg1 = CMD_ARGV (1); + const char *command = g_engfuncs.pfnCmd_Argv (0); + const char *arg1 = g_engfuncs.pfnCmd_Argv (1); static int fillServerTeam = 5; static bool fillCommand = false; - int issuerPlayerIndex = engine.IndexOfEntity (ent) - 1; + int issuerPlayerIndex = engine.indexOfEntity (ent) - 1; - if (!engine.IsBotCommand () && (ent == g_hostEntity || (g_clients[issuerPlayerIndex].flags & CF_ADMIN))) - { - if (A_stricmp (command, "yapb") == 0 || A_stricmp (command, "yb") == 0) - { - int state = BotCommandHandler (ent, IsNullString (arg1) ? "help" : arg1, CMD_ARGV (2), CMD_ARGV (3), CMD_ARGV (4), CMD_ARGV (5), CMD_ARGV (6), CMD_ARGV (0)); + if (!engine.isBotCmd () && (ent == g_hostEntity || (g_clients[issuerPlayerIndex].flags & CF_ADMIN))) { + if (stricmp (command, "yapb") == 0 || stricmp (command, "yb") == 0) { + int state = handleBotCommands (ent, isEmptyStr (arg1) ? "help" : arg1, g_engfuncs.pfnCmd_Argv (2), g_engfuncs.pfnCmd_Argv (3), g_engfuncs.pfnCmd_Argv (4), g_engfuncs.pfnCmd_Argv (5), g_engfuncs.pfnCmd_Argv (6), g_engfuncs.pfnCmd_Argv (0)); - switch (state) - { + switch (state) { case 0: - engine.ClientPrintf (ent, "Unknown command: %s", arg1); + engine.clientPrint (ent, "Unknown command: %s", arg1); break; case 2: - engine.ClientPrintf (ent, "Command %s, can only be executed from server console.", arg1); + engine.clientPrint (ent, "Command \"%s\", can only be executed from server console.", arg1); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (A_stricmp (command, "menuselect") == 0 && !IsNullString (arg1) && g_clients[issuerPlayerIndex].menu != BOT_MENU_INVALID) - { + else if (stricmp (command, "menuselect") == 0 && !isEmptyStr (arg1) && g_clients[issuerPlayerIndex].menu != BOT_MENU_INVALID) { Client *client = &g_clients[issuerPlayerIndex]; int selection = atoi (arg1); - if (client->menu == BOT_MENU_WAYPOINT_TYPE) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + if (client->menu == BOT_MENU_WAYPOINT_TYPE) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: @@ -1240,477 +1205,476 @@ void ClientCommand (edict_t *ent) case 5: case 6: case 7: - waypoints.Add (selection - 1); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_TYPE); + waypoints.push (selection - 1); + showMenu (ent, BOT_MENU_WAYPOINT_TYPE); break; case 8: - waypoints.Add (100); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_TYPE); + waypoints.push (100); + showMenu (ent, BOT_MENU_WAYPOINT_TYPE); break; case 9: - waypoints.SetLearnJumpWaypoint (); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_TYPE); + waypoints.startLearnJump (); + showMenu (ent, BOT_MENU_WAYPOINT_TYPE); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_WAYPOINT_FLAG) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WAYPOINT_FLAG) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: - waypoints.ToggleFlags (FLAG_NOHOSTAGE); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_FLAG); + waypoints.toggleFlags (FLAG_NOHOSTAGE); + showMenu (ent, BOT_MENU_WAYPOINT_FLAG); break; case 2: - waypoints.ToggleFlags (FLAG_TF_ONLY); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_FLAG); + waypoints.toggleFlags (FLAG_TF_ONLY); + showMenu (ent, BOT_MENU_WAYPOINT_FLAG); break; case 3: - waypoints.ToggleFlags (FLAG_CF_ONLY); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_FLAG); + waypoints.toggleFlags (FLAG_CF_ONLY); + showMenu (ent, BOT_MENU_WAYPOINT_FLAG); break; case 4: - waypoints.ToggleFlags (FLAG_LIFT); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_FLAG); + waypoints.toggleFlags (FLAG_LIFT); + showMenu (ent, BOT_MENU_WAYPOINT_FLAG); break; case 5: - waypoints.ToggleFlags (FLAG_SNIPER); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_FLAG); + waypoints.toggleFlags (FLAG_SNIPER); + showMenu (ent, BOT_MENU_WAYPOINT_FLAG); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_WAYPOINT_MAIN_PAGE1) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WAYPOINT_MAIN_PAGE1) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: if (g_waypointOn) - engine.IssueCmd ("yapb waypoint off"); + engine.execCmd ("yapb waypoint off"); else - engine.IssueCmd ("yapb waypoint on"); + engine.execCmd ("yapb waypoint on"); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); break; case 2: g_waypointOn = true; - waypoints.CacheWaypoint (); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); + waypoints.cachePoint (); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); break; case 3: g_waypointOn = true; - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_PATH); + showMenu (ent, BOT_MENU_WAYPOINT_PATH); break; case 4: g_waypointOn = true; - waypoints.DeletePath (); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); + waypoints.erasePath (); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); break; case 5: g_waypointOn = true; - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_TYPE); + showMenu (ent, BOT_MENU_WAYPOINT_TYPE); break; case 6: g_waypointOn = true; - waypoints.Delete (); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); + waypoints.erase (INVALID_WAYPOINT_INDEX); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); break; case 7: g_waypointOn = true; - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_AUTOPATH); + showMenu (ent, BOT_MENU_WAYPOINT_AUTOPATH); break; case 8: g_waypointOn = true; - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_RADIUS); + showMenu (ent, BOT_MENU_WAYPOINT_RADIUS); break; case 9: - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_WAYPOINT_MAIN_PAGE2) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WAYPOINT_MAIN_PAGE2) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { - case 1: - { - int terrPoints = 0; - int ctPoints = 0; - int goalPoints = 0; - int rescuePoints = 0; - int campPoints = 0; - int sniperPoints = 0; - int noHostagePoints = 0; + switch (selection) { + case 1: { + int terrPoints = 0; + int ctPoints = 0; + int goalPoints = 0; + int rescuePoints = 0; + int campPoints = 0; + int sniperPoints = 0; + int noHostagePoints = 0; - for (int i = 0; i < g_numWaypoints; i++) - { - Path *path = waypoints.GetPath (i); + for (int i = 0; i < waypoints.length (); i++) { + Path &path = waypoints[i]; - if (path->flags & FLAG_TF_ONLY) - terrPoints++; - - if (path->flags & FLAG_CF_ONLY) - ctPoints++; - - if (path->flags & FLAG_GOAL) - goalPoints++; - - if (path->flags & FLAG_RESCUE) - rescuePoints++; - - if (path->flags & FLAG_CAMP) - campPoints++; - - if (path->flags & FLAG_SNIPER) - sniperPoints++; - - if (path->flags & FLAG_NOHOSTAGE) - noHostagePoints++; + if (path.flags & FLAG_TF_ONLY) { + terrPoints++; } - engine.Printf ("Waypoints: %d - T Points: %d\n" - "CT Points: %d - Goal Points: %d\n" - "Rescue Points: %d - Camp Points: %d\n" - "Block Hostage Points: %d - Sniper Points: %d\n", g_numWaypoints, terrPoints, ctPoints, goalPoints, rescuePoints, campPoints, noHostagePoints, sniperPoints); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + if (path.flags & FLAG_CF_ONLY) { + ctPoints++; + } + + if (path.flags & FLAG_GOAL) { + goalPoints++; + } + + if (path.flags & FLAG_RESCUE) { + rescuePoints++; + } + + if (path.flags & FLAG_CAMP) { + campPoints++; + } + + if (path.flags & FLAG_SNIPER) { + sniperPoints++; + } + + if (path.flags & FLAG_NOHOSTAGE) { + noHostagePoints++; + } } - break; + engine.print ("Waypoints: %d - T Points: %d\n" + "CT Points: %d - Goal Points: %d\n" + "Rescue Points: %d - Camp Points: %d\n" + "Block Hostage Points: %d - Sniper Points: %d\n", + waypoints.length (), terrPoints, ctPoints, goalPoints, rescuePoints, campPoints, noHostagePoints, sniperPoints); + + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + } break; case 2: g_waypointOn = true; g_autoWaypoint &= 1; g_autoWaypoint ^= 1; - engine.CenterPrintf ("Auto-Waypoint %s", g_autoWaypoint ? "Enabled" : "Disabled"); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + engine.centerPrint ("Auto-Waypoint %s", g_autoWaypoint ? "Enabled" : "Disabled"); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 3: g_waypointOn = true; - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_FLAG); + showMenu (ent, BOT_MENU_WAYPOINT_FLAG); break; case 4: - if (waypoints.NodesValid ()) - waypoints.Save (); - else - engine.CenterPrintf ("Waypoint not saved\nThere are errors, see console"); - - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + if (waypoints.checkNodes ()) { + waypoints.save (); + } + else { + engine.centerPrint ("Waypoint not saved\nThere are errors, see console"); + } + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 5: - waypoints.Save (); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + waypoints.save (); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 6: - waypoints.Load (); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + waypoints.load (); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 7: - if (waypoints.NodesValid ()) - engine.CenterPrintf ("Nodes works fine"); - else - engine.CenterPrintf ("There are errors, see console"); - - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + if (waypoints.checkNodes ()) { + engine.centerPrint ("Nodes works fine"); + } + else { + engine.centerPrint ("There are errors, see console"); + } + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 8: - engine.IssueCmd ("yapb wp on noclip"); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); + engine.execCmd ("yapb wp on noclip"); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE2); break; case 9: - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); + showMenu (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_WAYPOINT_RADIUS) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WAYPOINT_RADIUS) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - g_waypointOn = true; // turn waypoints on in case + g_waypointOn = true; // turn waypoints on in case const int radiusValue[] = {0, 8, 16, 32, 48, 64, 80, 96, 128}; - if ((selection >= 1) && (selection <= 9)) - waypoints.SetRadius (radiusValue[selection - 1]); + if (selection >= 1 && selection <= 9) { + waypoints.setRadius (radiusValue[selection - 1]); + showMenu (ent, BOT_MENU_WAYPOINT_RADIUS); + } - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_RADIUS); - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); - + } return; } - else if (client->menu == BOT_MENU_MAIN) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_MAIN) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: fillCommand = false; - DisplayMenuToClient (ent, BOT_MENU_CONTROL); + showMenu (ent, BOT_MENU_CONTROL); break; case 2: - DisplayMenuToClient (ent, BOT_MENU_FEATURES); + showMenu (ent, BOT_MENU_FEATURES); break; case 3: fillCommand = true; - DisplayMenuToClient (ent, BOT_MENU_TEAM_SELECT); + showMenu (ent, BOT_MENU_TEAM_SELECT); break; case 4: - bots.KillAll (); + bots.killAllBots (); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; + default: + showMenu (ent, BOT_MENU_MAIN); + break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_CONTROL) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_CONTROL) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: - bots.AddRandom (); - DisplayMenuToClient (ent, BOT_MENU_CONTROL); + bots.createRandom (true); + showMenu (ent, BOT_MENU_CONTROL); break; case 2: - DisplayMenuToClient (ent, BOT_MENU_DIFFICULTY); + showMenu (ent, BOT_MENU_DIFFICULTY); break; case 3: - bots.RemoveRandom (); - DisplayMenuToClient (ent, BOT_MENU_CONTROL); + bots.kickRandom (); + showMenu (ent, BOT_MENU_CONTROL); break; case 4: - bots.RemoveAll (); + bots.kickEveryone (); break; case 5: - bots.RemoveMenu (ent, 1); + bots.kickBotByMenu (ent, 1); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_FEATURES) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_FEATURES) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: - DisplayMenuToClient (ent, BOT_MENU_WEAPON_MODE); + showMenu (ent, BOT_MENU_WEAPON_MODE); break; case 2: - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_MAIN_PAGE1); + showMenu (ent, engine.isDedicated () ? BOT_MENU_FEATURES : BOT_MENU_WAYPOINT_MAIN_PAGE1); break; case 3: - DisplayMenuToClient (ent, BOT_MENU_PERSONALITY); + showMenu (ent, BOT_MENU_PERSONALITY); break; case 4: extern ConVar yb_debug; - yb_debug.SetInt (yb_debug.GetInt () ^ 1); + yb_debug.set (yb_debug.integer () ^ 1); - DisplayMenuToClient (ent, BOT_MENU_FEATURES); + showMenu (ent, BOT_MENU_FEATURES); break; case 5: - if (IsAlive (ent)) - DisplayMenuToClient (ent, BOT_MENU_COMMANDS); - else - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display - engine.CenterPrintf ("You're dead, and have no access to this menu"); + if (isAlive (ent)) { + showMenu (ent, BOT_MENU_COMMANDS); + } + else { + showMenu (ent, BOT_MENU_INVALID); // reset menu display + engine.centerPrint ("You're dead, and have no access to this menu"); } break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_COMMANDS) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_COMMANDS) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display Bot *bot = nullptr; - switch (selection) - { + switch (selection) { case 1: case 2: - if (FindNearestPlayer (reinterpret_cast (&bot), client->ent, 450.0f, true, true, true)) - { - if (!bot->m_hasC4 && !bot->HasHostage ()) - { - if (selection == 1) - bot->StartDoubleJump (client->ent); - else if (selection == 2) - bot->ResetDoubleJumpState (); + if (findNearestPlayer (reinterpret_cast (&bot), client->ent, 600.0f, true, true, true)) { + if (!bot->m_hasC4 && !bot->hasHostage ()) { + if (selection == 1) { + bot->startDoubleJump (client->ent); + } + else { + bot->resetDoubleJump (); + } } } - DisplayMenuToClient (ent, BOT_MENU_COMMANDS); + showMenu (ent, BOT_MENU_COMMANDS); break; case 3: case 4: - if (FindNearestPlayer (reinterpret_cast (&bot), ent, 450.0f, true, true, true)) - bot->DiscardWeaponForUser (ent, selection == 4 ? false : true); - - DisplayMenuToClient (ent, BOT_MENU_COMMANDS); + if (findNearestPlayer (reinterpret_cast (&bot), ent, 600.0f, true, true, true, true, selection == 4 ? false : true)) { + bot->dropWeaponForUser (ent, selection == 4 ? false : true); + } + showMenu (ent, BOT_MENU_COMMANDS); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); - + } return; } - else if (client->menu == BOT_MENU_WAYPOINT_AUTOPATH) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WAYPOINT_AUTOPATH) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - const float autoDistanceValue[] = {0.0f, 100.0f, 130.0f, 160.0f, 190.0f, 220.0f, 250.0f }; + const float autoDistanceValue[] = {0.0f, 100.0f, 130.0f, 160.0f, 190.0f, 220.0f, 250.0f}; - if (selection >= 1 && selection <= 7) + if (selection >= 1 && selection <= 7) { g_autoPathDistance = autoDistanceValue[selection - 1]; + } - if (g_autoPathDistance == 0.0f) - engine.CenterPrintf ("AutoPath disabled"); - else - engine.CenterPrintf ("AutoPath maximum distance set to %.2f", g_autoPathDistance); + if (g_autoPathDistance == 0.0f) { + engine.centerPrint ("AutoPath disabled"); + } + else { + engine.centerPrint ("AutoPath maximum distance set to %.2f", g_autoPathDistance); + } + showMenu (ent, BOT_MENU_WAYPOINT_AUTOPATH); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_AUTOPATH); - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); - + } return; } - else if (client->menu == BOT_MENU_WAYPOINT_PATH) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WAYPOINT_PATH) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: - waypoints.CreatePath (CONNECTION_OUTGOING); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_PATH); + waypoints.pathCreate (CONNECTION_OUTGOING); + showMenu (ent, BOT_MENU_WAYPOINT_PATH); break; case 2: - waypoints.CreatePath (CONNECTION_INCOMING); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_PATH); + waypoints.pathCreate (CONNECTION_INCOMING); + showMenu (ent, BOT_MENU_WAYPOINT_PATH); break; case 3: - waypoints.CreatePath (CONNECTION_BOTHWAYS); - DisplayMenuToClient (ent, BOT_MENU_WAYPOINT_PATH); + waypoints.pathCreate (CONNECTION_BOTHWAYS); + showMenu (ent, BOT_MENU_WAYPOINT_PATH); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); - + } return; } - else if (client->menu == BOT_MENU_DIFFICULTY) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_DIFFICULTY) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display client->menu = BOT_MENU_PERSONALITY; - switch (selection) - { + switch (selection) { case 1: g_storeAddbotVars[0] = 0; break; @@ -1732,151 +1696,150 @@ void ClientCommand (edict_t *ent) break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } + showMenu (ent, BOT_MENU_PERSONALITY); - if (client->menu == BOT_MENU_PERSONALITY) - DisplayMenuToClient (ent, BOT_MENU_PERSONALITY); - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); - + } return; } - else if (client->menu == BOT_MENU_TEAM_SELECT && fillCommand) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_TEAM_SELECT && fillCommand) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + if (selection < 3) { + extern ConVar mp_limitteams; + extern ConVar mp_autoteambalance; + + // turn off cvars if specified team + mp_limitteams.set (0); + mp_autoteambalance.set (0); + } + + switch (selection) { case 1: case 2: - // turn off cvars if specified team - CVAR_SET_STRING ("mp_limitteams", "0"); - CVAR_SET_STRING ("mp_autoteambalance", "0"); - case 5: fillServerTeam = selection; - DisplayMenuToClient (ent, BOT_MENU_DIFFICULTY); + showMenu (ent, BOT_MENU_DIFFICULTY); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_PERSONALITY && fillCommand) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_PERSONALITY && fillCommand) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: case 4: - bots.FillServer (fillServerTeam, selection - 2, g_storeAddbotVars[0]); + bots.serverFill (fillServerTeam, selection - 2, g_storeAddbotVars[0]); + showMenu (ent, BOT_MENU_INVALID); + break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_TEAM_SELECT) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_TEAM_SELECT) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 5: g_storeAddbotVars[1] = selection; - if (selection == 5) - { + + if (selection == 5) { g_storeAddbotVars[2] = 5; - bots.AddBot ("", g_storeAddbotVars[0], g_storeAddbotVars[3], g_storeAddbotVars[1], g_storeAddbotVars[2]); + bots.addbot ("", g_storeAddbotVars[0], g_storeAddbotVars[3], g_storeAddbotVars[1], g_storeAddbotVars[2], true); } - else - { - if (selection == 1) - DisplayMenuToClient (ent, BOT_MENU_TERRORIST_SELECT); - else - DisplayMenuToClient (ent, BOT_MENU_CT_SELECT); + else { + if (selection == 1) { + showMenu (ent, BOT_MENU_TERRORIST_SELECT); + } + else { + showMenu (ent, BOT_MENU_CT_SELECT); + } } break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_PERSONALITY) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_PERSONALITY) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: case 4: g_storeAddbotVars[3] = selection - 2; - DisplayMenuToClient (ent, BOT_MENU_TEAM_SELECT); + showMenu (ent, BOT_MENU_TEAM_SELECT); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_TERRORIST_SELECT || client->menu == BOT_MENU_CT_SELECT) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_TERRORIST_SELECT || client->menu == BOT_MENU_CT_SELECT) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: case 4: case 5: g_storeAddbotVars[2] = selection; - bots.AddBot ("", g_storeAddbotVars[0], g_storeAddbotVars[3], g_storeAddbotVars[1], g_storeAddbotVars[2]); + bots.addbot ("", g_storeAddbotVars[0], g_storeAddbotVars[3], g_storeAddbotVars[1], g_storeAddbotVars[2], true); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_WEAPON_MODE) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_WEAPON_MODE) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: @@ -1884,25 +1847,24 @@ void ClientCommand (edict_t *ent) case 5: case 6: case 7: - bots.SetWeaponMode (selection); - DisplayMenuToClient (ent, BOT_MENU_WEAPON_MODE); + bots.setWeaponMode (selection); + showMenu (ent, BOT_MENU_WEAPON_MODE); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_KICK_PAGE_1) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_KICK_PAGE_1) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: @@ -1911,29 +1873,28 @@ void ClientCommand (edict_t *ent) case 6: case 7: case 8: - bots.GetBot (selection - 1)->Kick (); - bots.RemoveMenu (ent, 1); + bots.kickBot (selection - 1); + bots.kickBotByMenu (ent, 1); break; case 9: - bots.RemoveMenu (ent, 2); + bots.kickBotByMenu (ent, 2); break; case 10: - DisplayMenuToClient (ent, BOT_MENU_CONTROL); + showMenu (ent, BOT_MENU_CONTROL); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_KICK_PAGE_2) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_KICK_PAGE_2) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: @@ -1942,29 +1903,28 @@ void ClientCommand (edict_t *ent) case 6: case 7: case 8: - bots.GetBot (selection + 8 - 1)->Kick (); - bots.RemoveMenu (ent, 2); + bots.kickBot (selection + 8 - 1); + bots.kickBotByMenu (ent, 2); break; case 9: - bots.RemoveMenu (ent, 3); + bots.kickBotByMenu (ent, 3); break; case 10: - bots.RemoveMenu (ent, 1); + bots.kickBotByMenu (ent, 1); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_KICK_PAGE_3) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_KICK_PAGE_3) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: @@ -1973,29 +1933,28 @@ void ClientCommand (edict_t *ent) case 6: case 7: case 8: - bots.GetBot (selection + 16 - 1)->Kick (); - bots.RemoveMenu (ent, 3); + bots.kickBot (selection + 16 - 1); + bots.kickBotByMenu (ent, 3); break; case 9: - bots.RemoveMenu (ent, 4); + bots.kickBotByMenu (ent, 4); break; case 10: - bots.RemoveMenu (ent, 2); + bots.kickBotByMenu (ent, 2); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } - else if (client->menu == BOT_MENU_KICK_PAGE_4) - { - DisplayMenuToClient (ent, BOT_MENU_INVALID); // reset menu display + else if (client->menu == BOT_MENU_KICK_PAGE_4) { + showMenu (ent, BOT_MENU_INVALID); // reset menu display - switch (selection) - { + switch (selection) { case 1: case 2: case 3: @@ -2004,104 +1963,96 @@ void ClientCommand (edict_t *ent) case 6: case 7: case 8: - bots.GetBot (selection + 24 - 1)->Kick (); - bots.RemoveMenu (ent, 4); + bots.kickBot (selection + 24 - 1); + bots.kickBotByMenu (ent, 4); break; case 10: - bots.RemoveMenu (ent, 3); + bots.kickBotByMenu (ent, 3); break; } - if (g_gameFlags & GAME_METAMOD) - RETURN_META (MRES_SUPERCEDE); + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_SUPERCEDE); + } return; } } } - if (!engine.IsBotCommand () && (A_stricmp (command, "say") == 0 || A_stricmp (command, "say_team") == 0)) - { + if (!engine.isBotCmd () && (stricmp (command, "say") == 0 || stricmp (command, "say_team") == 0)) { Bot *bot = nullptr; - if (FStrEq (arg1, "dropme") || FStrEq (arg1, "dropc4")) - { - if (FindNearestPlayer (reinterpret_cast (&bot), ent, 300.0f, true, true, true)) - bot->DiscardWeaponForUser (ent, IsNullString (strstr (arg1, "c4")) ? false : true); - + if (strcmp (arg1, "dropme") == 0 || strcmp (arg1, "dropc4") == 0) { + if (findNearestPlayer (reinterpret_cast (&bot), ent, 300.0f, true, true, true)) { + bot->dropWeaponForUser (ent, isEmptyStr (strstr (arg1, "c4")) ? false : true); + } return; } - bool isAlive = IsAlive (ent); + bool alive = isAlive (ent); int team = -1; - if (FStrEq (command, "say_team")) - team = engine.GetTeam (ent); + if (strcmp (command, "say_team") == 0) { + team = engine.getTeam (ent); + } - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || (team != -1 && team != client.team) || isAlive != IsAlive (client.ent)) + if (!(client.flags & CF_USED) || (team != -1 && team != client.team) || alive != isAlive (client.ent)) { continue; + } + Bot *target = bots.getBot (i); - Bot *target = bots.GetBot (i); + if (target != nullptr) { + target->m_sayTextBuffer.entityIndex = engine.indexOfEntity (ent); - if (target != nullptr) - { - target->m_sayTextBuffer.entityIndex = engine.IndexOfEntity (ent); - - if (IsNullString (CMD_ARGS ())) + if (isEmptyStr (g_engfuncs.pfnCmd_Args ())) { continue; - - strncpy (target->m_sayTextBuffer.sayText, CMD_ARGS (), SIZEOF_CHAR (target->m_sayTextBuffer.sayText)); - target->m_sayTextBuffer.timeNextChat = engine.Time () + target->m_sayTextBuffer.chatDelay; + } + target->m_sayTextBuffer.sayText = g_engfuncs.pfnCmd_Args (); + target->m_sayTextBuffer.timeNextChat = engine.timebase () + target->m_sayTextBuffer.chatDelay; } } } - - int clientIndex = engine.IndexOfEntity (ent) - 1; + int clientIndex = engine.indexOfEntity (ent) - 1; const Client &radioTarget = g_clients[clientIndex]; // check if this player alive, and issue something - if ((radioTarget.flags & CF_ALIVE) && g_radioSelect[clientIndex] != 0 && strncmp (command, "menuselect", 10) == 0) - { + if ((radioTarget.flags & CF_ALIVE) && g_radioSelect[clientIndex] != 0 && strncmp (command, "menuselect", 10) == 0) { int radioCommand = atoi (arg1); - if (radioCommand != 0) - { + if (radioCommand != 0) { radioCommand += 10 * (g_radioSelect[clientIndex] - 1); - if (radioCommand != Radio_Affirmative && radioCommand != Radio_Negative && radioCommand != Radio_ReportingIn) - { - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + if (radioCommand != RADIO_AFFIRMATIVE && radioCommand != RADIO_NEGATIVE && radioCommand != RADIO_REPORTING_IN) { + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); // validate bot - if (bot != nullptr && bot->m_team == radioTarget.team && ent != bot->GetEntity () && bot->m_radioOrder == 0) - { + if (bot != nullptr && bot->m_team == radioTarget.team && ent != bot->ent () && bot->m_radioOrder == 0) { bot->m_radioOrder = radioCommand; bot->m_radioEntity = ent; } } } - g_lastRadioTime[radioTarget.team] = engine.Time (); + g_lastRadioTime[radioTarget.team] = engine.timebase (); } g_radioSelect[clientIndex] = 0; } - else if (strncmp (command, "radio", 5) == 0) + else if (strncmp (command, "radio", 5) == 0) { g_radioSelect[clientIndex] = atoi (&command[5]); + } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_functionTable.pfnClientCommand) (ent); + } + g_functionTable.pfnClientCommand (ent); } -void ServerActivate (edict_t *pentEdictList, int edictCount, int clientMax) -{ +void ServerActivate (edict_t *pentEdictList, int edictCount, int clientMax) { // this function is called when the server has fully loaded and is about to manifest itself // on the network as such. Since a mapchange is actually a server shutdown followed by a // restart, this function is also called when a new map is being loaded. Hence it's the @@ -2109,36 +2060,34 @@ void ServerActivate (edict_t *pentEdictList, int edictCount, int clientMax) // loading the bot profiles, and drawing the world map (ie, filling the navigation hashtable). // Once this function has been called, the server can be considered as "running". - FreeLibraryMemory (); - InitConfig (); // initialize all config files + cleanupGarbage (); + execBotConfigs (false); // initialize all config files + + // do a level initialization + engine.levelInitialize (); // do level initialization stuff here... - waypoints.Init (); - waypoints.Load (); - - // create global killer entity - bots.CreateKillerEntity (); + waypoints.init (); + waypoints.load (); // execute main config - engine.IssueCmd ("exec addons/yapb/conf/yapb.cfg"); + execBotConfigs (true); - if (File::Accessible (FormatBuffer ("%s/maps/%s_yapb.cfg", engine.GetModName (), engine.GetMapName ()))) - { - engine.IssueCmd ("exec maps/%s_yapb.cfg", engine.GetMapName ()); - engine.Printf ("Executing Map-Specific config file"); + if (File::exists (format ("%s/maps/%s_yapb.cfg", engine.getModName (), engine.getMapName ()))) { + engine.execCmd ("exec maps/%s_yapb.cfg", engine.getMapName ()); + engine.print ("Executing Map-Specific config file"); } - bots.InitQuota (); + bots.initQuota (); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); + } + g_functionTable.pfnServerActivate (pentEdictList, edictCount, clientMax); - (*g_functionTable.pfnServerActivate) (pentEdictList, edictCount, clientMax); - - waypoints.InitializeVisibility (); + waypoints.rebuildVisibility (); } -void ServerDeactivate (void) -{ +void ServerDeactivate (void) { // this function is called when the server is shutting down. A particular note about map // changes: changing the map means shutting down the server and starting a new one. Of course // this process is transparent to the user, but either in single player when the hero reaches @@ -2150,22 +2099,29 @@ void ServerDeactivate (void) // the loading of new bots and the new BSP data parsing there. // save collected experience on shutdown - waypoints.SaveExperienceTab (); - waypoints.SaveVisibilityTab (); + waypoints.saveExperience (); + waypoints.saveVisibility (); // destroy global killer entity - bots.DestroyKillerEntity (); + bots.destroyKillerEntity (); - FreeLibraryMemory (); + // set state to unprecached + engine.setUnprecached (); - if (g_gameFlags & GAME_METAMOD) + // xash is not kicking fakeclients on changelevel + if (g_gameFlags & GAME_XASH_ENGINE) { + bots.kickEveryone (true, false); + bots.destroy (); + } + cleanupGarbage (); + + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_functionTable.pfnServerDeactivate) (); + } + g_functionTable.pfnServerDeactivate (); } -void StartFrame (void) -{ +void StartFrame (void) { // this function starts a video frame. It is called once per video frame by the engine. If // you run Half-Life at 90 fps, this function will then be called 90 times per second. By // placing a hook on it, we have a good place to do things that should be done continuously @@ -2175,128 +2131,118 @@ void StartFrame (void) // player population decreases, we should fill the server with other bots. // run periodic update of bot states - bots.PeriodicThink (); + bots.frame (); // record some stats of all players on the server - for (int i = 0; i < engine.MaxClients (); i++) - { - edict_t *player = engine.EntityOfIndex (i + 1); - Client &storeClient = g_clients[i]; + for (int i = 0; i < engine.maxClients (); i++) { + edict_t *player = engine.entityOfIndex (i + 1); + Client &client = g_clients[i]; - if (!engine.IsNullEntity (player) && (player->v.flags & FL_CLIENT)) - { - storeClient.ent = player; - storeClient.flags |= CF_USED; + if (!engine.isNullEntity (player) && (player->v.flags & FL_CLIENT)) { + client.ent = player; + client.flags |= CF_USED; - if (IsAlive (player)) - storeClient.flags |= CF_ALIVE; - else - storeClient.flags &= ~CF_ALIVE; + if (isAlive (player)) { + client.flags |= CF_ALIVE; + } + else { + client.flags &= ~CF_ALIVE; + } - if (storeClient.flags & CF_ALIVE) - { + if (client.flags & CF_ALIVE) { // keep the clipping mode enabled, or it can be turned off after new round has started - if (g_hostEntity == player && g_editNoclip) + if (g_hostEntity == player && g_editNoclip) { g_hostEntity->v.movetype = MOVETYPE_NOCLIP; - - storeClient.origin = player->v.origin; - SoundSimulateUpdate (i); + } + client.origin = player->v.origin; + simulateSoundUpdates (i); } } - else - { - storeClient.flags &= ~(CF_USED | CF_ALIVE); - storeClient.ent = nullptr; + else { + client.flags &= ~(CF_USED | CF_ALIVE); + client.ent = nullptr; } } - if (!engine.IsDedicatedServer () && !engine.IsNullEntity (g_hostEntity)) - { - if (g_waypointOn) - waypoints.Think (); - - CheckWelcomeMessage (); + if (g_waypointOn && !engine.isDedicated () && !engine.isNullEntity (g_hostEntity)) { + waypoints.frame (); } - bots.SetDeathMsgState (false); + bots.updateDeathMsgState (false); - if (g_timePerSecondUpdate < engine.Time ()) - { - for (int i = 0; i < engine.MaxClients (); i++) - { - edict_t *player = engine.EntityOfIndex (i + 1); + if (g_timePerSecondUpdate < engine.timebase ()) { + checkWelcome (); + + for (int i = 0; i < engine.maxClients (); i++) { + edict_t *player = engine.entityOfIndex (i + 1); // code below is executed only on dedicated server - if (engine.IsDedicatedServer () && !engine.IsNullEntity (player) && (player->v.flags & FL_CLIENT) && !(player->v.flags & FL_FAKECLIENT)) - { + if (engine.isDedicated () && !engine.isNullEntity (player) && (player->v.flags & FL_CLIENT) && !(player->v.flags & FL_FAKECLIENT)) { Client &client = g_clients[i]; - if (client.flags & CF_ADMIN) - { - if (IsNullString (yb_password_key.GetString ()) && IsNullString (yb_password.GetString ())) + if (client.flags & CF_ADMIN) { + if (isEmptyStr (yb_password_key.str ()) && isEmptyStr (yb_password.str ())) { client.flags &= ~CF_ADMIN; - else if (strcmp (yb_password.GetString (), INFOKEY_VALUE (GET_INFOKEYBUFFER (client.ent), const_cast (yb_password_key.GetString ())))) - { + } + else if (!!strcmp (yb_password.str (), g_engfuncs.pfnInfoKeyValue (g_engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast (yb_password_key.str ())))) { client.flags &= ~CF_ADMIN; - engine.Printf ("Player %s had lost remote access to yapb.", STRING (player->v.netname)); + engine.print ("Player %s had lost remote access to yapb.", STRING (player->v.netname)); } } - else if (!(client.flags & CF_ADMIN) && !IsNullString (yb_password_key.GetString ()) && !IsNullString (yb_password.GetString ())) - { - if (strcmp (yb_password.GetString (), INFOKEY_VALUE (GET_INFOKEYBUFFER (client.ent), const_cast (yb_password_key.GetString ()))) == 0) - { + else if (!(client.flags & CF_ADMIN) && !isEmptyStr (yb_password_key.str ()) && !isEmptyStr (yb_password.str ())) { + if (strcmp (yb_password.str (), g_engfuncs.pfnInfoKeyValue (g_engfuncs.pfnGetInfoKeyBuffer (client.ent), const_cast (yb_password_key.str ()))) == 0) { client.flags |= CF_ADMIN; - engine.Printf ("Player %s had gained full remote access to yapb.", STRING (player->v.netname)); + engine.print ("Player %s had gained full remote access to yapb.", STRING (player->v.netname)); } } } } - bots.CalculatePingOffsets (); + bots.calculatePingOffsets (); - // select the leader each team - for (int team = TERRORIST; team < SPECTATOR; team++) - bots.SelectLeaderEachTeam (team, false); - - if (g_gameFlags & GAME_METAMOD) - { + if (g_gameFlags & GAME_METAMOD) { static auto dmActive = g_engfuncs.pfnCVarGetPointer ("csdm_active"); static auto freeForAll = g_engfuncs.pfnCVarGetPointer ("mp_freeforall"); - if (dmActive && freeForAll) - { - if (dmActive->value > 0.0f) - { + if (dmActive && freeForAll) { + if (dmActive->value > 0.0f) { g_gameFlags |= GAME_CSDM; - if (freeForAll->value > 0.0f) + if (freeForAll->value > 0.0f) { g_gameFlags |= GAME_CSDM_FFA; - else if (g_gameFlags & GAME_CSDM_FFA) + } + else if (g_gameFlags & GAME_CSDM_FFA) { g_gameFlags &= ~GAME_CSDM_FFA; + } } - else if (g_gameFlags & GAME_CSDM) + else if (g_gameFlags & GAME_CSDM) { g_gameFlags &= ~GAME_CSDM; + } } } - g_timePerSecondUpdate = engine.Time () + 1.0f; + g_timePerSecondUpdate = engine.timebase () + 1.0f; } - // keep track of grenades on map - bots.UpdateActiveGrenades (); + if (bots.getBotCount () > 0) { + // keep track of grenades on map + bots.updateActiveGrenade (); + + // keep track of intresting entities + bots.updateIntrestingEntities (); + } // keep bot number up to date - bots.MaintainBotQuota (); + bots.maintainQuota (); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_functionTable.pfnStartFrame) (); + } + g_functionTable.pfnStartFrame (); // **** AI EXECUTION STARTS **** - bots.Think (); + bots.framePeriodic (); // **** AI EXECUTION FINISH **** } -void StartFrame_Post (void) -{ +void StartFrame_Post (void) { // this function starts a video frame. It is called once per video frame by the engine. If // you run Half-Life at 90 fps, this function will then be called 90 times per second. By // placing a hook on it, we have a good place to do things that should be done continuously @@ -2304,14 +2250,13 @@ void StartFrame_Post (void) // for the bots by the MOD side, remember). Post version called only by metamod. // **** AI EXECUTION STARTS **** - bots.Think (); + bots.framePeriodic (); // **** AI EXECUTION FINISH **** RETURN_META (MRES_IGNORED); } -int Spawn_Post (edict_t *ent) -{ +int Spawn_Post (edict_t *ent) { // this function asks the game DLL to spawn (i.e, give a physical existence in the virtual // world, in other words to 'display') the entity pointed to by ent in the game. The // Spawn() function is one of the functions any entity is supposed to have in the game DLL, @@ -2319,14 +2264,13 @@ int Spawn_Post (edict_t *ent) // only by metamod. // solves the bots unable to see through certain types of glass bug. - if (ent->v.rendermode == kRenderTransTexture) + if (ent->v.rendermode == kRenderTransTexture) { ent->v.flags &= ~FL_WORLDBRUSH; // clear the FL_WORLDBRUSH flag out of transparent ents - + } RETURN_META_VALUE (MRES_IGNORED, 0); } -void ServerActivate_Post (edict_t *, int, int) -{ +void ServerActivate_Post (edict_t *, int, int) { // this function is called when the server has fully loaded and is about to manifest itself // on the network as such. Since a mapchange is actually a server shutdown followed by a // restart, this function is also called when a new map is being loaded. Hence it's the @@ -2335,13 +2279,12 @@ void ServerActivate_Post (edict_t *, int, int) // Once this function has been called, the server can be considered as "running". Post version // called only by metamod. - waypoints.InitializeVisibility (); + waypoints.rebuildVisibility (); RETURN_META (MRES_IGNORED); } -void pfnChangeLevel (char *s1, char *s2) -{ +void pfnChangeLevel (char *s1, char *s2) { // the purpose of this function is to ask the engine to shutdown the server and restart a // new one running the map whose name is s1. It is used ONLY IN SINGLE PLAYER MODE and is // transparent to the user, because it saves the player state and equipment and restores it @@ -2352,29 +2295,28 @@ void pfnChangeLevel (char *s1, char *s2) // spawn point named "tr_2lm". // save collected experience on map change - waypoints.SaveExperienceTab (); - waypoints.SaveVisibilityTab (); + waypoints.saveExperience (); + waypoints.saveVisibility (); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - CHANGE_LEVEL (s1, s2); + } + g_engfuncs.pfnChangeLevel (s1, s2); } -edict_t *pfnFindEntityByString (edict_t *edictStartSearchAfter, const char *field, const char *value) -{ +edict_t *pfnFindEntityByString (edict_t *edictStartSearchAfter, const char *field, const char *value) { // round starts in counter-strike 1.5 - if (strcmp (value, "info_map_parameters") == 0) - RoundInit (); + if ((g_gameFlags & GAME_LEGACY) && strcmp (value, "info_map_parameters") == 0) { + initRound (); + } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META_VALUE (MRES_IGNORED, 0); - - return FIND_ENTITY_BY_STRING (edictStartSearchAfter, field, value); + } + return g_engfuncs.pfnFindEntityByString (edictStartSearchAfter, field, value); } -void pfnEmitSound (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch) -{ +void pfnEmitSound (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch) { // this function tells the engine that the entity pointed to by "entity", is emitting a sound // which fileName is "sample", at level "channel" (CHAN_VOICE, etc...), with "volume" as // loudness multiplicator (normal volume VOL_NORM is 1.0), with a pitch of "pitch" (normal @@ -2385,16 +2327,15 @@ void pfnEmitSound (edict_t *entity, int channel, const char *sample, float volum // SoundAttachToThreat() to bring the sound to the ears of the bots. Since bots have no client DLL // to handle this for them, such a job has to be done manually. - SoundAttachToClients (entity, sample, volume); + attachSoundsToClients (entity, sample, volume); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_engfuncs.pfnEmitSound) (entity, channel, sample, volume, attenuation, flags, pitch); + } + g_engfuncs.pfnEmitSound (entity, channel, sample, volume, attenuation, flags, pitch); } -void pfnClientCommand (edict_t *ent, char const *format, ...) -{ +void pfnClientCommand (edict_t *ent, char const *format, ...) { // this function forces the client whose player entity is ent to issue a client command. // How it works is that clients all have a g_xgv global string in their client DLL that // stores the command string; if ever that string is filled with characters, the client DLL @@ -2413,218 +2354,203 @@ void pfnClientCommand (edict_t *ent, char const *format, ...) char buffer[MAX_PRINT_BUFFER]; va_start (ap, format); - _vsnprintf (buffer, SIZEOF_CHAR (buffer), format, ap); + _vsnprintf (buffer, cr::bufsize (buffer), format, ap); va_end (ap); - if (bots.GetBot (ent)) - { - engine.IssueBotCommand (ent, buffer); + if (ent && (ent->v.flags & (FL_FAKECLIENT | FL_DORMANT))) { + if (bots.getBot (ent)) { + engine.execBotCmd (ent, buffer); + } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands - + } return; } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - CLIENT_COMMAND (ent, buffer); + } + g_engfuncs.pfnClientCommand (ent, buffer); } -void pfnMessageBegin (int msgDest, int msgType, const float *origin, edict_t *ed) -{ +void pfnMessageBegin (int msgDest, int msgType, const float *origin, edict_t *ed) { // this function called each time a message is about to sent. - + // store the message type in our own variables, since the GET_USER_MSG_ID () will just do a lot of strcmp()'s... - if ((g_gameFlags & GAME_METAMOD) && engine.FindMessageId (NETMSG_MONEY) == -1) - { - engine.AssignMessageId (NETMSG_VGUI, GET_USER_MSG_ID (PLID, "VGUIMenu", nullptr)); - engine.AssignMessageId (NETMSG_SHOWMENU, GET_USER_MSG_ID (PLID, "ShowMenu", nullptr)); - engine.AssignMessageId (NETMSG_WEAPONLIST, GET_USER_MSG_ID (PLID, "WeaponList", nullptr)); - engine.AssignMessageId (NETMSG_CURWEAPON, GET_USER_MSG_ID (PLID, "CurWeapon", nullptr)); - engine.AssignMessageId (NETMSG_AMMOX, GET_USER_MSG_ID (PLID, "AmmoX", nullptr)); - engine.AssignMessageId (NETMSG_AMMOPICKUP, GET_USER_MSG_ID (PLID, "AmmoPickup", nullptr)); - engine.AssignMessageId (NETMSG_DAMAGE, GET_USER_MSG_ID (PLID, "Damage", nullptr)); - engine.AssignMessageId (NETMSG_MONEY, GET_USER_MSG_ID (PLID, "Money", nullptr)); - engine.AssignMessageId (NETMSG_STATUSICON, GET_USER_MSG_ID (PLID, "StatusIcon", nullptr)); - engine.AssignMessageId (NETMSG_DEATH, GET_USER_MSG_ID (PLID, "DeathMsg", nullptr)); - engine.AssignMessageId (NETMSG_SCREENFADE, GET_USER_MSG_ID (PLID, "ScreenFade", nullptr)); - engine.AssignMessageId (NETMSG_HLTV, GET_USER_MSG_ID (PLID, "HLTV", nullptr)); - engine.AssignMessageId (NETMSG_TEXTMSG, GET_USER_MSG_ID (PLID, "TextMsg", nullptr)); - engine.AssignMessageId (NETMSG_SCOREINFO, GET_USER_MSG_ID (PLID, "ScoreInfo", nullptr)); - engine.AssignMessageId (NETMSG_BARTIME, GET_USER_MSG_ID (PLID, "BarTime", nullptr)); - engine.AssignMessageId (NETMSG_SENDAUDIO, GET_USER_MSG_ID (PLID, "SendAudio", nullptr)); - engine.AssignMessageId (NETMSG_SAYTEXT, GET_USER_MSG_ID (PLID, "SayText", nullptr)); + if ((g_gameFlags & GAME_METAMOD) && engine.getMessageId (NETMSG_MONEY) == -1) { + engine.setMessageId (NETMSG_VGUI, GET_USER_MSG_ID (PLID, "VGUIMenu", nullptr)); + engine.setMessageId (NETMSG_SHOWMENU, GET_USER_MSG_ID (PLID, "ShowMenu", nullptr)); + engine.setMessageId (NETMSG_WEAPONLIST, GET_USER_MSG_ID (PLID, "WeaponList", nullptr)); + engine.setMessageId (NETMSG_CURWEAPON, GET_USER_MSG_ID (PLID, "CurWeapon", nullptr)); + engine.setMessageId (NETMSG_AMMOX, GET_USER_MSG_ID (PLID, "AmmoX", nullptr)); + engine.setMessageId (NETMSG_AMMOPICKUP, GET_USER_MSG_ID (PLID, "AmmoPickup", nullptr)); + engine.setMessageId (NETMSG_DAMAGE, GET_USER_MSG_ID (PLID, "Damage", nullptr)); + engine.setMessageId (NETMSG_MONEY, GET_USER_MSG_ID (PLID, "Money", nullptr)); + engine.setMessageId (NETMSG_STATUSICON, GET_USER_MSG_ID (PLID, "StatusIcon", nullptr)); + engine.setMessageId (NETMSG_DEATH, GET_USER_MSG_ID (PLID, "DeathMsg", nullptr)); + engine.setMessageId (NETMSG_SCREENFADE, GET_USER_MSG_ID (PLID, "ScreenFade", nullptr)); + engine.setMessageId (NETMSG_HLTV, GET_USER_MSG_ID (PLID, "HLTV", nullptr)); + engine.setMessageId (NETMSG_TEXTMSG, GET_USER_MSG_ID (PLID, "TextMsg", nullptr)); + engine.setMessageId (NETMSG_TEAMINFO, GET_USER_MSG_ID (PLID, "TeamInfo", nullptr)); + engine.setMessageId (NETMSG_BARTIME, GET_USER_MSG_ID (PLID, "BarTime", nullptr)); + engine.setMessageId (NETMSG_SENDAUDIO, GET_USER_MSG_ID (PLID, "SendAudio", nullptr)); + engine.setMessageId (NETMSG_SAYTEXT, GET_USER_MSG_ID (PLID, "SayText", nullptr)); - if (g_gameFlags & GAME_SUPPORT_BOT_VOICE) - engine.AssignMessageId (NETMSG_BOTVOICE, GET_USER_MSG_ID (PLID, "BotVoice", nullptr)); - } - engine.ResetMessageCapture (); - - if ((!(g_gameFlags & GAME_LEGACY) || (g_gameFlags & GAME_XASH_ENGINE)) && msgDest == MSG_SPEC && msgType == engine.FindMessageId (NETMSG_HLTV)) - engine.SetOngoingMessageId (NETMSG_HLTV); - - engine.TryCaptureMessage (msgType, NETMSG_WEAPONLIST); - - if (!engine.IsNullEntity (ed)) - { - int index = bots.GetIndex (ed); - - // is this message for a bot? - if (index != -1 && !(ed->v.flags & FL_DORMANT)) - { - engine.SetOngoingMessageReceiver (index); - - // message handling is done in usermsg.cpp - engine.TryCaptureMessage (msgType, NETMSG_VGUI); - engine.TryCaptureMessage (msgType, NETMSG_CURWEAPON); - engine.TryCaptureMessage (msgType, NETMSG_AMMOX); - engine.TryCaptureMessage (msgType, NETMSG_AMMOPICKUP); - engine.TryCaptureMessage (msgType, NETMSG_DAMAGE); - engine.TryCaptureMessage (msgType, NETMSG_MONEY); - engine.TryCaptureMessage (msgType, NETMSG_STATUSICON); - engine.TryCaptureMessage (msgType, NETMSG_SCREENFADE); - engine.TryCaptureMessage (msgType, NETMSG_BARTIME); - engine.TryCaptureMessage (msgType, NETMSG_TEXTMSG); - engine.TryCaptureMessage (msgType, NETMSG_SHOWMENU); + if (g_gameFlags & GAME_SUPPORT_BOT_VOICE) { + engine.setMessageId (NETMSG_BOTVOICE, GET_USER_MSG_ID (PLID, "BotVoice", nullptr)); } } - else if (msgDest == MSG_ALL) - { - engine.TryCaptureMessage (msgType, NETMSG_SCOREINFO); - engine.TryCaptureMessage (msgType, NETMSG_DEATH); - engine.TryCaptureMessage (msgType, NETMSG_TEXTMSG); + engine.resetMessages (); - if (msgType == SVC_INTERMISSION) - { - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + if ((!(g_gameFlags & GAME_LEGACY) || (g_gameFlags & GAME_XASH_ENGINE)) && msgDest == MSG_SPEC && msgType == engine.getMessageId (NETMSG_HLTV)) { + engine.setCurrentMessageId (NETMSG_HLTV); + } + engine.captureMessage (msgType, NETMSG_WEAPONLIST); - if (bot != nullptr) + if (!engine.isNullEntity (ed)) { + int index = bots.index (ed); + + // is this message for a bot? + if (index != -1 && !(ed->v.flags & FL_DORMANT)) { + engine.setCurrentMessageOwner (index); + + // message handling is done in usermsg.cpp + engine.captureMessage (msgType, NETMSG_VGUI); + engine.captureMessage (msgType, NETMSG_CURWEAPON); + engine.captureMessage (msgType, NETMSG_AMMOX); + engine.captureMessage (msgType, NETMSG_AMMOPICKUP); + engine.captureMessage (msgType, NETMSG_DAMAGE); + engine.captureMessage (msgType, NETMSG_MONEY); + engine.captureMessage (msgType, NETMSG_STATUSICON); + engine.captureMessage (msgType, NETMSG_SCREENFADE); + engine.captureMessage (msgType, NETMSG_BARTIME); + engine.captureMessage (msgType, NETMSG_TEXTMSG); + engine.captureMessage (msgType, NETMSG_SHOWMENU); + } + } + else if (msgDest == MSG_ALL) { + engine.captureMessage (msgType, NETMSG_TEAMINFO); + engine.captureMessage (msgType, NETMSG_DEATH); + engine.captureMessage (msgType, NETMSG_TEXTMSG); + + if (msgType == SVC_INTERMISSION) { + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); + + if (bot != nullptr) { bot->m_notKilled = false; + } } } } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnMessageBegin (msgDest, msgType, origin, ed); } -void pfnMessageEnd (void) -{ - engine.ResetMessageCapture (); +void pfnMessageEnd (void) { + engine.resetMessages (); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnMessageEnd (); // send latency fix - bots.SendDeathMsgFix (); + bots.sendDeathMsgFix (); } -void pfnMessageEnd_Post (void) -{ +void pfnMessageEnd_Post (void) { // send latency fix - bots.SendDeathMsgFix (); + bots.sendDeathMsgFix (); RETURN_META (MRES_IGNORED); } -void pfnWriteByte (int value) -{ +void pfnWriteByte (int value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteByte (value); } -void pfnWriteChar (int value) -{ +void pfnWriteChar (int value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteChar (value); } -void pfnWriteShort (int value) -{ +void pfnWriteShort (int value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteShort (value); } -void pfnWriteLong (int value) -{ +void pfnWriteLong (int value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteLong (value); } -void pfnWriteAngle (float value) -{ +void pfnWriteAngle (float value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteAngle (value); } -void pfnWriteCoord (float value) -{ +void pfnWriteCoord (float value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteCoord (value); } -void pfnWriteString (const char *sz) -{ +void pfnWriteString (const char *sz) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) sz); + engine.processMessages ((void *)sz); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteString (sz); } -void pfnWriteEntity (int value) -{ +void pfnWriteEntity (int value) { // if this message is for a bot, call the client message function... - engine.ProcessMessageCapture ((void *) &value); + engine.processMessages ((void *)&value); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - + } g_engfuncs.pfnWriteEntity (value); } -int pfnCmd_Argc (void) -{ +int pfnCmd_Argc (void) { // this function returns the number of arguments the current client command string has. Since // bots have no client DLL and we may want a bot to execute a client command, we had to // implement a g_xgv string in the bot DLL for holding the bots' commands, and also keep @@ -2633,22 +2559,20 @@ int pfnCmd_Argc (void) // the normal way, by asking the engine. // is this a bot issuing that client command? - if (engine.IsBotCommand ()) - { - if (g_gameFlags & GAME_METAMOD) - RETURN_META_VALUE (MRES_SUPERCEDE, engine.GetOverrideArgc ()); - - return engine.GetOverrideArgc (); // if so, then return the argument count we know + if (engine.isBotCmd ()) { + if (g_gameFlags & GAME_METAMOD) { + RETURN_META_VALUE (MRES_SUPERCEDE, engine.botArgc ()); + } + return engine.botArgc (); // if so, then return the argument count we know } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META_VALUE (MRES_IGNORED, 0); - - return CMD_ARGC (); // ask the engine how many arguments there are + } + return g_engfuncs.pfnCmd_Argc (); // ask the engine how many arguments there are } -const char *pfnCmd_Args (void) -{ +const char *pfnCmd_Args (void) { // this function returns a pointer to the whole current client command string. Since bots // have no client DLL and we may want a bot to execute a client command, we had to implement // a g_xgv string in the bot DLL for holding the bots' commands, and also keep track of the @@ -2657,22 +2581,20 @@ const char *pfnCmd_Args (void) // normal way, by asking the engine. // is this a bot issuing that client command? - if (engine.IsBotCommand ()) - { - if (g_gameFlags & GAME_METAMOD) - RETURN_META_VALUE (MRES_SUPERCEDE, engine.GetOverrideArgs ()); - - return engine.GetOverrideArgs (); // else return the whole bot client command string we know + if (engine.isBotCmd ()) { + if (g_gameFlags & GAME_METAMOD) { + RETURN_META_VALUE (MRES_SUPERCEDE, engine.botArgs ()); + } + return engine.botArgs (); // else return the whole bot client command string we know } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META_VALUE (MRES_IGNORED, nullptr); - - return CMD_ARGS (); // ask the client command string to the engine + } + return g_engfuncs.pfnCmd_Args (); // ask the client command string to the engine } -const char *pfnCmd_Argv (int argc) -{ +const char *pfnCmd_Argv (int argc) { // this function returns a pointer to a certain argument of the current client command. Since // bots have no client DLL and we may want a bot to execute a client command, we had to // implement a g_xgv string in the bot DLL for holding the bots' commands, and also keep @@ -2681,57 +2603,54 @@ const char *pfnCmd_Argv (int argc) // the normal way, by asking the engine. // is this a bot issuing that client command? - if (engine.IsBotCommand ()) - { - if (g_gameFlags & GAME_METAMOD) - RETURN_META_VALUE (MRES_SUPERCEDE, engine.GetOverrideArgv (argc)); - - return engine.GetOverrideArgv (argc); // if so, then return the wanted argument we know + if (engine.isBotCmd ()) { + if (g_gameFlags & GAME_METAMOD) { + RETURN_META_VALUE (MRES_SUPERCEDE, engine.botArgv (argc)); + } + return engine.botArgv (argc); // if so, then return the wanted argument we know } - if (g_gameFlags & GAME_METAMOD) - RETURN_META_VALUE (MRES_IGNORED, nullptr); - return CMD_ARGV (argc); // ask the argument number "argc" to the engine + if (g_gameFlags & GAME_METAMOD) { + RETURN_META_VALUE (MRES_IGNORED, nullptr); + } + return g_engfuncs.pfnCmd_Argv (argc); // ask the argument number "argc" to the engine } -void pfnClientPrintf (edict_t *ent, PRINT_TYPE printType, const char *message) -{ +void pfnClientPrintf (edict_t *ent, PRINT_TYPE printType, const char *message) { // this function prints the text message string pointed to by message by the client side of // the client entity pointed to by ent, in a manner depending of printType (print_console, // print_center or print_chat). Be certain never to try to feed a bot with this function, // as it will crash your server. Why would you, anyway ? bots have no client DLL as far as // we know, right ? But since stupidity rules this world, we do a preventive check :) - if (IsValidBot (ent)) - { - if (g_gameFlags & GAME_METAMOD) + if (isFakeClient (ent)) { + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_SUPERCEDE); - + } return; } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - CLIENT_PRINTF (ent, printType, message); + } + g_engfuncs.pfnClientPrintf (ent, printType, message); } -void pfnSetClientMaxspeed (const edict_t *ent, float newMaxspeed) -{ - Bot *bot = bots.GetBot (const_cast (ent)); +void pfnSetClientMaxspeed (const edict_t *ent, float newMaxspeed) { + Bot *bot = bots.getBot (const_cast (ent)); // check wether it's not a bot - if (bot != nullptr) + if (bot != nullptr) { bot->pev->maxspeed = newMaxspeed; + } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_engfuncs.pfnSetClientMaxspeed) (ent, newMaxspeed); + } + g_engfuncs.pfnSetClientMaxspeed (ent, newMaxspeed); } -int pfnRegUserMsg (const char *name, int size) -{ +int pfnRegUserMsg (const char *name, int size) { // this function registers a "user message" by the engine side. User messages are network // messages the game DLL asks the engine to send to clients. Since many MODs have completely // different client features (Counter-Strike has a radar and a timer, for example), network @@ -2742,88 +2661,100 @@ int pfnRegUserMsg (const char *name, int size) // using pfnMessageBegin (), it will know what message ID number to send, and the engine will // know what to do, only for non-metamod version - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META_VALUE (MRES_IGNORED, 0); + } + int message = g_engfuncs.pfnRegUserMsg (name, size); - int message = REG_USER_MSG (name, size); - - if (strcmp (name, "VGUIMenu") == 0) - engine.AssignMessageId (NETMSG_VGUI, message); - else if (strcmp (name, "ShowMenu") == 0) - engine.AssignMessageId (NETMSG_SHOWMENU, message); - else if (strcmp (name, "WeaponList") == 0) - engine.AssignMessageId (NETMSG_WEAPONLIST, message); - else if (strcmp (name, "CurWeapon") == 0) - engine.AssignMessageId (NETMSG_CURWEAPON, message); - else if (strcmp (name, "AmmoX") == 0) - engine.AssignMessageId (NETMSG_AMMOX, message); - else if (strcmp (name, "AmmoPickup") == 0) - engine.AssignMessageId (NETMSG_AMMOPICKUP, message); - else if (strcmp (name, "Damage") == 0) - engine.AssignMessageId (NETMSG_DAMAGE, message); - else if (strcmp (name, "Money") == 0) - engine.AssignMessageId (NETMSG_MONEY, message); - else if (strcmp (name, "StatusIcon") == 0) - engine.AssignMessageId (NETMSG_STATUSICON, message); - else if (strcmp (name, "DeathMsg") == 0) - engine.AssignMessageId (NETMSG_DEATH, message); - else if (strcmp (name, "ScreenFade") == 0) - engine.AssignMessageId (NETMSG_SCREENFADE, message); - else if (strcmp (name, "HLTV") == 0) - engine.AssignMessageId (NETMSG_HLTV, message); - else if (strcmp (name, "TextMsg") == 0) - engine.AssignMessageId (NETMSG_TEXTMSG, message); - else if (strcmp (name, "ScoreInfo") == 0) - engine.AssignMessageId (NETMSG_SCOREINFO, message); - else if (strcmp (name, "BarTime") == 0) - engine.AssignMessageId (NETMSG_BARTIME, message); - else if (strcmp (name, "SendAudio") == 0) - engine.AssignMessageId (NETMSG_SENDAUDIO, message); - else if (strcmp (name, "SayText") == 0) - engine.AssignMessageId (NETMSG_SAYTEXT, message); - else if (strcmp (name, "BotVoice") == 0) - engine.AssignMessageId (NETMSG_BOTVOICE, message); - + if (strcmp (name, "VGUIMenu") == 0) { + engine.setMessageId (NETMSG_VGUI, message); + } + else if (strcmp (name, "ShowMenu") == 0) { + engine.setMessageId (NETMSG_SHOWMENU, message); + } + else if (strcmp (name, "WeaponList") == 0) { + engine.setMessageId (NETMSG_WEAPONLIST, message); + } + else if (strcmp (name, "CurWeapon") == 0) { + engine.setMessageId (NETMSG_CURWEAPON, message); + } + else if (strcmp (name, "AmmoX") == 0) { + engine.setMessageId (NETMSG_AMMOX, message); + } + else if (strcmp (name, "AmmoPickup") == 0) { + engine.setMessageId (NETMSG_AMMOPICKUP, message); + } + else if (strcmp (name, "Damage") == 0) { + engine.setMessageId (NETMSG_DAMAGE, message); + } + else if (strcmp (name, "Money") == 0) { + engine.setMessageId (NETMSG_MONEY, message); + } + else if (strcmp (name, "StatusIcon") == 0) { + engine.setMessageId (NETMSG_STATUSICON, message); + } + else if (strcmp (name, "DeathMsg") == 0) { + engine.setMessageId (NETMSG_DEATH, message); + } + else if (strcmp (name, "ScreenFade") == 0) { + engine.setMessageId (NETMSG_SCREENFADE, message); + } + else if (strcmp (name, "HLTV") == 0) { + engine.setMessageId (NETMSG_HLTV, message); + } + else if (strcmp (name, "TextMsg") == 0) { + engine.setMessageId (NETMSG_TEXTMSG, message); + } + else if (strcmp (name, "TeamInfo") == 0) { + engine.setMessageId (NETMSG_TEAMINFO, message); + } + else if (strcmp (name, "BarTime") == 0) { + engine.setMessageId (NETMSG_BARTIME, message); + } + else if (strcmp (name, "SendAudio") == 0) { + engine.setMessageId (NETMSG_SENDAUDIO, message); + } + else if (strcmp (name, "SayText") == 0) { + engine.setMessageId (NETMSG_SAYTEXT, message); + } + else if (strcmp (name, "BotVoice") == 0) { + engine.setMessageId (NETMSG_BOTVOICE, message); + } return message; } -void pfnAlertMessage (ALERT_TYPE alertType, char *format, ...) -{ +void pfnAlertMessage (ALERT_TYPE alertType, char *format, ...) { va_list ap; char buffer[1024]; va_start (ap, format); - vsnprintf (buffer, SIZEOF_CHAR (buffer), format, ap); + vsnprintf (buffer, cr::bufsize (buffer), format, ap); va_end (ap); - if ((g_mapType & MAP_DE) && g_bombPlanted && strstr (buffer, "_Defuse_") != nullptr) - { + if ((g_mapFlags & MAP_DE) && g_bombPlanted && strstr (buffer, "_Defuse_") != nullptr) { // notify all terrorists that CT is starting bomb defusing - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot != nullptr && bot->m_team == TERRORIST && bot->m_notKilled) - { - bot->DeleteSearchNodes (); + if (bot != nullptr && bot->m_team == TEAM_TERRORIST && bot->m_notKilled) { + bot->clearSearchNodes (); - bot->m_position = waypoints.GetBombPosition (); - bot->PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, -1, 0.0f, true); + bot->m_position = waypoints.getBombPos (); + bot->startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, INVALID_WAYPOINT_INDEX, 0.0f, true); } } } - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { RETURN_META (MRES_IGNORED); - - (*g_engfuncs.pfnAlertMessage) (alertType, buffer); + } + g_engfuncs.pfnAlertMessage (alertType, buffer); } typedef void (*entity_func_t) (entvars_t *); gamedll_funcs_t gameDLLFunc; -SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) -{ +SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { // this function is called right after FuncPointers_t() by the engine in the game DLL (or // what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can // be called by the engine, into a memory block pointed to by the functionTable pointer @@ -2836,17 +2767,14 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) memset (functionTable, 0, sizeof (gamefuncs_t)); - if (!(g_gameFlags & GAME_METAMOD)) - { - auto api_GetEntityAPI = g_gameLib->GetFuncAddr ("GetEntityAPI"); + if (!(g_gameFlags & GAME_METAMOD)) { + auto api_GetEntityAPI = g_gameLib->resolve ("GetEntityAPI"); // pass other DLLs engine callbacks to function table... - if (api_GetEntityAPI (&g_functionTable, INTERFACE_VERSION) == 0) - { - AddLogEntry (true, LL_FATAL, "GetEntityAPI2: ERROR - Not Initialized."); - return FALSE; // error initializing function table!!! + if (api_GetEntityAPI (&g_functionTable, INTERFACE_VERSION) == 0) { + logEntry (true, LL_FATAL, "GetEntityAPI2: ERROR - Not Initialized."); + return FALSE; // error initializing function table!!! } - gameDLLFunc.dllapi_table = &g_functionTable; gpGamedllFuncs = &gameDLLFunc; @@ -2868,8 +2796,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) return TRUE; } -SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *) -{ +SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *) { // this function is called right after FuncPointers_t() by the engine in the game DLL (or // what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can // be called by the engine, into a memory block pointed to by the functionTable pointer @@ -2889,22 +2816,21 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int * return TRUE; } -SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceVersion) -{ +SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceVersion) { // it appears that an extra function table has been added in the engine to gamedll interface // since the date where the first enginefuncs table standard was frozen. These ones are // facultative and we don't hook them, but since some MODs might be featuring it, we have to // pass them too, else the DLL interfacing wouldn't be complete and the game possibly wouldn't // run properly. - auto api_GetNewDLLFunctions = g_gameLib->GetFuncAddr ("GetNewDLLFunctions"); + auto api_GetNewDLLFunctions = g_gameLib->resolve ("GetNewDLLFunctions"); - if (api_GetNewDLLFunctions == nullptr) + if (api_GetNewDLLFunctions == nullptr) { return FALSE; + } - if (!api_GetNewDLLFunctions (functionTable, interfaceVersion)) - { - AddLogEntry (true, LL_FATAL, "GetNewDLLFunctions: ERROR - Not Initialized."); + if (!api_GetNewDLLFunctions (functionTable, interfaceVersion)) { + logEntry (true, LL_FATAL, "GetNewDLLFunctions: ERROR - Not Initialized."); return FALSE; } @@ -2912,11 +2838,11 @@ SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, in return TRUE; } -SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) -{ - if (g_gameFlags & GAME_METAMOD) +SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { + if (g_gameFlags & GAME_METAMOD) { memset (functionTable, 0, sizeof (enginefuncs_t)); - + } + functionTable->pfnChangeLevel = pfnChangeLevel; functionTable->pfnFindEntityByString = pfnFindEntityByString; functionTable->pfnEmitSound = pfnEmitSound; @@ -2942,31 +2868,28 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int return TRUE; } -SHARED_LIBRARAY_EXPORT int GetEngineFunctions_Post (enginefuncs_t *functionTable, int *) -{ - memset (functionTable, 0, sizeof (enginefuncs_t)); +SHARED_LIBRARAY_EXPORT int GetEngineFunctions_Post (enginefuncs_t *functionTable, int *) { + memset (functionTable, 0, sizeof (enginefuncs_t)); functionTable->pfnMessageEnd = pfnMessageEnd_Post; return TRUE; } -SHARED_LIBRARAY_EXPORT int Server_GetBlendingInterface (int version, void **ppinterface, void *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[128][3][4]) -{ +SHARED_LIBRARAY_EXPORT int Server_GetBlendingInterface (int version, void **ppinterface, void *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[128][3][4]) { // this function synchronizes the studio model animation blending interface (i.e, what parts // of the body move, which bones, which hitboxes and how) between the server and the game DLL. // some MODs can be using a different hitbox scheme than the standard one. - auto api_GetBlendingInterface = g_gameLib->GetFuncAddr ("Server_GetBlendingInterface"); + auto api_GetBlendingInterface = g_gameLib->resolve ("Server_GetBlendingInterface"); - if (api_GetBlendingInterface == nullptr) + if (api_GetBlendingInterface == nullptr) { return FALSE; - + } return api_GetBlendingInterface (version, ppinterface, pstudio, rotationmatrix, bonetransform); } -SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) -{ +SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) { // this function is the first function ever called by metamod in the plugin DLL. Its purpose // is for metamod to retrieve basic information about the plugin, such as its meta-interface // version, for ensuring compatibility with the current version of the running metamod. @@ -2977,16 +2900,13 @@ SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_ return TRUE; // tell metamod this plugin looks safe } -SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) -{ +SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) { // this function is called when metamod attempts to load the plugin. Since it's the place // where we can tell if the plugin will be allowed to run or not, we wait until here to make // our initialization stuff, like registering CVARs and dedicated server commands. - // metamod engine & dllapi function tables - static metamod_funcs_t metamodFunctionTable = - { + static metamod_funcs_t metamodFunctionTable = { nullptr, // pfnGetEntityAPI () nullptr, // pfnGetEntityAPI_Post () GetEntityAPI2, // pfnGetEntityAPI2 () @@ -3005,96 +2925,91 @@ SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *function return TRUE; // returning true enables metamod to attach this plugin } -SHARED_LIBRARAY_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) -{ +SHARED_LIBRARAY_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) { // this function is called when metamod unloads the plugin. A basic check is made in order // to prevent unloading the plugin if its processing should not be interrupted. - bots.RemoveAll (); // kick all bots off this server - FreeLibraryMemory (); + bots.kickEveryone (true); // kick all bots off this server + cleanupGarbage (); return TRUE; } -SHARED_LIBRARAY_EXPORT void Meta_Init (void) -{ +SHARED_LIBRARAY_EXPORT void Meta_Init (void) { // this function is called by metamod, before any other interface functions. Purpose of this // function to give plugin a chance to determine is plugin running under metamod or not. g_gameFlags |= GAME_METAMOD; } -Library *LoadCSBinary (void) -{ - const char *modname = engine.GetModName (); +Library *LoadCSBinary (void) { + const char *modname = engine.getModName (); if (!modname) return nullptr; -#if defined (PLATFORM_WIN32) - const char *libs[] = { "mp.dll", "cs.dll" }; -#elif defined (PLATFORM_LINUX) - const char *libs[] = { "cs.so", "cs_i386.so" }; -#elif defined (PLATFORM_OSX) - const char *libs[] = { "cs.dylib" }; +#if defined(PLATFORM_WIN32) + const char *libs[] = {"mp.dll", "cs.dll"}; +#elif defined(PLATFORM_LINUX) + const char *libs[] = {"cs.so", "cs_i386.so"}; +#elif defined(PLATFORM_OSX) + const char *libs[] = {"cs.dylib"}; #endif // search the libraries inside game dlls directory - for (int i = 0; i < ARRAYSIZE_HLSDK (libs); i++) - { - char path[256]; - sprintf (path, "%s/dlls/%s", modname, libs[i]); + for (size_t i = 0; i < cr::arrsize (libs); i++) { + auto *path = format ("%s/dlls/%s", modname, libs[i]); // if we can't read file, skip it - if (!File::Accessible (path)) + if (!File::exists (path)) { continue; + } // special case, czero is always detected first, as it's has custom directory - if (strcmp (modname, "czero") == 0) - { - g_gameFlags |= GAME_CZERO; + if (strcmp (modname, "czero") == 0) { + g_gameFlags |= (GAME_CZERO | GAME_SUPPORT_BOT_VOICE | GAME_SUPPORT_SVC_PINGS); - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { return nullptr; - + } return new Library (path); } - else - { + else { Library *game = new Library (path); // try to load gamedll - if (!game->IsLoaded ()) - { - AddLogEntry (true, LL_FATAL | LL_IGNORE, "Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", libs[i], modname); + if (!game->isValid ()) { + logEntry (true, LL_FATAL | LL_IGNORE, "Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", libs[i], modname); + + delete game; return nullptr; } // detect if we're running modern game - auto entity = game->GetFuncAddr ("weapon_famas"); + auto entity = game->resolve ("weapon_famas"); // detect xash engine - if (g_engfuncs.pfnCVarGetPointer ("build") != nullptr) - { + if (g_engfuncs.pfnCVarGetPointer ("build") != nullptr) { g_gameFlags |= (GAME_LEGACY | GAME_XASH_ENGINE); - if (entity != nullptr) + if (entity != nullptr) { g_gameFlags |= GAME_SUPPORT_BOT_VOICE; + } - if (g_gameFlags & GAME_METAMOD) - { + if (g_gameFlags & GAME_METAMOD) { delete game; return nullptr; } return game; } - if (entity != nullptr) + if (entity != nullptr) { g_gameFlags |= (GAME_CSTRIKE16 | GAME_SUPPORT_BOT_VOICE | GAME_SUPPORT_SVC_PINGS); - else + } + else { g_gameFlags |= GAME_LEGACY; + } - if (g_gameFlags & GAME_METAMOD) - { + if (g_gameFlags & GAME_METAMOD) { delete game; return nullptr; } @@ -3104,8 +3019,7 @@ Library *LoadCSBinary (void) return nullptr; } -DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t *pGlobals) -{ +DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t *pGlobals) { // this is the very first function that is called in the game DLL by the engine. Its purpose // is to set the functions interfacing up, by exchanging the functionTable function list // along with a pointer to the engine's global variables structure pGlobals, with the game @@ -3122,22 +3036,26 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t g_pGlobals = pGlobals; // register our cvars - engine.PushRegisteredConVarsToEngine (); + engine.pushRegStackToEngine (); // ensure we're have all needed directories { - const char *mod = engine.GetModName (); + const char *mod = engine.getModName (); // create the needed paths - File::CreatePath (const_cast (FormatBuffer ("%s/addons/yapb/conf/lang", mod))); - File::CreatePath (const_cast (FormatBuffer ("%s/addons/yapb/data/learned", mod))); + File::pathCreate (const_cast (format ("%s/addons/yapb/conf/lang", mod))); + File::pathCreate (const_cast (format ("%s/addons/yapb/data/learned", mod))); } #ifdef PLATFORM_ANDROID - g_gameFlags |= (GAME_LEGACY | GAME_XASH_ENGINE | GAME_MOBILITY | GAME_SUPPORT_BOT_VOICE); + g_gameFlags |= (GAME_XASH_ENGINE | GAME_MOBILITY | GAME_SUPPORT_BOT_VOICE); - if (g_gameFlags & GAME_METAMOD) - return; // we should stop the attempt for loading the real gamedll, since metamod handle this for us + if (g_gameFlags & GAME_METAMOD) { + return; // we should stop the attempt for loading the real gamedll, since metamod handle this for us + } + + extern ConVar yb_difficulty; + yb_difficulty.set (2); #ifdef LOAD_HARDFP const char *serverDLL = "libserver_hardfp.so"; @@ -3146,99 +3064,97 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t #endif char gameDLLName[256]; - snprintf (gameDLLName, SIZEOF_CHAR (gameDLLName), "%s/%s", getenv ("XASH3D_GAMELIBDIR"), serverDLL); + snprintf (gameDLLName, cr::bufsize (gameDLLName), "%s/%s", getenv ("XASH3D_GAMELIBDIR"), serverDLL); g_gameLib = new Library (gameDLLName); - if (!g_gameLib->IsLoaded ()) - AddLogEntry (true, LL_FATAL | LL_IGNORE, "Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gameDLLName, engine.GetModName ()); + if (!g_gameLib->IsLoaded ()) { + logEntry (true, LL_FATAL | LL_IGNORE, "Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gameDLLName, engine.GetModName ()); + delete g_gameLib; + } #else g_gameLib = LoadCSBinary (); { - if (g_gameLib == nullptr && !(g_gameFlags & GAME_METAMOD)) - { - AddLogEntry (true, LL_FATAL | LL_IGNORE, "Mod that you has started, not supported by this bot (gamedir: %s)", engine.GetModName ()); + if (g_gameLib == nullptr && !(g_gameFlags & GAME_METAMOD)) { + logEntry (true, LL_FATAL | LL_IGNORE, "Mod that you has started, not supported by this bot (gamedir: %s)", engine.getModName ()); return; } // print game detection info String gameVersionStr; - if (g_gameFlags & GAME_LEGACY) - gameVersionStr.Assign ("Legacy"); + if (g_gameFlags & GAME_LEGACY) { + gameVersionStr.assign ("Legacy"); + } + else if (g_gameFlags & GAME_CZERO) { + gameVersionStr.assign ("Condition Zero"); + } + else if (g_gameFlags & GAME_CSTRIKE16) { + gameVersionStr.assign ("v1.6"); + } + if (g_gameFlags & GAME_XASH_ENGINE) { + gameVersionStr.append (" @ Xash3D Engine"); - else if (g_gameFlags & GAME_CZERO) - gameVersionStr.Assign ("Condition Zero"); - - else if (g_gameFlags & GAME_CSTRIKE16) - gameVersionStr.Assign ("v1.6"); - - if (g_gameFlags & GAME_XASH_ENGINE) - { - gameVersionStr.Append (" @ Xash3D Engine"); - - if (g_gameFlags & GAME_MOBILITY) - gameVersionStr.Append (" Mobile"); - - gameVersionStr.Replace ("Legacy", "1.6 Limited"); + if (g_gameFlags & GAME_MOBILITY) { + gameVersionStr.append (" Mobile"); + } + gameVersionStr.replace ("Legacy", "1.6 Limited"); } - if (g_gameFlags & GAME_SUPPORT_BOT_VOICE) - gameVersionStr.Append (" (BV)"); + if (g_gameFlags & GAME_SUPPORT_BOT_VOICE) { + gameVersionStr.append (" (BV)"); + } - if (g_gameFlags & GAME_SUPPORT_SVC_PINGS) - gameVersionStr.Append (" (SVC)"); + if (g_gameFlags & GAME_SUPPORT_SVC_PINGS) { + gameVersionStr.append (" (SVC)"); + } + engine.print ("[YAPB] Bot v%s.0.%d Loaded. Game detected as Counter-Strike: %s", PRODUCT_VERSION, buildNumber(), gameVersionStr.chars ()); - engine.Printf ("YaPB Bot has detect game version as Counter-Strike: %s", gameVersionStr.GetBuffer ()); - - if (g_gameFlags & GAME_METAMOD) + if (g_gameFlags & GAME_METAMOD) { return; + } } + #endif - auto api_GiveFnptrsToDll = g_gameLib->GetFuncAddr ("GiveFnptrsToDll"); - - if (!api_GiveFnptrsToDll) - TerminateOnMalloc (); + auto api_GiveFnptrsToDll = g_gameLib->resolve ("GiveFnptrsToDll"); + assert (api_GiveFnptrsToDll != nullptr); GetEngineFunctions (functionTable, nullptr); - + // give the engine functions to the other DLL... api_GiveFnptrsToDll (functionTable, pGlobals); } -DLL_ENTRYPOINT -{ +DLL_ENTRYPOINT { // dynamic library entry point, can be used for uninitialization stuff. NOT for initializing // anything because if you ever attempt to wander outside the scope of this function on a // DLL attach, LoadLibrary() will simply fail. And you can't do I/Os here either. // dynamic library detaching ?? - if (DLL_DETACHING) - { - FreeLibraryMemory (); // free everything that's freeable + if (DLL_DETACHING) { + cleanupGarbage (); // free everything that's freeable delete g_gameLib; // if dynamic link library of mod is load, free it } DLL_RETENTRY; // the return data type is OS specific too } -void LinkEntity_Helper (entity_func_t &addr, const char *name, entvars_t *pev) -{ - if (addr == nullptr) - addr = g_gameLib->GetFuncAddr (name); +void helper_LinkEntity (entity_func_t &addr, const char *name, entvars_t *pev) { + if (addr == nullptr) { + addr = g_gameLib->resolve (name); + } - if (addr == nullptr) + if (addr == nullptr) { return; - + } addr (pev); } -#define LINK_ENTITY(entityName) \ -SHARED_LIBRARAY_EXPORT void entityName (entvars_t *pev) \ -{ \ - static entity_func_t addr; \ - LinkEntity_Helper (addr, #entityName, pev); \ -} \ +#define LINK_ENTITY(entityName) \ + SHARED_LIBRARAY_EXPORT void entityName (entvars_t *pev) { \ + static entity_func_t addr; \ + helper_LinkEntity (addr, #entityName, pev); \ + } // entities in counter-strike... LINK_ENTITY (DelayedUse) diff --git a/source/manager.cpp b/source/manager.cpp index af8418c..849f5ed 100644 --- a/source/manager.cpp +++ b/source/manager.cpp @@ -4,16 +4,17 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include ConVar yb_autovacate ("yb_autovacate", "1"); -ConVar yb_autovacate_smart_kick ("yb_autovacate_smart_kick", "1"); ConVar yb_quota ("yb_quota", "0", VT_NORMAL); ConVar yb_quota_mode ("yb_quota_mode", "normal"); +ConVar yb_quota_match ("yb_quota_match", "0"); +ConVar yb_think_fps ("yb_think_fps", "30.0"); ConVar yb_join_after_player ("yb_join_after_player", "0"); ConVar yb_join_team ("yb_join_team", "any"); @@ -25,38 +26,34 @@ ConVar yb_latency_display ("yb_latency_display", "2"); ConVar yb_avatar_display ("yb_avatar_display", "1"); ConVar mp_limitteams ("mp_limitteams", nullptr, VT_NOREGISTER); +ConVar mp_autoteambalance ("mp_autoteambalance", nullptr, VT_NOREGISTER); -BotManager::BotManager (void) -{ +BotManager::BotManager (void) { // this is a bot manager class constructor m_lastWinner = -1; m_deathMsgSent = false; - for (int i = 0; i < SPECTATOR; i++) - { + for (int i = 0; i < MAX_TEAM_COUNT; i++) { m_leaderChoosen[i] = false; m_economicsGood[i] = true; } memset (m_bots, 0, sizeof (m_bots)); + reset (); - m_maintainTime = 0.0f; - m_quotaMaintainTime = 0.0f; - m_grenadeUpdateTime = 0.0f; - - m_creationTab.RemoveAll (); + m_creationTab.clear (); m_killerEntity = nullptr; - m_balanceCount = 0; + + m_activeGrenades.reserve (16); + m_intrestingEntities.reserve (128); } -BotManager::~BotManager (void) -{ +BotManager::~BotManager (void) { // this is a bot manager class destructor, do not use engine.MaxClients () here !! - Free (); + destroy (); } -void BotManager::CreateKillerEntity (void) -{ +void BotManager::createKillerEntity (void) { // this function creates single trigger_hurt for using in Bot::Kill, to reduce lags, when killing all the bots m_killerEntity = g_engfuncs.pfnCreateNamedEntity (MAKE_STRING ("trigger_hurt")); @@ -70,93 +67,96 @@ void BotManager::CreateKillerEntity (void) MDLL_Spawn (m_killerEntity); } -void BotManager::DestroyKillerEntity (void) -{ - if (!engine.IsNullEntity (m_killerEntity)) +void BotManager::destroyKillerEntity (void) { + if (!engine.isNullEntity (m_killerEntity)) { g_engfuncs.pfnRemoveEntity (m_killerEntity); + } } -void BotManager::TouchWithKillerEntity (Bot *bot) -{ - if (engine.IsNullEntity (m_killerEntity)) - { - MDLL_ClientKill (bot->GetEntity ()); +void BotManager::touchKillerEntity (Bot *bot) { + + // bot is already dead. + if (!bot->m_notKilled) { return; } + if (engine.isNullEntity (m_killerEntity)) { + createKillerEntity (); + + if (engine.isNullEntity (m_killerEntity)) { + MDLL_ClientKill (bot->ent ()); + return; + } + } + m_killerEntity->v.classname = MAKE_STRING (g_weaponDefs[bot->m_currentWeapon].className); - m_killerEntity->v.dmg_inflictor = bot->GetEntity (); + m_killerEntity->v.dmg_inflictor = bot->ent (); KeyValueData kv; kv.szClassName = const_cast (g_weaponDefs[bot->m_currentWeapon].className); kv.szKeyName = "damagetype"; - kv.szValue = const_cast (FormatBuffer ("%d", (1 << 4))); + kv.szValue = const_cast (format ("%d", (1 << 4))); kv.fHandled = FALSE; MDLL_KeyValue (m_killerEntity, &kv); - MDLL_Touch (m_killerEntity, bot->GetEntity ()); + MDLL_Touch (m_killerEntity, bot->ent ()); } // it's already defined in interface.cpp extern "C" void player (entvars_t *pev); -void BotManager::CallGameEntity (entvars_t *vars) -{ +void BotManager::execGameEntity (entvars_t *vars) { // this function calls gamedll player() function, in case to create player entity in game - if (g_gameFlags & GAME_METAMOD) - { + if (g_gameFlags & GAME_METAMOD) { CALL_GAME_ENTITY (PLID, "player", vars); return; } player (vars); } -BotCreationResult BotManager::CreateBot (const String &name, int difficulty, int personality, int team, int member, bool isConsoleCmd) -{ - // this function completely prepares bot entity (edict) for creation, creates team, difficulty, sets name etc, and +BotCreationResult BotManager::create (const String &name, int difficulty, int personality, int team, int member) { + // this function completely prepares bot entity (edict) for creation, creates team, difficulty, sets named etc, and // then sends result to bot constructor - - edict_t *bot = nullptr; - char outputName[33]; - if (g_numWaypoints < 1) // don't allow creating bots with no waypoints loaded - { - engine.CenterPrintf ("Map is not waypointed. Cannot create bot"); + edict_t *bot = nullptr; + String resultName; + + // do not allow create bots when there is no waypoints + if (!waypoints.length ()) { + engine.centerPrint ("Map is not waypointed. Cannot create bot"); return BOT_RESULT_NAV_ERROR; } - else if (waypoints.HasChanged ()) // don't allow creating bots with changed waypoints (distance tables are messed up) - { - engine.CenterPrintf ("Waypoints have been changed. Load waypoints again..."); + + // don't allow creating bots with changed waypoints (distance tables are messed up) + else if (waypoints.hasChanged ()) { + engine.centerPrint ("Waypoints have been changed. Load waypoints again..."); return BOT_RESULT_NAV_ERROR; } - else if (team != -1 && IsTeamStacked (team - 1)) - { - engine.CenterPrintf ("Desired team is stacked. Unable to proceed with bot creation"); + else if (team != -1 && isTeamStacked (team - 1)) { + engine.centerPrint ("Desired team is stacked. Unable to proceed with bot creation"); return BOT_RESULT_TEAM_STACKED; } + if (difficulty < 0 || difficulty > 4) { + difficulty = yb_difficulty.integer (); - if (difficulty < 0 || difficulty > 4) - { - difficulty = yb_difficulty.GetInt (); - - if (difficulty < 0 || difficulty > 4) - { - difficulty = Random.Int (3, 4); - yb_difficulty.SetInt (difficulty); + if (difficulty < 0 || difficulty > 4) { + difficulty = rng.getInt (3, 4); + yb_difficulty.set (difficulty); } } - if (personality < PERSONALITY_NORMAL || personality > PERSONALITY_CAREFUL) - { - if (Random.Int (0, 100) < 50) + if (personality < PERSONALITY_NORMAL || personality > PERSONALITY_CAREFUL) { + if (rng.getInt (0, 100) < 50) { personality = PERSONALITY_NORMAL; - else - { - if (Random.Int (0, 100) < 65) + } + else { + if (rng.getInt (0, 100) < 50) { personality = PERSONALITY_RUSHER; - else + } + else { personality = PERSONALITY_CAREFUL; + } } } @@ -164,696 +164,599 @@ BotCreationResult BotManager::CreateBot (const String &name, int difficulty, int BotName *botName = nullptr; // setup name - if (name.IsEmpty ()) - { - if (!g_botNames.IsEmpty ()) - { + if (name.empty ()) { + if (!g_botNames.empty ()) { bool nameFound = false; - FOR_EACH_AE (g_botNames, i) - { - if (nameFound) + for (int i = 0; i < MAX_ENGINE_PLAYERS * 4; i++) { + if (nameFound) { break; + } + botName = &g_botNames.random (); - botName = &g_botNames.GetRandomElement (); - - if (botName == nullptr) + if (botName->name.length () < 3 || botName->usedBy != 0) { continue; - - if (botName->name.GetLength () < 3 || botName->usedBy != 0) - continue; - + } nameFound = true; - strncpy (outputName, botName->name.GetBuffer (), SIZEOF_CHAR (outputName)); + resultName = botName->name; steamId = botName->steamId; } } else - sprintf (outputName, "bot%i", Random.Int (0, 100)); // just pick ugly random name + resultName.format ("yapb_%d.%d", rng.getInt (0, 10), rng.getInt (0, 10)); // just pick ugly random name + } + else { + resultName = name; } - else - strncpy (outputName, name, 21); - if (!IsNullString (yb_name_prefix.GetString ())) - { - char prefixedName[33]; // temp buffer for storing modified name - - if (!IsNullString (yb_name_prefix.GetString ())) - snprintf (prefixedName, SIZEOF_CHAR (prefixedName), "%s %s", yb_name_prefix.GetString (), outputName); + if (!isEmptyStr (yb_name_prefix.str ())) { + String prefixed; // temp buffer for storing modified name + prefixed.format ("%s %s", yb_name_prefix.str (), resultName.chars ()); // buffer has been modified, copy to real name - if (!IsNullString (prefixedName)) - strncpy (outputName, prefixedName, SIZEOF_CHAR (outputName)); + resultName = cr::move (prefixed); } - bot = g_engfuncs.pfnCreateFakeClient (outputName); + bot = g_engfuncs.pfnCreateFakeClient (resultName.chars ()); - if (engine.IsNullEntity (bot)) - { - engine.CenterPrintf ("Maximum players reached (%d/%d). Unable to create Bot.", engine.MaxClients (), engine.MaxClients ()); + if (engine.isNullEntity (bot)) { + engine.centerPrint ("Maximum players reached (%d/%d). Unable to create Bot.", engine.maxClients (), engine.maxClients ()); return BOT_RESULT_MAX_PLAYERS_REACHED; } - int index = engine.IndexOfEntity (bot) - 1; + int index = engine.indexOfEntity (bot) - 1; // ensure it free - Free (index); + destroy (index); - InternalAssert (index >= 0 && index <= MAX_ENGINE_PLAYERS); // check index - InternalAssert (m_bots[index] == nullptr); // check bot slot + assert (index >= 0 && index <= MAX_ENGINE_PLAYERS); // check index + assert (m_bots[index] == nullptr); // check bot slot m_bots[index] = new Bot (bot, difficulty, personality, team, member, steamId); - if (m_bots[index] == nullptr) - TerminateOnMalloc (); - // assign owner of bot name - if (botName != nullptr) - botName->usedBy = m_bots[index]->GetIndex (); - - engine.Printf ("Connecting Bot..."); - - if (isConsoleCmd) - yb_quota.SetInt (yb_quota.GetInt () + 1); + if (botName != nullptr) { + botName->usedBy = m_bots[index]->index (); + } + engine.print ("Connecting Bot..."); return BOT_RESULT_CREATED; } -int BotManager::GetIndex (edict_t *ent) -{ +int BotManager::index (edict_t *ent) { // this function returns index of bot (using own bot array) - if (engine.IsNullEntity (ent)) + if (engine.isNullEntity (ent)) { return -1; + } + int index = engine.indexOfEntity (ent) - 1; - int index = engine.IndexOfEntity (ent) - 1; - - if (index < 0 || index >= MAX_ENGINE_PLAYERS) + if (index < 0 || index >= MAX_ENGINE_PLAYERS) { return -1; + } - if (m_bots[index] != nullptr) + if (m_bots[index] != nullptr) { return index; - + } return -1; // if no edict, return -1; } -Bot *BotManager::GetBot (int index) -{ +Bot *BotManager::getBot (int index) { // this function finds a bot specified by index, and then returns pointer to it (using own bot array) - if (index < 0 || index >= MAX_ENGINE_PLAYERS) + if (index < 0 || index >= MAX_ENGINE_PLAYERS) { return nullptr; + } - if (m_bots[index] != nullptr) + if (m_bots[index] != nullptr) { return m_bots[index]; - + } return nullptr; // no bot } -Bot *BotManager::GetBot (edict_t *ent) -{ +Bot *BotManager::getBot (edict_t *ent) { // same as above, but using bot entity - return GetBot (GetIndex (ent)); + return getBot (index (ent)); } -Bot *BotManager::GetAliveBot (void) -{ +Bot *BotManager::getAliveBot (void) { // this function finds one bot, alive bot :) - Array result; + IntArray result; - for (int i = 0; i < engine.MaxClients (); i++) - { - if (result.GetSize () > 4) + for (int i = 0; i < engine.maxClients (); i++) { + if (result.length () > 4) { break; - - if (m_bots[i] != nullptr && IsAlive (m_bots[i]->GetEntity ())) - result.Push (i); + } + if (m_bots[i] != nullptr && isAlive (m_bots[i]->ent ())) { + result.push (i); + } } - if (!result.IsEmpty ()) - return m_bots[result.GetRandomElement ()]; - + if (!result.empty ()) { + return m_bots[result.random ()]; + } return nullptr; } -void BotManager::Think (void) -{ +void BotManager::framePeriodic (void) { // this function calls think () function for all available at call moment bots - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr) - m_bots[i]->Think (); + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = m_bots[i]; + + if (bot != nullptr) { + bot->framePeriodic (); + } } } -void BotManager::PeriodicThink (void) -{ +void BotManager::frame (void) { // this function calls periodic SecondThink () function for all available at call moment bots - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr) - m_bots[i]->PeriodicThink (); + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = m_bots[i]; + + if (bot != nullptr) { + bot->frame (); + } + } + + // select leader each team somewhere in round start + if (g_timeRoundStart + 5.0f > engine.timebase () && g_timeRoundStart + 10.0f < engine.timebase ()) { + for (int team = 0; team < MAX_TEAM_COUNT; team++) { + selectLeaders (team, false); + } } } -void BotManager::AddBot (const String &name, int difficulty, int personality, int team, int member, bool isConsoleCmd) -{ +void BotManager::addbot (const String &name, int difficulty, int personality, int team, int member, bool manual) { // this function putting bot creation process to queue to prevent engine crashes - CreateQueue bot; + CreateQueue create; // fill the holder - bot.name = name; - bot.difficulty = difficulty; - bot.personality = personality; - bot.team = team; - bot.member = member; - bot.console = isConsoleCmd; + create.name = name; + create.difficulty = difficulty; + create.personality = personality; + create.team = team; + create.member = member; + create.manual = manual; // put to queue - m_creationTab.Push (bot); + m_creationTab.push (cr::move (create)); } -void BotManager::AddBot (const String &name, const String &difficulty, const String &personality, const String &team, const String &member, bool isConsoleCmd) -{ +void BotManager::addbot (const String &name, const String &difficulty, const String &personality, const String &team, const String &member, bool manual) { // this function is same as the function above, but accept as parameters string instead of integers - CreateQueue bot; + CreateQueue create; const String &any = "*"; - bot.name = (name.IsEmpty () || name == any) ? String ("\0") : name; - bot.difficulty = (difficulty.IsEmpty () || difficulty == any) ? -1 : difficulty.ToInt (); - bot.team = (team.IsEmpty () || team == any) ? -1 : team.ToInt (); - bot.member = (member.IsEmpty () || member == any) ? -1 : member.ToInt (); - bot.personality = (personality.IsEmpty () || personality == any) ? -1 : personality.ToInt (); - bot.console = isConsoleCmd; + create.name = (name.empty () || name == any) ? String ("\0") : name; + create.difficulty = (difficulty.empty () || difficulty == any) ? -1 : difficulty.toInt32 (); + create.team = (team.empty () || team == any) ? -1 : team.toInt32 (); + create.member = (member.empty () || member == any) ? -1 : member.toInt32 (); + create.personality = (personality.empty () || personality == any) ? -1 : personality.toInt32 (); + create.manual = manual; - m_creationTab.Push (bot); + m_creationTab.push (cr::move (create)); } -void BotManager::AdjustQuota (bool isPlayerConnecting, edict_t *ent) -{ - // this function increases or decreases bot quota amount depending on auto vacate variables - - if (!engine.IsDedicatedServer () || !yb_autovacate.GetBool () || IsValidBot (ent)) - return; - - if (isPlayerConnecting) - { - if (yb_autovacate_smart_kick.GetBool ()) - AddPlayerToCheckTeamQueue (ent); - else - { - RemoveRandom (); - m_balanceCount--; - - m_quotaMaintainTime = engine.Time () + 2.0f; - } - } - else if (m_balanceCount < 0) - { - AddRandom (); - m_balanceCount++; - } -} - -void BotManager::AddPlayerToCheckTeamQueue (edict_t *ent) -{ - if (!engine.IsDedicatedServer () || !yb_autovacate.GetBool () || IsValidBot (ent)) - return; - - // entity must be unique - bool hasFound = false; - - FOR_EACH_AE (m_trackedPlayers, it) - { - if (m_trackedPlayers[it] == ent) - { - hasFound = true; - break; - } - } - - if (!hasFound) - m_trackedPlayers.Push (ent); -} - -void BotManager::VerifyPlayersHasJoinedTeam (int &desiredCount) -{ - if (!engine.IsDedicatedServer () || !yb_autovacate.GetBool () || m_trackedPlayers.IsEmpty ()) - return; - - for (int i = 0; i < engine.MaxClients (); i++) - { - const Client &client = g_clients[i]; - - if (!(client.flags & CF_USED) || client.team == SPECTATOR || IsValidBot (client.ent)) - continue; - - FOR_EACH_AE (m_trackedPlayers, it) - { - if (client.ent != m_trackedPlayers[it]) - continue; - - m_balanceCount--; - desiredCount--; - - m_trackedPlayers.RemoveAt (it); - - break; - } - } -} - -void BotManager::MaintainBotQuota (void) -{ +void BotManager::maintainQuota (void) { // this function keeps number of bots up to date, and don't allow to maintain bot creation // while creation process in process. - if (g_numWaypoints < 1 || waypoints.HasChanged ()) + if (waypoints.length () < 1 || waypoints.hasChanged ()) { + if (yb_quota.integer () > 0) { + engine.centerPrint ("Map is not waypointed. Cannot create bot"); + } + yb_quota.set (0); return; + } // bot's creation update - if (!m_creationTab.IsEmpty () && m_maintainTime < engine.Time ()) - { - const CreateQueue &last = m_creationTab.Pop (); - const BotCreationResult callResult = CreateBot (last.name, last.difficulty, last.personality, last.team, last.member, last.console); + if (!m_creationTab.empty () && m_maintainTime < engine.timebase ()) { + const CreateQueue &last = m_creationTab.pop (); + const BotCreationResult callResult = create (last.name, last.difficulty, last.personality, last.team, last.member); + + if (last.manual) { + yb_quota.set (yb_quota.integer () + 1); + } // check the result of creation - if (callResult == BOT_RESULT_NAV_ERROR) - { - m_creationTab.RemoveAll (); // something wrong with waypoints, reset tab of creation - yb_quota.SetInt (0); // reset quota + if (callResult == BOT_RESULT_NAV_ERROR) { + m_creationTab.clear (); // something wrong with waypoints, reset tab of creation + yb_quota.set (0); // reset quota } - else if (callResult == BOT_RESULT_MAX_PLAYERS_REACHED) - { - m_creationTab.RemoveAll (); // maximum players reached, so set quota to maximum players - yb_quota.SetInt (GetBotsNum ()); + else if (callResult == BOT_RESULT_MAX_PLAYERS_REACHED) { + m_creationTab.clear (); // maximum players reached, so set quota to maximum players + yb_quota.set (getBotCount ()); } - else if (callResult == BOT_RESULT_TEAM_STACKED) - { - engine.Printf ("Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round)"); + else if (callResult == BOT_RESULT_TEAM_STACKED) { + engine.print ("Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round)"); - m_creationTab.RemoveAll (); - yb_quota.SetInt (GetBotsNum ()); + m_creationTab.clear (); + yb_quota.set (getBotCount ()); } - m_maintainTime = engine.Time () + 0.20f; + m_maintainTime = engine.timebase () + 0.10f; } // now keep bot number up to date - if (m_quotaMaintainTime < engine.Time ()) - { - // keep the quota number in valid ranges - { - if (yb_quota.GetInt () < 0) - yb_quota.SetInt (0); - - int maxClients = engine.MaxClients (); - - if (yb_quota.GetInt () > maxClients) - yb_quota.SetInt (maxClients); - } - - int numBots = GetBotsNum (); - int numHumans = GetHumansNum (); - int desiredCount = yb_quota.GetInt (); - - int numHumansOnTeam = yb_autovacate_smart_kick.GetBool () ? GetHumansJoinedTeam () : numHumans; - - if (yb_join_after_player.GetBool () && !numHumansOnTeam) - desiredCount = 0; - - // quota mode - char mode = yb_quota_mode.GetString ()[0]; - - if (mode == 'f' || mode == 'F') // fill - desiredCount = A_max (0, desiredCount - numHumansOnTeam); - else if (mode == 'm' || mode == 'M') // match - desiredCount = A_max (0, yb_quota.GetInt () * numHumansOnTeam); - - desiredCount = A_min (desiredCount, engine.MaxClients () - (numHumansOnTeam + (yb_autovacate.GetBool () ? 1 : 0))); - - if (yb_autovacate_smart_kick.GetBool () && numBots > 1 && desiredCount > 1) - VerifyPlayersHasJoinedTeam (desiredCount); - - if (desiredCount > numBots) - AddRandom (false); - else if (desiredCount < numBots) - RemoveRandom (true); - - m_quotaMaintainTime = engine.Time () + 0.40f; + if (m_quotaMaintainTime > engine.timebase ()) { + return; } + yb_quota.set (cr::clamp (yb_quota.integer (), 0, engine.maxClients ())); + + int totalHumansInGame = getHumansCount (); + int humanPlayersInGame = getHumansCount (true); + + if (!engine.isDedicated () && !totalHumansInGame) { + return; + } + int desiredBotCount = yb_quota.integer (); + int botsInGame = getBotCount (); + + bool halfRoundPassed = g_gameWelcomeSent && g_timeRoundMid > engine.timebase (); + + if (stricmp (yb_quota_mode.str (), "fill") == 0) { + if (halfRoundPassed) { + desiredBotCount = botsInGame; + } + else { + desiredBotCount = cr::max (0, desiredBotCount - humanPlayersInGame); + } + } + else if (stricmp (yb_quota_mode.str (), "match") == 0) { + if (halfRoundPassed) { + desiredBotCount = botsInGame; + } + else { + int quotaMatch = yb_quota_match.integer () == 0 ? yb_quota.integer () : yb_quota_match.integer (); + desiredBotCount = cr::max (0, quotaMatch * humanPlayersInGame); + } + } + + if (yb_join_after_player.boolean () && humanPlayersInGame == 0) { + desiredBotCount = 0; + } + int maxClients = engine.maxClients (); + + if (yb_autovacate.boolean ()) { + desiredBotCount = cr::min (desiredBotCount, maxClients - (humanPlayersInGame + 1)); + } + else { + desiredBotCount = cr::min (desiredBotCount, maxClients - humanPlayersInGame); + } + + // add bots if necessary + if (desiredBotCount > botsInGame) { + createRandom (); + } + else if (desiredBotCount < botsInGame) { + kickRandom (false); + } + m_quotaMaintainTime = engine.timebase () + 0.40f; } -void BotManager::InitQuota (void) -{ - m_balanceCount = 0; +void BotManager::reset (void) { + m_maintainTime = 0.0f; + m_quotaMaintainTime = 0.0f; + m_grenadeUpdateTime = 0.0f; + m_entityUpdateTime = 0.0f; - m_maintainTime = engine.Time () + 3.0f; - m_quotaMaintainTime = engine.Time () + 3.0f; - - m_trackedPlayers.RemoveAll (); - m_creationTab.RemoveAll (); + m_intrestingEntities.clear (); + m_activeGrenades.clear (); } -void BotManager::FillServer (int selection, int personality, int difficulty, int numToAdd) -{ +void BotManager::decrementQuota (int by) { + if (by != 0) { + yb_quota.set (cr::clamp (yb_quota.integer () - by, 0, yb_quota.integer ())); + return; + } + yb_quota.set (0); +} + +void BotManager::initQuota (void) { + m_maintainTime = engine.timebase () + 3.0f; + m_quotaMaintainTime = engine.timebase () + 3.0f; + + m_creationTab.clear (); +} + +void BotManager::serverFill (int selection, int personality, int difficulty, int numToAdd) { // this function fill server with bots, with specified team & personality // always keep one slot - int maxClients = yb_autovacate.GetBool () ? engine.MaxClients () - 1 - (engine.IsDedicatedServer () ? 0 : GetHumansNum ()) : engine.MaxClients (); + int maxClients = yb_autovacate.boolean () ? engine.maxClients () - 1 - (engine.isDedicated () ? 0 : getHumansCount ()) : engine.maxClients (); - if (GetBotsNum () >= maxClients - GetHumansNum ()) + if (getBotCount () >= maxClients - getHumansCount ()) { return; - - if (selection == 1 || selection == 2) - { - CVAR_SET_STRING ("mp_limitteams", "0"); - CVAR_SET_STRING ("mp_autoteambalance", "0"); } - else + if (selection == 1 || selection == 2) { + mp_limitteams.set (0); + mp_autoteambalance.set (0); + } + else { selection = 5; + } + char teams[6][12] = {"", {"Terrorists"}, {"CTs"}, "", "", {"Random"}, }; - char teams[6][12] = - { - "", - {"Terrorists"}, - {"CTs"}, - "", - "", - {"Random"}, - }; + int toAdd = numToAdd == -1 ? maxClients - (getHumansCount () + getBotCount ()) : numToAdd; - int toAdd = numToAdd == -1 ? maxClients - (GetHumansNum () + GetBotsNum ()) : numToAdd; - - for (int i = 0; i <= toAdd; i++) - AddBot ("", difficulty, personality, selection, -1); - - engine.CenterPrintf ("Fill Server with %s bots...", &teams[selection][0]); + for (int i = 0; i <= toAdd; i++) { + addbot ("", difficulty, personality, selection, -1, true); + } + engine.centerPrint ("Fill Server with %s bots...", &teams[selection][0]); } -void BotManager::RemoveAll (void) -{ +void BotManager::kickEveryone (bool instant, bool zeroQuota) { // this function drops all bot clients from server (this function removes only yapb's)`q - engine.CenterPrintf ("Bots are removed from server."); + engine.centerPrint ("Bots are removed from server."); - m_creationTab.RemoveAll (); - yb_quota.SetInt (0); + if (zeroQuota) { + decrementQuota (0); + } + + if (instant) { + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = m_bots[i]; + + if (bot != nullptr) { + bot->kick (); + } + } + } + m_creationTab.clear (); } -void BotManager::RemoveFromTeam (Team team, bool removeAll) -{ +void BotManager::kickFromTeam (Team team, bool removeAll) { // this function remove random bot from specified team (if removeAll value = 1 then removes all players from team) - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr && team == engine.GetTeam (m_bots[i]->GetEntity ())) - { - m_bots[i]->Kick (); + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = m_bots[i]; - if (!removeAll) + if (bot != nullptr && team == bot->m_team) { + decrementQuota (); + bot->kick (); + + if (!removeAll) { break; + } } } } -void BotManager::RemoveMenu (edict_t *ent, int selection) -{ +void BotManager::kickBotByMenu (edict_t *ent, int selection) { // this function displays remove bot menu to specified entity (this function show's only yapb's). if (selection > 4 || selection < 1) return; - static char tempBuffer[1024]; - char buffer[1024]; + String menus; + menus.format ("\\yBots Remove Menu (%d/4):\\w\n\n", selection); - memset (tempBuffer, 0, sizeof (tempBuffer)); - memset (buffer, 0, sizeof (buffer)); + int menuKeys = (selection == 4) ? (1 << 9) : ((1 << 8) | (1 << 9)); + int menuKey = (selection - 1) * 8; - int validSlots = (selection == 4) ? (1 << 9) : ((1 << 8) | (1 << 9)); + for (int i = menuKey; i < selection * 8; i++) { + auto bot = getBot (i); - for (int i = (selection - 1) * 8; i < selection * 8; i++) - { - const Bot *bot = GetBot (i); - - if (bot != nullptr && (bot->pev->flags & FL_FAKECLIENT)) - { - validSlots |= 1 << (i - ((selection - 1) * 8)); - sprintf (buffer, "%s %1.1d. %s%s\n", buffer, i - ((selection - 1) * 8) + 1, STRING (bot->pev->netname), bot->m_team == CT ? " \\y(CT)\\w" : " \\r(T)\\w"); + if (bot != nullptr && (bot->pev->flags & FL_FAKECLIENT)) { + menuKeys |= 1 << (cr::abs (i - menuKey)); + menus.formatAppend ("%1.1d. %s%s\n", i - menuKey + 1, STRING (bot->pev->netname), bot->m_team == TEAM_COUNTER ? " \\y(CT)\\w" : " \\r(T)\\w"); + } + else { + menus.formatAppend ("\\d %1.1d. Not a Bot\\w\n", i - menuKey + 1); } - else - sprintf (buffer, "%s\\d %1.1d. Not a Bot\\w\n", buffer, i - ((selection - 1) * 8) + 1); } - - sprintf (tempBuffer, "\\yBots Remove Menu (%d/4):\\w\n\n%s\n%s 0. Back", selection, buffer, (selection == 4) ? "" : " 9. More...\n"); + menus.formatAppend ("\n%s 0. Back", (selection == 4) ? "" : " 9. More...\n"); // force to clear current menu - DisplayMenuToClient (ent, BOT_MENU_INVALID); + showMenu (ent, BOT_MENU_INVALID); - auto FindMenu = [] (MenuId id) - { + auto searchMenu = [](MenuId id) { int menuIndex = 0; - for (; menuIndex < ARRAYSIZE_HLSDK (g_menus); menuIndex++) - { - if (g_menus[menuIndex].id == id) + for (; menuIndex < BOT_MENU_TOTAL_MENUS; menuIndex++) { + if (g_menus[menuIndex].id == id) { break; + } } return &g_menus[menuIndex]; }; - MenuText *menu = nullptr; - const unsigned int slots = validSlots & static_cast (-1); + const unsigned int slots = menuKeys & static_cast (-1); + MenuId id = static_cast (BOT_MENU_KICK_PAGE_1 - 1 + selection); - switch (selection) - { - case 1: - menu = FindMenu (BOT_MENU_KICK_PAGE_1); + auto menu = searchMenu (id); - menu->slots = slots; - menu->text = tempBuffer; + menu->slots = slots; + menu->text = menus; - DisplayMenuToClient (ent, BOT_MENU_KICK_PAGE_1); - break; - - case 2: - menu = FindMenu (BOT_MENU_KICK_PAGE_2); - - menu->slots = slots; - menu->text = tempBuffer; - - DisplayMenuToClient (ent, BOT_MENU_KICK_PAGE_2); - break; - - case 3: - menu = FindMenu (BOT_MENU_KICK_PAGE_3); - - menu->slots = slots; - menu->text = tempBuffer; - - DisplayMenuToClient (ent, BOT_MENU_KICK_PAGE_3); - break; - - case 4: - menu = FindMenu (BOT_MENU_KICK_PAGE_4); - - menu->slots = slots; - menu->text = tempBuffer; - - DisplayMenuToClient (ent, BOT_MENU_KICK_PAGE_4); - break; - } + showMenu (ent, id); } -void BotManager::KillAll (int team) -{ +void BotManager::killAllBots (int team) { // this function kills all bots on server (only this dll controlled bots) - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr) - { - if (team != -1 && team != m_bots[i]->m_team) + for (int i = 0; i < engine.maxClients (); i++) { + if (m_bots[i] != nullptr) { + if (team != -1 && team != m_bots[i]->m_team) { continue; - - m_bots[i]->Kill (); + } + m_bots[i]->kill (); } } - engine.CenterPrintf ("All Bots died !"); + engine.centerPrint ("All Bots died !"); } -void BotManager::RemoveRandom (bool keepQuota) -{ +void BotManager::kickBot (int index) { + auto bot = getBot (index); + + if (bot) { + decrementQuota (); + bot->kick (); + } +} + +void BotManager::kickRandom (bool decQuota) { // this function removes random bot from server (only yapb's) bool deadBotFound = false; - // first try to kick the bot that is currently dead - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr && !m_bots[i]->m_notKilled) // is this slot used? - { - m_bots[i]->Kick (keepQuota); - deadBotFound = true; + auto updateQuota = [&](void) { + if (decQuota) { + decrementQuota (); + } + }; + // first try to kick the bot that is currently dead + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = bots.getBot (i); + + if (bot != nullptr && !bot->m_notKilled) // is this slot used? + { + updateQuota (); + bot->kick (); + + deadBotFound = true; break; } } - if (deadBotFound) + if (deadBotFound) { return; + } // if no dead bots found try to find one with lowest amount of frags int index = 0; float score = 9999.0f; // search bots in this team - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = bots.getBot (i); - if (bot != nullptr && bot->pev->frags < score) - { + if (bot != nullptr && bot->pev->frags < score) { index = i; score = bot->pev->frags; } } // if found some bots - if (index != 0) - { - m_bots[index]->Kick (keepQuota); + if (index != 0) { + updateQuota (); + m_bots[index]->kick (); + return; } // worst case, just kick some random bot - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr) // is this slot used? + for (int i = 0; i < engine.maxClients (); i++) { + if (m_bots[i] != nullptr) // is this slot used? { - m_bots[i]->Kick (keepQuota); + updateQuota (); + m_bots[i]->kick (); + break; } } } -void BotManager::SetWeaponMode (int selection) -{ +void BotManager::setWeaponMode (int selection) { // this function sets bots weapon mode - int tabMapStandart[7][NUM_WEAPONS] = - { - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // Knife only - {-1,-1,-1, 2, 2, 0, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // Pistols only - {-1,-1,-1,-1,-1,-1,-1, 2, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // Shotgun only - {-1,-1,-1,-1,-1,-1,-1,-1,-1, 2, 1, 2, 0, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2,-1}, // Machine Guns only - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 0, 1, 0, 1, 1,-1,-1,-1,-1,-1,-1}, // Rifles only - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2, 2, 0, 1,-1,-1}, // Snipers only - {-1,-1,-1, 2, 2, 0, 1, 2, 2, 2, 1, 2, 0, 2, 0, 0, 1, 0, 1, 1, 2, 2, 0, 1, 2, 1} // Standard + int tabMapStandart[7][NUM_WEAPONS] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // Knife only + {-1, -1, -1, 2, 2, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // Pistols only + {-1, -1, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // Shotgun only + {-1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 2, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1}, // Machine Guns only + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 1, 0, 1, 1, -1, -1, -1, -1, -1, -1}, // Rifles only + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 0, 1, -1, -1}, // Snipers only + {-1, -1, -1, 2, 2, 0, 1, 2, 2, 2, 1, 2, 0, 2, 0, 0, 1, 0, 1, 1, 2, 2, 0, 1, 2, 1} // Standard }; - int tabMapAS[7][NUM_WEAPONS] = - { - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // Knife only - {-1,-1,-1, 2, 2, 0, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // Pistols only - {-1,-1,-1,-1,-1,-1,-1, 1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // Shotgun only - {-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 0, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1}, // Machine Guns only - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0,-1, 1, 0, 1, 1,-1,-1,-1,-1,-1,-1}, // Rifles only - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 0,-1, 1,-1,-1}, // Snipers only - {-1,-1,-1, 2, 2, 0, 1, 1, 1, 1, 1, 1, 0, 2, 0,-1, 1, 0, 1, 1, 0, 0,-1, 1, 1, 1} // Standard + int tabMapAS[7][NUM_WEAPONS] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // Knife only + {-1, -1, -1, 2, 2, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // Pistols only + {-1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // Shotgun only + {-1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1}, // Machine Guns only + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 1, 0, 1, 1, -1, -1, -1, -1, -1, -1}, // Rifles only + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 1, -1, -1}, // Snipers only + {-1, -1, -1, 2, 2, 0, 1, 1, 1, 1, 1, 1, 0, 2, 0, -1, 1, 0, 1, 1, 0, 0, -1, 1, 1, 1} // Standard }; - char modeName[7][12] = - { - {"Knife"}, - {"Pistol"}, - {"Shotgun"}, - {"Machine Gun"}, - {"Rifle"}, - {"Sniper"}, - {"Standard"} - }; + char modeName[7][12] = {{"Knife"}, {"Pistol"}, {"Shotgun"}, {"Machine Gun"}, {"Rifle"}, {"Sniper"}, {"Standard"}}; selection--; - for (int i = 0; i < NUM_WEAPONS; i++) - { + for (int i = 0; i < NUM_WEAPONS; i++) { g_weaponSelect[i].teamStandard = tabMapStandart[selection][i]; g_weaponSelect[i].teamAS = tabMapAS[selection][i]; } - if (selection == 0) - yb_jasonmode.SetInt (1); - else - yb_jasonmode.SetInt (0); - - engine.CenterPrintf ("%s weapon mode selected", &modeName[selection][0]); + if (selection == 0) { + yb_jasonmode.set (1); + } + else { + yb_jasonmode.set (0); + } + engine.centerPrint ("%s weapon mode selected", &modeName[selection][0]); } -void BotManager::ListBots (void) -{ +void BotManager::listBots (void) { // this function list's bots currently playing on the server - engine.Printf ("%-3.5s %-9.13s %-17.18s %-3.4s %-3.4s %-3.4s", "index", "name", "personality", "team", "difficulty", "frags"); + engine.print ("%-3.5s %-9.13s %-17.18s %-3.4s %-3.4s %-3.4s", "index", "name", "personality", "team", "difficulty", "frags"); - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = getBot (i); // is this player slot valid - if (bot != nullptr) - engine.Printf ("[%-3.1d] %-9.13s %-17.18s %-3.4s %-3.1d %-3.1d", i, STRING (bot->pev->netname), bot->m_personality == PERSONALITY_RUSHER ? "rusher" : bot->m_personality == PERSONALITY_NORMAL ? "normal" : "careful", bot->m_team == CT ? "CT" : "T", bot->m_difficulty, static_cast (bot->pev->frags)); + if (bot != nullptr) { + engine.print ("[%-3.1d] %-9.13s %-17.18s %-3.4s %-3.1d %-3.1d", i, STRING (bot->pev->netname), bot->m_personality == PERSONALITY_RUSHER ? "rusher" : bot->m_personality == PERSONALITY_NORMAL ? "normal" : "careful", bot->m_team == TEAM_COUNTER ? "CT" : "T", bot->m_difficulty, static_cast (bot->pev->frags)); + } } } -int BotManager::GetBotsNum (void) -{ +int BotManager::getBotCount (void) { // this function returns number of yapb's playing on the server int count = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr) + for (int i = 0; i < engine.maxClients (); i++) { + if (m_bots[i] != nullptr) { count++; + } } return count; } -Bot *BotManager::GetHighestFragsBot (int team) -{ +Bot *BotManager::getHighfragBot (int team) { int bestIndex = 0; float bestScore = -1; // search bots in this team - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = bots.getBot (i); - if (bot != nullptr && bot->m_notKilled && bot->m_team == team) - { - if (bot->pev->frags > bestScore) - { + if (bot != nullptr && bot->m_notKilled && bot->m_team == team) { + if (bot->pev->frags > bestScore) { bestIndex = i; bestScore = bot->pev->frags; } } } - return GetBot (bestIndex); + return getBot (bestIndex); } -void BotManager::CheckTeamEconomics (int team, bool setTrue) -{ +void BotManager::updateTeamEconomics (int team, bool forceGoodEconomics) { // this function decides is players on specified team is able to buy primary weapons by calculating players // that have not enough money to buy primary (with economics), and if this result higher 80%, player is can't // buy primary weapons. extern ConVar yb_economics_rounds; - if (setTrue || !yb_economics_rounds.GetBool ()) - { + if (forceGoodEconomics || !yb_economics_rounds.boolean ()) { m_economicsGood[team] = true; return; // don't check economics while economics disable } @@ -862,141 +765,139 @@ void BotManager::CheckTeamEconomics (int team, bool setTrue) int numTeamPlayers = 0; // start calculating - for (int i = 0; i < engine.MaxClients (); i++) - { - if (m_bots[i] != nullptr && engine.GetTeam (m_bots[i]->GetEntity ()) == team) - { - if (m_bots[i]->m_moneyAmount <= g_botBuyEconomyTable[0]) - numPoorPlayers++; + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = m_bots[i]; + if (bot != nullptr && bot->m_team == team) { + if (bot->m_moneyAmount <= g_botBuyEconomyTable[0]) { + numPoorPlayers++; + } numTeamPlayers++; // update count of team } } m_economicsGood[team] = true; - if (numTeamPlayers <= 1) + if (numTeamPlayers <= 1) { return; - + } // if 80 percent of team have no enough money to purchase primary weapon - if ((numTeamPlayers * 80) / 100 <= numPoorPlayers) + if ((numTeamPlayers * 80) / 100 <= numPoorPlayers) { m_economicsGood[team] = false; + } // winner must buy something! - if (m_lastWinner == team) + if (m_lastWinner == team) { m_economicsGood[team] = true; + } } -void BotManager::Free (void) -{ +void BotManager::destroy (void) { // this function free all bots slots (used on server shutdown) - for (int i = 0; i < MAX_ENGINE_PLAYERS; i++) - Free (i); + for (int i = 0; i < MAX_ENGINE_PLAYERS; i++) { + destroy (i); + } } -void BotManager::Free (int index) -{ +void BotManager::destroy (int index) { // this function frees one bot selected by index (used on bot disconnect) delete m_bots[index]; m_bots[index] = nullptr; } -Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member, const String &steamId) -{ +Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member, const String &steamId) { // this function does core operation of creating bot, it's called by CreateBot (), // when bot setup completed, (this is a bot class constructor) - char rejectReason[128]; - int clientIndex = engine.IndexOfEntity (bot); + int clientIndex = engine.indexOfEntity (bot); memset (reinterpret_cast (this), 0, sizeof (*this)); - pev = &bot->v; - if (bot->pvPrivateData != nullptr) - FREE_PRIVATE (bot); + if (bot->pvPrivateData != nullptr) { + g_engfuncs.pfnFreeEntPrivateData (bot); + } bot->pvPrivateData = nullptr; bot->v.frags = 0; // create the player entity by calling MOD's player function - BotManager::CallGameEntity (&bot->v); + BotManager::execGameEntity (&bot->v); // set all info buffer keys for this bot - char *buffer = GET_INFOKEYBUFFER (bot); - SET_CLIENT_KEYVALUE (clientIndex, buffer, "_vgui_menus", "0"); + char *buffer = g_engfuncs.pfnGetInfoKeyBuffer (bot); + g_engfuncs.pfnSetClientKeyValue (clientIndex, buffer, "_vgui_menus", "0"); - if (!(g_gameFlags & GAME_LEGACY) && yb_latency_display.GetInt () == 1) - SET_CLIENT_KEYVALUE (clientIndex, buffer, "*bot", "1"); + if (!(g_gameFlags & GAME_LEGACY) && yb_latency_display.integer () == 1) { + g_engfuncs.pfnSetClientKeyValue (clientIndex, buffer, "*bot", "1"); + } - rejectReason[0] = 0; // reset the reject reason template string - MDLL_ClientConnect (bot, "BOT", FormatBuffer ("127.0.0.%d", engine.IndexOfEntity (bot) + 100), rejectReason); - - // should be set after client connect - if (yb_avatar_display.GetBool () && !steamId.IsEmpty ()) - SET_CLIENT_KEYVALUE (clientIndex, buffer, "*sid", const_cast (steamId.GetBuffer ())); + char reject[256] = {0, }; + MDLL_ClientConnect (bot, STRING (bot->v.netname), format ("127.0.0.%d", engine.indexOfEntity (bot) + 100), reject); - memset (&m_pingOffset, 0, sizeof (m_pingOffset)); - memset (&m_ping, 0, sizeof (m_ping)); - - if (!IsNullString (rejectReason)) - { - AddLogEntry (true, LL_WARNING, "Server refused '%s' connection (%s)", STRING (bot->v.netname), rejectReason); - engine.IssueCmd ("kick \"%s\"", STRING (bot->v.netname)); // kick the bot player if the server refused it + if (!isEmptyStr (reject)) { + logEntry (true, LL_WARNING, "Server refused '%s' connection (%s)", STRING (bot->v.netname), reject); + engine.execCmd ("kick \"%s\"", STRING (bot->v.netname)); // kick the bot player if the server refused it bot->v.flags |= FL_KILLME; return; } + // should be set after client connect + if (yb_avatar_display.boolean () && !steamId.empty ()) { + g_engfuncs.pfnSetClientKeyValue (clientIndex, buffer, "*sid", steamId.chars ()); + } + memset (&m_pingOffset, 0, sizeof (m_pingOffset)); + memset (&m_ping, 0, sizeof (m_ping)); + MDLL_ClientPutInServer (bot); bot->v.flags |= FL_FAKECLIENT; // set this player as fakeclient // initialize all the variables for this bot... - m_notStarted = true; // hasn't joined game yet + m_notStarted = true; // hasn't joined game yet m_forceRadio = false; m_startAction = GAME_MSG_NONE; m_retryJoin = 0; m_moneyAmount = 0; - m_logotypeIndex = Random.Int (0, 9); + m_logotypeIndex = rng.getInt (0, 9); + m_tasks.reserve (TASK_MAX); // assign how talkative this bot will be - m_sayTextBuffer.chatDelay = Random.Float (3.8f, 10.0f); - m_sayTextBuffer.chatProbability = Random.Int (1, 100); + m_sayTextBuffer.chatDelay = rng.getFloat (3.8f, 10.0f); + m_sayTextBuffer.chatProbability = rng.getInt (1, 100); m_notKilled = false; m_weaponBurstMode = BM_OFF; m_difficulty = difficulty; - if (difficulty < 0 || difficulty > 4) - { - difficulty = Random.Int (3, 4); - yb_difficulty.SetInt (difficulty); + if (difficulty < 0 || difficulty > 4) { + difficulty = rng.getInt (3, 4); + yb_difficulty.set (difficulty); } - m_lastCommandTime = engine.Time () - 0.1f; - m_frameInterval = engine.Time (); + m_lastCommandTime = engine.timebase () - 0.1f; + m_frameInterval = engine.timebase (); m_timePeriodicUpdate = 0.0f; - switch (personality) - { + switch (personality) { case 1: m_personality = PERSONALITY_RUSHER; - m_baseAgressionLevel = Random.Float (0.7f, 1.0f); - m_baseFearLevel = Random.Float (0.0f, 0.4f); + m_baseAgressionLevel = rng.getFloat (0.7f, 1.0f); + m_baseFearLevel = rng.getFloat (0.0f, 0.4f); break; case 2: m_personality = PERSONALITY_CAREFUL; - m_baseAgressionLevel = Random.Float (0.2f, 0.5f); - m_baseFearLevel = Random.Float (0.7f, 1.0f); + m_baseAgressionLevel = rng.getFloat (0.2f, 0.5f); + m_baseFearLevel = rng.getFloat (0.7f, 1.0f); break; default: m_personality = PERSONALITY_NORMAL; - m_baseAgressionLevel = Random.Float (0.4f, 0.7f); - m_baseFearLevel = Random.Float (0.4f, 0.7f); + m_baseAgressionLevel = rng.getFloat (0.4f, 0.7f); + m_baseFearLevel = rng.getFloat (0.4f, 0.7f); break; } @@ -1004,12 +905,12 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member, c memset (&m_ammo, 0, sizeof (m_ammo)); m_currentWeapon = 0; // current weapon is not assigned at start - m_voicePitch = Random.Int (80, 115); // assign voice pitch + m_voicePitch = rng.getInt (80, 115); // assign voice pitch // copy them over to the temp level variables m_agressionLevel = m_baseAgressionLevel; m_fearLevel = m_baseFearLevel; - m_nextEmotionUpdate = engine.Time () + 0.5f; + m_nextEmotionUpdate = engine.timebase () + 0.5f; // just to be sure m_actMessageIndex = 0; @@ -1019,147 +920,128 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member, c m_wantedTeam = team; m_wantedClass = member; - NewRound (); + newRound (); } -void Bot::ReleaseUsedName (void) -{ - FOR_EACH_AE (g_botNames, j) - { - BotName &name = g_botNames[j]; - - if (name.usedBy == GetIndex ()) - { +void Bot::clearUsedName (void) { + for (auto &name : g_botNames) { + if (name.usedBy == index ()) { name.usedBy = 0; break; } } } -float Bot::GetThinkInterval (void) -{ - if (Math::FltZero (m_thinkInterval)) - return m_frameInterval; - - return m_thinkInterval; +float Bot::calcThinkInterval (void) { + return cr::fzero (m_thinkInterval) ? m_frameInterval : m_thinkInterval; } -Bot::~Bot (void) -{ +Bot::~Bot (void) { // this is bot destructor - ReleaseUsedName (); - DeleteSearchNodes (); - ResetTasks (); + clearUsedName (); + clearSearchNodes (); + clearRoute (); + clearTasks (); } -int BotManager::GetHumansNum (void) -{ +int BotManager::getHumansCount (bool ignoreSpectators) { // this function returns number of humans playing on the server int count = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if ((client.flags & CF_USED) && m_bots[i] == nullptr && !(client.ent->v.flags & FL_FAKECLIENT)) + if ((client.flags & CF_USED) && m_bots[i] == nullptr && !(client.ent->v.flags & FL_FAKECLIENT)) { + if (ignoreSpectators && client.team2 != TEAM_TERRORIST && client.team2 != TEAM_COUNTER) { + continue; + } count++; + } } return count; } -int BotManager::GetHumansAliveNum (void) -{ +int BotManager::getAliveHumansCount (void) { // this function returns number of humans playing on the server int count = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if ((client.flags & (CF_USED | CF_ALIVE)) && m_bots[i] == nullptr && !(client.ent->v.flags & FL_FAKECLIENT)) + if ((client.flags & (CF_USED | CF_ALIVE)) && m_bots[i] == nullptr && !(client.ent->v.flags & FL_FAKECLIENT)) { count++; + } } return count; } -int BotManager::GetHumansJoinedTeam (void) -{ - // this function returns number of humans playing on the server - - int count = 0; - - for (int i = 0; i < engine.MaxClients (); i++) - { - const Client &client = g_clients[i]; - - if ((client.flags & (CF_USED | CF_ALIVE)) && m_bots[i] == nullptr && client.team != SPECTATOR && !(client.ent->v.flags & FL_FAKECLIENT)) - count++; - } - return count; -} - -bool BotManager::IsTeamStacked (int team) -{ - int teamLimit = mp_limitteams.GetInt (); - - if (!teamLimit) +bool BotManager::isTeamStacked (int team) { + if (team != TEAM_COUNTER && team != TEAM_TERRORIST) { return false; + } + int limitTeams = mp_limitteams.integer (); - int teamCount[SPECTATOR] = { 0, }; + if (!limitTeams) { + return false; + } + int teamCount[MAX_TEAM_COUNT] = { 0, }; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if ((client.flags & CF_USED) && client.team2 != SPECTATOR) + if ((client.flags & CF_USED) && client.team2 != TEAM_UNASSIGNED && client.team2 != TEAM_SPECTATOR) { teamCount[client.team2]++; + } } - return teamCount[team] + 1 > teamCount[team == CT ? TERRORIST : CT] + teamLimit; + return teamCount[team] + 1 > teamCount[team == TEAM_COUNTER ? TEAM_TERRORIST : TEAM_COUNTER] + limitTeams; } -void Bot::NewRound (void) -{ +void Bot::newRound (void) { // this function initializes a bot after creation & at the start of each round - + int i = 0; // delete all allocated path nodes - DeleteSearchNodes (); + clearSearchNodes (); + clearRoute (); - m_waypointOrigin.Zero (); - m_destOrigin.Zero (); - m_currentWaypointIndex = -1; + m_waypointOrigin.nullify (); + m_destOrigin.nullify (); m_currentPath = nullptr; m_currentTravelFlags = 0; - m_goalFailed = 0; - m_desiredVelocity.Zero (); - m_prevGoalIndex = -1; - m_chosenGoalIndex = -1; - m_loosedBombWptIndex = -1; + m_desiredVelocity.nullify (); + m_currentWaypointIndex = INVALID_WAYPOINT_INDEX; + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + m_chosenGoalIndex = INVALID_WAYPOINT_INDEX; + m_loosedBombWptIndex = INVALID_WAYPOINT_INDEX; + m_plantedBombWptIndex = INVALID_WAYPOINT_INDEX; + m_grenadeRequested = false; m_moveToC4 = false; m_duckDefuse = false; m_duckDefuseCheckTime = 0.0f; m_numFriendsLeft = 0; m_numEnemiesLeft = 0; + m_oldButtons = pev->button; + m_rechoiceGoalCount = 0; m_avoid = nullptr; m_avoidTime = 0.0f; - for (i = 0; i < 5; i++) - m_prevWptIndex[i] = -1; + for (i = 0; i < 5; i++) { + m_prevWptIndex[i] = INVALID_WAYPOINT_INDEX; + } + m_navTimeset = engine.timebase (); + m_team = engine.getTeam (ent ()); + m_isVIP = false; - m_navTimeset = engine.Time (); - m_team = engine.GetTeam (GetEntity ()); - - switch (m_personality) - { + switch (m_personality) { case PERSONALITY_NORMAL: - m_pathType = Random.Int (0, 100) > 50 ? SEARCH_PATH_SAFEST_FASTER : SEARCH_PATH_SAFEST; + m_pathType = rng.getInt (0, 100) > 50 ? SEARCH_PATH_SAFEST_FASTER : SEARCH_PATH_SAFEST; break; case PERSONALITY_RUSHER: @@ -1173,7 +1055,7 @@ void Bot::NewRound (void) // clear all states & tasks m_states = 0; - ResetTasks (); + clearTasks (); m_isVIP = false; m_isLeader = false; @@ -1181,17 +1063,16 @@ void Bot::NewRound (void) m_canChooseAimDirection = true; m_turnAwayFromFlashbang = 0.0f; - m_maxThrowTimer = 0.0f; m_timeTeamOrder = 0.0f; - m_timeRepotingInDelay = Random.Float (40.0f, 240.0f); + m_timeRepotingInDelay = rng.getFloat (40.0f, 240.0f); m_askCheckTime = 0.0f; m_minSpeed = 260.0f; m_prevSpeed = 0.0f; m_prevOrigin = Vector (9999.0f, 9999.0f, 9999.0f); - m_prevTime = engine.Time (); - m_blindRecognizeTime = engine.Time (); - m_lookUpdateTime = engine.Time (); - + m_prevTime = engine.timebase (); + m_lookUpdateTime = engine.timebase (); + m_aimErrorTime = engine.timebase (); + m_viewDistance = 4096.0f; m_maxViewDistance = 4096.0f; @@ -1201,22 +1082,23 @@ void Bot::NewRound (void) m_itemCheckTime = 0.0f; m_breakableEntity = nullptr; - m_breakableOrigin.Zero (); + m_breakableOrigin.nullify (); m_timeDoorOpen = 0.0f; - ResetCollideState (); - ResetDoubleJumpState (); + resetCollision (); + resetDoubleJump (); m_enemy = nullptr; m_lastVictim = nullptr; m_lastEnemy = nullptr; - m_lastEnemyOrigin.Zero (); + m_lastEnemyOrigin.nullify (); m_trackingEdict = nullptr; m_timeNextTracking = 0.0f; m_buttonPushTime = 0.0f; m_enemyUpdateTime = 0.0f; m_enemyIgnoreTimer = 0.0f; + m_retreatTime = 0.0f; m_seeEnemyTime = 0.0f; m_shootAtDeadTime = 0.0f; m_oldCombatDesire = 0.0f; @@ -1229,36 +1111,38 @@ void Bot::NewRound (void) m_voteKickIndex = 0; m_lastVoteKick = 0; m_voteMap = 0; - m_doorOpenAttempt = 0; + m_tryOpenDoor = 0; m_aimFlags = 0; m_liftState = 0; - m_position.Zero (); - m_liftTravelPos.Zero (); + m_aimLastError.nullify (); + m_position.nullify (); + m_liftTravelPos.nullify (); - SetIdealReactionTimes (true); + setIdealReactionTimers (true); m_targetEntity = nullptr; - m_tasks.RemoveAll (); + m_tasks.reserve (TASK_MAX); m_followWaitTime = 0.0f; - for (i = 0; i < MAX_HOSTAGES; i++) - m_hostages[i] = nullptr; + m_hostages.clear (); - for (i = 0; i < Chatter_Total; i++) + for (i = 0; i < CHATTER_MAX; i++) { m_chatterTimes[i] = -1.0f; - + } m_isReloading = false; m_reloadState = RELOAD_NONE; m_reloadCheckTime = 0.0f; - m_shootTime = engine.Time (); - m_playerTargetTime = engine.Time (); + m_shootTime = engine.timebase (); + m_playerTargetTime = engine.timebase (); m_firePause = 0.0f; m_timeLastFired = 0.0f; + m_sniperStopTime = 0.0f; m_grenadeCheckTime = 0.0f; m_isUsingGrenade = false; + m_bombSearchOverridden = false; m_blindButton = 0; m_blindTime = 0.0f; @@ -1267,26 +1151,27 @@ void Bot::NewRound (void) m_jumpFinished = false; m_isStuck = false; - m_sayTextBuffer.timeNextChat = engine.Time (); + m_sayTextBuffer.timeNextChat = engine.timebase (); m_sayTextBuffer.entityIndex = -1; - m_sayTextBuffer.sayText[0] = 0x0; + m_sayTextBuffer.sayText.clear (); m_buyState = BUYSTATE_PRIMARY_WEAPON; m_lastEquipTime = 0.0f; - if (!m_notKilled) // if bot died, clear all weapon stuff and force buying again - { + // if bot died, clear all weapon stuff and force buying again + if (!m_notKilled) { memset (&m_ammoInClip, 0, sizeof (m_ammoInClip)); memset (&m_ammo, 0, sizeof (m_ammo)); m_currentWeapon = 0; } - m_knifeAttackTime = engine.Time () + Random.Float (1.3f, 2.6f); - m_nextBuyTime = engine.Time () + Random.Float (0.6f, 2.0f); + m_knifeAttackTime = engine.timebase () + rng.getFloat (1.3f, 2.6f); + m_nextBuyTime = engine.timebase () + rng.getFloat (0.6f, 2.0f); m_buyPending = false; m_inBombZone = false; + m_ignoreBuyDelay = false; m_hasC4 = false; m_shieldCheckTime = 0.0f; @@ -1306,181 +1191,178 @@ void Bot::NewRound (void) m_defendHostage = false; m_headedTime = 0.0f; - m_timeLogoSpray = engine.Time () + Random.Float (5.0f, 30.0f); - m_spawnTime = engine.Time (); - m_lastChatTime = engine.Time (); + m_timeLogoSpray = engine.timebase () + rng.getFloat (5.0f, 30.0f); + m_spawnTime = engine.timebase (); + m_lastChatTime = engine.timebase (); - m_timeCamping = 0; + m_timeCamping = 0.0f; m_campDirection = 0; m_nextCampDirTime = 0; m_campButtons = 0; m_soundUpdateTime = 0.0f; - m_heardSoundTime = engine.Time (); + m_heardSoundTime = engine.timebase (); // clear its message queue - for (i = 0; i < 32; i++) + for (i = 0; i < 32; i++) { m_messageQueue[i] = GAME_MSG_NONE; - + } m_actMessageIndex = 0; m_pushMessageIndex = 0; // and put buying into its message queue - PushMessageQueue (GAME_MSG_PURCHASE); - PushTask (TASK_NORMAL, TASKPRI_NORMAL, -1, 0.0f, true); + pushMsgQueue (GAME_MSG_PURCHASE); + startTask (TASK_NORMAL, TASKPRI_NORMAL, INVALID_WAYPOINT_INDEX, 0.0f, true); - if (Random.Int (0, 100) < 50) - ChatterMessage (Chatter_NewRound); - - m_thinkInterval = (g_gameFlags & GAME_LEGACY) ? 0.0f : (1.0f / 30.0f) * Random.Float (0.95f, 1.05f); + if (rng.getInt (0, 100) < 50) { + pushChatterMessage (CHATTER_NEW_ROUND); + } + m_thinkInterval = (g_gameFlags & (GAME_LEGACY | GAME_XASH_ENGINE)) ? 0.0f : (1.0f / cr::clamp (yb_think_fps.flt (), 30.0f, 90.0f)) * rng.getFloat (0.95f, 1.05f); } -void Bot::Kill (void) -{ +void Bot::kill (void) { // this function kills a bot (not just using ClientKill, but like the CSBot does) // base code courtesy of Lazy (from bots-united forums!) - bots.TouchWithKillerEntity (this); + bots.touchKillerEntity (this); } -void Bot::Kick (bool keepQuota) -{ +void Bot::kick (void) { // this function kick off one bot from the server. - auto username = STRING (pev->netname); - if (!(pev->flags & FL_FAKECLIENT) || IsNullString (username)) + if (!(pev->flags & FL_FAKECLIENT) || isEmptyStr (username)) { return; - - // clear fakeclient bit immediately + } + // clear fakeclient bit pev->flags &= ~FL_FAKECLIENT; - engine.IssueCmd ("kick \"%s\"", username); - engine.CenterPrintf ("Bot '%s' kicked", username); - - // keep quota number up to date - if (!keepQuota) - yb_quota.SetInt (A_clamp (yb_quota.GetInt () - 1, 0, yb_quota.GetInt ())); + engine.execCmd ("kick \"%s\"", username); + engine.centerPrint ("Bot '%s' kicked", username); } -void Bot::StartGame (void) -{ +void Bot::processTeamJoin (void) { // this function handles the selection of teams & class // cs prior beta 7.0 uses hud-based motd, so press fire once - if (g_gameFlags & GAME_LEGACY) + if (g_gameFlags & GAME_LEGACY) { pev->button |= IN_ATTACK; + } // check if something has assigned team to us - else if (m_team == TERRORIST || m_team == CT) + else if (m_team == TEAM_TERRORIST || m_team == TEAM_COUNTER) { m_notStarted = false; + } + else if (m_team == TEAM_UNASSIGNED && m_retryJoin > 2) { + m_startAction = GAME_MSG_TEAM_SELECT; + } // if bot was unable to join team, and no menus popups, check for stacked team - if (m_startAction == GAME_MSG_NONE && ++m_retryJoin > 2) - { - if (bots.IsTeamStacked (m_wantedTeam - 1)) - { + if (m_startAction == GAME_MSG_NONE && ++m_retryJoin > 3) { + if (bots.isTeamStacked (m_wantedTeam - 1)) { m_retryJoin = 0; - engine.Printf ("Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round)."); - Kick (); + engine.print ("Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round)."); + kick (); return; } } // handle counter-strike stuff here... - if (m_startAction == GAME_MSG_TEAM_SELECT) - { - m_startAction = GAME_MSG_NONE; // switch back to idle - - char teamJoin = yb_join_team.GetString ()[0]; + if (m_startAction == GAME_MSG_TEAM_SELECT) { + m_startAction = GAME_MSG_NONE; // switch back to idle - if (teamJoin == 'C' || teamJoin == 'c') + char teamJoin = yb_join_team.str ()[0]; + + if (teamJoin == 'C' || teamJoin == 'c') { m_wantedTeam = 2; - else if (teamJoin == 'T' || teamJoin == 't') + } + else if (teamJoin == 'T' || teamJoin == 't') { m_wantedTeam = 1; + } - if (m_wantedTeam != 1 && m_wantedTeam != 2) + if (m_wantedTeam != 1 && m_wantedTeam != 2) { m_wantedTeam = 5; + } // select the team the bot wishes to join... - engine.IssueBotCommand (GetEntity (), "menuselect %d", m_wantedTeam); + engine.execBotCmd (ent (), "menuselect %d", m_wantedTeam); } - else if (m_startAction == GAME_MSG_CLASS_SELECT) - { - m_startAction = GAME_MSG_NONE; // switch back to idle + else if (m_startAction == GAME_MSG_CLASS_SELECT) { + m_startAction = GAME_MSG_NONE; // switch back to idle // czero has additional models int maxChoice = (g_gameFlags & GAME_CZERO) ? 5 : 4; - if (m_wantedClass < 1 || m_wantedClass > maxChoice) - m_wantedClass = Random.Int (1, maxChoice); // use random if invalid + if (m_wantedClass < 1 || m_wantedClass > maxChoice) { + m_wantedClass = rng.getInt (1, maxChoice); // use random if invalid + } // select the class the bot wishes to use... - engine.IssueBotCommand (GetEntity (), "menuselect %d", m_wantedClass); + engine.execBotCmd (ent (), "menuselect %d", m_wantedClass); // bot has now joined the game (doesn't need to be started) m_notStarted = false; - + // check for greeting other players, since we connected - if (Random.Int (0, 100) < 20) - ChatMessage (CHAT_WELCOME); + if (rng.getInt (0, 100) < 20) { + pushChatMessage (CHAT_WELCOME); + } } } -void BotManager::CalculatePingOffsets (void) -{ - if (!(g_gameFlags & GAME_SUPPORT_SVC_PINGS) || yb_latency_display.GetInt () != 2) +void BotManager::calculatePingOffsets (void) { + if (!(g_gameFlags & GAME_SUPPORT_SVC_PINGS) || yb_latency_display.integer () != 2) { return; - + } int averagePing = 0; int numHumans = 0; - for (int i = 0; i < engine.MaxClients (); i++) - { - edict_t *ent = engine.EntityOfIndex (i + 1); + for (int i = 0; i < engine.maxClients (); i++) { + edict_t *ent = engine.entityOfIndex (i + 1); - if (!IsValidPlayer (ent)) + if (!isPlayer (ent)) { continue; - + } numHumans++; int ping, loss; - PLAYER_CNX_STATS (ent, &ping, &loss); - - if (ping < 0 || ping > 100) - ping = Random.Int (3, 15); + g_engfuncs.pfnGetPlayerStats (ent, &ping, &loss); + if (ping < 0 || ping > 100) { + ping = rng.getInt (3, 15); + } averagePing += ping; } - if (numHumans > 0) + if (numHumans > 0) { averagePing /= numHumans; - else - averagePing = Random.Int (30, 40); + } + else { + averagePing = rng.getInt (30, 40); + } - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = getBot (i); - if (bot == nullptr) + if (bot == nullptr) { continue; + } int part = static_cast (averagePing * 0.2f); - int botPing = Random.Int (averagePing - part, averagePing + part) + Random.Int (bot->m_difficulty + 3, bot->m_difficulty + 6) + 10; + int botPing = rng.getInt (averagePing - part, averagePing + part) + rng.getInt (bot->m_difficulty + 3, bot->m_difficulty + 6) + 10; - if (botPing <= 5) - botPing = Random.Int (10, 23); - else if (botPing > 100) - botPing = Random.Int (30, 40); + if (botPing <= 5) { + botPing = rng.getInt (10, 23); + } + else if (botPing > 100) { + botPing = rng.getInt (30, 40); + } - for (int j = 0; j < 2; j++) - { - for (bot->m_pingOffset[j] = 0; bot->m_pingOffset[j] < 4; bot->m_pingOffset[j]++) - { - if ((botPing - bot->m_pingOffset[j]) % 4 == 0) - { + for (int j = 0; j < 2; j++) { + for (bot->m_pingOffset[j] = 0; bot->m_pingOffset[j] < 4; bot->m_pingOffset[j]++) { + if ((botPing - bot->m_pingOffset[j]) % 4 == 0) { bot->m_ping[j] = (botPing - bot->m_pingOffset[j]) / 4; break; } @@ -1490,206 +1372,199 @@ void BotManager::CalculatePingOffsets (void) } } -void BotManager::SendPingDataOffsets (edict_t *to) -{ - if (!(g_gameFlags & GAME_SUPPORT_SVC_PINGS) || yb_latency_display.GetInt () != 2 || engine.IsNullEntity (to) || (to->v.flags & FL_FAKECLIENT)) +void BotManager::sendPingOffsets (edict_t *to) { + if (!(g_gameFlags & GAME_SUPPORT_SVC_PINGS) || yb_latency_display.integer () != 2 || engine.isNullEntity (to) || (to->v.flags & FL_FAKECLIENT)) { return; + } - if (!(to->v.flags & FL_CLIENT) && !(((to->v.button & IN_SCORE) || !(to->v.oldbuttons & IN_SCORE)))) + if (!(to->v.flags & FL_CLIENT) && !(((to->v.button & IN_SCORE) || !(to->v.oldbuttons & IN_SCORE)))) { return; - - static int sending; - sending = 0; + } + MessageWriter msg; // missing from sdk - static const int SVC_PINGS = 17; + constexpr int SVC_PINGS = 17; - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { Bot *bot = m_bots[i]; - if (bot == nullptr) + if (bot == nullptr) { continue; - - switch (sending) - { - case 0: - { - // start a new message - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, SVC_PINGS, nullptr, to); - WRITE_BYTE ((bot->m_pingOffset[sending] * 64) + (1 + 2 * i)); - WRITE_SHORT (bot->m_ping[sending]); - - sending++; - } - case 1: - { - // append additional data - WRITE_BYTE ((bot->m_pingOffset[sending] * 128) + (2 + 4 * i)); - WRITE_SHORT (bot->m_ping[sending]); - - sending++; - } - case 2: - { - // append additional data and end message - WRITE_BYTE (4 + 8 * i); - WRITE_SHORT (bot->m_ping[sending]); - WRITE_BYTE (0); - MESSAGE_END (); - - sending = 0; - } } - } - - // end message if not yet sent - if (sending) - { - WRITE_BYTE (0); - MESSAGE_END (); + msg.start (MSG_ONE_UNRELIABLE, SVC_PINGS, Vector::null (), to) + .writeByte (bot->m_pingOffset[0] * 64 + (1 + 2 * i)) + .writeShort (bot->m_ping[0]) + .writeByte (bot->m_pingOffset[1] * 128 + (2 + 4 * i)) + .writeShort (bot->m_ping[1]) + .writeByte (4 + 8 * i) + .writeShort (bot->m_ping[2]) + .writeByte (0) + .end (); } } -void BotManager::SendDeathMsgFix (void) -{ - if (yb_latency_display.GetInt () == 2 && m_deathMsgSent) - { +void BotManager::sendDeathMsgFix (void) { + if (yb_latency_display.integer () == 2 && m_deathMsgSent) { m_deathMsgSent = false; - for (int i = 0; i < engine.MaxClients (); i++) - SendPingDataOffsets (g_clients[i].ent); + for (int i = 0; i < engine.maxClients (); i++) { + sendPingOffsets (g_clients[i].ent); + } } } -void BotManager::UpdateActiveGrenades (void) -{ - if (m_grenadeUpdateTime > engine.Time ()) +void BotManager::updateActiveGrenade (void) { + if (m_grenadeUpdateTime > engine.timebase ()) { return; - + } edict_t *grenade = nullptr; // clear previously stored grenades - m_activeGrenades.RemoveAll (); + m_activeGrenades.clear (); // search the map for any type of grenade - while (!engine.IsNullEntity (grenade = FIND_ENTITY_BY_CLASSNAME (grenade, "grenade"))) - { + while (!engine.isNullEntity (grenade = g_engfuncs.pfnFindEntityByString (grenade, "classname", "grenade"))) { // do not count c4 as a grenade - if (strcmp (STRING (grenade->v.model) + 9, "c4.mdl") == 0) + if (strcmp (STRING (grenade->v.model) + 9, "c4.mdl") == 0) { continue; - - m_activeGrenades.Push (grenade); + } + m_activeGrenades.push (grenade); } - m_grenadeUpdateTime = 0.213f + engine.Time (); + m_grenadeUpdateTime = engine.timebase () + 0.213f; } -const Array &BotManager::GetActiveGrenades (void) -{ - return m_activeGrenades; +void BotManager::updateIntrestingEntities (void) { + if (m_entityUpdateTime > engine.timebase ()) { + return; + } + + // clear previously stored entities + m_intrestingEntities.clear (); + + // search the map for entities + for (int i = MAX_ENGINE_PLAYERS - 1; i < g_pGlobals->maxEntities; i++) { + auto ent = engine.entityOfIndex (i); + + // only valid drawn entities + if (engine.isNullEntity (ent) || ent->free || ent->v.classname == 0 || (ent->v.effects & EF_NODRAW)) { + continue; + } + auto classname = STRING (ent->v.classname); + + // 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) { + m_intrestingEntities.push (ent); + } + + // pickup some csdm stuff if we're running csdm + if ((g_mapFlags & MAP_CS) && strncmp ("hostage", classname, 7) == 0) { + m_intrestingEntities.push (ent); + } + + // pickup some csdm stuff if we're running csdm + if ((g_gameFlags & GAME_CSDM) && strncmp ("csdm", classname, 4) == 0) { + m_intrestingEntities.push (ent); + } + } + m_entityUpdateTime = engine.timebase () + 0.5f; } -void BotManager::SelectLeaderEachTeam (int team, bool reset) -{ - if (reset) - { +void BotManager::selectLeaders (int team, bool reset) { + if (reset) { m_leaderChoosen[team] = false; return; } - if (g_mapType & MAP_AS) - { - if (team == CT && !m_leaderChoosen[CT]) - { - for (int i = 0; i < engine.MaxClients (); i++) - { + if (m_leaderChoosen[team]) { + return; + } + + if (g_mapFlags & MAP_AS) { + if (team == TEAM_COUNTER && !m_leaderChoosen[TEAM_COUNTER]) { + for (int i = 0; i < engine.maxClients (); i++) { auto bot = m_bots[i]; - if (bot != nullptr && bot->m_isVIP) - { + if (bot != nullptr && bot->m_isVIP) { // vip bot is the leader bot->m_isLeader = true; - if (Random.Int (1, 100) < 50) - { - bot->RadioMessage (Radio_FollowMe); + if (rng.getInt (1, 100) < 50) { + bot->pushRadioMessage (RADIO_FOLLOW_ME); bot->m_campButtons = 0; } } } - m_leaderChoosen[CT] = true; + m_leaderChoosen[TEAM_COUNTER] = true; } - else if (team == TERRORIST && !m_leaderChoosen[TERRORIST]) - { - auto bot = bots.GetHighestFragsBot (team); + else if (team == TEAM_TERRORIST && !m_leaderChoosen[TEAM_TERRORIST]) { + auto bot = bots.getHighfragBot (team); - if (bot != nullptr && bot->m_notKilled) - { + if (bot != nullptr && bot->m_notKilled) { bot->m_isLeader = true; - if (Random.Int (1, 100) < 45) - bot->RadioMessage (Radio_FollowMe); + if (rng.getInt (1, 100) < 45) { + bot->pushRadioMessage (RADIO_FOLLOW_ME); + } } - m_leaderChoosen[TERRORIST] = true; + m_leaderChoosen[TEAM_TERRORIST] = true; } } - else if (g_mapType & MAP_DE) - { - if (team == TERRORIST && !m_leaderChoosen[TERRORIST]) - { - for (int i = 0; i < engine.MaxClients (); i++) - { + else if (g_mapFlags & MAP_DE) { + if (team == TEAM_TERRORIST && !m_leaderChoosen[TEAM_TERRORIST]) { + for (int i = 0; i < engine.maxClients (); i++) { auto bot = m_bots[i]; - if (bot != nullptr && bot->m_hasC4) - { + if (bot != nullptr && bot->m_hasC4) { // bot carrying the bomb is the leader bot->m_isLeader = true; // terrorist carrying a bomb needs to have some company - if (Random.Int (1, 100) < 80) - { - if (yb_communication_type.GetInt () == 2) - bot->ChatterMessage (Chatter_GoingToPlantBomb); - else - bot->ChatterMessage (Radio_FollowMe); - + if (rng.getInt (1, 100) < 80) { + if (yb_communication_type.integer () == 2) { + bot->pushChatterMessage (CHATTER_GOING_TO_PLANT_BOMB); + } + else { + bot->pushChatterMessage (RADIO_FOLLOW_ME); + } bot->m_campButtons = 0; } } - m_leaderChoosen[TERRORIST] = true; } + m_leaderChoosen[TEAM_TERRORIST] = true; } - else if (!m_leaderChoosen[CT]) - { - if (auto bot = bots.GetHighestFragsBot (team)) - { + else if (!m_leaderChoosen[TEAM_COUNTER]) { + if (auto bot = bots.getHighfragBot (team)) { bot->m_isLeader = true; - if (Random.Int (1, 100) < 30) - bot->RadioMessage (Radio_FollowMe); + if (rng.getInt (1, 100) < 30) { + bot->pushRadioMessage (RADIO_FOLLOW_ME); + } } - m_leaderChoosen[CT] = true; + m_leaderChoosen[TEAM_COUNTER] = true; } } - else if (g_mapType & (MAP_ES | MAP_KA | MAP_FY)) - { - if (auto bot = bots.GetHighestFragsBot (team)) - { + else if (g_mapFlags & (MAP_ES | MAP_KA | MAP_FY)) { + auto bot = bots.getHighfragBot (team); + + if (!m_leaderChoosen[team] && bot) { bot->m_isLeader = true; - if (Random.Int (1, 100) < 30) - bot->RadioMessage (Radio_FollowMe); + if (rng.getInt (1, 100) < 30) { + bot->pushRadioMessage (RADIO_FOLLOW_ME); + } + m_leaderChoosen[team] = true; } } - else - { - if (auto bot = bots.GetHighestFragsBot (team)) - { + else { + auto bot = bots.getHighfragBot (team); + + if (!m_leaderChoosen[team] && bot) { bot->m_isLeader = true; - if (Random.Int (1, 100) < (team == TERRORIST ? 30 : 40)) - bot->RadioMessage (Radio_FollowMe); + if (rng.getInt (1, 100) < (team == TEAM_TERRORIST ? 30 : 40)) { + bot->pushRadioMessage (RADIO_FOLLOW_ME); + } + m_leaderChoosen[team] = true; } } } diff --git a/source/navigate.cpp b/source/navigate.cpp index ecc4985..3e47a5b 100644 --- a/source/navigate.cpp +++ b/source/navigate.cpp @@ -4,36 +4,35 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include ConVar yb_whose_your_daddy ("yb_whose_your_daddy", "0"); +ConVar yb_debug_heuristic_type ("yb_debug_heuristic_type", "0"); + +int Bot::searchGoal (void) { -int Bot::FindGoal (void) -{ // chooses a destination (goal) waypoint for a bot - if (!g_bombPlanted && m_team == TERRORIST && (g_mapType & MAP_DE)) - { + if (!g_bombPlanted && m_team == TEAM_TERRORIST && (g_mapFlags & MAP_DE)) { edict_t *pent = nullptr; - while (!engine.IsNullEntity (pent = FIND_ENTITY_BY_STRING (pent, "classname", "weaponbox"))) - { - if (strcmp (STRING (pent->v.model), "models/w_backpack.mdl") == 0) - { - int index = waypoints.FindNearest (engine.GetAbsOrigin (pent)); + while (!engine.isNullEntity (pent = g_engfuncs.pfnFindEntityByString (pent, "classname", "weaponbox"))) { + if (strcmp (STRING (pent->v.model), "models/w_backpack.mdl") == 0) { + int index = waypoints.getNearest (engine.getAbsPos (pent)); - if (index >= 0 && index < g_numWaypoints) + if (waypoints.exists (index)) { return m_loosedBombWptIndex = index; - + } break; } } // forcing terrorist bot to not move to another bomb spot - if (m_inBombZone && !m_hasProgressBar && m_hasC4) - return waypoints.FindNearest (pev->origin, 400.0f, FLAG_GOAL); + if (m_inBombZone && !m_hasProgressBar && m_hasC4) { + return waypoints.getNearest (pev->origin, 768.0f, FLAG_GOAL); + } } int tactic = 0; @@ -47,17 +46,16 @@ int Bot::FindGoal (void) float backoffDesire = 0.0f; float tacticChoice = 0.0f; - Array *offensiveWpts = nullptr; - Array *defensiveWpts = nullptr; + IntArray *offensiveWpts = nullptr; + IntArray *defensiveWpts = nullptr; - switch (m_team) - { - case TERRORIST: + switch (m_team) { + case TEAM_TERRORIST: offensiveWpts = &waypoints.m_ctPoints; defensiveWpts = &waypoints.m_terrorPoints; break; - case CT: + case TEAM_COUNTER: default: offensiveWpts = &waypoints.m_terrorPoints; defensiveWpts = &waypoints.m_ctPoints; @@ -65,178 +63,163 @@ int Bot::FindGoal (void) } // terrorist carrying the C4? - if (m_hasC4 || m_isVIP) - { + if (m_hasC4 || m_isVIP) { tactic = 3; - return FinishFindGoal (tactic, defensiveWpts, offensiveWpts); + return getGoalProcess (tactic, defensiveWpts, offensiveWpts); } - else if (m_team == CT && HasHostage ()) - { + else if (m_team == TEAM_COUNTER && hasHostage ()) { tactic = 2; offensiveWpts = &waypoints.m_rescuePoints; - return FinishFindGoal (tactic, defensiveWpts, offensiveWpts); + return getGoalProcess (tactic, defensiveWpts, offensiveWpts); } offensive = m_agressionLevel * 100.0f; defensive = m_fearLevel * 100.0f; - if (g_mapType & (MAP_AS | MAP_CS)) - { - if (m_team == TERRORIST) - { + if (g_mapFlags & (MAP_AS | MAP_CS)) { + if (m_team == TEAM_TERRORIST) { defensive += 25.0f; offensive -= 25.0f; } - else if (m_team == CT) - { + else if (m_team == TEAM_COUNTER) { // on hostage maps force more bots to save hostages - if (g_mapType & MAP_CS) - { + if (g_mapFlags & MAP_CS) { defensive -= 25.0f - m_difficulty * 0.5f; offensive += 25.0f + m_difficulty * 5.0f; } - else // on AS leave as is - { + else { defensive -= 25.0f; offensive += 25.0f; } } } - else if ((g_mapType & MAP_DE) && m_team == CT) - { - if (g_bombPlanted && GetTaskId () != TASK_ESCAPEFROMBOMB && !waypoints.GetBombPosition ().IsZero ()) - { - if (g_bombSayString) - { - ChatMessage (CHAT_BOMBPLANT); + else if ((g_mapFlags & MAP_DE) && m_team == TEAM_COUNTER) { + if (g_bombPlanted && taskId () != TASK_ESCAPEFROMBOMB && !waypoints.getBombPos ().empty ()) { + if (g_bombSayString) { + pushChatMessage (CHAT_BOMBPLANT); g_bombSayString = false; } - return m_chosenGoalIndex = ChooseBombWaypoint (); + return m_chosenGoalIndex = getBombPoint (); } defensive += 25.0f + m_difficulty * 4.0f; offensive -= 25.0f - m_difficulty * 0.5f; - if (m_personality != PERSONALITY_RUSHER) + if (m_personality != PERSONALITY_RUSHER) { defensive += 10.0f; + } } - else if ((g_mapType & MAP_DE) && m_team == TERRORIST && g_timeRoundStart + 10.0f < engine.Time ()) - { + else if ((g_mapFlags & MAP_DE) && m_team == TEAM_TERRORIST && g_timeRoundStart + 10.0f < engine.timebase ()) { // send some terrorists to guard planted bomb - if (!m_defendedBomb && g_bombPlanted && GetTaskId () != TASK_ESCAPEFROMBOMB && GetBombTimeleft () >= 15.0) - return m_chosenGoalIndex = FindDefendWaypoint (waypoints.GetBombPosition ()); + if (!m_defendedBomb && g_bombPlanted && taskId () != TASK_ESCAPEFROMBOMB && getBombTimeleft () >= 15.0) { + return m_chosenGoalIndex = getDefendPoint (waypoints.getBombPos ()); + } } - goalDesire = Random.Float (0.0f, 100.0f) + offensive; - forwardDesire = Random.Float (0.0f, 100.0f) + offensive; - campDesire = Random.Float (0.0f, 100.0f) + defensive; - backoffDesire = Random.Float (0.0f, 100.0f) + defensive; + goalDesire = rng.getFloat (0.0f, 100.0f) + offensive; + forwardDesire = rng.getFloat (0.0f, 100.0f) + offensive; + campDesire = rng.getFloat (0.0f, 100.0f) + defensive; + backoffDesire = rng.getFloat (0.0f, 100.0f) + defensive; - if (!UsesCampGun ()) + if (!usesCampGun ()) { campDesire *= 0.5f; + } tacticChoice = backoffDesire; tactic = 0; - if (campDesire > tacticChoice) - { + if (campDesire > tacticChoice) { tacticChoice = campDesire; tactic = 1; } - if (forwardDesire > tacticChoice) - { + if (forwardDesire > tacticChoice) { tacticChoice = forwardDesire; tactic = 2; } - if (goalDesire > tacticChoice) + if (goalDesire > tacticChoice) { tactic = 3; - - return FinishFindGoal (tactic, defensiveWpts, offensiveWpts); + } + return getGoalProcess (tactic, defensiveWpts, offensiveWpts); } -int Bot::FinishFindGoal (int tactic, Array *defensive, Array *offsensive) -{ - int goalChoices[4] = { -1, -1, -1, -1 }; +int Bot::getGoalProcess (int tactic, IntArray *defensive, IntArray *offsensive) { + int goalChoices[4] = { INVALID_WAYPOINT_INDEX, INVALID_WAYPOINT_INDEX, INVALID_WAYPOINT_INDEX, INVALID_WAYPOINT_INDEX }; - if (tactic == 0 && !(*defensive).IsEmpty ()) // careful goal - FilterGoals (*defensive, goalChoices); - else if (tactic == 1 && !waypoints.m_campPoints.IsEmpty ()) // camp waypoint goal + if (tactic == 0 && !(*defensive).empty ()) { // careful goal + filterGoals (*defensive, goalChoices); + } + else if (tactic == 1 && !waypoints.m_campPoints.empty ()) // camp waypoint goal { // pickup sniper points if possible for sniping bots - if (!waypoints.m_sniperPoints.IsEmpty () && UsesSniper ()) - FilterGoals (waypoints.m_sniperPoints, goalChoices); - else - FilterGoals (waypoints.m_campPoints, goalChoices); + if (!waypoints.m_sniperPoints.empty () && usesSniper ()) { + filterGoals (waypoints.m_sniperPoints, goalChoices); + } + else { + filterGoals (waypoints.m_campPoints, goalChoices); + } } - else if (tactic == 2 && !(*offsensive).IsEmpty ()) // offensive goal - FilterGoals (*offsensive, goalChoices); - else if (tactic == 3 && !waypoints.m_goalPoints.IsEmpty ()) // map goal waypoint + else if (tactic == 2 && !(*offsensive).empty ()) { // offensive goal + filterGoals (*offsensive, goalChoices); + } + else if (tactic == 3 && !waypoints.m_goalPoints.empty ()) // map goal waypoint { // force bomber to select closest goal, if round-start goal was reset by something - if (m_hasC4 && g_timeRoundStart + 10.0f < engine.Time ()) - { + if (m_hasC4 && g_timeRoundStart + 20.0f < engine.timebase ()) { float minDist = 99999.0f; int count = 0; - FOR_EACH_AE (waypoints.m_goalPoints, i) - { - Path *path = waypoints.GetPath (waypoints.m_goalPoints[i]); + for (auto &point : waypoints.m_goalPoints) { + float distance = (waypoints[point].origin - pev->origin).lengthSq (); - float distance = (path->origin - pev->origin).GetLength (); - - if (distance > 1024.0f) + if (distance > 1024.0f) { continue; + } + if (distance < minDist) { + goalChoices[count] = point; - if (distance < minDist) - { - goalChoices[count] = i; - - if (++count > 3) + if (++count > 3) { count = 0; - + } minDist = distance; } } - for (int i = 0; i < 4; i++) - { - if (goalChoices[i] == -1) - { - goalChoices[i] = waypoints.m_goalPoints.GetRandomElement (); - InternalAssert (goalChoices[i] >= 0 && goalChoices[i] < g_numWaypoints); + for (int i = 0; i < 4; i++) { + if (goalChoices[i] == INVALID_WAYPOINT_INDEX) { + goalChoices[i] = waypoints.m_goalPoints.random (); } } } - else - FilterGoals (waypoints.m_goalPoints, goalChoices); + else { + filterGoals (waypoints.m_goalPoints, goalChoices); + } } - if (m_currentWaypointIndex == -1 || m_currentWaypointIndex >= g_numWaypoints) - m_currentWaypointIndex = ChangeWptIndex (waypoints.FindNearest (pev->origin)); - - if (goalChoices[0] == -1) - return m_chosenGoalIndex = Random.Int (0, g_numWaypoints - 1); + if (!waypoints.exists (m_currentWaypointIndex)) { + m_currentWaypointIndex = changePointIndex (getNearestPoint ()); + } + if (goalChoices[0] == INVALID_WAYPOINT_INDEX) { + return m_chosenGoalIndex = rng.getInt (0, waypoints.length () - 1); + } bool isSorting = false; - do - { + do { isSorting = false; - for (int i = 0; i < 3; i++) - { + for (int i = 0; i < 3; i++) { int testIndex = goalChoices[i + 1]; - if (testIndex < 0) + if (testIndex < 0) { break; + } - int goal1 = m_team == TERRORIST ? (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i])->team0Value : (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i])->team1Value; - int goal2 = m_team == TERRORIST ? (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i + 1])->team0Value : (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i + 1])->team1Value; + int goal1 = m_team == TEAM_TERRORIST ? (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + goalChoices[i])->team0Value : (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + goalChoices[i])->team1Value; + int goal2 = m_team == TEAM_TERRORIST ? (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + goalChoices[i + 1])->team0Value : (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + goalChoices[i + 1])->team1Value; - if (goal1 < goal2) - { + if (goal1 < goal2) { goalChoices[i + 1] = goalChoices[i]; goalChoices[i] = testIndex; @@ -248,21 +231,18 @@ int Bot::FinishFindGoal (int tactic, Array *defensive, Array *offsen return m_chosenGoalIndex = goalChoices[0]; // return and store goal } -void Bot::FilterGoals (const Array &goals, int *result) -{ +void Bot::filterGoals (const IntArray &goals, int *result) { // this function filters the goals, so new goal is not bot's old goal, and array of goals doesn't contains duplicate goals int searchCount = 0; - for (int index = 0; index < 4; index++) - { - int rand = goals.GetRandomElement (); + for (int index = 0; index < 4; index++) { + int rand = goals.random (); - if (searchCount <= 8 && (m_prevGoalIndex == rand || ((result[0] == rand || result[1] == rand || result[2] == rand || result[3] == rand) && goals.GetElementNumber () > 4)) && !IsPointOccupied (rand)) - { - if (index > 0) + if (searchCount <= 8 && (m_prevGoalIndex == rand || ((result[0] == rand || result[1] == rand || result[2] == rand || result[3] == rand) && goals.length () > 4)) && !isOccupiedPoint (rand)) { + if (index > 0) { index--; - + } searchCount++; continue; } @@ -270,31 +250,22 @@ void Bot::FilterGoals (const Array &goals, int *result) } } -bool Bot::GoalIsValid (void) -{ - int goal = GetTask ()->data; +bool Bot::hasActiveGoal (void) { + int goal = task ()->data; - if (goal == -1) // not decided about a goal + if (goal == INVALID_WAYPOINT_INDEX) { // not decided about a goal return false; - else if (goal == m_currentWaypointIndex) // no nodes needed + } + else if (goal == m_currentWaypointIndex) { // no nodes needed return true; - else if (m_navNode == nullptr) // no path calculated + } + else if (m_path.empty ()) { // no path calculated return false; - - // got path - check if still valid - PathNode *node = m_navNode; - - while (node->next != nullptr) - node = node->next; - - if (node->index == goal) - return true; - - return false; + } + return goal == m_path.back (); // got path - check if still valid } -void Bot::ResetCollideState (void) -{ +void Bot::resetCollision (void) { m_collideTime = 0.0f; m_probeTime = 0.0f; @@ -302,97 +273,131 @@ void Bot::ResetCollideState (void) m_collisionState = COLLISION_NOTDECICED; m_collStateIndex = 0; - for (int i = 0; i < MAX_COLLIDE_MOVES; i++) + for (int i = 0; i < MAX_COLLIDE_MOVES; i++) { m_collideMoves[i] = 0; -} - -void Bot::IgnoreCollisionShortly (void) -{ - ResetCollideState (); - - m_lastCollTime = engine.Time () + 0.35f; - m_isStuck = false; - m_checkTerrain = false; -} - -void Bot::CheckCloseAvoidance (const Vector &dirNormal) -{ - if (m_seeEnemyTime + 1.5f < engine.Time ()) - return; - - if (m_avoidTime < engine.Time () || m_avoid == nullptr) - return; - - MakeVectors (m_moveAngles); // use our movement angles - - float interval = GetThinkInterval (); - - // try to predict where we should be next frame - Vector moved = pev->origin + g_pGlobals->v_forward * m_moveSpeed * interval; - moved += g_pGlobals->v_right * m_strafeSpeed * interval; - moved += pev->velocity * interval; - - float nearestDistance = (m_avoid->v.origin - pev->origin).GetLength2D (); - float nextFrameDistance = ((m_avoid->v.origin + m_avoid->v.velocity * interval) - pev->origin).GetLength2D (); - - // is player that near now or in future that we need to steer away? - if ((m_avoid->v.origin - moved).GetLength2D () <= 48.0f || (nearestDistance <= 56.0f && nextFrameDistance < nearestDistance)) - { - // to start strafing, we have to first figure out if the target is on the left side or right side - const Vector &dirToPoint = (pev->origin - m_avoid->v.origin).Get2D (); - - if ((dirToPoint | g_pGlobals->v_right.Get2D ()) > 0.0f) - SetStrafeSpeed (dirNormal, pev->maxspeed); - else - SetStrafeSpeed (dirNormal, -pev->maxspeed); - - if (nearestDistance < 56.0f && (dirToPoint | g_pGlobals->v_forward.Get2D ()) < 0.0f) - m_moveSpeed = -pev->maxspeed; } } -void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) -{ +void Bot::ignoreCollision (void) { + resetCollision (); + + m_prevTime = engine.timebase () + 1.2f; + m_lastCollTime = engine.timebase () + 1.5f; + m_isStuck = false; + m_checkTerrain = false; + m_prevSpeed = m_moveSpeed; + m_prevOrigin = pev->origin; +} + +void Bot::avoidIncomingPlayers (edict_t *touch) { + auto task = taskId (); + + if (task == TASK_PLANTBOMB || task == TASK_DEFUSEBOMB || task == TASK_CAMP || m_moveSpeed <= 100.0f) { + return; + } + + int ownId = engine.indexOfEntity (ent ()); + int otherId = engine.indexOfEntity (touch); + + if (ownId < otherId) { + return; + } + + if (m_avoid) { + int currentId = engine.indexOfEntity (m_avoid); + + if (currentId < otherId) { + return; + } + } + m_avoid = touch; + m_avoidTime = engine.timebase () + 0.33f + calcThinkInterval (); +} + +bool Bot::doPlayerAvoidance (const Vector &normal) { + // avoid collision entity, got it form official csbot + + if (m_avoidTime > engine.timebase () && isAlive (m_avoid)) { + + Vector dir (cr::cosf (pev->v_angle.y), cr::sinf (pev->v_angle.y), 0.0f); + Vector lat (-dir.y, dir.x, 0.0f); + Vector to = Vector (m_avoid->v.origin.x - pev->origin.x, m_avoid->v.origin.y - pev->origin.y, 0.0f).normalize (); + + float toProj = to.x * dir.x + to.y * dir.y; + float latProj = to.x * lat.x + to.y * lat.y; + + const float c = 0.5f; + + if (toProj > c) { + m_moveSpeed = -pev->maxspeed; + return true; + } + else if (toProj < -c) { + m_moveSpeed = pev->maxspeed; + return true; + } + + if (latProj >= c) { + pev->button |= IN_MOVELEFT; + setStrafeSpeed (normal, pev->maxspeed); + return true; + } + else if (latProj <= -c) { + pev->button |= IN_MOVERIGHT; + setStrafeSpeed (normal, -pev->maxspeed); + return true; + } + return false; + } + else { + m_avoid = nullptr; + } + return false; +} + +void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { m_isStuck = false; TraceResult tr; // Standing still, no need to check? // FIXME: doesn't care for ladder movement (handled separately) should be included in some way - if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < engine.Time () && m_seeEnemyTime + 0.8f < engine.Time () && GetTaskId () != TASK_ATTACK) - { - if (movedDistance < 2.0f && m_prevSpeed >= 20.0f) // didn't we move enough previously? - { - // Then consider being stuck - m_prevTime = engine.Time (); + if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < engine.timebase () && m_seeEnemyTime + 0.8f < engine.timebase () && taskId () != TASK_ATTACK) { + + // didn't we move enough previously? + if (movedDistance < 2.0f && m_prevSpeed >= 20.0f) { + m_prevTime = engine.timebase (); // then consider being stuck m_isStuck = true; - if (m_firstCollideTime == 0.0) - m_firstCollideTime = engine.Time () + 0.2f; - } - else // not stuck yet - { - // test if there's something ahead blocking the way - if (CantMoveForward (dirNormal, &tr) && !IsOnLadder ()) - { - if (m_firstCollideTime == 0.0f) - m_firstCollideTime = engine.Time () + 0.2f; - - else if (m_firstCollideTime <= engine.Time ()) - m_isStuck = true; + if (cr::fzero (m_firstCollideTime)) { + m_firstCollideTime = engine.timebase () + 0.2f; } - else + } + // not stuck yet + else { + // test if there's something ahead blocking the way + if (cantMoveForward (dirNormal, &tr) && !isOnLadder ()) { + if (m_firstCollideTime == 0.0f) { + m_firstCollideTime = engine.timebase () + 0.2f; + } + else if (m_firstCollideTime <= engine.timebase ()) { + m_isStuck = true; + } + } + else { m_firstCollideTime = 0.0f; + } } - if (!m_isStuck) // not stuck? - { - if (m_probeTime + 0.5f < engine.Time ()) - ResetCollideState (); // reset collision memory if not being stuck for 0.5 secs - else - { + // not stuck? + if (!m_isStuck) { + if (m_probeTime + 0.5f < engine.timebase ()) { + resetCollision (); // reset collision memory if not being stuck for 0.5 secs + } + else { // remember to keep pressing duck if it was necessary ago - if (m_collideMoves[m_collStateIndex] == COLLISION_DUCK && (IsOnFloor () || IsInWater ())) + if (m_collideMoves[m_collStateIndex] == COLLISION_DUCK && (isOnFloor () || isInWater ())) { pev->button |= IN_DUCK; + } } return; } @@ -400,22 +405,23 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) Vector src; Vector dst; - + // not yet decided what to do? - if (m_collisionState == COLLISION_NOTDECICED) - { + if (m_collisionState == COLLISION_NOTDECICED) { int bits = 0; - if (IsOnLadder ()) + if (isOnLadder ()) { bits |= PROBE_STRAFE; - else if (IsInWater ()) + } + else if (isInWater ()) { bits |= (PROBE_JUMP | PROBE_STRAFE); - else - bits |= (PROBE_STRAFE | (m_jumpStateTimer < engine.Time () ? PROBE_JUMP : 0)); + } + else { + bits |= (PROBE_STRAFE | (m_jumpStateTimer < engine.timebase () ? PROBE_JUMP : 0)); + } // collision check allowed if not flying through the air - if (IsOnFloor () || IsOnLadder () || IsInWater ()) - { + if (isOnFloor () || isOnLadder () || isInWater ()) { int state[MAX_COLLIDE_MOVES * 2 + 1]; int i = 0; @@ -425,34 +431,34 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) state[i++] = COLLISION_JUMP; state[i++] = COLLISION_DUCK; - if (bits & PROBE_STRAFE) - { + if (bits & PROBE_STRAFE) { state[i] = 0; state[i + 1] = 0; // to start strafing, we have to first figure out if the target is on the left side or right side - MakeVectors (m_moveAngles); + makeVectors (m_moveAngles); - Vector dirToPoint = (pev->origin - m_destOrigin).Normalize2D (); - Vector rightSide = g_pGlobals->v_right.Normalize2D (); + Vector dirToPoint = (pev->origin - m_destOrigin).normalize2D (); + Vector rightSide = g_pGlobals->v_right.normalize2D (); bool dirRight = false; bool dirLeft = false; bool blockedLeft = false; bool blockedRight = false; - if ((dirToPoint | rightSide) > 0.0f) + if ((dirToPoint | rightSide) > 0.0f) { dirRight = true; - else + } + else { dirLeft = true; - + } const Vector &testDir = m_moveSpeed > 0.0f ? g_pGlobals->v_forward : -g_pGlobals->v_forward; // now check which side is blocked src = pev->origin + g_pGlobals->v_right * 32.0f; dst = src + testDir * 32.0f; - engine.TestHull (src, dst, TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); + engine.testHull (src, dst, TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); if (tr.flFraction != 1.0f) blockedRight = true; @@ -460,74 +466,83 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) src = pev->origin - g_pGlobals->v_right * 32.0f; dst = src + testDir * 32.0f; - engine.TestHull (src, dst, TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); + engine.testHull (src, dst, TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); - if (tr.flFraction != 1.0f) + if (tr.flFraction != 1.0f) { blockedLeft = true; + } - if (dirLeft) + if (dirLeft) { state[i] += 5; - else + } + else { state[i] -= 5; + } - if (blockedLeft) + if (blockedLeft) { state[i] -= 5; - + } i++; - if (dirRight) + if (dirRight) { state[i] += 5; - else + } + else { state[i] -= 5; + } - if (blockedRight) + if (blockedRight) { state[i] -= 5; + } } // now weight all possible states - if (bits & PROBE_JUMP) - { + if (bits & PROBE_JUMP) { state[i] = 0; - if (CanJumpUp (dirNormal)) + if (canJumpUp (dirNormal)) { state[i] += 10; + } - if (m_destOrigin.z >= pev->origin.z + 18.0f) + if (m_destOrigin.z >= pev->origin.z + 18.0f) { state[i] += 5; + } - if (EntityIsVisible (m_destOrigin)) - { - MakeVectors (m_moveAngles); + if (seesEntity (m_destOrigin)) { + makeVectors (m_moveAngles); - src = EyePosition (); + src = eyePos (); src = src + g_pGlobals->v_right * 15.0f; - engine.TestLine (src, m_destOrigin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (src, m_destOrigin, TRACE_IGNORE_EVERYTHING, ent (), &tr); - if (tr.flFraction >= 1.0f) - { - src = EyePosition (); + if (tr.flFraction >= 1.0f) { + src = eyePos (); src = src - g_pGlobals->v_right * 15.0f; - engine.TestLine (src, m_destOrigin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (src, m_destOrigin, TRACE_IGNORE_EVERYTHING, ent (), &tr); - if (tr.flFraction >= 1.0f) + if (tr.flFraction >= 1.0f) { state[i] += 5; + } } } - if (pev->flags & FL_DUCKING) + if (pev->flags & FL_DUCKING) { src = pev->origin; - else + } + else { src = pev->origin + Vector (0.0f, 0.0f, -17.0f); - + } dst = src + dirNormal * 30.0f; - engine.TestLine (src, dst, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (src, dst, TRACE_IGNORE_EVERYTHING, ent (), &tr); - if (tr.flFraction != 1.0f) + if (tr.flFraction != 1.0f) { state[i] += 10; + } } - else + else { state[i] = 0; + } i++; #if 0 @@ -535,27 +550,26 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) { state[i] = 0; - if (CanDuckUnder (dirNormal)) + if (canDuckUnder (dirNormal)) { state[i] += 10; + } - if ((m_destOrigin.z + 36.0f <= pev->origin.z) && EntityIsVisible (m_destOrigin)) + if ((m_destOrigin.z + 36.0f <= pev->origin.z) && seesEntity (m_destOrigin)) { state[i] += 5; + } } else #endif state[i] = 0; i++; - + // weighted all possible moves, now sort them to start with most probable bool isSorting = false; - do - { + do { isSorting = false; - for (i = 0; i < 3; i++) - { - if (state[i + MAX_COLLIDE_MOVES] < state[i + MAX_COLLIDE_MOVES + 1]) - { + for (i = 0; i < 3; i++) { + if (state[i + MAX_COLLIDE_MOVES] < state[i + MAX_COLLIDE_MOVES + 1]) { int temp = state[i]; state[i] = state[i + 1]; @@ -571,222 +585,204 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) } } while (isSorting); - for (i = 0; i < MAX_COLLIDE_MOVES; i++) + for (i = 0; i < MAX_COLLIDE_MOVES; i++) { m_collideMoves[i] = state[i]; + } - m_collideTime = engine.Time (); - m_probeTime = engine.Time () + 0.5f; + m_collideTime = engine.timebase (); + m_probeTime = engine.timebase () + 0.5f; m_collisionProbeBits = bits; m_collisionState = COLLISION_PROBING; m_collStateIndex = 0; } } - if (m_collisionState == COLLISION_PROBING) - { - if (m_probeTime < engine.Time ()) - { + if (m_collisionState == COLLISION_PROBING) { + if (m_probeTime < engine.timebase ()) { m_collStateIndex++; - m_probeTime = engine.Time () + 0.5f; + m_probeTime = engine.timebase () + 0.5f; - if (m_collStateIndex > MAX_COLLIDE_MOVES) - { - m_navTimeset = engine.Time () - 5.0f; - ResetCollideState (); + if (m_collStateIndex > MAX_COLLIDE_MOVES) { + m_navTimeset = engine.timebase () - 5.0f; + resetCollision (); } } - if (m_collStateIndex < MAX_COLLIDE_MOVES) - { - switch (m_collideMoves[m_collStateIndex]) - { + if (m_collStateIndex < MAX_COLLIDE_MOVES) { + switch (m_collideMoves[m_collStateIndex]) { case COLLISION_JUMP: - if (IsOnFloor () || IsInWater ()) - { + if (isOnFloor () || isInWater ()) { pev->button |= IN_JUMP; - m_jumpStateTimer = engine.Time () + Random.Float (0.7f, 1.5f); + m_jumpStateTimer = engine.timebase () + rng.getFloat (0.7f, 1.5f); } break; case COLLISION_DUCK: - if (IsOnFloor () || IsInWater ()) + if (isOnFloor () || isInWater ()) { pev->button |= IN_DUCK; + } break; case COLLISION_STRAFELEFT: pev->button |= IN_MOVELEFT; - SetStrafeSpeed (dirNormal, -pev->maxspeed); + setStrafeSpeed (dirNormal, -pev->maxspeed); break; case COLLISION_STRAFERIGHT: pev->button |= IN_MOVERIGHT; - SetStrafeSpeed (dirNormal, pev->maxspeed); + setStrafeSpeed (dirNormal, pev->maxspeed); break; } } } } - CheckCloseAvoidance (dirNormal); + doPlayerAvoidance (dirNormal); } -bool Bot::DoWaypointNav (void) -{ +bool Bot::processNavigation (void) { // this function is a main path navigation TraceResult tr, tr2; - + // check if we need to find a waypoint... - if (m_currentWaypointIndex == -1) - { - GetValidWaypoint (); + if (m_currentWaypointIndex == INVALID_WAYPOINT_INDEX) { + getValidPoint (); m_waypointOrigin = m_currentPath->origin; - + // if wayzone radios non zero vary origin a bit depending on the body angles - if (m_currentPath->radius > 0) - { - MakeVectors (Vector (pev->angles.x, AngleNormalize (pev->angles.y + Random.Float (-90.0f, 90.0f)), 0.0f)); - m_waypointOrigin = m_waypointOrigin + g_pGlobals->v_forward * Random.Float (0, m_currentPath->radius); + if (m_currentPath->radius > 0) { + makeVectors (Vector (pev->angles.x, cr::angleNorm (pev->angles.y + rng.getFloat (-90.0f, 90.0f)), 0.0f)); + m_waypointOrigin = m_waypointOrigin + g_pGlobals->v_forward * rng.getFloat (0, m_currentPath->radius); } - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); } + m_destOrigin = m_waypointOrigin + pev->view_ofs; - if (pev->flags & FL_DUCKING) - m_destOrigin = m_waypointOrigin; - else - m_destOrigin = m_waypointOrigin + pev->view_ofs; - - float waypointDistance = (pev->origin - m_waypointOrigin).GetLength (); + float waypointDistance = (pev->origin - m_waypointOrigin).length (); // this waypoint has additional travel flags - care about them - if (m_currentTravelFlags & PATHFLAG_JUMP) - { - // bot is not jumped yet? - if (!m_jumpFinished) - { - // if bot's on the ground or on the ladder we're free to jump. actually setting the correct velocity is cheating. - // pressing the jump button gives the illusion of the bot actual jmping. - if (IsOnFloor () || IsOnLadder ()) - { - if (m_desiredVelocity.x != 0.0f && m_desiredVelocity.y != 0.0f) - pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.046f; + if (m_currentTravelFlags & PATHFLAG_JUMP) { + // bot is not jumped yet? + if (!m_jumpFinished) { + + // if bot's on the ground or on the ladder we're free to jump. actually setting the correct velocity is cheating. + // pressing the jump button gives the illusion of the bot actual jumping. + if (isOnFloor () || isOnLadder ()) { + pev->velocity = m_desiredVelocity; pev->button |= IN_JUMP; m_jumpFinished = true; m_checkTerrain = false; - m_desiredVelocity.Zero (); + m_desiredVelocity.nullify (); } } - else if (!yb_jasonmode.GetBool () && m_currentWeapon == WEAPON_KNIFE && IsOnFloor ()) - SelectBestWeapon (); + else if (!yb_jasonmode.boolean () && m_currentWeapon == WEAPON_KNIFE && isOnFloor ()) { + selectBestWeapon (); + } } - if (m_currentPath->flags & FLAG_LADDER) - { - if (m_waypointOrigin.z >= (pev->origin.z + 16.0f)) + if (m_currentPath->flags & FLAG_LADDER) { + if (m_waypointOrigin.z >= (pev->origin.z + 16.0f)) { m_waypointOrigin = m_currentPath->origin + Vector (0.0f, 0.0f, 16.0f); - else if (m_waypointOrigin.z < pev->origin.z + 16.0f && !IsOnLadder () && IsOnFloor () && !(pev->flags & FL_DUCKING)) - { + } + else if (m_waypointOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !(pev->flags & FL_DUCKING)) { m_moveSpeed = waypointDistance; - if (m_moveSpeed < 150.0f) + if (m_moveSpeed < 150.0f) { m_moveSpeed = 150.0f; - else if (m_moveSpeed > pev->maxspeed) + } + else if (m_moveSpeed > pev->maxspeed) { m_moveSpeed = pev->maxspeed; + } } } // special lift handling (code merged from podbotmm) - if (m_currentPath->flags & FLAG_LIFT) - { + if (m_currentPath->flags & FLAG_LIFT) { bool liftClosedDoorExists = false; // update waypoint time set - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); // trace line to door - engine.TestLine (pev->origin, m_currentPath->origin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr2); + engine.testLine (pev->origin, m_currentPath->origin, TRACE_IGNORE_EVERYTHING, ent (), &tr2); - if (tr2.flFraction < 1.0f && strcmp (STRING (tr2.pHit->v.classname), "func_door") == 0 && (m_liftState == LIFT_NO_NEARBY || m_liftState == LIFT_WAITING_FOR || m_liftState == LIFT_LOOKING_BUTTON_OUTSIDE) && pev->groundentity != tr2.pHit) - { - if (m_liftState == LIFT_NO_NEARBY) - { + if (tr2.flFraction < 1.0f && strcmp (STRING (tr2.pHit->v.classname), "func_door") == 0 && (m_liftState == LIFT_NO_NEARBY || m_liftState == LIFT_WAITING_FOR || m_liftState == LIFT_LOOKING_BUTTON_OUTSIDE) && pev->groundentity != tr2.pHit) { + if (m_liftState == LIFT_NO_NEARBY) { m_liftState = LIFT_LOOKING_BUTTON_OUTSIDE; - m_liftUsageTime = engine.Time () + 7.0f; + m_liftUsageTime = engine.timebase () + 7.0f; } liftClosedDoorExists = true; } // trace line down - engine.TestLine (m_currentPath->origin, m_currentPath->origin + Vector (0.0f, 0.0f, -50.0f), TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + engine.testLine (m_currentPath->origin, m_currentPath->origin + Vector (0.0f, 0.0f, -50.0f), TRACE_IGNORE_EVERYTHING, ent (), &tr); // if trace result shows us that it is a lift - if (!engine.IsNullEntity (tr.pHit) && m_navNode != nullptr && (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 == LIFT_NO_NEARBY || m_liftState == LIFT_WAITING_FOR || m_liftState == LIFT_LOOKING_BUTTON_OUTSIDE) && tr.pHit->v.velocity.z == 0.0f) - { - if (fabsf (pev->origin.z - tr.vecEndPos.z) < 70.0f) - { + if (!engine.isNullEntity (tr.pHit) && !m_path.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 == LIFT_NO_NEARBY || m_liftState == LIFT_WAITING_FOR || m_liftState == LIFT_LOOKING_BUTTON_OUTSIDE) && tr.pHit->v.velocity.z == 0.0f) { + if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) { m_liftEntity = tr.pHit; m_liftState = LIFT_ENTERING_IN; m_liftTravelPos = m_currentPath->origin; - m_liftUsageTime = engine.Time () + 5.0f; + m_liftUsageTime = engine.timebase () + 5.0f; } } - else if (m_liftState == LIFT_TRAVELING_BY) - { + else if (m_liftState == LIFT_TRAVELING_BY) { m_liftState = LIFT_LEAVING; - m_liftUsageTime = engine.Time () + 7.0f; + m_liftUsageTime = engine.timebase () + 7.0f; } } - else if (m_navNode != nullptr) // no lift found at waypoint + else if (!m_path.empty ()) // no lift found at waypoint { - if ((m_liftState == LIFT_NO_NEARBY || m_liftState == LIFT_WAITING_FOR) && m_navNode->next != nullptr) - { - if (m_navNode->next->index >= 0 && m_navNode->next->index < g_numWaypoints && (waypoints.GetPath (m_navNode->next->index)->flags & FLAG_LIFT)) - { - engine.TestLine (m_currentPath->origin, waypoints.GetPath (m_navNode->next->index)->origin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + if ((m_liftState == LIFT_NO_NEARBY || m_liftState == LIFT_WAITING_FOR) && m_path.hasNext ()) { + int nextNode = m_path.next (); - if (!engine.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 (waypoints.exists (nextNode) && (waypoints[nextNode].flags & FLAG_LIFT)) { + engine.testLine (m_currentPath->origin, waypoints[nextNode].origin, TRACE_IGNORE_EVERYTHING, ent (), &tr); + + if (!engine.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)) { m_liftEntity = tr.pHit; + } } m_liftState = LIFT_LOOKING_BUTTON_OUTSIDE; - m_liftUsageTime = engine.Time () + 15.0f; + m_liftUsageTime = engine.timebase () + 15.0f; } } // bot is going to enter the lift - if (m_liftState == LIFT_ENTERING_IN) - { + if (m_liftState == LIFT_ENTERING_IN) { m_destOrigin = m_liftTravelPos; // check if we enough to destination - if ((pev->origin - m_destOrigin).GetLengthSquared () < 225) - { + if ((pev->origin - m_destOrigin).lengthSq () < 225.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_aimFlags |= AIM_NAVPOINT; - ResetCollideState (); + resetCollision (); // need to wait our following teammate ? bool needWaitForTeammate = false; // if some bot is following a bot going into lift - he should take the same lift to go - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot == nullptr || bot == this) + if (bot == nullptr || bot == this) { continue; + } - if (!bot->m_notKilled || bot->m_team != m_team || bot->m_targetEntity != GetEntity () || bot->GetTaskId () != TASK_FOLLOWUSER) + if (!bot->m_notKilled || bot->m_team != m_team || bot->m_targetEntity != ent () || bot->taskId () != TASK_FOLLOWUSER) { continue; + } - if (bot->pev->groundentity == m_liftEntity && bot->IsOnFloor ()) + if (bot->pev->groundentity == m_liftEntity && bot->isOnFloor ()) { break; + } bot->m_liftEntity = m_liftEntity; bot->m_liftState = LIFT_ENTERING_IN; @@ -795,256 +791,230 @@ bool Bot::DoWaypointNav (void) needWaitForTeammate = true; } - if (needWaitForTeammate) - { + if (needWaitForTeammate) { m_liftState = LIFT_WAIT_FOR_TEAMMATES; - m_liftUsageTime = engine.Time () + 8.0f; + m_liftUsageTime = engine.timebase () + 8.0f; } - else - { + else { m_liftState = LIFT_LOOKING_BUTTON_INSIDE; - m_liftUsageTime = engine.Time () + 10.0f; + m_liftUsageTime = engine.timebase () + 10.0f; } } } // bot is waiting for his teammates - if (m_liftState == LIFT_WAIT_FOR_TEAMMATES) - { + if (m_liftState == LIFT_WAIT_FOR_TEAMMATES) { // need to wait our following teammate ? bool needWaitForTeammate = false; - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); + for (int i = 0; i < engine.maxClients (); i++) { + Bot *bot = bots.getBot (i); - if (bot == nullptr) + if (bot == nullptr) { continue; // skip invalid bots + } - if (!bot->m_notKilled || bot->m_team != m_team || bot->m_targetEntity != GetEntity () || bot->GetTaskId () != TASK_FOLLOWUSER || bot->m_liftEntity != m_liftEntity) + if (!bot->m_notKilled || bot->m_team != m_team || bot->m_targetEntity != ent () || bot->taskId () != TASK_FOLLOWUSER || bot->m_liftEntity != m_liftEntity) { continue; + } - if (bot->pev->groundentity == m_liftEntity || !bot->IsOnFloor ()) - { + if (bot->pev->groundentity == m_liftEntity || !bot->isOnFloor ()) { needWaitForTeammate = true; break; } } // need to wait for teammate - if (needWaitForTeammate) - { + if (needWaitForTeammate) { m_destOrigin = m_liftTravelPos; - if ((pev->origin - m_destOrigin).GetLengthSquared () < 225.0f) - { + if ((pev->origin - m_destOrigin).lengthSq () < 225.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_aimFlags |= AIM_NAVPOINT; - ResetCollideState (); + resetCollision (); } } // else we need to look for button - if (!needWaitForTeammate || m_liftUsageTime < engine.Time ()) - { + if (!needWaitForTeammate || m_liftUsageTime < engine.timebase ()) { m_liftState = LIFT_LOOKING_BUTTON_INSIDE; - m_liftUsageTime = engine.Time () + 10.0f; + m_liftUsageTime = engine.timebase () + 10.0f; } } // bot is trying to find button inside a lift - if (m_liftState == LIFT_LOOKING_BUTTON_INSIDE) - { - edict_t *button = FindNearestButton (STRING (m_liftEntity->v.targetname)); + if (m_liftState == LIFT_LOOKING_BUTTON_INSIDE) { + edict_t *button = getNearestButton (STRING (m_liftEntity->v.targetname)); // got a valid button entity ? - if (!engine.IsNullEntity (button) && pev->groundentity == m_liftEntity && m_buttonPushTime + 1.0f < engine.Time () && m_liftEntity->v.velocity.z == 0.0f && IsOnFloor ()) - { + if (!engine.isNullEntity (button) && pev->groundentity == m_liftEntity && m_buttonPushTime + 1.0f < engine.timebase () && m_liftEntity->v.velocity.z == 0.0f && isOnFloor ()) { m_pickupItem = button; m_pickupType = PICKUP_BUTTON; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); } } // is lift activated and bot is standing on it and lift is moving ? - if (m_liftState == LIFT_LOOKING_BUTTON_INSIDE || m_liftState == LIFT_ENTERING_IN || m_liftState == LIFT_WAIT_FOR_TEAMMATES || m_liftState == LIFT_WAITING_FOR) - { - if (pev->groundentity == m_liftEntity && m_liftEntity->v.velocity.z != 0.0f && IsOnFloor () && ((waypoints.GetPath (m_prevWptIndex[0])->flags & FLAG_LIFT) || !engine.IsNullEntity (m_targetEntity))) - { + if (m_liftState == LIFT_LOOKING_BUTTON_INSIDE || m_liftState == LIFT_ENTERING_IN || m_liftState == LIFT_WAIT_FOR_TEAMMATES || m_liftState == LIFT_WAITING_FOR) { + if (pev->groundentity == m_liftEntity && m_liftEntity->v.velocity.z != 0.0f && isOnFloor () && ((waypoints[m_prevWptIndex[0]].flags & FLAG_LIFT) || !engine.isNullEntity (m_targetEntity))) { m_liftState = LIFT_TRAVELING_BY; - m_liftUsageTime = engine.Time () + 14.0f; + m_liftUsageTime = engine.timebase () + 14.0f; - if ((pev->origin - m_destOrigin).GetLengthSquared () < 225.0f) - { + if ((pev->origin - m_destOrigin).lengthSq () < 225.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_aimFlags |= AIM_NAVPOINT; - ResetCollideState (); + resetCollision (); } } } // bots is currently moving on lift - if (m_liftState == LIFT_TRAVELING_BY) - { + if (m_liftState == LIFT_TRAVELING_BY) { m_destOrigin = Vector (m_liftTravelPos.x, m_liftTravelPos.y, pev->origin.z); - if ((pev->origin - m_destOrigin).GetLengthSquared () < 225.0f) - { + if ((pev->origin - m_destOrigin).lengthSq () < 225.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_aimFlags |= AIM_NAVPOINT; - ResetCollideState (); + resetCollision (); } } // need to find a button outside the lift - if (m_liftState == LIFT_LOOKING_BUTTON_OUTSIDE) - { + if (m_liftState == LIFT_LOOKING_BUTTON_OUTSIDE) { // button has been pressed, lift should come - if (m_buttonPushTime + 8.0f >= engine.Time ()) - { - if (m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints) - m_destOrigin = waypoints.GetPath (m_prevWptIndex[0])->origin; - else + if (m_buttonPushTime + 8.0f >= engine.timebase ()) { + if (waypoints.exists (m_prevWptIndex[0])) { + m_destOrigin = waypoints[m_prevWptIndex[0]].origin; + } + else { m_destOrigin = pev->origin; + } - if ((pev->origin - m_destOrigin).GetLengthSquared () < 225.0f) - { + if ((pev->origin - m_destOrigin).lengthSq () < 225.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_aimFlags |= AIM_NAVPOINT; - ResetCollideState (); + resetCollision (); } } - else if (!engine.IsNullEntity(m_liftEntity)) - { - edict_t *button = FindNearestButton (STRING (m_liftEntity->v.targetname)); + else if (!engine.isNullEntity (m_liftEntity)) { + edict_t *button = getNearestButton (STRING (m_liftEntity->v.targetname)); // if we got a valid button entity - if (!engine.IsNullEntity (button)) - { + if (!engine.isNullEntity (button)) { // lift is already used ? bool liftUsed = false; // iterate though clients, and find if lift already used - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity () || engine.IsNullEntity (client.ent->v.groundentity)) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent () || engine.isNullEntity (client.ent->v.groundentity)) { continue; + } - if (client.ent->v.groundentity == m_liftEntity) - { + if (client.ent->v.groundentity == m_liftEntity) { liftUsed = true; break; } } // lift is currently used - if (liftUsed) - { - if (m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints) - m_destOrigin = waypoints.GetPath (m_prevWptIndex[0])->origin; - else + if (liftUsed) { + if (waypoints.exists (m_prevWptIndex[0])) { + m_destOrigin = waypoints[m_prevWptIndex[0]].origin; + } + else { m_destOrigin = button->v.origin; + } - if ((pev->origin - m_destOrigin).GetLengthSquared () < 225.0f) - { + if ((pev->origin - m_destOrigin).lengthSq () < 225.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; } } - else - { + else { m_pickupItem = button; m_pickupType = PICKUP_BUTTON; m_liftState = LIFT_WAITING_FOR; - m_navTimeset = engine.Time (); - m_liftUsageTime = engine.Time () + 20.0f; + m_navTimeset = engine.timebase (); + m_liftUsageTime = engine.timebase () + 20.0f; } } - else - { + else { m_liftState = LIFT_WAITING_FOR; - m_liftUsageTime = engine.Time () + 15.0f; + m_liftUsageTime = engine.timebase () + 15.0f; } } } // bot is waiting for lift - if (m_liftState == LIFT_WAITING_FOR) - { - if (m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints) - { - if (!(waypoints.GetPath (m_prevWptIndex[0])->flags & FLAG_LIFT)) - m_destOrigin = waypoints.GetPath (m_prevWptIndex[0])->origin; - else if (m_prevWptIndex[1] >= 0 && m_prevWptIndex[1] < g_numWaypoints) - m_destOrigin = waypoints.GetPath (m_prevWptIndex[1])->origin; + if (m_liftState == LIFT_WAITING_FOR) { + if (waypoints.exists (m_prevWptIndex[0])) { + if (!(waypoints[m_prevWptIndex[0]].flags & FLAG_LIFT)) { + m_destOrigin = waypoints[m_prevWptIndex[0]].origin; + } + else if (waypoints.exists (m_prevWptIndex[1])) { + m_destOrigin = waypoints[m_prevWptIndex[1]].origin; + } } - if ((pev->origin - m_destOrigin).GetLengthSquared () < 100.0f) - { + if ((pev->origin - m_destOrigin).lengthSq () < 100.0f) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); m_aimFlags |= AIM_NAVPOINT; - ResetCollideState (); + resetCollision (); } } // if bot is waiting for lift, or going to it - if (m_liftState == LIFT_WAITING_FOR || m_liftState == LIFT_ENTERING_IN) - { + if (m_liftState == LIFT_WAITING_FOR || m_liftState == LIFT_ENTERING_IN) { // bot fall down somewhere inside the lift's groove :) - if (pev->groundentity != m_liftEntity && m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints) - { - if ((waypoints.GetPath (m_prevWptIndex[0])->flags & FLAG_LIFT) && (m_currentPath->origin.z - pev->origin.z) > 50.0f && (waypoints.GetPath (m_prevWptIndex[0])->origin.z - pev->origin.z) > 50.0f) - { + if (pev->groundentity != m_liftEntity && waypoints.exists (m_prevWptIndex[0])) { + if ((waypoints[m_prevWptIndex[0]].flags & FLAG_LIFT) && (m_currentPath->origin.z - pev->origin.z) > 50.0f && (waypoints[m_prevWptIndex[0]].origin.z - pev->origin.z) > 50.0f) { m_liftState = LIFT_NO_NEARBY; m_liftEntity = nullptr; m_liftUsageTime = 0.0f; - DeleteSearchNodes (); - FindWaypoint (); - - if (m_prevWptIndex[2] >= 0 && m_prevWptIndex[2] < g_numWaypoints) - FindShortestPath (m_currentWaypointIndex, m_prevWptIndex[2]); + clearSearchNodes (); + searchOptimalPoint (); + if (waypoints.exists (m_prevWptIndex[2])) { + searchShortestPath (m_currentWaypointIndex, m_prevWptIndex[2]); + } return false; } } } } - if (!engine.IsNullEntity (m_liftEntity) && !(m_currentPath->flags & FLAG_LIFT)) - { - if (m_liftState == LIFT_TRAVELING_BY) - { + if (!engine.isNullEntity (m_liftEntity) && !(m_currentPath->flags & FLAG_LIFT)) { + if (m_liftState == LIFT_TRAVELING_BY) { m_liftState = LIFT_LEAVING; - m_liftUsageTime = engine.Time () + 10.0f; + m_liftUsageTime = engine.timebase () + 10.0f; } - if (m_liftState == LIFT_LEAVING && m_liftUsageTime < engine.Time () && pev->groundentity != m_liftEntity) - { + if (m_liftState == LIFT_LEAVING && m_liftUsageTime < engine.timebase () && pev->groundentity != m_liftEntity) { m_liftState = LIFT_NO_NEARBY; m_liftUsageTime = 0.0f; @@ -1052,1089 +1022,983 @@ bool Bot::DoWaypointNav (void) } } - if (m_liftUsageTime < engine.Time () && m_liftUsageTime != 0.0f) - { + if (m_liftUsageTime < engine.timebase () && m_liftUsageTime != 0.0f) { m_liftEntity = nullptr; m_liftState = LIFT_NO_NEARBY; m_liftUsageTime = 0.0f; - DeleteSearchNodes (); + clearSearchNodes (); - if (m_prevWptIndex[0] >= 0 && m_prevWptIndex[0] < g_numWaypoints) - { - if (!(waypoints.GetPath (m_prevWptIndex[0])->flags & FLAG_LIFT)) - ChangeWptIndex (m_prevWptIndex[0]); - else - FindWaypoint (); + if (waypoints.exists (m_prevWptIndex[0])) { + if (!(waypoints[m_prevWptIndex[0]].flags & FLAG_LIFT)) { + changePointIndex (m_prevWptIndex[0]); + } + else { + searchOptimalPoint (); + } + } + else { + searchOptimalPoint (); } - else - FindWaypoint (); - return false; } // check if we are going through a door... - engine.TestLine (pev->origin, m_waypointOrigin, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + if (g_mapFlags & MAP_HAS_DOORS) { + engine.testLine (pev->origin, m_waypointOrigin, TRACE_IGNORE_MONSTERS, ent (), &tr); - if (!engine.IsNullEntity (tr.pHit) && engine.IsNullEntity (m_liftEntity) && strncmp (STRING (tr.pHit->v.classname), "func_door", 9) == 0) - { - // if the door is near enough... - if ((engine.GetAbsOrigin (tr.pHit) - pev->origin).GetLengthSquared () < 2500.0f) - { - IgnoreCollisionShortly (); // don't consider being stuck + if (!engine.isNullEntity (tr.pHit) && engine.isNullEntity (m_liftEntity) && strncmp (STRING (tr.pHit->v.classname), "func_door", 9) == 0) { + // if the door is near enough... + if ((engine.getAbsPos (tr.pHit) - pev->origin).lengthSq () < 2500.0f) { + ignoreCollision (); // don't consider being stuck - if (Random.Int (1, 100) < 50) - { - // do not use door directrly under xash, or we will get failed assert in gamedll code - if (g_gameFlags & GAME_XASH_ENGINE) - pev->button |= IN_USE; - else - MDLL_Use (tr.pHit, GetEntity ()); // also 'use' the door randomly + if (rng.getInt (1, 100) < 50) { + // do not use door directrly under xash, or we will get failed assert in gamedll code + if (g_gameFlags & GAME_XASH_ENGINE) { + pev->button |= IN_USE; + } + else { + MDLL_Use (tr.pHit, ent ()); // also 'use' the door randomly + } + } } - } - // make sure we are always facing the door when going through it - m_aimFlags &= ~(AIM_LAST_ENEMY | AIM_PREDICT_PATH); + // make sure we are always facing the door when going through it + m_aimFlags &= ~(AIM_LAST_ENEMY | AIM_PREDICT_PATH); + m_canChooseAimDirection = false; - edict_t *button = FindNearestButton (STRING (tr.pHit->v.targetname)); + edict_t *button = getNearestButton (STRING (tr.pHit->v.targetname)); - // check if we got valid button - if (!engine.IsNullEntity (button)) - { - m_pickupItem = button; - m_pickupType = PICKUP_BUTTON; + // check if we got valid button + if (!engine.isNullEntity (button)) { + m_pickupItem = button; + m_pickupType = PICKUP_BUTTON; - m_navTimeset = engine.Time (); - } + m_navTimeset = engine.timebase (); + } - // if bot hits the door, then it opens, so wait a bit to let it open safely - if (pev->velocity.GetLength2D () < 2 && m_timeDoorOpen < engine.Time ()) - { - PushTask (TASK_PAUSE, TASKPRI_PAUSE, -1, engine.Time () + 1, false); + // if bot hits the door, then it opens, so wait a bit to let it open safely + if (pev->velocity.length2D () < 2 && m_timeDoorOpen < engine.timebase ()) { + startTask (TASK_PAUSE, TASKPRI_PAUSE, INVALID_WAYPOINT_INDEX, engine.timebase () + 0.5f, false); + m_timeDoorOpen = engine.timebase () + 1.0f; // retry in 1 sec until door is open - m_doorOpenAttempt++; - m_timeDoorOpen = engine.Time () + 1.0f; // retry in 1 sec until door is open + edict_t *pent = nullptr; - edict_t *ent = nullptr; - - if (m_doorOpenAttempt > 2 && !engine.IsNullEntity (ent = FIND_ENTITY_IN_SPHERE (ent, pev->origin, 512.0f))) - { - if (IsValidPlayer (ent) && IsAlive (ent) && m_team != engine.GetTeam (ent) && GetWeaponPenetrationPower (m_currentWeapon) > 0) - { - m_seeEnemyTime = engine.Time () - 0.5f; + if (m_tryOpenDoor++ > 2 && findNearestPlayer (reinterpret_cast (&pent), ent (), 256.0f, false, false, true, true, false)) { + m_seeEnemyTime = engine.timebase () - 0.5f; m_states |= STATE_SEEING_ENEMY; m_aimFlags |= AIM_ENEMY; - m_lastEnemy = ent; - m_enemy = ent; - m_lastEnemyOrigin = ent->v.origin; + m_lastEnemy = pent; + m_enemy = pent; + m_lastEnemyOrigin = pent->v.origin; + + m_tryOpenDoor = 0; } - else if (IsValidPlayer (ent) && IsAlive (ent) && m_team == engine.GetTeam (ent)) - { - DeleteSearchNodes (); - ResetTasks (); - } - else if (IsValidPlayer (ent) && (!IsAlive (ent) || (ent->v.deadflag & DEAD_DYING))) - m_doorOpenAttempt = 0; // reset count + else + m_tryOpenDoor = 0; } } } - float desiredDistance = 0.0f; // initialize the radius for a special waypoint type, where the wpt is considered to be reached - if (m_currentPath->flags & FLAG_LIFT) + if (m_currentPath->flags & FLAG_LIFT) { desiredDistance = 50.0f; - else if ((pev->flags & FL_DUCKING) || (m_currentPath->flags & FLAG_GOAL)) + } + else if ((pev->flags & FL_DUCKING) || (m_currentPath->flags & FLAG_GOAL)) { desiredDistance = 25.0f; - else if (m_currentPath->flags & FLAG_LADDER) + } + else if (m_currentPath->flags & FLAG_LADDER) { desiredDistance = 15.0f; - else if (m_currentTravelFlags & PATHFLAG_JUMP) + } + else if (m_currentTravelFlags & PATHFLAG_JUMP) { desiredDistance = 0.0f; - else + } + else if (isOccupiedPoint (m_currentWaypointIndex)) { + desiredDistance = 120.0f; + } + else { desiredDistance = m_currentPath->radius; + } // check if waypoint has a special travelflag, so they need to be reached more precisely - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (m_currentPath->connectionFlags[i] != 0) - { - desiredDistance = 0; + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (m_currentPath->connectionFlags[i] != 0) { + desiredDistance = 0.0f; break; } } // needs precise placement - check if we get past the point - if (desiredDistance < 16.0f && waypointDistance < 30.0f && (pev->origin + (pev->velocity * GetThinkInterval ()) - m_waypointOrigin).GetLength () > waypointDistance) + if (desiredDistance < 22.0f && waypointDistance < 30.0f && (pev->origin + (pev->velocity * calcThinkInterval ()) - m_waypointOrigin).lengthSq () > cr::square (waypointDistance)) { desiredDistance = waypointDistance + 1.0f; + } - if (waypointDistance < desiredDistance) - { + if (waypointDistance < desiredDistance) { + // did we reach a destination waypoint? - if (GetTask ()->data == m_currentWaypointIndex) - { + if (task ()->data == m_currentWaypointIndex) { // add goal values - if (m_chosenGoalIndex != -1) - { + if (m_chosenGoalIndex != INVALID_WAYPOINT_INDEX) { int16 waypointValue; int startIndex = m_chosenGoalIndex; int goalIndex = m_currentWaypointIndex; - if (m_team == TERRORIST) - { - waypointValue = (g_experienceData + (startIndex * g_numWaypoints) + goalIndex)->team0Value; + if (m_team == TEAM_TERRORIST) { + waypointValue = (g_experienceData + (startIndex * waypoints.length ()) + goalIndex)->team0Value; waypointValue += static_cast (pev->health * 0.5f); waypointValue += static_cast (m_goalValue * 0.5f); - if (waypointValue < -MAX_GOAL_VALUE) + if (waypointValue < -MAX_GOAL_VALUE) { waypointValue = -MAX_GOAL_VALUE; - else if (waypointValue > MAX_GOAL_VALUE) + } + else if (waypointValue > MAX_GOAL_VALUE) { waypointValue = MAX_GOAL_VALUE; - - (g_experienceData + (startIndex * g_numWaypoints) + goalIndex)->team0Value = waypointValue; + } + (g_experienceData + (startIndex * waypoints.length ()) + goalIndex)->team0Value = waypointValue; } - else - { - waypointValue = (g_experienceData + (startIndex * g_numWaypoints) + goalIndex)->team1Value; + else { + waypointValue = (g_experienceData + (startIndex * waypoints.length ()) + goalIndex)->team1Value; waypointValue += static_cast (pev->health * 0.5f); waypointValue += static_cast (m_goalValue * 0.5f); - if (waypointValue < -MAX_GOAL_VALUE) + if (waypointValue < -MAX_GOAL_VALUE) { waypointValue = -MAX_GOAL_VALUE; - else if (waypointValue > MAX_GOAL_VALUE) + } + else if (waypointValue > MAX_GOAL_VALUE) { waypointValue = MAX_GOAL_VALUE; - - (g_experienceData + (startIndex * g_numWaypoints) + goalIndex)->team1Value = waypointValue; + } + (g_experienceData + (startIndex * waypoints.length ()) + goalIndex)->team1Value = waypointValue; } } return true; } - else if (m_navNode == nullptr) + else if (m_path.empty ()) { return false; + } + int taskTarget = task ()->data; - int taskTarget = GetTask ()->data; - - if ((g_mapType & MAP_DE) && g_bombPlanted && m_team == CT && GetTaskId () != TASK_ESCAPEFROMBOMB && taskTarget != -1) - { - const Vector &bombOrigin = CheckBombAudible (); + if ((g_mapFlags & MAP_DE) && g_bombPlanted && m_team == TEAM_COUNTER && taskId () != TASK_ESCAPEFROMBOMB && taskTarget != INVALID_WAYPOINT_INDEX) { + const Vector &bombOrigin = isBombAudible (); // bot within 'hearable' bomb tick noises? - if (!bombOrigin.IsZero ()) - { - float distance = (bombOrigin - waypoints.GetPath (taskTarget)->origin).GetLength (); + if (!bombOrigin.empty ()) { + float distance = (bombOrigin - waypoints[taskTarget].origin).length (); - if (distance > 512.0f) - { - if (Random.Int (0, 100) < 50 && !waypoints.IsGoalVisited (taskTarget)) - RadioMessage (Radio_SectorClear); - - waypoints.SetGoalVisited (taskTarget); // doesn't hear so not a good goal + if (distance > 512.0f) { + if (rng.getInt (0, 100) < 50 && !waypoints.isVisited (taskTarget)) { + pushRadioMessage (RADIO_SECTOR_CLEAR); + } + waypoints.setVisited (taskTarget); // doesn't hear so not a good goal } } - else - { - if (Random.Int (0, 100) < 50 && !waypoints.IsGoalVisited (taskTarget)) - RadioMessage (Radio_SectorClear); - - waypoints.SetGoalVisited (taskTarget); // doesn't hear so not a good goal + else { + if (rng.getInt (0, 100) < 50 && !waypoints.isVisited (taskTarget)) { + pushRadioMessage (RADIO_SECTOR_CLEAR); + } + waypoints.setVisited (taskTarget); // doesn't hear so not a good goal } } - HeadTowardWaypoint (); // do the actual movement checking + advanceMovement (); // do the actual movement checking } return false; } - -void Bot::FindShortestPath (int srcIndex, int destIndex) -{ +void Bot::searchShortestPath (int srcIndex, int destIndex) { // this function finds the shortest path from source index to destination index - if (srcIndex > g_numWaypoints - 1 || srcIndex < 0) - { - AddLogEntry (true, LL_ERROR, "Pathfinder source path index not valid (%d)", srcIndex); + if (!waypoints.exists (srcIndex)){ + logEntry (true, LL_ERROR, "Pathfinder source path index not valid (%d)", srcIndex); return; } - - if (destIndex > g_numWaypoints - 1 || destIndex < 0) - { - AddLogEntry (true, LL_ERROR, "Pathfinder destination path index not valid (%d)", destIndex); + else if (!waypoints.exists (destIndex)) { + logEntry (true, LL_ERROR, "Pathfinder destination path index not valid (%d)", destIndex); return; } - - DeleteSearchNodes (); + clearSearchNodes (); m_chosenGoalIndex = srcIndex; m_goalValue = 0.0f; - PathNode *node = new PathNode; + m_path.push (srcIndex); - node->index = srcIndex; - node->next = nullptr; + while (srcIndex != destIndex) { + srcIndex = *(waypoints.m_pathMatrix + (srcIndex * waypoints.length ()) + destIndex); - m_navNodeStart = node; - m_navNode = m_navNodeStart; - - while (srcIndex != destIndex) - { - srcIndex = *(waypoints.m_pathMatrix + (srcIndex * g_numWaypoints) + destIndex); - - if (srcIndex < 0) - { - m_prevGoalIndex = -1; - GetTask ()->data = -1; + if (srcIndex < 0) { + m_prevGoalIndex = INVALID_WAYPOINT_INDEX; + task ()->data = INVALID_WAYPOINT_INDEX; return; } - - node->next = new PathNode; - node = node->next; - - if (node == nullptr) - TerminateOnMalloc (); - - node->index = srcIndex; - node->next = nullptr; + m_path.push (srcIndex); } } -// priority queue class (smallest item out first, hlsdk) -class PriorityQueue -{ -private: - struct Node - { - int id; - float pri; - }; - - int m_allocCount; - int m_size; - int m_heapSize; - Node *m_heap; - -public: - - inline bool IsEmpty (void) - { - return m_size == 0; - } - - inline PriorityQueue (int initialSize = MAX_WAYPOINTS * 0.5f) - { - m_size = 0; - m_heapSize = initialSize; - m_allocCount = 0; - - m_heap = static_cast (malloc (sizeof (Node) * m_heapSize)); - } - - inline ~PriorityQueue (void) - { - free (m_heap); - m_heap = nullptr; - } - - // inserts a value into the priority queue - inline void Push (int value, float pri) - { - if (m_allocCount > 20) - { - AddLogEntry (false, LL_FATAL, "Tried to re-allocate heap too many times in pathfinder. This usually indicates corrupted waypoint file. Please obtain new copy of waypoint."); - return; - } - - if (m_heap == nullptr) - return; - - if (m_size >= m_heapSize) - { - m_allocCount++; - m_heapSize += 100; - - Node *newHeap = static_cast (realloc (m_heap, sizeof (Node) * m_heapSize)); - - if (newHeap != nullptr) - m_heap = newHeap; - } - - m_heap[m_size].pri = pri; - m_heap[m_size].id = value; - - int child = ++m_size - 1; - - while (child) - { - int parent = static_cast ((child - 1) * 0.5f); - - if (m_heap[parent].pri <= m_heap[child].pri) - break; - - Node ref = m_heap[child]; - - m_heap[child] = m_heap[parent]; - m_heap[parent] = ref; - - child = parent; - } - } - - // removes the smallest item from the priority queue - inline int Pop (void) - { - int result = m_heap[0].id; - - m_size--; - m_heap[0] = m_heap[m_size]; - - int parent = 0; - int child = (2 * parent) + 1; - - Node ref = m_heap[parent]; - - while (child < m_size) - { - int right = (2 * parent) + 2; - - if (right < m_size && m_heap[right].pri < m_heap[child].pri) - child = right; - - if (ref.pri <= m_heap[child].pri) - break; - - m_heap[parent] = m_heap[child]; - - parent = child; - child = (2 * parent) + 1; - } - m_heap[parent] = ref; - return result; - } -}; - -float gfunctionKillsDistT (int currentIndex, int parentIndex) -{ +float gfunctionKillsDistT (int currentIndex, int parentIndex) { // least kills and number of nodes to goal for a team - if (parentIndex == -1) + if (parentIndex == INVALID_WAYPOINT_INDEX) { return 0.0f; + } + float cost = static_cast ((g_experienceData + (currentIndex * waypoints.length ()) + currentIndex)->team0Damage + g_highestDamageT); - float cost = static_cast ((g_experienceData + (currentIndex * g_numWaypoints) + currentIndex)->team0Damage + g_highestDamageT); + Path ¤t = waypoints[currentIndex]; - Path *current = waypoints.GetPath (currentIndex); + for (int i = 0; i < MAX_PATH_INDEX; i++) { + int neighbour = current.index[i]; - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - int neighbour = current->index[i]; - - if (neighbour != -1) - cost += (g_experienceData + (neighbour * g_numWaypoints) + neighbour)->team0Damage; + if (neighbour != INVALID_WAYPOINT_INDEX) { + cost += (g_experienceData + (neighbour * waypoints.length ()) + neighbour)->team0Damage; + } } - if (current->flags & FLAG_CROUCH) + if (current.flags & FLAG_CROUCH) { cost *= 1.5f; - - return static_cast (waypoints.GetPathDistance (parentIndex, currentIndex)) + cost; -} - - -float gfunctionKillsDistCT (int currentIndex, int parentIndex) -{ - // least kills and number of nodes to goal for a team - - if (parentIndex == -1) - return 0.0f; - - float cost = static_cast ((g_experienceData + (currentIndex * g_numWaypoints) + currentIndex)->team1Damage + g_highestDamageCT); - - Path *current = waypoints.GetPath (currentIndex); - - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - int neighbour = current->index[i]; - - if (neighbour != -1) - cost += static_cast ((g_experienceData + (neighbour * g_numWaypoints) + neighbour)->team1Damage); } - - if (current->flags & FLAG_CROUCH) - cost *= 1.5f; - - return static_cast (waypoints.GetPathDistance (parentIndex, currentIndex)) + cost; -} - -float gfunctionKillsDistCTWithHostage (int currentIndex, int parentIndex) -{ - // least kills and number of nodes to goal for a team - - Path *current = waypoints.GetPath (currentIndex); - - if (current->flags & FLAG_NOHOSTAGE) - return 65355.0f; - - else if (current->flags & (FLAG_CROUCH | FLAG_LADDER)) - return gfunctionKillsDistCT (currentIndex, parentIndex) * 500.0f; - - return gfunctionKillsDistCT (currentIndex, parentIndex); -} - -float gfunctionKillsT (int currentIndex, int) -{ - // least kills to goal for a team - - float cost = (g_experienceData + (currentIndex * g_numWaypoints) + currentIndex)->team0Damage; - - Path *current = waypoints.GetPath (currentIndex); - - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - int neighbour = current->index[i]; - - if (neighbour != -1) - cost += (g_experienceData + (neighbour * g_numWaypoints) + neighbour)->team0Damage; - } - - if (current->flags & FLAG_CROUCH) - cost *= 1.5f; - return cost; } -float gfunctionKillsCT (int currentIndex, int parentIndex) -{ - // least kills to goal for a team +float gfunctionKillsDistCT (int currentIndex, int parentIndex) { + // least kills and number of nodes to goal for a team - if (parentIndex == -1) + if (parentIndex == INVALID_WAYPOINT_INDEX) { return 0.0f; + } + float cost = static_cast ((g_experienceData + (currentIndex * waypoints.length ()) + currentIndex)->team1Damage + g_highestDamageCT); - float cost = (g_experienceData + (currentIndex * g_numWaypoints) + currentIndex)->team1Damage; + Path ¤t = waypoints[currentIndex]; - Path *current = waypoints.GetPath (currentIndex); + for (int i = 0; i < MAX_PATH_INDEX; i++) { + int neighbour = current.index[i]; - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - int neighbour = current->index[i]; - - if (neighbour != -1) - cost += (g_experienceData + (neighbour * g_numWaypoints) + neighbour)->team1Damage; + if (neighbour != INVALID_WAYPOINT_INDEX) { + cost += static_cast ((g_experienceData + (neighbour * waypoints.length ()) + neighbour)->team1Damage); + } } - if (current->flags & FLAG_CROUCH) + if (current.flags & FLAG_CROUCH) { cost *= 1.5f; + } + return cost; +} + +float gfunctionKillsDistCTWithHostage (int currentIndex, int parentIndex) { + // least kills and number of nodes to goal for a team + + Path ¤t = waypoints[currentIndex]; + + if (current.flags & FLAG_NOHOSTAGE) { + return 65355.0f; + } + else if (current.flags & (FLAG_CROUCH | FLAG_LADDER)) { + return gfunctionKillsDistCT (currentIndex, parentIndex) * 500.0f; + } + return gfunctionKillsDistCT (currentIndex, parentIndex); +} + +float gfunctionKillsT (int currentIndex, int) { + // least kills to goal for a team + + float cost = (g_experienceData + (currentIndex * waypoints.length ()) + currentIndex)->team0Damage; + + Path ¤t = waypoints[currentIndex]; + + for (int i = 0; i < MAX_PATH_INDEX; i++) { + int neighbour = current.index[i]; + + if (neighbour != INVALID_WAYPOINT_INDEX) { + cost += (g_experienceData + (neighbour * waypoints.length ()) + neighbour)->team0Damage; + } + } + + if (current.flags & FLAG_CROUCH) { + cost *= 1.5f; + } return cost + 0.5f; } -float gfunctionKillsCTWithHostage (int currentIndex, int parentIndex) -{ +float gfunctionKillsCT (int currentIndex, int parentIndex) { // least kills to goal for a team - if (parentIndex == -1) + if (parentIndex == INVALID_WAYPOINT_INDEX) { return 0.0f; + } + float cost = (g_experienceData + (currentIndex * waypoints.length ()) + currentIndex)->team1Damage; - Path *current = waypoints.GetPath (currentIndex); + Path ¤t = waypoints[currentIndex]; - if (current->flags & FLAG_NOHOSTAGE) + for (int i = 0; i < MAX_PATH_INDEX; i++) { + int neighbour = current.index[i]; + + if (neighbour != INVALID_WAYPOINT_INDEX) { + cost += (g_experienceData + (neighbour * waypoints.length ()) + neighbour)->team1Damage; + } + } + + if (current.flags & FLAG_CROUCH) { + cost *= 1.5f; + } + return cost + 0.5f; +} + +float gfunctionKillsCTWithHostage (int currentIndex, int parentIndex) { + // least kills to goal for a team + + if (parentIndex == INVALID_WAYPOINT_INDEX) { + return 0.0f; + } + Path ¤t = waypoints[currentIndex]; + + if (current.flags & FLAG_NOHOSTAGE) { return 65355.0f; - - else if (current->flags & (FLAG_CROUCH | FLAG_LADDER)) + } + else if (current.flags & (FLAG_CROUCH | FLAG_LADDER)) { return gfunctionKillsDistCT (currentIndex, parentIndex) * 500.0f; - + } return gfunctionKillsCT (currentIndex, parentIndex); } -float gfunctionPathDist (int currentIndex, int parentIndex) -{ - if (parentIndex == -1) +float gfunctionPathDist (int currentIndex, int parentIndex) { + if (parentIndex == INVALID_WAYPOINT_INDEX) { return 0.0f; + } + Path &parent = waypoints[parentIndex]; + Path ¤t = waypoints[currentIndex]; - Path *parent = waypoints.GetPath (parentIndex); - Path *current = waypoints.GetPath (currentIndex); - - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (parent->index[i] == currentIndex) - { + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (parent.index[i] == currentIndex) { // we don't like ladder or crouch point - if (current->flags & (FLAG_CROUCH | FLAG_LADDER)) - return parent->distances[i] * 1.5f; - - return static_cast (parent->distances[i]); + if (current.flags & (FLAG_CROUCH | FLAG_LADDER)) { + return parent.distances[i] * 1.5f; + } + return static_cast (parent.distances[i]); } } return 65355.0f; } -float gfunctionPathDistWithHostage (int currentIndex, int parentIndex) -{ - Path *current = waypoints.GetPath (currentIndex); +float gfunctionPathDistWithHostage (int currentIndex, int parentIndex) { + Path ¤t = waypoints[currentIndex]; - if (current->flags & FLAG_NOHOSTAGE) + if (current.flags & FLAG_NOHOSTAGE) { return 65355.0f; - - else if (current->flags & (FLAG_CROUCH | FLAG_LADDER)) + } + else if (current.flags & (FLAG_CROUCH | FLAG_LADDER)) { return gfunctionPathDist (currentIndex, parentIndex) * 500.0f; - + } return gfunctionPathDist (currentIndex, parentIndex); } -float hfunctionSquareDist (int index, int, int goalIndex) -{ +float hfunctionPathDist (int index, int, int goalIndex) { // square distance heuristic - Path *start = waypoints.GetPath (index); - Path *goal = waypoints.GetPath (goalIndex); + Path &start = waypoints[index]; + Path &goal = waypoints[goalIndex]; - float xDist = fabsf (start->origin.x - goal->origin.x); - float yDist = fabsf (start->origin.y - goal->origin.y); - float zDist = fabsf (start->origin.z - goal->origin.z); + float x = cr::abs (start.origin.x - goal.origin.x); + float y = cr::abs (start.origin.y - goal.origin.y); + float z = cr::abs (start.origin.z - goal.origin.z); - if (xDist > yDist) - return 1.4f * yDist + (xDist - yDist) + zDist; + switch (yb_debug_heuristic_type.integer ()) { + case 0: + default: + return cr::max (cr::max (x, y), z); // chebyshev distance - return 1.4f * xDist + (yDist - xDist) + zDist; + case 1: + return x + y + z; // manhattan distance + + case 2: + return 0.0f; // no heuristic + + case 3: + case 4: + // euclidean based distance + float euclidean = cr::powf (cr::powf (x, 2.0f) + cr::powf (y, 2.0f) + cr::powf (z, 2.0f), 0.5f); + + if (yb_debug_heuristic_type.integer () == 4) { + return 1000.0f * (cr::ceilf (euclidean) - euclidean); + } + return euclidean; + } } -float hfunctionSquareDistWithHostage (int index, int startIndex, int goalIndex) -{ +float hfunctionPathDistWithHostage (int index, int startIndex, int goalIndex) { // square distance heuristic with hostages - if (waypoints.GetPath (startIndex)->flags & FLAG_NOHOSTAGE) + if (waypoints[startIndex].flags & FLAG_NOHOSTAGE) { return 65355.0f; - - return hfunctionSquareDist (index, startIndex, goalIndex); + } + return hfunctionPathDist (index, startIndex, goalIndex); } -float hfunctionNone (int index, int startIndex, int goalIndex) -{ - return hfunctionSquareDist (index, startIndex, goalIndex) / (128.0f * 10.0f); +float hfunctionNone (int index, int startIndex, int goalIndex) { + return hfunctionPathDist (index, startIndex, goalIndex) / 128.0f * 10.0f; } -float hfunctionNumberNodes (int index, int startIndex, int goalIndex) -{ - return hfunctionSquareDist (index, startIndex, goalIndex) / 128.0f * g_highestKills; -} - -void Bot::FindPath(int srcIndex, int destIndex, SearchPathType pathType /*= SEARCH_PATH_FASTEST */) -{ +void Bot::searchPath (int srcIndex, int destIndex, SearchPathType pathType /*= SEARCH_PATH_FASTEST */) { // this function finds a path from srcIndex to destIndex - if (srcIndex > g_numWaypoints - 1 || srcIndex < 0) - { - AddLogEntry (true, LL_ERROR, "Pathfinder source path index not valid (%d)", srcIndex); + if (!waypoints.exists (srcIndex)) { + logEntry (true, LL_ERROR, "Pathfinder source path index not valid (%d)", srcIndex); return; } - - if (destIndex > g_numWaypoints - 1 || destIndex < 0) - { - AddLogEntry (true, LL_ERROR, "Pathfinder destination path index not valid (%d)", destIndex); + else if (!waypoints.exists (destIndex)) { + logEntry (true, LL_ERROR, "Pathfinder destination path index not valid (%d)", destIndex); return; } - DeleteSearchNodes (); + clearSearchNodes (); m_chosenGoalIndex = srcIndex; m_goalValue = 0.0f; - // A* Stuff - enum AStarState {OPEN, CLOSED, NEW}; - - struct AStar - { - float g; - float f; - int parentIndex; - AStarState state; - } astar[MAX_WAYPOINTS]; - - PriorityQueue openList; - - for (int i = 0; i < MAX_WAYPOINTS; i++) - { - astar[i].g = 0.0f; - astar[i].f = 0.0f; - astar[i].parentIndex = -1; - astar[i].state = NEW; - } + clearRoute (); float (*gcalc) (int, int) = nullptr; float (*hcalc) (int, int, int) = nullptr; - switch (pathType) - { + switch (pathType) { + default: case SEARCH_PATH_FASTEST: - if ((g_mapType & MAP_CS) && HasHostage ()) - { + if ((g_mapFlags & MAP_CS) && hasHostage ()) { gcalc = gfunctionPathDistWithHostage; - hcalc = hfunctionSquareDistWithHostage; + hcalc = hfunctionPathDistWithHostage; } - else - { + else { gcalc = gfunctionPathDist; - hcalc = hfunctionSquareDist; + hcalc = hfunctionPathDist; } break; case SEARCH_PATH_SAFEST_FASTER: - if (m_team == TERRORIST) - { + if (m_team == TEAM_TERRORIST) { gcalc = gfunctionKillsDistT; - hcalc = hfunctionSquareDist; + hcalc = hfunctionPathDist; } - else if ((g_mapType & MAP_CS) && HasHostage ()) - { + else if ((g_mapFlags & MAP_CS) && hasHostage ()) { gcalc = gfunctionKillsDistCTWithHostage; - hcalc = hfunctionSquareDistWithHostage; + hcalc = hfunctionPathDistWithHostage; } - else - { + else { gcalc = gfunctionKillsDistCT; - hcalc = hfunctionSquareDist; + hcalc = hfunctionPathDist; } break; case SEARCH_PATH_SAFEST: - default: - if (m_team == TERRORIST) - { + if (m_team == TEAM_TERRORIST) { gcalc = gfunctionKillsT; hcalc = hfunctionNone; } - else if ((g_mapType & MAP_CS) && HasHostage ()) - { - gcalc = gfunctionKillsDistCTWithHostage; + else if ((g_mapFlags & MAP_CS) && hasHostage ()) { + gcalc = gfunctionKillsCTWithHostage; hcalc = hfunctionNone; } - else - { + else { gcalc = gfunctionKillsCT; hcalc = hfunctionNone; } break; } + auto srcRoute = &m_routes[srcIndex]; // put start node into open list - astar[srcIndex].g = gcalc (srcIndex, -1); - astar[srcIndex].f = astar[srcIndex].g + hcalc (srcIndex, srcIndex, destIndex); - astar[srcIndex].state = OPEN; + srcRoute->g = gcalc (srcIndex, INVALID_WAYPOINT_INDEX); + srcRoute->f = srcRoute->g + hcalc (srcIndex, srcIndex, destIndex); + srcRoute->state = ROUTE_OPEN; - openList.Push (srcIndex, astar[srcIndex].g); + m_routeQue.clear (); + m_routeQue.push (srcIndex, srcRoute->g); - while (!openList.IsEmpty ()) - { + while (!m_routeQue.empty ()) { // remove the first node from the open list - int currentIndex = openList.Pop (); + int currentIndex = m_routeQue.pop (); - if (currentIndex < 0 || currentIndex > g_numWaypoints) - { - AddLogEntry (false, LL_FATAL, "openList.Pop () = %d. It's not possible to continue execution. Please obtain better waypoint."); + // safes us from bad waypoints... + if (m_routeQue.length () >= MAX_ROUTE_LENGTH - 1) { + logEntry (true, LL_ERROR, "A* Search for bots \"%s\" has tried to build path with at least %d nodes. Seems to be waypoints are broken.", STRING (pev->netname), m_routeQue.length ()); + + // bail out to shortest path + searchShortestPath (srcIndex, destIndex); return; } // is the current node the goal node? - if (currentIndex == destIndex) - { + if (currentIndex == destIndex) { + // build the complete path - m_navNode = nullptr; + do { + m_path.push (currentIndex); + currentIndex = m_routes[currentIndex].parent; - do - { - PathNode *path = new PathNode; + } while (currentIndex != INVALID_WAYPOINT_INDEX); - path->index = currentIndex; - path->next = m_navNode; - - m_navNode = path; - currentIndex = astar[currentIndex].parentIndex; - - } while (currentIndex != -1); - - m_navNodeStart = m_navNode; + m_path.reverse (); return; } + auto curRoute = &m_routes[currentIndex]; - if (astar[currentIndex].state != OPEN) + if (curRoute->state != ROUTE_OPEN) { continue; + } // put current node into CLOSED list - astar[currentIndex].state = CLOSED; + curRoute->state = ROUTE_CLOSED; // now expand the current node - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - int currentChild = waypoints.GetPath (currentIndex)->index[i]; + for (int i = 0; i < MAX_PATH_INDEX; i++) { + int currentChild = waypoints[currentIndex].index[i]; - if (currentChild == -1) + if (currentChild == INVALID_WAYPOINT_INDEX) { continue; + } + auto childRoute = &m_routes[currentChild]; // calculate the F value as F = G + H - float g = astar[currentIndex].g + gcalc (currentChild, currentIndex); + float g = curRoute->g + gcalc (currentChild, currentIndex); float h = hcalc (currentChild, srcIndex, destIndex); float f = g + h; - if (astar[currentChild].state == NEW || astar[currentChild].f > f) - { + if (childRoute->state == ROUTE_NEW || childRoute->f > f) { // put the current child into open list - astar[currentChild].parentIndex = currentIndex; - astar[currentChild].state = OPEN; + childRoute->parent = currentIndex; + childRoute->state = ROUTE_OPEN; - astar[currentChild].g = g; - astar[currentChild].f = f; + childRoute->g = g; + childRoute->f = f; - openList.Push (currentChild, g); + m_routeQue.push (currentChild, g); } } } - FindShortestPath (srcIndex, destIndex); // A* found no path, try floyd pathfinder instead + searchShortestPath (srcIndex, destIndex); // A* found no path, try floyd pathfinder instead } -void Bot::DeleteSearchNodes (void) -{ - PathNode *deletingNode = nullptr; - PathNode *node = m_navNodeStart; +void Bot::clearSearchNodes (void) { + m_path.clear (); + m_chosenGoalIndex = INVALID_WAYPOINT_INDEX; +} - while (node != nullptr) - { - deletingNode = node->next; - delete node; - - node = deletingNode; +void Bot::clearRoute (void) { + int maxWaypoints = waypoints.length (); + + if (m_routes.capacity () < static_cast (waypoints.length ())) { + m_routes.reserve (maxWaypoints); } - m_navNodeStart = nullptr; - m_navNode = nullptr; - m_chosenGoalIndex = -1; + + for (int i = 0; i < maxWaypoints; i++) { + auto route = &m_routes[i]; + + route->g = route->f = 0.0f; + route->parent = INVALID_WAYPOINT_INDEX; + route->state = ROUTE_NEW; + } + m_routes.clear (); } -int Bot::GetAimingWaypoint (const Vector &to) -{ +int Bot::searchAimingPoint (const Vector &to) { // return the most distant waypoint which is seen from the bot to the target and is within count - if (m_currentWaypointIndex == -1) - m_currentWaypointIndex = ChangeWptIndex (waypoints.FindNearest (pev->origin)); + if (m_currentWaypointIndex == INVALID_WAYPOINT_INDEX) { + m_currentWaypointIndex = changePointIndex (getNearestPoint ()); + } - int srcIndex = m_currentWaypointIndex; - int destIndex = waypoints.FindNearest (to); - int bestIndex = srcIndex; + int destIndex = waypoints.getNearest (to); + int bestIndex = m_currentWaypointIndex; - while (destIndex != srcIndex) - { - destIndex = *(waypoints.m_pathMatrix + (destIndex * g_numWaypoints) + srcIndex); + if (destIndex == INVALID_WAYPOINT_INDEX) { + return INVALID_WAYPOINT_INDEX; + } + + while (destIndex != m_currentWaypointIndex) { + destIndex = *(waypoints.m_pathMatrix + (destIndex * waypoints.length ()) + m_currentWaypointIndex); - if (destIndex < 0) + if (destIndex < 0) { break; + } - if (waypoints.IsVisible (m_currentWaypointIndex, destIndex)) - { + if (waypoints.isVisible (m_currentWaypointIndex, destIndex) && waypoints.isVisible (destIndex, m_currentWaypointIndex)) { bestIndex = destIndex; break; } } + + if (bestIndex == m_currentWaypointIndex) { + return INVALID_WAYPOINT_INDEX; + } return bestIndex; } -bool Bot::FindWaypoint (void) -{ +bool Bot::searchOptimalPoint (void) { // this function find a waypoint in the near of the bot if bot had lost his path of pathfinder needs // to be restarted over again. - int waypointIndeces[3], coveredWaypoint = -1, i = 0; - float reachDistances[3]; + int busy = INVALID_WAYPOINT_INDEX; - // nullify all waypoint search stuff - for (i = 0; i < 3; i++) - { - waypointIndeces[i] = -1; - reachDistances[i] = 9999.0f; - } + float lessDist[3] = { 99999.0f, 99999.0f , 99999.0f }; + int lessIndex[3] = { INVALID_WAYPOINT_INDEX, INVALID_WAYPOINT_INDEX , INVALID_WAYPOINT_INDEX }; - // do main search loop - for (i = 0; i < g_numWaypoints; i++) - { - // ignore current waypoint and previous recent waypoints... - if (i == m_currentWaypointIndex || i == m_prevWptIndex[0] || i == m_prevWptIndex[1] || i == m_prevWptIndex[2]) - continue; + auto &bucket = waypoints.getWaypointsInBucket (pev->origin); + int numToSkip = cr::clamp (rng.getInt (0, static_cast (bucket.length () - 1)), 0, 5); - if ((g_mapType & MAP_CS) && HasHostage () && (waypoints.GetPath (i)->flags & FLAG_NOHOSTAGE)) - continue; + for (const int at : bucket) { + bool skip = !!(at == m_currentWaypointIndex); - // ignore non-reacheable waypoints... - if (!waypoints.Reachable (this, i)) - continue; - - // check if waypoint is already used by another bot... - if (IsPointOccupied (i)) - { - coveredWaypoint = i; + // skip the current waypoint, if any + if (skip) { continue; } - // now pick 1-2 random waypoints that near the bot - float distance = (waypoints.GetPath (i)->origin - pev->origin).GetLengthSquared (); - - // now fill the waypoint list - for (int j = 0; j < 3; j++) - { - if (distance < reachDistances[j]) - { - waypointIndeces[j] = i; - reachDistances[j] = distance; - } - } - } - - // now pick random one from choosen - if (waypointIndeces[2] != -1) - i = Random.Int (0, 2); - - else if (waypointIndeces[1] != -1) - i = Random.Int (0, 1); - - else if (waypointIndeces[0] != -1) - i = Random.Int (0, 0); - - else if (coveredWaypoint != -1) - { - i = 0; - waypointIndeces[i] = coveredWaypoint; - } - else - { - i = 0; - - Array found; - waypoints.FindInRadius (found, 256.0f, pev->origin); - - if (!found.IsEmpty ()) - { - bool gotId = false; - int random = found.GetRandomElement ();; - - while (!found.IsEmpty ()) - { - int index = found.Pop (); - - if (!waypoints.Reachable (this, index)) - continue; - - waypointIndeces[i] = index; - gotId = true; - + // skip current and recent previous waypoints + for (int j = 0; j < numToSkip; j++) { + if (at == m_prevWptIndex[j]) { + skip = true; break; } - - if (!gotId) - waypointIndeces[i] = random; } - else - waypointIndeces[i] = Random.Int (0, g_numWaypoints - 1); + + // skip waypoint from recent list + if (skip) { + continue; + } + + // cts with hostages should not pick waypoints with no hostage flag + if ((g_mapFlags & MAP_CS) && m_team == TEAM_COUNTER && (waypoints[at].flags & FLAG_NOHOSTAGE) && hasHostage ()) { + continue; + } + + // ignore non-reacheable waypoints... + if (!waypoints.isReachable (this, at)) { + continue; + } + + // check if waypoint is already used by another bot... + if (g_timeRoundStart + 5.0f > engine.timebase () && isOccupiedPoint (at)) { + busy = at; + continue; + } + + // if we're still here, find some close waypoints + float distance = (pev->origin - waypoints[at].origin).lengthSq (); + + if (distance < lessDist[0]) { + lessDist[2] = lessDist[1]; + lessIndex[2] = lessIndex[1]; + + lessDist[1] = lessDist[0]; + lessIndex[1] = lessIndex[0]; + + lessDist[0] = distance; + lessIndex[0] = at; + } + else if (distance < lessDist[1]) { + lessDist[2] = lessDist[1]; + lessIndex[2] = lessIndex[1]; + + lessDist[1] = distance; + lessIndex[1] = at; + } + else if (distance < lessDist[2]) { + lessDist[2] = distance; + lessIndex[2] = at; + } + } + int selected = INVALID_WAYPOINT_INDEX; + + // now pick random one from choosen + int index = 0; + + // choice from found + if (lessIndex[2] != INVALID_WAYPOINT_INDEX) { + index = rng.getInt (0, 2); + } + else if (lessIndex[1] != INVALID_WAYPOINT_INDEX) { + index = rng.getInt (0, 1); + } + else if (lessIndex[0] != INVALID_WAYPOINT_INDEX) { + index = 0; + } + selected = lessIndex[index]; + + // if we're still have no waypoint and have busy one (by other bot) pick it up + if (selected == INVALID_WAYPOINT_INDEX && busy != INVALID_WAYPOINT_INDEX) { + selected = busy; } - m_collideTime = engine.Time (); - ChangeWptIndex (waypointIndeces[i]); + // worst case... find atleast something + if (selected == INVALID_WAYPOINT_INDEX) { + selected = getNearestPoint (); + } + + ignoreCollision (); + changePointIndex (selected); return true; } -void Bot::GetValidWaypoint (void) -{ +float Bot::getReachTime (void) { + auto task = taskId (); + float estimatedTime = 0.0f; + + switch (task) { + case TASK_PAUSE: + case TASK_CAMP: + case TASK_HIDE: + return 0.0f; + + default: + estimatedTime = 2.8f; // time to reach next waypoint + } + + // calculate 'real' time that we need to get from one waypoint to another + if (waypoints.exists (m_currentWaypointIndex) && waypoints.exists (m_prevWptIndex[0])) { + float distance = (waypoints[m_prevWptIndex[0]].origin - waypoints[m_currentWaypointIndex].origin).length (); + + // caclulate estimated time + if (pev->maxspeed <= 0.0f) { + estimatedTime = 3.0f * distance / 240.0f; + } + else { + estimatedTime = 3.0f * distance / pev->maxspeed; + } + bool longTermReachability = (m_currentPath->flags & FLAG_CROUCH) || (m_currentPath->flags & FLAG_LADDER) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK); + + // check for special waypoints, that can slowdown our movement + if (longTermReachability) { + estimatedTime *= 2.0f; + } + estimatedTime = cr::clamp (estimatedTime, 1.2f, longTermReachability ? 8.0f : 5.0f); + } + return estimatedTime; +} + +void Bot::getValidPoint (void) { // checks if the last waypoint the bot was heading for is still valid - // if bot hasn't got a waypoint we need a new one anyway or if time to get there expired get new one as well - if (m_currentWaypointIndex == -1) - { - DeleteSearchNodes (); - FindWaypoint (); - - m_waypointOrigin = m_currentPath->origin; - - // FIXME: Do some error checks if we got a waypoint - } - else if (m_navTimeset + GetEstimatedReachTime () < engine.Time () && engine.IsNullEntity (m_enemy)) - { - if (m_team == TERRORIST) - { - int value = (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + m_currentWaypointIndex)->team0Damage; - value += 100; - - if (value > MAX_DAMAGE_VALUE) - value = MAX_DAMAGE_VALUE; - - (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + m_currentWaypointIndex)->team0Damage = static_cast (value); - - // affect nearby connected with victim waypoints - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if ((m_currentPath->index[i] > -1) && (m_currentPath->index[i] < g_numWaypoints)) - { - value = (g_experienceData + (m_currentPath->index[i] * g_numWaypoints) + m_currentPath->index[i])->team0Damage; - value += 2; - - if (value > MAX_DAMAGE_VALUE) - value = MAX_DAMAGE_VALUE; - - (g_experienceData + (m_currentPath->index[i] * g_numWaypoints) + m_currentPath->index[i])->team0Damage = static_cast (value); - } - } - } - else - { - int value = (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + m_currentWaypointIndex)->team1Damage; - value += 100; - - if (value > MAX_DAMAGE_VALUE) - value = MAX_DAMAGE_VALUE; - - (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + m_currentWaypointIndex)->team1Damage = static_cast (value); - - // affect nearby connected with victim waypoints - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if ((m_currentPath->index[i] > -1) && (m_currentPath->index[i] < g_numWaypoints)) - { - value = (g_experienceData + (m_currentPath->index[i] * g_numWaypoints) + m_currentPath->index[i])->team1Damage; - value += 2; - - if (value > MAX_DAMAGE_VALUE) - value = MAX_DAMAGE_VALUE; - - (g_experienceData + (m_currentPath->index[i] * g_numWaypoints) + m_currentPath->index[i])->team1Damage = static_cast (value); - } - } - } - DeleteSearchNodes (); - - if (m_goalFailed > 1) - { - int newGoal = FindGoal (); + auto rechoiceGoal = [&] (void) { + if (m_rechoiceGoalCount > 1) { + int newGoal = searchGoal (); m_prevGoalIndex = newGoal; m_chosenGoalIndex = newGoal; - - // remember index - GetTask ()->data = newGoal; + task ()->data = newGoal; // do path finding if it's not the current waypoint - if (newGoal != m_currentWaypointIndex) - FindPath (m_currentWaypointIndex, newGoal, m_pathType); - - m_goalFailed = 0; + if (newGoal != m_currentWaypointIndex) { + searchPath (m_currentWaypointIndex, newGoal, m_pathType); + } + m_rechoiceGoalCount = 0; } - else - { - FindWaypoint (); - m_goalFailed++; + else { + searchOptimalPoint (); + m_rechoiceGoalCount++; } + }; + // if bot hasn't got a waypoint we need a new one anyway or if time to get there expired get new one as well + if (m_currentWaypointIndex == INVALID_WAYPOINT_INDEX) { + clearSearchNodes (); + rechoiceGoal (); + + m_waypointOrigin = m_currentPath->origin; + } + else if (m_navTimeset + getReachTime () < engine.timebase () && engine.isNullEntity (m_enemy)) { + if (m_team == TEAM_TERRORIST) { + int value = (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + m_currentWaypointIndex)->team0Damage; + value += 100; + + if (value > MAX_DAMAGE_VALUE) { + value = MAX_DAMAGE_VALUE; + } + (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + m_currentWaypointIndex)->team0Damage = static_cast (value); + + // affect nearby connected with victim waypoints + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (waypoints.exists (m_currentPath->index[i])) { + value = (g_experienceData + (m_currentPath->index[i] * waypoints.length ()) + m_currentPath->index[i])->team0Damage; + value += 2; + + if (value > MAX_DAMAGE_VALUE) { + value = MAX_DAMAGE_VALUE; + } + (g_experienceData + (m_currentPath->index[i] * waypoints.length ()) + m_currentPath->index[i])->team0Damage = static_cast (value); + } + } + } + else { + int value = (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + m_currentWaypointIndex)->team1Damage; + value += 100; + + if (value > MAX_DAMAGE_VALUE) { + value = MAX_DAMAGE_VALUE; + } + (g_experienceData + (m_currentWaypointIndex * waypoints.length ()) + m_currentWaypointIndex)->team1Damage = static_cast (value); + + // affect nearby connected with victim waypoints + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (waypoints.exists (m_currentPath->index[i])) { + value = (g_experienceData + (m_currentPath->index[i] * waypoints.length ()) + m_currentPath->index[i])->team1Damage; + value += 2; + + if (value > MAX_DAMAGE_VALUE) { + value = MAX_DAMAGE_VALUE; + } + (g_experienceData + (m_currentPath->index[i] * waypoints.length ()) + m_currentPath->index[i])->team1Damage = static_cast (value); + } + } + } + clearSearchNodes (); + rechoiceGoal (); + m_waypointOrigin = m_currentPath->origin; } } -int Bot::ChangeWptIndex(int waypointIndex) -{ - if (waypointIndex == -1) +int Bot::changePointIndex (int index) { + if (index == INVALID_WAYPOINT_INDEX) { return 0; - + } m_prevWptIndex[4] = m_prevWptIndex[3]; m_prevWptIndex[3] = m_prevWptIndex[2]; m_prevWptIndex[2] = m_prevWptIndex[1]; m_prevWptIndex[0] = m_currentWaypointIndex; - m_currentWaypointIndex = waypointIndex; - m_navTimeset = engine.Time (); + m_currentWaypointIndex = index; + m_navTimeset = engine.timebase (); - m_currentPath = waypoints.GetPath (m_currentWaypointIndex); + m_currentPath = &waypoints[index]; m_waypointFlags = m_currentPath->flags; return m_currentWaypointIndex; // to satisfy static-code analyzers } -int Bot::ChooseBombWaypoint (void) -{ +int Bot::getNearestPoint (void) { + // get the current nearest waypoint to bot with visibility checks + + int index = INVALID_WAYPOINT_INDEX; + float minimum = cr::square (1024.0f); + + auto &bucket = waypoints.getWaypointsInBucket (pev->origin); + + for (const auto at : bucket) { + if (at == m_currentWaypointIndex) { + continue; + } + float distance = (waypoints[at].origin - pev->origin).lengthSq (); + + if (distance < minimum) { + + // if bot doing navigation, make sure waypoint really visible and not too high + if ((m_currentWaypointIndex != INVALID_WAYPOINT_INDEX && waypoints.isVisible (m_currentWaypointIndex, at)) || waypoints.isReachable (this, at)) { + index = at; + minimum = distance; + } + } + } + + // worst case, take any waypoint... + if (index == INVALID_WAYPOINT_INDEX) { + index = waypoints.getNearestNoBuckets (pev->origin); + } + return index; +} + +int Bot::getBombPoint (void) { // this function finds the best goal (bomb) waypoint for CTs when searching for a planted bomb. - Array goals = waypoints.m_goalPoints; + auto &goals = waypoints.m_goalPoints; + auto bomb = isBombAudible (); - if (goals.IsEmpty ()) - return Random.Int (0, g_numWaypoints - 1); // reliability check + if (goals.empty () || bomb.empty ()) { + return rng.getInt (0, waypoints.length () - 1); // reliability check + } - Vector bombOrigin = CheckBombAudible (); + // take the nearest to bomb waypoints instead of goal if close enough + else if ((pev->origin - bomb).lengthSq () < cr::square (512.0f)) { + int waypoint = waypoints.getNearest (bomb, 80.0f); + m_bombSearchOverridden = true; - // if bomb returns no valid vector, return the current bot pos - if (bombOrigin.IsZero ()) - bombOrigin = pev->origin; + if (waypoint != INVALID_WAYPOINT_INDEX) { + return waypoint; + } + } int goal = 0, count = 0; float lastDistance = 999999.0f; // find nearest goal waypoint either to bomb (if "heard" or player) - FOR_EACH_AE (goals, i) - { - float distance = (waypoints.GetPath (goals[i])->origin - bombOrigin).GetLengthSquared (); + for (auto &point : goals) { + float distance = (waypoints[point].origin - bomb).lengthSq (); // check if we got more close distance - if (distance < lastDistance) - { - goal = goals[i]; + if (distance < lastDistance) { + goal = point; lastDistance = distance; } } - while (waypoints.IsGoalVisited (goal)) - { - goal = goals.GetRandomElement (); + while (waypoints.isVisited (goal)) { + goal = goals.random (); - if (count++ >= goals.GetElementNumber ()) + if (count++ >= static_cast (goals.length ())) { break; + } } return goal; } -int Bot::FindDefendWaypoint (const Vector &origin) -{ +int Bot::getDefendPoint (const Vector &origin) { // this function tries to find a good position which has a line of sight to a position, // provides enough cover point, and is far away from the defending position + if (m_currentWaypointIndex == INVALID_WAYPOINT_INDEX) { + m_currentWaypointIndex = changePointIndex (getNearestPoint ()); + } TraceResult tr; int waypointIndex[MAX_PATH_INDEX]; int minDistance[MAX_PATH_INDEX]; - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - waypointIndex[i] = -1; + for (int i = 0; i < MAX_PATH_INDEX; i++) { + waypointIndex[i] = INVALID_WAYPOINT_INDEX; minDistance[i] = 128; } - int posIndex = waypoints.FindNearest (origin); - int srcIndex = waypoints.FindNearest (pev->origin); + int posIndex = waypoints.getNearest (origin); + int srcIndex = m_currentWaypointIndex; // some of points not found, return random one - if (srcIndex == -1 || posIndex == -1) - return Random.Int (0, g_numWaypoints - 1); + if (srcIndex == INVALID_WAYPOINT_INDEX || posIndex == INVALID_WAYPOINT_INDEX) { + return rng.getInt (0, waypoints.length () - 1); + } - for (int i = 0; i < g_numWaypoints; i++) // find the best waypoint now - { + // find the best waypoint now + for (int i = 0; i < waypoints.length (); i++) { // exclude ladder & current waypoints - if ((waypoints.GetPath (i)->flags & FLAG_LADDER) || i == srcIndex || !waypoints.IsVisible (i, posIndex) || IsPointOccupied (i)) + if ((waypoints[i].flags & FLAG_LADDER) || i == srcIndex || !waypoints.isVisible (i, posIndex) || isOccupiedPoint (i)) { continue; + } // use the 'real' pathfinding distances - int distance = waypoints.GetPathDistance (srcIndex, i); + int distance = waypoints.getPathDist (srcIndex, i); // skip wayponts with distance more than 512 units - if (distance > 512) + if (distance > 512) { continue; - - engine.TestLine (waypoints.GetPath (i)->origin, waypoints.GetPath (posIndex)->origin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + } + engine.testLine (waypoints[i].origin, waypoints[posIndex].origin, TRACE_IGNORE_EVERYTHING, ent (), &tr); // check if line not hit anything - if (tr.flFraction != 1.0f) + if (tr.flFraction != 1.0f) { continue; + } - for (int j = 0; j < MAX_PATH_INDEX; j++) - { - if (distance > minDistance[j]) - { + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (distance > minDistance[j]) { waypointIndex[j] = i; minDistance[j] = distance; } @@ -2142,34 +2006,31 @@ int Bot::FindDefendWaypoint (const Vector &origin) } // use statistic if we have them - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (waypointIndex[i] != -1) - { - Experience *exp = (g_experienceData + (waypointIndex[i] * g_numWaypoints) + waypointIndex[i]); - int experience = -1; + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (waypointIndex[i] != INVALID_WAYPOINT_INDEX) { + Experience *exp = (g_experienceData + (waypointIndex[i] * waypoints.length ()) + waypointIndex[i]); + int experience = INVALID_WAYPOINT_INDEX; - if (m_team == TERRORIST) + if (m_team == TEAM_TERRORIST) { experience = exp->team0Damage; - else + } + else { experience = exp->team1Damage; + } - experience = (experience * 100) / (m_team == TERRORIST ? g_highestDamageT : g_highestDamageCT); + experience = (experience * 100) / (m_team == TEAM_TERRORIST ? g_highestDamageT : g_highestDamageCT); minDistance[i] = (experience * 100) / 8192; minDistance[i] += experience; } } - bool isOrderChanged = false; // sort results waypoints for farest distance - do - { + do { isOrderChanged = false; // completely sort the data - for (int i = 0; i < 3 && waypointIndex[i] != -1 && waypointIndex[i + 1] != -1 && minDistance[i] > minDistance[i + 1]; i++) - { + for (int i = 0; i < 3 && waypointIndex[i] != INVALID_WAYPOINT_INDEX && waypointIndex[i + 1] != INVALID_WAYPOINT_INDEX && minDistance[i] > minDistance[i + 1]; i++) { int index = waypointIndex[i]; waypointIndex[i] = waypointIndex[i + 1]; @@ -2184,104 +2045,102 @@ int Bot::FindDefendWaypoint (const Vector &origin) } } while (isOrderChanged); - + if (waypointIndex[0] == INVALID_WAYPOINT_INDEX) { + IntArray found; - if (waypointIndex[0] == -1) - { - Array found; - - for (int i = 0; i < g_numWaypoints; i++) - { - if ((waypoints.GetPath (i)->origin - origin).GetLength () <= 1248.0f && !waypoints.IsVisible (i, posIndex) && !IsPointOccupied (i)) - found.Push (i); + for (int i = 0; i < waypoints.length (); i++) { + if ((waypoints[i].origin - origin).lengthSq () <= cr::square (1248.0f) && !waypoints.isVisible (i, posIndex) && !isOccupiedPoint (i)) { + found.push (i); + } } - if (found.IsEmpty ()) - return Random.Int (0, g_numWaypoints - 1); // most worst case, since there a evil error in waypoints - - return found.GetRandomElement (); + if (found.empty ()) { + return rng.getInt (0, waypoints.length () - 1); // most worst case, since there a evil error in waypoints + } + return found.random (); } - int index = 0; - for (; index < MAX_PATH_INDEX; index++) - { - if (waypointIndex[index] == -1) + for (; index < MAX_PATH_INDEX; index++) { + if (waypointIndex[index] == INVALID_WAYPOINT_INDEX) { break; + } } - return waypointIndex[Random.Int (0, static_cast ((index - 1) * 0.5f))]; + return waypointIndex[rng.getInt (0, static_cast ((index - 1) * 0.5f))]; } -int Bot::FindCoverWaypoint (float maxDistance) -{ +int Bot::getCoverPoint (float maxDistance) { // this function tries to find a good cover waypoint if bot wants to hide - // do not move to a position near to the enemy - if (maxDistance > (m_lastEnemyOrigin - pev->origin).GetLength ()) - maxDistance = (m_lastEnemyOrigin - pev->origin).GetLength (); + const float enemyMax = (m_lastEnemyOrigin - pev->origin).length (); - if (maxDistance < 300.0f) + // do not move to a position near to the enemy + if (maxDistance > enemyMax) { + maxDistance = enemyMax; + } + + if (maxDistance < 300.0f) { maxDistance = 300.0f; + } int srcIndex = m_currentWaypointIndex; - int enemyIndex = waypoints.FindNearest (m_lastEnemyOrigin); - Array enemyIndices; + int enemyIndex = waypoints.getNearest (m_lastEnemyOrigin); + + IntArray enemies; + enemies.reserve (MAX_ENGINE_PLAYERS); int waypointIndex[MAX_PATH_INDEX]; int minDistance[MAX_PATH_INDEX]; - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - waypointIndex[i] = -1; + for (int i = 0; i < MAX_PATH_INDEX; i++) { + waypointIndex[i] = INVALID_WAYPOINT_INDEX; minDistance[i] = static_cast (maxDistance); } - if (enemyIndex == -1) - return -1; + if (enemyIndex == INVALID_WAYPOINT_INDEX) { + return INVALID_WAYPOINT_INDEX; + } // now get enemies neigbouring points - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (waypoints.GetPath (enemyIndex)->index[i] != -1) - enemyIndices.Push (waypoints.GetPath (enemyIndex)->index[i]); + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (waypoints[enemyIndex].index[i] != INVALID_WAYPOINT_INDEX) { + enemies.push (waypoints[enemyIndex].index[i]); + } } // ensure we're on valid point - ChangeWptIndex (srcIndex); + changePointIndex (srcIndex); // find the best waypoint now - for (int i = 0; i < g_numWaypoints; i++) - { + for (int i = 0; i < waypoints.length (); i++) { // exclude ladder, current waypoints and waypoints seen by the enemy - if ((waypoints.GetPath (i)->flags & FLAG_LADDER) || i == srcIndex || waypoints.IsVisible (enemyIndex, i)) + if ((waypoints[i].flags & FLAG_LADDER) || i == srcIndex || waypoints.isVisible (enemyIndex, i)) { continue; + } + bool neighbourVisible = false; // now check neighbour waypoints for visibility - bool neighbourVisible = false; // now check neighbour waypoints for visibility - - FOR_EACH_AE (enemyIndices, j) - { - if (waypoints.IsVisible (enemyIndices[j], i)) - { + for (auto &enemy : enemies) { + if (waypoints.isVisible (enemy, i)) { neighbourVisible = true; break; } } // skip visible points - if (neighbourVisible) + if (neighbourVisible) { continue; + } // use the 'real' pathfinding distances - int distances = waypoints.GetPathDistance (srcIndex, i); - int enemyDistance = waypoints.GetPathDistance (enemyIndex, i); + int distances = waypoints.getPathDist (srcIndex, i); + int enemyDistance = waypoints.getPathDist (enemyIndex, i); - if (distances >= enemyDistance) + if (distances >= enemyDistance) { continue; + } - for (int j = 0; j < MAX_PATH_INDEX; j++) - { - if (distances < minDistance[j]) - { + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (distances < minDistance[j]) { waypointIndex[j] = i; minDistance[j] = distances; @@ -2291,37 +2150,35 @@ int Bot::FindCoverWaypoint (float maxDistance) } // use statistic if we have them - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (waypointIndex[i] != -1) - { - Experience *exp = (g_experienceData + (waypointIndex[i] * g_numWaypoints) + waypointIndex[i]); - int experience = -1; + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (waypointIndex[i] != INVALID_WAYPOINT_INDEX) { + Experience *exp = (g_experienceData + (waypointIndex[i] * waypoints.length ()) + waypointIndex[i]); + int experience = INVALID_WAYPOINT_INDEX; - if (m_team == TERRORIST) + if (m_team == TEAM_TERRORIST) { experience = exp->team0Damage; - else + } + else { experience = exp->team1Damage; - + } experience = (experience * 100) / MAX_DAMAGE_VALUE; + minDistance[i] = (experience * 100) / 8192; minDistance[i] += experience; } } - bool isOrderChanged; // sort resulting waypoints for nearest distance - do - { + do { isOrderChanged = false; - for (int i = 0; i < 3 && waypointIndex[i] != -1 && waypointIndex[i + 1] != -1 && minDistance[i] > minDistance[i + 1]; i++) - { + for (int i = 0; i < 3 && waypointIndex[i] != INVALID_WAYPOINT_INDEX && waypointIndex[i + 1] != INVALID_WAYPOINT_INDEX && minDistance[i] > minDistance[i + 1]; i++) { int index = waypointIndex[i]; waypointIndex[i] = waypointIndex[i + 1]; waypointIndex[i + 1] = index; + index = minDistance[i]; minDistance[i] = minDistance[i + 1]; minDistance[i + 1] = index; @@ -2333,47 +2190,46 @@ int Bot::FindCoverWaypoint (float maxDistance) TraceResult tr; // take the first one which isn't spotted by the enemy - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (waypointIndex[i] != -1) - { - engine.TestLine (m_lastEnemyOrigin + Vector (0.0f, 0.0f, 36.0f), waypoints.GetPath (waypointIndex[i])->origin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (waypointIndex[i] != INVALID_WAYPOINT_INDEX) { + engine.testLine (m_lastEnemyOrigin + Vector (0.0f, 0.0f, 36.0f), waypoints[waypointIndex[i]].origin, TRACE_IGNORE_EVERYTHING, ent (), &tr); - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return waypointIndex[i]; + } } } // if all are seen by the enemy, take the first one - if (waypointIndex[0] != -1) + if (waypointIndex[0] != INVALID_WAYPOINT_INDEX) { return waypointIndex[0]; - - return -1; // do not use random points + } + return INVALID_WAYPOINT_INDEX; // do not use random points } -bool Bot::GetBestNextWaypoint (void) -{ +bool Bot::getNextBestPoint (void) { // this function does a realtime post processing of waypoints return from the // pathfinder, to vary paths and find the best waypoint on our way - InternalAssert (m_navNode != nullptr); - InternalAssert (m_navNode->next != nullptr); + assert (!m_path.empty ()); + assert (m_path.hasNext ()); - if (!IsPointOccupied (m_navNode->index)) + if (!isOccupiedPoint (m_path.first ())) { return false; + } - for (int i = 0; i < MAX_PATH_INDEX; i++) - { + for (int i = 0; i < MAX_PATH_INDEX; i++) { int id = m_currentPath->index[i]; - if (id != -1 && waypoints.IsConnected (id, m_navNode->next->index) && waypoints.IsConnected (m_currentWaypointIndex, id)) - { - if (waypoints.GetPath (id)->flags & FLAG_LADDER) // don't use ladder waypoints as alternative - continue; + if (id != INVALID_WAYPOINT_INDEX && waypoints.isConnected (id, m_path.next ()) && waypoints.isConnected (m_currentWaypointIndex, id)) { - if (!IsPointOccupied (id)) - { - m_navNode->index = id; + // don't use ladder waypoints as alternative + if (waypoints[id].flags & FLAG_LADDER) { + continue; + } + + if (!isOccupiedPoint (id)) { + m_path.first () = id; return true; } } @@ -2381,50 +2237,46 @@ bool Bot::GetBestNextWaypoint (void) return false; } -bool Bot::HeadTowardWaypoint (void) -{ +bool Bot::advanceMovement (void) { // advances in our pathfinding list and sets the appropiate destination origins for this bot - GetValidWaypoint (); // check if old waypoints is still reliable - + getValidPoint (); // check if old waypoints is still reliable + // no waypoints from pathfinding? - if (m_navNode == nullptr) + if (m_path.empty ()) { return false; - + } TraceResult tr; - - m_navNode = m_navNode->next; // advance in list + + m_path.shift (); // advance in list m_currentTravelFlags = 0; // reset travel flags (jumping etc) - + // we're not at the end of the list? - if (m_navNode != nullptr) - { + if (!m_path.empty ()) { // if in between a route, postprocess the waypoint (find better alternatives)... - if (m_navNode != m_navNodeStart && m_navNode->next != nullptr) - { - GetBestNextWaypoint (); + if (m_path.hasNext () && m_path.first () != m_path.back ()) { + getNextBestPoint (); m_minSpeed = pev->maxspeed; - TaskID taskID = GetTaskId (); + TaskID taskID = taskId (); // only if we in normal task and bomb is not planted - if (taskID == TASK_NORMAL && g_timeRoundMid + 5.0f < engine.Time () && m_timeCamping + 5.0f < engine.Time () && !g_bombPlanted && m_personality != PERSONALITY_RUSHER && !m_hasC4 && !m_isVIP && m_loosedBombWptIndex == -1 && !HasHostage ()) - { + if (taskID == TASK_NORMAL && g_timeRoundMid + 5.0f < engine.timebase () && m_timeCamping + 5.0f < engine.timebase () && !g_bombPlanted && m_personality != PERSONALITY_RUSHER && !m_hasC4 && !m_isVIP && m_loosedBombWptIndex == INVALID_WAYPOINT_INDEX && !hasHostage ()) { m_campButtons = 0; - int nextIndex = m_navNode->next->index; + const int nextIndex = m_path.next (); float kills = 0; - if (m_team == TERRORIST) - kills = (g_experienceData + (nextIndex * g_numWaypoints) + nextIndex)->team0Damage; - else - kills = (g_experienceData + (nextIndex * g_numWaypoints) + nextIndex)->team1Damage; + if (m_team == TEAM_TERRORIST) { + kills = (g_experienceData + (nextIndex * waypoints.length ()) + nextIndex)->team0Damage; + } + else { + kills = (g_experienceData + (nextIndex * waypoints.length ()) + nextIndex)->team1Damage; + } // if damage done higher than one - if (kills > 1.0f && g_timeRoundMid > engine.Time ()) - { - switch (m_personality) - { + if (kills > 1.0f && g_timeRoundMid > engine.timebase ()) { + switch (m_personality) { case PERSONALITY_NORMAL: kills *= 0.33f; break; @@ -2434,51 +2286,46 @@ bool Bot::HeadTowardWaypoint (void) break; } - if (m_baseAgressionLevel < kills && HasPrimaryWeapon ()) - { - PushTask (TASK_CAMP, TASKPRI_CAMP, -1, engine.Time () + Random.Float (m_difficulty * 0.5f, static_cast (m_difficulty)) * 5.0f, true); - PushTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, FindDefendWaypoint (waypoints.GetPath (nextIndex)->origin), engine.Time () + Random.Float (3.0f, 10.0f), true); + if (m_baseAgressionLevel < kills && hasPrimaryWeapon ()) { + startTask (TASK_CAMP, TASKPRI_CAMP, INVALID_WAYPOINT_INDEX, engine.timebase () + rng.getFloat (m_difficulty * 0.5f, static_cast (m_difficulty)) * 5.0f, true); + startTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, getDefendPoint (waypoints[nextIndex].origin), engine.timebase () + rng.getFloat (3.0f, 10.0f), true); } } - else if (g_botsCanPause && !IsOnLadder () && !IsInWater () && !m_currentTravelFlags && IsOnFloor ()) - { - if (static_cast (kills) == m_baseAgressionLevel) + else if (g_botsCanPause && !isOnLadder () && !isInWater () && !m_currentTravelFlags && isOnFloor ()) { + if (static_cast (kills) == m_baseAgressionLevel) { m_campButtons |= IN_DUCK; - else if (Random.Int (1, 100) > m_difficulty * 25) - m_minSpeed = GetWalkSpeed (); + } + else if (rng.getInt (1, 100) > m_difficulty * 25) { + m_minSpeed = getShiftSpeed (); + } } // force terrorist bot to plant bomb - if (taskID == TASK_NORMAL && m_inBombZone && !m_hasProgressBar && m_hasC4) - { - int newGoal = FindGoal (); + if (m_inBombZone && !m_hasProgressBar && m_hasC4) { + int newGoal = searchGoal (); m_prevGoalIndex = newGoal; m_chosenGoalIndex = newGoal; // remember index - GetTask ()->data = newGoal; + task ()->data = newGoal; // do path finding if it's not the current waypoint - if (newGoal != m_currentWaypointIndex) - FindPath (m_currentWaypointIndex, newGoal, m_pathType); - + if (newGoal != m_currentWaypointIndex) { + searchPath (m_currentWaypointIndex, newGoal, m_pathType); + } return false; } } } - if (m_navNode != nullptr) - { - int destIndex = m_navNode->index; - + if (!m_path.empty ()) { + const int destIndex = m_path.first (); + // find out about connection flags - if (m_currentWaypointIndex != -1) - { - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (m_currentPath->index[i] == m_navNode->index) - { + if (m_currentWaypointIndex != INVALID_WAYPOINT_INDEX) { + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (m_currentPath->index[i] == destIndex) { m_currentTravelFlags = m_currentPath->connectionFlags[i]; m_desiredVelocity = m_currentPath->connectionVelocity[i]; m_jumpFinished = false; @@ -2493,21 +2340,20 @@ bool Bot::HeadTowardWaypoint (void) Vector src; Vector dst; - + // try to find out about future connection flags - if (m_navNode->next != nullptr) - { - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - Path *path = waypoints.GetPath (m_navNode->index); - Path *next = waypoints.GetPath (m_navNode->next->index); + if (m_path.hasNext ()) { + for (int i = 0; i < MAX_PATH_INDEX; i++) { + const int nextIndex = m_path.next (); - if (path->index[i] == m_navNode->next->index && (path->connectionFlags[i] & PATHFLAG_JUMP)) - { - src = path->origin; - dst = next->origin; + Path &path = waypoints[destIndex]; + Path &next = waypoints[nextIndex]; - jumpDistance = (path->origin - next->origin).GetLength (); + if (path.index[i] == nextIndex && (path.connectionFlags[i] & PATHFLAG_JUMP)) { + src = path.origin; + dst = next.origin; + + jumpDistance = (path.origin - next.origin).length (); willJump = true; break; @@ -2516,236 +2362,241 @@ bool Bot::HeadTowardWaypoint (void) } // is there a jump waypoint right ahead and do we need to draw out the light weapon ? - if (willJump && m_currentWeapon != WEAPON_KNIFE && m_currentWeapon != WEAPON_SCOUT && !m_isReloading && !UsesPistol () && (jumpDistance > 210.0f || (dst.z + 32.0f > src.z && jumpDistance > 150.0f) || ((dst - src).GetLength2D () < 60.0f && jumpDistance > 60.0f)) && engine.IsNullEntity (m_enemy)) - SelectWeaponByName ("weapon_knife"); // draw out the knife if we needed + if (willJump && m_currentWeapon != WEAPON_KNIFE && m_currentWeapon != WEAPON_SCOUT && !m_isReloading && !usesPistol () && (jumpDistance > 210.0f || (dst.z - 32.0f > src.z && jumpDistance > 150.0f)) && !(m_states & (STATE_SEEING_ENEMY | STATE_SUSPECT_ENEMY))) { + selectWeaponByName ("weapon_knife"); // draw out the knife if we needed + } // bot not already on ladder but will be soon? - if ((waypoints.GetPath (destIndex)->flags & FLAG_LADDER) && !IsOnLadder ()) - { + if ((waypoints[destIndex].flags & FLAG_LADDER) && !isOnLadder ()) { // get ladder waypoints used by other (first moving) bots - for (int c = 0; c < engine.MaxClients (); c++) - { - Bot *otherBot = bots.GetBot (c); + for (int c = 0; c < engine.maxClients (); c++) { + Bot *otherBot = bots.getBot (c); // if another bot uses this ladder, wait 3 secs - if (otherBot != nullptr && otherBot != this && IsAlive (otherBot->GetEntity ()) && otherBot->m_currentWaypointIndex == m_navNode->index) - { - PushTask (TASK_PAUSE, TASKPRI_PAUSE, -1, engine.Time () + 3.0f, false); + if (otherBot != nullptr && otherBot != this && otherBot->m_notKilled && otherBot->m_currentWaypointIndex == destIndex) { + startTask (TASK_PAUSE, TASKPRI_PAUSE, INVALID_WAYPOINT_INDEX, engine.timebase () + 3.0f, false); return true; } } } } - ChangeWptIndex (destIndex); + changePointIndex (destIndex); } } m_waypointOrigin = m_currentPath->origin; // if wayzone radius non zero vary origin a bit depending on the body angles - if (m_currentPath->radius > 0.0f) - { - MakeVectors (Vector (pev->angles.x, AngleNormalize (pev->angles.y + Random.Float (-90.0f, 90.0f)), 0.0f)); - m_waypointOrigin = m_waypointOrigin + g_pGlobals->v_forward * Random.Float (0.0f, m_currentPath->radius); + if (m_currentPath->radius > 0.0f) { + makeVectors (Vector (pev->angles.x, cr::angleNorm (pev->angles.y + rng.getFloat (-90.0f, 90.0f)), 0.0f)); + m_waypointOrigin = m_waypointOrigin + g_pGlobals->v_forward * rng.getFloat (0.0f, m_currentPath->radius); } - if (IsOnLadder ()) - { - engine.TestLine (Vector (pev->origin.x, pev->origin.y, pev->absmin.z), m_waypointOrigin, TRACE_IGNORE_EVERYTHING, GetEntity (), &tr); + if (isOnLadder ()) { + engine.testLine (Vector (pev->origin.x, pev->origin.y, pev->absmin.z), m_waypointOrigin, TRACE_IGNORE_EVERYTHING, ent (), &tr); - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { m_waypointOrigin = m_waypointOrigin + (pev->origin - m_waypointOrigin) * 0.5f + Vector (0.0f, 0.0f, 32.0f); + } } - m_navTimeset = engine.Time (); + m_navTimeset = engine.timebase (); return true; } -bool Bot::CantMoveForward (const Vector &normal, TraceResult *tr) -{ +bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) { // checks if bot is blocked in his movement direction (excluding doors) // use some TraceLines to determine if anything is blocking the current path of the bot. // first do a trace from the bot's eyes forward... - Vector src = EyePosition (); + Vector src = eyePos (); Vector forward = src + normal * 24.0f; - MakeVectors (Vector (0.0f, pev->angles.y, 0.0f)); + makeVectors (Vector (0.0f, pev->angles.y, 0.0f)); + + auto checkDoor = [] (TraceResult *tr) { + if (!(g_mapFlags & MAP_HAS_DOORS)) { + return false; + } + return tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0; + }; // trace from the bot's eyes straight forward... - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f) - { - if (strncmp ("func_door", STRING (tr->pHit->v.classname), 9) == 0) + if (tr->flFraction < 1.0f) { + if ((g_mapFlags & MAP_HAS_DOORS) && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) == 0) { return false; - - return true; // bot's head will hit something + } + return true; // bot's head will hit something } // bot's head is clear, check at shoulder level... // trace from the bot's shoulder left diagonal forward to the right shoulder... - src = EyePosition () + Vector (0.0f, 0.0f, -16.0f) - g_pGlobals->v_right * -16.0f; - forward = EyePosition () + Vector (0.0f, 0.0f, -16.0f) + g_pGlobals->v_right * 16.0f + normal * 24.0f; + src = eyePos () + Vector (0.0f, 0.0f, -16.0f) - g_pGlobals->v_right * -16.0f; + forward = eyePos () + Vector (0.0f, 0.0f, -16.0f) + g_pGlobals->v_right * 16.0f + normal * 24.0f; - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0) - return true; // bot's body will hit something + if (checkDoor (tr)) { + return true; // bot's body will hit something + } // bot's head is clear, check at shoulder level... // trace from the bot's shoulder right diagonal forward to the left shoulder... - src = EyePosition () + Vector (0.0f, 0.0f, -16.0f) + g_pGlobals->v_right * 16.0f; - forward = EyePosition () + Vector (0.0f, 0.0f, -16.0f) - g_pGlobals->v_right * -16.0f + normal * 24.0f; + src = eyePos () + Vector (0.0f, 0.0f, -16.0f) + g_pGlobals->v_right * 16.0f; + forward = eyePos () + Vector (0.0f, 0.0f, -16.0f) - g_pGlobals->v_right * -16.0f + normal * 24.0f; - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0) - return true; // bot's body will hit something + if (checkDoor (tr)) { + return true; // bot's body will hit something + } // now check below waist - if (pev->flags & FL_DUCKING) - { + if (pev->flags & FL_DUCKING) { src = pev->origin + Vector (0.0f, 0.0f, -19.0f + 19.0f); forward = src + Vector (0.0f, 0.0f, 10.0f) + normal * 24.0f; - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0) - return true; // bot's body will hit something - + if (checkDoor (tr)) { + return true; // bot's body will hit something + } src = pev->origin; forward = src + normal * 24.0f; - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0) - return true; // bot's body will hit something + if (checkDoor (tr)) { + return true; // bot's body will hit something + } } - else - { + else { // trace from the left waist to the right forward waist pos src = pev->origin + Vector (0.0f, 0.0f, -17.0f) - g_pGlobals->v_right * -16.0f; forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) + g_pGlobals->v_right * 16.0f + normal * 24.0f; // trace from the bot's waist straight forward... - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0) - return true; // bot's body will hit something + if (checkDoor (tr)) { + return true; // bot's body will hit something + } // trace from the left waist to the right forward waist pos src = pev->origin + Vector (0.0f, 0.0f, -17.0f) + g_pGlobals->v_right * 16.0f; forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) - g_pGlobals->v_right * -16.0f + normal * 24.0f; - engine.TestLine (src, forward, TRACE_IGNORE_MONSTERS, GetEntity (), tr); + engine.testLine (src, forward, TRACE_IGNORE_MONSTERS, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f && strncmp ("func_door", STRING (tr->pHit->v.classname), 9) != 0) - return true; // bot's body will hit something + if (checkDoor (tr)) { + return true; // bot's body will hit something + } } - return false; // bot can move forward, return false + return false; // bot can move forward, return false } #ifdef DEAD_CODE -bool Bot::CanStrafeLeft (TraceResult *tr) -{ +bool Bot::canStrafeLeft (TraceResult *tr) { // this function checks if bot can move sideways - MakeVectors (pev->v_angle); + makeVectors (pev->v_angle); Vector src = pev->origin; Vector left = src - g_pGlobals->v_right * -40.0f; // trace from the bot's waist straight left... - TraceLine (src, left, true, GetEntity (), tr); + TraceLine (src, left, true, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f) - return false; // bot's body will hit something + if (tr->flFraction < 1.0f) { + return false; // bot's body will hit something + } src = left; left = left + g_pGlobals->v_forward * 40.0f; // trace from the strafe pos straight forward... - TraceLine (src, left, true, GetEntity (), tr); + TraceLine (src, left, true, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f) - return false; // bot's body will hit something - + if (tr->flFraction < 1.0f) { + return false; // bot's body will hit something + } return true; } -bool Bot::CanStrafeRight (TraceResult * tr) -{ +bool Bot::canStrafeRight (TraceResult *tr) { // this function checks if bot can move sideways - MakeVectors (pev->v_angle); + makeVectors (pev->v_angle); Vector src = pev->origin; Vector right = src + g_pGlobals->v_right * 40.0f; // trace from the bot's waist straight right... - TraceLine (src, right, true, GetEntity (), tr); + TraceLine (src, right, true, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f) - return false; // bot's body will hit something - + if (tr->flFraction < 1.0f) { + return false; // bot's body will hit something + } src = right; right = right + g_pGlobals->v_forward * 40.0f; // trace from the strafe pos straight forward... - TraceLine (src, right, true, GetEntity (), tr); + TraceLine (src, right, true, ent (), tr); // check if the trace hit something... - if (tr->flFraction < 1.0f) - return false; // bot's body will hit something - + if (tr->flFraction < 1.0f) { + return false; // bot's body will hit something + } return true; } #endif -bool Bot::CanJumpUp (const Vector &normal) -{ +bool Bot::canJumpUp (const Vector &normal) { // this function check if bot can jump over some obstacle TraceResult tr; // can't jump if not on ground and not on ladder/swimming - if (!IsOnFloor () && (IsOnLadder () || !IsInWater ())) + if (!isOnFloor () && (isOnLadder () || !isInWater ())) { return false; + } // convert current view angle to vectors for traceline math... - MakeVectors (Vector (0.0f, pev->angles.y, 0.0f)); + makeVectors (Vector (0.0f, pev->angles.y, 0.0f)); // check for normal jump height first... Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f); Vector dest = src + normal * 32.0f; // trace a line forward at maximum jump height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); - if (tr.flFraction < 1.0f) - return FinishCanJumpUp (normal); - else - { + if (tr.flFraction < 1.0f) { + return doneCanJumpUp (normal); + } + else { // now trace from jump height upward to check for obstructions... src = dest; dest.z = dest.z + 37.0f; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } } // now check same height to one side of the bot... @@ -2753,45 +2604,47 @@ bool Bot::CanJumpUp (const Vector &normal) dest = src + normal * 32.0f; // trace a line forward at maximum jump height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) - return FinishCanJumpUp (normal); + if (tr.flFraction < 1.0f) { + return doneCanJumpUp (normal); + } // now trace from jump height upward to check for obstructions... src = dest; dest.z = dest.z + 37.0f; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } // now check same height on the other side of the bot... src = pev->origin + (-g_pGlobals->v_right * 16.0f) + Vector (0.0f, 0.0f, -36.0f + 45.0f); dest = src + normal * 32.0f; // trace a line forward at maximum jump height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) - return FinishCanJumpUp (normal); + if (tr.flFraction < 1.0f) { + return doneCanJumpUp (normal); + } // now trace from jump height upward to check for obstructions... src = dest; dest.z = dest.z + 37.0f; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false return tr.flFraction > 1.0f; } -bool Bot::FinishCanJumpUp (const Vector &normal) -{ +bool Bot::doneCanJumpUp (const Vector &normal) { // use center of the body first... maximum duck jump height is 62, so check one unit above that (63) Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 63.0f); Vector dest = src + normal * 32.0f; @@ -2799,21 +2652,22 @@ bool Bot::FinishCanJumpUp (const Vector &normal) TraceResult tr; // trace a line forward at maximum jump height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; - else - { + } + else { // now trace from jump height upward to check for obstructions... src = dest; dest.z = dest.z + 37.0f; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, check duckjump - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } } // now check same height to one side of the bot... @@ -2821,86 +2675,92 @@ bool Bot::FinishCanJumpUp (const Vector &normal) dest = src + normal * 32.0f; // trace a line forward at maximum jump height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } // now trace from jump height upward to check for obstructions... src = dest; dest.z = dest.z + 37.0f; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } // now check same height on the other side of the bot... src = pev->origin + (-g_pGlobals->v_right * 16.0f) + Vector (0.0f, 0.0f, -36.0f + 63.0f); dest = src + normal * 32.0f; // trace a line forward at maximum jump height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } // now trace from jump height upward to check for obstructions... src = dest; dest.z = dest.z + 37.0f; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false return tr.flFraction > 1.0f; } -bool Bot::CanDuckUnder (const Vector &normal) -{ +bool Bot::canDuckUnder (const Vector &normal) { // this function check if bot can duck under obstacle TraceResult tr; Vector baseHeight; // convert current view angle to vectors for TraceLine math... - MakeVectors (Vector (0.0f, pev->angles.y, 0.0f)); + makeVectors (Vector (0.0f, pev->angles.y, 0.0f)); // use center of the body first... - if (pev->flags & FL_DUCKING) + if (pev->flags & FL_DUCKING) { baseHeight = pev->origin + Vector (0.0f, 0.0f, -17.0f); - else + } + else { baseHeight = pev->origin; + } Vector src = baseHeight; Vector dest = src + normal * 32.0f; // trace a line forward at duck height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } // now check same height to one side of the bot... src = baseHeight + g_pGlobals->v_right * 16.0f; dest = src + normal * 32.0f; // trace a line forward at duck height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return false; + } // now check same height on the other side of the bot... src = baseHeight + (-g_pGlobals->v_right * 16.0f); dest = src + normal * 32.0f; // trace a line forward at duck height... - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (src, dest, TRACE_IGNORE_MONSTERS, ent (), &tr); // if trace hit something, return false return tr.flFraction > 1.0f; @@ -2908,129 +2768,122 @@ bool Bot::CanDuckUnder (const Vector &normal) #ifdef DEAD_CODE -bool Bot::IsBlockedLeft (void) -{ +bool Bot::isBlockedLeft (void) { TraceResult tr; int direction = 48; - if (m_moveSpeed < 0.0f) + if (m_moveSpeed < 0.0f) { direction = -48; - - MakeVectors (pev->angles); + } + makeVectors (pev->angles); // do a trace to the left... - engine.TestLine (pev->origin, g_pGlobals->v_forward * direction - g_pGlobals->v_right * 48.0f, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.TestLine (pev->origin, g_pGlobals->v_forward * direction - g_pGlobals->v_right * 48.0f, TRACE_IGNORE_MONSTERS, ent (), &tr); // check if the trace hit something... - if (tr.flFraction < 1.0f && strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0) - return true; // bot's body will hit something - + if ((g_mapFlags & MAP_HAS_DOORS) && tr.flFraction < 1.0f && strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0) { + return true; // bot's body will hit something + } return false; } -bool Bot::IsBlockedRight (void) -{ +bool Bot::isBlockedRight (void) { TraceResult tr; int direction = 48; - if (m_moveSpeed < 0) + if (m_moveSpeed < 0) { direction = -48; - - MakeVectors (pev->angles); + } + makeVectors (pev->angles); // do a trace to the right... - engine.TestLine (pev->origin, pev->origin + g_pGlobals->v_forward * direction + g_pGlobals->v_right * 48.0f, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.TestLine (pev->origin, pev->origin + g_pGlobals->v_forward * direction + g_pGlobals->v_right * 48.0f, TRACE_IGNORE_MONSTERS, ent (), &tr); // check if the trace hit something... - if (tr.flFraction < 1.0f && (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0)) + if ((g_mapFlags & MAP_HAS_DOORS) && tr.flFraction < 1.0f && (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) != 0)) { return true; // bot's body will hit something - + } return false; } #endif -bool Bot::CheckWallOnLeft (void) -{ +bool Bot::checkWallOnLeft (void) { TraceResult tr; - MakeVectors (pev->angles); + makeVectors (pev->angles); - engine.TestLine (pev->origin, pev->origin - g_pGlobals->v_right * 40.0f, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (pev->origin, pev->origin - g_pGlobals->v_right * 40.0f, TRACE_IGNORE_MONSTERS, ent (), &tr); // check if the trace hit something... - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return true; - + } return false; } -bool Bot::CheckWallOnRight (void) -{ +bool Bot::checkWallOnRight (void) { TraceResult tr; - MakeVectors (pev->angles); + makeVectors (pev->angles); // do a trace to the right... - engine.TestLine (pev->origin, pev->origin + g_pGlobals->v_right * 40.0f, TRACE_IGNORE_MONSTERS, GetEntity (), &tr); + engine.testLine (pev->origin, pev->origin + g_pGlobals->v_right * 40.0f, TRACE_IGNORE_MONSTERS, ent (), &tr); // check if the trace hit something... - if (tr.flFraction < 1.0f) + if (tr.flFraction < 1.0f) { return true; - + } return false; } -bool Bot::IsDeadlyDrop (const Vector &to) -{ +bool Bot::isDeadlyMove (const Vector &to) { // this function eturns if given location would hurt Bot with falling damage Vector botPos = pev->origin; TraceResult tr; - Vector move ((to - botPos).ToYaw (), 0.0f, 0.0f); - MakeVectors (move); + Vector move ((to - botPos).toYaw (), 0.0f, 0.0f); + makeVectors (move); - Vector direction = (to - botPos).Normalize (); // 1 unit long + Vector direction = (to - botPos).normalize (); // 1 unit long Vector check = botPos; Vector down = botPos; - down.z = down.z - 1000.0f; // straight down 1000 units + down.z = down.z - 1000.0f; // straight down 1000 units - engine.TestHull (check, down, TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); + engine.testHull (check, down, TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); - if (tr.flFraction > 0.036f) // we're not on ground anymore? + if (tr.flFraction > 0.036f) { // we're not on ground anymore? tr.flFraction = 0.036f; + } - float lastHeight = tr.flFraction * 1000.0f; // height from ground + float lastHeight = tr.flFraction * 1000.0f; // height from ground + float distance = (to - check).length (); // distance from goal - float distance = (to - check).GetLength (); // distance from goal - - while (distance > 16.0f) - { + while (distance > 16.0f) { check = check + direction * 16.0f; // move 10 units closer to the goal... down = check; - down.z = down.z - 1000.0f; // straight down 1000 units + down.z = down.z - 1000.0f; // straight down 1000 units - engine.TestHull (check, down, TRACE_IGNORE_MONSTERS, head_hull, GetEntity (), &tr); + engine.testHull (check, down, TRACE_IGNORE_MONSTERS, head_hull, ent (), &tr); - if (tr.fStartSolid) // Wall blocking? + if (tr.fStartSolid) { // Wall blocking? return false; + } + float height = tr.flFraction * 1000.0f; // height from ground - float height = tr.flFraction * 1000.0f; // height from ground - - if (lastHeight < height - 100.0f) // Drops more than 100 Units? + if (lastHeight < height - 100.0f) {// Drops more than 100 Units? return true; - + } lastHeight = height; - distance = (to - check).GetLength (); // distance from goal + distance = (to - check).length (); // distance from goal } return false; } #ifdef DEAD_CODE -void Bot::ChangePitch (float speed) -{ +void Bot::changePitch (float speed) { // this function turns a bot towards its ideal_pitch float idealPitch = AngleNormalize (pev->idealpitch); @@ -3042,30 +2895,30 @@ void Bot::ChangePitch (float speed) // find the difference in the curent and ideal angle float normalizePitch = AngleNormalize (idealPitch - curent); - if (normalizePitch > 0.0f) - { - if (normalizePitch > speed) + if (normalizePitch > 0.0f) { + if (normalizePitch > speed) { normalizePitch = speed; + } } - else - { - if (normalizePitch < -speed) + else { + if (normalizePitch < -speed) { normalizePitch = -speed; + } } pev->v_angle.x = AngleNormalize (curent + normalizePitch); - if (pev->v_angle.x > 89.9f) + if (pev->v_angle.x > 89.9f) { pev->v_angle.x = 89.9f; + } - if (pev->v_angle.x < -89.9f) + if (pev->v_angle.x < -89.9f) { pev->v_angle.x = -89.9f; - + } pev->angles.x = -pev->v_angle.x / 3; } -void Bot::ChangeYaw (float speed) -{ +void Bot::changeYaw (float speed) { // this function turns a bot towards its ideal_yaw float idealPitch = AngleNormalize (pev->ideal_yaw); @@ -3077,15 +2930,15 @@ void Bot::ChangeYaw (float speed) // find the difference in the curent and ideal angle float normalizePitch = AngleNormalize (idealPitch - curent); - if (normalizePitch > 0.0f) - { - if (normalizePitch > speed) + if (normalizePitch > 0.0f) { + if (normalizePitch > speed) { normalizePitch = speed; + } } - else - { - if (normalizePitch < -speed) + else { + if (normalizePitch < -speed) { normalizePitch = -speed; + } } pev->v_angle.y = AngleNormalize (curent + normalizePitch); pev->angles.y = pev->v_angle.y; @@ -3093,44 +2946,39 @@ void Bot::ChangeYaw (float speed) #endif -int Bot::GetCampAimingWaypoint (void) -{ +int Bot::searchCampDir (void) { // find a good waypoint to look at when camping + if (m_currentWaypointIndex == INVALID_WAYPOINT_INDEX) { + m_currentWaypointIndex = changePointIndex (getNearestPoint ()); + } + int count = 0, indices[3]; float distTab[3]; uint16 visibility[3]; int currentWaypoint = m_currentWaypointIndex; - if (currentWaypoint == -1) - return Random.Int (0, g_numWaypoints - 1); - - for (int i = 0; i < g_numWaypoints; i++) - { - if (currentWaypoint == i || !waypoints.IsVisible (currentWaypoint, i)) + for (int i = 0; i < waypoints.length (); i++) { + if (currentWaypoint == i || !waypoints.isVisible (currentWaypoint, i)) { continue; + } + Path &path = waypoints[i]; - Path *path = waypoints.GetPath (i); - - if (count < 3) - { + if (count < 3) { indices[count] = i; - distTab[count] = (pev->origin - path->origin).GetLengthSquared (); - visibility[count] = path->vis.crouch + path->vis.stand; + distTab[count] = (pev->origin - path.origin).lengthSq (); + visibility[count] = path.vis.crouch + path.vis.stand; count++; } - else - { - float distance = (pev->origin - path->origin).GetLengthSquared (); - uint16 visBits = path->vis.crouch + path->vis.stand; + else { + float distance = (pev->origin - path.origin).lengthSq (); + uint16 visBits = path.vis.crouch + path.vis.stand; - for (int j = 0; j < 3; j++) - { - if (visBits >= visibility[j] && distance > distTab[j]) - { + for (int j = 0; j < 3; j++) { + if (visBits >= visibility[j] && distance > distTab[j]) { indices[j] = i; distTab[j] = distance; @@ -3143,105 +2991,88 @@ int Bot::GetCampAimingWaypoint (void) } count--; - if (count >= 0) - return indices[Random.Int (0, count)]; - - return Random.Int (0, g_numWaypoints - 1); + if (count >= 0) { + return indices[rng.getInt (0, count)]; + } + return rng.getInt (0, waypoints.length () - 1); } -void Bot::UpdateBodyAngles (void) -{ +void Bot::processBodyAngles (void) { // set the body angles to point the gun correctly pev->angles.x = -pev->v_angle.x * (1.0f / 3.0f); pev->angles.y = pev->v_angle.y; - pev->angles.ClampAngles (); + pev->angles.clampAngles (); } -void Bot::UpdateLookAngles (void) -{ - const float delta = F_clamp (engine.Time () - m_lookUpdateTime, MATH_EQEPSILON, 0.05f); - m_lookUpdateTime = engine.Time (); +void Bot::processLookAngles (void) { + const float delta = cr::clamp (engine.timebase () - m_lookUpdateTime, cr::EQEPSILON, 0.05f); + m_lookUpdateTime = engine.timebase (); // adjust all body and view angles to face an absolute vector - Vector direction = (m_lookAt - EyePosition ()).ToAngles (); + Vector direction = (m_lookAt - eyePos ()).toAngles (); direction.x *= -1.0f; // invert for engine - direction.ClampAngles (); + direction.clampAngles (); // lower skilled bot's have lower aiming - if (m_difficulty < 3) - { - UpdateLookAnglesLowSkill (direction, delta); - UpdateBodyAngles (); + if (m_difficulty < 2) { + updateLookAnglesNewbie (direction, delta); + processBodyAngles (); return; } - // this is what makes bot almost godlike (got it from sypb) - if (m_difficulty > 3 && (m_aimFlags & AIM_ENEMY) && (m_wantsToFire || UsesSniper ()) && yb_whose_your_daddy.GetBool ()) - { + if (m_difficulty > 3 && (m_aimFlags & AIM_ENEMY) && (m_wantsToFire || usesSniper ()) && yb_whose_your_daddy.boolean ()) { pev->v_angle = direction; - UpdateBodyAngles (); + processBodyAngles (); return; } - bool needPreciseAim = (m_aimFlags & (AIM_ENEMY | AIM_ENTITY | AIM_GRENADE | AIM_LAST_ENEMY | AIM_CAMP) || GetTaskId () == TASK_SHOOTBREAKABLE); - float accelerate = needPreciseAim ? 3600.0f : 3000.0f; - float stiffness = needPreciseAim ? 300.0f : 200.0f; - float damping = needPreciseAim ? 20.0f : 25.0f; + float accelerate = 3000.0f; + float stiffness = 200.0f; + float damping = 25.0f; + if ((m_aimFlags & (AIM_ENEMY | AIM_ENTITY | AIM_GRENADE)) && m_difficulty > 2) { + accelerate += 800.0f; + stiffness += 320.0f; + damping -= 8.0f; + } m_idealAngles = pev->v_angle; - float angleDiffYaw = AngleDiff (direction.y, m_idealAngles.y); - float angleDiffPitch = AngleDiff (direction.x, m_idealAngles.x); + float angleDiffYaw = cr::angleDiff (direction.y, m_idealAngles.y); + float angleDiffPitch = cr::angleDiff (direction.x, m_idealAngles.x); - if (angleDiffYaw < 1.0f && angleDiffYaw > -1.0f) - { + if (angleDiffYaw < 1.0f && angleDiffYaw > -1.0f) { m_lookYawVel = 0.0f; m_idealAngles.y = direction.y; } - else - { - float accel = stiffness * angleDiffYaw - damping * m_lookYawVel; - - if (accel > accelerate) - accel = accelerate; - else if (accel < -accelerate) - accel = -accelerate; + else { + float accel = cr::clamp (stiffness * angleDiffYaw - damping * m_lookYawVel, -accelerate, accelerate); m_lookYawVel += delta * accel; m_idealAngles.y += delta * m_lookYawVel; } - float accel = 2.0f * stiffness * angleDiffPitch - damping * m_lookPitchVel; - - if (accel > accelerate) - accel = accelerate; - else if (accel < -accelerate) - accel = -accelerate; + float accel = cr::clamp (2.0f * stiffness * angleDiffPitch - damping * m_lookPitchVel, -accelerate, accelerate); m_lookPitchVel += delta * accel; - m_idealAngles.x += delta * m_lookPitchVel; - - if (m_idealAngles.x < -89.0f) - m_idealAngles.x = -89.0f; - else if (m_idealAngles.x > 89.0f) - m_idealAngles.x = 89.0f; + m_idealAngles.x += cr::clamp (delta * m_lookPitchVel, -89.0f, 89.0f); pev->v_angle = m_idealAngles; - pev->v_angle.ClampAngles (); + pev->v_angle.clampAngles (); - UpdateBodyAngles (); + processBodyAngles (); } -void Bot::UpdateLookAnglesLowSkill (const Vector &direction, const float delta) -{ +void Bot::updateLookAnglesNewbie (const Vector &direction, const float delta) { Vector spring (13.0f, 13.0f, 0.0f); Vector damperCoefficient (0.22f, 0.22f, 0.0f); - Vector influence = Vector (0.25f, 0.17f, 0.0f) * ((100 - (m_difficulty * 25)) / 100.f); - Vector randomization = Vector (2.0f, 0.18f, 0.0f) * ((100 - (m_difficulty * 25)) / 100.f); + const float offset = cr::clamp (m_difficulty, 1, 4) * 25.0f; + + Vector influence = Vector (0.25f, 0.17f, 0.0f) * (100.0f - offset) / 100.f; + Vector randomization = Vector (2.0f, 0.18f, 0.0f) * (100.0f - offset) / 100.f; const float noTargetRatio = 0.3f; const float offsetDelay = 1.2f; @@ -3249,57 +3080,55 @@ void Bot::UpdateLookAnglesLowSkill (const Vector &direction, const float delta) Vector stiffness; Vector randomize; - m_idealAngles = direction.Get2D (); - m_idealAngles.ClampAngles (); + m_idealAngles = direction.make2D (); + m_idealAngles.clampAngles (); - if (m_aimFlags & (AIM_ENEMY | AIM_ENTITY | AIM_GRENADE | AIM_LAST_ENEMY) || GetTaskId () == TASK_SHOOTBREAKABLE) - { - m_playerTargetTime = engine.Time (); + if (m_aimFlags & (AIM_ENEMY | AIM_ENTITY)) { + m_playerTargetTime = engine.timebase (); m_randomizedIdealAngles = m_idealAngles; - stiffness = spring * (0.2f + (m_difficulty * 25) / 125.0f); + stiffness = spring * (0.2f + offset / 125.0f); } - else - { + else { // is it time for bot to randomize the aim direction again (more often where moving) ? - if (m_randomizeAnglesTime < engine.Time () && ((pev->velocity.GetLength () > 1.0f && m_angularDeviation.GetLength () < 5.0f) || m_angularDeviation.GetLength () < 1.0f)) - { + if (m_randomizeAnglesTime < engine.timebase () && ((pev->velocity.length () > 1.0f && m_angularDeviation.length () < 5.0f) || m_angularDeviation.length () < 1.0f)) { // is the bot standing still ? - if (pev->velocity.GetLength () < 1.0f) + if (pev->velocity.length () < 1.0f) { randomize = randomization * 0.2f; // randomize less - else + } + else { randomize = randomization; - + } // randomize targeted location a bit (slightly towards the ground) - m_randomizedIdealAngles = m_idealAngles + Vector (Random.Float (-randomize.x * 0.5f, randomize.x * 1.5f), Random.Float (-randomize.y, randomize.y), 0.0f); + m_randomizedIdealAngles = m_idealAngles + Vector (rng.getFloat (-randomize.x * 0.5f, randomize.x * 1.5f), rng.getFloat (-randomize.y, randomize.y), 0.0f); // set next time to do this - m_randomizeAnglesTime = engine.Time () + Random.Float (0.4f, offsetDelay); + m_randomizeAnglesTime = engine.timebase () + rng.getFloat (0.4f, offsetDelay); } float stiffnessMultiplier = noTargetRatio; // take in account whether the bot was targeting someone in the last N seconds - if (engine.Time () - (m_playerTargetTime + offsetDelay) < noTargetRatio * 10.0f) - { - stiffnessMultiplier = 1.0f - (engine.Time () - m_timeLastFired) * 0.1f; + if (engine.timebase () - (m_playerTargetTime + offsetDelay) < noTargetRatio * 10.0f) { + stiffnessMultiplier = 1.0f - (engine.timebase () - m_timeLastFired) * 0.1f; // don't allow that stiffness multiplier less than zero - if (stiffnessMultiplier < 0.0f) + if (stiffnessMultiplier < 0.0f) { stiffnessMultiplier = 0.5f; + } } // also take in account the remaining deviation (slow down the aiming in the last 10°) - stiffnessMultiplier *= m_angularDeviation.GetLength () * 0.1f * 0.5f; + stiffnessMultiplier *= m_angularDeviation.length () * 0.1f * 0.5f; // but don't allow getting below a certain value - if (stiffnessMultiplier < 0.35f) + if (stiffnessMultiplier < 0.35f) { stiffnessMultiplier = 0.35f; - + } stiffness = spring * stiffnessMultiplier; // increasingly slow aim } // compute randomized angle deviation this time m_angularDeviation = m_randomizedIdealAngles - pev->v_angle; - m_angularDeviation.ClampAngles (); + m_angularDeviation.clampAngles (); // spring/damper model aiming m_aimSpeed.x = stiffness.x * m_angularDeviation.x - damperCoefficient.x * m_aimSpeed.x; @@ -3307,115 +3136,105 @@ void Bot::UpdateLookAnglesLowSkill (const Vector &direction, const float delta) // influence of y movement on x axis and vice versa (less influence than x on y since it's // easier and more natural for the bot to "move its mouse" horizontally than vertically) - m_aimSpeed.x += m_aimSpeed.y * influence.y; - m_aimSpeed.y += m_aimSpeed.x * influence.x; + m_aimSpeed.x += cr::clamp (m_aimSpeed.y * influence.y, -50.0f, 50.0f); + m_aimSpeed.y += cr::clamp (m_aimSpeed.x * influence.x, -200.0f, 200.0f); // move the aim cursor pev->v_angle = pev->v_angle + delta * Vector (m_aimSpeed.x, m_aimSpeed.y, 0.0f); - pev->v_angle.ClampAngles (); + pev->v_angle.clampAngles (); } -void Bot::SetStrafeSpeed (const Vector &moveDir, float strafeSpeed) -{ - MakeVectors (pev->angles); +void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) { + makeVectors (pev->angles); - const Vector &los = (moveDir - pev->origin).Normalize2D (); - float dot = los | g_pGlobals->v_forward.Get2D (); + const Vector &los = (moveDir - pev->origin).normalize2D (); + float dot = los | g_pGlobals->v_forward.make2D (); - if (dot > 0.0f && !CheckWallOnRight ()) + if (dot > 0.0f && !checkWallOnRight ()) { m_strafeSpeed = strafeSpeed; - else if (!CheckWallOnLeft ()) + } + else if (!checkWallOnLeft ()) { m_strafeSpeed = -strafeSpeed; + } } -int Bot::FindPlantedBomb (void) -{ +int Bot::locatePlantedC4 (void) { // this function tries to find planted c4 on the defuse scenario map and returns nearest to it waypoint - if (m_team != TERRORIST || !(g_mapType & MAP_DE)) - return -1; // don't search for bomb if the player is CT, or it's not defusing bomb + if (m_team != TEAM_TERRORIST || !(g_mapFlags & MAP_DE)) { + return INVALID_WAYPOINT_INDEX; // don't search for bomb if the player is CT, or it's not defusing bomb + } edict_t *bombEntity = nullptr; // temporaly pointer to bomb // search the bomb on the map - while (!engine.IsNullEntity (bombEntity = FIND_ENTITY_BY_CLASSNAME (bombEntity, "grenade"))) - { - if (strcmp (STRING (bombEntity->v.model) + 9, "c4.mdl") == 0) - { - int nearestIndex = waypoints.FindNearest (engine.GetAbsOrigin (bombEntity)); + while (!engine.isNullEntity (bombEntity = g_engfuncs.pfnFindEntityByString (bombEntity, "classname", "grenade"))) { + if (strcmp (STRING (bombEntity->v.model) + 9, "c4.mdl") == 0) { + int nearestIndex = waypoints.getNearest (engine.getAbsPos (bombEntity)); - if (nearestIndex >= 0 && nearestIndex < g_numWaypoints) + if (waypoints.exists (nearestIndex)) { return nearestIndex; - + } break; } } - return -1; + return INVALID_WAYPOINT_INDEX; } -bool Bot::IsPointOccupied (int index) -{ - if (index < 0 || index >= g_numWaypoints) +bool Bot::isOccupiedPoint (int index) { + if (!waypoints.exists (index)) { return true; + } + for (int i = 0; i < engine.maxClients (); i++) { + const Client &client = g_clients[i]; - // first check if current waypoint of one of the bots is index waypoint - for (int i = 0; i < engine.MaxClients (); i++) - { - Bot *bot = bots.GetBot (i); - - if (bot == nullptr || bot == this) + if (!(client.flags & (CF_USED | CF_ALIVE)) || client.team != m_team || client.ent == ent ()) { continue; + } + auto bot = bots.getBot (i); - // check if this waypoint is already used - // TODO: take in account real players + if (bot == this) { + continue; + } - if (bot->m_notKilled && m_currentWaypointIndex != -1 && bot->m_prevWptIndex[0] != -1) - { - int targetId = bot->GetTask ()->data; + if (bot != nullptr) { + int occupyId = getShootingConeDeviation (bot->ent (), pev->origin) >= 0.7f ? bot->m_prevWptIndex[0] : bot->m_currentWaypointIndex; - if (index == targetId) - return true; + if (bot != nullptr) { + if (index == occupyId) { + return true; + } + } + } + float length = (waypoints[index].origin - client.origin).lengthSq (); - // check bot's current waypoint - int occupyId = GetShootingConeDeviation (bot->GetEntity (), &pev->origin) >= 0.7f ? bot->m_prevWptIndex[0] : m_currentWaypointIndex; - - if (index == occupyId) - return true; - - // length check - float length = (waypoints.GetPath (occupyId)->origin - waypoints.GetPath (index)->origin).GetLengthSquared (); - - if (length < GET_SQUARE (128.0f)) - return true; + if (length < cr::clamp (waypoints[index].radius, cr::square (32.0f), cr::square (90.0f))) { + return true; } } return false; } -edict_t *Bot::FindNearestButton (const char *targetName) -{ +edict_t *Bot::getNearestButton (const char *targetName) { // this function tries to find nearest to current bot button, and returns pointer to // it's entity, also here must be specified the target, that button must open. - if (IsNullString (targetName)) + if (isEmptyStr (targetName)) { return nullptr; - + } float nearestDistance = 99999.0f; edict_t *searchEntity = nullptr, *foundEntity = nullptr; // find the nearest button which can open our target - while (!engine.IsNullEntity(searchEntity = FIND_ENTITY_BY_TARGET (searchEntity, targetName))) - { - Vector entityOrign = engine.GetAbsOrigin (searchEntity); + while (!engine.isNullEntity (searchEntity = g_engfuncs.pfnFindEntityByString (searchEntity, "target", targetName))) { + const Vector &pos = engine.getAbsPos (searchEntity); // check if this place safe - if (!IsDeadlyDrop (entityOrign)) - { - float distance = (pev->origin - entityOrign).GetLengthSquared (); + if (!isDeadlyMove (pos)) { + float distance = (pev->origin - pos).lengthSq (); // check if we got more close button - if (distance <= nearestDistance) - { + if (distance <= nearestDistance) { nearestDistance = distance; foundEntity = searchEntity; } diff --git a/source/support.cpp b/source/support.cpp index 2375c38..2d8ad60 100644 --- a/source/support.cpp +++ b/source/support.cpp @@ -4,10 +4,10 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include ConVar yb_display_menu_text ("yb_display_menu_text", "1"); ConVar yb_display_welcome_text ("yb_display_welcome_text", "1"); @@ -15,40 +15,13 @@ ConVar yb_display_welcome_text ("yb_display_welcome_text", "1"); ConVar mp_roundtime ("mp_roundtime", nullptr, VT_NOREGISTER); ConVar mp_freezetime ("mp_freezetime", nullptr, VT_NOREGISTER, true, "0"); -uint16 FixedUnsigned16 (float value, float scale) -{ - int output = (static_cast (value * scale)); - - if (output < 0) - output = 0; - - if (output > 0xffff) - output = 0xffff; - - return static_cast (output); -} - -short FixedSigned16 (float value, float scale) -{ - int output = (static_cast (value * scale)); - - if (output > 32767) - output = 32767; - - if (output < -32768) - output = -32768; - - return static_cast (output); -} - -const char *FormatBuffer (const char *format, ...) -{ +const char *format (const char *format, ...) { static char strBuffer[2][MAX_PRINT_BUFFER]; static int rotator = 0; - if (format == nullptr) + if (format == nullptr) { return strBuffer[rotator]; - + } static char *ptr = strBuffer[rotator ^= 1]; va_list ap; @@ -59,218 +32,198 @@ const char *FormatBuffer (const char *format, ...) return ptr; } -bool IsAlive (edict_t *ent) -{ - if (engine.IsNullEntity (ent)) +bool isAlive (edict_t *ent) { + if (engine.isNullEntity (ent)) { return false; - + } return ent->v.deadflag == DEAD_NO && ent->v.health > 0 && ent->v.movetype != MOVETYPE_NOCLIP; } -float GetShootingConeDeviation (edict_t *ent, Vector *position) -{ - const Vector &dir = (*position - (ent->v.origin + ent->v.view_ofs)).Normalize (); - MakeVectors (ent->v.v_angle); +float getShootingConeDeviation (edict_t *ent, const Vector &position) { + makeVectors (ent->v.v_angle); // he's facing it, he meant it - return g_pGlobals->v_forward | dir; + return g_pGlobals->v_forward | (position - (ent->v.origin + ent->v.view_ofs)).normalize (); } -bool IsInViewCone (const Vector &origin, edict_t *ent) -{ - MakeVectors (ent->v.v_angle); - - if (((origin - (ent->v.origin + ent->v.view_ofs)).Normalize () | g_pGlobals->v_forward) >= A_cosf (((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f) * MATH_D2R)) - return true; - - return false; +bool isInViewCone (const Vector &origin, edict_t *ent) { + return getShootingConeDeviation (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f)); } -bool IsVisible (const Vector &origin, edict_t *ent) -{ - if (engine.IsNullEntity (ent)) +bool isVisible (const Vector &origin, edict_t *ent) { + if (engine.isNullEntity (ent)) { return false; - + } TraceResult tr; - engine.TestLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr); + engine.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr); - if (tr.flFraction != 1.0f) + if (tr.flFraction != 1.0f) { return false; - + } return true; } -void DisplayMenuToClient (edict_t *ent, MenuId menu) -{ +void showMenu (edict_t *ent, MenuId menu) { static bool s_menusParsed = false; // make menus looks like we need only once - if (!s_menusParsed) - { - extern void SetupBotMenus (void); - SetupBotMenus (); - - for (int i = 0; i < ARRAYSIZE_HLSDK (g_menus); i++) - { + if (!s_menusParsed) { + extern void setupBotMenus (void); + setupBotMenus (); + + for (int i = 0; i < BOT_MENU_TOTAL_MENUS; i++) { auto parsed = &g_menus[i]; + const String &translated = engine.translate (parsed->text.chars ()); // translate all the things - parsed->text.Replace ("\v", "\n"); - parsed->text.Assign (engine.TraslateMessage (parsed->text.GetBuffer ())); + parsed->text = translated; // make menu looks best - if (!(g_gameFlags & GAME_LEGACY)) - { - for (int j = 0; j < 10; j++) - parsed->text.Replace (FormatBuffer ("%d.", j), FormatBuffer ("\\r%d.\\w", j)); + if (!(g_gameFlags & GAME_LEGACY)) { + for (int j = 0; j < 10; j++) { + parsed->text.replace (format ("%d.", j), format ("\\r%d.\\w", j)); + } } } s_menusParsed = true; } - if (!IsValidPlayer (ent)) + if (!isPlayer (ent)) { return; + } + Client &client = g_clients[engine.indexOfEntity (ent) - 1]; - Client &client = g_clients[engine.IndexOfEntity (ent) - 1]; + if (menu == BOT_MENU_INVALID) { + MessageWriter (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent) + .writeShort (0) + .writeChar (0) + .writeByte (0) + .writeString (""); - if (menu == BOT_MENU_INVALID) - { - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, engine.FindMessageId (NETMSG_SHOWMENU), nullptr, ent); - WRITE_SHORT (0); - WRITE_CHAR (0); - WRITE_BYTE (0); - WRITE_STRING (""); - MESSAGE_END (); - - client.menu = BOT_MENU_INVALID; + client.menu = menu; return; } int menuIndex = 0; - for (; menuIndex < ARRAYSIZE_HLSDK (g_menus); menuIndex++) - { - if (g_menus[menuIndex].id == menu) + for (; menuIndex < BOT_MENU_TOTAL_MENUS; menuIndex++) { + if (g_menus[menuIndex].id == menu) { break; + } } - const auto &menuRef = g_menus[menuIndex]; - const char *displayText = ((g_gameFlags & (GAME_XASH_ENGINE | GAME_MOBILITY)) && !yb_display_menu_text.GetBool ()) ? " " : menuRef.text.GetBuffer (); + const auto &menuText = g_menus[menuIndex]; + const char *text = ((g_gameFlags & (GAME_XASH_ENGINE | GAME_MOBILITY)) && !yb_display_menu_text.boolean ()) ? " " : menuText.text.chars (); + MessageWriter msg; - while (strlen (displayText) >= 64) - { - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, engine.FindMessageId (NETMSG_SHOWMENU), nullptr, ent); - WRITE_SHORT (menuRef.slots); - WRITE_CHAR (-1); - WRITE_BYTE (1); + while (strlen (text) >= 64) { + msg.start (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent) + .writeShort (menuText.slots) + .writeChar (-1) + .writeByte (1); - for (int i = 0; i <= 63; i++) - WRITE_CHAR (displayText[i]); - - MESSAGE_END (); - displayText += 64; + for (int i = 0; i < 64; i++) { + msg.writeChar (text[i]); + } + msg.end (); + text += 64; } - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, engine.FindMessageId (NETMSG_SHOWMENU), nullptr, ent); - WRITE_SHORT (menuRef.slots); - WRITE_CHAR (-1); - WRITE_BYTE (0); - WRITE_STRING (displayText); - MESSAGE_END(); + MessageWriter (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent) + .writeShort (menuText.slots) + .writeChar (-1) + .writeByte (0) + .writeString (text); client.menu = menu; - CLIENT_COMMAND (ent, "speak \"player/geiger1\"\n"); // Stops others from hearing menu sounds.. + g_engfuncs.pfnClientCommand (ent, "speak \"player/geiger1\"\n"); // Stops others from hearing menu sounds.. } -void DecalTrace (entvars_t *pev, TraceResult *trace, int logotypeIndex) -{ +void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) { // this function draw spraypaint depending on the tracing results. - static Array logotypes; + static StringArray logotypes; - if (logotypes.IsEmpty ()) - logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").Split (";"); - - int entityIndex = -1, message = TE_DECAL; - int decalIndex = (*g_engfuncs.pfnDecalIndex) (logotypes[logotypeIndex].GetBuffer ()); - - if (decalIndex < 0) - decalIndex = (*g_engfuncs.pfnDecalIndex) ("{lambda06"); - - if (trace->flFraction == 1.0f) - return; - - if (!engine.IsNullEntity (trace->pHit)) - { - if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) - entityIndex = engine.IndexOfEntity (trace->pHit); - else - return; + if (logotypes.empty ()) { + logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").split (";"); } - else - entityIndex = 0; + int entityIndex = -1, message = TE_DECAL; + int decalIndex = g_engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ()); - if (entityIndex != 0) - { - if (decalIndex > 255) - { + if (decalIndex < 0) { + decalIndex = g_engfuncs.pfnDecalIndex ("{lambda06"); + } + + if (trace->flFraction == 1.0f) { + return; + } + if (!engine.isNullEntity (trace->pHit)) { + if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) { + entityIndex = engine.indexOfEntity (trace->pHit); + } + else { + return; + } + } + else { + entityIndex = 0; + } + + if (entityIndex != 0) { + if (decalIndex > 255) { message = TE_DECALHIGH; decalIndex -= 256; } } - else - { + else { message = TE_WORLDDECAL; - if (decalIndex > 255) - { + if (decalIndex > 255) { message = TE_WORLDDECALHIGH; decalIndex -= 256; } } - if (logotypes[logotypeIndex].Contains ("{")) - { - MESSAGE_BEGIN (MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE (TE_PLAYERDECAL); - WRITE_BYTE (engine.IndexOfEntity (pev->pContainingEntity)); - WRITE_COORD (trace->vecEndPos.x); - WRITE_COORD (trace->vecEndPos.y); - WRITE_COORD (trace->vecEndPos.z); - WRITE_SHORT (static_cast (engine.IndexOfEntity (trace->pHit))); - WRITE_BYTE (decalIndex); - MESSAGE_END (); + if (logotypes[logotypeIndex].contains ("{")) { + MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY) + .writeByte (TE_PLAYERDECAL) + .writeByte (engine.indexOfEntity (pev->pContainingEntity)) + .writeCoord (trace->vecEndPos.x) + .writeCoord (trace->vecEndPos.y) + .writeCoord (trace->vecEndPos.z) + .writeShort (static_cast (engine.indexOfEntity (trace->pHit))) + .writeByte (decalIndex); } - else - { - MESSAGE_BEGIN (MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE (message); - WRITE_COORD (trace->vecEndPos.x); - WRITE_COORD (trace->vecEndPos.y); - WRITE_COORD (trace->vecEndPos.z); - WRITE_BYTE (decalIndex); + else { + MessageWriter msg; - if (entityIndex) - WRITE_SHORT (entityIndex); + msg.start (MSG_BROADCAST, SVC_TEMPENTITY) + .writeByte (message) + .writeCoord (trace->vecEndPos.x) + .writeCoord (trace->vecEndPos.y) + .writeCoord (trace->vecEndPos.z) + .writeByte (decalIndex); - MESSAGE_END(); + if (entityIndex) { + msg.writeShort (entityIndex); + } + msg.end (); } } -void FreeLibraryMemory (void) -{ +void cleanupGarbage (void) { // this function free's all allocated memory - waypoints.Init (); // frees waypoint data + waypoints.init (); // frees waypoint data - delete [] g_experienceData; + delete[] g_experienceData; g_experienceData = nullptr; } -void UpdateGlobalExperienceData (void) -{ +void updateGlobalExperience (void) { // this function called after each end of the round to update knowledge about most dangerous waypoints for each team. // no waypoints, no experience used or waypoints edited or being edited? - if (g_numWaypoints < 1 || waypoints.HasChanged ()) + if (waypoints.length () < 1 || waypoints.hasChanged ()) { return; // no action + } uint16 maxDamage; // maximum damage uint16 actDamage; // actual damage @@ -279,82 +232,74 @@ void UpdateGlobalExperienceData (void) bool recalcKills = false; // get the most dangerous waypoint for this position for terrorist team - for (int i = 0; i < g_numWaypoints; i++) - { + for (int i = 0; i < waypoints.length (); i++) { maxDamage = 0; - bestIndex = -1; + bestIndex = INVALID_WAYPOINT_INDEX; - for (int j = 0; j < g_numWaypoints; j++) - { - if (i == j) + for (int j = 0; j < waypoints.length (); j++) { + if (i == j) { continue; + } + actDamage = (g_experienceData + (i * waypoints.length ()) + j)->team0Damage; - actDamage = (g_experienceData + (i * g_numWaypoints) + j)->team0Damage; - - if (actDamage > maxDamage) - { + if (actDamage > maxDamage) { maxDamage = actDamage; bestIndex = j; } } - if (maxDamage > MAX_DAMAGE_VALUE) + if (maxDamage > MAX_DAMAGE_VALUE) { recalcKills = true; - - (g_experienceData + (i * g_numWaypoints) + i)->team0DangerIndex = static_cast (bestIndex); + } + (g_experienceData + (i * waypoints.length ()) + i)->team0DangerIndex = static_cast (bestIndex); } // get the most dangerous waypoint for this position for counter-terrorist team - for (int i = 0; i < g_numWaypoints; i++) - { + for (int i = 0; i < waypoints.length (); i++) { maxDamage = 0; - bestIndex = -1; + bestIndex = INVALID_WAYPOINT_INDEX; - for (int j = 0; j < g_numWaypoints; j++) - { - if (i == j) + for (int j = 0; j < waypoints.length (); j++) { + if (i == j) { continue; + } + actDamage = (g_experienceData + (i * waypoints.length ()) + j)->team1Damage; - actDamage = (g_experienceData + (i * g_numWaypoints) + j)->team1Damage; - - if (actDamage > maxDamage) - { + if (actDamage > maxDamage) { maxDamage = actDamage; bestIndex = j; } } - if (maxDamage > MAX_DAMAGE_VALUE) + if (maxDamage > MAX_DAMAGE_VALUE) { recalcKills = true; - - (g_experienceData + (i * g_numWaypoints) + i)->team1DangerIndex = static_cast (bestIndex); + } + (g_experienceData + (i * waypoints.length ()) + i)->team1DangerIndex = static_cast (bestIndex); } // adjust values if overflow is about to happen - if (recalcKills) - { - for (int i = 0; i < g_numWaypoints; i++) - { - for (int j = 0; j < g_numWaypoints; j++) - { - if (i == j) + if (recalcKills) { + for (int i = 0; i < waypoints.length (); i++) { + for (int j = 0; j < waypoints.length (); j++) { + if (i == j) { continue; + } - int clip = (g_experienceData + (i * g_numWaypoints) + j)->team0Damage; + int clip = (g_experienceData + (i * waypoints.length ()) + j)->team0Damage; clip -= static_cast (MAX_DAMAGE_VALUE * 0.5); - if (clip < 0) + if (clip < 0) { clip = 0; + } + (g_experienceData + (i * waypoints.length ()) + j)->team0Damage = static_cast (clip); - (g_experienceData + (i * g_numWaypoints) + j)->team0Damage = static_cast (clip); - - clip = (g_experienceData + (i * g_numWaypoints) + j)->team1Damage; + clip = (g_experienceData + (i * waypoints.length ()) + j)->team1Damage; clip -= static_cast (MAX_DAMAGE_VALUE * 0.5); - if (clip < 0) + if (clip < 0) { clip = 0; - - (g_experienceData + (i * g_numWaypoints) + j)->team1Damage = static_cast (clip); + } + (g_experienceData + (i * waypoints.length ()) + j)->team1Damage = static_cast (clip); } } } @@ -362,246 +307,244 @@ void UpdateGlobalExperienceData (void) int clip = g_highestDamageT - static_cast (MAX_DAMAGE_VALUE * 0.5); - if (clip < 1) + if (clip < 1) { clip = 1; - + } g_highestDamageT = clip; - clip = (int) g_highestDamageCT - static_cast (MAX_DAMAGE_VALUE * 0.5); + clip = (int)g_highestDamageCT - static_cast (MAX_DAMAGE_VALUE * 0.5); - if (clip < 1) + if (clip < 1) { clip = 1; - + } g_highestDamageCT = clip; - if (g_highestKills == MAX_KILL_HISTORY) - { - for (int i = 0; i < g_numWaypoints; i++) - { - (g_experienceData + (i * g_numWaypoints) + i)->team0Damage /= static_cast (engine.MaxClients () * 0.5); - (g_experienceData + (i * g_numWaypoints) + i)->team1Damage /= static_cast (engine.MaxClients () * 0.5); + if (g_highestKills == MAX_KILL_HISTORY) { + for (int i = 0; i < waypoints.length (); i++) { + (g_experienceData + (i * waypoints.length ()) + i)->team0Damage /= static_cast (engine.maxClients () * 0.5); + (g_experienceData + (i * waypoints.length ()) + i)->team1Damage /= static_cast (engine.maxClients () * 0.5); } g_highestKills = 1; } } -void RoundInit (void) -{ +void initRound (void) { // this is called at the start of each round g_roundEnded = false; g_canSayBombPlanted = true; // check team economics - for (int team = TERRORIST; team < SPECTATOR; team++) - { - bots.CheckTeamEconomics (team); - bots.SelectLeaderEachTeam (team, true); + for (int team = 0; team < MAX_TEAM_COUNT; team++) { + bots.updateTeamEconomics (team); + bots.selectLeaders (team, true); } + bots.reset (); - for (int i = 0; i < engine.MaxClients (); i++) - { - if (bots.GetBot (i)) - bots.GetBot (i)->NewRound (); + for (int i = 0; i < engine.maxClients (); i++) { + auto bot = bots.getBot (i); + if (bot != nullptr) { + bot->newRound (); + } g_radioSelect[i] = 0; } - waypoints.SetBombPosition (true); - waypoints.ClearVisitedGoals (); + waypoints.setBombPos (true); + waypoints.clearVisited (); g_bombSayString = false; g_timeBombPlanted = 0.0f; g_timeNextBombUpdate = 0.0f; - g_lastRadioTime[0] = 0.0f; - g_lastRadioTime[1] = 0.0f; + for (int i = 0; i < MAX_TEAM_COUNT; i++) { + g_lastRadioTime[i] = 0.0f; + } g_botsCanPause = false; - for (int i = 0; i < TASK_MAX; i++) + for (int i = 0; i < TASK_MAX; i++) { g_taskFilters[i].time = 0.0f; - - UpdateGlobalExperienceData (); // update experience data on round start + } + updateGlobalExperience (); // update experience data on round start // calculate the round mid/end in world time - g_timeRoundStart = engine.Time () + mp_freezetime.GetFloat (); - g_timeRoundMid = g_timeRoundStart + mp_roundtime.GetFloat () * 60.0f * 0.5f; - g_timeRoundEnd = g_timeRoundStart + mp_roundtime.GetFloat () * 60.0f; + g_timeRoundStart = engine.timebase () + mp_freezetime.flt (); + g_timeRoundMid = g_timeRoundStart + mp_roundtime.flt () * 60.0f * 0.5f; + g_timeRoundEnd = g_timeRoundStart + mp_roundtime.flt () * 60.0f; } -int GetWeaponPenetrationPower (int id) -{ +int getWeaponPenetrationPower (int id) { // returns if weapon can pierce through a wall int i = 0; - while (g_weaponSelect[i].id) - { - if (g_weaponSelect[i].id == id) + while (g_weaponSelect[i].id) { + if (g_weaponSelect[i].id == id) { return g_weaponSelect[i].penetratePower; - + } i++; } return 0; } -bool IsValidPlayer (edict_t *ent) -{ - if (engine.IsNullEntity (ent)) +bool isPlayer (edict_t *ent) { + if (engine.isNullEntity (ent)) { return false; + } - if (ent->v.flags & FL_PROXY) + if (ent->v.flags & FL_PROXY) { return false; + } - if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots.GetBot (ent) != nullptr) - return !IsNullString (STRING (ent->v.netname)); - + if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots.getBot (ent) != nullptr) { + return !isEmptyStr (STRING (ent->v.netname)); + } return false; } -bool IsPlayerVIP (edict_t *ent) -{ - if (!(g_mapType & MAP_AS)) +bool isPlayerVIP (edict_t *ent) { + if (!(g_mapFlags & MAP_AS)) { return false; + } - if (!IsValidPlayer (ent)) + if (!isPlayer (ent)) { return false; - - return *(INFOKEY_VALUE (GET_INFOKEYBUFFER (ent), "model")) == 'v'; + } + return *(g_engfuncs.pfnInfoKeyValue (g_engfuncs.pfnGetInfoKeyBuffer (ent), "model")) == 'v'; } -bool IsValidBot (edict_t *ent) -{ - if (bots.GetBot (ent) != nullptr || (!engine.IsNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) +bool isFakeClient (edict_t *ent) { + if (bots.getBot (ent) != nullptr || (!engine.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) { return true; - + } return false; } -bool OpenConfig (const char *fileName, const char *errorIfNotExists, MemoryFile *outFile, bool languageDependant /*= false*/) -{ - if (outFile->IsValid ()) - outFile->Close (); +bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) { + if (outFile->isValid ()) { + outFile->close (); + } // save config dir const char *configDir = "addons/yapb/conf"; - if (languageDependant) - { + if (languageDependant) { extern ConVar yb_language; - if (strcmp (fileName, "lang.cfg") == 0 && strcmp (yb_language.GetString (), "en") == 0) + if (strcmp (fileName, "lang.cfg") == 0 && strcmp (yb_language.str (), "en") == 0) { return false; - - const char *langConfig = FormatBuffer ("%s/lang/%s_%s", configDir, yb_language.GetString (), fileName); + } + const char *langConfig = format ("%s/lang/%s_%s", configDir, yb_language.str (), fileName); // check file existence int size = 0; uint8 *buffer = nullptr; // check is file is exists for this language - if ((buffer = MemoryFile::Loader (langConfig, &size)) != nullptr) - { - MemoryFile::Unloader (buffer); + if ((buffer = MemoryLoader::ref ().load (langConfig, &size)) != nullptr) { + MemoryLoader::ref ().unload (buffer); // unload and reopen file using MemoryFile - outFile->Open (langConfig); + outFile->open (langConfig); } else - outFile->Open (FormatBuffer ("%s/lang/en_%s", configDir, fileName)); + outFile->open (format ("%s/lang/en_%s", configDir, fileName)); } else - outFile->Open (FormatBuffer ("%s/%s", configDir, fileName)); + outFile->open (format ("%s/%s", configDir, fileName)); - if (!outFile->IsValid ()) - { - AddLogEntry (true, LL_ERROR, errorIfNotExists); + if (!outFile->isValid ()) { + logEntry (true, LL_ERROR, errorIfNotExists); return false; } return true; } -void CheckWelcomeMessage (void) -{ +void checkWelcome (void) { // the purpose of this function, is to send quick welcome message, to the listenserver entity. - static bool alreadyReceived = !yb_display_welcome_text.GetBool (); - static float receiveTime = 0.0f; - - if (alreadyReceived) + if (engine.isDedicated ()) return; - Array sentences; + static bool messageSent = !yb_display_welcome_text.boolean (); + static float receiveTime = 0.0f; - if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE))) - { - // add default messages - sentences.Push ("hello user,communication is acquired"); - sentences.Push ("your presence is acknowledged"); - sentences.Push ("high man, your in command now"); - sentences.Push ("blast your hostile for good"); - sentences.Push ("high man, kill some idiot here"); - sentences.Push ("is there a doctor in the area"); - sentences.Push ("warning, experimental materials detected"); - sentences.Push ("high amigo, shoot some but"); - sentences.Push ("attention, hours of work software, detected"); - sentences.Push ("time for some bad ass explosion"); - sentences.Push ("bad ass son of a breach device activated"); - sentences.Push ("high, do not question this great service"); - sentences.Push ("engine is operative, hello and goodbye"); - sentences.Push ("high amigo, your administration has been great last day"); - sentences.Push ("attention, expect experimental armed hostile presence"); - sentences.Push ("warning, medical attention required"); + if (messageSent) { + return; } - if (IsAlive (g_hostEntity) && !alreadyReceived && receiveTime < 1.0 && (g_numWaypoints > 0 ? g_gameWelcomeSent : true)) - receiveTime = engine.Time () + 4.0f; // receive welcome message in four seconds after game has commencing + if (g_gameFlags & GAME_LEGACY) { + g_gameWelcomeSent = true; + } - if (receiveTime > 0.0f && receiveTime < engine.Time () && !alreadyReceived && (g_numWaypoints > 0 ? g_gameWelcomeSent : true)) - { - if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE))) - engine.IssueCmd ("speak \"%s\"", const_cast (sentences.GetRandomElement ().GetBuffer ())); + static StringArray sentences; - engine.ChatPrintf ("----- %s v%s (Build: %u), {%s}, (c) 2016, by %s (%s)-----", PRODUCT_NAME, PRODUCT_VERSION, GenerateBuildNumber (), PRODUCT_DATE, PRODUCT_AUTHOR, PRODUCT_URL); - - MESSAGE_BEGIN (MSG_ONE, SVC_TEMPENTITY, nullptr, g_hostEntity); - WRITE_BYTE (TE_TEXTMESSAGE); - WRITE_BYTE (1); - WRITE_SHORT (FixedSigned16 (-1, 1 << 13)); - WRITE_SHORT (FixedSigned16 (-1, 1 << 13)); - WRITE_BYTE (2); - WRITE_BYTE (Random.Int (33, 255)); - WRITE_BYTE (Random.Int (33, 255)); - WRITE_BYTE (Random.Int (33, 255)); - WRITE_BYTE (0); - WRITE_BYTE (Random.Int (230, 255)); - WRITE_BYTE (Random.Int (230, 255)); - WRITE_BYTE (Random.Int (230, 255)); - WRITE_BYTE (200); - WRITE_SHORT (FixedUnsigned16 (0.0078125f, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (2.0f, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (6.0f, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (0.1f, 1 << 8)); - WRITE_STRING (FormatBuffer ("\nServer is running YaPB v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_VERSION, GenerateBuildNumber (), PRODUCT_AUTHOR, waypoints.GetInfo ())); - MESSAGE_END (); + if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE)) && sentences.empty ()) { + // add default messages + sentences.push ("hello user,communication is acquired"); + sentences.push ("your presence is acknowledged"); + sentences.push ("high man, your in command now"); + sentences.push ("blast your hostile for good"); + sentences.push ("high man, kill some idiot here"); + sentences.push ("is there a doctor in the area"); + sentences.push ("warning, experimental materials detected"); + sentences.push ("high amigo, shoot some but"); + sentences.push ("attention, hours of work software, detected"); + sentences.push ("time for some bad ass explosion"); + sentences.push ("bad ass son of a breach device activated"); + sentences.push ("high, do not question this great service"); + sentences.push ("engine is operative, hello and goodbye"); + sentences.push ("high amigo, your administration has been great last day"); + sentences.push ("attention, expect experimental armed hostile presence"); + sentences.push ("warning, medical attention required"); + } + bool needToSendMsg = (waypoints.length () > 0 ? g_gameWelcomeSent : true); + + if (isAlive (g_hostEntity) && receiveTime < 1.0 && needToSendMsg) { + receiveTime = engine.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing + } + + if (receiveTime > 0.0f && receiveTime < engine.timebase () && needToSendMsg) { + if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE))) { + engine.execCmd ("speak \"%s\"", sentences.random ().chars ()); + } + engine.chatPrint ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL); + + MessageWriter (MSG_ONE, SVC_TEMPENTITY, Vector::null (), g_hostEntity) + .writeByte (TE_TEXTMESSAGE) + .writeByte (1) + .writeShort (MessageWriter::fs16 (-1, 1 << 13)) + .writeShort (MessageWriter::fs16 (-1, 1 << 13)) + .writeByte (2) + .writeByte (rng.getInt (33, 255)) + .writeByte (rng.getInt (33, 255)) + .writeByte (rng.getInt (33, 255)) + .writeByte (0) + .writeByte (rng.getInt (230, 255)) + .writeByte (rng.getInt (230, 255)) + .writeByte (rng.getInt (230, 255)) + .writeByte (200) + .writeShort (MessageWriter::fu16 (0.0078125f, 1 << 8)) + .writeShort (MessageWriter::fu16 (2.0f, 1 << 8)) + .writeShort (MessageWriter::fu16 (6.0f, 1 << 8)) + .writeShort (MessageWriter::fu16 (0.1f, 1 << 8)) + .writeString (format ("\nServer is running YaPB v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, waypoints.getAuthor ())); receiveTime = 0.0; - alreadyReceived = true; + messageSent = true; } } -void AddLogEntry (bool outputToConsole, int logLevel, const char *format, ...) -{ +void logEntry (bool outputToConsole, int logLevel, const char *format, ...) { // this function logs a message to the message log file root directory. va_list ap; - char buffer[MAX_PRINT_BUFFER] = {0, }, levelString[32] = {0, }, logLine[MAX_PRINT_BUFFER] = {0, }; + char buffer[MAX_PRINT_BUFFER] = { 0, }, levelString[32] = { 0, }; va_start (ap, format); - vsnprintf (buffer, SIZEOF_CHAR (buffer), format, ap); + vsnprintf (buffer, cr::bufsize (buffer), format, ap); va_end (ap); - switch (logLevel) - { + switch (logLevel) { case LL_DEFAULT: strcpy (levelString, "LOG: "); break; @@ -619,52 +562,53 @@ void AddLogEntry (bool outputToConsole, int logLevel, const char *format, ...) break; } - if (outputToConsole) - engine.Printf ("%s%s", levelString, buffer); + if (outputToConsole) { + engine.print ("%s%s", levelString, buffer); + } // now check if logging disabled - if (!(logLevel & LL_IGNORE)) - { + if (!(logLevel & LL_IGNORE)) { extern ConVar yb_debug; - if (logLevel == LL_DEFAULT && yb_debug.GetInt () < 3) + if (logLevel == LL_DEFAULT && yb_debug.integer () < 3) { return; // no log, default logging is disabled + } - if (logLevel == LL_WARNING && yb_debug.GetInt () < 2) + if (logLevel == LL_WARNING && yb_debug.integer () < 2) { return; // no log, warning logging is disabled + } - if (logLevel == LL_ERROR && yb_debug.GetInt () < 1) + if (logLevel == LL_ERROR && yb_debug.integer () < 1) { return; // no log, error logging is disabled + } } // open file in a standard stream File fp ("yapb.txt", "at"); // check if we got a valid handle - if (!fp.IsValid ()) + if (!fp.isValid ()) { return; + } time_t tickTime = time (&tickTime); tm *time = localtime (&tickTime); - sprintf (logLine, "[%02d:%02d:%02d] %s%s", time->tm_hour, time->tm_min, time->tm_sec, levelString, buffer); + fp.writeFormat ("%02d:%02d:%02d --> %s%s", time->tm_hour, time->tm_min, time->tm_sec, levelString, buffer); + fp.close (); - fp.Printf ("%s\n", logLine); - fp.Close (); + if (logLevel == LL_FATAL) { + bots.kickEveryone (true); + cleanupGarbage (); - if (logLevel == LL_FATAL) - { - bots.RemoveAll (); - FreeLibraryMemory (); - -#if defined (PLATFORM_WIN32) +#if defined(PLATFORM_WIN32) DestroyWindow (GetForegroundWindow ()); MessageBoxA (GetActiveWindow (), buffer, "YaPB Error", MB_ICONSTOP); #else - printf ("%s", buffer); + printf ("%s\n", buffer); #endif -#if defined (PLATFORM_WIN32) +#if defined(PLATFORM_WIN32) _exit (1); #else exit (1); @@ -672,8 +616,7 @@ void AddLogEntry (bool outputToConsole, int logLevel, const char *format, ...) } } -bool FindNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool isAlive, bool needDrawn) -{ +bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool isAlive, bool needDrawn, bool needBotWithC4) { // this function finds nearest to to, player with set of parameters, like his // team, live status, search distance etc. if needBot is true, then pvHolder, will // be filled with bot pointer, else with edict pointer(!). @@ -681,71 +624,67 @@ bool FindNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool edict_t *survive = nullptr; // pointer to temporally & survive entity float nearestPlayer = 4096.0f; // nearest player - int toTeam = engine.GetTeam (to); + int toTeam = engine.getTeam (to); - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || client.ent == to) + if (!(client.flags & CF_USED) || client.ent == to) { continue; + } - if ((sameTeam && client.team != toTeam) || (isAlive && !(client.flags & CF_ALIVE)) || (needBot && !IsValidBot (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW))) + if ((sameTeam && client.team != toTeam) || (isAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) { continue; // filter players with parameters + } + float distance = (client.ent->v.origin - to->v.origin).length (); - float distance = (client.ent->v.origin - to->v.origin).GetLength (); - - if (distance < nearestPlayer && distance < searchDistance) - { + if (distance < nearestPlayer && distance < searchDistance) { nearestPlayer = distance; survive = client.ent; } } - if (engine.IsNullEntity (survive)) + if (engine.isNullEntity (survive)) return false; // nothing found // fill the holder - if (needBot) - *pvHolder = reinterpret_cast (bots.GetBot (survive)); - else + if (needBot) { + *pvHolder = reinterpret_cast (bots.getBot (survive)); + } + else { *pvHolder = reinterpret_cast (survive); - + } return true; } -void SoundAttachToClients (edict_t *ent, const char *sample, float volume) -{ +void attachSoundsToClients (edict_t *ent, const char *sample, float volume) { // this function called by the sound hooking code (in emit_sound) enters the played sound into // the array associated with the entity - if (engine.IsNullEntity (ent) || IsNullString (sample)) + if (engine.isNullEntity (ent) || isEmptyStr (sample)) { return; + } + const Vector &origin = engine.getAbsPos (ent); - const Vector &origin = engine.GetAbsOrigin (ent); - - if (origin.IsZero ()) + if (origin.empty ()) { return; + } + int index = engine.indexOfEntity (ent) - 1; - int index = engine.IndexOfEntity (ent) - 1; - - if (index < 0 || index >= engine.MaxClients ()) - { + if (index < 0 || index >= engine.maxClients ()) { float nearestDistance = 99999.0f; // loop through all players - for (int i = 0; i < engine.MaxClients (); i++) - { + for (int i = 0; i < engine.maxClients (); i++) { const Client &client = g_clients[i]; - if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) + if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) { continue; - - float distance = (client.origin - origin).GetLength (); + } + float distance = (client.origin - origin).length (); // now find nearest player - if (distance < nearestDistance) - { + if (distance < nearestDistance) { index = i; nearestDistance = distance; } @@ -753,70 +692,62 @@ void SoundAttachToClients (edict_t *ent, const char *sample, float volume) } // in case of worst case - if (index < 0 || index >= engine.MaxClients ()) + if (index < 0 || index >= engine.maxClients ()) { return; - + } Client &client = g_clients[index]; - if (strncmp ("player/bhit_flesh", sample, 17) == 0 || strncmp ("player/headshot", sample, 15) == 0) - { + if (strncmp ("player/bhit_flesh", sample, 17) == 0 || strncmp ("player/headshot", sample, 15) == 0) { // hit/fall sound? client.hearingDistance = 768.0f * volume; - client.timeSoundLasting = engine.Time () + 0.5f; + client.timeSoundLasting = engine.timebase () + 0.5f; client.soundPos = origin; } - else if (strncmp ("items/gunpickup", sample, 15) == 0) - { + else if (strncmp ("items/gunpickup", sample, 15) == 0) { // weapon pickup? client.hearingDistance = 768.0f * volume; - client.timeSoundLasting = engine.Time () + 0.5f; + client.timeSoundLasting = engine.timebase () + 0.5f; client.soundPos = origin; } - else if (strncmp ("weapons/zoom", sample, 12) == 0) - { + else if (strncmp ("weapons/zoom", sample, 12) == 0) { // sniper zooming? client.hearingDistance = 512.0f * volume; - client.timeSoundLasting = engine.Time () + 0.1f; + client.timeSoundLasting = engine.timebase () + 0.1f; client.soundPos = origin; } - else if (strncmp ("items/9mmclip", sample, 13) == 0) - { + else if (strncmp ("items/9mmclip", sample, 13) == 0) { // ammo pickup? client.hearingDistance = 512.0f * volume; - client.timeSoundLasting = engine.Time () + 0.1f; + client.timeSoundLasting = engine.timebase () + 0.1f; client.soundPos = origin; } - else if (strncmp ("hostage/hos", sample, 11) == 0) - { + else if (strncmp ("hostage/hos", sample, 11) == 0) { // CT used hostage? client.hearingDistance = 1024.0f * volume; - client.timeSoundLasting = engine.Time () + 5.0f; + client.timeSoundLasting = engine.timebase () + 5.0f; client.soundPos = origin; } - else if (strncmp ("debris/bustmetal", sample, 16) == 0 || strncmp ("debris/bustglass", sample, 16) == 0) - { + else if (strncmp ("debris/bustmetal", sample, 16) == 0 || strncmp ("debris/bustglass", sample, 16) == 0) { // broke something? client.hearingDistance = 1024.0f * volume; - client.timeSoundLasting = engine.Time () + 2.0f; + client.timeSoundLasting = engine.timebase () + 2.0f; client.soundPos = origin; } - else if (strncmp ("doors/doormove", sample, 14) == 0) - { + else if (strncmp ("doors/doormove", sample, 14) == 0) { // someone opened a door client.hearingDistance = 1024.0f * volume; - client.timeSoundLasting = engine.Time () + 3.0f; + client.timeSoundLasting = engine.timebase () + 3.0f; client.soundPos = origin; } } -void SoundSimulateUpdate (int playerIndex) -{ +void simulateSoundUpdates (int playerIndex) { // this function tries to simulate playing of sounds to let the bots hear sounds which aren't // captured through server sound hooking - if (playerIndex < 0 || playerIndex >= engine.MaxClients ()) + if (playerIndex < 0 || playerIndex >= engine.maxClients ()) { return; // reliability check - + } Client &client = g_clients[playerIndex]; float hearDistance = 0.0f; @@ -825,54 +756,49 @@ void SoundSimulateUpdate (int playerIndex) if (client.ent->v.oldbuttons & IN_ATTACK) // pressed attack button? { hearDistance = 2048.0f; - timeSound = engine.Time () + 0.3f; + timeSound = engine.timebase () + 0.3f; } else if (client.ent->v.oldbuttons & IN_USE) // pressed used button? { hearDistance = 512.0f; - timeSound = engine.Time () + 0.5f; + timeSound = engine.timebase () + 0.5f; } else if (client.ent->v.oldbuttons & IN_RELOAD) // pressed reload button? { hearDistance = 512.0f; - timeSound = engine.Time () + 0.5f; + timeSound = engine.timebase () + 0.5f; } else if (client.ent->v.movetype == MOVETYPE_FLY) // uses ladder? { - if (fabsf (client.ent->v.velocity.z) > 50.0f) - { + if (cr::abs (client.ent->v.velocity.z) > 50.0f) { hearDistance = 1024.0f; - timeSound = engine.Time () + 0.3f; + timeSound = engine.timebase () + 0.3f; } } - else - { + else { extern ConVar mp_footsteps; - if (mp_footsteps.GetBool ()) - { + if (mp_footsteps.boolean ()) { // moves fast enough? - hearDistance = 1280.0f * (client.ent->v.velocity.GetLength2D () / 260.0f); - timeSound = engine.Time () + 0.3f; + hearDistance = 1280.0f * (client.ent->v.velocity.length2D () / 260.0f); + timeSound = engine.timebase () + 0.3f; } } - if (hearDistance <= 0.0) + if (hearDistance <= 0.0) { return; // didn't issue sound? + } // some sound already associated - if (client.timeSoundLasting > engine.Time ()) - { - if (client.hearingDistance <= hearDistance) - { + if (client.timeSoundLasting > engine.timebase ()) { + if (client.hearingDistance <= hearDistance) { // override it with new client.hearingDistance = hearDistance; client.timeSoundLasting = timeSound; client.soundPos = client.ent->v.origin; } } - else - { + else { // just remember it client.hearingDistance = hearDistance; client.timeSoundLasting = timeSound; @@ -880,15 +806,14 @@ void SoundSimulateUpdate (int playerIndex) } } -int GenerateBuildNumber (void) -{ +int buildNumber (void) { // this function generates build number from the compiler date macros static int buildNumber = 0; - if (buildNumber != 0) + if (buildNumber != 0) { return buildNumber; - + } // get compiling date using compiler macros const char *date = __DATE__; @@ -903,11 +828,10 @@ int GenerateBuildNumber (void) int i = 0; // go through all months, and calculate, days since year start - for (i = 0; i < 11; i++) - { - if (strncmp (&date[0], months[i], 3) == 0) + for (i = 0; i < 11; i++) { + if (strncmp (&date[0], months[i], 3) == 0) { break; // found current month break - + } day += monthDays[i]; // add month days } day += atoi (&date[4]) - 1; // finally calculate day @@ -916,28 +840,25 @@ int GenerateBuildNumber (void) buildNumber = day + static_cast ((year - 1) * 365.25); // if the year is a leap year? - if ((year % 4) == 0 && i > 1) + if ((year % 4) == 0 && i > 1) { buildNumber += 1; // add one year more - + } buildNumber -= 1114; return buildNumber; } -int GetWeaponReturn (bool needString, const char *weaponAlias, int weaponIndex) -{ +int getWeaponData (bool needString, const char *weaponAlias, int weaponIndex) { // this function returning weapon id from the weapon alias and vice versa. // structure definition for weapon tab - struct WeaponTab_t - { + struct WeaponTab_t { Weapon weaponIndex; // weapon id const char *alias; // weapon alias }; // weapon enumeration - WeaponTab_t weaponTab[] = - { + WeaponTab_t weaponTab[] = { {WEAPON_USP, "usp"}, // HK USP .45 Tactical {WEAPON_GLOCK, "glock"}, // Glock18 Select Fire {WEAPON_DEAGLE, "deagle"}, // Desert Eagle .50AE @@ -969,24 +890,24 @@ int GetWeaponReturn (bool needString, const char *weaponAlias, int weaponIndex) {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 (int i = 0; i < ARRAYSIZE_HLSDK (weaponTab); i++) - { - if (weaponTab[i].weaponIndex == weaponIndex) // is weapon id found? + if (needString && weaponIndex != -1) { + for (size_t i = 0; i < cr::arrsize (weaponTab); i++) { + if (weaponTab[i].weaponIndex == weaponIndex) { // is weapon id found? return MAKE_STRING (weaponTab[i].alias); + } } return MAKE_STRING ("(none)"); // return none } // else search weapon by name and return weapon id - for (int i = 0; i < ARRAYSIZE_HLSDK (weaponTab); i++) - { - if (strncmp (weaponTab[i].alias, weaponAlias, strlen (weaponTab[i].alias)) == 0) + for (size_t i = 0; i < cr::arrsize (weaponTab); i++) { + if (strncmp (weaponTab[i].alias, weaponAlias, strlen (weaponTab[i].alias)) == 0) { return weaponTab[i].weaponIndex; + } } return -1; // no weapon was found return -1 -} +} \ No newline at end of file diff --git a/source/waypoint.cpp b/source/waypoint.cpp index 9f594e6..c719195 100644 --- a/source/waypoint.cpp +++ b/source/waypoint.cpp @@ -4,105 +4,95 @@ // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: -// https://yapb.jeefo.net/license +// https://yapb.ru/license // -#include +#include ConVar yb_wptsubfolder ("yb_wptsubfolder", ""); -ConVar yb_waypoint_autodl_host ("yb_waypoint_autodl_host", "yapb.jeefo.net"); +ConVar yb_waypoint_autodl_host ("yb_waypoint_autodl_host", "yapb.ru"); ConVar yb_waypoint_autodl_enable ("yb_waypoint_autodl_enable", "1"); -void Waypoint::Init (void) -{ +void Waypoint::init (void) { // this function initialize the waypoint structures.. m_loadTries = 0; - m_learnVelocity.Zero (); - m_learnPosition.Zero (); - m_lastWaypoint.Zero (); + m_learnVelocity.nullify (); + m_learnPosition.nullify (); + m_lastWaypoint.nullify (); + + m_pathDisplayTime = 0.0f; + m_arrowDisplayTime = 0.0f; // have any waypoint path nodes been allocated yet? - if (m_waypointPaths) - CleanupPathMemory (); - - g_numWaypoints = 0; + if (m_waypointPaths) { + cleanupPathMemory (); + } + m_numWaypoints = 0; } -void Waypoint::CleanupPathMemory (void) -{ - for (int i = 0; i < g_numWaypoints && m_paths[i] != nullptr; i++) - { +void Waypoint::cleanupPathMemory (void) { + for (int i = 0; i < m_numWaypoints && m_paths[i] != nullptr; i++) { delete m_paths[i]; m_paths[i] = nullptr; } } -void Waypoint::AddPath (int addIndex, int pathIndex, float distance) -{ - if (addIndex < 0 || addIndex >= g_numWaypoints || pathIndex < 0 || pathIndex >= g_numWaypoints || addIndex == pathIndex) +void Waypoint::addPath (int addIndex, int pathIndex, float distance) { + if (!exists (addIndex) || !exists (pathIndex)) { return; - + } Path *path = m_paths[addIndex]; // don't allow paths get connected twice - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (path->index[i] == pathIndex) - { - AddLogEntry (true, LL_WARNING, "Denied path creation from %d to %d (path already exists)", addIndex, pathIndex); + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (path->index[i] == pathIndex) { + logEntry (true, LL_WARNING, "Denied path creation from %d to %d (path already exists)", addIndex, pathIndex); return; } } // check for free space in the connection indices - for (int16 i = 0; i < MAX_PATH_INDEX; i++) - { - if (path->index[i] == -1) - { + for (int16 i = 0; i < MAX_PATH_INDEX; i++) { + if (path->index[i] == INVALID_WAYPOINT_INDEX) { path->index[i] = static_cast (pathIndex); - path->distances[i] = abs (static_cast (distance)); + path->distances[i] = cr::abs (static_cast (distance)); - AddLogEntry (true, LL_DEFAULT, "Path added from %d to %d", addIndex, pathIndex); + logEntry (true, LL_DEFAULT, "Path added from %d to %d", addIndex, pathIndex); return; } } // there wasn't any free space. try exchanging it with a long-distance path int maxDistance = -9999; - int slotID = -1; + int slotID = INVALID_WAYPOINT_INDEX; - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (path->distances[i] > maxDistance) - { + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (path->distances[i] > maxDistance) { maxDistance = path->distances[i]; slotID = i; } } - if (slotID != -1) - { - AddLogEntry (true, LL_DEFAULT, "Path added from %d to %d", addIndex, pathIndex); + if (slotID != INVALID_WAYPOINT_INDEX) { + logEntry (true, LL_DEFAULT, "Path added from %d to %d", addIndex, pathIndex); path->index[slotID] = static_cast (pathIndex); - path->distances[slotID] = abs (static_cast (distance)); + path->distances[slotID] = cr::abs (static_cast (distance)); } } -int Waypoint::FindFarest (const Vector &origin, float maxDistance) -{ +int Waypoint::getFarest (const Vector &origin, float maxDistance) { // find the farest waypoint to that Origin, and return the index to this waypoint - int index = -1; + int index = INVALID_WAYPOINT_INDEX; + maxDistance = cr::square (maxDistance); - for (int i = 0; i < g_numWaypoints; i++) - { - float distance = (m_paths[i]->origin - origin).GetLength (); + for (int i = 0; i < m_numWaypoints; i++) { + float distance = (m_paths[i]->origin - origin).lengthSq (); - if (distance > maxDistance) - { + if (distance > maxDistance) { index = i; maxDistance = distance; } @@ -110,21 +100,20 @@ int Waypoint::FindFarest (const Vector &origin, float maxDistance) return index; } -int Waypoint::FindNearest (const Vector &origin, float minDistance, int flags) -{ +int Waypoint::getNearestNoBuckets (const Vector &origin, float minDistance, int flags) { // find the nearest waypoint to that origin and return the index - int index = -1; + // fallback and go thru wall the waypoints... + int index = INVALID_WAYPOINT_INDEX; + minDistance = cr::square (minDistance); - for (int i = 0; i < g_numWaypoints; i++) - { - if (flags != -1 && !(m_paths[i]->flags & flags)) + for (int i = 0; i < m_numWaypoints; i++) { + if (flags != -1 && !(m_paths[i]->flags & flags)) { continue; // if flag not -1 and waypoint has no this flag, skip waypoint + } + float distance = (m_paths[i]->origin - origin).lengthSq (); - float distance = (m_paths[i]->origin - origin).GetLength (); - - if (distance < minDistance) - { + if (distance < minDistance) { index = i; minDistance = distance; } @@ -132,26 +121,72 @@ int Waypoint::FindNearest (const Vector &origin, float minDistance, int flags) return index; } -void Waypoint::FindInRadius (Array &radiusHolder, float radius, const Vector &origin, int maxCount) -{ - // returns all waypoints within radius from position - - for (int i = 0; i < g_numWaypoints; i++) - { - if (maxCount != -1 && radiusHolder.GetElementNumber () > maxCount) - break; - - if ((m_paths[i]->origin - origin).GetLength () < radius) - radiusHolder.Push (i); +int Waypoint::getEditorNeareset (void) { + if (!g_waypointOn) { + return INVALID_WAYPOINT_INDEX; } + return getNearestNoBuckets (g_hostEntity->v.origin, 50.0f); } -void Waypoint::Add (int flags, const Vector &waypointOrigin) -{ - if (engine.IsNullEntity (g_hostEntity)) - return; +int Waypoint::getNearest (const Vector &origin, float minDistance, int flags) { + // find the nearest waypoint to that origin and return the index - int index = -1, i; + auto &bucket = getWaypointsInBucket (origin); + + if (bucket.empty ()) { + return getNearestNoBuckets (origin, minDistance, flags); + } + int index = INVALID_WAYPOINT_INDEX; + minDistance = cr::square (minDistance); + + for (const auto at : bucket) { + if (flags != -1 && !(m_paths[at]->flags & flags)) { + continue; // if flag not -1 and waypoint has no this flag, skip waypoint + } + float distance = (m_paths[at]->origin - origin).lengthSq (); + + if (distance < minDistance) { + index = at; + minDistance = distance; + } + } + return index; +} + +IntArray Waypoint::searchRadius (float radius, const Vector &origin, int maxCount) { + // returns all waypoints within radius from position + + IntArray result; + auto &bucket = getWaypointsInBucket (origin); + + if (bucket.empty ()) { + result.push (getNearestNoBuckets (origin, radius)); + return cr::move (result); + } + radius = cr::square (radius); + + if (maxCount != -1) { + result.reserve (maxCount); + } + + for (const auto at : bucket) { + if (maxCount != -1 && static_cast (result.length ()) > maxCount) { + break; + } + + if ((m_paths[at]->origin - origin).lengthSq () < radius) { + result.push (at); + } + } + return cr::move (result); +} + +void Waypoint::push (int flags, const Vector &waypointOrigin) { + if (engine.isNullEntity (g_hostEntity)) { + return; + } + + int index = INVALID_WAYPOINT_INDEX, i; float distance; Vector forward; @@ -160,54 +195,49 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) bool placeNew = true; Vector newOrigin = waypointOrigin; - if (waypointOrigin.IsZero ()) + if (waypointOrigin.empty ()) { newOrigin = g_hostEntity->v.origin; + } - if (bots.GetBotsNum () > 0) - bots.RemoveAll (); - + if (bots.getBotCount () > 0) { + bots.kickEveryone (true); + } m_waypointsChanged = true; - switch (flags) - { + switch (flags) { case 6: - index = FindNearest (g_hostEntity->v.origin, 50.0f); + index = getEditorNeareset (); - if (index != -1) - { + if (index != INVALID_WAYPOINT_INDEX) { path = m_paths[index]; - if (!(path->flags & FLAG_CAMP)) - { - engine.CenterPrintf ("This is not Camping Waypoint"); + if (!(path->flags & FLAG_CAMP)) { + engine.centerPrint ("This is not Camping Waypoint"); return; } - MakeVectors (g_hostEntity->v.v_angle); + makeVectors (g_hostEntity->v.v_angle); forward = g_hostEntity->v.origin + g_hostEntity->v.view_ofs + g_pGlobals->v_forward * 640.0f; path->campEndX = forward.x; path->campEndY = forward.y; // play "done" sound... - engine.EmitSound (g_hostEntity, "common/wpn_hudon.wav"); + engine.playSound (g_hostEntity, "common/wpn_hudon.wav"); } return; case 9: - index = FindNearest (g_hostEntity->v.origin, 50.0f); + index = getEditorNeareset (); - if (index != -1) - { - distance = (m_paths[index]->origin - g_hostEntity->v.origin).GetLength (); + if (index != INVALID_WAYPOINT_INDEX && m_paths[index] != nullptr) { + distance = (m_paths[index]->origin - g_hostEntity->v.origin).length (); - if (distance < 50.0f) - { + if (distance < 50.0f) { placeNew = false; - path = m_paths[index]; - if (flags == 9) - path->origin = (path->origin + m_learnPosition) * 0.5f; + path = m_paths[index]; + path->origin = (path->origin + m_learnPosition) * 0.5f; } } else @@ -215,63 +245,57 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) break; case 10: - index = FindNearest (g_hostEntity->v.origin, 50.0f); + index = getEditorNeareset (); - if (index != -1 && m_paths[index] != nullptr) - { - distance = (m_paths[index]->origin - g_hostEntity->v.origin).GetLength (); + if (index != INVALID_WAYPOINT_INDEX && m_paths[index] != nullptr) { + distance = (m_paths[index]->origin - g_hostEntity->v.origin).length (); - if (distance < 50.0f) - { + if (distance < 50.0f) { placeNew = false; path = m_paths[index]; - flags = 0; + int connectionFlags = 0; - for (i = 0; i < MAX_PATH_INDEX; i++) - flags += path->connectionFlags[i]; - - if (flags == 0) + for (i = 0; i < MAX_PATH_INDEX; i++) { + connectionFlags += path->connectionFlags[i]; + } + if (connectionFlags == 0) { path->origin = (path->origin + g_hostEntity->v.origin) * 0.5f; + } } } break; } - if (placeNew) - { - if (g_numWaypoints >= MAX_WAYPOINTS) + if (placeNew) { + if (m_numWaypoints >= MAX_WAYPOINTS) { return; - - index = g_numWaypoints; + } + index = m_numWaypoints; m_paths[index] = new Path; - - if (m_paths[index] == nullptr) - TerminateOnMalloc (); - path = m_paths[index]; // increment total number of waypoints - g_numWaypoints++; + m_numWaypoints++; path->pathNumber = index; path->flags = 0; // store the origin (location) of this waypoint path->origin = newOrigin; + addToBucket (newOrigin, index); path->campEndX = 0.0f; path->campEndY = 0.0f; path->campStartX = 0.0f; path->campStartY = 0.0f; - for (i = 0; i < MAX_PATH_INDEX; i++) - { - path->index[i] = -1; + for (i = 0; i < MAX_PATH_INDEX; i++) { + path->index[i] = INVALID_WAYPOINT_INDEX; path->distances[i] = 0; path->connectionFlags[i] = 0; - path->connectionVelocity[i].Zero (); + path->connectionVelocity[i].nullify (); } // store the last used waypoint for the auto waypoint code... @@ -281,17 +305,15 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) // set the time that this waypoint was originally displayed... m_waypointDisplayTime[index] = 0; - if (flags == 9) + if (flags == 9) { m_lastJumpWaypoint = index; - else if (flags == 10) - { - distance = (m_paths[m_lastJumpWaypoint]->origin - g_hostEntity->v.origin).GetLength (); - AddPath (m_lastJumpWaypoint, index, distance); + } + else if (flags == 10) { + distance = (m_paths[m_lastJumpWaypoint]->origin - g_hostEntity->v.origin).length (); + addPath (m_lastJumpWaypoint, index, distance); - for (i = 0; i < MAX_PATH_INDEX; i++) - { - if (m_paths[m_lastJumpWaypoint]->index[i] == index) - { + for (i = 0; i < MAX_PATH_INDEX; i++) { + if (m_paths[m_lastJumpWaypoint]->index[i] == index) { m_paths[m_lastJumpWaypoint]->connectionFlags[i] |= PATHFLAG_JUMP; m_paths[m_lastJumpWaypoint]->connectionVelocity[i] = m_learnVelocity; @@ -299,26 +321,30 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) } } - CalculateWayzone (index); + calculatePathRadius (index); return; } - if (g_hostEntity->v.flags & FL_DUCKING) - path->flags |= FLAG_CROUCH; // set a crouch waypoint + if (path == nullptr) { + return; + } - if (g_hostEntity->v.movetype == MOVETYPE_FLY) - { + if (g_hostEntity->v.flags & FL_DUCKING) { + path->flags |= FLAG_CROUCH; // set a crouch waypoint + } + + if (g_hostEntity->v.movetype == MOVETYPE_FLY) { path->flags |= FLAG_LADDER; - MakeVectors (g_hostEntity->v.v_angle); + makeVectors (g_hostEntity->v.v_angle); forward = g_hostEntity->v.origin + g_hostEntity->v.view_ofs + g_pGlobals->v_forward * 640.0f; path->campStartY = forward.y; } - else if (m_isOnLadder) + else if (m_isOnLadder) { path->flags |= FLAG_LADDER; + } - switch (flags) - { + switch (flags) { case 1: path->flags |= FLAG_CROSSING; path->flags |= FLAG_TF_ONLY; @@ -341,7 +367,7 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) path->flags |= FLAG_CROSSING; path->flags |= FLAG_CAMP; - MakeVectors (g_hostEntity->v.v_angle); + makeVectors (g_hostEntity->v.v_angle); forward = g_hostEntity->v.origin + g_hostEntity->v.view_ofs + g_pGlobals->v_forward * 640.0f; path->campStartX = forward.x; @@ -354,42 +380,36 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) } // Ladder waypoints need careful connections - if (path->flags & FLAG_LADDER) - { + if (path->flags & FLAG_LADDER) { float minDistance = 9999.0f; - int destIndex = -1; + int destIndex = INVALID_WAYPOINT_INDEX; TraceResult tr; // calculate all the paths to this new waypoint - for (i = 0; i < g_numWaypoints; i++) - { - if (i == index) + for (i = 0; i < m_numWaypoints; i++) { + if (i == index) { continue; // skip the waypoint that was just added + } - // Other ladder waypoints should connect to this - if (m_paths[i]->flags & FLAG_LADDER) - { + // other ladder waypoints should connect to this + if (m_paths[i]->flags & FLAG_LADDER) { // check if the waypoint is reachable from the new one - engine.TestLine (newOrigin, m_paths[i]->origin, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); + engine.testLine (newOrigin, m_paths[i]->origin, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); - if (tr.flFraction == 1.0f && fabs (newOrigin.x - m_paths[i]->origin.x) < 64.0f && fabs (newOrigin.y - m_paths[i]->origin.y) < 64.0f && fabs (newOrigin.z - m_paths[i]->origin.z) < g_autoPathDistance) - { - distance = (m_paths[i]->origin - newOrigin).GetLength (); + if (tr.flFraction == 1.0f && cr::abs (newOrigin.x - m_paths[i]->origin.x) < 64.0f && cr::abs (newOrigin.y - m_paths[i]->origin.y) < 64.0f && cr::abs (newOrigin.z - m_paths[i]->origin.z) < g_autoPathDistance) { + distance = (m_paths[i]->origin - newOrigin).length (); - AddPath (index, i, distance); - AddPath (i, index, distance); + addPath (index, i, distance); + addPath (i, index, distance); } } - else - { + else { // check if the waypoint is reachable from the new one - if (IsNodeReachable (newOrigin, m_paths[i]->origin) || IsNodeReachable (m_paths[i]->origin, newOrigin)) - { - distance = (m_paths[i]->origin - newOrigin).GetLength (); + if (isNodeReacheable (newOrigin, m_paths[i]->origin) || isNodeReacheable (m_paths[i]->origin, newOrigin)) { + distance = (m_paths[i]->origin - newOrigin).length (); - if (distance < minDistance) - { + if (distance < minDistance) { destIndex = i; minDistance = distance; } @@ -397,283 +417,260 @@ void Waypoint::Add (int flags, const Vector &waypointOrigin) } } - if (destIndex > -1 && destIndex < g_numWaypoints) - { + if (exists (destIndex)) { // check if the waypoint is reachable from the new one (one-way) - if (IsNodeReachable (newOrigin, m_paths[destIndex]->origin)) - { - distance = (m_paths[destIndex]->origin - newOrigin).GetLength (); - AddPath (index, destIndex, distance); + if (isNodeReacheable (newOrigin, m_paths[destIndex]->origin)) { + distance = (m_paths[destIndex]->origin - newOrigin).length (); + addPath (index, destIndex, distance); } // check if the new one is reachable from the waypoint (other way) - if (IsNodeReachable (m_paths[destIndex]->origin, newOrigin)) - { - distance = (m_paths[destIndex]->origin - newOrigin).GetLength (); - AddPath (destIndex, index, distance); + if (isNodeReacheable (m_paths[destIndex]->origin, newOrigin)) { + distance = (m_paths[destIndex]->origin - newOrigin).length (); + addPath (destIndex, index, distance); } } } - else - { + else { // calculate all the paths to this new waypoint - for (i = 0; i < g_numWaypoints; i++) - { - if (i == index) + for (i = 0; i < m_numWaypoints; i++) { + if (i == index) { continue; // skip the waypoint that was just added + } // check if the waypoint is reachable from the new one (one-way) - if (IsNodeReachable (newOrigin, m_paths[i]->origin)) - { - distance = (m_paths[i]->origin - newOrigin).GetLength (); - AddPath (index, i, distance); + if (isNodeReacheable (newOrigin, m_paths[i]->origin)) { + distance = (m_paths[i]->origin - newOrigin).length (); + addPath (index, i, distance); } // check if the new one is reachable from the waypoint (other way) - if (IsNodeReachable (m_paths[i]->origin, newOrigin)) - { - distance = (m_paths[i]->origin - newOrigin).GetLength (); - AddPath (i, index, distance); + if (isNodeReacheable (m_paths[i]->origin, newOrigin)) { + distance = (m_paths[i]->origin - newOrigin).length (); + addPath (i, index, distance); } } } - engine.EmitSound (g_hostEntity, "weapons/xbow_hit1.wav"); - CalculateWayzone (index); // calculate the wayzone of this waypoint + engine.playSound (g_hostEntity, "weapons/xbow_hit1.wav"); + calculatePathRadius (index); // calculate the wayzone of this waypoint } -void Waypoint::Delete (void) -{ +void Waypoint::erase (int target) { m_waypointsChanged = true; - if (g_numWaypoints < 1) + if (m_numWaypoints < 1) { return; + } - if (bots.GetBotsNum () > 0) - bots.RemoveAll (); + if (bots.getBotCount () > 0) { + bots.kickEveryone (true); + } + int index = (target == INVALID_WAYPOINT_INDEX) ? getEditorNeareset () : target; - int index = FindNearest (g_hostEntity->v.origin, 50.0f); - - if (index < 0) + if (index == INVALID_WAYPOINT_INDEX) { return; + } Path *path = nullptr; - InternalAssert (m_paths[index] != nullptr); + assert (m_paths[index] != nullptr); int i, j; - for (i = 0; i < g_numWaypoints; i++) // delete all references to Node + for (i = 0; i < m_numWaypoints; i++) // delete all references to Node { path = m_paths[i]; - for (j = 0; j < MAX_PATH_INDEX; j++) - { - if (path->index[j] == index) - { - path->index[j] = -1; // unassign this path + for (j = 0; j < MAX_PATH_INDEX; j++) { + if (path->index[j] == index) { + path->index[j] = INVALID_WAYPOINT_INDEX; // unassign this path path->connectionFlags[j] = 0; path->distances[j] = 0; - path->connectionVelocity[j].Zero (); + path->connectionVelocity[j].nullify (); } } } - for (i = 0; i < g_numWaypoints; i++) - { + for (i = 0; i < m_numWaypoints; i++) { path = m_paths[i]; - if (path->pathNumber > index) // if pathnumber bigger than deleted node... + if (path->pathNumber > index) { // if pathnumber bigger than deleted node... path->pathNumber--; + } - for (j = 0; j < MAX_PATH_INDEX; j++) - { - if (path->index[j] > index) + for (j = 0; j < MAX_PATH_INDEX; j++) { + if (path->index[j] > index) { path->index[j]--; + } } } + eraseFromBucket (m_paths[index]->origin, index); // free deleted node delete m_paths[index]; m_paths[index] = nullptr; // rotate path array down - for (i = index; i < g_numWaypoints - 1; i++) + for (i = index; i < m_numWaypoints - 1; i++) { m_paths[i] = m_paths[i + 1]; - - g_numWaypoints--; + } + m_numWaypoints--; m_waypointDisplayTime[index] = 0; - engine.EmitSound (g_hostEntity, "weapons/mine_activate.wav"); + engine.playSound (g_hostEntity, "weapons/mine_activate.wav"); } -void Waypoint::ToggleFlags (int toggleFlag) -{ +void Waypoint::toggleFlags (int toggleFlag) { // this function allow manually changing flags - int index = FindNearest (g_hostEntity->v.origin, 50.0f); + int index = getEditorNeareset (); - if (index != -1) - { - if (m_paths[index]->flags & toggleFlag) + if (index != INVALID_WAYPOINT_INDEX) { + if (m_paths[index]->flags & toggleFlag) { m_paths[index]->flags &= ~toggleFlag; - - else if (!(m_paths[index]->flags & toggleFlag)) - { - if (toggleFlag == FLAG_SNIPER && !(m_paths[index]->flags & FLAG_CAMP)) - { - AddLogEntry (true, LL_ERROR, "Cannot assign sniper flag to waypoint #%d. This is not camp waypoint", index); + } + else if (!(m_paths[index]->flags & toggleFlag)) { + if (toggleFlag == FLAG_SNIPER && !(m_paths[index]->flags & FLAG_CAMP)) { + logEntry (true, LL_ERROR, "Cannot assign sniper flag to waypoint #%d. This is not camp waypoint", index); return; } m_paths[index]->flags |= toggleFlag; } // play "done" sound... - engine.EmitSound (g_hostEntity, "common/wpn_hudon.wav"); + engine.playSound (g_hostEntity, "common/wpn_hudon.wav"); } } -void Waypoint::SetRadius (int radius) -{ +void Waypoint::setRadius (int radius) { // this function allow manually setting the zone radius - int index = FindNearest (g_hostEntity->v.origin, 50.0f); + int index = getEditorNeareset (); - if (index != -1) - { + if (index != INVALID_WAYPOINT_INDEX) { m_paths[index]->radius = static_cast (radius); // play "done" sound... - engine.EmitSound (g_hostEntity, "common/wpn_hudon.wav"); + engine.playSound (g_hostEntity, "common/wpn_hudon.wav"); } } -bool Waypoint::IsConnected (int pointA, int pointB) -{ +bool Waypoint::isConnected (int pointA, int pointB) { // this function checks if waypoint A has a connection to waypoint B - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (m_paths[pointA]->index[i] == pointB) + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (m_paths[pointA]->index[i] == pointB) { return true; + } } return false; } -int Waypoint::GetFacingIndex (void) -{ +int Waypoint::getFacingIndex (void) { // this function finds waypoint the user is pointing at. - int pointedIndex = -1; + int pointedIndex = INVALID_WAYPOINT_INDEX; float viewCone[3] = {0.0f, 0.0f, 0.0f}; // find the waypoint the user is pointing at - for (int i = 0; i < g_numWaypoints; i++) - { - if ((m_paths[i]->origin - g_hostEntity->v.origin).GetLengthSquared () > 250000.0f) + for (int i = 0; i < m_numWaypoints; i++) { + if ((m_paths[i]->origin - g_hostEntity->v.origin).lengthSq () > 250000.0f) { continue; - + } // get the current view cone - viewCone[0] = GetShootingConeDeviation (g_hostEntity, &m_paths[i]->origin); + viewCone[0] = getShootingConeDeviation (g_hostEntity, m_paths[i]->origin); Vector bound = m_paths[i]->origin - Vector (0.0f, 0.0f, (m_paths[i]->flags & FLAG_CROUCH) ? 8.0f : 15.0f); // get the current view cone - viewCone[1] = GetShootingConeDeviation (g_hostEntity, &bound); + viewCone[1] = getShootingConeDeviation (g_hostEntity, bound); bound = m_paths[i]->origin + Vector (0.0f, 0.0f, (m_paths[i]->flags & FLAG_CROUCH) ? 8.0f : 15.0f); // get the current view cone - viewCone[2] = GetShootingConeDeviation (g_hostEntity, &bound); + viewCone[2] = getShootingConeDeviation (g_hostEntity, bound); // check if we can see it - if (viewCone[0] < 0.998f && viewCone[1] < 0.997f && viewCone[2] < 0.997f) + if (viewCone[0] < 0.998f && viewCone[1] < 0.997f && viewCone[2] < 0.997f) { continue; - + } pointedIndex = i; } return pointedIndex; } -void Waypoint::CreatePath (char dir) -{ +void Waypoint::pathCreate (char dir) { // this function allow player to manually create a path from one waypoint to another - int nodeFrom = FindNearest (g_hostEntity->v.origin, 50.0f); + int nodeFrom = getEditorNeareset (); - if (nodeFrom == -1) - { - engine.CenterPrintf ("Unable to find nearest waypoint in 50 units"); + if (nodeFrom == INVALID_WAYPOINT_INDEX) { + engine.centerPrint ("Unable to find nearest waypoint in 50 units"); return; } int nodeTo = m_facingAtIndex; - if (nodeTo < 0 || nodeTo >= g_numWaypoints) - { - if (m_cacheWaypointIndex >= 0 && m_cacheWaypointIndex < g_numWaypoints) + if (!exists (nodeTo)) { + if (exists (m_cacheWaypointIndex)) { nodeTo = m_cacheWaypointIndex; - else - { - engine.CenterPrintf ("Unable to find destination waypoint"); + } + else { + engine.centerPrint ("Unable to find destination waypoint"); return; } } - if (nodeTo == nodeFrom) - { - engine.CenterPrintf ("Unable to connect waypoint with itself"); + if (nodeTo == nodeFrom) { + engine.centerPrint ("Unable to connect waypoint with itself"); return; } - float distance = (m_paths[nodeTo]->origin - m_paths[nodeFrom]->origin).GetLength (); + float distance = (m_paths[nodeTo]->origin - m_paths[nodeFrom]->origin).length (); - if (dir == CONNECTION_OUTGOING) - AddPath (nodeFrom, nodeTo, distance); - else if (dir == CONNECTION_INCOMING) - AddPath (nodeTo, nodeFrom, distance); - else - { - AddPath (nodeFrom, nodeTo, distance); - AddPath (nodeTo, nodeFrom, distance); + if (dir == CONNECTION_OUTGOING) { + addPath (nodeFrom, nodeTo, distance); + } + else if (dir == CONNECTION_INCOMING) { + addPath (nodeTo, nodeFrom, distance); + } + else { + addPath (nodeFrom, nodeTo, distance); + addPath (nodeTo, nodeFrom, distance); } - engine.EmitSound (g_hostEntity, "common/wpn_hudon.wav"); + engine.playSound (g_hostEntity, "common/wpn_hudon.wav"); m_waypointsChanged = true; } -void Waypoint::DeletePath (void) -{ +void Waypoint::erasePath (void) { // this function allow player to manually remove a path from one waypoint to another - int nodeFrom = FindNearest (g_hostEntity->v.origin, 50.0f); + int nodeFrom = getEditorNeareset (); int index = 0; - if (nodeFrom == -1) - { - engine.CenterPrintf ("Unable to find nearest waypoint in 50 units"); + if (nodeFrom == INVALID_WAYPOINT_INDEX) { + engine.centerPrint ("Unable to find nearest waypoint in 50 units"); return; } int nodeTo = m_facingAtIndex; - if (nodeTo < 0 || nodeTo >= g_numWaypoints) - { - if (m_cacheWaypointIndex >= 0 && m_cacheWaypointIndex < g_numWaypoints) + if (!exists (nodeTo)) { + if (exists (m_cacheWaypointIndex)) { nodeTo = m_cacheWaypointIndex; - else - { - engine.CenterPrintf ("Unable to find destination waypoint"); + } + else { + engine.centerPrint ("Unable to find destination waypoint"); return; } } - for (index = 0; index < MAX_PATH_INDEX; index++) - { - if (m_paths[nodeFrom]->index[index] == nodeTo) - { + for (index = 0; index < MAX_PATH_INDEX; index++) { + if (m_paths[nodeFrom]->index[index] == nodeTo) { m_waypointsChanged = true; - m_paths[nodeFrom]->index[index] = -1; // unassigns this path + m_paths[nodeFrom]->index[index] = INVALID_WAYPOINT_INDEX; // unassigns this path m_paths[nodeFrom]->distances[index] = 0; m_paths[nodeFrom]->connectionFlags[index] = 0; - m_paths[nodeFrom]->connectionVelocity[index].Zero (); + m_paths[nodeFrom]->connectionVelocity[index].nullify (); - engine.EmitSound (g_hostEntity, "weapons/mine_activate.wav"); + engine.playSound (g_hostEntity, "weapons/mine_activate.wav"); return; } } @@ -683,96 +680,82 @@ void Waypoint::DeletePath (void) nodeFrom = nodeTo; nodeTo = index; - for (index = 0; index < MAX_PATH_INDEX; index++) - { - if (m_paths[nodeFrom]->index[index] == nodeTo) - { + for (index = 0; index < MAX_PATH_INDEX; index++) { + if (m_paths[nodeFrom]->index[index] == nodeTo) { m_waypointsChanged = true; - m_paths[nodeFrom]->index[index] = -1; // unassign this path + m_paths[nodeFrom]->index[index] = INVALID_WAYPOINT_INDEX; // unassign this path m_paths[nodeFrom]->distances[index] = 0; m_paths[nodeFrom]->connectionFlags[index] = 0; - m_paths[nodeFrom]->connectionVelocity[index].Zero (); + m_paths[nodeFrom]->connectionVelocity[index].nullify (); - engine.EmitSound (g_hostEntity, "weapons/mine_activate.wav"); + engine.playSound (g_hostEntity, "weapons/mine_activate.wav"); return; } } - engine.CenterPrintf ("There is already no path on this waypoint"); + engine.centerPrint ("There is already no path on this waypoint"); } -void Waypoint::CacheWaypoint (void) -{ - int node = FindNearest (g_hostEntity->v.origin, 50.0f); +void Waypoint::cachePoint (void) { + int node = getEditorNeareset (); - if (node == -1) - { - m_cacheWaypointIndex = -1; - engine.CenterPrintf ("Cached waypoint cleared (nearby point not found in 50 units range)"); + if (node == INVALID_WAYPOINT_INDEX) { + m_cacheWaypointIndex = INVALID_WAYPOINT_INDEX; + engine.centerPrint ("Cached waypoint cleared (nearby point not found in 50 units range)"); return; } m_cacheWaypointIndex = node; - engine.CenterPrintf ("Waypoint #%d has been put into memory", m_cacheWaypointIndex); + engine.centerPrint ("Waypoint #%d has been put into memory", m_cacheWaypointIndex); } -void Waypoint::CalculateWayzone (int index) -{ +void Waypoint::calculatePathRadius (int index) { // calculate "wayzones" for the nearest waypoint to pentedict (meaning a dynamic distance area to vary waypoint origin) Path *path = m_paths[index]; Vector start, direction; - TraceResult tr; - bool wayBlocked = false; - - if ((path->flags & (FLAG_LADDER | FLAG_GOAL | FLAG_CAMP | FLAG_RESCUE | FLAG_CROUCH)) || m_learnJumpWaypoint) - { + if ((path->flags & (FLAG_LADDER | FLAG_GOAL | FLAG_CAMP | FLAG_RESCUE | FLAG_CROUCH)) || m_learnJumpWaypoint) { path->radius = 0.0f; return; } - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (path->index[i] != -1 && (m_paths[path->index[i]]->flags & FLAG_LADDER)) - { + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (path->index[i] != INVALID_WAYPOINT_INDEX && (m_paths[path->index[i]]->flags & FLAG_LADDER)) { path->radius = 0.0f; return; } } + TraceResult tr; + bool wayBlocked = false; - for (float scanDistance = 16.0f; scanDistance < 128.0f; scanDistance += 16.0f) - { + for (float scanDistance = 32.0f; scanDistance < 128.0f; scanDistance += 16.0f) { start = path->origin; - MakeVectors (Vector::GetZero ()); + makeVectors (Vector::null ()); direction = g_pGlobals->v_forward * scanDistance; - direction = direction.ToAngles (); + direction = direction.toAngles (); path->radius = scanDistance; - for (float circleRadius = 0.0f; circleRadius < 180.0f; circleRadius += 5.0f) - { - MakeVectors (direction); + for (float circleRadius = 0.0f; circleRadius < 360.0f; circleRadius += 20.0f) { + makeVectors (direction); - Vector radiusStart = start - g_pGlobals->v_forward * scanDistance; + Vector radiusStart = start + g_pGlobals->v_forward * scanDistance; Vector radiusEnd = start + g_pGlobals->v_forward * scanDistance; - engine.TestHull (radiusStart, radiusEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); + engine.testHull (radiusStart, radiusEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); - if (tr.flFraction < 1.0f) - { - engine.TestLine (radiusStart, radiusEnd, TRACE_IGNORE_MONSTERS, nullptr, &tr); + if (tr.flFraction < 1.0f) { + engine.testLine (radiusStart, radiusEnd, TRACE_IGNORE_MONSTERS, nullptr, &tr); - if (FClassnameIs (tr.pHit, "func_door") || FClassnameIs (tr.pHit, "func_door_rotating")) - { + if (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) == 0) { path->radius = 0.0f; wayBlocked = true; break; } - wayBlocked = true; path->radius -= 16.0f; @@ -782,10 +765,9 @@ void Waypoint::CalculateWayzone (int index) Vector dropStart = start + g_pGlobals->v_forward * scanDistance; Vector dropEnd = dropStart - Vector (0.0f, 0.0f, scanDistance + 60.0f); - engine.TestHull (dropStart, dropEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); + engine.testHull (dropStart, dropEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); - if (tr.flFraction >= 1.0f) - { + if (tr.flFraction >= 1.0f) { wayBlocked = true; path->radius -= 16.0f; @@ -794,29 +776,29 @@ void Waypoint::CalculateWayzone (int index) dropStart = start - g_pGlobals->v_forward * scanDistance; dropEnd = dropStart - Vector (0.0f, 0.0f, scanDistance + 60.0f); - engine.TestHull (dropStart, dropEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); + engine.testHull (dropStart, dropEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); - if (tr.flFraction >= 1.0f) - { + if (tr.flFraction >= 1.0f) { wayBlocked = true; path->radius -= 16.0f; break; } radiusEnd.z += 34.0f; - engine.TestHull (radiusStart, radiusEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); + engine.testHull (radiusStart, radiusEnd, TRACE_IGNORE_MONSTERS, head_hull, nullptr, &tr); - if (tr.flFraction < 1.0f) - { + if (tr.flFraction < 1.0f) { wayBlocked = true; path->radius -= 16.0f; + break; } - - direction.y = AngleNormalize (direction.y + circleRadius); + direction.y = cr::angleNorm (direction.y + circleRadius); } - if (wayBlocked) + + if (wayBlocked) { break; + } } path->radius -= 16.0f; @@ -824,133 +806,120 @@ void Waypoint::CalculateWayzone (int index) path->radius = 0.0f; } -void Waypoint::SaveExperienceTab (void) -{ +void Waypoint::saveExperience (void) { ExtensionHeader header; - if (g_numWaypoints < 1 || m_waypointsChanged) + if (m_numWaypoints < 1 || m_waypointsChanged) { return; + } memset (header.header, 0, sizeof (header.header)); strcpy (header.header, FH_EXPERIENCE); header.fileVersion = FV_EXPERIENCE; - header.pointNumber = g_numWaypoints; + header.pointNumber = m_numWaypoints; - ExperienceSave *experienceSave = new ExperienceSave[g_numWaypoints * g_numWaypoints]; + ExperienceSave *experienceSave = new ExperienceSave[m_numWaypoints * m_numWaypoints]; - for (int i = 0; i < g_numWaypoints; i++) - { - for (int j = 0; j < g_numWaypoints; j++) - { - (experienceSave + (i * g_numWaypoints) + j)->team0Damage = static_cast ((g_experienceData + (i * g_numWaypoints) + j)->team0Damage >> 3); - (experienceSave + (i * g_numWaypoints) + j)->team1Damage = static_cast ((g_experienceData + (i * g_numWaypoints) + j)->team1Damage >> 3); - (experienceSave + (i * g_numWaypoints) + j)->team0Value = static_cast ((g_experienceData + (i * g_numWaypoints) + j)->team0Value / 8); - (experienceSave + (i * g_numWaypoints) + j)->team1Value = static_cast ((g_experienceData + (i * g_numWaypoints) + j)->team1Value / 8); + for (int i = 0; i < m_numWaypoints; i++) { + for (int j = 0; j < m_numWaypoints; j++) { + (experienceSave + (i * m_numWaypoints) + j)->team0Damage = static_cast ((g_experienceData + (i * m_numWaypoints) + j)->team0Damage >> 3); + (experienceSave + (i * m_numWaypoints) + j)->team1Damage = static_cast ((g_experienceData + (i * m_numWaypoints) + j)->team1Damage >> 3); + (experienceSave + (i * m_numWaypoints) + j)->team0Value = static_cast ((g_experienceData + (i * m_numWaypoints) + j)->team0Value / 8); + (experienceSave + (i * m_numWaypoints) + j)->team1Value = static_cast ((g_experienceData + (i * m_numWaypoints) + j)->team1Value / 8); } } + int result = Compress::encode (format ("%slearned/%s.exp", getDataDirectory (), engine.getMapName ()), (uint8 *)&header, sizeof (ExtensionHeader), (uint8 *) experienceSave, m_numWaypoints * m_numWaypoints * sizeof (ExperienceSave)); - int result = Compressor::Compress (FormatBuffer ("%slearned/%s.exp", GetDataDir (), engine.GetMapName ()), (uint8 *)&header, sizeof (ExtensionHeader), (uint8 *)experienceSave, g_numWaypoints * g_numWaypoints * sizeof (ExperienceSave)); + delete[] experienceSave; - delete [] experienceSave; - - if (result == -1) - { - AddLogEntry (true, LL_ERROR, "Couldn't save experience data"); + if (result == -1) { + logEntry (true, LL_ERROR, "Couldn't save experience data"); return; } } -void Waypoint::InitExperienceTab (void) -{ +void Waypoint::initExperience (void) { int i, j; - delete [] g_experienceData; + delete[] g_experienceData; g_experienceData = nullptr; - if (g_numWaypoints < 1) + if (m_numWaypoints < 1) { return; - - g_experienceData = new Experience[g_numWaypoints * g_numWaypoints]; + } + g_experienceData = new Experience[m_numWaypoints * m_numWaypoints]; g_highestDamageCT = 1; g_highestDamageT = 1; // initialize table by hand to correct values, and NOT zero it out - for (i = 0; i < g_numWaypoints; i++) - { - for (j = 0; j < g_numWaypoints; j++) - { - (g_experienceData + (i * g_numWaypoints) + j)->team0DangerIndex = -1; - (g_experienceData + (i * g_numWaypoints) + j)->team1DangerIndex = -1; - (g_experienceData + (i * g_numWaypoints) + j)->team0Damage = 0; - (g_experienceData + (i * g_numWaypoints) + j)->team1Damage = 0; - (g_experienceData + (i * g_numWaypoints) + j)->team0Value = 0; - (g_experienceData + (i * g_numWaypoints) + j)->team1Value = 0; + for (i = 0; i < m_numWaypoints; i++) { + for (j = 0; j < m_numWaypoints; j++) { + (g_experienceData + (i * m_numWaypoints) + j)->team0DangerIndex = INVALID_WAYPOINT_INDEX; + (g_experienceData + (i * m_numWaypoints) + j)->team1DangerIndex = INVALID_WAYPOINT_INDEX; + + (g_experienceData + (i * m_numWaypoints) + j)->team0Damage = 0; + (g_experienceData + (i * m_numWaypoints) + j)->team1Damage = 0; + + (g_experienceData + (i * m_numWaypoints) + j)->team0Value = 0; + (g_experienceData + (i * m_numWaypoints) + j)->team1Value = 0; } } - File fp (FormatBuffer ("%slearned/%s.exp", GetDataDir (), engine.GetMapName ()), "rb"); + File fp (format ("%slearned/%s.exp", getDataDirectory (), engine.getMapName ()), "rb"); // if file exists, read the experience data from it - if (fp.IsValid ()) - { + if (fp.isValid ()) { ExtensionHeader header; memset (&header, 0, sizeof (header)); - if (fp.Read (&header, sizeof (header)) == 0) - { - AddLogEntry (true, LL_ERROR, "Experience data damaged (unable to read header)"); + if (fp.read (&header, sizeof (header)) == 0) { + logEntry (true, LL_ERROR, "Experience data damaged (unable to read header)"); - fp.Close (); + fp.close (); return; } - fp.Close (); + fp.close (); - if (strncmp (header.header, FH_EXPERIENCE, strlen (FH_EXPERIENCE)) == 0) - { - if (header.fileVersion == FV_EXPERIENCE && header.pointNumber == g_numWaypoints) - { - ExperienceSave *experienceLoad = new ExperienceSave[g_numWaypoints * g_numWaypoints * sizeof (ExperienceSave)]; + if (strncmp (header.header, FH_EXPERIENCE, strlen (FH_EXPERIENCE)) == 0) { + if (header.fileVersion == FV_EXPERIENCE && header.pointNumber == m_numWaypoints) { + ExperienceSave *experienceLoad = new ExperienceSave[m_numWaypoints * m_numWaypoints * sizeof (ExperienceSave)]; - Compressor::Uncompress (FormatBuffer ("%slearned/%s.exp", GetDataDir (), engine.GetMapName ()), sizeof (ExtensionHeader), (uint8 *)experienceLoad, g_numWaypoints * g_numWaypoints * sizeof (ExperienceSave)); + Compress::decode (format ("%slearned/%s.exp", getDataDirectory (), engine.getMapName ()), sizeof (ExtensionHeader), (uint8 *)experienceLoad, m_numWaypoints * m_numWaypoints * sizeof (ExperienceSave)); - for (i = 0; i < g_numWaypoints; i++) - { - for (j = 0; j < g_numWaypoints; j++) - { - if (i == j) - { - (g_experienceData + (i * g_numWaypoints) + j)->team0Damage = (uint16) ((experienceLoad + (i * g_numWaypoints) + j)->team0Damage); - (g_experienceData + (i * g_numWaypoints) + j)->team1Damage = (uint16) ((experienceLoad + (i * g_numWaypoints) + j)->team1Damage); + for (i = 0; i < m_numWaypoints; i++) { + for (j = 0; j < m_numWaypoints; j++) { + if (i == j) { + (g_experienceData + (i * m_numWaypoints) + j)->team0Damage = (uint16) ((experienceLoad + (i * m_numWaypoints) + j)->team0Damage); + (g_experienceData + (i * m_numWaypoints) + j)->team1Damage = (uint16) ((experienceLoad + (i * m_numWaypoints) + j)->team1Damage); - if ((g_experienceData + (i * g_numWaypoints) + j)->team0Damage > g_highestDamageT) - g_highestDamageT = (g_experienceData + (i * g_numWaypoints) + j)->team0Damage; - - if ((g_experienceData + (i * g_numWaypoints) + j)->team1Damage > g_highestDamageCT) - g_highestDamageCT = (g_experienceData + (i * g_numWaypoints) + j)->team1Damage; + if ((g_experienceData + (i * m_numWaypoints) + j)->team0Damage > g_highestDamageT) + g_highestDamageT = (g_experienceData + (i * m_numWaypoints) + j)->team0Damage; + + if ((g_experienceData + (i * m_numWaypoints) + j)->team1Damage > g_highestDamageCT) + g_highestDamageCT = (g_experienceData + (i * m_numWaypoints) + j)->team1Damage; } - else - { - (g_experienceData + (i * g_numWaypoints) + j)->team0Damage = (uint16) ((experienceLoad + (i * g_numWaypoints) + j)->team0Damage) << 3; - (g_experienceData + (i * g_numWaypoints) + j)->team1Damage = (uint16) ((experienceLoad + (i * g_numWaypoints) + j)->team1Damage) << 3; + else { + (g_experienceData + (i * m_numWaypoints) + j)->team0Damage = (uint16) ((experienceLoad + (i * m_numWaypoints) + j)->team0Damage) << 3; + (g_experienceData + (i * m_numWaypoints) + j)->team1Damage = (uint16) ((experienceLoad + (i * m_numWaypoints) + j)->team1Damage) << 3; } - (g_experienceData + (i * g_numWaypoints) + j)->team0Value = (int16) ((experienceLoad + i * (g_numWaypoints) + j)->team0Value) * 8; - (g_experienceData + (i * g_numWaypoints) + j)->team1Value = (int16) ((experienceLoad + i * (g_numWaypoints) + j)->team1Value) * 8; + (g_experienceData + (i * m_numWaypoints) + j)->team0Value = (int16) ((experienceLoad + i * (m_numWaypoints) + j)->team0Value) * 8; + (g_experienceData + (i * m_numWaypoints) + j)->team1Value = (int16) ((experienceLoad + i * (m_numWaypoints) + j)->team1Value) * 8; } } - delete [] experienceLoad; + delete[] experienceLoad; } else - AddLogEntry (true, LL_WARNING, "Experience data damaged (wrong version, or not for this map)"); + logEntry (true, LL_WARNING, "Experience data damaged (wrong version, or not for this map)"); } } } -void Waypoint::SaveVisibilityTab (void) -{ - if (g_numWaypoints == 0) +void Waypoint::saveVisibility (void) { + if (m_numWaypoints == 0) { return; + } ExtensionHeader header; memset (&header, 0, sizeof (ExtensionHeader)); @@ -960,253 +929,211 @@ void Waypoint::SaveVisibilityTab (void) strcpy (header.header, FH_VISTABLE); header.fileVersion = FV_VISTABLE; - header.pointNumber = g_numWaypoints; + header.pointNumber = m_numWaypoints; - File fp (FormatBuffer ("%slearned/%s.vis", GetDataDir (), engine.GetMapName ()), "wb"); + File fp (format ("%slearned/%s.vis", getDataDirectory (), engine.getMapName ()), "wb"); - if (!fp.IsValid ()) - { - AddLogEntry (true, LL_ERROR, "Failed to open visibility table for writing"); + if (!fp.isValid ()) { + logEntry (true, LL_ERROR, "Failed to open visibility table for writing"); return; } - fp.Close (); + fp.close (); - Compressor::Compress (FormatBuffer ("%slearned/%s.vis", GetDataDir (), engine.GetMapName ()), (uint8 *) &header, sizeof (ExtensionHeader), (uint8 *) m_visLUT, MAX_WAYPOINTS * (MAX_WAYPOINTS / 4) * sizeof (uint8)); + Compress::encode (format ("%slearned/%s.vis", getDataDirectory (), engine.getMapName ()), (uint8 *)&header, sizeof (ExtensionHeader), (uint8 *)m_visLUT, MAX_WAYPOINTS * (MAX_WAYPOINTS / 4) * sizeof (uint8)); } -void Waypoint::InitVisibilityTab (void) -{ - if (g_numWaypoints == 0) +void Waypoint::initVisibility (void) { + if (m_numWaypoints == 0) return; ExtensionHeader header; - File fp (FormatBuffer ("%slearned/%s.vis", GetDataDir (), engine.GetMapName ()), "rb"); + File fp (format ("%slearned/%s.vis", getDataDirectory (), engine.getMapName ()), "rb"); m_redoneVisibility = false; - if (!fp.IsValid ()) - { + if (!fp.isValid ()) { m_visibilityIndex = 0; m_redoneVisibility = true; - AddLogEntry (true, LL_DEFAULT, "Vistable doesn't, vistable will be rebuilded"); + logEntry (true, LL_DEFAULT, "Vistable doesn't exists, vistable will be rebuilded"); return; } // read the header of the file - if (fp.Read (&header, sizeof (header)) == 0) - { - AddLogEntry (true, LL_ERROR, "Vistable damaged (unable to read header)"); + if (fp.read (&header, sizeof (header)) == 0) { + logEntry (true, LL_ERROR, "Vistable damaged (unable to read header)"); - fp.Close (); + fp.close (); return; } - if (strncmp (header.header, FH_VISTABLE, strlen (FH_VISTABLE)) != 0 || header.fileVersion != FV_VISTABLE || header.pointNumber != g_numWaypoints) - { + if (strncmp (header.header, FH_VISTABLE, strlen (FH_VISTABLE)) != 0 || header.fileVersion != FV_VISTABLE || header.pointNumber != m_numWaypoints) { m_visibilityIndex = 0; m_redoneVisibility = true; - AddLogEntry (true, LL_WARNING, "Visibility table damaged (wrong version, or not for this map), vistable will be rebuilded."); - fp.Close (); + logEntry (true, LL_WARNING, "Visibility table damaged (wrong version, or not for this map), vistable will be rebuilded."); + fp.close (); return; } - int result = Compressor::Uncompress (FormatBuffer ("%slearned/%s.vis", GetDataDir (), engine.GetMapName ()), sizeof (ExtensionHeader), (uint8 *) m_visLUT, MAX_WAYPOINTS * (MAX_WAYPOINTS / 4) * sizeof (uint8)); + int result = Compress::decode (format ("%slearned/%s.vis", getDataDirectory (), engine.getMapName ()), sizeof (ExtensionHeader), (uint8 *)m_visLUT, MAX_WAYPOINTS * (MAX_WAYPOINTS / 4) * sizeof (uint8)); - if (result == -1) - { + if (result == -1) { m_visibilityIndex = 0; m_redoneVisibility = true; - AddLogEntry (true, LL_ERROR, "Failed to decode vistable, vistable will be rebuilded."); - fp.Close (); + logEntry (true, LL_ERROR, "Failed to decode vistable, vistable will be rebuilded."); + fp.close (); return; } - fp.Close (); + fp.close (); } -void Waypoint::InitTypes (void) -{ - m_terrorPoints.RemoveAll (); - m_ctPoints.RemoveAll (); - m_goalPoints.RemoveAll (); - m_campPoints.RemoveAll (); - m_rescuePoints.RemoveAll (); - m_sniperPoints.RemoveAll (); - m_visitedGoals.RemoveAll (); +void Waypoint::initTypes (void) { + m_terrorPoints.clear (); + m_ctPoints.clear (); + m_goalPoints.clear (); + m_campPoints.clear (); + m_rescuePoints.clear (); + m_sniperPoints.clear (); + m_visitedGoals.clear (); - for (int i = 0; i < g_numWaypoints; i++) - { - if (m_paths[i]->flags & FLAG_TF_ONLY) - m_terrorPoints.Push (i); - else if (m_paths[i]->flags & FLAG_CF_ONLY) - m_ctPoints.Push (i); - else if (m_paths[i]->flags & FLAG_GOAL) - m_goalPoints.Push (i); - else if (m_paths[i]->flags & FLAG_CAMP) - m_campPoints.Push (i); - else if (m_paths[i]->flags & FLAG_SNIPER) - m_sniperPoints.Push (i); - else if (m_paths[i]->flags & FLAG_RESCUE) - m_rescuePoints.Push (i); + for (int i = 0; i < m_numWaypoints; i++) { + if (m_paths[i]->flags & FLAG_TF_ONLY) { + m_terrorPoints.push (i); + } + else if (m_paths[i]->flags & FLAG_CF_ONLY) { + m_ctPoints.push (i); + } + else if (m_paths[i]->flags & FLAG_GOAL) { + m_goalPoints.push (i); + } + else if (m_paths[i]->flags & FLAG_CAMP) { + m_campPoints.push (i); + } + else if (m_paths[i]->flags & FLAG_SNIPER) { + m_sniperPoints.push (i); + } + else if (m_paths[i]->flags & FLAG_RESCUE) { + m_rescuePoints.push (i); + } } } -bool Waypoint::Load (void) -{ - if (m_loadTries++ > 3) - { - sprintf (m_infoBuffer, "Giving up loading waypoint file (%s). Something went wrong.", engine.GetMapName ()); - AddLogEntry (true, LL_ERROR, m_infoBuffer); +bool Waypoint::load (void) { + initBuckets (); + + if (m_loadTries++ > 3) { + m_loadTries = 0; + + sprintf (m_infoBuffer, "Giving up loading waypoint file (%s). Something went wrong.", engine.getMapName ()); + logEntry (true, LL_ERROR, m_infoBuffer); return false; } - MemoryFile fp (GetFileName (true)); - + MemFile fp (getWaypointFilename (true)); + WaypointHeader header; memset (&header, 0, sizeof (header)); // save for faster access - const char *map = engine.GetMapName (); + const char *map = engine.getMapName (); - if (fp.IsValid ()) - { - if (fp.Read (&header, sizeof (header)) == 0) - { - sprintf (m_infoBuffer, "%s.pwf - damaged waypoint file (unable to read header)", map); - AddLogEntry (true, LL_ERROR, m_infoBuffer); + // helper function + auto throwError = [&] (const char *fmt, ...) -> bool { + va_list ap; + va_start (ap, fmt); + vsnprintf (m_infoBuffer, MAX_PRINT_BUFFER - 1, fmt, ap); + va_end (ap); - fp.Close (); - return false; + logEntry (true, LL_ERROR, m_infoBuffer); + + if (fp.isValid ()) { + fp.close (); + } + m_numWaypoints = 0; + m_waypointPaths = false; + + return false; + }; + + if (fp.isValid ()) { + if (fp.read (&header, sizeof (header)) == 0) { + return throwError ("%s.pwf - damaged waypoint file (unable to read header)", map); } - if (strncmp (header.header, FH_WAYPOINT, strlen (FH_WAYPOINT)) == 0) - { - if (header.fileVersion != FV_WAYPOINT) - { - sprintf (m_infoBuffer, "%s.pwf - incorrect waypoint file version (expected '%d' found '%ld')", map, FV_WAYPOINT, header.fileVersion); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - fp.Close (); - return false; + if (strncmp (header.header, FH_WAYPOINT, strlen (FH_WAYPOINT)) == 0) { + if (header.fileVersion != FV_WAYPOINT) { + return throwError ("%s.pwf - incorrect waypoint file version (expected '%d' found '%ld')", map, FV_WAYPOINT, header.fileVersion); } - else if (A_stricmp (header.mapName, map)) - { - sprintf (m_infoBuffer, "%s.pwf - hacked waypoint file, file name doesn't match waypoint header information (mapname: '%s', header: '%s')", map, map, header.mapName); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - fp.Close (); - return false; + else if (!!stricmp (header.mapName, map)) { + return throwError ("%s.pwf - hacked waypoint file, file name doesn't match waypoint header information (mapname: '%s', header: '%s')", map, map, header.mapName); } - else - { - if (header.pointNumber == 0 || header.pointNumber > MAX_WAYPOINTS) - { - sprintf (m_infoBuffer, "%s.pwf - waypoint file contains illegal number of waypoints (mapname: '%s', header: '%s')", map, map, header.mapName); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - fp.Close (); - return false; + else { + if (header.pointNumber == 0 || header.pointNumber > MAX_WAYPOINTS) { + return throwError ("%s.pwf - waypoint file contains illegal number of waypoints (mapname: '%s', header: '%s')", map, map, header.mapName); } - Init (); - g_numWaypoints = header.pointNumber; + init (); + m_numWaypoints = header.pointNumber; - for (int i = 0; i < g_numWaypoints; i++) - { + for (int i = 0; i < m_numWaypoints; i++) { m_paths[i] = new Path; - if (m_paths[i] == nullptr) - TerminateOnMalloc (); - - if (fp.Read (m_paths[i], sizeof (Path)) == 0) - { - sprintf (m_infoBuffer, "%s.pwf - truncated waypoint file (count: %d, need: %d)", map, i, g_numWaypoints); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - fp.Close (); - return false; + if (fp.read (m_paths[i], sizeof (Path)) == 0) { + return throwError ("%s.pwf - truncated waypoint file (count: %d, need: %d)", map, i, m_numWaypoints); } // more checks of waypoint quality - if (m_paths[i]->pathNumber < 0 || m_paths[i]->pathNumber > g_numWaypoints) - { - sprintf (m_infoBuffer, "%s.pwf - bad waypoint file (path #%d index is out of bounds)", map, i); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - fp.Close (); - return false; + if (m_paths[i]->pathNumber < 0 || m_paths[i]->pathNumber > m_numWaypoints) { + return throwError ("%s.pwf - bad waypoint file (path #%d index is out of bounds)", map, i); } + addToBucket (m_paths[i]->origin, i); } m_waypointPaths = true; } } - else - { - sprintf (m_infoBuffer, "%s.pwf is not a yapb waypoint file (header found '%s' needed '%s'", map, header.header, FH_WAYPOINT); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - fp.Close (); - return false; + else { + return throwError ("%s.pwf is not a yapb waypoint file (header found '%s' needed '%s'", map, header.header, FH_WAYPOINT); } - fp.Close (); + fp.close (); } - else - { - if (yb_waypoint_autodl_enable.GetBool ()) - { - AddLogEntry (true, LL_DEFAULT, "%s.pwf does not exist, trying to download from waypoint database", map); - WaypointDownloadError status = RequestWaypoint (); - - if (status == WDE_SOCKET_ERROR) - { - sprintf (m_infoBuffer, "%s.pwf does not exist. Can't autodownload. Socket error.", map); - AddLogEntry (true, LL_ERROR, m_infoBuffer); + else { + if (yb_waypoint_autodl_enable.boolean ()) { + logEntry (true, LL_DEFAULT, "%s.pwf does not exist, trying to download from waypoint database", map); + + switch (downloadWaypoint ()) { + case WDE_SOCKET_ERROR: + return throwError ("%s.pwf does not exist. Can't autodownload. Socket error.", map); - yb_waypoint_autodl_enable.SetInt (0); + case WDE_CONNECT_ERROR: + return throwError ("%s.pwf does not exist. Can't autodownload. Connection problems.", map); - return false; - } - else if (status == WDE_CONNECT_ERROR) - { - sprintf (m_infoBuffer, "%s.pwf does not exist. Can't autodownload. Connection problems.", map); - AddLogEntry (true, LL_ERROR, m_infoBuffer); + case WDE_NOTFOUND_ERROR: + return throwError ("%s.pwf does not exist. Can't autodownload. Waypoint not available.", map); - yb_waypoint_autodl_enable.SetInt (0); - - return false; - } - else if (status == WDE_NOTFOUND_ERROR) - { - sprintf (m_infoBuffer, "%s.pwf does not exist. Can't autodownload. Waypoint not available.", map); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - return false; - } - else - { - AddLogEntry (true, LL_DEFAULT, "%s.pwf was downloaded from waypoint database. Trying to load...", map); - return Load (); + case WDE_NOERROR: + logEntry (true, LL_DEFAULT, "%s.pwf was downloaded from waypoint database. Trying to load...", map); + return load (); } } - sprintf (m_infoBuffer, "%s.pwf does not exist", map); - AddLogEntry (true, LL_ERROR, m_infoBuffer); - - return false; + return throwError ("%s.pwf does not exist", map); } - if (strncmp (header.author, "official", 7) == 0) + if (strncmp (header.author, "official", 7) == 0) { sprintf (m_infoBuffer, "Using Official Waypoint File"); - else + } + else { sprintf (m_infoBuffer, "Using waypoint file by: %s", header.author); - - for (int i = 0; i < g_numWaypoints; i++) + } + + for (int i = 0; i < m_numWaypoints; i++) { m_waypointDisplayTime[i] = 0.0; + } - InitPathMatrix (); - InitTypes (); + initPathMatrix (); + initTypes (); m_waypointsChanged = false; g_highestKills = 1; @@ -1214,246 +1141,275 @@ bool Waypoint::Load (void) m_pathDisplayTime = 0.0f; m_arrowDisplayTime = 0.0f; - InitVisibilityTab (); - InitExperienceTab (); + initVisibility (); + initExperience (); extern ConVar yb_debug_goal; - yb_debug_goal.SetInt (-1); + yb_debug_goal.set (INVALID_WAYPOINT_INDEX); return true; } -void Waypoint::Save (void) -{ +void Waypoint::save (void) { WaypointHeader header; memset (header.mapName, 0, sizeof (header.mapName)); - memset (header.author , 0, sizeof (header.author)); + memset (header.author, 0, sizeof (header.author)); memset (header.header, 0, sizeof (header.header)); strcpy (header.header, FH_WAYPOINT); - strncpy (header.author, STRING (g_hostEntity->v.netname), SIZEOF_CHAR (header.author)); - strncpy (header.mapName, engine.GetMapName (), SIZEOF_CHAR (header.mapName)); + strncpy (header.author, STRING (g_hostEntity->v.netname), cr::bufsize (header.author)); + strncpy (header.mapName, engine.getMapName (), cr::bufsize (header.mapName)); header.mapName[31] = 0; header.fileVersion = FV_WAYPOINT; - header.pointNumber = g_numWaypoints; + header.pointNumber = m_numWaypoints; - File fp (GetFileName (), "wb"); + File fp (getWaypointFilename (), "wb"); // file was opened - if (fp.IsValid ()) - { + if (fp.isValid ()) { // write the waypoint header to the file... - fp.Write (&header, sizeof (header), 1); + fp.write (&header, sizeof (header), 1); // save the waypoint paths... - for (int i = 0; i < g_numWaypoints; i++) - fp.Write (m_paths[i], sizeof (Path)); - - fp.Close (); + for (int i = 0; i < m_numWaypoints; i++) { + fp.write (m_paths[i], sizeof (Path)); + } + fp.close (); } else - AddLogEntry (true, LL_ERROR, "Error writing '%s.pwf' waypoint file", engine.GetMapName ()); + logEntry (true, LL_ERROR, "Error writing '%s.pwf' waypoint file", engine.getMapName ()); } -const char *Waypoint::GetFileName (bool isMemoryFile) -{ - String returnFile = ""; +const char *Waypoint::getWaypointFilename (bool isMemoryFile) { + static String buffer; + buffer.format ("%s%s%s.pwf", getDataDirectory (isMemoryFile), isEmptyStr (yb_wptsubfolder.str ()) ? "" : yb_wptsubfolder.str (), engine.getMapName ()); - if (!IsNullString (yb_wptsubfolder.GetString ())) - returnFile += (String (yb_wptsubfolder.GetString ()) + "/"); - - returnFile = FormatBuffer ("%s%s%s.pwf", GetDataDir (isMemoryFile), returnFile.GetBuffer (), engine.GetMapName ()); - - if (File::Accessible (returnFile)) - return returnFile.GetBuffer (); - - return FormatBuffer ("%s%s.pwf", GetDataDir (isMemoryFile), engine.GetMapName ()); + if (File::exists (buffer)) { + return buffer.chars (); + } + return format ("%s%s.pwf", getDataDirectory (isMemoryFile), engine.getMapName ()); } -float Waypoint::GetTravelTime (float maxSpeed, const Vector &src, const Vector &origin) -{ +float Waypoint::calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin) { // this function returns 2D traveltime to a position - return (origin - src).GetLength2D () / maxSpeed; + return (origin - src).length2D () / maxSpeed; } -bool Waypoint::Reachable (Bot *bot, int index) -{ - // this function return wether bot able to reach index waypoint or not, depending on several factors. +bool Waypoint::isReachable (Bot *bot, int index) { + // this function return whether bot able to reach index waypoint or not, depending on several factors. - if (bot == nullptr || index < 0 || index >= g_numWaypoints) + if (!bot || !exists (index)) { return false; + } - Vector src = bot->pev->origin; - Vector dest = m_paths[index]->origin; + const Vector &src = bot->pev->origin; + const Vector &dst = m_paths[index]->origin; - float distance = (dest - src).GetLength (); - - // check is destination is close to us enough - if (distance >= 150.0f) + // is the destination close enough? + if ((dst - src).lengthSq () >= cr::square (320.0f)) { return false; + } + float ladderDist = (dst - src).length2D (); TraceResult tr; - engine.TestLine (src, dest, TRACE_IGNORE_MONSTERS, bot->GetEntity (), &tr); + engine.testLine (src, dst, TRACE_IGNORE_MONSTERS, bot->ent (), &tr); // if waypoint is visible from current position (even behind head)... - if (tr.flFraction >= 1.0f) - { - if (bot->pev->waterlevel == 2 || bot->pev->waterlevel == 3) + if (tr.flFraction >= 1.0f) { + + // it's should be not a problem to reach waypoint inside water... + if (bot->pev->waterlevel == 2 || bot->pev->waterlevel == 3) { return true; + } - float distance2D = (dest - src).GetLength2D (); + // check for ladder + bool nonLadder = !(m_paths[index]->flags & FLAG_LADDER) || ladderDist > 16.0f; - // is destination waypoint higher that source (62 is max jump height), or destination waypoint higher that source - if (((dest.z > src.z + 62.0f || dest.z < src.z - 100.0f) && (!(m_paths[index]->flags & FLAG_LADDER))) || distance2D >= 120.0f) - return false; // unable to reach this one + // is dest waypoint higher than src? (62 is max jump height) + if (nonLadder && dst.z > src.z + 62.0f) { + return false; // can't reach this one + } + // is dest waypoint lower than src? + if (nonLadder && dst.z < src.z - 100.0f) { + return false; // can't reach this one + } return true; } return false; } -bool Waypoint::IsNodeReachable (const Vector &src, const Vector &destination) -{ +bool Waypoint::isNodeReacheable (const Vector &src, const Vector &destination) { TraceResult tr; - float distance = (destination - src).GetLength (); + float distance = (destination - src).length (); // is the destination not close enough? - if (distance > g_autoPathDistance) + if (distance > g_autoPathDistance) { return false; + } // check if we go through a func_illusionary, in which case return false - engine.TestHull (src, destination, TRACE_IGNORE_MONSTERS, head_hull, g_hostEntity, &tr); + engine.testHull (src, destination, TRACE_IGNORE_MONSTERS, head_hull, g_hostEntity, &tr); - if (!engine.IsNullEntity (tr.pHit) && strcmp ("func_illusionary", STRING (tr.pHit->v.classname)) == 0) + if (!engine.isNullEntity (tr.pHit) && strcmp ("func_illusionary", STRING (tr.pHit->v.classname)) == 0) { return false; // don't add pathwaypoints through func_illusionaries + } // check if this waypoint is "visible"... - engine.TestLine (src, destination, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); + engine.testLine (src, destination, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); // if waypoint 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", STRING (tr.pHit->v.classname), 9) == 0) { // if it's a door check if nothing blocks behind - if (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) == 0) - { - engine.TestLine (tr.vecEndPos, destination, TRACE_IGNORE_MONSTERS, tr.pHit, &tr); + if (strncmp ("func_door", STRING (tr.pHit->v.classname), 9) == 0) { + engine.testLine (tr.vecEndPos, destination, TRACE_IGNORE_MONSTERS, tr.pHit, &tr); if (tr.flFraction < 1.0f) return false; } // check for special case of both waypoints being in water... - if (POINT_CONTENTS (src) == CONTENTS_WATER && POINT_CONTENTS (destination) == CONTENTS_WATER) - return true; // then they're reachable each other + if (g_engfuncs.pfnPointContents (src) == CONTENTS_WATER && g_engfuncs.pfnPointContents (destination) == CONTENTS_WATER) { + return true; // then they're reachable each other + } // is dest waypoint higher than src? (45 is max jump height) - if (destination.z > src.z + 45.0f) - { + if (destination.z > src.z + 45.0f) { Vector sourceNew = destination; Vector destinationNew = destination; destinationNew.z = destinationNew.z - 50.0f; // straight down 50 units - engine.TestLine (sourceNew, destinationNew, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); + engine.testLine (sourceNew, destinationNew, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); // check if we didn't hit anything, if not then it's in mid-air - if (tr.flFraction >= 1.0) + if (tr.flFraction >= 1.0) { return false; // can't reach this one + } } // check if distance to ground drops more than step height at points between source and destination... - Vector direction = (destination - src).Normalize(); // 1 unit long + Vector direction = (destination - src).normalize (); // 1 unit long Vector check = src, down = src; down.z = down.z - 1000.0f; // straight down 1000 units - engine.TestLine (check, down, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); + engine.testLine (check, down, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); float lastHeight = tr.flFraction * 1000.0f; // height from ground - distance = (destination - check).GetLength (); // distance from goal + distance = (destination - check).length (); // distance from goal - while (distance > 10.0f) - { + while (distance > 10.0f) { // move 10 units closer to the goal... check = check + (direction * 10.0f); down = check; down.z = down.z - 1000.0f; // straight down 1000 units - engine.TestLine (check, down, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); + engine.testLine (check, down, TRACE_IGNORE_MONSTERS, g_hostEntity, &tr); float height = tr.flFraction * 1000.0f; // height from ground // is the current height greater than the step height? - if (height < lastHeight - 18.0f) + if (height < lastHeight - 18.0f) { return false; // can't get there without jumping... - + } lastHeight = height; - distance = (destination - check).GetLength (); // distance from goal + distance = (destination - check).length (); // distance from goal } return true; } return false; } -void Waypoint::InitializeVisibility (void) -{ - if (!m_redoneVisibility) +void Waypoint::rebuildVisibility (void) { + if (!m_redoneVisibility) { return; + } TraceResult tr; uint8 res, shift; - for (m_visibilityIndex = 0; m_visibilityIndex < g_numWaypoints; m_visibilityIndex++) - { + for (m_visibilityIndex = 0; m_visibilityIndex < m_numWaypoints; m_visibilityIndex++) { Vector sourceDuck = m_paths[m_visibilityIndex]->origin; Vector sourceStand = m_paths[m_visibilityIndex]->origin; - if (m_paths[m_visibilityIndex]->flags & FLAG_CROUCH) - { + if (m_paths[m_visibilityIndex]->flags & FLAG_CROUCH) { sourceDuck.z += 12.0f; sourceStand.z += 18.0f + 28.0f; } - else - { + else { sourceDuck.z += -18.0f + 12.0f; sourceStand.z += 28.0f; } uint16 standCount = 0, crouchCount = 0; - for (int i = 0; i < g_numWaypoints; i++) - { + for (int i = 0; i < m_numWaypoints; i++) { // first check ducked visibility Vector dest = m_paths[i]->origin; - engine.TestLine (sourceDuck, dest, TRACE_IGNORE_MONSTERS, nullptr, &tr); + engine.testLine (sourceDuck, dest, TRACE_IGNORE_MONSTERS, nullptr, &tr); // check if line of sight to object is not blocked (i.e. visible) - if (tr.flFraction != 1.0f || tr.fStartSolid) + if (tr.flFraction != 1.0f || tr.fStartSolid) { res = 1; - else + } + else { res = 0; - + } res <<= 1; - engine.TestLine (sourceStand, dest, TRACE_IGNORE_MONSTERS, nullptr, &tr); + engine.testLine (sourceStand, dest, TRACE_IGNORE_MONSTERS, nullptr, &tr); // check if line of sight to object is not blocked (i.e. visible) - if (tr.flFraction != 1.0f || tr.fStartSolid) + if (tr.flFraction != 1.0f || tr.fStartSolid) { res |= 1; + } + if (res != 0) { + dest = m_paths[i]->origin; + + // first check ducked visibility + if (m_paths[i]->flags & FLAG_CROUCH) { + dest.z += 18.0f + 28.0f; + } + else { + dest.z += 28.0f; + } + engine.testLine (sourceDuck, dest, TRACE_IGNORE_MONSTERS, nullptr, &tr); + + // check if line of sight to object is not blocked (i.e. visible) + if (tr.flFraction != 1.0f || tr.fStartSolid) { + res |= 2; + } + else { + res &= 1; + } + engine.testLine (sourceStand, dest, TRACE_IGNORE_MONSTERS, nullptr, &tr); + + // check if line of sight to object is not blocked (i.e. visible) + if (tr.flFraction != 1.0f || tr.fStartSolid) { + res |= 1; + } + else { + res &= 2; + } + } shift = (i % 4) << 1; + m_visLUT[m_visibilityIndex][i >> 2] &= ~(3 << shift); m_visLUT[m_visibilityIndex][i >> 2] |= res << shift; - if (!(res & 2)) + if (!(res & 2)) { crouchCount++; + } - if (!(res & 1)) + if (!(res & 1)) { standCount++; + } } m_paths[m_visibilityIndex]->vis.crouch = crouchCount; m_paths[m_visibilityIndex]->vis.stand = standCount; @@ -1461,48 +1417,56 @@ void Waypoint::InitializeVisibility (void) m_redoneVisibility = false; } -bool Waypoint::IsVisible (int srcIndex, int destIndex) -{ +bool Waypoint::isVisible (int srcIndex, int destIndex) { + if (!exists (srcIndex) || !exists (destIndex)) { + return false; + } + uint8 res = m_visLUT[srcIndex][destIndex >> 2]; res >>= (destIndex % 4) << 1; return !((res & 3) == 3); } -bool Waypoint::IsDuckVisible (int srcIndex, int destIndex) -{ +bool Waypoint::isDuckVisible (int srcIndex, int destIndex) { + if (!exists (srcIndex) || !exists (destIndex)) { + return false; + } + uint8 res = m_visLUT[srcIndex][destIndex >> 2]; res >>= (destIndex % 4) << 1; return !((res & 2) == 2); } -bool Waypoint::IsStandVisible (int srcIndex, int destIndex) -{ +bool Waypoint::isStandVisible (int srcIndex, int destIndex) { + if (!exists (srcIndex) || !exists (destIndex)) { + return false; + } + uint8 res = m_visLUT[srcIndex][destIndex >> 2]; res >>= (destIndex % 4) << 1; return !((res & 1) == 1); } -const char *Waypoint::GetWaypointInfo(int id) -{ +const char *Waypoint::getInformation (int id) { // this function returns path information for waypoint pointed by id. Path *path = m_paths[id]; // if this path is null, return - if (path == nullptr) + if (path == nullptr) { return "\0"; - + } bool jumpPoint = false; // iterate through connections and find, if it's a jump path - for (int i = 0; i < MAX_PATH_INDEX; i++) - { + for (int i = 0; i < MAX_PATH_INDEX; i++) { // check if we got a valid connection - if (path->index[i] != -1 && (path->connectionFlags[i] & PATHFLAG_JUMP)) + if (path->index[i] != INVALID_WAYPOINT_INDEX && (path->connectionFlags[i] & PATHFLAG_JUMP)) { jumpPoint = true; + } } static char messageBuffer[MAX_PRINT_BUFFER]; @@ -1512,37 +1476,32 @@ const char *Waypoint::GetWaypointInfo(int id) return messageBuffer; } -void Waypoint::Think (void) -{ +void Waypoint::frame (void) { // this function executes frame of waypoint operation code. - if (engine.IsNullEntity (g_hostEntity)) + if (engine.isNullEntity (g_hostEntity)) { return; // this function is only valid on listenserver, and in waypoint enabled mode. + } float nearestDistance = 99999.0f; - int nearestIndex = -1; + int nearestIndex = INVALID_WAYPOINT_INDEX; // check if it's time to add jump waypoint - if (m_learnJumpWaypoint) - { - if (!m_endJumpPoint) - { - if (g_hostEntity->v.button & IN_JUMP) - { - Add (9); + if (m_learnJumpWaypoint) { + if (!m_endJumpPoint) { + if (g_hostEntity->v.button & IN_JUMP) { + push (9); - m_timeJumpStarted = engine.Time (); + m_timeJumpStarted = engine.timebase (); m_endJumpPoint = true; } - else - { + else { m_learnVelocity = g_hostEntity->v.velocity; m_learnPosition = g_hostEntity->v.origin; } } - else if (((g_hostEntity->v.flags & FL_ONGROUND) || g_hostEntity->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1 < engine.Time () && m_endJumpPoint) - { - Add (10); + else if (((g_hostEntity->v.flags & FL_ONGROUND) || g_hostEntity->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1f < engine.timebase () && m_endJumpPoint) { + push (10); m_learnJumpWaypoint = false; m_endJumpPoint = false; @@ -1550,125 +1509,133 @@ void Waypoint::Think (void) } // check if it's a autowaypoint mode enabled - if (g_autoWaypoint && (g_hostEntity->v.flags & (FL_ONGROUND | FL_PARTIALGROUND))) - { + if (g_autoWaypoint && (g_hostEntity->v.flags & (FL_ONGROUND | FL_PARTIALGROUND))) { // find the distance from the last used waypoint - float distance = (m_lastWaypoint - g_hostEntity->v.origin).GetLengthSquared (); + float distance = (m_lastWaypoint - g_hostEntity->v.origin).lengthSq (); - if (distance > 16384.0f) - { + if (distance > 16384.0f) { // check that no other reachable waypoints are nearby... - for (int i = 0; i < g_numWaypoints; i++) - { - if (IsNodeReachable (g_hostEntity->v.origin, m_paths[i]->origin)) - { - distance = (m_paths[i]->origin - g_hostEntity->v.origin).GetLengthSquared (); + for (int i = 0; i < m_numWaypoints; i++) { + if (isNodeReacheable (g_hostEntity->v.origin, m_paths[i]->origin)) { + distance = (m_paths[i]->origin - g_hostEntity->v.origin).lengthSq (); - if (distance < nearestDistance) + if (distance < nearestDistance) { nearestDistance = distance; + } } } // make sure nearest waypoint is far enough away... - if (nearestDistance >= 16384.0f) - Add (0); // place a waypoint here + if (nearestDistance >= 16384.0f) { + push (0); // place a waypoint here + } } } - m_facingAtIndex = GetFacingIndex (); + m_facingAtIndex = getFacingIndex (); // reset the minimal distance changed before nearestDistance = 999999.0f; // now iterate through all waypoints in a map, and draw required ones - for (int i = 0; i < g_numWaypoints; i++) - { - float distance = (m_paths[i]->origin - g_hostEntity->v.origin).GetLength (); + for (int i = 0; i < m_numWaypoints; i++) { + float distance = (m_paths[i]->origin - g_hostEntity->v.origin).length (); // check if waypoint is whitin a distance, and is visible - if (distance < 512.0f && ((::IsVisible (m_paths[i]->origin, g_hostEntity) && IsInViewCone (m_paths[i]->origin, g_hostEntity)) || !IsAlive (g_hostEntity) || distance < 128.0f)) - { + if (distance < 512.0f && ((::isVisible (m_paths[i]->origin, g_hostEntity) && isInViewCone (m_paths[i]->origin, g_hostEntity)) || !isAlive (g_hostEntity) || distance < 128.0f)) { // check the distance - if (distance < nearestDistance) - { + if (distance < nearestDistance) { nearestIndex = i; nearestDistance = distance; } - if (m_waypointDisplayTime[i] + 0.8f < engine.Time ()) - { + if (m_waypointDisplayTime[i] + 0.8f < engine.timebase ()) { float nodeHeight = 0.0f; // check the node height - if (m_paths[i]->flags & FLAG_CROUCH) + if (m_paths[i]->flags & FLAG_CROUCH) { nodeHeight = 36.0f; - else + } + else { nodeHeight = 72.0f; - + } float nodeHalfHeight = nodeHeight * 0.5f; // all waypoints are by default are green Vector nodeColor; // colorize all other waypoints - if (m_paths[i]->flags & FLAG_CAMP) + if (m_paths[i]->flags & FLAG_CAMP) { nodeColor = Vector (0, 255, 255); - else if (m_paths[i]->flags & FLAG_GOAL) + } + else if (m_paths[i]->flags & FLAG_GOAL) { nodeColor = Vector (128, 0, 255); - else if (m_paths[i]->flags & FLAG_LADDER) + } + else if (m_paths[i]->flags & FLAG_LADDER) { nodeColor = Vector (128, 64, 0); - else if (m_paths[i]->flags & FLAG_RESCUE) + } + else if (m_paths[i]->flags & FLAG_RESCUE) { nodeColor = Vector (255, 255, 255); - else + } + else { nodeColor = Vector (0, 255, 0); + } // colorize additional flags Vector nodeFlagColor = Vector (-1, -1, -1); // check the colors - if (m_paths[i]->flags & FLAG_SNIPER) + if (m_paths[i]->flags & FLAG_SNIPER) { nodeFlagColor = Vector (130, 87, 0); - else if (m_paths[i]->flags & FLAG_NOHOSTAGE) + } + else if (m_paths[i]->flags & FLAG_NOHOSTAGE) { nodeFlagColor = Vector (255, 255, 255); - else if (m_paths[i]->flags & FLAG_TF_ONLY) + } + else if (m_paths[i]->flags & FLAG_TF_ONLY) { nodeFlagColor = Vector (255, 0, 0); - else if (m_paths[i]->flags & FLAG_CF_ONLY) + } + else if (m_paths[i]->flags & FLAG_CF_ONLY) { nodeFlagColor = Vector (0, 0, 255); + } // draw node without additional flags - if (nodeFlagColor.x == -1) - engine.DrawLine (g_hostEntity, m_paths[i]->origin - Vector (0, 0, nodeHalfHeight), m_paths[i]->origin + Vector (0, 0, nodeHalfHeight), 15, 0, static_cast (nodeColor.x), static_cast (nodeColor.y), static_cast (nodeColor.z), 250, 0, 10); - else // draw node with flags - { - engine.DrawLine (g_hostEntity, m_paths[i]->origin - Vector (0, 0, nodeHalfHeight), m_paths[i]->origin - Vector (0, 0, nodeHalfHeight - nodeHeight * 0.75f), 14, 0, static_cast (nodeColor.x), static_cast (nodeColor.y), static_cast (nodeColor.z), 250, 0, 10); // draw basic path - engine.DrawLine (g_hostEntity, m_paths[i]->origin - Vector (0, 0, nodeHalfHeight - nodeHeight * 0.75f), m_paths[i]->origin + Vector (0, 0, nodeHalfHeight), 14, 0, static_cast (nodeFlagColor.x), static_cast (nodeFlagColor.y), static_cast (nodeFlagColor.z), 250, 0, 10); // draw additional path + if (nodeFlagColor.x == -1) { + engine.drawLine (g_hostEntity, m_paths[i]->origin - Vector (0, 0, nodeHalfHeight), m_paths[i]->origin + Vector (0, 0, nodeHalfHeight), 15, 0, static_cast (nodeColor.x), static_cast (nodeColor.y), static_cast (nodeColor.z), 250, 0, 10); } - m_waypointDisplayTime[i] = engine.Time (); + + // draw node with flags + else { + engine.drawLine (g_hostEntity, m_paths[i]->origin - Vector (0, 0, nodeHalfHeight), m_paths[i]->origin - Vector (0, 0, nodeHalfHeight - nodeHeight * 0.75f), 14, 0, static_cast (nodeColor.x), static_cast (nodeColor.y), static_cast (nodeColor.z), 250, 0, 10); // draw basic path + engine.drawLine (g_hostEntity, m_paths[i]->origin - Vector (0, 0, nodeHalfHeight - nodeHeight * 0.75f), m_paths[i]->origin + Vector (0, 0, nodeHalfHeight), 14, 0, static_cast (nodeFlagColor.x), static_cast (nodeFlagColor.y), static_cast (nodeFlagColor.z), 250, 0, 10); // draw additional path + } + m_waypointDisplayTime[i] = engine.timebase (); } } } - if (nearestIndex == -1) + if (nearestIndex == INVALID_WAYPOINT_INDEX) { return; + } // draw arrow to a some importaint waypoints - if ((m_findWPIndex != -1 && m_findWPIndex < g_numWaypoints) || (m_cacheWaypointIndex != -1 && m_cacheWaypointIndex < g_numWaypoints) || (m_facingAtIndex != -1 && m_facingAtIndex < g_numWaypoints)) - { + if (exists (m_findWPIndex) || exists (m_cacheWaypointIndex) || exists (m_facingAtIndex)) { // check for drawing code - if (m_arrowDisplayTime + 0.5f < engine.Time ()) - { + if (m_arrowDisplayTime + 0.5f < engine.timebase ()) { + // finding waypoint - pink arrow - if (m_findWPIndex != -1) - engine.DrawLine (g_hostEntity, g_hostEntity->v.origin, m_paths[m_findWPIndex]->origin, 10, 0, 128, 0, 128, 200, 0, 5, DRAW_ARROW); + if (m_findWPIndex != INVALID_WAYPOINT_INDEX) { + engine.drawLine (g_hostEntity, g_hostEntity->v.origin, m_paths[m_findWPIndex]->origin, 10, 0, 128, 0, 128, 200, 0, 5, DRAW_ARROW); + } // cached waypoint - yellow arrow - if (m_cacheWaypointIndex != -1) - engine.DrawLine (g_hostEntity, g_hostEntity->v.origin, m_paths[m_cacheWaypointIndex]->origin, 10, 0, 255, 255, 0, 200, 0, 5, DRAW_ARROW); + if (m_cacheWaypointIndex != INVALID_WAYPOINT_INDEX) { + engine.drawLine (g_hostEntity, g_hostEntity->v.origin, m_paths[m_cacheWaypointIndex]->origin, 10, 0, 255, 255, 0, 200, 0, 5, DRAW_ARROW); + } // waypoint user facing at - white arrow - if (m_facingAtIndex != -1) - engine.DrawLine (g_hostEntity, g_hostEntity->v.origin, m_paths[m_facingAtIndex]->origin, 10, 0, 255, 255, 255, 200, 0, 5, DRAW_ARROW); - - m_arrowDisplayTime = engine.Time (); + if (m_facingAtIndex != INVALID_WAYPOINT_INDEX) { + engine.drawLine (g_hostEntity, g_hostEntity->v.origin, m_paths[m_facingAtIndex]->origin, 10, 0, 255, 255, 255, 200, 0, 5, DRAW_ARROW); + } + m_arrowDisplayTime = engine.timebase (); } } @@ -1676,182 +1643,175 @@ void Waypoint::Think (void) Path *path = m_paths[nearestIndex]; // draw a paths, camplines and danger directions for nearest waypoint - if (nearestDistance <= 56.0f && m_pathDisplayTime <= engine.Time ()) - { - m_pathDisplayTime = engine.Time () + 1.0f; + if (nearestDistance <= 56.0f && m_pathDisplayTime <= engine.timebase ()) { + m_pathDisplayTime = engine.timebase () + 1.0f; // draw the camplines - if (path->flags & FLAG_CAMP) - { + if (path->flags & FLAG_CAMP) { Vector campSourceOrigin = path->origin + Vector (0.0f, 0.0f, 36.0f); // check if it's a source - if (path->flags & FLAG_CROUCH) + if (path->flags & FLAG_CROUCH) { campSourceOrigin = path->origin + Vector (0.0f, 0.0f, 18.0f); - + } Vector campStartOrigin = Vector (path->campStartX, path->campStartY, campSourceOrigin.z); // camp start - Vector campEndOrigin = Vector (path->campEndX, path->campEndY, campSourceOrigin.z); // camp end + Vector campEndOrigin = Vector (path->campEndX, path->campEndY, campSourceOrigin.z); // camp end // draw it now - engine.DrawLine (g_hostEntity, campSourceOrigin, campStartOrigin, 10, 0, 255, 0, 0, 200, 0, 10); - engine.DrawLine (g_hostEntity, campSourceOrigin, campEndOrigin, 10, 0, 255, 0, 0, 200, 0, 10); + engine.drawLine (g_hostEntity, campSourceOrigin, campStartOrigin, 10, 0, 255, 0, 0, 200, 0, 10); + engine.drawLine (g_hostEntity, campSourceOrigin, campEndOrigin, 10, 0, 255, 0, 0, 200, 0, 10); } // draw the connections - for (int i = 0; i < MAX_PATH_INDEX; i++) - { - if (path->index[i] == -1) + for (int i = 0; i < MAX_PATH_INDEX; i++) { + if (path->index[i] == INVALID_WAYPOINT_INDEX) { continue; - + } // jump connection - if (path->connectionFlags[i] & PATHFLAG_JUMP) - engine.DrawLine (g_hostEntity, path->origin, m_paths[path->index[i]]->origin, 5, 0, 255, 0, 128, 200, 0, 10); - else if (IsConnected (path->index[i], nearestIndex)) // twoway connection - engine.DrawLine (g_hostEntity, path->origin, m_paths[path->index[i]]->origin, 5, 0, 255, 255, 0, 200, 0, 10); - else // oneway connection - engine.DrawLine (g_hostEntity, path->origin, m_paths[path->index[i]]->origin, 5, 0, 250, 250, 250, 200, 0, 10); + if (path->connectionFlags[i] & PATHFLAG_JUMP) { + engine.drawLine (g_hostEntity, path->origin, m_paths[path->index[i]]->origin, 5, 0, 255, 0, 128, 200, 0, 10); + } + else if (isConnected (path->index[i], nearestIndex)) { // twoway connection + engine.drawLine (g_hostEntity, path->origin, m_paths[path->index[i]]->origin, 5, 0, 255, 255, 0, 200, 0, 10); + } + else { // oneway connection + engine.drawLine (g_hostEntity, path->origin, m_paths[path->index[i]]->origin, 5, 0, 250, 250, 250, 200, 0, 10); + } } // now look for oneway incoming connections - for (int i = 0; i < g_numWaypoints; i++) - { - if (IsConnected (m_paths[i]->pathNumber, path->pathNumber) && !IsConnected (path->pathNumber, m_paths[i]->pathNumber)) - engine.DrawLine (g_hostEntity, path->origin, m_paths[i]->origin, 5, 0, 0, 192, 96, 200, 0, 10); + for (int i = 0; i < m_numWaypoints; i++) { + if (isConnected (m_paths[i]->pathNumber, path->pathNumber) && !isConnected (path->pathNumber, m_paths[i]->pathNumber)) { + engine.drawLine (g_hostEntity, path->origin, m_paths[i]->origin, 5, 0, 0, 192, 96, 200, 0, 10); + } } // draw the radius circle Vector origin = (path->flags & FLAG_CROUCH) ? path->origin : path->origin - Vector (0.0f, 0.0f, 18.0f); // if radius is nonzero, draw a full circle - if (path->radius > 0.0f) - { - float squareRoot = A_sqrtf (path->radius * path->radius * 0.5f); + if (path->radius > 0.0f) { + float squareRoot = cr::sqrtf (path->radius * path->radius * 0.5f); - engine.DrawLine (g_hostEntity, origin + Vector (path->radius, 0.0f, 0.0f), origin + Vector (squareRoot, -squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (squareRoot, -squareRoot, 0.0f), origin + Vector (0.0f, -path->radius, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (path->radius, 0.0f, 0.0f), origin + Vector (squareRoot, -squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (squareRoot, -squareRoot, 0.0f), origin + Vector (0.0f, -path->radius, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (0.0f, -path->radius, 0.0f), origin + Vector (-squareRoot, -squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (-squareRoot, -squareRoot, 0.0f), origin + Vector (-path->radius, 0.0f, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (0.0f, -path->radius, 0.0f), origin + Vector (-squareRoot, -squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (-squareRoot, -squareRoot, 0.0f), origin + Vector (-path->radius, 0.0f, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (-path->radius, 0.0f, 0.0f), origin + Vector (-squareRoot, squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (-squareRoot, squareRoot, 0.0f), origin + Vector (0.0f, path->radius, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (-path->radius, 0.0f, 0.0f), origin + Vector (-squareRoot, squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (-squareRoot, squareRoot, 0.0f), origin + Vector (0.0f, path->radius, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (0.0f, path->radius, 0.0f), origin + Vector (squareRoot, squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (squareRoot, squareRoot, 0.0f), origin + Vector (path->radius, 0.0f, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (0.0f, path->radius, 0.0f), origin + Vector (squareRoot, squareRoot, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (squareRoot, squareRoot, 0.0f), origin + Vector (path->radius, 0.0f, 0.0f), 5, 0, 0, 0, 255, 200, 0, 10); } - else - { - float squareRoot = A_sqrtf (32.0f); + else { + float squareRoot = cr::sqrtf (32.0f); - engine.DrawLine (g_hostEntity, origin + Vector (squareRoot, -squareRoot, 0.0f), origin + Vector (-squareRoot, squareRoot, 0.0f), 5, 0, 255, 0, 0, 200, 0, 10); - engine.DrawLine (g_hostEntity, origin + Vector (-squareRoot, -squareRoot, 0.0f), origin + Vector (squareRoot, squareRoot, 0.0f), 5, 0, 255, 0, 0, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (squareRoot, -squareRoot, 0.0f), origin + Vector (-squareRoot, squareRoot, 0.0f), 5, 0, 255, 0, 0, 200, 0, 10); + engine.drawLine (g_hostEntity, origin + Vector (-squareRoot, -squareRoot, 0.0f), origin + Vector (squareRoot, squareRoot, 0.0f), 5, 0, 255, 0, 0, 200, 0, 10); } // draw the danger directions - if (!m_waypointsChanged) - { - if ((g_experienceData + (nearestIndex * g_numWaypoints) + nearestIndex)->team0DangerIndex != -1 && engine.GetTeam (g_hostEntity) == TERRORIST) - engine.DrawLine (g_hostEntity, path->origin, m_paths[(g_experienceData + (nearestIndex * g_numWaypoints) + nearestIndex)->team0DangerIndex]->origin, 15, 0, 255, 0, 0, 200, 0, 10, DRAW_ARROW); // draw a red arrow to this index's danger point + if (!m_waypointsChanged) { + if ((g_experienceData + (nearestIndex * m_numWaypoints) + nearestIndex)->team0DangerIndex != INVALID_WAYPOINT_INDEX && engine.getTeam (g_hostEntity) == TEAM_TERRORIST) { + engine.drawLine (g_hostEntity, path->origin, m_paths[(g_experienceData + (nearestIndex * m_numWaypoints) + nearestIndex)->team0DangerIndex]->origin, 15, 0, 255, 0, 0, 200, 0, 10, DRAW_ARROW); // draw a red arrow to this index's danger point + } - if ((g_experienceData + (nearestIndex * g_numWaypoints) + nearestIndex)->team1DangerIndex != -1 && engine.GetTeam (g_hostEntity) == CT) - engine.DrawLine (g_hostEntity, path->origin, m_paths[(g_experienceData + (nearestIndex * g_numWaypoints) + nearestIndex)->team1DangerIndex]->origin, 15, 0, 0, 0, 255, 200, 0, 10, DRAW_ARROW); // draw a blue arrow to this index's danger point + if ((g_experienceData + (nearestIndex * m_numWaypoints) + nearestIndex)->team1DangerIndex != INVALID_WAYPOINT_INDEX && engine.getTeam (g_hostEntity) == TEAM_COUNTER) { + engine.drawLine (g_hostEntity, path->origin, m_paths[(g_experienceData + (nearestIndex * m_numWaypoints) + nearestIndex)->team1DangerIndex]->origin, 15, 0, 0, 0, 255, 200, 0, 10, DRAW_ARROW); // draw a blue arrow to this index's danger point + } } - // display some information char tempMessage[4096]; // show the information about that point - int length = sprintf (tempMessage, "\n\n\n\n Waypoint Information:\n\n" - " Waypoint %d of %d, Radius: %.1f\n" - " Flags: %s\n\n", nearestIndex, g_numWaypoints, path->radius, GetWaypointInfo (nearestIndex)); - + int length = sprintf (tempMessage, + "\n\n\n\n Waypoint Information:\n\n" + " Waypoint %d of %d, Radius: %.1f\n" + " Flags: %s\n\n", + nearestIndex, m_numWaypoints, path->radius, getInformation (nearestIndex)); // if waypoint is not changed display experience also - if (!m_waypointsChanged) - { - int dangerIndexCT = (g_experienceData + nearestIndex * g_numWaypoints + nearestIndex)->team1DangerIndex; - int dangerIndexT = (g_experienceData + nearestIndex * g_numWaypoints + nearestIndex)->team0DangerIndex; + if (!m_waypointsChanged) { + int dangerIndexCT = (g_experienceData + nearestIndex * m_numWaypoints + nearestIndex)->team1DangerIndex; + int dangerIndexT = (g_experienceData + nearestIndex * m_numWaypoints + nearestIndex)->team0DangerIndex; length += sprintf (&tempMessage[length], - " Experience Info:\n" - " CT: %d / %d\n" - " T: %d / %d\n", dangerIndexCT, dangerIndexCT != -1 ? (g_experienceData + nearestIndex * g_numWaypoints + dangerIndexCT)->team1Damage : 0, dangerIndexT, dangerIndexT != -1 ? (g_experienceData + nearestIndex * g_numWaypoints + dangerIndexT)->team0Damage : 0); + " Experience Info:\n" + " CT: %d / %d\n" + " T: %d / %d\n", + dangerIndexCT, dangerIndexCT != INVALID_WAYPOINT_INDEX ? (g_experienceData + nearestIndex * m_numWaypoints + dangerIndexCT)->team1Damage : 0, dangerIndexT, dangerIndexT != INVALID_WAYPOINT_INDEX ? (g_experienceData + nearestIndex * m_numWaypoints + dangerIndexT)->team0Damage : 0); } // check if we need to show the cached point index - if (m_cacheWaypointIndex != -1) - { - length += sprintf (&tempMessage[length], "\n Cached Waypoint Information:\n\n" - " Waypoint %d of %d, Radius: %.1f\n" - " Flags: %s\n", m_cacheWaypointIndex, g_numWaypoints, m_paths[m_cacheWaypointIndex]->radius, GetWaypointInfo (m_cacheWaypointIndex)); + if (m_cacheWaypointIndex != INVALID_WAYPOINT_INDEX) { + length += sprintf (&tempMessage[length], + "\n Cached Waypoint Information:\n\n" + " Waypoint %d of %d, Radius: %.1f\n" + " Flags: %s\n", + m_cacheWaypointIndex, m_numWaypoints, m_paths[m_cacheWaypointIndex]->radius, getInformation (m_cacheWaypointIndex)); } // check if we need to show the facing point index - if (m_facingAtIndex != -1) - { - length += sprintf (&tempMessage[length], "\n Facing Waypoint Information:\n\n" - " Waypoint %d of %d, Radius: %.1f\n" - " Flags: %s\n", m_facingAtIndex, g_numWaypoints, m_paths[m_facingAtIndex]->radius, GetWaypointInfo (m_facingAtIndex)); + if (m_facingAtIndex != INVALID_WAYPOINT_INDEX) { + length += sprintf (&tempMessage[length], + "\n Facing Waypoint Information:\n\n" + " Waypoint %d of %d, Radius: %.1f\n" + " Flags: %s\n", + m_facingAtIndex, m_numWaypoints, m_paths[m_facingAtIndex]->radius, getInformation (m_facingAtIndex)); } // draw entire message - MESSAGE_BEGIN (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, g_hostEntity); - WRITE_BYTE (TE_TEXTMESSAGE); - WRITE_BYTE (4); // channel - WRITE_SHORT (FixedSigned16 (0, 1 << 13)); // x - WRITE_SHORT (FixedSigned16 (0, 1 << 13)); // y - WRITE_BYTE (0); // effect - WRITE_BYTE (255); // r1 - WRITE_BYTE (255); // g1 - WRITE_BYTE (255); // b1 - WRITE_BYTE (1); // a1 - WRITE_BYTE (255); // r2 - WRITE_BYTE (255); // g2 - WRITE_BYTE (255); // b2 - WRITE_BYTE (255); // a2 - WRITE_SHORT (0); // fadeintime - WRITE_SHORT (0); // fadeouttime - WRITE_SHORT (FixedUnsigned16 (1.1f, 1 << 8)); // holdtime - WRITE_STRING (tempMessage); - MESSAGE_END (); + MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, Vector::null (), g_hostEntity) + .writeByte (TE_TEXTMESSAGE) + .writeByte (4) // channel + .writeShort (MessageWriter::fs16 (0, 1 << 13)) // x + .writeShort (MessageWriter::fs16 (0, 1 << 13)) // y + .writeByte (0) // effect + .writeByte (255) // r1 + .writeByte (255) // g1 + .writeByte (255) // b1 + .writeByte (1) // a1 + .writeByte (255) // r2 + .writeByte (255) // g2 + .writeByte (255) // b2 + .writeByte (255) // a2 + .writeShort (0) // fadeintime + .writeShort (0) // fadeouttime + .writeShort (MessageWriter::fu16 (1.1f, 1 << 8)) // holdtime + .writeString (tempMessage); } } -bool Waypoint::IsConnected (int index) -{ - for (int i = 0; i < g_numWaypoints; i++) - { - if (i != index) - { - for (int j = 0; j < MAX_PATH_INDEX; j++) - { - if (m_paths[i]->index[j] == index) - return true; +bool Waypoint::isConnected (int index) { + for (int i = 0; i < m_numWaypoints; i++) { + if (i == index) { + continue; + } + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[i]->index[j] == index) { + return true; } } } return false; } -bool Waypoint::NodesValid (void) -{ +bool Waypoint::checkNodes (void) { int terrPoints = 0; int ctPoints = 0; int goalPoints = 0; int rescuePoints = 0; int i, j; - for (i = 0; i < g_numWaypoints; i++) - { + for (i = 0; i < m_numWaypoints; i++) { int connections = 0; - for (j = 0; j < MAX_PATH_INDEX; j++) - { - if (m_paths[i]->index[j] != -1) - { - if (m_paths[i]->index[j] > g_numWaypoints) - { - AddLogEntry (true, LL_WARNING, "Waypoint %d connected with invalid Waypoint #%d!", i, m_paths[i]->index[j]); + for (j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[i]->index[j] != INVALID_WAYPOINT_INDEX) { + if (m_paths[i]->index[j] > m_numWaypoints) { + logEntry (true, LL_WARNING, "Waypoint %d connected with invalid Waypoint #%d!", i, m_paths[i]->index[j]); return false; } connections++; @@ -1859,59 +1819,53 @@ bool Waypoint::NodesValid (void) } } - if (connections == 0) - { - if (!IsConnected (i)) - { - AddLogEntry (true, LL_WARNING, "Waypoint %d isn't connected with any other Waypoint!", i); + if (connections == 0) { + if (!isConnected (i)) { + logEntry (true, LL_WARNING, "Waypoint %d isn't connected with any other Waypoint!", i); return false; } } - if (m_paths[i]->pathNumber != i) - { - AddLogEntry (true, LL_WARNING, "Waypoint %d pathnumber differs from index!", i); + if (m_paths[i]->pathNumber != i) { + logEntry (true, LL_WARNING, "Waypoint %d pathnumber differs from index!", i); return false; } - if (m_paths[i]->flags & FLAG_CAMP) - { - if (m_paths[i]->campEndX == 0 && m_paths[i]->campEndY == 0) - { - AddLogEntry (true, LL_WARNING, "Waypoint %d Camp-Endposition not set!", i); + if (m_paths[i]->flags & FLAG_CAMP) { + if (m_paths[i]->campEndX == 0.0f && m_paths[i]->campEndY == 0.0f) { + logEntry (true, LL_WARNING, "Waypoint %d Camp-Endposition not set!", i); return false; } } - else if (m_paths[i]->flags & FLAG_TF_ONLY) + else if (m_paths[i]->flags & FLAG_TF_ONLY) { terrPoints++; - else if (m_paths[i]->flags & FLAG_CF_ONLY) + } + else if (m_paths[i]->flags & FLAG_CF_ONLY) { ctPoints++; - else if (m_paths[i]->flags & FLAG_GOAL) + } + else if (m_paths[i]->flags & FLAG_GOAL) { goalPoints++; - else if (m_paths[i]->flags & FLAG_RESCUE) + } + else if (m_paths[i]->flags & FLAG_RESCUE) { rescuePoints++; + } - for (int k = 0; k < MAX_PATH_INDEX; k++) - { - if (m_paths[i]->index[k] != -1) - { - if (m_paths[i]->index[k] >= g_numWaypoints || m_paths[i]->index[k] < -1) - { - AddLogEntry (true, LL_WARNING, "Waypoint %d - Pathindex %d out of Range!", i, k); - (*g_engfuncs.pfnSetOrigin) (g_hostEntity, m_paths[i]->origin); + for (int k = 0; k < MAX_PATH_INDEX; k++) { + if (m_paths[i]->index[k] != INVALID_WAYPOINT_INDEX) { + if (!exists (m_paths[i]->index[k])) { + logEntry (true, LL_WARNING, "Waypoint %d - Pathindex %d out of Range!", i, k); + g_engfuncs.pfnSetOrigin (g_hostEntity, m_paths[i]->origin); g_waypointOn = true; g_editNoclip = true; return false; } - else if (m_paths[i]->index[k] == i) - { - AddLogEntry (true, LL_WARNING, "Waypoint %d - Pathindex %d points to itself!", i, k); + else if (m_paths[i]->index[k] == i) { + logEntry (true, LL_WARNING, "Waypoint %d - Pathindex %d points to itself!", i, k); - if (g_waypointOn && !engine.IsDedicatedServer ()) - { - (*g_engfuncs.pfnSetOrigin) (g_hostEntity, m_paths[i]->origin); + if (g_waypointOn && !engine.isDedicated ()) { + g_engfuncs.pfnSetOrigin (g_hostEntity, m_paths[i]->origin); g_waypointOn = true; g_editNoclip = true; @@ -1922,79 +1876,60 @@ bool Waypoint::NodesValid (void) } } - if (g_mapType & MAP_CS) - { - if (rescuePoints == 0) - { - AddLogEntry (true, LL_WARNING, "You didn't set a Rescue Point!"); + if (g_mapFlags & MAP_CS) { + if (rescuePoints == 0) { + logEntry (true, LL_WARNING, "You didn't set a Rescue Point!"); return false; } } - if (terrPoints == 0) - { - AddLogEntry (true, LL_WARNING, "You didn't set any Terrorist Important Point!"); + if (terrPoints == 0) { + logEntry (true, LL_WARNING, "You didn't set any Terrorist Important Point!"); return false; } - else if (ctPoints == 0) - { - AddLogEntry (true, LL_WARNING, "You didn't set any CT Important Point!"); + else if (ctPoints == 0) { + logEntry (true, LL_WARNING, "You didn't set any CT Important Point!"); return false; } - else if (goalPoints == 0) - { - AddLogEntry (true, LL_WARNING, "You didn't set any Goal Point!"); + else if (goalPoints == 0) { + logEntry (true, LL_WARNING, "You didn't set any Goal Point!"); return false; } // perform DFS instead of floyd-warshall, this shit speedup this process in a bit - PathNode *stack = nullptr; - bool visited[MAX_WAYPOINTS]; + PathWalk walk; + Array visited; + visited.reserve (m_numWaypoints); // first check incoming connectivity, initialize the "visited" table - for (i = 0; i < g_numWaypoints; i++) + for (i = 0; i < m_numWaypoints; i++) { visited[i] = false; + } + walk.push (0); // always check from waypoint number 0 - // check from waypoint nr. 0 - stack = new PathNode; - stack->next = nullptr; - stack->index = 0; - - while (stack != nullptr) - { + while (!walk.empty ()) { // pop a node from the stack - PathNode *current = stack; - stack = stack->next; + const int current = walk.first (); + walk.shift (); - visited[current->index] = true; + visited[current] = true; - for (j = 0; j < MAX_PATH_INDEX; j++) - { - int index = m_paths[current->index]->index[j]; + for (j = 0; j < MAX_PATH_INDEX; j++) { + int index = m_paths[current]->index[j]; - if (visited[index]) - continue; // skip this waypoint as it's already visited - - if (index >= 0 && index < g_numWaypoints) - { - PathNode *pNewNode = new PathNode; - - pNewNode->next = stack; - pNewNode->index = index; - stack = pNewNode; + // skip this waypoint as it's already visited + if (exists (index) && !visited[index]) { + visited[index] = true; + walk.push (index); } } - delete current; } - for (i = 0; i < g_numWaypoints; i++) - { - if (!visited[i]) - { - AddLogEntry (true, LL_WARNING, "Path broken from Waypoint #0 to Waypoint #%d!", i); + for (i = 0; i < m_numWaypoints; i++) { + if (!visited[i]) { + logEntry (true, LL_WARNING, "Path broken from Waypoint #0 to Waypoint #%d!", i); - if (g_waypointOn && !engine.IsDedicatedServer ()) - { - (*g_engfuncs.pfnSetOrigin) (g_hostEntity, m_paths[i]->origin); + if (g_waypointOn && !engine.isDedicated ()) { + g_engfuncs.pfnSetOrigin (g_hostEntity, m_paths[i]->origin); g_waypointOn = true; g_editNoclip = true; @@ -2004,58 +1939,45 @@ bool Waypoint::NodesValid (void) } // then check outgoing connectivity - Array outgoingPaths[MAX_WAYPOINTS]; // store incoming paths for speedup + Array outgoingPaths; // store incoming paths for speedup + outgoingPaths.reserve (m_numWaypoints); - for (i = 0; i < g_numWaypoints; i++) - { - for (j = 0; j < MAX_PATH_INDEX; j++) - { - if (m_paths[i]->index[j] >= 0 && m_paths[i]->index[j] < g_numWaypoints) - outgoingPaths[m_paths[i]->index[j]].Push (i); + for (i = 0; i < m_numWaypoints; i++) { + outgoingPaths[i].reserve (m_numWaypoints + 1); + + for (j = 0; j < MAX_PATH_INDEX; j++) { + if (exists (m_paths[i]->index[j])) { + outgoingPaths[m_paths[i]->index[j]].push (i); + } } } // initialize the "visited" table - for (i = 0; i < g_numWaypoints; i++) + for (i = 0; i < m_numWaypoints; i++) { visited[i] = false; - - // check from Waypoint nr. 0 - stack = new PathNode; - stack->next = nullptr; - stack->index = 0; - - while (stack != nullptr) - { - // pop a node from the stack - PathNode *current = stack; - stack = stack->next; - - visited[current->index] = true; - - FOR_EACH_AE (outgoingPaths[current->index], p) - { - if (visited[outgoingPaths[current->index][p]]) - continue; // skip this waypoint as it's already visited - - PathNode *newNode = new PathNode; - - newNode->next = stack; - newNode->index = outgoingPaths[current->index][p]; - - stack = newNode; - } - delete current; } + walk.clear (); + walk.push (0); // always check from waypoint number 0 - for (i = 0; i < g_numWaypoints; i++) - { - if (!visited[i]) - { - AddLogEntry (true, LL_WARNING, "Path broken from Waypoint #%d to Waypoint #0!", i); + while (!walk.empty ()) { + const int current = walk.first (); // pop a node from the stack + walk.shift (); - if (g_waypointOn && !engine.IsDedicatedServer ()) - { - (*g_engfuncs.pfnSetOrigin) (g_hostEntity, m_paths[i]->origin); + for (auto &outgoing : outgoingPaths[current]) { + if (visited[outgoing]) { + continue; // skip this waypoint as it's already visited + } + visited[outgoing] = true; + walk.push (outgoing); + } + } + + for (i = 0; i < m_numWaypoints; i++) { + if (!visited[i]) { + logEntry (true, LL_WARNING, "Path broken from Waypoint #%d to Waypoint #0!", i); + + if (g_waypointOn && !engine.isDedicated ()) { + g_engfuncs.pfnSetOrigin (g_hostEntity, m_paths[i]->origin); g_waypointOn = true; g_editNoclip = true; @@ -2066,170 +1988,162 @@ bool Waypoint::NodesValid (void) return true; } -void Waypoint::InitPathMatrix (void) -{ +void Waypoint::initPathMatrix (void) { int i, j, k; - delete [] m_distMatrix; - delete [] m_pathMatrix; + delete[] m_distMatrix; + delete[] m_pathMatrix; m_distMatrix = nullptr; m_pathMatrix = nullptr; - m_distMatrix = new int [g_numWaypoints * g_numWaypoints]; - m_pathMatrix = new int [g_numWaypoints * g_numWaypoints]; + m_distMatrix = new int[m_numWaypoints * m_numWaypoints]; + m_pathMatrix = new int[m_numWaypoints * m_numWaypoints]; - if (LoadPathMatrix ()) + if (loadPathMatrix ()) { return; // matrix loaded from file + } - for (i = 0; i < g_numWaypoints; i++) - { - for (j = 0; j < g_numWaypoints; j++) - { - *(m_distMatrix + i * g_numWaypoints + j) = 999999; - *(m_pathMatrix + i * g_numWaypoints + j) = -1; + for (i = 0; i < m_numWaypoints; i++) { + for (j = 0; j < m_numWaypoints; j++) { + *(m_distMatrix + i * m_numWaypoints + j) = 999999; + *(m_pathMatrix + i * m_numWaypoints + j) = INVALID_WAYPOINT_INDEX; } } - for (i = 0; i < g_numWaypoints; i++) - { - for (j = 0; j < MAX_PATH_INDEX; j++) - { - if (m_paths[i]->index[j] >= 0 && m_paths[i]->index[j] < g_numWaypoints) - { - *(m_distMatrix + (i * g_numWaypoints) + m_paths[i]->index[j]) = m_paths[i]->distances[j]; - *(m_pathMatrix + (i * g_numWaypoints) + m_paths[i]->index[j]) = m_paths[i]->index[j]; + for (i = 0; i < m_numWaypoints; i++) { + for (j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[i]->index[j] >= 0 && m_paths[i]->index[j] < m_numWaypoints) { + *(m_distMatrix + (i * m_numWaypoints) + m_paths[i]->index[j]) = m_paths[i]->distances[j]; + *(m_pathMatrix + (i * m_numWaypoints) + m_paths[i]->index[j]) = m_paths[i]->index[j]; } } } - for (i = 0; i < g_numWaypoints; i++) - *(m_distMatrix + (i * g_numWaypoints) + i) = 0; + for (i = 0; i < m_numWaypoints; i++) { + *(m_distMatrix + (i * m_numWaypoints) + i) = 0; + } - for (k = 0; k < g_numWaypoints; k++) - { - for (i = 0; i < g_numWaypoints; i++) - { - for (j = 0; j < g_numWaypoints; j++) - { - if (*(m_distMatrix + (i * g_numWaypoints) + k) + *(m_distMatrix + (k * g_numWaypoints) + j) < (*(m_distMatrix + (i * g_numWaypoints) + j))) - { - *(m_distMatrix + (i * g_numWaypoints) + j) = *(m_distMatrix + (i * g_numWaypoints) + k) + *(m_distMatrix + (k * g_numWaypoints) + j); - *(m_pathMatrix + (i * g_numWaypoints) + j) = *(m_pathMatrix + (i * g_numWaypoints) + k); + for (k = 0; k < m_numWaypoints; k++) { + for (i = 0; i < m_numWaypoints; i++) { + for (j = 0; j < m_numWaypoints; j++) { + if (*(m_distMatrix + (i * m_numWaypoints) + k) + *(m_distMatrix + (k * m_numWaypoints) + j) < (*(m_distMatrix + (i * m_numWaypoints) + j))) { + *(m_distMatrix + (i * m_numWaypoints) + j) = *(m_distMatrix + (i * m_numWaypoints) + k) + *(m_distMatrix + (k * m_numWaypoints) + j); + *(m_pathMatrix + (i * m_numWaypoints) + j) = *(m_pathMatrix + (i * m_numWaypoints) + k); } } } } // save path matrix to file for faster access - SavePathMatrix (); + savePathMatrix (); } -void Waypoint::SavePathMatrix (void) -{ - File fp (FormatBuffer ("%slearned/%s.pmt", GetDataDir (), engine.GetMapName ()), "wb"); - - // unable to open file - if (!fp.IsValid ()) - { - AddLogEntry (false, LL_FATAL, "Failed to open file for writing"); +void Waypoint::savePathMatrix (void) { + if (m_numWaypoints < 1 || m_waypointsChanged) { return; } - // write number of waypoints - fp.Write (&g_numWaypoints, sizeof (int)); + File fp (format ("%slearned/%s.pmt", getDataDirectory (), engine.getMapName ()), "wb"); + + // unable to open file + if (!fp.isValid ()) { + logEntry (false, LL_FATAL, "Failed to open file for writing"); + return; + } + ExtensionHeader header; + + memset (header.header, 0, sizeof (header.header)); + strcpy (header.header, FH_MATRIX); + + header.fileVersion = FV_MATRIX; + header.pointNumber = m_numWaypoints; + + // write header info + fp.write (&header, sizeof (ExtensionHeader)); // write path & distance matrix - fp.Write (m_pathMatrix, sizeof (int), g_numWaypoints * g_numWaypoints); - fp.Write (m_distMatrix, sizeof (int), g_numWaypoints * g_numWaypoints); + fp.write (m_pathMatrix, sizeof (int), m_numWaypoints * m_numWaypoints); + fp.write (m_distMatrix, sizeof (int), m_numWaypoints * m_numWaypoints); // and close the file - fp.Close (); + fp.close (); } -bool Waypoint::LoadPathMatrix (void) -{ - File fp (FormatBuffer ("%slearned/%s.pmt", GetDataDir (), engine.GetMapName ()), "rb"); +bool Waypoint::loadPathMatrix (void) { + File fp (format ("%slearned/%s.pmt", getDataDirectory (), engine.getMapName ()), "rb"); // file doesn't exists return false - if (!fp.IsValid ()) - return false; - - int num = 0; - - // read number of waypoints - if (fp.Read (&num, sizeof (int)) == 0) - { - fp.Close (); + if (!fp.isValid ()) { return false; } - if (num != g_numWaypoints) - { - AddLogEntry (true, LL_WARNING, "Pathmatrix damaged (wrong version, or not for this map). Pathmatrix will be rebuilt."); - fp.Close (); + ExtensionHeader header; + memset (&header, 0, sizeof (header)); + + // read number of waypoints + if (fp.read (&header, sizeof (ExtensionHeader)) == 0) { + fp.close (); + return false; + } + + if (header.pointNumber != m_numWaypoints || header.fileVersion != FV_MATRIX) { + logEntry (true, LL_WARNING, "Pathmatrix damaged (wrong version, or not for this map). Pathmatrix will be rebuilt."); + fp.close (); return false; } // read path & distance matrixes - if (fp.Read (m_pathMatrix, sizeof (int), g_numWaypoints * g_numWaypoints) == 0) - { - fp.Close (); + if (fp.read (m_pathMatrix, sizeof (int), m_numWaypoints * m_numWaypoints) == 0) { + fp.close (); return false; } - if (fp.Read (m_distMatrix, sizeof (int), g_numWaypoints * g_numWaypoints) == 0) - { - fp.Close (); + if (fp.read (m_distMatrix, sizeof (int), m_numWaypoints * m_numWaypoints) == 0) { + fp.close (); return false; } - fp.Close (); // and close the file + fp.close (); // and close the file return true; } -int Waypoint::GetPathDistance (int srcIndex, int destIndex) -{ - if (srcIndex < 0 || srcIndex >= g_numWaypoints || destIndex < 0 || destIndex >= g_numWaypoints) +int Waypoint::getPathDist (int srcIndex, int destIndex) { + if (!exists (srcIndex) || !exists (destIndex)) { return 1; - - return *(m_distMatrix + (srcIndex * g_numWaypoints) + destIndex); + } + return *(m_distMatrix + (srcIndex * m_numWaypoints) + destIndex); } -void Waypoint::SetGoalVisited (int index) -{ - if (index < 0 || index >= g_numWaypoints) +void Waypoint::setVisited (int index) { + if (!exists (index)) { return; - - if (!IsGoalVisited (index) && (m_paths[index]->flags & FLAG_GOAL)) - m_visitedGoals.Push (index); + } + if (!isVisited (index) && (m_paths[index]->flags & FLAG_GOAL)) + m_visitedGoals.push (index); } -void Waypoint::ClearVisitedGoals (void) -{ - m_visitedGoals.RemoveAll (); +void Waypoint::clearVisited (void) { + m_visitedGoals.clear (); } -bool Waypoint::IsGoalVisited (int index) -{ - FOR_EACH_AE (m_visitedGoals, i) - { - if (m_visitedGoals[i] == index) +bool Waypoint::isVisited (int index) { + for (auto &visited : m_visitedGoals) { + if (visited == index) { return true; + } } return false; } -void Waypoint::CreateBasic (void) -{ +void Waypoint::addBasic (void) { // this function creates basic waypoint types on map edict_t *ent = nullptr; // first of all, if map contains ladder points, create it - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "func_ladder"))) - { + while (!engine.isNullEntity (ent = g_engfuncs.pfnFindEntityByString (ent, "classname", "func_ladder"))) { Vector ladderLeft = ent->v.absmin; Vector ladderRight = ent->v.absmax; ladderLeft.z = ladderRight.z; @@ -2237,8 +2151,8 @@ void Waypoint::CreateBasic (void) TraceResult tr; Vector up, down, front, back; - Vector diff = ((ladderLeft - ladderRight) ^ Vector (0.0f, 0.0f, 0.0f)).Normalize () * 15.0f; - front = back = engine.GetAbsOrigin (ent); + Vector diff = ((ladderLeft - ladderRight) ^ Vector (0.0f, 0.0f, 0.0f)).normalize () * 15.0f; + front = back = engine.getAbsPos (ent); front = front + diff; // front back = back - diff; // back @@ -2246,229 +2160,144 @@ void Waypoint::CreateBasic (void) up = down = front; down.z = ent->v.absmax.z; - engine.TestHull (down, up, TRACE_IGNORE_MONSTERS, point_hull, nullptr, &tr); + engine.testHull (down, up, TRACE_IGNORE_MONSTERS, point_hull, nullptr, &tr); - if (POINT_CONTENTS (up) == CONTENTS_SOLID || tr.flFraction != 1.0f) - { + if (g_engfuncs.pfnPointContents (up) == CONTENTS_SOLID || tr.flFraction != 1.0f) { up = down = back; down.z = ent->v.absmax.z; } - engine.TestHull (down, up - Vector (0.0f, 0.0f, 1000.0f), TRACE_IGNORE_MONSTERS, point_hull, nullptr, &tr); + engine.testHull (down, up - Vector (0.0f, 0.0f, 1000.0f), TRACE_IGNORE_MONSTERS, point_hull, nullptr, &tr); up = tr.vecEndPos; - Vector pointOrigin = up + Vector (0.0f, 0.0f, 39.0f); + Vector point = up + Vector (0.0f, 0.0f, 39.0f); m_isOnLadder = true; - do - { - if (FindNearest (pointOrigin, 50.0f) == -1) - Add (3, pointOrigin); + do { + if (getNearestNoBuckets (point, 50.0f) == INVALID_WAYPOINT_INDEX) { + push (3, point); + } + point.z += 160; + } while (point.z < down.z - 40.0f); - pointOrigin.z += 160; - } while (pointOrigin.z < down.z - 40.0f); - - pointOrigin = down + Vector (0.0f, 0.0f, 38.0f); - - if (FindNearest (pointOrigin, 50.0f) == -1) - Add (3, pointOrigin); + point = down + Vector (0.0f, 0.0f, 38.0f); + if (getNearestNoBuckets (point, 50.0f) == INVALID_WAYPOINT_INDEX) { + push (3, point); + } m_isOnLadder = false; } - // then terrortist spawnpoints - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "info_player_deathmatch"))) - { - Vector origin = engine.GetAbsOrigin (ent); + auto autoCreateForEntity = [](int type, const char *entity) { + edict_t *ent = nullptr; - if (FindNearest (origin, 50.0f) == -1) - Add (0, origin); - } + while (!engine.isNullEntity (ent = g_engfuncs.pfnFindEntityByString (ent, "classname", entity))) { + const Vector &pos = engine.getAbsPos (ent); - // then add ct spawnpoints - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "info_player_start"))) - { - Vector origin = engine.GetAbsOrigin (ent); + if (waypoints.getNearestNoBuckets (pos, 50.0f) == INVALID_WAYPOINT_INDEX) { + waypoints.push (type, pos); + } + } + }; - if (FindNearest (origin, 50.0f) == -1) - Add (0, origin); - } + autoCreateForEntity (0, "info_player_deathmatch"); // then terrortist spawnpoints + autoCreateForEntity (0, "info_player_start"); // then add ct spawnpoints + autoCreateForEntity (0, "info_vip_start"); // then vip spawnpoint + autoCreateForEntity (0, "armoury_entity"); // weapons on the map ? - // then vip spawnpoint - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "info_vip_start"))) - { - Vector origin = engine.GetAbsOrigin (ent); + autoCreateForEntity (4, "func_hostage_rescue"); // hostage rescue zone + autoCreateForEntity (4, "info_hostage_rescue"); // hostage rescue zone (same as above) - if (FindNearest (origin, 50.0f) == -1) - Add (0, origin); - } - - // hostage rescue zone - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "func_hostage_rescue"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (4, origin); - } - - // hostage rescue zone (same as above) - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "info_hostage_rescue"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (4, origin); - } - - // bombspot zone - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "func_bomb_target"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (100, origin); - } - - // bombspot zone (same as above) - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "info_bomb_target"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (100, origin); - } - - // hostage entities - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "hostage_entity"))) - { - // if already saved || moving skip it - if ((ent->v.effects & EF_NODRAW) && ent->v.speed > 0.0f) - continue; - - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50) == -1) - Add (100, origin); - } - - // vip rescue (safety) zone - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "func_vip_safetyzone"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (100, origin); - } - - // terrorist escape zone - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "func_escapezone"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (100, origin); - } - - // weapons on the map ? - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "armoury_entity"))) - { - Vector origin = engine.GetAbsOrigin (ent); - - if (FindNearest (origin, 50.0f) == -1) - Add (0, origin); - } + autoCreateForEntity (100, "func_bomb_target"); // bombspot zone + autoCreateForEntity (100, "info_bomb_target"); // bombspot zone (same as above) + autoCreateForEntity (100, "hostage_entity"); // hostage entities + autoCreateForEntity (100, "func_vip_safetyzone"); // vip rescue (safety) zone + autoCreateForEntity (100, "func_escapezone"); // terrorist escape zone } -Path *Waypoint::GetPath (int id) -{ - Path *path = m_paths[id]; - - if (path == nullptr) - return nullptr; - - return path; -} - -void Waypoint::EraseFromHardDisk (void) -{ +void Waypoint::eraseFromDisk (void) { // this function removes waypoint file from the hard disk - String deleteList[4]; - const char *map = engine.GetMapName (); + StringArray forErase; + const char *map = engine.getMapName (); + + bots.kickEveryone (true); // if we're delete waypoint, delete all corresponding to it files - deleteList[0] = FormatBuffer ("%s%s.pwf", GetDataDir (), map); // waypoint itself - deleteList[1] = FormatBuffer ("%slearned/%s.exp", GetDataDir (), map); // corresponding to waypoint experience - deleteList[3] = FormatBuffer ("%slearned/%s.vis", GetDataDir (), map); // corresponding to waypoint vistable - deleteList[3] = FormatBuffer ("%slearned/%s.pmt", GetDataDir (), map); // corresponding to waypoint path matrix + forErase.push (format ("%s%s.pwf", getDataDirectory (), map)); // waypoint itself + forErase.push (format ("%slearned/%s.exp", getDataDirectory (), map)); // corresponding to waypoint experience + forErase.push (format ("%slearned/%s.vis", getDataDirectory (), map)); // corresponding to waypoint vistable + forErase.push (format ("%slearned/%s.pmt", getDataDirectory (), map)); // corresponding to waypoint path matrix - for (int i = 0; i < 4; i++) - { - if (File::Accessible (const_cast (deleteList[i].GetBuffer ()))) - { - _unlink (deleteList[i].GetBuffer ()); - AddLogEntry (true, LL_DEFAULT, "File %s, has been deleted from the hard disk", deleteList[i].GetBuffer ()); + for (auto &item : forErase) { + if (File::exists (const_cast (item.chars ()))) { + _unlink (item.chars ()); + logEntry (true, LL_DEFAULT, "File %s, has been deleted from the hard disk", item.chars ()); + } + else { + logEntry (true, LL_ERROR, "Unable to open %s", item.chars ()); } - else - AddLogEntry (true, LL_ERROR, "Unable to open %s", deleteList[i].GetBuffer ()); } - Init (); // reintialize points + init (); // reintialize points } -const char *Waypoint::GetDataDir (bool isMemoryFile) -{ - static char buffer[256]; - - if (isMemoryFile) - sprintf (buffer, "addons/yapb/data/"); - else - sprintf (buffer, "%s/addons/yapb/data/", engine.GetModName ()); +const char *Waypoint::getDataDirectory (bool isMemoryFile) { + static String buffer; - return &buffer[0]; + if (isMemoryFile) { + buffer.assign ("addons/yapb/data/"); + } + else { + buffer.format ("%s/addons/yapb/data/", engine.getModName ()); + } + return buffer.chars (); } -void Waypoint::SetBombPosition (bool shouldReset) -{ +void Waypoint::setBombPos (bool reset, const Vector &pos) { // this function stores the bomb position as a vector - if (shouldReset) - { - m_foundBombOrigin.Zero (); + if (reset) { + m_bombPos.nullify (); g_bombPlanted = false; return; } + + if (!pos.empty ()) { + m_bombPos = pos; + return; + } edict_t *ent = nullptr; - while (!engine.IsNullEntity (ent = FIND_ENTITY_BY_CLASSNAME (ent, "grenade"))) - { - if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) - { - m_foundBombOrigin = engine.GetAbsOrigin (ent); + while (!engine.isNullEntity (ent = g_engfuncs.pfnFindEntityByString (ent, "classname", "grenade"))) { + if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) { + m_bombPos = engine.getAbsPos (ent); break; } } } -void Waypoint::SetLearnJumpWaypoint (void) -{ +void Waypoint::startLearnJump (void) { m_learnJumpWaypoint = true; } -void Waypoint::SetFindIndex (int index) -{ +void Waypoint::setSearchIndex (int index) { m_findWPIndex = index; - if (m_findWPIndex < g_numWaypoints) - engine.Printf ("Showing Direction to Waypoint #%d", m_findWPIndex); - else - m_findWPIndex = -1; + if (exists (m_findWPIndex)) { + engine.print ("Showing Direction to Waypoint #%d", m_findWPIndex); + } + else { + m_findWPIndex = INVALID_WAYPOINT_INDEX; + } } -Waypoint::Waypoint (void) -{ - CleanupPathMemory (); +Waypoint::Waypoint (void) { + cleanupPathMemory (); + + memset (m_visLUT, 0, sizeof (m_visLUT)); + memset (m_waypointDisplayTime, 0, sizeof (m_waypointDisplayTime)); + memset (m_infoBuffer, 0, sizeof (m_infoBuffer)); m_waypointPaths = false; m_endJumpPoint = false; @@ -2477,46 +2306,49 @@ Waypoint::Waypoint (void) m_waypointsChanged = false; m_timeJumpStarted = 0.0f; - m_lastJumpWaypoint = -1; - m_cacheWaypointIndex = -1; - m_findWPIndex = -1; - m_facingAtIndex = -1; + m_lastJumpWaypoint = INVALID_WAYPOINT_INDEX; + m_cacheWaypointIndex = INVALID_WAYPOINT_INDEX; + m_findWPIndex = INVALID_WAYPOINT_INDEX; + m_facingAtIndex = INVALID_WAYPOINT_INDEX; m_visibilityIndex = 0; m_loadTries = 0; - + m_numWaypoints = 0; m_isOnLadder = false; - m_pathDisplayTime = 0.0f; - m_arrowDisplayTime = 0.0f; - - m_terrorPoints.RemoveAll (); - m_ctPoints.RemoveAll (); - m_goalPoints.RemoveAll (); - m_campPoints.RemoveAll (); - m_rescuePoints.RemoveAll (); - m_sniperPoints.RemoveAll (); + m_terrorPoints.clear (); + m_ctPoints.clear (); + m_goalPoints.clear (); + m_campPoints.clear (); + m_rescuePoints.clear (); + m_sniperPoints.clear (); m_distMatrix = nullptr; m_pathMatrix = nullptr; + + for (int i = 0; i < MAX_WAYPOINTS; i++) { + m_paths[i] = nullptr; + } } -Waypoint::~Waypoint (void) -{ - CleanupPathMemory (); +Waypoint::~Waypoint (void) { + cleanupPathMemory (); - delete [] m_distMatrix; - delete [] m_pathMatrix; + delete[] m_distMatrix; + delete[] m_pathMatrix; m_distMatrix = nullptr; m_pathMatrix = nullptr; + + for (int i = 0; i < MAX_WAYPOINTS; i++) { + m_paths[i] = nullptr; + } } -void Waypoint::CloseSocketHandle (int sock) -{ -#if defined (PLATFORM_WIN32) - if (sock != -1) +void Waypoint::closeSocket (int sock) { +#if defined(PLATFORM_WIN32) + if (sock != -1) { closesocket (sock); - + } WSACleanup (); #else if (sock != -1) @@ -2524,29 +2356,27 @@ void Waypoint::CloseSocketHandle (int sock) #endif } - -WaypointDownloadError Waypoint::RequestWaypoint (void) -{ -#if defined (PLATFORM_WIN32) +WaypointDownloadError Waypoint::downloadWaypoint (void) { +#if defined(PLATFORM_WIN32) WORD requestedVersion = MAKEWORD (1, 1); WSADATA wsaData; int wsa = WSAStartup (requestedVersion, &wsaData); - if (wsa != 0) + if (wsa != 0) { return WDE_SOCKET_ERROR; + } #endif - hostent *host = gethostbyname (yb_waypoint_autodl_host.GetString ()); + hostent *host = gethostbyname (yb_waypoint_autodl_host.str ()); - if (host == nullptr) + if (host == nullptr) { return WDE_SOCKET_ERROR; + } + auto socketHandle = static_cast (socket (AF_INET, SOCK_STREAM, 0)); - int socketHandle = socket (AF_INET, SOCK_STREAM, 0); - - if (socketHandle < 0) - { - CloseSocketHandle (socketHandle); + if (socketHandle < 0) { + closeSocket (socketHandle); return WDE_SOCKET_ERROR; } sockaddr_in dest; @@ -2557,36 +2387,32 @@ WaypointDownloadError Waypoint::RequestWaypoint (void) int result = setsockopt (socketHandle, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof (timeout)); - if (result < 0) - { - CloseSocketHandle (socketHandle); + if (result < 0) { + closeSocket (socketHandle); return WDE_SOCKET_ERROR; } result = setsockopt (socketHandle, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof (timeout)); - if (result < 0) - { - CloseSocketHandle (socketHandle); + if (result < 0) { + closeSocket (socketHandle); return WDE_SOCKET_ERROR; } memset (&dest, 0, sizeof (dest)); dest.sin_family = AF_INET; dest.sin_port = htons (80); - dest.sin_addr.s_addr = inet_addr (inet_ntoa (*((struct in_addr *) host->h_addr))); + dest.sin_addr.s_addr = inet_addr (inet_ntoa (*((struct in_addr *)host->h_addr))); - if (connect (socketHandle, (struct sockaddr*) &dest, (int) sizeof (dest)) == -1) - { - CloseSocketHandle (socketHandle); + if (connect (socketHandle, (struct sockaddr *)&dest, (int)sizeof (dest)) == -1) { + closeSocket (socketHandle); return WDE_CONNECT_ERROR; } String request; - request.AssignFormat ("GET /wpdb/%s.pwf HTTP/1.0\r\nAccept: */*\r\nUser-Agent: YaPB/%s\r\nHost: %s\r\n\r\n", engine.GetMapName (), PRODUCT_VERSION, yb_waypoint_autodl_host.GetString ()); + request.format ("GET /wpdb/%s.pwf HTTP/1.0\r\nAccept: */*\r\nUser-Agent: YaPB/%s\r\nHost: %s\r\n\r\n", engine.getMapName (), PRODUCT_VERSION, yb_waypoint_autodl_host.str ()); - if (send (socketHandle, request.GetBuffer (), request.GetLength () + 1, 0) < 1) - { - CloseSocketHandle (socketHandle); + if (send (socketHandle, request.chars (), static_cast (request.length () + 1), 0) < 1) { + closeSocket (socketHandle); return WDE_SOCKET_ERROR; } @@ -2598,20 +2424,18 @@ WaypointDownloadError Waypoint::RequestWaypoint (void) int symbolsInLine = 0; // scan for the end of the header - while (!finished && recvPosition < ChunkSize) - { - if (recv (socketHandle, &buffer[recvPosition], 1, 0) == 0) + while (!finished && recvPosition < ChunkSize) { + if (recv (socketHandle, &buffer[recvPosition], 1, 0) == 0) { finished = true; + } // ugly, but whatever - if (recvPosition > 2 && buffer[recvPosition - 2] == '4' && buffer[recvPosition - 1] == '0' && buffer[recvPosition] == '4') - { - CloseSocketHandle (socketHandle); + if (recvPosition > 2 && buffer[recvPosition - 2] == '4' && buffer[recvPosition - 1] == '0' && buffer[recvPosition] == '4') { + closeSocket (socketHandle); return WDE_NOTFOUND_ERROR; } - switch (buffer[recvPosition]) - { + switch (buffer[recvPosition]) { case '\r': break; @@ -2629,30 +2453,71 @@ WaypointDownloadError Waypoint::RequestWaypoint (void) recvPosition++; } - File fp (waypoints.GetFileName (), "wb"); + File fp (waypoints.getWaypointFilename (), "wb"); - if (!fp.IsValid ()) - { - CloseSocketHandle (socketHandle); + if (!fp.isValid ()) { + closeSocket (socketHandle); return WDE_SOCKET_ERROR; } - int recvSize = 0; - do - { + do { recvSize = recv (socketHandle, buffer, ChunkSize, 0); - if (recvSize > 0) - { - fp.Write (buffer, recvSize); - fp.Flush (); + if (recvSize > 0) { + fp.write (buffer, recvSize); + fp.flush (); } } while (recvSize != 0); - fp.Close (); - CloseSocketHandle (socketHandle); + fp.close (); + closeSocket (socketHandle); return WDE_NOERROR; } + +void Waypoint::initBuckets (void) { + m_numWaypoints = 0; + + for (int x = 0; x < MAX_WAYPOINT_BUCKET_MAX; x++) { + for (int y = 0; y < MAX_WAYPOINT_BUCKET_MAX; y++) { + for (int z = 0; z < MAX_WAYPOINT_BUCKET_MAX; z++) { + m_buckets[x][y][z].reserve (MAX_WAYPOINT_BUCKET_WPTS); + m_buckets[x][y][z].clear (); + } + } + } +} + +void Waypoint::addToBucket (const Vector &pos, int index) { + const Bucket &bucket = locateBucket (pos); + m_buckets[bucket.x][bucket.y][bucket.z].push (index); +} + +void Waypoint::eraseFromBucket (const Vector &pos, int index) { + const Bucket &bucket = locateBucket (pos); + IntArray &data = m_buckets[bucket.x][bucket.y][bucket.z]; + + for (size_t i = 0; i < data.length (); i++) { + if (data[i] == index) { + data.erase (i, 1); + break; + } + } +} + +Waypoint::Bucket Waypoint::locateBucket (const Vector &pos) { + constexpr float size = 4096.0f; + + return { + cr::abs (static_cast ((pos.x + size) / MAX_WAYPOINT_BUCKET_SIZE)), + cr::abs (static_cast ((pos.y + size) / MAX_WAYPOINT_BUCKET_SIZE)), + cr::abs (static_cast ((pos.z + size) / MAX_WAYPOINT_BUCKET_SIZE)) + }; +} + +IntArray &Waypoint::getWaypointsInBucket (const Vector &pos) { + const Bucket &bucket = locateBucket (pos); + return m_buckets[bucket.x][bucket.y][bucket.z]; +}