From 5f6a1638d6ec6866db86d898557d20ed3ac8387d Mon Sep 17 00:00:00 2001 From: jeefo Date: Sun, 28 Oct 2018 19:26:36 +0300 Subject: [PATCH] 2.9 Update (#64) * Fixed bots not camping in camp spots. Fixed chatter/radio message cycling. (need feedback). Fixed CTs unable to defuse bomb. Fixed backward jump path generation in waypoint editor. Fixed autoradius in waypoint editor. Fixed autoradius menu non closeable. Fixed bots version display on entering game. Fixed memory leak in DLL-loader. (non metamod). Fixed bots able to see through smoke. Fixed team-detection on non-standard modes. Fixed quota & autovacate management. Fixed bunch of warnings from static analyzers. Greatly imporoved grenade throwing. Grealty reduced bot CPU usage. * Fixed stack-corruption in memory-file reader. Fixed A* pathfinder not working correctly. Fixed 'Tried to write to uninitialized sizebuf_t error' on bot add/remove. Minor tweaks to camping and bot enemy aiming * Make clang happy. * Fixed VIP-dection on some maps. Fixed occupied waypoint checker. Small refactoring of code with clang-format. * Fixed clang compilation * Fixed compilation. * Debugging seek cover task. Some more code cleanup. * Fixed typos. * Fixes to attack movement. Revert Z component updates. * Fixes for aiming at enemy. Fixes for seek cover & enemy hunt tasks. More refactoring. * Making clang happy once again? Tweaked grenade timers. * Revised language comparer hasher * Fixed build. * Fixed build. * Optimized headshot offsets. Optimized aim errors and enemy searches. Get rid of preprocessor macroses. Added back yb_think_fps. Use with caution. * Minor refactoring of code. * Check if tracking entity is still alive. Do not duck in crouch-goal waypoints. Remove ancient hack with failed goals. * Get rid of c++14 stuff. Tweaked isOccupiedPoint. * Changed pickup check radius. * Fix compilation. * Fixed bots ignore breakables. Fixed A* pathfinder. Fixed searching for optimal waypoints. Fixed bot waypoint reachability functions. * Get rid of new/delete calls in pathfinder. Disallow access to yapb waypoint menu on hlds. Minor refactoring. * Updated linux/osx makefile * Spaces -> Tabs in makefile. Made G++ happy. * Updated makefile. * Fixed heap buffer overflow in config loader code. * Lowered CPU usage a bit, by using "waypoint buckets" for searching closest node. Do not traceline for doors on map, that have no doors. Get rid stack-based containers. * Remove win-only debug crap. * Refactored string class. * Fix OSX compiling. * Minor refactoring of corelib to use cpp move-semantic. * Use reference for active grenades searcher. * Use system's atan2f () as it's eror rate is a bit lower. Fixed bots continuously stays in throw smoke task. Fixed bots reaching camp-goal jumping or stays they for some time. Increased radius for searching targets for grenades. Tweaked bot difficulty levels. Improved sniper weapon handling. Trying to stand still while shooting. Increase retreat level only if sniper weapon is low on ammo. Fixed predict path enemy tracking timer is always true. Allow bots to process their tasks while on freezetime, so on small maps they already aiming enemies when freezetime ends. Fied bots endlessy trying to pickup weapons. Reduce surpise timers when holding sniper weapons. New aim-at-head position calculation. Shoot delay timers are now based on bot's difficulty. Prefer smoke grenades more than flashbangs. Fixed kill-all bot command not killing one random bot for first time use. Do not play with jump velocity, now using the same as in waypoints. Tweaked shift move, so zero move speed not overriden with shift speed. Radius waypoint searcher use waypoint bucket as well. Increase reachability radius for dest waypoint, if it's currenlty owned by other bot. Partially fixed bots choice to use unreachable waypoints. * Makes OSX clang happy? * Support for compiling on llvm-win32, makefile to be done. Increased default reachability time. * Fixed build. * Move level-initialization stuff from Spawn to ServerActivate, so bot will not check init-stuff every entity spawn. This should save few CPU cycles. * Fixed active grenades list not working after changelevel. Reworked items pickup code, so every bot is not firing sphere search every time, but instead we maintain our own list of intresting entities, so every bot is accessing this list. This should lower CPU usage more a little. * Precache should be done in spawn... * Do not use engfuncs in intresting entities. * Fixed GCC-8.2 warnings. Minor refactoring. * Added some safety checks to intresting entities. Get rid of stdc++ dependency for GCC & ICC under linux. * Remove -g from release make. Cosmetic changes. * Re-enabled debug overlay. * Remove test header... * Some static-analyzer warnings fixed. Support for X64 build for FWGS Xash3D Engine. * Reduced time between selecting grenade and throwing it away. Do not try to kill bots that already dead with kill command. Several fixes from static-analyzers. * Update CI. * Fixed bot's not added after the changelevel on Xash3D engine. * Revert commit that enables movement during freezetime. Everything goes bad, when there is no freezetime.... * Bots will try to not strafe while in combat if seeing enemy only partially. Do not use "shift" when considering stuck. * Weapon price for Elite is 800$ since CS 1.6... * Fixed bots at difficulty 0 can't shoot enemies. * Cosmetic change. * Fixed assert in ClientDisconnect when quitting game while meta unloaded yapb module. Consider freed entities as invalid. * Bigger distance for throwing he grenades. * Faster version of atan2f(). * Removed accidentally left SSE header. * Cosmetic changes to enums. * Tweaked difficulty levels. Bots on Android will have a difficulty level 2 by default. Fixed LTO builds under linux. * Do not consider Android CS as legacy. * Get rid of system's math functions. Just for fun) * Use SSE2 for sincos function. * Fixed failed during load wayponts still allows to add bots, thus causing bot to crash. Added ability to delete waypoint by number using "yb wp delete". Enabled Link Time Optimization for Linux and OSX. * Fixed CI Builds. --- .gitignore | 1 + .travis.yml | 35 +- LICENSE => LICENSE.txt | 0 include/compress.h | 390 +- include/corelib.h | 5085 +++++++------------------ include/engine.h | 292 +- include/engine/const.h | 705 ++-- include/engine/eiface.h | 521 ++- include/engine/enginecallback.h | 145 - include/engine/extdll.h | 100 +- include/engine/meta_api.h | 259 +- include/engine/progdefs.h | 91 +- include/engine/util.h | 284 +- include/globals.h | 33 +- include/platform.h | 136 +- include/resource.h | 25 +- include/{core.h => yapb.h} | 1347 +++---- project/makefile | 30 +- project/yapb.rc | 2 +- project/yapb.sln | 7 +- project/yapb.vcxproj | 9 +- project/yapb.vcxproj.filters | 13 +- source/basecode.cpp | 6289 +++++++++++++++---------------- source/chatlib.cpp | 455 ++- source/combat.cpp | 1565 ++++---- source/engine.cpp | 942 ++--- source/globals.cpp | 487 ++- source/interface.cpp | 3160 ++++++++-------- source/manager.cpp | 1651 ++++---- source/navigate.cpp | 3295 ++++++++-------- source/support.cpp | 847 ++--- source/waypoint.cpp | 2535 ++++++------- 32 files changed, 13626 insertions(+), 17110 deletions(-) rename LICENSE => LICENSE.txt (100%) delete mode 100644 include/engine/enginecallback.h rename include/{core.h => yapb.h} (51%) 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]; +}