diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..90cbab7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,114 @@ +name: Build + +on: ["push", "workflow_dispatch"] + +jobs: + build_main: + name: Build for ${{ matrix.os_short }} + runs-on: ${{ matrix.os_version }} + + # skip build on '[ci skip]' + if: "!contains(github.event.head_commit.message, '[ci skip]')" + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-22.04 + - windows-x32 + include: + - os: ubuntu-22.04 + os_short: linux + os_version: ubuntu-22.04 + package_ext: tar.gz + dbg_ext: dbg + cc: clang + cxx: clang++ + vs_arch: unused + am_arch: x86 + + - os: windows-x32 + os_short: win32 + os_version: windows-latest + package_ext: zip + dbg_ext: pdb + cc: not-used + cxx: not-used + vs_arch: x32 + am_arch: x86 + + + steps: + - name: Install (Linux) + if: runner.os == 'Linux' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y clang g++-multilib + echo "CC=clang" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + + - name: Add msbuild to PATH (Windows) + if: runner.os == 'Windows' + uses: microsoft/setup-msbuild@v2 + + - name: Install (Windows) + if: runner.os == 'Windows' + shell: cmd + run: | + :: See https://github.com/microsoft/vswhere/wiki/Find-VC + for /f "usebackq delims=*" %%i in (`vswhere -latest -property installationPath`) do ( + call "%%i"\Common7\Tools\vsdevcmd.bat -arch=${{ matrix.vs_arch }} -host_arch=x64 + ) + + :: Loop over all environment variables and make them global. + for /f "delims== tokens=1,2" %%a in ('set') do ( + echo>>"%GITHUB_ENV%" %%a=%%b + ) + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Setup ambuild + run: | + python -m pip install --upgrade pip + python -m pip install wheel + pip install git+https://github.com/alliedmodders/ambuild + + - name: Fetch RealBot + uses: actions/checkout@v4 + with: + path: realbot + submodules: recursive + + - name: Build Files + shell: bash + working-directory: realbot + run: | + mkdir post + cd post + + export AM_ARCH=${{ matrix.am_arch }} + + python3 ../configure.py --symbol-files --enable-optimize + + ambuild + + - uses: benjlevesque/short-sha@v2.2 + id: short-sha + + - name: Upload Binary + uses: actions/upload-artifact@v4 + with: + name: realbot-${{ matrix.os_short }}-${{ steps.short-sha.outputs.sha }} + path: | + realbot/post/* + + - name: Upload Debug Symbols + uses: actions/upload-artifact@v4 + with: + name: realbot-dbgsym-${{ matrix.os_short }}-${{ steps.short-sha.outputs.sha }} + path: | + realbot/post/**/*.${{ matrix.dbg_ext }} diff --git a/.gitignore b/.gitignore index 2efd4e6..ab58d96 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ x64/ x86/ build/ +build_linux/ bld/ [Bb]in/ [Oo]bj/ @@ -216,3 +217,5 @@ CMakeLists.txt cmake-build-debug/ *.o +realbot_mm.so +apg-load diff --git a/AMBuildScript b/AMBuildScript new file mode 100644 index 0000000..a5d68dd --- /dev/null +++ b/AMBuildScript @@ -0,0 +1,161 @@ +# AMBuildScript for RealBot, written by Anonymous Player +# vim: set sts=4 ts=8 sw=4 tw=99 et ft=python: +import os, sys + +builder.cxx = builder.DetectCxx(target_arch = 'x86') + +# Include search paths +include_paths = [ + os.path.join(builder.currentSourcePath, 'dependencies', 'metamod-hl1', 'metamod'), + os.path.join(builder.currentSourcePath, 'dependencies', 'hlsdk', 'common'), + os.path.join(builder.currentSourcePath, 'dependencies', 'hlsdk', 'public'), + os.path.join(builder.currentSourcePath, 'dependencies', 'hlsdk', 'dlls'), + os.path.join(builder.currentSourcePath, 'dependencies', 'hlsdk', 'engine'), + os.path.join(builder.currentSourcePath, 'dependencies', 'hlsdk', 'game_shared'), + os.path.join(builder.currentSourcePath, 'dependencies', 'hlsdk', 'pm_shared'), +] + +# Compiler setup +builder.cxx.defines += [ + 'HAVE_STDINT_H', + ] + +if builder.cxx.like('gcc'): + builder.cxx.defines += [ + 'stricmp=strcasecmp', + 'strcmpi=strcasecmp' + ] + + builder.cxx.c_only_flags += ['-std=gnu99'] +if builder.cxx.target.platform == 'linux': + # Linux defines + builder.cxx.defines += ['_LINUX', 'POSIX', 'LINUX', 'linux'] + # Linux compiler C flags + builder.cxx.cflags += [ + '-mtune=generic', + '-pipe', + '-fPIC', + '-msse4.2', + '-mfpmath=sse', + '-fno-strict-aliasing', + '-Wall', + '-Werror', + '-Wno-uninitialized', + '-Wno-unused', + '-Wno-switch', + '-Wno-format', + '-Wno-format-security', + '-Wno-unknown-attributes', + '-Wno-logical-op-parentheses', + '-Wno-return-stack-address', + '-m32', + ] + # Linux compiler C++ flags + builder.cxx.cxxflags += [ + '-Wno-invalid-offsetof', + '-Wno-write-strings', + '-std=c++17', + ] + # Linux linker flags + builder.cxx.linkflags += ['-m32', '-ldl', '-lm', '-flto'] +elif builder.cxx.target.platform == 'windows': + # Windows defines + builder.cxx.defines += [ + '_CRT_SECURE_NO_DEPRECATE', + '_CRT_SECURE_NO_WARNINGS', + '_CRT_NONSTDC_NO_DEPRECATE', + 'NOMINMAX', + 'WIN32', + '_WINDOWS' + ] + # Windows compiler C flags + builder.cxx.cflags += [] + # Windows compiler C++ flags + builder.cxx.cxxflags += [ + '/std:c++17', + '/arch:SSE2', + '/fp:precise', + '/Qspectre', + '/EHsc' + ] + # Windows linker flags + builder.cxx.linkflags += [ + '/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1', + '/SECTION:.data,RW', + '/MACHINE:X86' + ] + +# Compiler options for optimization ( --enable-optimize ) +if builder.options.optimize: + # Shared optimization definitions + builder.cxx.defines += ['NDEBUG'] + if builder.cxx.target.platform == 'linux': + # Linux optimization flags + builder.cxx.cflags += ['-O3'] + # Linux optimization link flags #TODO + #builder.cxx.linkflags += ['-flto'] + elif builder.cxx.target.platform == 'windows': + # Windows optimization flags - /Ob3 needs to be after /Ox, enables aggressive function inling -caxanga334 + builder.cxx.cflags += ['/Ox', '/Zo', '/Ob3', '/GL'] + # Windows optimization link flags + builder.cxx.linkflags += ['/OPT:ICF', '/OPT:REF', '/LTCG'] + # This needs to be after our optimization flags which could otherwise disable it. + builder.cxx.cflags += ['/Oy-'] + +# Compiler options for debugging ( --enable-debug ) +if builder.options.debug: + # Shared debug definitions + builder.cxx.defines += ['DEBUG', '_DEBUG'] + if builder.cxx.target.platform == 'linux': + # Linux debug flags + builder.cxx.cflags += ['-g3', '-Og', '-ggdb3'] + elif builder.cxx.target.platform == 'windows': + # Windows debug flags + builder.cxx.cflags += ['/Od', '/RTC1', '/MTd'] + # Windows debug link flags + builder.cxx.linkflags += ['/NODEFAULTLIB:libcmt'] + +# Handle --enable-static-lib and --enable-shared-lib +if builder.cxx.target.platform == 'linux': + if builder.options.staticlib == '1': + builder.cxx.linkflags += [ + '-static-libgcc', + '-static-libstdc++' + ] + elif builder.options.sharedlib == '1': + builder.cxx.linkflags += [ + '-shared-libgcc', + ] + +library = builder.cxx.Library('realbot_mm') + +library.compiler.includes += include_paths + +library.sources += [ + 'bot.cpp', + 'bot_buycode.cpp', + 'bot_client.cpp', + 'bot_func.cpp', + 'bot_navigate.cpp', + 'build.cpp', + 'ChatEngine.cpp', + 'dll.cpp', + 'engine.cpp', + 'game.cpp', + 'IniParser.cpp', + 'NodeMachine.cpp', + 'util.cpp', +] + +#builder.RunBuildScripts( +# [ +# 'Bsp2Rbn/AMBuilder', +# ], +# #{ 'AMXX': AMXX } +#) + +# +# Run scripts, add binaries +# + +builder.Add(library) \ No newline at end of file diff --git a/Bsp2Rbn/AMBuilder b/Bsp2Rbn/AMBuilder new file mode 100644 index 0000000..aec23f5 --- /dev/null +++ b/Bsp2Rbn/AMBuilder @@ -0,0 +1,24 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os.path + +#binary = AMXX.MetaModule(builder, 'bsp2rbn') + +binary.sources = [ + '../../public/sdk/amxxmodule.cpp', + 'CRank.cpp', + 'CMisc.cpp', + 'NBase.cpp', + 'NRank.cpp', + 'usermsg.cpp', + 'Utils.cpp', + 'moduleconfig.cpp', +] + +if builder.target_platform == 'windows': + #binary.sources += ['version.rc'] + binary.compiler.linkflags += [ + '/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1', + '/SECTION:.data,RW', + ] + +##AMXX.modules += [builder.Add(binary)] diff --git a/Bsp2Rbn/Bsp2Rbn.sln b/Bsp2Rbn/Bsp2Rbn.sln new file mode 100644 index 0000000..a9af632 --- /dev/null +++ b/Bsp2Rbn/Bsp2Rbn.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32414.318 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Bsp2Rbn", "Bsp2Rbn.vcxproj", "{85813A68-52FB-4F75-91BA-DD3B6C50FD68}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Debug|x64.ActiveCfg = Debug|x64 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Debug|x64.Build.0 = Debug|x64 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Debug|x86.ActiveCfg = Debug|Win32 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Debug|x86.Build.0 = Debug|Win32 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Release|x64.ActiveCfg = Release|x64 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Release|x64.Build.0 = Release|x64 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Release|x86.ActiveCfg = Release|Win32 + {85813A68-52FB-4F75-91BA-DD3B6C50FD68}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E7378DCB-1E54-43A4-B950-59F6E738F898} + EndGlobalSection +EndGlobal diff --git a/Bsp2Rbn/Bsp2Rbn.vcxproj b/Bsp2Rbn/Bsp2Rbn.vcxproj new file mode 100644 index 0000000..0c9f2ae --- /dev/null +++ b/Bsp2Rbn/Bsp2Rbn.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 + Win32Proj + {85813a68-52fb-4f75-91ba-dd3b6c50fd68} + Bsp2Rbn + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v120 + false + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + ..\;..\dependencies\metamod-hl1\metamod;..\dependencies\hlsdk\common;..\dependencies\hlsdk\engine;..\dependencies\hlsdk\dlls;..\dependencies\hlsdk\pm_shared;..\dependencies\hlsdk\public;%(AdditionalIncludeDirectories) + Default + + + Console + false + true + true + false + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + ..\;..\dependencies\metamod-hl1\metamod;..\dependencies\hlsdk\common;..\dependencies\hlsdk\engine;..\dependencies\hlsdk\dlls;..\dependencies\hlsdk\pm_shared;..\dependencies\hlsdk\public;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + + + + \ No newline at end of file diff --git a/Bsp2Rbn/Bsp2Rbn.vcxproj.filters b/Bsp2Rbn/Bsp2Rbn.vcxproj.filters new file mode 100644 index 0000000..c6780bb --- /dev/null +++ b/Bsp2Rbn/Bsp2Rbn.vcxproj.filters @@ -0,0 +1,87 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Bsp2Rbn/DrawNodes.c b/Bsp2Rbn/DrawNodes.c index 1d0cd1c..526f196 100644 --- a/Bsp2Rbn/DrawNodes.c +++ b/Bsp2Rbn/DrawNodes.c @@ -1,464 +1,464 @@ -// Based on Pierre-Marie Batty bmpfile.cpp - -// Tuned for Realbot .RBN files by evyncke@students.hec.be, June 2004 - -// RACC - AI development project for first-person shooter games derived from Valve's Half-Life -// (http://www.racc-ai.com/) -// -// The game to engine interfacing code is based on the work done by Jeffrey 'botman' Broome -// (http://planethalflife.com/botman/) -// -// This project is partially based on the work done by Eric Bieschke in his BSDbot -// (http://gamershomepage.com/csbot/) -// -// This project is partially based on the work done by Brendan "Spyro" McCarthy in his ODD Bot -// (http://oddbot.hlfusion.com/) -// -// This project is partially based on the work done by Alistair 'eLiTe' Stewart in his TEAMbot -// (http://www.planethalflife.com/teambot/) -// -// The BMP writing functions in this file come primarily from botman's BSP slicer utility -// (http://planethalflife.com/botman/) -// -// Rational Autonomous Cybernetic Commandos AI -// -// bmpfile.cpp -// - -#include -#include -#include -#include -#include -#include -#include -#include - -extern char * Version ; - -// width and height of the debug bitmap image -#define DEBUG_BMP_WIDTH 2048 -#define DEBUG_BMP_HEIGHT 2048 - -float scalex, scaley, scale; -char *bmp_buffer; - -#include "../bot.h" -#include "../NodeMachine.h" - -extern char * Version ; - -/* Copy from NodeMachine.cpp by Stefan Hendricks */ - -tNode Nodes[MAX_NODES]; // Nodes -tInfoNode InfoNodes[MAX_NODES]; // Info for Nodes -tMeredian Meredians[MAX_MEREDIANS][MAX_MEREDIANS]; // Meredian lookup search for Nodes -int iMaxUsedNodes; -float maxx, maxy, minx, miny ; - -void load (char * mapname) -{ - char filename[256]; - int i, n; - - // Set Directory name - strcpy (filename, mapname); - strcat (filename, ".rbn"); // nodes file - - FILE *rbl; - rbl = fopen (filename, "rb"); - - if (rbl != NULL) - { - int iVersion ; - fread (&iVersion, sizeof (int), 1, rbl); - - // Version 1.0 - if (iVersion == FILE_NODE_VER1) - { - for (i = 0; i < MAX_NODES; i++) - { - fread (&Nodes[i].origin, sizeof (Vector), 1, rbl); - for (n = 0; n < MAX_NEIGHBOURS; n++) - { - fread (&Nodes[i].iNeighbour[n], sizeof (int), 1, rbl); - } - - // save bit flags - fread (&Nodes[i].iNodeBits, sizeof (int), 1, rbl); - - if (Nodes[i].origin != Vector (9999, 9999, 9999)) - iMaxUsedNodes = i; - } - } - } else { - fprintf(stderr,"Cannot open file %s\n",filename) ; - exit ; - } - printf("%d nodes loaded out of %d.\n",iMaxUsedNodes,MAX_NODES) ; -} - -void InitDebugBitmap (void) -{ - // this function allocates memory and clears the debug bitmap buffer - - if (bmp_buffer) - free (bmp_buffer); // reliability check, free BMP buffer if already allocated - bmp_buffer = NULL; - bmp_buffer = (char *) malloc (DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); // allocate memory - if (bmp_buffer == NULL) { - fprintf (stderr,"InitDebugBitmap(): unable to allocate %d kbytes for BMP buffer!\n", DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT / 1024); - exit(1) ; - } - - memset (bmp_buffer, 14, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); // Set all to white - return; // yes, it's as simple as that -} - - -void DrawPoint (const Vector v, unsigned char color) -{ - int offset, fraction, x0, y0; - float scalex, scaley, scale; - - if (bmp_buffer == NULL) - { - fprintf (stderr,"DrawLineInDebugBitmap(): function called with NULL BMP buffer!\n"); - return; // reliability check: cancel if bmp buffer unallocated - } - - // first compute the X and Y divider scale, and take the greatest of both - scalex = (maxx - minx) / DEBUG_BMP_WIDTH ; - scaley = (maxy - miny) / DEBUG_BMP_WIDTH ; - if (scalex > scaley) - scale = scalex + scalex / 100; // add a little offset (margin) for safety - else - scale = scaley + scaley / 100; // add a little offset (margin) for safety - - // translate the world coordinates in image pixel coordinates - x0 = (int) ((v.x - minx) / scale); - y0 = (int) ((v.y - miny) / scale); - - offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf( stderr,"DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1) ; - } - - bmp_buffer[offset] = color; // draw the point itself - if (offset+1 < DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) bmp_buffer[offset+1] = color; // make a small star on the right - if (offset-1 >= 0) bmp_buffer[offset-1] = color; // make a small star on the left - if (offset+DEBUG_BMP_WIDTH < DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) bmp_buffer[offset+DEBUG_BMP_WIDTH] = color; - if (offset-DEBUG_BMP_WIDTH >= 0) bmp_buffer[offset-DEBUG_BMP_WIDTH] = color; // make a small star above -} - -void DrawLineInDebugBitmap (const Vector v_from, const Vector v_to, unsigned char color) -{ - // blind copy of botman's Bresenham(). This function prints a vector line into a bitmap dot - // matrix. The dot matrix (bmp_buffer) is a global array. The size of the bitmap is always - // assumed to be DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT pixels (currently 2000 * 2000 to fit with - // the size of the universe, with an adaptative unit scale, up to 1 pixel = 10 vector units). - - int x0, y0, x1, y1; - int dx, stepx, dy, stepy; - int offset, fraction; - - if (bmp_buffer == NULL) - { - fprintf (stderr,"DrawLineInDebugBitmap(): function called with NULL BMP buffer!\n"); - return; // reliability check: cancel if bmp buffer unallocated - } - - // translate the world coordinates in image pixel coordinates - x0 = (int) ((v_from.x - minx) / scale); - y0 = (int) ((v_from.y - miny) / scale); - x1 = (int) ((v_to.x - minx) / scale); - y1 = (int) ((v_to.y - miny) / scale); - - dx = (x1 - x0) * 2; - dy = (y1 - y0) * 2; - if (dx < 0) { dx = -dx; stepx = -1; } else stepx = 1; - if (dy < 0) { dy = -dy; stepy = -1; } else stepy = 1; - - offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf( stderr,"DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1) ; - } - - bmp_buffer[offset] = color; // draw the first point of the line - - // is the line rather horizontal than vertical ? We need to know this to determine the step - // advance in the Bresenham grid, either we draw y = f(x), or x = f(y). - if (dx > dy) - { - // the line is rather horizontal, we can draw it safely for incremental values of x - - fraction = 2 * dy - dx; // fraction of height in x0 pixel's 'square' where y0 should be - - // while we've not reached the end of the segment... - while (x0 != x1) - { - // if y0 should rather be drawn on a different height than its previous height... - if (fraction >= 0) - { - y0 += stepy; // draw it one pixel aside, then (depending on line orientation) - fraction -= 2 * dx; // and reset its fraction (Bresenham, not sure I get the math) - } - x0 += stepx; // in either case, draw x0 one pixel aside its previous position - fraction += 2 * dy; // and update y0's fraction (not sure I get the math - but whatever) - - // compute the offset in the BMP buffer corresponding to this point - offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1) ; - } - - bmp_buffer[offset] = color; // set this point to have the specified color - } - } - else - { - // else the line is rather vertical, we NEED to draw it for incremental values of y (if we - // did it for incremental values of x instead, we would drop half the pixels). - - fraction = 2 * dx - dy; // fraction of width in y0 pixel's 'square' where x0 should be - - // while we've not reached the end of the segment... - while (y0 != y1) - { - // if x0 should rather be drawn on a different width than its previous width... - if (fraction >= 0) - { - x0 += stepx; // draw it one pixel aside, then (depending on line orientation) - fraction -= 2 * dy; // and reset its fraction (Bresenham, not sure I get the math) - } - y0 += stepy; // in either case, draw y0 one pixel aside its previous position - fraction += 2 * dx; // and update x0's fraction (not sure I get the math - but whatever) - - // compute the offset in the BMP buffer corresponding to this point - offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf (stderr,"DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1) ; - } - bmp_buffer[offset] = color; // set this point to have the specified color - } - } - - return; // finished, segment has been printed into the BMP dot matrix -} - - -void WriteDebugBitmap (const char *mapname) -{ - // this function writes the debug bitmap image buffer in a .BMP file to disk. The format is - // 256 color and 2000 * 2000 pixels. The center of the world being roughly the center of the - // bitmap. The bitmap is stored in the file specified by 'filename' (which can be a relative - // path from the Half-Life base directory). - char filename[256]; - FILE *fp; - int data_start, file_size; - unsigned long dummy; - - if (bmp_buffer == NULL) - { - fprintf (stderr,"WriteDebugBitmap(): function called with NULL BMP buffer!\n"); - return; // reliability check: cancel if bmp buffer unallocated - } - - // open (or create) the .bmp file for writing in binary mode... - // Set Directory name -// strcpy (filename, "data/cstrike/maps/"); - strcpy (filename, mapname); - strcat (filename, ".bmp"); // bitmap file - fp = fopen (filename, "wb"); - if (fp == NULL) - { - fprintf (stderr,"RACC: WriteDebugBitmap(): unable to open BMP file!\n"); - if (bmp_buffer) - free (bmp_buffer); // cannot open file, free DXF buffer - bmp_buffer = NULL; - return; // cancel if error creating file - } - - // write the BMP header - fwrite ("BM", 2, 1, fp); // write the BMP header tag - fseek (fp, sizeof (unsigned long), SEEK_CUR); // skip the file size field (will write it last) - fwrite ("\0\0", sizeof (short), 1, fp); // dump zeros in the first reserved field (unused) - fwrite ("\0\0", sizeof (short), 1, fp); // dump zeros in the second reserved field (unused) - fseek (fp, sizeof (unsigned long), SEEK_CUR); // skip the data start field (will write it last) - - // write the info header - dummy = 40; - fwrite (&dummy, sizeof (unsigned long), 1, fp); // write the info header size (does 40 bytes) - dummy = DEBUG_BMP_WIDTH; - fwrite (&dummy, sizeof (long), 1, fp); // write the image width (2000 px) - dummy = DEBUG_BMP_HEIGHT; - fwrite (&dummy, sizeof (long), 1, fp); // write the image height (2000 px) - dummy = 1; - fwrite (&dummy, sizeof (short), 1, fp); // write the # of planes (1) - dummy = 8; - fwrite (&dummy, sizeof (short), 1, fp); // write the bit count (8) - dummy = 0; - fwrite (&dummy, sizeof (unsigned long), 1, fp); // write the compression id (no compression) - dummy = DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT; - fwrite (&dummy, sizeof (unsigned long), 1, fp); // write the image size (2000 * 2000) - dummy = 0; - fwrite (&dummy, sizeof (long), 1, fp); // write the X pixels per meter (not specified) - fwrite (&dummy, sizeof (long), 1, fp); // write the Y pixels per meter (not specified) - dummy = 256; - fwrite (&dummy, sizeof (unsigned long), 1, fp); // write the # of colors used (all) - fwrite (&dummy, sizeof (unsigned long), 1, fp); // write the # of important colors (wtf ?) - - // write the color palette (R, G, B, reserved byte) - fputc (0x00, fp); fputc (0x00, fp); fputc (0x00, fp); fputc (0x00, fp); // 0=BLACK - fputc (0xFF, fp); fputc (0xFF, fp); fputc (0xFF, fp); fputc (0x00, fp); // 1=WHITE - fputc (0x80, fp); fputc (0x80, fp); fputc (0x80, fp); fputc (0x00, fp); // 2=GREY - fputc (0xC0, fp); fputc (0xC0, fp); fputc (0xC0, fp); fputc (0x00, fp); // 3=SILVER - fputc (0x80, fp); fputc (0x00, fp); fputc (0x00, fp); fputc (0x00, fp); // 4=DARK RED - fputc (0xFF, fp); fputc (0x00, fp); fputc (0x00, fp); fputc (0x00, fp); // 5=RED - fputc (0x80, fp); fputc (0x80, fp); fputc (0x00, fp); fputc (0x00, fp); // 6=DARK YELLOW - fputc (0xFF, fp); fputc (0xFF, fp); fputc (0x00, fp); fputc (0x00, fp); // 7=YELLOW - fputc (0x00, fp); fputc (0x80, fp); fputc (0x00, fp); fputc (0x00, fp); // 8=DARK GREEN - fputc (0x00, fp); fputc (0xFF, fp); fputc (0x00, fp); fputc (0x00, fp); // 9=GREEN - fputc (0x00, fp); fputc (0x00, fp); fputc (0x80, fp); fputc (0x00, fp); // 10=DARK BLUE - fputc (0x00, fp); fputc (0x00, fp); fputc (0x80, fp); fputc (0x00, fp); // 11=BLUE - fputc (0x80, fp); fputc (0x00, fp); fputc (0x80, fp); fputc (0x00, fp); // 12=DARK PURPLE - fputc (0x80, fp); fputc (0x00, fp); fputc (0x80, fp); fputc (0x00, fp); // 13=PURPLE - fputc (0xFF, fp); fputc (0xFF, fp); fputc (0xFF, fp); fputc (0x00, fp); // 14=WHITE - fputc (0xEF, fp); fputc (0xEF, fp); fputc (0xEF, fp); fputc (0x00, fp); // 15=WHITE-GREY - fputc (0xDF, fp); fputc (0xDF, fp); fputc (0xDF, fp); fputc (0x00, fp); // 16=GREY - fputc (0xCF, fp); fputc (0xCF, fp); fputc (0xCF, fp); fputc (0x00, fp); // 17=DARKGREY - fputc (0xBF, fp); fputc (0xBF, fp); fputc (0xBF, fp); fputc (0x00, fp); // 18=DARKGREY - fputc (0xAF, fp); fputc (0xAF, fp); fputc (0xAF, fp); fputc (0x00, fp); // 19=DARKGREY - - for (dummy = 20; dummy < 256; dummy++) - { - // fill out the rest of the palette with zeros - fputc (0x00, fp); fputc (0x00, fp); fputc (0x00, fp); fputc (0x00, fp); - } - - // write the actual image data - data_start = ftell (fp); // get the data start position (that's where we are now) - fwrite (bmp_buffer, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT, 1, fp); // write the image - file_size = ftell (fp); // get the file size now that the image is dumped - - // now that we've dumped our data, we know the file size and the data start position - - fseek (fp, 0, SEEK_SET); // rewind - fseek (fp, 2, SEEK_CUR); // skip the BMP header tag "BM" - fwrite (&file_size, sizeof (unsigned long), 1, fp); // write the file size at its location - fseek (fp, sizeof (short), SEEK_CUR); // skip the first reserved field - fseek (fp, sizeof (short), SEEK_CUR); // skip the second reserved field - fwrite (&data_start, sizeof (unsigned long), 1, fp); // write the data start at its location - - fclose (fp); // finished, close the BMP file - - if (bmp_buffer) - free (bmp_buffer); // and free the BMP buffer - bmp_buffer = NULL; - - return; // and return -} - -void FindMinMax(void) -{ - int i ; - - minx = miny = 9999.0 ; - maxx = maxy = -9999.0 ; - for (i=0; i maxx) maxx = Nodes[i].origin.x ; - if (Nodes[i].origin.y > maxy) maxy = Nodes[i].origin.y ; - if (Nodes[i].origin.x < minx) minx = Nodes[i].origin.x ; - if (Nodes[i].origin.y < miny) miny = Nodes[i].origin.y ; - } - - // Add some margin - minx -= 32 ; - miny -= 32 ; - maxx += 32 ; - maxy += 32 ; - // first compute the X and Y divider scale, and take the greatest of both - scalex = (maxx - minx) / DEBUG_BMP_WIDTH ; - scaley = (maxy - miny) / DEBUG_BMP_HEIGHT ; - if (scalex > scaley) - scale = scalex + scalex / 100; // add a little offset (margin) for safety - else - scale = scaley + scaley / 100; // add a little offset (margin) for safety -} - -// Mark meridians as slighly darker in alternance - -void MarkAxis(void) -{ - int x, y, x0, y0 ; - - x0 = (int) ((0 - minx) / scale); - y0 = (int) ((0 - miny) / scale); - - // Mark X axis by keeping X to 0 and varying Y - if ((minx < 0) && (0 < maxx)) - for (y=0; y < DEBUG_BMP_HEIGHT ; y ++) - bmp_buffer[y*DEBUG_BMP_WIDTH+x0]+=2 ; - - // Mar - if ((miny < 0) && (0 < maxy)) - for (x=0; x < DEBUG_BMP_WIDTH; x++) - bmp_buffer[y0*DEBUG_BMP_WIDTH+x]+=2 ; -} - -void MarkMeredians(void) -{ - int x, y ; - int Meredian ; - - // Mark some meredians - for (x=0; x < DEBUG_BMP_WIDTH; x++) { - Meredian = abs(((float) x * scale + minx + 8192.0) / (float) SIZE_MEREDIAN) ; - if (Meredian & 0x01) { - for (y=0; y < DEBUG_BMP_HEIGHT ; y ++) - bmp_buffer[y*DEBUG_BMP_WIDTH+x]++ ; - } - } - - // Mark some meredians - for (y=0; y < DEBUG_BMP_HEIGHT; y++) { - Meredian = abs(((float) y * scale + miny + 8192.0) / (float) SIZE_MEREDIAN) ; - if (Meredian & 0x01) { - for (x=0; x < DEBUG_BMP_HEIGHT ; x ++) - bmp_buffer[y*DEBUG_BMP_WIDTH+x]++ ; - } - } -} - -void PlotNodes(void) -{ - int i, j ; - - for (i=0; i = 0) ; j++) - DrawLineInDebugBitmap(Nodes[i].origin, - Nodes[Nodes[i].iNeighbour[j]].origin,0) ; - for (i=0; i +#include +#include +#include +#include +#include +#include +#include + +extern char* Version; + +// width and height of the debug bitmap image +#define DEBUG_BMP_WIDTH 2048 +#define DEBUG_BMP_HEIGHT 2048 + +float scalex, scaley, scale; +char* bmp_buffer; + +#include "../bot.h" +#include "../NodeMachine.h" + +extern char* Version; + +/* Copy from NodeMachine.cpp by Stefan Hendricks */ + +tNode Nodes[MAX_NODES]; // Nodes +tInfoNode InfoNodes[MAX_NODES]; // Info for Nodes +tMeredian Meredians[MAX_MEREDIANS][MAX_MEREDIANS]; // Meredian lookup search for Nodes +int iMaxUsedNodes; +float maxx, maxy, minx, miny; + +void load(char* mapname) +{ + char filename[256]; + int i, n; + + // Set Directory name + strcpy(filename, mapname); + strcat(filename, ".rbn"); // nodes file + + FILE* rbl; + rbl = fopen(filename, "rb"); + + if (rbl != NULL) + { + int iVersion; + fread(&iVersion, sizeof(int), 1, rbl); + + // Version 1.0 + if (iVersion == FILE_NODE_VER1) + { + for (i = 0; i < MAX_NODES; i++) + { + fread(&Nodes[i].origin, sizeof(Vector), 1, rbl); + for (n = 0; n < MAX_NEIGHBOURS; n++) + { + fread(&Nodes[i].iNeighbour[n], sizeof(int), 1, rbl); + } + + // save bit flags + fread(&Nodes[i].iNodeBits, sizeof(int), 1, rbl); + + if (Nodes[i].origin != Vector(9999, 9999, 9999)) + iMaxUsedNodes = i; + } + } + } + else { + fprintf(stderr, "Cannot open file %s\n", filename); + exit; + } + printf("%d nodes loaded out of %d.\n", iMaxUsedNodes, MAX_NODES); +} + +void InitDebugBitmap(void) +{ + // this function allocates memory and clears the debug bitmap buffer + + if (bmp_buffer) + free(bmp_buffer); // reliability check, free BMP buffer if already allocated + bmp_buffer = NULL; + bmp_buffer = (char*)malloc(DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); // allocate memory + if (bmp_buffer == NULL) { + fprintf(stderr, "InitDebugBitmap(): unable to allocate %d kbytes for BMP buffer!\n", DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT / 1024); + exit(1); + } + + memset(bmp_buffer, 14, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); // Set all to white + return; // yes, it's as simple as that +} + +void DrawPoint(const Vector v, unsigned char color) +{ + int offset, fraction, x0, y0; + float scalex, scaley, scale; + + if (bmp_buffer == NULL) + { + fprintf(stderr, "DrawLineInDebugBitmap(): function called with NULL BMP buffer!\n"); + return; // reliability check: cancel if bmp buffer unallocated + } + + // first compute the X and Y divider scale, and take the greatest of both + scalex = (maxx - minx) / DEBUG_BMP_WIDTH; + scaley = (maxy - miny) / DEBUG_BMP_WIDTH; + if (scalex > scaley) + scale = scalex + scalex / 100; // add a little offset (margin) for safety + else + scale = scaley + scaley / 100; // add a little offset (margin) for safety + + // translate the world coordinates in image pixel coordinates + x0 = (int)((v.x - minx) / scale); + y0 = (int)((v.y - miny) / scale); + + offset = y0 * DEBUG_BMP_WIDTH + x0; + if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { + fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); + exit(1); + } + + bmp_buffer[offset] = color; // draw the point itself + if (offset + 1 < DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) bmp_buffer[offset + 1] = color; // make a small star on the right + if (offset - 1 >= 0) bmp_buffer[offset - 1] = color; // make a small star on the left + if (offset + DEBUG_BMP_WIDTH < DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) bmp_buffer[offset + DEBUG_BMP_WIDTH] = color; + if (offset - DEBUG_BMP_WIDTH >= 0) bmp_buffer[offset - DEBUG_BMP_WIDTH] = color; // make a small star above +} + +void DrawLineInDebugBitmap(const Vector v_from, const Vector v_to, unsigned char color) +{ + // blind copy of botman's Bresenham(). This function prints a vector line into a bitmap dot + // matrix. The dot matrix (bmp_buffer) is a global array. The size of the bitmap is always + // assumed to be DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT pixels (currently 2000 * 2000 to fit with + // the size of the universe, with an adaptative unit scale, up to 1 pixel = 10 vector units). + + int x0, y0, x1, y1; + int dx, stepx, dy, stepy; + int offset, fraction; + + if (bmp_buffer == NULL) + { + fprintf(stderr, "DrawLineInDebugBitmap(): function called with NULL BMP buffer!\n"); + return; // reliability check: cancel if bmp buffer unallocated + } + + // translate the world coordinates in image pixel coordinates + x0 = (int)((v_from.x - minx) / scale); + y0 = (int)((v_from.y - miny) / scale); + x1 = (int)((v_to.x - minx) / scale); + y1 = (int)((v_to.y - miny) / scale); + + dx = (x1 - x0) * 2; + dy = (y1 - y0) * 2; + if (dx < 0) { dx = -dx; stepx = -1; } + else stepx = 1; + if (dy < 0) { dy = -dy; stepy = -1; } + else stepy = 1; + + offset = y0 * DEBUG_BMP_WIDTH + x0; + if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { + fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); + exit(1); + } + + bmp_buffer[offset] = color; // draw the first point of the line + + // is the line rather horizontal than vertical ? We need to know this to determine the step + // advance in the Bresenham grid, either we draw y = f(x), or x = f(y). + if (dx > dy) + { + // the line is rather horizontal, we can draw it safely for incremental values of x + + fraction = 2 * dy - dx; // fraction of height in x0 pixel's 'square' where y0 should be + + // while we've not reached the end of the segment... + while (x0 != x1) + { + // if y0 should rather be drawn on a different height than its previous height... + if (fraction >= 0) + { + y0 += stepy; // draw it one pixel aside, then (depending on line orientation) + fraction -= 2 * dx; // and reset its fraction (Bresenham, not sure I get the math) + } + x0 += stepx; // in either case, draw x0 one pixel aside its previous position + fraction += 2 * dy; // and update y0's fraction (not sure I get the math - but whatever) + + // compute the offset in the BMP buffer corresponding to this point + offset = y0 * DEBUG_BMP_WIDTH + x0; + if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { + fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); + exit(1); + } + + bmp_buffer[offset] = color; // set this point to have the specified color + } + } + else + { + // else the line is rather vertical, we NEED to draw it for incremental values of y (if we + // did it for incremental values of x instead, we would drop half the pixels). + + fraction = 2 * dx - dy; // fraction of width in y0 pixel's 'square' where x0 should be + + // while we've not reached the end of the segment... + while (y0 != y1) + { + // if x0 should rather be drawn on a different width than its previous width... + if (fraction >= 0) + { + x0 += stepx; // draw it one pixel aside, then (depending on line orientation) + fraction -= 2 * dy; // and reset its fraction (Bresenham, not sure I get the math) + } + y0 += stepy; // in either case, draw y0 one pixel aside its previous position + fraction += 2 * dx; // and update x0's fraction (not sure I get the math - but whatever) + + // compute the offset in the BMP buffer corresponding to this point + offset = y0 * DEBUG_BMP_WIDTH + x0; + if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { + fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); + exit(1); + } + bmp_buffer[offset] = color; // set this point to have the specified color + } + } + + return; // finished, segment has been printed into the BMP dot matrix +} + +void WriteDebugBitmap(const char* mapname) +{ + // this function writes the debug bitmap image buffer in a .BMP file to disk. The format is + // 256 color and 2000 * 2000 pixels. The center of the world being roughly the center of the + // bitmap. The bitmap is stored in the file specified by 'filename' (which can be a relative + // path from the Half-Life base directory). + char filename[256]; + FILE* fp; + int data_start, file_size; + unsigned long dummy; + + if (bmp_buffer == NULL) + { + fprintf(stderr, "WriteDebugBitmap(): function called with NULL BMP buffer!\n"); + return; // reliability check: cancel if bmp buffer unallocated + } + + // open (or create) the .bmp file for writing in binary mode... + // Set Directory name + // strcpy (filename, "data/cstrike/maps/"); + strcpy(filename, mapname); + strcat(filename, ".bmp"); // bitmap file + fp = fopen(filename, "wb"); + if (fp == NULL) + { + fprintf(stderr, "RACC: WriteDebugBitmap(): unable to open BMP file!\n"); + if (bmp_buffer) + free(bmp_buffer); // cannot open file, free DXF buffer + bmp_buffer = NULL; + return; // cancel if error creating file + } + + // write the BMP header + fwrite("BM", 2, 1, fp); // write the BMP header tag + fseek(fp, sizeof(unsigned long), SEEK_CUR); // skip the file size field (will write it last) + fwrite("\0\0", sizeof(short), 1, fp); // dump zeros in the first reserved field (unused) + fwrite("\0\0", sizeof(short), 1, fp); // dump zeros in the second reserved field (unused) + fseek(fp, sizeof(unsigned long), SEEK_CUR); // skip the data start field (will write it last) + + // write the info header + dummy = 40; + fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the info header size (does 40 bytes) + dummy = DEBUG_BMP_WIDTH; + fwrite(&dummy, sizeof(long), 1, fp); // write the image width (2000 px) + dummy = DEBUG_BMP_HEIGHT; + fwrite(&dummy, sizeof(long), 1, fp); // write the image height (2000 px) + dummy = 1; + fwrite(&dummy, sizeof(short), 1, fp); // write the # of planes (1) + dummy = 8; + fwrite(&dummy, sizeof(short), 1, fp); // write the bit count (8) + dummy = 0; + fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the compression id (no compression) + dummy = DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT; + fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the image size (2000 * 2000) + dummy = 0; + fwrite(&dummy, sizeof(long), 1, fp); // write the X pixels per meter (not specified) + fwrite(&dummy, sizeof(long), 1, fp); // write the Y pixels per meter (not specified) + dummy = 256; + fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the # of colors used (all) + fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the # of important colors (wtf ?) + + // write the color palette (R, G, B, reserved byte) + fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); // 0=BLACK + fputc(0xFF, fp); fputc(0xFF, fp); fputc(0xFF, fp); fputc(0x00, fp); // 1=WHITE + fputc(0x80, fp); fputc(0x80, fp); fputc(0x80, fp); fputc(0x00, fp); // 2=GREY + fputc(0xC0, fp); fputc(0xC0, fp); fputc(0xC0, fp); fputc(0x00, fp); // 3=SILVER + fputc(0x80, fp); fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); // 4=DARK RED + fputc(0xFF, fp); fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); // 5=RED + fputc(0x80, fp); fputc(0x80, fp); fputc(0x00, fp); fputc(0x00, fp); // 6=DARK YELLOW + fputc(0xFF, fp); fputc(0xFF, fp); fputc(0x00, fp); fputc(0x00, fp); // 7=YELLOW + fputc(0x00, fp); fputc(0x80, fp); fputc(0x00, fp); fputc(0x00, fp); // 8=DARK GREEN + fputc(0x00, fp); fputc(0xFF, fp); fputc(0x00, fp); fputc(0x00, fp); // 9=GREEN + fputc(0x00, fp); fputc(0x00, fp); fputc(0x80, fp); fputc(0x00, fp); // 10=DARK BLUE + fputc(0x00, fp); fputc(0x00, fp); fputc(0x80, fp); fputc(0x00, fp); // 11=BLUE + fputc(0x80, fp); fputc(0x00, fp); fputc(0x80, fp); fputc(0x00, fp); // 12=DARK PURPLE + fputc(0x80, fp); fputc(0x00, fp); fputc(0x80, fp); fputc(0x00, fp); // 13=PURPLE + fputc(0xFF, fp); fputc(0xFF, fp); fputc(0xFF, fp); fputc(0x00, fp); // 14=WHITE + fputc(0xEF, fp); fputc(0xEF, fp); fputc(0xEF, fp); fputc(0x00, fp); // 15=WHITE-GREY + fputc(0xDF, fp); fputc(0xDF, fp); fputc(0xDF, fp); fputc(0x00, fp); // 16=GREY + fputc(0xCF, fp); fputc(0xCF, fp); fputc(0xCF, fp); fputc(0x00, fp); // 17=DARKGREY + fputc(0xBF, fp); fputc(0xBF, fp); fputc(0xBF, fp); fputc(0x00, fp); // 18=DARKGREY + fputc(0xAF, fp); fputc(0xAF, fp); fputc(0xAF, fp); fputc(0x00, fp); // 19=DARKGREY + + for (dummy = 20; dummy < 256; dummy++) + { + // fill out the rest of the palette with zeros + fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); + } + + // write the actual image data + data_start = ftell(fp); // get the data start position (that's where we are now) + fwrite(bmp_buffer, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT, 1, fp); // write the image + file_size = ftell(fp); // get the file size now that the image is dumped + + // now that we've dumped our data, we know the file size and the data start position + + fseek(fp, 0, SEEK_SET); // rewind + fseek(fp, 2, SEEK_CUR); // skip the BMP header tag "BM" + fwrite(&file_size, sizeof(unsigned long), 1, fp); // write the file size at its location + fseek(fp, sizeof(short), SEEK_CUR); // skip the first reserved field + fseek(fp, sizeof(short), SEEK_CUR); // skip the second reserved field + fwrite(&data_start, sizeof(unsigned long), 1, fp); // write the data start at its location + + fclose(fp); // finished, close the BMP file + + if (bmp_buffer) + free(bmp_buffer); // and free the BMP buffer + bmp_buffer = NULL; + + return; // and return +} + +void FindMinMax(void) +{ + int i; + + minx = miny = 9999.0; + maxx = maxy = -9999.0; + for (i = 0; i < iMaxUsedNodes; i++) { + if (Nodes[i].origin.x > maxx) maxx = Nodes[i].origin.x; + if (Nodes[i].origin.y > maxy) maxy = Nodes[i].origin.y; + if (Nodes[i].origin.x < minx) minx = Nodes[i].origin.x; + if (Nodes[i].origin.y < miny) miny = Nodes[i].origin.y; + } + + // Add some margin + minx -= 32; + miny -= 32; + maxx += 32; + maxy += 32; + // first compute the X and Y divider scale, and take the greatest of both + scalex = (maxx - minx) / DEBUG_BMP_WIDTH; + scaley = (maxy - miny) / DEBUG_BMP_HEIGHT; + if (scalex > scaley) + scale = scalex + scalex / 100; // add a little offset (margin) for safety + else + scale = scaley + scaley / 100; // add a little offset (margin) for safety +} + +// Mark meridians as slighly darker in alternance + +void MarkAxis(void) +{ + int x, y, x0, y0; + + x0 = (int)((0 - minx) / scale); + y0 = (int)((0 - miny) / scale); + + // Mark X axis by keeping X to 0 and varying Y + if ((minx < 0) && (0 < maxx)) + for (y = 0; y < DEBUG_BMP_HEIGHT; y++) + bmp_buffer[y * DEBUG_BMP_WIDTH + x0] += 2; + + // Mar + if ((miny < 0) && (0 < maxy)) + for (x = 0; x < DEBUG_BMP_WIDTH; x++) + bmp_buffer[y0 * DEBUG_BMP_WIDTH + x] += 2; +} + +void MarkMeredians(void) +{ + int x, y; + int Meredian; + + // Mark some meredians + for (x = 0; x < DEBUG_BMP_WIDTH; x++) { + Meredian = abs(((float)x * scale + minx + 8192.0) / (float)SIZE_MEREDIAN); + if (Meredian & 0x01) { + for (y = 0; y < DEBUG_BMP_HEIGHT; y++) + bmp_buffer[y * DEBUG_BMP_WIDTH + x]++; + } + } + + // Mark some meredians + for (y = 0; y < DEBUG_BMP_HEIGHT; y++) { + Meredian = abs(((float)y * scale + miny + 8192.0) / (float)SIZE_MEREDIAN); + if (Meredian & 0x01) { + for (x = 0; x < DEBUG_BMP_HEIGHT; x++) + bmp_buffer[y * DEBUG_BMP_WIDTH + x]++; + } + } +} + +void PlotNodes(void) +{ + int i, j; + + for (i = 0; i < iMaxUsedNodes; i++) + for (j = 0; (j < MAX_NEIGHBOURS) && (Nodes[i].iNeighbour[j] >= 0); j++) + DrawLineInDebugBitmap(Nodes[i].origin, + Nodes[Nodes[i].iNeighbour[j]].origin, 0); + for (i = 0; i < iMaxUsedNodes; i++) + DrawPoint(Nodes[i].origin, 5); +} + +int main(int argc, char* argv[]) +{ + printf("DrawNodes Version %s\nBy eric@vyncke.org\n", Version); + if (argc != 2) { + fprintf(stderr, "Usage is %s mapname\n", argv[0]); + exit; + } + load(argv[1]); + FindMinMax(); + InitDebugBitmap(); + MarkMeredians(); + MarkAxis(); + PlotNodes(); + WriteDebugBitmap(argv[1]); +} \ No newline at end of file diff --git a/Bsp2Rbn/DumpNodes.c b/Bsp2Rbn/DumpNodes.c index 5341fe3..1a35d78 100644 --- a/Bsp2Rbn/DumpNodes.c +++ b/Bsp2Rbn/DumpNodes.c @@ -2,7 +2,6 @@ evyncke@hec.be, June 2004 - Based on routines from Stefan H. */ @@ -18,7 +17,7 @@ Based on routines from Stefan H. #include "../bot.h" #include "../NodeMachine.h" -extern char * Version ; +extern char* Version; /* Copy from NodeMachine.cpp by Stefan Hendricks */ @@ -28,170 +27,168 @@ int Meredians[MAX_MEREDIANS][MAX_MEREDIANS]; // Meredian lookup search for No int iMaxUsedNodes; // Input: Vector, Output X and Y Meredians -void VectorToMeredian (Vector vOrigin, int *iX, int *iY) +void VectorToMeredian(Vector vOrigin, int* iX, int* iY) { - // Called for lookupt and for storing - int iCoordX = abs (vOrigin.x + 8192.0); // map height (converts from - to +) - int iCoordY = abs (vOrigin.y + 8192.0); // map width (converts from - to +) + // Called for lookupt and for storing + int iCoordX = abs(vOrigin.x + 8192.0); // map height (converts from - to +) + int iCoordY = abs(vOrigin.y + 8192.0); // map width (converts from - to +) - // Meredian: - iCoordX = abs (iCoordX / SIZE_MEREDIAN); - iCoordY = abs (iCoordY / SIZE_MEREDIAN); + // Meredian: + iCoordX = abs(iCoordX / SIZE_MEREDIAN); + iCoordY = abs(iCoordY / SIZE_MEREDIAN); - *iX = iCoordX; - *iY = iCoordY; + *iX = iCoordX; + *iY = iCoordY; } -void load (char * mapname) +void load(char* mapname) { - char filename[256]; - int i, j, n; - - strcpy (filename, mapname); - strcat (filename, ".rbn"); // nodes file - - FILE *rbl; - rbl = fopen (filename, "rb"); - - if (rbl != NULL) - { - int iVersion ; - fread (&iVersion, sizeof (int), 1, rbl); - - // Version 1.0 - if (iVersion == FILE_NODE_VER1) - { - for (i = 0; i < MAX_NODES; i++) - { - fread (&Nodes[i].origin, sizeof (Vector), 1, rbl); - for (n = 0; n < MAX_NEIGHBOURS; n++) - { - fread (&Nodes[i].iNeighbour[n], sizeof (int), 1, rbl); - } - - // save bit flags - fread (&Nodes[i].iNodeBits, sizeof (int), 1, rbl); - - if (Nodes[i].origin != Vector (9999, 9999, 9999)) - iMaxUsedNodes = i; - } - } - } else { - fprintf(stderr,"Cannot open file %s\n",filename) ; - exit ; - } - printf("%d nodes loaded out of %d.\n",iMaxUsedNodes,MAX_NODES) ; - fclose(rbl) ; - - // Zero the Meredians table - for (i=0;i -1 && iY > -1) - Meredians[iX][iY] ++ ; - } + char filename[256]; + int i, j, n; + + strcpy(filename, mapname); + strcat(filename, ".rbn"); // nodes file + FILE* rbl; + rbl = fopen(filename, "rb"); + if (rbl != NULL) + { + int iVersion; + fread(&iVersion, sizeof(int), 1, rbl); + + // Version 1.0 + if (iVersion == FILE_NODE_VER1) + { + for (i = 0; i < MAX_NODES; i++) + { + fread(&Nodes[i].origin, sizeof(Vector), 1, rbl); + for (n = 0; n < MAX_NEIGHBOURS; n++) + { + fread(&Nodes[i].iNeighbour[n], sizeof(int), 1, rbl); + } + + // save bit flags + fread(&Nodes[i].iNodeBits, sizeof(int), 1, rbl); + + if (Nodes[i].origin != Vector(9999, 9999, 9999)) + iMaxUsedNodes = i; + } + } + } + else { + fprintf(stderr, "Cannot open file %s\n", filename); + exit; + } + printf("%d nodes loaded out of %d.\n", iMaxUsedNodes, MAX_NODES); + fclose(rbl); + + // Zero the Meredians table + for (i = 0;i < MAX_MEREDIANS;i++) + for (j = 0;j < MAX_MEREDIANS;j++) + Meredians[i][j] = 0; + + // Add nodes to meredians + for (i = 0; i < MAX_NODES; i++) + if (Nodes[i].origin != Vector(9999, 9999, 9999)) + { + int iX, iY; + VectorToMeredian(Nodes[i].origin, &iX, &iY); + if (iX > -1 && iY > -1) + Meredians[iX][iY] ++; + } } -void PrintNodesPair(int iMe, int iNext, Vector Me, Vector Next) +void PrintNodesPair(int iMe, int iNext, Vector Me, Vector Next) { - int MeX, MeY, NextX, NextY ; // Meredians - - printf(" dist(%d,%d)=%.0f",iMe,iNext,(Next-Me).Length()) ; - VectorToMeredian(Me,&MeX,&MeY) ; - VectorToMeredian(Next,&NextX,&NextY) ; - if (abs(Me.z-Next.z) > 5) printf(", altitude diff=%d",abs(Me.z-Next.z)) ; - if ((MeX != NextX) && (MeY != NextY)) - printf(", Both meredians do not match!") ; - else if ((MeX != NextX) || (MeY != NextY)) - printf(", One Meredian does not match!") ; - printf("\n") ; + int MeX, MeY, NextX, NextY; // Meredians + + printf(" dist(%d,%d)=%.0f", iMe, iNext, (Next - Me).Length()); + VectorToMeredian(Me, &MeX, &MeY); + VectorToMeredian(Next, &NextX, &NextY); + if (abs(Me.z - Next.z) > 5) printf(", altitude diff=%d", abs(Me.z - Next.z)); + if ((MeX != NextX) && (MeY != NextY)) + printf(", Both meredians do not match!"); + else if ((MeX != NextX) || (MeY != NextY)) + printf(", One Meredian does not match!"); + printf("\n"); } // Analyze the RBN file which has just been read... void AnalyseNeighbours(void) { - int MeX, MeY, NextX, NextY ; // Meredians - int Count,i,j ; - int Histogram[MAX_NEIGHBOURS+1] ; // Will count the frequency of nodes having 0... N neighbours - Vector Me, Next ; - float Distance ; - - for (i=0;i= 0);j++) - Count ++ ; - Histogram[j]++ ; + for (j = 0; (j < MAX_NEIGHBOURS) && (Nodes[i].iNeighbour[j] >= 0);j++) + Count++; + Histogram[j]++; } - printf("There are %d neighbours (i.e. %.1f neighbour(s) per node out of %d)\n",Count,(float) Count / (float) iMaxUsedNodes,MAX_NEIGHBOURS) ; - printf("Neighbours distribution\n") ; - for (j=0;j NODE_ZONE*2) continue ; - PrintNodesPair(i, i-1, Me, Next) ; + for (i = 1; i < iMaxUsedNodes; i++) + if (Nodes[i].iNeighbour[0] < 0) { + Me = Nodes[i].origin; + Next = Nodes[i - 1].origin; + Distance = (Next - Me).Length(); + if (Distance > NODE_ZONE * 2) continue; + PrintNodesPair(i, i - 1, Me, Next); } - printf("Isolated nodes that are close to the next one (HIGHLY suspicious):\n") ; - for (i=0; i < iMaxUsedNodes-1; i++) - if (Nodes[i].iNeighbour[0]< 0) { - Me=Nodes[i].origin ; - Next=Nodes[i+1].origin ; - Distance=(Next-Me).Length() ; - if (Distance > NODE_ZONE*2) continue ; - PrintNodesPair(i, i+1, Me, Next) ; + printf("Isolated nodes that are close to the next one (HIGHLY suspicious):\n"); + for (i = 0; i < iMaxUsedNodes - 1; i++) + if (Nodes[i].iNeighbour[0] < 0) { + Me = Nodes[i].origin; + Next = Nodes[i + 1].origin; + Distance = (Next - Me).Length(); + if (Distance > NODE_ZONE * 2) continue; + PrintNodesPair(i, i + 1, Me, Next); } } } void AnalyseMeredians(void) { - int i, j ; + int i, j; - for (i=0;i= MAX_NODES_IN_MEREDIANS) printf("Too many nodes on meredians(%d,%d): %d (max %d)\n", - i, j, Meredians[i][j], MAX_NODES_IN_MEREDIANS) ; + i, j, Meredians[i][j], MAX_NODES_IN_MEREDIANS); } -int main (int argc, char * argv[]) +int main(int argc, char* argv[]) { - if (argc != 2) { - fprintf(stderr,"Usage is %s mapname\n",argv[0]) ; - exit ; + if (argc != 2) { + fprintf(stderr, "Usage is %s mapname\n", argv[0]); + exit; } - printf("DumpNodes Version %s\nBy eric@vyncke.org\n",Version) ; - load(argv[1]) ; - AnalyseNeighbours() ; - AnalyseMeredians() ; -} - + printf("DumpNodes Version %s\nBy eric@vyncke.org\n", Version); + load(argv[1]); + AnalyseNeighbours(); + AnalyseMeredians(); +} \ No newline at end of file diff --git a/Bsp2Rbn/Makefile b/Bsp2Rbn/Makefile index 4037829..85df3d4 100644 --- a/Bsp2Rbn/Makefile +++ b/Bsp2Rbn/Makefile @@ -1,14 +1,14 @@ # CPP must be g++ on Linux CPP = g++ -ARCHFLAG = i586 +ARCHFLAG = i686 -METAMOD_SRCDIR = ../../metamod-1.17/metamod +METAMOD_SRCDIR = ../../metamod-p/metamod -HLSDK_BASEDIR = ../../HLSDK +HLSDK_BASEDIR = ../../hlsdk-2.3-p4 BASEFLAGS = -g -Wall -CPPFLAGS = ${BASEFLAGS} -march=${ARCHFLAG} -O2 -w -I"${METAMOD_SRCDIR}" -I"${HLSDK_BASEDIR}/multiplayer/common" -I"${HLSDK_BASEDIR}/multiplayer/dlls" -I"${HLSDK_BASEDIR}/multiplayer/engine" -I"${HLSDK_BASEDIR}/multiplayer/pm_shared" +CPPFLAGS = ${BASEFLAGS} -march=${ARCHFLAG} -O2 -w -I"${METAMOD_SRCDIR}" -I"${HLSDK_BASEDIR}/multiplayer/common" -I"${HLSDK_BASEDIR}/multiplayer/dlls" -I"${HLSDK_BASEDIR}/multiplayer/engine" -I"${HLSDK_BASEDIR}/multiplayer/pm_shared" -I"${HLSDK_BASEDIR}/multiplayer/public" all: Bsp2Rbn DumpNodes DrawNodes @@ -34,4 +34,4 @@ clean: %.o: %.c ${CPP} ${CPPFLAGS} -c $< -o $@ - + diff --git a/Bsp2Rbn/bspfile.cpp b/Bsp2Rbn/bspfile.cpp index 046b1cd..c02cbe5 100644 --- a/Bsp2Rbn/bspfile.cpp +++ b/Bsp2Rbn/bspfile.cpp @@ -2,8 +2,8 @@ * * Copyright (c) 1998, Valve LLC. All rights reserved. * -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * ****/ @@ -27,70 +27,69 @@ // Make all of these dynamice for BSP_tool... int nummodels = 0; -dmodel_t *dmodels = NULL; +dmodel_t* dmodels = NULL; int visdatasize = 0; -byte *dvisdata = NULL; +byte* dvisdata = NULL; int lightdatasize = 0; -byte *dlightdata = NULL; +byte* dlightdata = NULL; int texdatasize = 0; -byte *dtexdata = NULL; +byte* dtexdata = NULL; int entdatasize = 0; -char *dentdata = NULL; +char* dentdata = NULL; int numleafs = 0; -dleaf_t *dleafs = NULL; +dleaf_t* dleafs = NULL; int numplanes = 0; -dplane_t *dplanes = NULL; +dplane_t* dplanes = NULL; int numvertexes = 0; -dvertex_t *dvertexes = NULL; +dvertex_t* dvertexes = NULL; int numnodes = 0; -dnode_t *dnodes = NULL; +dnode_t* dnodes = NULL; int numtexinfo = 0; -texinfo_t *texinfo = NULL; +texinfo_t* texinfo = NULL; int numfaces = 0; -dface_t *dfaces = NULL; +dface_t* dfaces = NULL; int numclipnodes = 0; -dclipnode_t *dclipnodes = NULL; +dclipnode_t* dclipnodes = NULL; int numedges = 0; -dedge_t *dedges = NULL; +dedge_t* dedges = NULL; int nummarksurfaces = 0; -unsigned short *dmarksurfaces = NULL; +unsigned short* dmarksurfaces = NULL; int numsurfedges = 0; -int *dsurfedges = NULL; +int* dsurfedges = NULL; int num_entities = 0; entity_t entities[MAX_MAP_ENTITIES]; - #ifdef __linux__ -unsigned _rotl ( unsigned val, int shift) +unsigned _rotl(unsigned val, int shift) { - register unsigned hibit; /* non-zero means hi bit set */ - register unsigned num = val; /* number to rotate */ - - shift &= 0x1f; /* modulo 32 -- this will also make - negative shifts work */ - while (shift--) { - hibit = num & 0x80000000; /* get high bit */ - num <<= 1; /* shift left one bit */ - if (hibit) - num |= 1; /* set lo bit if hi bit was set */ - } - - return num; + register unsigned hibit; /* non-zero means hi bit set */ + register unsigned num = val; /* number to rotate */ + + shift &= 0x1f; /* modulo 32 -- this will also make + negative shifts work */ + while (shift--) { + hibit = num & 0x80000000; /* get high bit */ + num <<= 1; /* shift left one bit */ + if (hibit) + num |= 1; /* set lo bit if hi bit was set */ + } + + return num; } #endif @@ -100,17 +99,17 @@ FastChecksum =============== */ -int FastChecksum(char *buffer, int bytes) +int FastChecksum(char* buffer, int bytes) { - int checksum = 0; + int checksum = 0; - while( bytes-- ) - { - checksum = _rotl(checksum, 4) ^ *(char *)buffer; - buffer++; - } + while (bytes--) + { + checksum = _rotl(checksum, 4) ^ *(char*)buffer; + buffer++; + } - return checksum; + return checksum; } /* @@ -118,66 +117,65 @@ int FastChecksum(char *buffer, int bytes) CompressVis =============== */ -int CompressVis (byte *vis, byte *dest) +int CompressVis(byte* vis, byte* dest) { - int j; - int rep; - int visrow; - byte *dest_p; - - dest_p = dest; - visrow = (numleafs + 7)>>3; - - for (j=0 ; j> 3; + + for (j = 0; j < visrow; j++) + { + *dest_p++ = vis[j]; + if (vis[j]) + continue; + + rep = 1; + for (j++; j < visrow; j++) + if (vis[j] || rep == 255) + break; + else + rep++; + *dest_p++ = rep; + j--; + } + + return dest_p - dest; } - /* =================== DecompressVis =================== */ -void DecompressVis (byte *in, byte *decompressed) +void DecompressVis(byte* in, byte* decompressed) { - int c; - byte *out; - int row; - - row = (numleafs+7)>>3; - out = decompressed; - - do - { - if (*in) - { - *out++ = *in++; - continue; - } - - c = in[1]; - in += 2; - while (c) - { - *out++ = 0; - c--; - } - } while (out - decompressed < row); + int c; + byte* out; + int row; + + row = (numleafs + 7) >> 3; + out = decompressed; + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); } //============================================================================= @@ -189,186 +187,184 @@ SwapBSPFile Byte swaps all data in a bsp file. ============= */ -void SwapBSPFile (qboolean todisk) +void SwapBSPFile(qboolean todisk) { - int i, j, c; - dmodel_t *d; - dmiptexlump_t *mtl; - - -// models - for (i=0 ; iheadnode[j] = LittleLong (d->headnode[j]); - - d->visleafs = LittleLong (d->visleafs); - d->firstface = LittleLong (d->firstface); - d->numfaces = LittleLong (d->numfaces); - - for (j=0 ; j<3 ; j++) - { - d->mins[j] = LittleFloat(d->mins[j]); - d->maxs[j] = LittleFloat(d->maxs[j]); - d->origin[j] = LittleFloat(d->origin[j]); - } - } - -// -// vertexes -// - for (i=0 ; inummiptex; - else - c = LittleLong(mtl->nummiptex); - mtl->nummiptex = LittleLong (mtl->nummiptex); - for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); - } - -// -// marksurfaces -// - for (i=0 ; iheadnode[j] = LittleLong(d->headnode[j]); + + d->visleafs = LittleLong(d->visleafs); + d->firstface = LittleLong(d->firstface); + d->numfaces = LittleLong(d->numfaces); + + for (j = 0; j < 3; j++) + { + d->mins[j] = LittleFloat(d->mins[j]); + d->maxs[j] = LittleFloat(d->maxs[j]); + d->origin[j] = LittleFloat(d->origin[j]); + } + } + + // + // vertexes + // + for (i = 0; i < numvertexes; i++) + { + for (j = 0; j < 3; j++) + dvertexes[i].point[j] = LittleFloat(dvertexes[i].point[j]); + } + + // + // planes + // + for (i = 0; i < numplanes; i++) + { + for (j = 0; j < 3; j++) + dplanes[i].normal[j] = LittleFloat(dplanes[i].normal[j]); + dplanes[i].dist = LittleFloat(dplanes[i].dist); + dplanes[i].type = LittleLong(dplanes[i].type); + } + + // + // texinfos + // + for (i = 0; i < numtexinfo; i++) + { + for (j = 0; j < 8; j++) + texinfo[i].vecs[0][j] = LittleFloat(texinfo[i].vecs[0][j]); + texinfo[i].miptex = LittleLong(texinfo[i].miptex); + texinfo[i].flags = LittleLong(texinfo[i].flags); + } + + // + // faces + // + for (i = 0; i < numfaces; i++) + { + dfaces[i].texinfo = LittleShort(dfaces[i].texinfo); + dfaces[i].planenum = LittleShort(dfaces[i].planenum); + dfaces[i].side = LittleShort(dfaces[i].side); + dfaces[i].lightofs = LittleLong(dfaces[i].lightofs); + dfaces[i].firstedge = LittleLong(dfaces[i].firstedge); + dfaces[i].numedges = LittleShort(dfaces[i].numedges); + } + + // + // nodes + // + for (i = 0; i < numnodes; i++) + { + dnodes[i].planenum = LittleLong(dnodes[i].planenum); + for (j = 0; j < 3; j++) + { + dnodes[i].mins[j] = LittleShort(dnodes[i].mins[j]); + dnodes[i].maxs[j] = LittleShort(dnodes[i].maxs[j]); + } + dnodes[i].children[0] = LittleShort(dnodes[i].children[0]); + dnodes[i].children[1] = LittleShort(dnodes[i].children[1]); + dnodes[i].firstface = LittleShort(dnodes[i].firstface); + dnodes[i].numfaces = LittleShort(dnodes[i].numfaces); + } + + // + // leafs + // + for (i = 0; i < numleafs; i++) + { + dleafs[i].contents = LittleLong(dleafs[i].contents); + for (j = 0; j < 3; j++) + { + dleafs[i].mins[j] = LittleShort(dleafs[i].mins[j]); + dleafs[i].maxs[j] = LittleShort(dleafs[i].maxs[j]); + } + + dleafs[i].firstmarksurface = LittleShort(dleafs[i].firstmarksurface); + dleafs[i].nummarksurfaces = LittleShort(dleafs[i].nummarksurfaces); + dleafs[i].visofs = LittleLong(dleafs[i].visofs); + } + + // + // clipnodes + // + for (i = 0; i < numclipnodes; i++) + { + dclipnodes[i].planenum = LittleLong(dclipnodes[i].planenum); + dclipnodes[i].children[0] = LittleShort(dclipnodes[i].children[0]); + dclipnodes[i].children[1] = LittleShort(dclipnodes[i].children[1]); + } + + // + // miptex + // + if (texdatasize) + { + mtl = (dmiptexlump_t*)dtexdata; + if (todisk) + c = mtl->nummiptex; + else + c = LittleLong(mtl->nummiptex); + mtl->nummiptex = LittleLong(mtl->nummiptex); + for (i = 0; i < c; i++) + mtl->dataofs[i] = LittleLong(mtl->dataofs[i]); + } + + // + // marksurfaces + // + for (i = 0; i < nummarksurfaces; i++) + dmarksurfaces[i] = LittleShort(dmarksurfaces[i]); + + // + // surfedges + // + for (i = 0; i < numsurfedges; i++) + dsurfedges[i] = LittleLong(dsurfedges[i]); + + // + // edges + // + for (i = 0; i < numedges; i++) + { + dedges[i].v[0] = LittleShort(dedges[i].v[0]); + dedges[i].v[1] = LittleShort(dedges[i].v[1]); + } } +dheader_t* header; -dheader_t *header; - -int CopyLump (int lump, void **dest, int size) +int CopyLump(int lump, void** dest, int size) { - int length, ofs; - int num; + int length, ofs; + int num; - length = header->lumps[lump].filelen; - ofs = header->lumps[lump].fileofs; + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; - if (length % size) - Error ("LoadBSPFile: odd lump size"); + if (length % size) + Error("LoadBSPFile: odd lump size"); - num = length / size; + num = length / size; - if (*dest == NULL) - { - // allocate memory for this lump... - *dest = malloc(num * size); + if (*dest == NULL) + { + // allocate memory for this lump... + *dest = malloc(num * size); - if (*dest == NULL) - Error("Error allocating memory for BSP file lump!\n"); - } + if (*dest == NULL) + Error("Error allocating memory for BSP file lump!\n"); + } - memcpy (*dest, (byte *)header + ofs, length); + memcpy(*dest, (byte*)header + ofs, length); - return num; + return num; } /* @@ -376,82 +372,81 @@ int CopyLump (int lump, void **dest, int size) LoadBSPFile ============= */ -void LoadBSPFile (char *filename) +void LoadBSPFile(char* filename) { - int i; - -// -// load the file header -// - if (LoadFile (filename, (void **)&header) < sizeof(dheader_t)) - Error ("File %s is too short",filename) ; - - if (header == NULL) - Error ("Cannot read file %s",filename) ; - -// swap the header - for (i=0 ; i< sizeof(dheader_t)/4 ; i++) - ((int *)header)[i] = LittleLong ( ((int *)header)[i]); - - if (header->version != BSPVERSION) - Error ("%s is version %i, not %i", filename, header->version, BSPVERSION); - - nummodels = CopyLump (LUMP_MODELS, (void **)&dmodels, sizeof(dmodel_t)); - numvertexes = CopyLump (LUMP_VERTEXES, (void **)&dvertexes, sizeof(dvertex_t)); - numplanes = CopyLump (LUMP_PLANES, (void **)&dplanes, sizeof(dplane_t)); - numleafs = CopyLump (LUMP_LEAFS, (void **)&dleafs, sizeof(dleaf_t)); - numnodes = CopyLump (LUMP_NODES, (void **)&dnodes, sizeof(dnode_t)); - numtexinfo = CopyLump (LUMP_TEXINFO, (void **)&texinfo, sizeof(texinfo_t)); - numclipnodes = CopyLump (LUMP_CLIPNODES, (void **)&dclipnodes, sizeof(dclipnode_t)); - numfaces = CopyLump (LUMP_FACES, (void **)&dfaces, sizeof(dface_t)); - nummarksurfaces = CopyLump (LUMP_MARKSURFACES, (void **)&dmarksurfaces, sizeof(dmarksurfaces[0])); - numsurfedges = CopyLump (LUMP_SURFEDGES, (void **)&dsurfedges, sizeof(dsurfedges[0])); - numedges = CopyLump (LUMP_EDGES, (void **)&dedges, sizeof(dedge_t)); - - texdatasize = CopyLump (LUMP_TEXTURES, (void **)&dtexdata, 1); - visdatasize = CopyLump (LUMP_VISIBILITY, (void **)&dvisdata, 1); - lightdatasize = CopyLump (LUMP_LIGHTING, (void **)&dlightdata, 1); - entdatasize = CopyLump (LUMP_ENTITIES, (void **)&dentdata, 1); - - free (header); // everything has been copied out - -// -// swap everything -// - SwapBSPFile (false); - -// dmodels_checksum = FastChecksum( (char *)dmodels, nummodels*sizeof(dmodels[0]) ); -// dvertexes_checksum = FastChecksum( (char *)dvertexes, numvertexes*sizeof(dvertexes[0]) ); -// dplanes_checksum = FastChecksum( (char *)dplanes, numplanes*sizeof(dplanes[0]) ); -// dleafs_checksum = FastChecksum( (char *)dleafs, numleafs*sizeof(dleafs[0]) ); -// dnodes_checksum = FastChecksum( (char *)dnodes, numnodes*sizeof(dnodes[0]) ); -// texinfo_checksum = FastChecksum( (char *)texinfo, numtexinfo*sizeof(texinfo[0]) ); -// dclipnodes_checksum = FastChecksum( (char *)dclipnodes, numclipnodes*sizeof(dclipnodes[0]) ); -// dfaces_checksum = FastChecksum( (char *)dfaces, numfaces*sizeof(dfaces[0]) ); -// dmarksurfaces_checksum = FastChecksum( (char *)dmarksurfaces, nummarksurfaces*sizeof(dmarksurfaces[0]) ); -// dsurfedges_checksum = FastChecksum( (char *)dsurfedges, numsurfedges*sizeof(dsurfedges[0]) ); -// dedges_checksum = FastChecksum( (char *)dedges, numedges*sizeof(dedges[0]) ); -// dtexdata_checksum = FastChecksum( (char *)dtexdata, numedges*sizeof(dtexdata[0]) ); -// dvisdata_checksum = FastChecksum( (char *)dvisdata, visdatasize*sizeof(dvisdata[0]) ); -// dlightdata_checksum = FastChecksum( (char *)dlightdata, lightdatasize*sizeof(dlightdata[0]) ); -// dentdata_checksum = FastChecksum( (char *)dentdata, entdatasize*sizeof(dentdata[0]) ); - + int i; + + // + // load the file header + // + if (LoadFile(filename, (void**)&header) < sizeof(dheader_t)) + Error("File %s is too short", filename); + + if (header == NULL) + Error("Cannot read file %s", filename); + + // swap the header + for (i = 0; i < sizeof(dheader_t) / 4; i++) + ((int*)header)[i] = LittleLong(((int*)header)[i]); + + if (header->version != BSPVERSION) + Error("%s is version %i, not %i", filename, header->version, BSPVERSION); + + nummodels = CopyLump(LUMP_MODELS, (void**)&dmodels, sizeof(dmodel_t)); + numvertexes = CopyLump(LUMP_VERTEXES, (void**)&dvertexes, sizeof(dvertex_t)); + numplanes = CopyLump(LUMP_PLANES, (void**)&dplanes, sizeof(dplane_t)); + numleafs = CopyLump(LUMP_LEAFS, (void**)&dleafs, sizeof(dleaf_t)); + numnodes = CopyLump(LUMP_NODES, (void**)&dnodes, sizeof(dnode_t)); + numtexinfo = CopyLump(LUMP_TEXINFO, (void**)&texinfo, sizeof(texinfo_t)); + numclipnodes = CopyLump(LUMP_CLIPNODES, (void**)&dclipnodes, sizeof(dclipnode_t)); + numfaces = CopyLump(LUMP_FACES, (void**)&dfaces, sizeof(dface_t)); + nummarksurfaces = CopyLump(LUMP_MARKSURFACES, (void**)&dmarksurfaces, sizeof(dmarksurfaces[0])); + numsurfedges = CopyLump(LUMP_SURFEDGES, (void**)&dsurfedges, sizeof(dsurfedges[0])); + numedges = CopyLump(LUMP_EDGES, (void**)&dedges, sizeof(dedge_t)); + + texdatasize = CopyLump(LUMP_TEXTURES, (void**)&dtexdata, 1); + visdatasize = CopyLump(LUMP_VISIBILITY, (void**)&dvisdata, 1); + lightdatasize = CopyLump(LUMP_LIGHTING, (void**)&dlightdata, 1); + entdatasize = CopyLump(LUMP_ENTITIES, (void**)&dentdata, 1); + + free(header); // everything has been copied out + + // + // swap everything + // + SwapBSPFile(false); + + // dmodels_checksum = FastChecksum( (char *)dmodels, nummodels*sizeof(dmodels[0]) ); + // dvertexes_checksum = FastChecksum( (char *)dvertexes, numvertexes*sizeof(dvertexes[0]) ); + // dplanes_checksum = FastChecksum( (char *)dplanes, numplanes*sizeof(dplanes[0]) ); + // dleafs_checksum = FastChecksum( (char *)dleafs, numleafs*sizeof(dleafs[0]) ); + // dnodes_checksum = FastChecksum( (char *)dnodes, numnodes*sizeof(dnodes[0]) ); + // texinfo_checksum = FastChecksum( (char *)texinfo, numtexinfo*sizeof(texinfo[0]) ); + // dclipnodes_checksum = FastChecksum( (char *)dclipnodes, numclipnodes*sizeof(dclipnodes[0]) ); + // dfaces_checksum = FastChecksum( (char *)dfaces, numfaces*sizeof(dfaces[0]) ); + // dmarksurfaces_checksum = FastChecksum( (char *)dmarksurfaces, nummarksurfaces*sizeof(dmarksurfaces[0]) ); + // dsurfedges_checksum = FastChecksum( (char *)dsurfedges, numsurfedges*sizeof(dsurfedges[0]) ); + // dedges_checksum = FastChecksum( (char *)dedges, numedges*sizeof(dedges[0]) ); + // dtexdata_checksum = FastChecksum( (char *)dtexdata, numedges*sizeof(dtexdata[0]) ); + // dvisdata_checksum = FastChecksum( (char *)dvisdata, visdatasize*sizeof(dvisdata[0]) ); + // dlightdata_checksum = FastChecksum( (char *)dlightdata, lightdatasize*sizeof(dlightdata[0]) ); + // dentdata_checksum = FastChecksum( (char *)dentdata, entdatasize*sizeof(dentdata[0]) ); } //============================================================================ -FILE *wadfile; +FILE* wadfile; dheader_t outheader; -void AddLump (int lumpnum, void *data, int len) +void AddLump(int lumpnum, void* data, int len) { - lump_t *lump; + lump_t* lump; - lump = &header->lumps[lumpnum]; + lump = &header->lumps[lumpnum]; - lump->fileofs = LittleLong( ftell(wadfile) ); - lump->filelen = LittleLong(len); - SafeWrite (wadfile, data, (len+3)&~3); + lump->fileofs = LittleLong(ftell(wadfile)); + lump->filelen = LittleLong(len); + SafeWrite(wadfile, data, (len + 3) & ~3); } /* @@ -461,38 +456,38 @@ WriteBSPFile Swaps the bsp file in place, so it should not be referenced again ============= */ -void WriteBSPFile (char *filename) -{ - header = &outheader; - memset (header, 0, sizeof(dheader_t)); - - SwapBSPFile (true); - - header->version = LittleLong (BSPVERSION); - - wadfile = SafeOpenWrite (filename); - SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later - - AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t)); - AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t)); - AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t)); - AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t)); - AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t)); - AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t)); - AddLump (LUMP_CLIPNODES, dclipnodes, numclipnodes*sizeof(dclipnode_t)); - AddLump (LUMP_MARKSURFACES, dmarksurfaces, nummarksurfaces*sizeof(dmarksurfaces[0])); - AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0])); - AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t)); - AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t)); - - AddLump (LUMP_LIGHTING, dlightdata, lightdatasize); - AddLump (LUMP_VISIBILITY, dvisdata, visdatasize); - AddLump (LUMP_ENTITIES, dentdata, entdatasize); - AddLump (LUMP_TEXTURES, dtexdata, texdatasize); - - fseek (wadfile, 0, SEEK_SET); - SafeWrite (wadfile, header, sizeof(dheader_t)); - fclose (wadfile); +void WriteBSPFile(char* filename) +{ + header = &outheader; + memset(header, 0, sizeof(dheader_t)); + + SwapBSPFile(true); + + header->version = LittleLong(BSPVERSION); + + wadfile = SafeOpenWrite(filename); + SafeWrite(wadfile, header, sizeof(dheader_t)); // overwritten later + + AddLump(LUMP_PLANES, dplanes, numplanes * sizeof(dplane_t)); + AddLump(LUMP_LEAFS, dleafs, numleafs * sizeof(dleaf_t)); + AddLump(LUMP_VERTEXES, dvertexes, numvertexes * sizeof(dvertex_t)); + AddLump(LUMP_NODES, dnodes, numnodes * sizeof(dnode_t)); + AddLump(LUMP_TEXINFO, texinfo, numtexinfo * sizeof(texinfo_t)); + AddLump(LUMP_FACES, dfaces, numfaces * sizeof(dface_t)); + AddLump(LUMP_CLIPNODES, dclipnodes, numclipnodes * sizeof(dclipnode_t)); + AddLump(LUMP_MARKSURFACES, dmarksurfaces, nummarksurfaces * sizeof(dmarksurfaces[0])); + AddLump(LUMP_SURFEDGES, dsurfedges, numsurfedges * sizeof(dsurfedges[0])); + AddLump(LUMP_EDGES, dedges, numedges * sizeof(dedge_t)); + AddLump(LUMP_MODELS, dmodels, nummodels * sizeof(dmodel_t)); + + AddLump(LUMP_LIGHTING, dlightdata, lightdatasize); + AddLump(LUMP_VISIBILITY, dvisdata, visdatasize); + AddLump(LUMP_ENTITIES, dentdata, entdatasize); + AddLump(LUMP_TEXTURES, dtexdata, texdatasize); + + fseek(wadfile, 0, SEEK_SET); + SafeWrite(wadfile, header, sizeof(dheader_t)); + fclose(wadfile); } //============================================================================ @@ -500,37 +495,37 @@ void WriteBSPFile (char *filename) //#define ENTRIES(a) (sizeof(a)/sizeof(*(a))) //#define ENTRYSIZE(a) (sizeof(*(a))) -int ArrayUsage( char *szItem, int items, int maxitems, int itemsize ) +int ArrayUsage(char* szItem, int items, int maxitems, int itemsize) { - float percentage = maxitems ? items * (float)100.0 / maxitems : (float)0.0; - - printf("%-12s %7i/%-7i %7i/%-7i (%4.1f%%)", - szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); - if ( percentage > 80.0 ) - printf( "VERY FULL!\n" ); - else if ( percentage > 95.0 ) - printf( "SIZE DANGER!\n" ); - else if ( percentage > 99.9 ) - printf( "SIZE OVERFLOW!!!\n" ); - else - printf( "\n" ); - return items * itemsize; + float percentage = maxitems ? items * (float)100.0 / maxitems : (float)0.0; + + printf("%-12s %7i/%-7i %7i/%-7i (%4.1f%%)", + szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage); + if (percentage > 80.0) + printf("VERY FULL!\n"); + else if (percentage > 95.0) + printf("SIZE DANGER!\n"); + else if (percentage > 99.9) + printf("SIZE OVERFLOW!!!\n"); + else + printf("\n"); + return items * itemsize; } -int GlobUsage( char *szItem, int itemstorage, int maxstorage ) +int GlobUsage(char* szItem, int itemstorage, int maxstorage) { - float percentage = maxstorage ? itemstorage * (float)100.0 / maxstorage : (float)0.0; - printf("%-12s [variable] %7i/%-7i (%4.1f%%)", - szItem, itemstorage, maxstorage, percentage ); - if ( percentage > 80.0 ) - printf( "VERY FULL!\n" ); - else if ( percentage > 95.0 ) - printf( "SIZE DANGER!\n" ); - else if ( percentage > 99.9 ) - printf( "SIZE OVERFLOW!!!\n" ); - else - printf( "\n" ); - return itemstorage; + float percentage = maxstorage ? itemstorage * (float)100.0 / maxstorage : (float)0.0; + printf("%-12s [variable] %7i/%-7i (%4.1f%%)", + szItem, itemstorage, maxstorage, percentage); + if (percentage > 80.0) + printf("VERY FULL!\n"); + else if (percentage > 95.0) + printf("SIZE DANGER!\n"); + else if (percentage > 99.9) + printf("SIZE OVERFLOW!!!\n"); + else + printf("\n"); + return itemstorage; } /* @@ -540,73 +535,71 @@ PrintBSPFileSizes Dumps info about current file ============= */ -void PrintBSPFileSizes (void) +void PrintBSPFileSizes(void) { - int numtextures = texdatasize ? ((dmiptexlump_t*)dtexdata)->nummiptex : 0; - int totalmemory = 0; - - printf("\n"); - printf("Object names Objects/Maxobjs Memory / Maxmem Fullness\n" ); - printf("------------ --------------- --------------- --------\n" ); - - totalmemory += ArrayUsage( "models", nummodels, MAX_MAP_MODELS, sizeof(dmodel_t) ); - totalmemory += ArrayUsage( "planes", numplanes, MAX_MAP_PLANES, sizeof(dplane_t) ); - totalmemory += ArrayUsage( "vertexes", numvertexes, MAX_MAP_VERTS, sizeof(dvertex_t) ); - totalmemory += ArrayUsage( "nodes", numnodes, MAX_MAP_NODES, sizeof(dnode_t) ); - totalmemory += ArrayUsage( "texinfos", numtexinfo, MAX_MAP_TEXINFO, sizeof(texinfo_t) ); - totalmemory += ArrayUsage( "faces", numfaces, MAX_MAP_FACES, sizeof(dface_t) ); - totalmemory += ArrayUsage( "clipnodes", numclipnodes, MAX_MAP_CLIPNODES, sizeof(dclipnode_t) ); - totalmemory += ArrayUsage( "leaves", numleafs, MAX_MAP_LEAFS, sizeof(dleaf_t) ); - totalmemory += ArrayUsage( "marksurfaces", nummarksurfaces, MAX_MAP_MARKSURFACES, sizeof(unsigned short) ); - totalmemory += ArrayUsage( "surfedges", numsurfedges, MAX_MAP_SURFEDGES, sizeof(int) ); - totalmemory += ArrayUsage( "edges", numedges, MAX_MAP_EDGES, sizeof(dedge_t) ); - - totalmemory += GlobUsage( "texdata", texdatasize, MAX_MAP_MIPTEX ); - totalmemory += GlobUsage( "lightdata", lightdatasize, MAX_MAP_LIGHTING ); - totalmemory += GlobUsage( "visdata", visdatasize, MAX_MAP_VISIBILITY ); - totalmemory += GlobUsage( "entdata", entdatasize, MAX_MAP_ENTSTRING ); - - printf( "=== Total BSP file data space used: %d bytes ===\n", totalmemory ); + int numtextures = texdatasize ? ((dmiptexlump_t*)dtexdata)->nummiptex : 0; + int totalmemory = 0; + + printf("\n"); + printf("Object names Objects/Maxobjs Memory / Maxmem Fullness\n"); + printf("------------ --------------- --------------- --------\n"); + + totalmemory += ArrayUsage("models", nummodels, MAX_MAP_MODELS, sizeof(dmodel_t)); + totalmemory += ArrayUsage("planes", numplanes, MAX_MAP_PLANES, sizeof(dplane_t)); + totalmemory += ArrayUsage("vertexes", numvertexes, MAX_MAP_VERTS, sizeof(dvertex_t)); + totalmemory += ArrayUsage("nodes", numnodes, MAX_MAP_NODES, sizeof(dnode_t)); + totalmemory += ArrayUsage("texinfos", numtexinfo, MAX_MAP_TEXINFO, sizeof(texinfo_t)); + totalmemory += ArrayUsage("faces", numfaces, MAX_MAP_FACES, sizeof(dface_t)); + totalmemory += ArrayUsage("clipnodes", numclipnodes, MAX_MAP_CLIPNODES, sizeof(dclipnode_t)); + totalmemory += ArrayUsage("leaves", numleafs, MAX_MAP_LEAFS, sizeof(dleaf_t)); + totalmemory += ArrayUsage("marksurfaces", nummarksurfaces, MAX_MAP_MARKSURFACES, sizeof(unsigned short)); + totalmemory += ArrayUsage("surfedges", numsurfedges, MAX_MAP_SURFEDGES, sizeof(int)); + totalmemory += ArrayUsage("edges", numedges, MAX_MAP_EDGES, sizeof(dedge_t)); + + totalmemory += GlobUsage("texdata", texdatasize, MAX_MAP_MIPTEX); + totalmemory += GlobUsage("lightdata", lightdatasize, MAX_MAP_LIGHTING); + totalmemory += GlobUsage("visdata", visdatasize, MAX_MAP_VISIBILITY); + totalmemory += GlobUsage("entdata", entdatasize, MAX_MAP_ENTSTRING); + + printf("=== Total BSP file data space used: %d bytes ===\n", totalmemory); } - /* ================= ParseEpair ================= */ -epair_t *ParseEpair (void) +epair_t* ParseEpair(void) { - epair_t *e; + epair_t* e; - e = (epair_t *)malloc (sizeof(epair_t)); - memset (e, 0, sizeof(epair_t)); + e = (epair_t*)malloc(sizeof(epair_t)); + memset(e, 0, sizeof(epair_t)); - if (strlen(token) >= MAX_KEY-1) - Error ("ParseEpar: token too long"); - e->key = copystring(token); - GetToken (false); - if (strlen(token) >= MAX_VALUE-1) - Error ("ParseEpar: token too long"); - e->value = copystring(token); + if (strlen(token) >= MAX_KEY - 1) + Error("ParseEpar: token too long"); + e->key = copystring(token); + GetToken(false); + if (strlen(token) >= MAX_VALUE - 1) + Error("ParseEpar: token too long"); + e->value = copystring(token); - return e; + return e; } - void DumpEntity(int i) { - epair_t *e; - entity_t *mapent; - - if ((i<0) || (i>=num_entities)) return ; - mapent = &entities[i]; - e = mapent->epairs ; - printf("Entity #%d:\n",i) ; - while (e != NULL) { - printf("\t%s = %s\n",e->key,e->value) ; - e=e->next ; - } + epair_t* e; + entity_t* mapent; + + if ((i < 0) || (i >= num_entities)) return; + mapent = &entities[i]; + e = mapent->epairs; + printf("Entity #%d:\n", i); + while (e != NULL) { + printf("\t%s = %s\n", e->key, e->value); + e = e->next; + } } /* @@ -614,35 +607,35 @@ void DumpEntity(int i) ParseEntity ================ */ -qboolean ParseEntity (void) +qboolean ParseEntity(void) { - epair_t *e; - entity_t *mapent; + epair_t* e; + entity_t* mapent; - if (!GetToken (true)) - return false; + if (!GetToken(true)) + return false; - if (strcmp (token, "{") ) - Error ("ParseEntity: { not found"); + if (strcmp(token, "{")) + Error("ParseEntity: { not found"); - if (num_entities == MAX_MAP_ENTITIES) - Error ("num_entities == MAX_MAP_ENTITIES"); + if (num_entities == MAX_MAP_ENTITIES) + Error("num_entities == MAX_MAP_ENTITIES"); - mapent = &entities[num_entities]; - num_entities++; + mapent = &entities[num_entities]; + num_entities++; - do - { - if (!GetToken (true)) - Error ("ParseEntity: EOF without closing brace"); - if (!strcmp (token, "}") ) - break; - e = ParseEpair (); - e->next = mapent->epairs; - mapent->epairs = e; - } while (1); + do + { + if (!GetToken(true)) + Error("ParseEntity: EOF without closing brace"); + if (!strcmp(token, "}")) + break; + e = ParseEpair(); + e->next = mapent->epairs; + mapent->epairs = e; + } while (1); - return true; + return true; } /* @@ -652,17 +645,16 @@ ParseEntities Parses the dentdata string into entities ================ */ -void ParseEntities (void) +void ParseEntities(void) { - num_entities = 0; - ParseFromMemory (dentdata, entdatasize); + num_entities = 0; + ParseFromMemory(dentdata, entdatasize); - while (ParseEntity ()) - { - } + while (ParseEntity()) + { + } } - /* ================ UnparseEntities @@ -670,211 +662,209 @@ UnparseEntities Generates the dentdata string from all the entities ================ */ -void UnparseEntities (void) +void UnparseEntities(void) { - char *buf, *end; - epair_t *ep; - char line[2048]; - int i; - - buf = dentdata; - end = buf; - *end = 0; - - for (i=0 ; inext) - { - sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value); - strcat (end, line); - end += strlen(line); - } - strcat (end,"}\n"); - end += 2; - - if (end > buf + MAX_MAP_ENTSTRING) - Error ("Entity text too long"); - } - entdatasize = end - buf + 1; + char* buf, * end; + epair_t* ep; + char line[2048]; + int i; + + buf = dentdata; + end = buf; + *end = 0; + + for (i = 0; i < num_entities; i++) + { + ep = entities[i].epairs; + if (!ep) + continue; // ent got removed + + strcat(end, "{\n"); + end += 2; + + for (ep = entities[i].epairs; ep; ep = ep->next) + { + sprintf(line, "\"%s\" \"%s\"\n", ep->key, ep->value); + strcat(end, line); + end += strlen(line); + } + strcat(end, "}\n"); + end += 2; + + if (end > buf + MAX_MAP_ENTSTRING) + Error("Entity text too long"); + } + entdatasize = end - buf + 1; } -void SetKeyValue (entity_t *ent, char *key, char *value) +void SetKeyValue(entity_t* ent, char* key, char* value) { - epair_t *ep; - epair_t *prev_ep = NULL; - - for (ep=ent->epairs ; ep ; ep=ep->next) - { - if (!strcmp (ep->key, key) ) - { - free (ep->value); - ep->value = copystring(value); - return; - } - } - - prev_ep = NULL; - for (ep=ent->epairs ; ep ; prev_ep=ep, ep=ep->next) - ; // get to the end of the linked list - - ep = (epair_t *)malloc (sizeof(*ep)); - memset(ep, 0, sizeof(ep)); - - if (prev_ep) - prev_ep->next = ep; // link it at the end - else - ent->epairs = ep; // link it at the beginning (first and only) - - ep->key = copystring(key); - ep->value = copystring(value); + epair_t* ep; + epair_t* prev_ep = NULL; + + for (ep = ent->epairs; ep; ep = ep->next) + { + if (!strcmp(ep->key, key)) + { + free(ep->value); + ep->value = copystring(value); + return; + } + } + + prev_ep = NULL; + for (ep = ent->epairs; ep; prev_ep = ep, ep = ep->next) + ; // get to the end of the linked list + + ep = (epair_t*)malloc(sizeof(*ep)); + memset(ep, 0, sizeof(ep)); + + if (prev_ep) + prev_ep->next = ep; // link it at the end + else + ent->epairs = ep; // link it at the beginning (first and only) + + ep->key = copystring(key); + ep->value = copystring(value); } -void RemoveKey(entity_t *ent, char *key) +void RemoveKey(entity_t* ent, char* key) { - epair_t *ep; - epair_t *prev_ep = NULL; - - for (ep=ent->epairs ; ep ; ep=ep->next) - { - if (!strcmp (ep->key, key) ) - { - free (ep->value); - free (ep->key); - if (prev_ep) - { - prev_ep->next = ep->next; - free(ep); - } - else - { - ent->epairs = ep->next; - free(ep); - } - return; - } - prev_ep = ep; - } + epair_t* ep; + epair_t* prev_ep = NULL; + + for (ep = ent->epairs; ep; ep = ep->next) + { + if (!strcmp(ep->key, key)) + { + free(ep->value); + free(ep->key); + if (prev_ep) + { + prev_ep->next = ep->next; + free(ep); + } + else + { + ent->epairs = ep->next; + free(ep); + } + return; + } + prev_ep = ep; + } } -int FindEntityByClassname(int index, const char *classname) +int FindEntityByClassname(int index, const char* classname) { - epair_t *ep; + epair_t* ep; - // index should be -1 to start at first entity... - index++; + // index should be -1 to start at first entity... + index++; - while (index < num_entities) - { - ep = entities[index].epairs; + while (index < num_entities) + { + ep = entities[index].epairs; - while (ep) - { - if ((strcmp(ep->key, "classname") == 0) && - (strcmp(ep->value, classname) == 0)) - { - return index; - } + while (ep) + { + if ((strcmp(ep->key, "classname") == 0) && + (strcmp(ep->value, classname) == 0)) + { + return index; + } - ep = ep->next; - } + ep = ep->next; + } - index ++; - } + index++; + } - return -1; // entity not found + return -1; // entity not found } -int FindEntityByWildcard(int index, const char *classname, int length) +int FindEntityByWildcard(int index, const char* classname, int length) { - epair_t *ep; + epair_t* ep; - // index should be -1 to start at first entity... - index++; + // index should be -1 to start at first entity... + index++; - while (index < num_entities) - { - ep = entities[index].epairs; + while (index < num_entities) + { + ep = entities[index].epairs; - while (ep) - { - if ((strcmp(ep->key, "classname") == 0) && - (strncmp(ep->value, classname, length) == 0)) - { - return index; - } + while (ep) + { + if ((strcmp(ep->key, "classname") == 0) && + (strncmp(ep->value, classname, length) == 0)) + { + return index; + } - ep = ep->next; - } + ep = ep->next; + } - index ++; - } + index++; + } - return -1; // entity not found + return -1; // entity not found } -char *ValueForKey (entity_t *ent, char *key) +char* ValueForKey(entity_t* ent, char* key) { - epair_t *ep; + epair_t* ep; - for (ep=ent->epairs ; ep ; ep=ep->next) - if (!strcmp (ep->key, key) ) - return ep->value; - return ""; + for (ep = ent->epairs; ep; ep = ep->next) + if (!strcmp(ep->key, key)) + return ep->value; + return ""; } -vec_t FloatForKey (entity_t *ent, char *key) +vec_t FloatForKey(entity_t* ent, char* key) { - char *k; + char* k; - k = ValueForKey (ent, key); - return (float)atof(k); + k = ValueForKey(ent, key); + return (float)atof(k); } -void GetVectorForKey (entity_t *ent, char *key, vec3_t vec) +void GetVectorForKey(entity_t* ent, char* key, vec3_t vec) { - char *k; - double v1, v2, v3; - - k = ValueForKey (ent, key); -// scanf into doubles, then assign, so it is vec_t size independent - v1 = v2 = v3 = 0; - sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); - vec[0] = (float)v1; - vec[1] = (float)v2; - vec[2] = (float)v3; + char* k; + double v1, v2, v3; + + k = ValueForKey(ent, key); + // scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = v3 = 0; + sscanf(k, "%lf %lf %lf", &v1, &v2, &v3); + vec[0] = (float)v1; + vec[1] = (float)v2; + vec[2] = (float)v3; } - void FreeEntities(void) { - int i; - epair_t *pEpair, *pEpairNext; - - for (i=0; i < num_entities; i++) - { - pEpair = entities[i].epairs; - - while (pEpair) - { - pEpairNext = pEpair->next; - free(pEpair->key); - free(pEpair->value); - free(pEpair); - pEpair = pEpairNext; - } - -// num_entities = 0; EVY - } - num_entities = 0; // EVY: should rather be here - - for (i=0; i < MAX_MAP_ENTITIES; i++) - entities[i].epairs = NULL; -} - + int i; + epair_t* pEpair, * pEpairNext; + + for (i = 0; i < num_entities; i++) + { + pEpair = entities[i].epairs; + + while (pEpair) + { + pEpairNext = pEpair->next; + free(pEpair->key); + free(pEpair->value); + free(pEpair); + pEpair = pEpairNext; + } + + // num_entities = 0; EVY + } + num_entities = 0; // EVY: should rather be here + + for (i = 0; i < MAX_MAP_ENTITIES; i++) + entities[i].epairs = NULL; +} \ No newline at end of file diff --git a/Bsp2Rbn/bspfile.h b/Bsp2Rbn/bspfile.h index 4f015ac..8aef463 100644 --- a/Bsp2Rbn/bspfile.h +++ b/Bsp2Rbn/bspfile.h @@ -1,339 +1,321 @@ -/*** -* -* Copyright (c) 1998, Valve LLC. 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. -* -****/ - -#ifndef __CMDLIB__ -#include "cmdlib.h" -#endif - -#ifndef BSPFILE_H -#define BSPFILE_H - - -// upper design bounds - -#define MAX_MAP_HULLS 4 - -#define MAX_MAP_MODELS 400 -#define MAX_MAP_BRUSHES 4096 -#define MAX_MAP_ENTITIES 1024 -#define MAX_MAP_ENTSTRING (128*1024) - -#define MAX_MAP_PLANES 32767 -#define MAX_MAP_NODES 32767 // because negative shorts are contents -#define MAX_MAP_CLIPNODES 32767 // -#define MAX_MAP_LEAFS 8192 -#define MAX_MAP_VERTS 65535 -#define MAX_MAP_FACES 65535 -#define MAX_MAP_MARKSURFACES 65535 -#define MAX_MAP_TEXINFO 8192 -#define MAX_MAP_EDGES 256000 -#define MAX_MAP_SURFEDGES 512000 -#define MAX_MAP_TEXTURES 512 -#define MAX_MAP_MIPTEX 0x200000 -#define MAX_MAP_LIGHTING 0x200000 -#define MAX_MAP_VISIBILITY 0x200000 - -#define MAX_MAP_PORTALS 65536 - -// key / value pair sizes - -#define MAX_KEY 32 -#define MAX_VALUE 4096 - -//============================================================================= - - -#define BSPVERSION 30 -#define TOOLVERSION 2 - - -typedef struct -{ - int fileofs, filelen; -} lump_t; - -#define LUMP_ENTITIES 0 -#define LUMP_PLANES 1 -#define LUMP_TEXTURES 2 -#define LUMP_VERTEXES 3 -#define LUMP_VISIBILITY 4 -#define LUMP_NODES 5 -#define LUMP_TEXINFO 6 -#define LUMP_FACES 7 -#define LUMP_LIGHTING 8 -#define LUMP_CLIPNODES 9 -#define LUMP_LEAFS 10 -#define LUMP_MARKSURFACES 11 -#define LUMP_EDGES 12 -#define LUMP_SURFEDGES 13 -#define LUMP_MODELS 14 - -#define HEADER_LUMPS 15 - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; - int headnode[MAX_MAP_HULLS]; - int visleafs; // not including the solid leaf 0 - int firstface, numfaces; -} dmodel_t; - -typedef struct -{ - int version; - lump_t lumps[HEADER_LUMPS]; -} dheader_t; - -typedef struct -{ - int nummiptex; - int dataofs[4]; // [nummiptex] -} dmiptexlump_t; - -#define MIPLEVELS 4 -typedef struct miptex_s -{ - char name[16]; - unsigned width, height; - unsigned offsets[MIPLEVELS]; // four mip maps stored -} miptex_t; - - -typedef struct -{ - float point[3]; -} dvertex_t; - - -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 - -// 3-5 are non-axial planes snapped to the nearest -#define PLANE_ANYX 3 -#define PLANE_ANYY 4 -#define PLANE_ANYZ 5 - -typedef struct -{ - float normal[3]; - float dist; - int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate -} dplane_t; - - - -#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_ORIGIN -7 // removed at csg time -#define CONTENTS_CLIP -8 // changed to contents_solid - -#define CONTENTS_CURRENT_0 -9 -#define CONTENTS_CURRENT_90 -10 -#define CONTENTS_CURRENT_180 -11 -#define CONTENTS_CURRENT_270 -12 -#define CONTENTS_CURRENT_UP -13 -#define CONTENTS_CURRENT_DOWN -14 - -#define CONTENTS_TRANSLUCENT -15 - -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct -{ - int planenum; - short children[2]; // negative numbers are -(leafs+1), not nodes - short mins[3]; // for sphere culling - short maxs[3]; - unsigned short firstface; - unsigned short numfaces; // counting both sides -} dnode_t; - -typedef struct -{ - int planenum; - short children[2]; // negative numbers are contents -} dclipnode_t; - - -typedef struct texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int miptex; - int flags; -} texinfo_t; - -#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision - -// note that edge 0 is never used, because negative edge nums are used for -// counterclockwise use of the edge in a face -typedef struct -{ - unsigned short v[2]; // vertex numbers -} dedge_t; - -#define MAXLIGHTMAPS 4 - -typedef struct -{ - short planenum; - short side; - - int firstedge; // we must support > 64k edges - short numedges; - short texinfo; - -// lighting info - byte styles[MAXLIGHTMAPS]; - int lightofs; // start of [numstyles*surfsize] samples -} dface_t; - - - -#define AMBIENT_WATER 0 -#define AMBIENT_SKY 1 -#define AMBIENT_SLIME 2 -#define AMBIENT_LAVA 3 - -#define NUM_AMBIENTS 4 // automatic ambient sounds - -// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas -// all other leafs need visibility info -typedef struct -{ - int contents; - int visofs; // -1 = no visibility info - - short mins[3]; // for frustum culling - short maxs[3]; - - unsigned short firstmarksurface; - unsigned short nummarksurfaces; - - byte ambient_level[NUM_AMBIENTS]; -} dleaf_t; - - -//============================================================================ - - -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 - - -// the utilities get to be lazy and just use large static arrays - -// Make all of these dynamic for BSP_tool... - -extern int nummodels; -extern dmodel_t *dmodels; - -extern int visdatasize; -extern byte *dvisdata; - -extern int lightdatasize; -extern byte *dlightdata; - -extern int texdatasize; -extern byte *dtexdata; - -extern int entdatasize; -extern char *dentdata; - -extern int numleafs; -extern dleaf_t *dleafs; - -extern int numplanes; -extern dplane_t *dplanes; - -extern int numvertexes; -extern dvertex_t *dvertexes; - -extern int numnodes; -extern dnode_t *dnodes; - -extern int numtexinfo; -extern texinfo_t *texinfo; - -extern int numfaces; -extern dface_t *dfaces; - -extern int numclipnodes; -extern dclipnode_t *dclipnodes; - -extern int numedges; -extern dedge_t *dedges; - -extern int nummarksurfaces; -extern unsigned short *dmarksurfaces; - -extern int numsurfedges; -extern int *dsurfedges; - - -int FastChecksum(char *buffer, int bytes); - -void DecompressVis (byte *in, byte *decompressed); -int CompressVis (byte *vis, byte *dest); - -void SwapBSPFile (qboolean todisk); -int CopyLump (int lump, void **dest, int size); -void LoadBSPFile (char *filename); -void WriteBSPFile (char *filename); -void PrintBSPFileSizes (void); - - -//=============== - - -typedef struct epair_s -{ - struct epair_s *next; - char *key; - char *value; -} epair_t; - -typedef struct -{ - vec3_t origin; - int firstbrush; - int numbrushes; - epair_t *epairs; -} entity_t; - -extern int num_entities; -extern entity_t entities[MAX_MAP_ENTITIES]; - -void ParseEntities (void); -void DumpEntity(int i) ; -void UnparseEntities (void); - -void SetKeyValue (entity_t *ent, char *key, char *value); -void RemoveKey(entity_t *ent, char *key); -int FindEntityByClassname(int index, const char *classname); -int FindEntityByWildcard(int index, const char *classname, int length); -char *ValueForKey (entity_t *ent, char *key); -// will return "" if not present - -vec_t FloatForKey (entity_t *ent, char *key); -void GetVectorForKey (entity_t *ent, char *key, vec3_t vec); - -epair_t *ParseEpair (void); - -void FreeEntities(void); - - -#endif // BSPFILE_H +/*** +* +* Copyright (c) 1998, Valve LLC. 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. +* +****/ +#ifndef __CMDLIB__ +#include "cmdlib.h" +#endif + +#ifndef BSPFILE_H +#define BSPFILE_H + +// upper design bounds + +#define MAX_MAP_HULLS 4 + +#define MAX_MAP_MODELS 400 +#define MAX_MAP_BRUSHES 4096 +#define MAX_MAP_ENTITIES 1024 +#define MAX_MAP_ENTSTRING (128*1024) + +#define MAX_MAP_PLANES 32767 +#define MAX_MAP_NODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES 32767 // +#define MAX_MAP_LEAFS 8192 +#define MAX_MAP_VERTS 65535 +#define MAX_MAP_FACES 65535 +#define MAX_MAP_MARKSURFACES 65535 +#define MAX_MAP_TEXINFO 8192 +#define MAX_MAP_EDGES 256000 +#define MAX_MAP_SURFEDGES 512000 +#define MAX_MAP_TEXTURES 512 +#define MAX_MAP_MIPTEX 0x200000 +#define MAX_MAP_LIGHTING 0x200000 +#define MAX_MAP_VISIBILITY 0x200000 + +#define MAX_MAP_PORTALS 65536 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 4096 + +//============================================================================= + +#define BSPVERSION 30 +#define TOOLVERSION 2 + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_TEXTURES 2 +#define LUMP_VERTEXES 3 +#define LUMP_VISIBILITY 4 +#define LUMP_NODES 5 +#define LUMP_TEXINFO 6 +#define LUMP_FACES 7 +#define LUMP_LIGHTING 8 +#define LUMP_CLIPNODES 9 +#define LUMP_LEAFS 10 +#define LUMP_MARKSURFACES 11 +#define LUMP_EDGES 12 +#define LUMP_SURFEDGES 13 +#define LUMP_MODELS 14 + +#define HEADER_LUMPS 15 + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; + int headnode[MAX_MAP_HULLS]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} dmodel_t; + +typedef struct +{ + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct +{ + int nummiptex; + int dataofs[4]; // [nummiptex] +} dmiptexlump_t; + +#define MIPLEVELS 4 +typedef struct miptex_s +{ + char name[16]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored +} miptex_t; + +typedef struct +{ + float point[3]; +} dvertex_t; + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + +#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_ORIGIN -7 // removed at csg time +#define CONTENTS_CLIP -8 // changed to contents_solid + +#define CONTENTS_CURRENT_0 -9 +#define CONTENTS_CURRENT_90 -10 +#define CONTENTS_CURRENT_180 -11 +#define CONTENTS_CURRENT_270 -12 +#define CONTENTS_CURRENT_UP -13 +#define CONTENTS_CURRENT_DOWN -14 + +#define CONTENTS_TRANSLUCENT -15 + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + int planenum; + short children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for sphere culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + +typedef struct +{ + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_t; + +typedef struct texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int miptex; + int flags; +} texinfo_t; + +#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 + +typedef struct +{ + short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + + // lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + +#define AMBIENT_WATER 0 +#define AMBIENT_SKY 1 +#define AMBIENT_SLIME 2 +#define AMBIENT_LAVA 3 + +#define NUM_AMBIENTS 4 // automatic ambient sounds + +// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas +// all other leafs need visibility info +typedef struct +{ + int contents; + int visofs; // -1 = no visibility info + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstmarksurface; + unsigned short nummarksurfaces; + + byte ambient_level[NUM_AMBIENTS]; +} dleaf_t; + +//============================================================================ + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + +// the utilities get to be lazy and just use large static arrays + +// Make all of these dynamic for BSP_tool... + +extern int nummodels; +extern dmodel_t* dmodels; + +extern int visdatasize; +extern byte* dvisdata; + +extern int lightdatasize; +extern byte* dlightdata; + +extern int texdatasize; +extern byte* dtexdata; + +extern int entdatasize; +extern char* dentdata; + +extern int numleafs; +extern dleaf_t* dleafs; + +extern int numplanes; +extern dplane_t* dplanes; + +extern int numvertexes; +extern dvertex_t* dvertexes; + +extern int numnodes; +extern dnode_t* dnodes; + +extern int numtexinfo; +extern texinfo_t* texinfo; + +extern int numfaces; +extern dface_t* dfaces; + +extern int numclipnodes; +extern dclipnode_t* dclipnodes; + +extern int numedges; +extern dedge_t* dedges; + +extern int nummarksurfaces; +extern unsigned short* dmarksurfaces; + +extern int numsurfedges; +extern int* dsurfedges; + +int FastChecksum(char* buffer, int bytes); + +void DecompressVis(byte* in, byte* decompressed); +int CompressVis(byte* vis, byte* dest); + +void SwapBSPFile(qboolean todisk); +int CopyLump(int lump, void** dest, int size); +void LoadBSPFile(char* filename); +void WriteBSPFile(char* filename); +void PrintBSPFileSizes(void); + +//=============== + +typedef struct epair_s +{ + struct epair_s* next; + char* key; + char* value; +} epair_t; + +typedef struct +{ + vec3_t origin; + int firstbrush; + int numbrushes; + epair_t* epairs; +} entity_t; + +extern int num_entities; +extern entity_t entities[MAX_MAP_ENTITIES]; + +void ParseEntities(void); +void DumpEntity(int i); +void UnparseEntities(void); + +void SetKeyValue(entity_t* ent, char* key, char* value); +void RemoveKey(entity_t* ent, char* key); +int FindEntityByClassname(int index, const char* classname); +int FindEntityByWildcard(int index, const char* classname, int length); +char* ValueForKey(entity_t* ent, char* key); +// will return "" if not present + +vec_t FloatForKey(entity_t* ent, char* key); +void GetVectorForKey(entity_t* ent, char* key, vec3_t vec); + +epair_t* ParseEpair(void); + +void FreeEntities(void); + +#endif // BSPFILE_H diff --git a/Bsp2Rbn/build.cpp b/Bsp2Rbn/build.cpp index 881b47a..4629175 100644 --- a/Bsp2Rbn/build.cpp +++ b/Bsp2Rbn/build.cpp @@ -1,4 +1,4 @@ -char * Version = "0.9.7 of $Date$" ; +char* Version = "0.9.7 of $Date$"; // $Log: build.cpp,v $ // Revision 1.6 2004/07/27 07:43:35 eric @@ -17,4 +17,4 @@ char * Version = "0.9.7 of $Date$" ; // Revision 1.3 2004/07/20 12:36:10 eric // - bumped version to 0.9.3 // - it compiles eventually on Windows with mingw... -// +// \ No newline at end of file diff --git a/Bsp2Rbn/cmdlib.cpp b/Bsp2Rbn/cmdlib.cpp index ec46971..204013d 100644 --- a/Bsp2Rbn/cmdlib.cpp +++ b/Bsp2Rbn/cmdlib.cpp @@ -2,8 +2,8 @@ * * Copyright (c) 1998, Valve LLC. All rights reserved. * -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * ****/ @@ -31,7 +31,7 @@ // set these before calling CheckParm int myargc; -char **myargv; +char** myargv; char com_token[1024]; qboolean com_eof; @@ -39,7 +39,6 @@ qboolean com_eof; qboolean archive; char archivedir[1024]; - /* ================= Error @@ -47,44 +46,43 @@ Error For abnormal program terminations ================= */ -void Error (char *error, ...) +void Error(char* error, ...) { - va_list argptr; - char msg[256]; + va_list argptr; + char msg[256]; - va_start (argptr, error); - vsprintf (msg, error, argptr); - va_end (argptr); + va_start(argptr, error); + vsprintf(msg, error, argptr); + va_end(argptr); #ifdef __linux__ - printf("\n************ ERROR ************\n"); - printf("%s\n", msg); + printf("\n************ ERROR ************\n"); + printf("%s\n", msg); #else - char input[1]; + char input[1]; - printf("\n************ ERROR ************\n"); - printf("%s\n", msg); - printf("\nPress to exit\n"); - gets(input); + printf("\n************ ERROR ************\n"); + printf("%s\n", msg); + printf("\nPress to exit\n"); + gets(input); #endif - exit (1); + exit(1); } // only printf if in verbose mode qboolean verbose = false; -void qprintf (char *format, ...) +void qprintf(char* format, ...) { - va_list argptr; + va_list argptr; - if (!verbose) - return; - va_start (argptr,format); - vprintf (format,argptr); - va_end (argptr); + if (!verbose) + return; + va_start(argptr, format); + vprintf(format, argptr); + va_end(argptr); } - /* qdir will hold the path up to the quake directory, including the slash @@ -96,208 +94,203 @@ gamedir will hold qdir + the game directory (id1, id2, etc) */ -char qproject[ 1024 ]={'\0'}; -char qdir[1024]={'\0'}; -char gamedir[1024]={'\0'}; +char qproject[1024] = { '\0' }; +char qdir[1024] = { '\0' }; +char gamedir[1024] = { '\0' }; -void SetQdirFromPath (char *path) +void SetQdirFromPath(char* path) { #ifndef OLD_BOGUS_PATH_CODE - if ( qproject[0]=='\0' ) - { - if ( getenv("QPROJECT") ) - { - char c = qproject[ strlen(qproject)-1 ]; - strcpy( qproject, getenv("QPROJECT") ); - if ( !PATHSEPARATOR( c ) ) - strcat( qproject, "\\" ); - } - else - strcpy( qproject, "quiver\\" ); - } - if ( qproject[0] != '\\' && qproject[0] != '/' && qproject[1] != ':' ) - { - strcpy( qdir, "\\" ); - } - - strcat( qdir, qproject ); - strcpy( gamedir, qdir ); - strcat( gamedir, "\\valve\\" ); + if (qproject[0] == '\0') + { + if (getenv("QPROJECT")) + { + char c = qproject[strlen(qproject) - 1]; + strcpy(qproject, getenv("QPROJECT")); + if (!PATHSEPARATOR(c)) + strcat(qproject, "\\"); + } + else + strcpy(qproject, "quiver\\"); + } + if (qproject[0] != '\\' && qproject[0] != '/' && qproject[1] != ':') + { + strcpy(qdir, "\\"); + } + + strcat(qdir, qproject); + strcpy(gamedir, qdir); + strcat(gamedir, "\\valve\\"); #else - char temp[1024]; - char *c; + char temp[1024]; + char* c; - if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) - { // path is partial - Q_getwd (temp); - strcat (temp, path); - path = temp; - } + if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) + { // path is partial + Q_getwd(temp); + strcat(temp, path); + path = temp; + } -// search for "quake" or quiver in path - if( !qproject[0] ) - { - char *pszProj; + // search for "quake" or quiver in path + if (!qproject[0]) + { + char* pszProj; - pszProj = getenv("QPROJECT"); + pszProj = getenv("QPROJECT"); - if (pszProj != NULL) - strcpy(qproject, pszProj); - else - strcpy(qproject, "quiver"); - } + if (pszProj != NULL) + strcpy(qproject, pszProj); + else + strcpy(qproject, "quiver"); + } try_again: - for (c=path ; *c ; c++) - { - int iSize = 0; - - if (!Q_strncasecmp( c, qproject, strlen( qproject ) ) ) - iSize = strlen( qproject ) + 1; - - if (iSize > 0) - { - strncpy (qdir, path, c + iSize - path); - printf ("qdir: %s\n", qdir); - c += iSize; - while (*c) - { - if (*c == '/' || *c == '\\') - { - strncpy (gamedir, path, c+1-path); - printf ("gamedir: %s\n", gamedir); - return; - } - c++; - } - Error ("No gamedir in %s", path); - return; - } - } - - if (!strcmp(qproject, "quiver")) - { - strcpy(qproject, "prospero"); - goto try_again; - } - - Error ("SetQdirFromPath: no '%s' in %s", qproject, path); + for (c = path; *c; c++) + { + int iSize = 0; + + if (!Q_strncasecmp(c, qproject, strlen(qproject))) + iSize = strlen(qproject) + 1; + + if (iSize > 0) + { + strncpy(qdir, path, c + iSize - path); + printf("qdir: %s\n", qdir); + c += iSize; + while (*c) + { + if (*c == '/' || *c == '\\') + { + strncpy(gamedir, path, c + 1 - path); + printf("gamedir: %s\n", gamedir); + return; + } + c++; + } + Error("No gamedir in %s", path); + return; + } + } + + if (!strcmp(qproject, "quiver")) + { + strcpy(qproject, "prospero"); + goto try_again; + } + + Error("SetQdirFromPath: no '%s' in %s", qproject, path); #endif } - -char *ExpandArg (char *path) +char* ExpandArg(char* path) { - static char full[1024]; - - if (path[0] != '/' && path[0] != '\\' && path[1] != ':') - { - Q_getwd (full); - strcat (full, path); - } - else - strcpy (full, path); - return full; + static char full[1024]; + + if (path[0] != '/' && path[0] != '\\' && path[1] != ':') + { + Q_getwd(full); + strcat(full, path); + } + else + strcpy(full, path); + return full; } -char *ExpandPath (char *path) +char* ExpandPath(char* path) { - char *psz; - static char full[1024]; - if (!qdir) - Error ("ExpandPath called without qdir set"); - if (path[0] == '/' || path[0] == '\\' || path[1] == ':') - return path; - psz = strstr(path, qdir); - if (psz) - strcpy(full, path); - else - sprintf (full, "%s%s", qdir, path); - - return full; + char* psz; + static char full[1024]; + if (!qdir) + Error("ExpandPath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') + return path; + psz = strstr(path, qdir); + if (psz) + strcpy(full, path); + else + sprintf(full, "%s%s", qdir, path); + + return full; } -char *ExpandPathAndArchive (char *path) +char* ExpandPathAndArchive(char* path) { - char *expanded; - char archivename[1024]; + char* expanded; + char archivename[1024]; - expanded = ExpandPath (path); + expanded = ExpandPath(path); - if (archive) - { - sprintf (archivename, "%s/%s", archivedir, path); - QCopyFile (expanded, archivename); - } - return expanded; + if (archive) + { + sprintf(archivename, "%s/%s", archivedir, path); + QCopyFile(expanded, archivename); + } + return expanded; } - -char *copystring(char *s) +char* copystring(char* s) { - char *b; - b = (char *)malloc(strlen(s)+1); - strcpy (b, s); - return b; + char* b; + b = (char*)malloc(strlen(s) + 1); + strcpy(b, s); + return b; } - - /* ================ I_FloatTime ================ */ -double I_FloatTime (void) +double I_FloatTime(void) { - time_t t; - - time (&t); - - return t; + time_t t; + + time(&t); + + return t; #if 0 -// more precise, less portable - struct timeval tp; - struct timezone tzp; - static int secbase; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; + // more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec / 1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec / 1000000.0; #endif } -void Q_getwd (char *out) +void Q_getwd(char* out) { #ifndef __linux__ - _getcwd (out, 256); - strcat (out, "\\"); + _getcwd(out, 256); + strcat(out, "\\"); #else - getcwd (out, 256); + getcwd(out, 256); #endif } - -void Q_mkdir (char *path) +void Q_mkdir(char* path) { #ifndef __linux__ - if (_mkdir (path) != -1) - return; + if (_mkdir(path) != -1) + return; #else - if (mkdir (path, 0777) != -1) - return; + if (mkdir(path, 0777) != -1) + return; #endif - if (errno != EEXIST) - Error ("mkdir %s: %s",path, strerror(errno)); + if (errno != EEXIST) + Error("mkdir %s: %s", path, strerror(errno)); } /* @@ -307,17 +300,15 @@ FileTime returns -1 if not present ============ */ -int FileTime (char *path) +int FileTime(char* path) { - struct stat buf; - - if (stat (path,&buf) == -1) - return -1; - - return buf.st_mtime; -} + struct stat buf; + if (stat(path, &buf) == -1) + return -1; + return buf.st_mtime; +} /* ============== @@ -326,148 +317,143 @@ COM_Parse Parse a token out of a string (into global: char com_token[1024]) ============== */ -char *COM_Parse (char *data) -{ - int c; - int len; - - len = 0; - com_token[0] = 0; - - if (!data) - return NULL; - -// skip whitespace -skipwhite: - while ( (c = *data) <= ' ') - { - if (c == 0) - { - com_eof = true; - return NULL; // end of file; - } - data++; - } - -// skip // comments - if (c=='/' && data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - - -// handle quoted strings specially - if (c == '\"') - { - data++; - do - { - c = *data++; - if (c=='\"') - { - com_token[len] = 0; - return data; - } - com_token[len] = c; - len++; - } while (1); - } - -// parse single characters - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') - { - com_token[len] = c; - len++; - com_token[len] = 0; - return data+1; - } - -// parse a regular word - do - { - com_token[len] = c; - data++; - len++; - c = *data; - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') - break; - } while (c>32); - - com_token[len] = 0; - return data; -} - - -int Q_strncasecmp (char *s1, char *s2, int n) +char* COM_Parse(char* data) { - int c1, c2; - - while (1) - { - c1 = *s1++; - c2 = *s2++; - - if (!n--) - return 0; // strings are equal until end point - - if (c1 != c2) - { - if (c1 >= 'a' && c1 <= 'z') - c1 -= ('a' - 'A'); - if (c2 >= 'a' && c2 <= 'z') - c2 -= ('a' - 'A'); - if (c1 != c2) - return -1; // strings not equal - } - if (!c1) - return 0; // strings are equal - } - - return -1; -} - -int Q_strcasecmp (char *s1, char *s2) -{ - return Q_strncasecmp (s1, s2, 99999); -} + int c; + int len; + len = 0; + com_token[0] = 0; -char *strupr (char *start) -{ - char *in; - in = start; - while (*in) - { - *in = toupper(*in); - in++; - } - return start; -} + if (!data) + return NULL; -char *strlower (char *start) -{ - char *in; - in = start; - while (*in) - { - *in = tolower(*in); - in++; - } - return start; + // skip whitespace +skipwhite: + while ((c = *data) <= ' ') + { + if (c == 0) + { + com_eof = true; + return NULL; // end of file; + } + data++; + } + + // skip // comments + if (c == '/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + // handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c == '\"') + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while (1); + } + + // parse single characters + if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data + 1; + } + + // parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') + break; + } while (c > 32); + + com_token[len] = 0; + return data; +} + +int Q_strncasecmp(char* s1, char* s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal + } + + return -1; +} + +int Q_strcasecmp(char* s1, char* s2) +{ + return Q_strncasecmp(s1, s2, 99999); +} + +char* strupr(char* start) +{ + char* in; + in = start; + while (*in) + { + *in = toupper(*in); + in++; + } + return start; +} + +char* strlower(char* start) +{ + char* in; + in = start; + while (*in) + { + *in = tolower(*in); + in++; + } + return start; } - /* ============================================================================= - MISC FUNCTIONS + MISC FUNCTIONS ============================================================================= */ - /* ================= CheckParm @@ -476,283 +462,267 @@ Checks for the given parameter in the program's command line arguments Returns the argument number (1 to argc-1) or 0 if not present ================= */ -int CheckParm (char *check) +int CheckParm(char* check) { - int i; + int i; - for (i = 1;i 0 && !PATHSEPARATOR(path[length])) - length--; - path[length] = 0; + length = strlen(path) - 1; + while (length > 0 && !PATHSEPARATOR(path[length])) + length--; + path[length] = 0; } -void StripExtension (char *path) +void StripExtension(char* path) { - int length; - - length = strlen(path)-1; - while (length > 0 && path[length] != '.') - { - length--; - if (path[length] == '/') - return; // no extension - } - if (length) - path[length] = 0; -} + int length; + length = strlen(path) - 1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} /* ==================== Extract file parts ==================== */ -void ExtractFilePath (char *path, char *dest) +void ExtractFilePath(char* path, char* dest) { - char *src; + char* src; - src = path + strlen(path) - 1; + src = path + strlen(path) - 1; -// -// back up until a \ or the start -// - while (src != path && !PATHSEPARATOR(*(src-1))) - src--; + // + // back up until a \ or the start + // + while (src != path && !PATHSEPARATOR(*(src - 1))) + src--; - memcpy (dest, path, src-path); - dest[src-path] = 0; + memcpy(dest, path, src - path); + dest[src - path] = 0; } -void ExtractFileBase (char *path, char *dest) +void ExtractFileBase(char* path, char* dest) { - char *src; + char* src; - src = path + strlen(path) - 1; + src = path + strlen(path) - 1; -// -// back up until a \ or the start -// - while (src != path && !PATHSEPARATOR(*(src-1))) - src--; - - while (*src && *src != '.') - { - *dest++ = *src++; - } - *dest = 0; + // + // back up until a \ or the start + // + while (src != path && !PATHSEPARATOR(*(src - 1))) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; } -void ExtractFileExtension (char *path, char *dest) +void ExtractFileExtension(char* path, char* dest) { - char *src; + char* src; - src = path + strlen(path) - 1; + src = path + strlen(path) - 1; -// -// back up until a . or the start -// - while (src != path && *(src-1) != '.') - src--; - if (src == path) - { - *dest = 0; // no extension - return; - } - - strcpy (dest,src); -} + // + // back up until a . or the start + // + while (src != path && *(src - 1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + strcpy(dest, src); +} /* ============== ParseNum / ParseHex ============== */ -int ParseHex (char *hex) +int ParseHex(char* hex) { - char *str; - int num; - - num = 0; - str = hex; - - while (*str) - { - num <<= 4; - if (*str >= '0' && *str <= '9') - num += *str-'0'; - else if (*str >= 'a' && *str <= 'f') - num += 10 + *str-'a'; - else if (*str >= 'A' && *str <= 'F') - num += 10 + *str-'A'; - else - Error ("Bad hex number: %s",hex); - str++; - } - - return num; -} + char* str; + int num; + num = 0; + str = hex; -int ParseNum (char *str) -{ - if (str[0] == '$') - return ParseHex (str+1); - if (str[0] == '0' && str[1] == 'x') - return ParseHex (str+2); - return atol (str); -} + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str - '0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str - 'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str - 'A'; + else + Error("Bad hex number: %s", hex); + str++; + } + return num; +} +int ParseNum(char* str) +{ + if (str[0] == '$') + return ParseHex(str + 1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex(str + 2); + return atol(str); +} /* ============================================================================ - BYTE ORDER FUNCTIONS + BYTE ORDER FUNCTIONS ============================================================================ */ @@ -763,120 +733,112 @@ int ParseNum (char *str) #ifdef __BIG_ENDIAN__ -short LittleShort (short l) +short LittleShort(short l) { - byte b1,b2; + byte b1, b2; - b1 = l&255; - b2 = (l>>8)&255; + b1 = l & 255; + b2 = (l >> 8) & 255; - return (b1<<8) + b2; + return (b1 << 8) + b2; } -short BigShort (short l) +short BigShort(short l) { - return l; + return l; } - -int LittleLong (int l) +int LittleLong(int l) { - byte b1,b2,b3,b4; + byte b1, b2, b3, b4; - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; + b1 = l & 255; + b2 = (l >> 8) & 255; + b3 = (l >> 16) & 255; + b4 = (l >> 24) & 255; - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; + return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4; } -int BigLong (int l) +int BigLong(int l) { - return l; + return l; } - -float LittleFloat (float l) +float LittleFloat(float l) { - union {byte b[4]; float f;} in, out; - - in.f = l; - out.b[0] = in.b[3]; - out.b[1] = in.b[2]; - out.b[2] = in.b[1]; - out.b[3] = in.b[0]; - - return out.f; + union { byte b[4]; float f; } in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; } -float BigFloat (float l) +float BigFloat(float l) { - return l; + return l; } - #else - -short BigShort (short l) +short BigShort(short l) { - byte b1,b2; + byte b1, b2; - b1 = l&255; - b2 = (l>>8)&255; + b1 = l & 255; + b2 = (l >> 8) & 255; - return (b1<<8) + b2; + return (b1 << 8) + b2; } -short LittleShort (short l) +short LittleShort(short l) { - return l; + return l; } - -int BigLong (int l) +int BigLong(int l) { - byte b1,b2,b3,b4; + byte b1, b2, b3, b4; - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; + b1 = l & 255; + b2 = (l >> 8) & 255; + b3 = (l >> 16) & 255; + b4 = (l >> 24) & 255; - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; + return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4; } -int LittleLong (int l) +int LittleLong(int l) { - return l; + return l; } -float BigFloat (float l) +float BigFloat(float l) { - union {byte b[4]; float f;} in, out; - - in.f = l; - out.b[0] = in.b[3]; - out.b[1] = in.b[2]; - out.b[2] = in.b[1]; - out.b[3] = in.b[0]; - - return out.f; + union { byte b[4]; float f; } in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; } -float LittleFloat (float l) +float LittleFloat(float l) { - return l; + return l; } - #endif - //======================================================= - // FIXME: byte swap? // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 @@ -888,53 +850,53 @@ float LittleFloat (float l) static unsigned short crctable[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; -void CRC_Init(unsigned short *crcvalue) +void CRC_Init(unsigned short* crcvalue) { - *crcvalue = CRC_INIT_VALUE; + *crcvalue = CRC_INIT_VALUE; } -void CRC_ProcessByte(unsigned short *crcvalue, byte data) +void CRC_ProcessByte(unsigned short* crcvalue, byte data) { - *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } unsigned short CRC_Value(unsigned short crcvalue) { - return crcvalue ^ CRC_XOR_VALUE; + return crcvalue ^ CRC_XOR_VALUE; } //============================================================================= @@ -943,22 +905,21 @@ unsigned short CRC_Value(unsigned short crcvalue) CreatePath ============ */ -void CreatePath (char *path) +void CreatePath(char* path) { - char *ofs, c; - - for (ofs = path+1 ; *ofs ; ofs++) - { - c = *ofs; - if (c == '/' || c == '\\') - { // create the directory - *ofs = 0; - Q_mkdir (path); - *ofs = c; - } - } -} + char* ofs, c; + for (ofs = path + 1; *ofs; ofs++) + { + c = *ofs; + if (c == '/' || c == '\\') + { // create the directory + *ofs = 0; + Q_mkdir(path); + *ofs = c; + } + } +} /* ============ @@ -967,18 +928,17 @@ QCopyFile Used to archive source files ============ */ -void QCopyFile (char *from, char *to) +void QCopyFile(char* from, char* to) { - void *buffer; - int length; + void* buffer; + int length; - length = LoadFile (from, &buffer); - CreatePath (to); - SaveFile (to, buffer, length); - free (buffer); + length = LoadFile(from, &buffer); + CreatePath(to); + SaveFile(to, buffer, length); + free(buffer); } - /* ============ ListPak @@ -989,44 +949,41 @@ ListPak void ListPak(char* pakname) { - FILE* f = SafeOpenRead(pakname); - packheader_t head; - packfile_t* pdir; - long i=0,imax=0; - long totlen=0; + FILE* f = SafeOpenRead(pakname); + packheader_t head; + packfile_t* pdir; + long i = 0, imax = 0; + long totlen = 0; - SafeRead(f,&head,sizeof(packheader_t)); - pdir = (packfile_t *)malloc(head.dirlen); + SafeRead(f, &head, sizeof(packheader_t)); + pdir = (packfile_t*)malloc(head.dirlen); - fseek(f,head.dirofs,SEEK_SET); - SafeRead(f,pdir,head.dirlen); - - fseek(f,0,SEEK_END); - totlen=ftell(f); + fseek(f, head.dirofs, SEEK_SET); + SafeRead(f, pdir, head.dirlen); - fclose(f); + fseek(f, 0, SEEK_END); + totlen = ftell(f); - imax=head.dirlen/sizeof(packfile_t); + fclose(f); - for(i;iidentifier) - // set these before calling CheckParm extern int myargc; -extern char **myargv; +extern char** myargv; -char *strupr (char *in); -char *strlower (char *in); -int Q_strncasecmp (char *s1, char *s2, int n); -int Q_strcasecmp (char *s1, char *s2); -void Q_getwd (char *out); +char* strupr(char* in); +char* strlower(char* in); +int Q_strncasecmp(char* s1, char* s2, int n); +int Q_strcasecmp(char* s1, char* s2); +void Q_getwd(char* out); -int filelength (FILE *f); -int FileTime (char *path); +int filelength(FILE* f); +int FileTime(char* path); -void Q_mkdir (char *path); +void Q_mkdir(char* path); extern char qdir[1024]; extern char gamedir[1024]; -void SetQdirFromPath (char *path); -char *ExpandArg (char *path); // from cmd line -char *ExpandPath (char *path); // from scripts -char *ExpandPathAndArchive (char *path); - +void SetQdirFromPath(char* path); +char* ExpandArg(char* path); // from cmd line +char* ExpandPath(char* path); // from scripts +char* ExpandPathAndArchive(char* path); -double I_FloatTime (void); +double I_FloatTime(void); -void Error (char *error, ...); -int CheckParm (char *check); +void Error(char* error, ...); +int CheckParm(char* check); -FILE *SafeOpenWrite (char *filename); -FILE *SafeOpenRead (char *filename); -void SafeRead (FILE *f, void *buffer, int count); -void SafeWrite (FILE *f, void *buffer, int count); +FILE* SafeOpenWrite(char* filename); +FILE* SafeOpenRead(char* filename); +void SafeRead(FILE* f, void* buffer, int count); +void SafeWrite(FILE* f, void* buffer, int count); -int LoadFile (char *filename, void **bufferptr); -void SaveFile (char *filename, void *buffer, int count); +int LoadFile(char* filename, void** bufferptr); +void SaveFile(char* filename, void* buffer, int count); -void DefaultExtension (char *path, char *extension); -void DefaultPath (char *path, char *basepath); -void StripFilename (char *path); -void StripExtension (char *path); +void DefaultExtension(char* path, char* extension); +void DefaultPath(char* path, char* basepath); +void StripFilename(char* path); +void StripExtension(char* path); -void ExtractFilePath (char *path, char *dest); -void ExtractFileBase (char *path, char *dest); -void ExtractFileExtension (char *path, char *dest); +void ExtractFilePath(char* path, char* dest); +void ExtractFileBase(char* path, char* dest); +void ExtractFileExtension(char* path, char* dest); -int ParseNum (char *str); +int ParseNum(char* str); -short BigShort (short l); -short LittleShort (short l); -int BigLong (int l); -int LittleLong (int l); -float BigFloat (float l); -float LittleFloat (float l); +short BigShort(short l); +short LittleShort(short l); +int BigLong(int l); +int LittleLong(int l); +float BigFloat(float l); +float LittleFloat(float l); long flen(FILE* f); - - -char *COM_Parse (char *data); +char* COM_Parse(char* data); extern char com_token[1024]; extern qboolean com_eof; -char *copystring(char *s); - +char* copystring(char* s); -void CRC_Init(unsigned short *crcvalue); -void CRC_ProcessByte(unsigned short *crcvalue, byte data); +void CRC_Init(unsigned short* crcvalue); +void CRC_ProcessByte(unsigned short* crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); -void CreatePath (char *path); -void QCopyFile (char *from, char *to); +void CreatePath(char* path); +void QCopyFile(char* from, char* to); extern qboolean archive; extern char archivedir[1024]; - extern qboolean verbose; -void qprintf (char *format, ...); - +void qprintf(char* format, ...); typedef struct { - char name[56]; - int filepos, filelen; + char name[56]; + int filepos, filelen; } packfile_t; typedef struct { - char id[4]; - int dirofs; - int dirlen; + char id[4]; + int dirofs; + int dirlen; } packheader_t; - void ListPak(char* pakname); #endif // CMDLIB - diff --git a/Bsp2Rbn/dummy.cpp b/Bsp2Rbn/dummy.cpp index 349ed67..c89480d 100644 --- a/Bsp2Rbn/dummy.cpp +++ b/Bsp2Rbn/dummy.cpp @@ -13,7 +13,6 @@ #include #include - #include "../bot.h" #include "../game.h" #include "../bot_weapons.h" @@ -26,7 +25,7 @@ enginefuncs_t g_engfuncs; globalvars_t pGlobals; -globalvars_t *gpGlobals = &pGlobals; +globalvars_t* gpGlobals = &pGlobals; char g_argv[1024]; cBot bots[32]; @@ -39,96 +38,92 @@ cGame Game; cNodeMachine NodeMachine; cChatEngine ChatEngine; -void FakeClientCommand (edict_t * pBot, char *arg1, char *arg2, char *arg3) -{ - fprintf(stderr,"FakeClientCommand is called!\n") ; - exit(1) ; +void FakeClientCommand(edict_t* pBot, char* arg1, char* arg2, char* arg3) +{ + fprintf(stderr, "FakeClientCommand is called!\n"); + exit(1); } - // From game.cpp // Debug message -void REALBOT_PRINT (cBot * pBot, const char *Function, const char *msg) +void REALBOT_PRINT(cBot* pBot, const char* Function, const char* msg) { - // Message format: - // Function name - [BOT NAME, BOT TEAM]: Message - char team[9]; - char name[32]; - - memset(team, 0, sizeof(team)); // clear - memset(name, 0, sizeof(name)); // clear - - strcpy(team, "TERROR"); // t - strcpy(name, "FUNCTION"); - - if (pBot) - { - memset(name, 0, sizeof(name)); // clear - strcpy(name, pBot->name); // copy name - - if (pBot->iTeam == 2) strcpy(team, "COUNTER"); - } - else - { - strcpy(team, "NONE"); - } - - printf ("RBPRINT->[%s '%s']-[Team %s] : %s\n", name, Function, team, msg); - - char msgForFile[512]; - memset(msgForFile, 0, sizeof(msgForFile)); // clear - sprintf (msgForFile, "RBPRINT->[%s '%s']-[Team %s] : %s\n", name, Function, team, msg); - rblog(msgForFile); + // Message format: + // Function name - [BOT NAME, BOT TEAM]: Message + char team[9]; + char name[32]; + + memset(team, 0, sizeof(team)); // clear + memset(name, 0, sizeof(name)); // clear + + strcpy(team, "TERROR"); // t + strcpy(name, "FUNCTION"); + + if (pBot) + { + memset(name, 0, sizeof(name)); // clear + strcpy(name, pBot->name); // copy name + + if (pBot->iTeam == 2) strcpy(team, "COUNTER"); + } + else + { + strcpy(team, "NONE"); + } + + printf("RBPRINT->[%s '%s']-[Team %s] : %s\n", name, Function, team, msg); + + char msgForFile[512]; + memset(msgForFile, 0, sizeof(msgForFile)); // clear + sprintf(msgForFile, "RBPRINT->[%s '%s']-[Team %s] : %s\n", name, Function, team, msg); + rblog(msgForFile); } - // from ChatENgine.cpp void cChatEngine::set_sentence(char csender[30], char csentence[128]) { - fprintf(stderr,"cChatEngine::set_sentence is called!\n") ; - exit(1) ; + fprintf(stderr, "cChatEngine::set_sentence is called!\n"); + exit(1); } // From bot.cpp // Can see Edict? -bool cBot::canSeeEntity (edict_t * pEntity) +bool cBot::canSeeEntity(edict_t* pEntity) const { - TraceResult tr; - Vector start = pEdict->v.origin + pEdict->v.view_ofs; - Vector vDest = pEntity->v.origin; + TraceResult tr; + Vector start = pEdict->v.origin + pEdict->v.view_ofs; + Vector vDest = pEntity->v.origin; - // trace a line from bot's eyes to destination... - UTIL_TraceLine (start, vDest, ignore_monsters, pEdict->v.pContainingEntity, &tr); + // trace a line from bot's eyes to destination... + UTIL_TraceLine(start, vDest, ignore_monsters, pEdict->v.pContainingEntity, &tr); - if (tr.flFraction < 1.0) - { - // when the 'hit entity' is the same as pEntity, then its ok - if (tr.pHit == pEntity) - return true; // it is visible + if (tr.flFraction < 1.0) + { + // when the 'hit entity' is the same as pEntity, then its ok + if (tr.pHit == pEntity) + return true; // it is visible - return false; - } + return false; + } - return true; + return true; } -bool cBot::Defuse () +bool cBot::Defuse() { - fprintf(stderr,"cBot::Defuse is called!\n") ; - exit(1) ; + fprintf(stderr, "cBot::Defuse is called!\n"); + exit(1); } - // From IniParser.cpp // Parse IAD file: // Important Area Definition file void INI_PARSE_IAD() { - fprintf(stderr,"INI_PARSE_IAD is called!\n") ; - exit(1) ; -} - + fprintf(stderr, "INI_PARSE_IAD is called!\n"); + exit(1); +} \ No newline at end of file diff --git a/Bsp2Rbn/entity.cpp b/Bsp2Rbn/entity.cpp index 33996de..f8edaab 100644 --- a/Bsp2Rbn/entity.cpp +++ b/Bsp2Rbn/entity.cpp @@ -1,164 +1,159 @@ -// -// BSP_tool - botman's Half-Life BSP utilities -// -// (http://planethalflife.com/botman/) -// -// entity.cpp -// -// Copyright (C) 2001 - Jeffrey "botman" Broome -// -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// -// See the GNU General Public License for more details at: -// http://www.gnu.org/copyleft/gpl.html -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include - -#include -#include - -// chierie de /home/evyncke/cstrike/Realbot/HLSDK/multiplayer/cl_dll/util_vector.h definissant vec3_t comme Vector - - -#include "cmdlib.h" -#include "mathlib.h" -#include "bspfile.h" -#include "entity.h" - -vec3_t spawn_point; -float spawn_point_yaw; - -epair_t *pEpair = NULL; - -int Botman_num_entvars = 0; -Botman_entvars_t Botman_entvars[MAX_MAP_ENTITIES]; - - -void LoadEntVars(void) -{ - int ent_index = 0; - char *value; - - while (ent_index < num_entities) - { - value = ValueForKey(&entities[ent_index], "classname"); - - if (value[0]) - { - strcpy(Botman_entvars[Botman_num_entvars].classname, value); - - // initialize the default entvars fields... - Botman_entvars[Botman_num_entvars].origin[0] = 0.0f; - Botman_entvars[Botman_num_entvars].origin[1] = 0.0f; - Botman_entvars[Botman_num_entvars].origin[2] = 0.0f; - - Botman_entvars[Botman_num_entvars].angles[0] = 0.0f; - Botman_entvars[Botman_num_entvars].angles[1] = 0.0f; - Botman_entvars[Botman_num_entvars].angles[2] = 0.0f; - - Botman_entvars[Botman_num_entvars].rendermode = 0; - Botman_entvars[Botman_num_entvars].renderamt = 1.0f; - Botman_entvars[Botman_num_entvars].rendercolor[0] = 1.0f; - Botman_entvars[Botman_num_entvars].rendercolor[1] = 1.0f; - Botman_entvars[Botman_num_entvars].rendercolor[2] = 1.0f; - Botman_entvars[Botman_num_entvars].renderfx = 0; - - Botman_entvars[Botman_num_entvars].brush_model_index = 0; - - Botman_entvars[Botman_num_entvars].studio_model = NULL; - - value = ValueForKey(&entities[ent_index], "origin"); - if (value[0]) - { - sscanf(value, "%f %f %f", &Botman_entvars[Botman_num_entvars].origin[0], - &Botman_entvars[Botman_num_entvars].origin[1], - &Botman_entvars[Botman_num_entvars].origin[2]); - } - - value = ValueForKey(&entities[ent_index], "angle"); - if (value[0]) - { - // set the yaw angle... - sscanf(value, "%f", &Botman_entvars[Botman_num_entvars].angles[1]); - } - - value = ValueForKey(&entities[ent_index], "renderamt"); - if (value[0]) - { - int n_renderamt; - - sscanf(value, "%d", &n_renderamt); - Botman_entvars[Botman_num_entvars].renderamt = n_renderamt / 255.0f; - } - - value = ValueForKey(&entities[ent_index], "rendercolor"); - if (value[0]) - { - int n_color_r, n_color_b, n_color_g; - - sscanf(value, "%d %d %d", &n_color_r, &n_color_g, &n_color_b); - Botman_entvars[Botman_num_entvars].rendercolor[0] = n_color_r / 255.0f; - Botman_entvars[Botman_num_entvars].rendercolor[1] = n_color_g / 255.0f; - Botman_entvars[Botman_num_entvars].rendercolor[2] = n_color_b / 255.0f; - } - - value = ValueForKey(&entities[ent_index], "model"); - if (value[0]) - { - if (sscanf(value, "*%d", &Botman_entvars[Botman_num_entvars].brush_model_index) == 1) - { - dmodel_t *model; - - // calculate the origin for this brush model... - model = &dmodels[Botman_entvars[Botman_num_entvars].brush_model_index]; - - Botman_entvars[Botman_num_entvars].origin[0] = (model->mins[0] + model->maxs[0]) / 2.0f; - Botman_entvars[Botman_num_entvars].origin[1] = (model->mins[1] + model->maxs[1]) / 2.0f; - Botman_entvars[Botman_num_entvars].origin[2] = (model->mins[2] + model->maxs[2]) / 2.0f; - } - } - - if ((strcmp(Botman_entvars[Botman_num_entvars].classname, "func_button") == 0) || - (strcmp(Botman_entvars[Botman_num_entvars].classname, "func_door") == 0)) - { - // always render func_button and func_door entities... - Botman_entvars[Botman_num_entvars].renderamt = 255; - } - - Botman_num_entvars++; - } - - ent_index++; - } -} - - -void InitSpawnPoint(void) -{ - int ent_index; - int count = 0; - char *value; - int pick, loop; - - spawn_point[0] = 0.0; - spawn_point[1] = 0.0; - spawn_point[2] = 0.0; - spawn_point_yaw = 0.0; - -// if (config.spawnpoint[0] == 0) - return; // no spawn points configured, just return - -} +// +// BSP_tool - botman's Half-Life BSP utilities +// +// (http://planethalflife.com/botman/) +// +// entity.cpp +// +// Copyright (C) 2001 - Jeffrey "botman" Broome +// +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License for more details at: +// http://www.gnu.org/copyleft/gpl.html +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#include +#include + +// chierie de /home/evyncke/cstrike/Realbot/HLSDK/multiplayer/cl_dll/util_vector.h definissant vec3_t comme Vector + +#include "cmdlib.h" +#include "mathlib.h" +#include "bspfile.h" +#include "entity.h" + +vec3_t spawn_point; +float spawn_point_yaw; + +epair_t* pEpair = NULL; + +int Botman_num_entvars = 0; +Botman_entvars_t Botman_entvars[MAX_MAP_ENTITIES]; + +void LoadEntVars(void) +{ + int ent_index = 0; + char* value; + + while (ent_index < num_entities) + { + value = ValueForKey(&entities[ent_index], "classname"); + + if (value[0]) + { + strcpy(Botman_entvars[Botman_num_entvars].classname, value); + + // initialize the default entvars fields... + Botman_entvars[Botman_num_entvars].origin[0] = 0.0f; + Botman_entvars[Botman_num_entvars].origin[1] = 0.0f; + Botman_entvars[Botman_num_entvars].origin[2] = 0.0f; + + Botman_entvars[Botman_num_entvars].angles[0] = 0.0f; + Botman_entvars[Botman_num_entvars].angles[1] = 0.0f; + Botman_entvars[Botman_num_entvars].angles[2] = 0.0f; + + Botman_entvars[Botman_num_entvars].rendermode = 0; + Botman_entvars[Botman_num_entvars].renderamt = 1.0f; + Botman_entvars[Botman_num_entvars].rendercolor[0] = 1.0f; + Botman_entvars[Botman_num_entvars].rendercolor[1] = 1.0f; + Botman_entvars[Botman_num_entvars].rendercolor[2] = 1.0f; + Botman_entvars[Botman_num_entvars].renderfx = 0; + + Botman_entvars[Botman_num_entvars].brush_model_index = 0; + + Botman_entvars[Botman_num_entvars].studio_model = NULL; + + value = ValueForKey(&entities[ent_index], "origin"); + if (value[0]) + { + sscanf(value, "%f %f %f", &Botman_entvars[Botman_num_entvars].origin[0], + &Botman_entvars[Botman_num_entvars].origin[1], + &Botman_entvars[Botman_num_entvars].origin[2]); + } + + value = ValueForKey(&entities[ent_index], "angle"); + if (value[0]) + { + // set the yaw angle... + sscanf(value, "%f", &Botman_entvars[Botman_num_entvars].angles[1]); + } + + value = ValueForKey(&entities[ent_index], "renderamt"); + if (value[0]) + { + int n_renderamt; + + sscanf(value, "%d", &n_renderamt); + Botman_entvars[Botman_num_entvars].renderamt = n_renderamt / 255.0f; + } + + value = ValueForKey(&entities[ent_index], "rendercolor"); + if (value[0]) + { + int n_color_r, n_color_b, n_color_g; + + sscanf(value, "%d %d %d", &n_color_r, &n_color_g, &n_color_b); + Botman_entvars[Botman_num_entvars].rendercolor[0] = n_color_r / 255.0f; + Botman_entvars[Botman_num_entvars].rendercolor[1] = n_color_g / 255.0f; + Botman_entvars[Botman_num_entvars].rendercolor[2] = n_color_b / 255.0f; + } + + value = ValueForKey(&entities[ent_index], "model"); + if (value[0]) + { + if (sscanf(value, "*%d", &Botman_entvars[Botman_num_entvars].brush_model_index) == 1) + { + dmodel_t* model; + + // calculate the origin for this brush model... + model = &dmodels[Botman_entvars[Botman_num_entvars].brush_model_index]; + + Botman_entvars[Botman_num_entvars].origin[0] = (model->mins[0] + model->maxs[0]) / 2.0f; + Botman_entvars[Botman_num_entvars].origin[1] = (model->mins[1] + model->maxs[1]) / 2.0f; + Botman_entvars[Botman_num_entvars].origin[2] = (model->mins[2] + model->maxs[2]) / 2.0f; + } + } + + if ((strcmp(Botman_entvars[Botman_num_entvars].classname, "func_button") == 0) || + (strcmp(Botman_entvars[Botman_num_entvars].classname, "func_door") == 0)) + { + // always render func_button and func_door entities... + Botman_entvars[Botman_num_entvars].renderamt = 255; + } + + Botman_num_entvars++; + } + + ent_index++; + } +} + +void InitSpawnPoint(void) +{ + int ent_index; + int count = 0; + char* value; + int pick, loop; + + spawn_point[0] = 0.0; + spawn_point[1] = 0.0; + spawn_point[2] = 0.0; + spawn_point_yaw = 0.0; + + // if (config.spawnpoint[0] == 0) + return; // no spawn points configured, just return +} \ No newline at end of file diff --git a/Bsp2Rbn/entity.h b/Bsp2Rbn/entity.h index d3b5828..52178ad 100644 --- a/Bsp2Rbn/entity.h +++ b/Bsp2Rbn/entity.h @@ -15,7 +15,7 @@ // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License for more details at: // http://www.gnu.org/copyleft/gpl.html @@ -33,17 +33,17 @@ typedef struct Botman_entvars_s { - char classname[64]; - vec3_t origin; - vec3_t angles; + char classname[64]; + vec3_t origin; + vec3_t angles; - int rendermode; - float renderamt; - vec3_t rendercolor; - int renderfx; + int rendermode; + float renderamt; + vec3_t rendercolor; + int renderfx; - int brush_model_index; - StudioModel *studio_model; + int brush_model_index; + StudioModel* studio_model; } Botman_entvars_t; extern int Botman_num_entvars; @@ -58,4 +58,3 @@ void LoadEntVars(void); void InitSpawnPoint(void); #endif - diff --git a/Bsp2Rbn/mathlib.cpp b/Bsp2Rbn/mathlib.cpp index 61f9815..31118ce 100644 --- a/Bsp2Rbn/mathlib.cpp +++ b/Bsp2Rbn/mathlib.cpp @@ -1,9 +1,9 @@ /*** * * Copyright (c) 1996-2001, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * ****/ @@ -19,143 +19,140 @@ // chierie de /home/evyncke/cstrike/Realbot/HLSDK/multiplayer/cl_dll/util_vector.h definissant vec3_t comme Vector - #include "cmdlib.h" #include "mathlib.h" -vec3_t vec3_origin = Vector(0,0,0); - +vec3_t vec3_origin = Vector(0, 0, 0); double Vector2DLength(vec3_t v) { - int i; - double length; + int i; + double length; - length = 0; - for (i=0 ; i< 2 ; i++) // ignore the Z axis - length += v[i]*v[i]; - length = (float)sqrt(length); // FIXME + length = 0; + for (i = 0; i < 2; i++) // ignore the Z axis + length += v[i] * v[i]; + length = (float)sqrt(length); // FIXME - return length; + return length; } double VectorLength(vec3_t v) { int i; double length; - + length = 0; - for (i=0 ; i< 3 ; i++) - length += v[i]*v[i]; - length = sqrt (length); // FIXME + for (i = 0; i < 3; i++) + length += v[i] * v[i]; + length = sqrt(length); // FIXME return length; } - -int VectorCompare (vec3_t v1, vec3_t v2) +int VectorCompare(vec3_t v1, vec3_t v2) { int i; - - for (i=0 ; i<3 ; i++) - if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) + + for (i = 0; i < 3; i++) + if (fabs(v1[i] - v2[i]) > EQUAL_EPSILON) return false; - + return true; } -vec_t Q_rint (vec_t in) +vec_t Q_rint(vec_t in) { - return floor (in + 0.5); + return floor(in + 0.5); } -void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc) +void VectorMA(vec3_t va, double scale, vec3_t vb, vec3_t vc) { - vc[0] = va[0] + scale*vb[0]; - vc[1] = va[1] + scale*vb[1]; - vc[2] = va[2] + scale*vb[2]; + vc[0] = va[0] + scale * vb[0]; + vc[1] = va[1] + scale * vb[1]; + vc[2] = va[2] + scale * vb[2]; } -void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) +void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross) { - cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; - cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; - cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; + cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; + cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; + cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; } -vec_t _DotProduct (vec3_t v1, vec3_t v2) +vec_t _DotProduct(vec3_t v1, vec3_t v2) { - return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } -void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out) +void _VectorSubtract(vec3_t va, vec3_t vb, vec3_t out) { - out[0] = va[0]-vb[0]; - out[1] = va[1]-vb[1]; - out[2] = va[2]-vb[2]; + out[0] = va[0] - vb[0]; + out[1] = va[1] - vb[1]; + out[2] = va[2] - vb[2]; } -void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out) +void _VectorAdd(vec3_t va, vec3_t vb, vec3_t out) { - out[0] = va[0]+vb[0]; - out[1] = va[1]+vb[1]; - out[2] = va[2]+vb[2]; + out[0] = va[0] + vb[0]; + out[1] = va[1] + vb[1]; + out[2] = va[2] + vb[2]; } -void _VectorCopy (vec3_t in, vec3_t out) +void _VectorCopy(vec3_t in, vec3_t out) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; } -void _VectorScale (vec3_t v, vec_t scale, vec3_t out) +void _VectorScale(vec3_t v, vec_t scale, vec3_t out) { out[0] = v[0] * scale; out[1] = v[1] * scale; out[2] = v[2] * scale; } -vec_t VectorNormalize (vec3_t v) +vec_t VectorNormalize(vec3_t v) { int i; double length; -if ( fabs(v[1] - 0.000215956) < 0.0001) -i=1; + if (fabs(v[1] - 0.000215956) < 0.0001) + i = 1; length = 0; - for (i=0 ; i< 3 ; i++) - length += v[i]*v[i]; - length = sqrt (length); + for (i = 0; i < 3; i++) + length += v[i] * v[i]; + length = sqrt(length); if (length == 0) return 0; - - for (i=0 ; i< 3 ; i++) - v[i] /= length; + + for (i = 0; i < 3; i++) + v[i] /= length; return length; } -void VectorInverse (vec3_t v) +void VectorInverse(vec3_t v) { v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2]; } -void ClearBounds (vec3_t mins, vec3_t maxs) +void ClearBounds(vec3_t mins, vec3_t maxs) { mins[0] = mins[1] = mins[2] = 99999; maxs[0] = maxs[1] = maxs[2] = -99999; } -void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs) +void AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs) { int i; vec_t val; - for (i=0 ; i<3 ; i++) + for (i = 0; i < 3; i++) { val = v[i]; if (val < mins[i]) @@ -165,158 +162,151 @@ void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs) } } -void AngleMatrix (const vec3_t angles, float (*matrix)[4] ) +void AngleMatrix(const vec3_t angles, float(*matrix)[4]) { float angle; float sr, sp, sy, cr, cp, cy; - - angle = angles[2] * (Q_PI*2 / 360); + + angle = angles[2] * (Q_PI * 2 / 360); sy = sin(angle); cy = cos(angle); - angle = angles[1] * (Q_PI*2 / 360); + angle = angles[1] * (Q_PI * 2 / 360); sp = sin(angle); cp = cos(angle); - angle = angles[0] * (Q_PI*2 / 360); + angle = angles[0] * (Q_PI * 2 / 360); sr = sin(angle); cr = cos(angle); // matrix = (Z * Y) * X - matrix[0][0] = cp*cy; - matrix[1][0] = cp*sy; + matrix[0][0] = cp * cy; + matrix[1][0] = cp * sy; matrix[2][0] = -sp; - matrix[0][1] = sr*sp*cy+cr*-sy; - matrix[1][1] = sr*sp*sy+cr*cy; - matrix[2][1] = sr*cp; - matrix[0][2] = (cr*sp*cy+-sr*-sy); - matrix[1][2] = (cr*sp*sy+-sr*cy); - matrix[2][2] = cr*cp; + matrix[0][1] = sr * sp * cy + cr * -sy; + matrix[1][1] = sr * sp * sy + cr * cy; + matrix[2][1] = sr * cp; + matrix[0][2] = (cr * sp * cy + -sr * -sy); + matrix[1][2] = (cr * sp * sy + -sr * cy); + matrix[2][2] = cr * cp; matrix[0][3] = 0.0; matrix[1][3] = 0.0; matrix[2][3] = 0.0; } -void AngleIMatrix (const vec3_t angles, float matrix[3][4] ) +void AngleIMatrix(const vec3_t angles, float matrix[3][4]) { float angle; float sr, sp, sy, cr, cp, cy; - - angle = angles[2] * (Q_PI*2 / 360); + + angle = angles[2] * (Q_PI * 2 / 360); sy = sin(angle); cy = cos(angle); - angle = angles[1] * (Q_PI*2 / 360); + angle = angles[1] * (Q_PI * 2 / 360); sp = sin(angle); cp = cos(angle); - angle = angles[0] * (Q_PI*2 / 360); + angle = angles[0] * (Q_PI * 2 / 360); sr = sin(angle); cr = cos(angle); // matrix = (Z * Y) * X - matrix[0][0] = cp*cy; - matrix[0][1] = cp*sy; + matrix[0][0] = cp * cy; + matrix[0][1] = cp * sy; matrix[0][2] = -sp; - matrix[1][0] = sr*sp*cy+cr*-sy; - matrix[1][1] = sr*sp*sy+cr*cy; - matrix[1][2] = sr*cp; - matrix[2][0] = (cr*sp*cy+-sr*-sy); - matrix[2][1] = (cr*sp*sy+-sr*cy); - matrix[2][2] = cr*cp; + matrix[1][0] = sr * sp * cy + cr * -sy; + matrix[1][1] = sr * sp * sy + cr * cy; + matrix[1][2] = sr * cp; + matrix[2][0] = (cr * sp * cy + -sr * -sy); + matrix[2][1] = (cr * sp * sy + -sr * cy); + matrix[2][2] = cr * cp; matrix[0][3] = 0.0; matrix[1][3] = 0.0; matrix[2][3] = 0.0; } -void R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]) +void R_ConcatTransforms(const float in1[3][4], const float in2[3][4], float out[3][4]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + - in1[0][2] * in2[2][0]; + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + - in1[0][2] * in2[2][1]; + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + - in1[0][2] * in2[2][2]; + in1[0][2] * in2[2][2]; out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + - in1[0][2] * in2[2][3] + in1[0][3]; + in1[0][2] * in2[2][3] + in1[0][3]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + - in1[1][2] * in2[2][0]; + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + - in1[1][2] * in2[2][1]; + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + - in1[1][2] * in2[2][2]; + in1[1][2] * in2[2][2]; out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + - in1[1][2] * in2[2][3] + in1[1][3]; + in1[1][2] * in2[2][3] + in1[1][3]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + - in1[2][2] * in2[2][0]; + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + - in1[2][2] * in2[2][1]; + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + - in1[2][2] * in2[2][2]; + in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + - in1[2][2] * in2[2][3] + in1[2][3]; + in1[2][2] * in2[2][3] + in1[2][3]; } - - -void VectorRotate (const vec3_t in1, const float in2[3][4], vec3_t out) +void VectorRotate(const vec3_t in1, const float in2[3][4], vec3_t out) { out[0] = DotProduct(in1, in2[0]); out[1] = DotProduct(in1, in2[1]); out[2] = DotProduct(in1, in2[2]); } - // rotate by the inverse of the matrix -void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out) +void VectorIRotate(const vec3_t in1, const float in2[3][4], vec3_t out) { - out[0] = in1[0]*in2[0][0] + in1[1]*in2[1][0] + in1[2]*in2[2][0]; - out[1] = in1[0]*in2[0][1] + in1[1]*in2[1][1] + in1[2]*in2[2][1]; - out[2] = in1[0]*in2[0][2] + in1[1]*in2[1][2] + in1[2]*in2[2][2]; + out[0] = in1[0] * in2[0][0] + in1[1] * in2[1][0] + in1[2] * in2[2][0]; + out[1] = in1[0] * in2[0][1] + in1[1] * in2[1][1] + in1[2] * in2[2][1]; + out[2] = in1[0] * in2[0][2] + in1[1] * in2[1][2] + in1[2] * in2[2][2]; } - -void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out) +void VectorTransform(const vec3_t in1, const float in2[3][4], vec3_t out) { out[0] = DotProduct(in1, in2[0]) + in2[0][3]; - out[1] = DotProduct(in1, in2[1]) + in2[1][3]; - out[2] = DotProduct(in1, in2[2]) + in2[2][3]; + out[1] = DotProduct(in1, in2[1]) + in2[1][3]; + out[2] = DotProduct(in1, in2[2]) + in2[2][3]; } - // WARNING!!! the "vector" parameter should be NORMALIZED!!! float DistanceToIntersection(const vec3_t origin, const vec3_t vector, - const vec3_t plane_origin, const vec3_t plane_normal) + const vec3_t plane_origin, const vec3_t plane_normal) { - float d = -(DotProduct(plane_normal, plane_origin)); - - float numerator = DotProduct(plane_normal, origin) + d; - float denominator = DotProduct(plane_normal, vector); - - if (fabs(denominator) < 0.00001) - return (-1.0f); // normal is orthogonal to vector, no intersection - - return -(numerator/denominator); -} + float d = -(DotProduct(plane_normal, plane_origin)); + + float numerator = DotProduct(plane_normal, origin) + d; + float denominator = DotProduct(plane_normal, vector); + if (fabs(denominator) < 0.00001) + return (-1.0f); // normal is orthogonal to vector, no intersection + + return -(numerator / denominator); +} // return TRUE or FALSE if vector intersects a plane... bool VectorIntersectPlane(const vec3_t origin, const vec3_t vector, - const vec3_t plane_origin, const vec3_t plane_normal, - vec3_t intersect_point) + const vec3_t plane_origin, const vec3_t plane_normal, + vec3_t intersect_point) { - float dist; - vec3_t v_temp; + float dist; + vec3_t v_temp; - dist = DistanceToIntersection(origin, vector, plane_origin, plane_normal); + dist = DistanceToIntersection(origin, vector, plane_origin, plane_normal); - if (dist < 0) - return FALSE; + if (dist < 0) + return FALSE; - VectorScale(vector, dist, v_temp); - VectorAdd(origin, v_temp, intersect_point); + VectorScale(vector, dist, v_temp); + VectorAdd(origin, v_temp, intersect_point); - return TRUE; + return TRUE; } - -void AngleQuaternion( const vec3_t angles, vec4_t quaternion ) +void AngleQuaternion(const vec3_t angles, vec4_t quaternion) { float angle; float sr, sp, sy, cr, cp, cy; @@ -332,15 +322,14 @@ void AngleQuaternion( const vec3_t angles, vec4_t quaternion ) sr = sin(angle); cr = cos(angle); - quaternion[0] = sr*cp*cy-cr*sp*sy; // X - quaternion[1] = cr*sp*cy+sr*cp*sy; // Y - quaternion[2] = cr*cp*sy-sr*sp*cy; // Z - quaternion[3] = cr*cp*cy+sr*sp*sy; // W + quaternion[0] = sr * cp * cy - cr * sp * sy; // X + quaternion[1] = cr * sp * cy + sr * cp * sy; // Y + quaternion[2] = cr * cp * sy - sr * sp * cy; // Z + quaternion[3] = cr * cp * cy + sr * sp * sy; // W } -void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ) +void QuaternionMatrix(const vec4_t quaternion, float(*matrix)[4]) { - matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2]; matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2]; matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1]; @@ -354,7 +343,7 @@ void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ) matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1]; } -void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) +void QuaternionSlerp(const vec4_t p, vec4_t q, float t, vec4_t qt) { int i; float omega, cosom, sinom, sclp, sclq; @@ -363,8 +352,8 @@ void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) float a = 0; float b = 0; for (i = 0; i < 4; i++) { - a += (p[i]-q[i])*(p[i]-q[i]); - b += (p[i]+q[i])*(p[i]+q[i]); + a += (p[i] - q[i]) * (p[i] - q[i]); + b += (p[i] + q[i]) * (p[i] + q[i]); } if (a > b) { for (i = 0; i < 4; i++) { @@ -372,14 +361,14 @@ void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) } } - cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3]; + cosom = p[0] * q[0] + p[1] * q[1] + p[2] * q[2] + p[3] * q[3]; if ((1.0 + cosom) > 0.00000001) { if ((1.0 - cosom) > 0.00000001) { - omega = acos( cosom ); - sinom = sin( omega ); - sclp = sin( (1.0 - t)*omega) / sinom; - sclq = sin( t*omega ) / sinom; + omega = acos(cosom); + sinom = sin(omega); + sclp = sin((1.0 - t) * omega) / sinom; + sclq = sin(t * omega) / sinom; } else { sclp = 1.0 - t; @@ -394,12 +383,10 @@ void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) qt[1] = p[0]; qt[2] = -p[3]; qt[3] = p[2]; - sclp = sin( (1.0 - t) * 0.5 * Q_PI); - sclq = sin( t * 0.5 * Q_PI); + sclp = sin((1.0 - t) * 0.5 * Q_PI); + sclq = sin(t * 0.5 * Q_PI); for (i = 0; i < 3; i++) { qt[i] = sclp * p[i] + sclq * qt[i]; } } -} - - +} \ No newline at end of file diff --git a/Bsp2Rbn/mathlib.h b/Bsp2Rbn/mathlib.h index 50f6ab6..b356b42 100644 --- a/Bsp2Rbn/mathlib.h +++ b/Bsp2Rbn/mathlib.h @@ -1,97 +1,95 @@ -/*** -* -* Copyright (c) 1996-2001, Valve LLC. 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. -* -****/ - -#ifndef __MATHLIB__ -#define __MATHLIB__ - -// mathlib.h - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef DOUBLEVEC_T -typedef double vec_t; -#else -typedef float vec_t; -#endif -//typedef vec_t vec3_t[3]; // x,y,z -typedef vec_t vec4_t[4]; // x,y,z,w - -#define SIDE_FRONT 0 -#define SIDE_ON 2 -#define SIDE_BACK 1 -#define SIDE_CROSS -2 - -#define Q_PI 3.14159265358979323846 - -extern vec3_t vec3_origin; - -// Use this definition globally -#define ON_EPSILON 0.01 -#define EQUAL_EPSILON 0.001 - -int VectorCompare (vec3_t v1, vec3_t v2); - -#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) -#define VectorFill(a,b) { (a)[0]=(b); (a)[1]=(b); (a)[2]=(b);} -#define VectorAvg(a) ( ( (a)[0] + (a)[1] + (a)[2] ) / 3 ) -#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];} -#define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];} -#define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];} -#define VectorScale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];(c)[2]=(b)*(a)[2];} - -vec_t Q_rint (vec_t in); -vec_t _DotProduct (vec3_t v1, vec3_t v2); -void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); -void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); -void _VectorCopy (vec3_t in, vec3_t out); -void _VectorScale (vec3_t v, vec_t scale, vec3_t out); - -double Vector2DLength(vec3_t v); -double VectorLength(vec3_t v); - -void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc); - -void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); -vec_t VectorNormalize (vec3_t v); -void VectorInverse (vec3_t v); - -void ClearBounds (vec3_t mins, vec3_t maxs); -void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); - -void AngleMatrix (const vec3_t angles, float matrix[3][4] ); -void AngleIMatrix (const vec3_t angles, float matrix[3][4] ); -void R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]); - -void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out); -void VectorRotate (const vec3_t in1, const float in2[3][4], vec3_t out); - -void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out); - -float DistanceToIntersection(const vec3_t origin, const vec3_t vector, - const vec3_t plane_origin, const vec3_t plane_normal); -bool VectorIntersectPlane(const vec3_t origin, const vec3_t vector, - const vec3_t plane_origin, const vec3_t plane_normal, - vec3_t intersect_point); - -void AngleQuaternion( const vec3_t angles, vec4_t quaternion ); -void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ); -void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ); - - -#ifdef __cplusplus -} -#endif - -#endif +/*** +* +* Copyright (c) 1996-2001, Valve LLC. 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. +* +****/ +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DOUBLEVEC_T + typedef double vec_t; +#else + typedef float vec_t; +#endif + //typedef vec_t vec3_t[3]; // x,y,z + typedef vec_t vec4_t[4]; // x,y,z,w + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +#define Q_PI 3.14159265358979323846 + + extern vec3_t vec3_origin; + + // Use this definition globally +#define ON_EPSILON 0.01 +#define EQUAL_EPSILON 0.001 + + int VectorCompare(vec3_t v1, vec3_t v2); + +#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) +#define VectorFill(a,b) { (a)[0]=(b); (a)[1]=(b); (a)[2]=(b);} +#define VectorAvg(a) ( ( (a)[0] + (a)[1] + (a)[2] ) / 3 ) +#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];} +#define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];} +#define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];} +#define VectorScale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];(c)[2]=(b)*(a)[2];} + + vec_t Q_rint(vec_t in); + vec_t _DotProduct(vec3_t v1, vec3_t v2); + void _VectorSubtract(vec3_t va, vec3_t vb, vec3_t out); + void _VectorAdd(vec3_t va, vec3_t vb, vec3_t out); + void _VectorCopy(vec3_t in, vec3_t out); + void _VectorScale(vec3_t v, vec_t scale, vec3_t out); + + double Vector2DLength(vec3_t v); + double VectorLength(vec3_t v); + + void VectorMA(vec3_t va, double scale, vec3_t vb, vec3_t vc); + + void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross); + vec_t VectorNormalize(vec3_t v); + void VectorInverse(vec3_t v); + + void ClearBounds(vec3_t mins, vec3_t maxs); + void AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs); + + void AngleMatrix(const vec3_t angles, float matrix[3][4]); + void AngleIMatrix(const vec3_t angles, float matrix[3][4]); + void R_ConcatTransforms(const float in1[3][4], const float in2[3][4], float out[3][4]); + + void VectorIRotate(const vec3_t in1, const float in2[3][4], vec3_t out); + void VectorRotate(const vec3_t in1, const float in2[3][4], vec3_t out); + + void VectorTransform(const vec3_t in1, const float in2[3][4], vec3_t out); + + float DistanceToIntersection(const vec3_t origin, const vec3_t vector, + const vec3_t plane_origin, const vec3_t plane_normal); + bool VectorIntersectPlane(const vec3_t origin, const vec3_t vector, + const vec3_t plane_origin, const vec3_t plane_normal, + vec3_t intersect_point); + + void AngleQuaternion(const vec3_t angles, vec4_t quaternion); + void QuaternionMatrix(const vec4_t quaternion, float(*matrix)[4]); + void QuaternionSlerp(const vec4_t p, vec4_t q, float t, vec4_t qt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Bsp2Rbn/paklib.h b/Bsp2Rbn/paklib.h index aec01b1..f9bcc41 100644 --- a/Bsp2Rbn/paklib.h +++ b/Bsp2Rbn/paklib.h @@ -15,7 +15,7 @@ // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License for more details at: // http://www.gnu.org/copyleft/gpl.html @@ -34,13 +34,13 @@ header. This is seperated into three, 4 byte blocks as follows: -Header- - Signature (4 bytes of char, equals 'PACK' in ALL cases. If it doesn't then - it is not considered a pack file.) + it is not considered a pack file.) - Directory Offset (4 bytes, single integer. Specifies the position of the - first of X Directory areas in the file.) + first of X Directory areas in the file.) - Directory Length (4 bytes, single integer. Specifies the length of the X - dirextory areas.) + dirextory areas.) (Note: We can find the number X by dividing the length by the 64, because each directory section is 64 bytes. If the mod of this division is not zero then @@ -49,51 +49,51 @@ header. This is seperated into three, 4 byte blocks as follows: -Directory section- - File name (56 bytes of char, specifies the name of the file pointed to by - the File Position data. Includes path info. ie maps/base1.bsp) + the File Position data. Includes path info. ie maps/base1.bsp) - File Position (4 bytes, single integer. The first byte(address) of the - file named by File Name) + file named by File Name) - File Length (4 bytes, single integer. The length of the file named by - File Name) + File Name) Notes: Normally, the header is at the start of the file and the X number of directory areas at the very end. The file data is usually in between. - ________________________________ - HEADER starts here ---> | - Signature (4 bytes) | - | - Directory Offset (4 bytes) | - | - Directory Length (4 bytes) | - |________________________________| - FILE DATA starts here ---> | | - | | - | BINARY DATA | - | (pointed to by the | - | File Position data) | - | | - ~~~~~ ~~~~~ - ~~~~~ ~~~~~ - | | - | | - | | - | | - |________________________________| + ________________________________ + HEADER starts here ---> | - Signature (4 bytes) | + | - Directory Offset (4 bytes) | + | - Directory Length (4 bytes) | + |________________________________| + FILE DATA starts here ---> | | + | | + | BINARY DATA | + | (pointed to by the | + | File Position data) | + | | + ~~~~~ ~~~~~ + ~~~~~ ~~~~~ + | | + | | + | | + | | + |________________________________| DIRECTORY SECTION starts here ---> | - File name (56 bytes) | - | - File Position (4 bytes) | - | - File Length (4 bytes) | - |________________________________| - | - File name (56 bytes) | - | - File Position (4 bytes) | - | - File Length (4 bytes) | - |________________________________| - | | - ~~~~~ ~~~~~ - ~~~~~ ~~~~~ - |________________________________| - | - File name (56 bytes) | - | - File Position (4 bytes) | - | - File Length (4 bytes) | - |________________________________| + | - File Position (4 bytes) | + | - File Length (4 bytes) | + |________________________________| + | - File name (56 bytes) | + | - File Position (4 bytes) | + | - File Length (4 bytes) | + |________________________________| + | | + ~~~~~ ~~~~~ + ~~~~~ ~~~~~ + |________________________________| + | - File name (56 bytes) | + | - File Position (4 bytes) | + | - File Length (4 bytes) | + |________________________________| */ // @@ -102,34 +102,33 @@ DIRECTORY SECTION starts here ---> | - File name (56 bytes) | typedef struct { - char identification[4]; // should be PACK or KCAP - int dir_offset; // directory offset - int dir_length; // directory length + char identification[4]; // should be PACK or KCAP + int dir_offset; // directory offset + int dir_length; // directory length } pakheader_t; typedef struct { - char filename[56]; // PAK entry filename - int file_pos; // PAK entry file position - int file_length; // PAK entry length + char filename[56]; // PAK entry filename + int file_pos; // PAK entry file position + int file_length; // PAK entry length } pakinfo_t; typedef struct pakconfig_s { - FILE *pakhandle; - pakheader_t pakheader; - pakinfo_t *pakinfo; - int num_entries; - struct pakconfig_s *next_config; + FILE* pakhandle; + pakheader_t pakheader; + pakinfo_t* pakinfo; + int num_entries; + struct pakconfig_s* next_config; } pakconfig_t; -FILE *P_OpenPak (char *filename); -int P_ReadPakHeader(FILE *pakhandle, pakheader_t *pakheader); -void P_ReadPakInfo(FILE *pakhandle, long offset, int num_entries, pakinfo_t *pakinfo); -void P_ReadPakItem(FILE *pakhandle, pakinfo_t *pakinfo, void *buffer); +FILE* P_OpenPak(char* filename); +int P_ReadPakHeader(FILE* pakhandle, pakheader_t* pakheader); +void P_ReadPakInfo(FILE* pakhandle, long offset, int num_entries, pakinfo_t* pakinfo); +void P_ReadPakItem(FILE* pakhandle, pakinfo_t* pakinfo, void* buffer); void Cmd_PakFile(void); -bool SearchPakFilename(char *filename, pakconfig_t **pakconfig, pakinfo_t **pakinfo); -bool LoadPakBSPFile(char *filename); +bool SearchPakFilename(char* filename, pakconfig_t** pakconfig, pakinfo_t** pakinfo); +bool LoadPakBSPFile(char* filename); #endif - diff --git a/Bsp2Rbn/scriplib.cpp b/Bsp2Rbn/scriplib.cpp index 2865066..b55e775 100644 --- a/Bsp2Rbn/scriplib.cpp +++ b/Bsp2Rbn/scriplib.cpp @@ -2,8 +2,8 @@ * * Copyright (c) 1998, Valve LLC. All rights reserved. * -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * ****/ @@ -18,21 +18,21 @@ /* ============================================================================= - PARSING STUFF + PARSING STUFF ============================================================================= */ typedef struct { - char filename[1024]; - char *buffer,*script_p,*end_p; - int line; + char filename[1024]; + char* buffer, * script_p, * end_p; + int line; } script_t; #define MAX_INCLUDES 8 script_t scriptstack[MAX_INCLUDES]; -script_t *script; +script_t* script; int scriptline; char token[MAXTOKEN]; @@ -44,64 +44,61 @@ qboolean tokenready; // only true if UnGetToken was just cal AddScriptToStack ============== */ -void AddScriptToStack (char *filename) +void AddScriptToStack(char* filename) { - int size; + int size; - script++; - if (script == &scriptstack[MAX_INCLUDES]) - Error ("script file exceeded MAX_INCLUDES"); - strcpy (script->filename, ExpandPath (filename) ); + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error("script file exceeded MAX_INCLUDES"); + strcpy(script->filename, ExpandPath(filename)); - size = LoadFile (script->filename, (void **)&script->buffer); + size = LoadFile(script->filename, (void**)&script->buffer); -// printf ("entering %s\n", script->filename); + // printf ("entering %s\n", script->filename); - script->line = 1; + script->line = 1; - script->script_p = script->buffer; - script->end_p = script->buffer + size; + script->script_p = script->buffer; + script->end_p = script->buffer + size; } - /* ============== LoadScriptFile ============== */ -void LoadScriptFile (char *filename) +void LoadScriptFile(char* filename) { - script = scriptstack; - AddScriptToStack (filename); + script = scriptstack; + AddScriptToStack(filename); - endofscript = false; - tokenready = false; + endofscript = false; + tokenready = false; } - /* ============== ParseFromMemory ============== */ -void ParseFromMemory (char *buffer, int size) +void ParseFromMemory(char* buffer, int size) { - script = scriptstack; - script++; - if (script == &scriptstack[MAX_INCLUDES]) - Error ("script file exceeded MAX_INCLUDES"); - strcpy (script->filename, "memory buffer" ); - - script->buffer = buffer; - script->line = 1; - script->script_p = script->buffer; - script->end_p = script->buffer + size; - - endofscript = false; - tokenready = false; + script = scriptstack; + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error("script file exceeded MAX_INCLUDES"); + strcpy(script->filename, "memory buffer"); + + script->buffer = buffer; + script->line = 1; + script->script_p = script->buffer; + script->end_p = script->buffer + size; + + endofscript = false; + tokenready = false; } - /* ============== UnGetToken @@ -116,33 +113,32 @@ GetToken (false); could cross a line boundary. ============== */ -void UnGetToken (void) +void UnGetToken(void) { - tokenready = true; + tokenready = true; } - -qboolean EndOfScript (qboolean crossline) +qboolean EndOfScript(qboolean crossline) { - if (!crossline) - Error ("Line %i is incomplete\n",scriptline+1); - - if (!strcmp (script->filename, "memory buffer")) - { - endofscript = true; - return false; - } - - free (script->buffer); - if (script == scriptstack+1) - { - endofscript = true; - return false; - } - script--; - scriptline = script->line; -// printf ("returning to %s\n", script->filename); - return GetToken (crossline); + if (!crossline) + Error("Line %i is incomplete\n", scriptline + 1); + + if (!strcmp(script->filename, "memory buffer")) + { + endofscript = true; + return false; + } + + free(script->buffer); + if (script == scriptstack + 1) + { + endofscript = true; + return false; + } + script--; + scriptline = script->line; + // printf ("returning to %s\n", script->filename); + return GetToken(crossline); } /* @@ -150,92 +146,91 @@ qboolean EndOfScript (qboolean crossline) GetToken ============== */ -qboolean GetToken (qboolean crossline) +qboolean GetToken(qboolean crossline) { - char *token_p; + char* token_p; - if (tokenready) // is a token allready waiting? - { - tokenready = false; - return true; - } + if (tokenready) // is a token allready waiting? + { + tokenready = false; + return true; + } - if (script->script_p >= script->end_p) - return EndOfScript (crossline); + if (script->script_p >= script->end_p) + return EndOfScript(crossline); -// -// skip space -// + // + // skip space + // skipspace: - while (*script->script_p <= 32) - { - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - if (*script->script_p++ == '\n') - { - if (!crossline) - Error ("Line %i is incomplete\n",scriptline+1); - scriptline = script->line++; - } - } - - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - - if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field - (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field - { - if (!crossline) - Error ("Line %i is incomplete\n",scriptline+1); - while (*script->script_p++ != '\n') - if (script->script_p >= script->end_p) - return EndOfScript (crossline); - scriptline = script->line++; - goto skipspace; - } - -// -// copy token -// - token_p = token; - - if (*script->script_p == '"') - { - // quoted token - script->script_p++; - while (*script->script_p != '"') - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - } - script->script_p++; - } - else // regular token - while ( *script->script_p > 32 && *script->script_p != ';') - { - *token_p++ = *script->script_p++; - if (script->script_p == script->end_p) - break; - if (token_p == &token[MAXTOKEN]) - Error ("Token too large on line %i\n",scriptline); - } - - *token_p = 0; - - if (!strcmp (token, "$include")) - { - GetToken (false); - AddScriptToStack (token); - return GetToken (crossline); - } - - return true; + while (*script->script_p <= 32) + { + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + if (*script->script_p++ == '\n') + { + if (!crossline) + Error("Line %i is incomplete\n", scriptline + 1); + scriptline = script->line++; + } + } + + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + + if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field + (*script->script_p == '/' && *((script->script_p) + 1) == '/')) // also make // a comment field + { + if (!crossline) + Error("Line %i is incomplete\n", scriptline + 1); + while (*script->script_p++ != '\n') + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + scriptline = script->line++; + goto skipspace; + } + + // + // copy token + // + token_p = token; + + if (*script->script_p == '"') + { + // quoted token + script->script_p++; + while (*script->script_p != '"') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error("Token too large on line %i\n", scriptline); + } + script->script_p++; + } + else // regular token + while (*script->script_p > 32 && *script->script_p != ';') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error("Token too large on line %i\n", scriptline); + } + + *token_p = 0; + + if (!strcmp(token, "$include")) + { + GetToken(false); + AddScriptToStack(token); + return GetToken(crossline); + } + + return true; } - /* ============== TokenAvailable @@ -243,28 +238,26 @@ TokenAvailable Returns true if there is another token on the line ============== */ -qboolean TokenAvailable (void) +qboolean TokenAvailable(void) { - char *search_p; + char* search_p; - search_p = script->script_p; + search_p = script->script_p; - if (search_p >= script->end_p) - return false; + if (search_p >= script->end_p) + return false; - while ( *search_p <= 32) - { - if (*search_p == '\n') - return false; - search_p++; - if (search_p == script->end_p) - return false; + while (*search_p <= 32) + { + if (*search_p == '\n') + return false; + search_p++; + if (search_p == script->end_p) + return false; + } - } - - if (*search_p == ';') - return false; - - return true; -} + if (*search_p == ';') + return false; + return true; +} \ No newline at end of file diff --git a/Bsp2Rbn/scriplib.h b/Bsp2Rbn/scriplib.h index 3200c13..306ba99 100644 --- a/Bsp2Rbn/scriplib.h +++ b/Bsp2Rbn/scriplib.h @@ -2,8 +2,8 @@ * * Copyright (c) 1998, Valve LLC. All rights reserved. * -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * ****/ @@ -17,22 +17,19 @@ #ifndef SCRIPLIB_H #define SCRIPLIB_H - #define MAXTOKEN 4096 extern char token[MAXTOKEN]; -extern char *scriptbuffer,*script_p,*scriptend_p; +extern char* scriptbuffer, * script_p, * scriptend_p; extern int grabbed; extern int scriptline; extern qboolean endofscript; +void LoadScriptFile(char* filename); +void ParseFromMemory(char* buffer, int size); -void LoadScriptFile (char *filename); -void ParseFromMemory (char *buffer, int size); - -qboolean GetToken (qboolean crossline); -void UnGetToken (void); -qboolean TokenAvailable (void); +qboolean GetToken(qboolean crossline); +void UnGetToken(void); +qboolean TokenAvailable(void); #endif // SCRIPLIB_H - diff --git a/Bsp2Rbn/studio_model.h b/Bsp2Rbn/studio_model.h index 3fac790..ad08082 100644 --- a/Bsp2Rbn/studio_model.h +++ b/Bsp2Rbn/studio_model.h @@ -1,91 +1,90 @@ -/*** -* -* Copyright (c) 1996-2001, Valve LLC. 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. -* -****/ - -#ifndef __MATHLIB__ -#include "mathlib.h" -#endif - -#ifndef _STUDIO_H_ -#include "studio.h" -#endif - -#ifndef __STUDIO_MODEL__ -#define __STUDIO_MODEL__ - -typedef unsigned char byte; - -class StudioModel -{ -public: - bool Init( char *modelname ); - void DrawModel( void ); - void AdvanceFrame( float dt ); - - void ExtractBbox( float *mins, float *maxs ); - - int SetSequence( int iSequence ); - int GetSequence( void ); - void GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed ); - - float SetController( int iController, float flValue ); - float SetMouth( float flValue ); - float SetBlending( int iBlender, float flValue ); - int SetBodygroup( int iGroup, int iValue ); - int SetSkin( int iValue ); - -private: - // entity settings - vec3_t m_origin; - vec3_t m_angles; - int m_sequence; // sequence index - float m_frame; // frame - int m_bodynum; // bodypart selection - int m_skinnum; // skin group selection - byte m_controller[4]; // bone controllers - byte m_blending[2]; // animation blending - byte m_mouth; // mouth position - - // internal data - studiohdr_t *m_pstudiohdr; - mstudiomodel_t *m_pmodel; - - studiohdr_t *m_ptexturehdr; - studioseqhdr_t *m_panimhdr[32]; - - vec4_t m_adj; // FIX: non persistant, make static - - studiohdr_t *LoadModel( char *modelname ); - studioseqhdr_t *LoadDemandSequences( char *modelname ); - - void CalcBoneAdj( void ); - void CalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *q ); - void CalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *pos ); - void CalcRotations ( vec3_t *pos, vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f ); - mstudioanim_t *GetAnim( mstudioseqdesc_t *pseqdesc ); - void SlerpBones( vec4_t q1[], vec3_t pos1[], vec4_t q2[], vec3_t pos2[], float s ); - void SetUpBones ( void ); - - void DrawPoints( void ); - - void Lighting (float *lv, int bone, int flags, vec3_t normal); - void Chrome (int *chrome, int bone, vec3_t normal); - - void SetupLighting( void ); - - void SetupModel ( int bodypart ); - - void UploadTexture( mstudiotexture_t *ptexture, byte *data, byte *pal ); -}; - -extern vec3_t g_vright; // needs to be set to viewer's right in order for chrome to work -extern float g_lambert; // modifier for pseudo-hemispherical lighting - -#endif +/*** +* +* Copyright (c) 1996-2001, Valve LLC. 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. +* +****/ +#ifndef __MATHLIB__ +#include "mathlib.h" +#endif + +#ifndef _STUDIO_H_ +#include "studio.h" +#endif + +#ifndef __STUDIO_MODEL__ +#define __STUDIO_MODEL__ + +typedef unsigned char byte; + +class StudioModel +{ +public: + bool Init(char* modelname); + void DrawModel(void); + void AdvanceFrame(float dt); + + void ExtractBbox(float* mins, float* maxs); + + int SetSequence(int iSequence); + int GetSequence(void); + void GetSequenceInfo(float* pflFrameRate, float* pflGroundSpeed); + + float SetController(int iController, float flValue); + float SetMouth(float flValue); + float SetBlending(int iBlender, float flValue); + int SetBodygroup(int iGroup, int iValue); + int SetSkin(int iValue); + +private: + // entity settings + vec3_t m_origin; + vec3_t m_angles; + int m_sequence; // sequence index + float m_frame; // frame + int m_bodynum; // bodypart selection + int m_skinnum; // skin group selection + byte m_controller[4]; // bone controllers + byte m_blending[2]; // animation blending + byte m_mouth; // mouth position + + // internal data + studiohdr_t* m_pstudiohdr; + mstudiomodel_t* m_pmodel; + + studiohdr_t* m_ptexturehdr; + studioseqhdr_t* m_panimhdr[32]; + + vec4_t m_adj; // FIX: non persistant, make static + + studiohdr_t* LoadModel(char* modelname); + studioseqhdr_t* LoadDemandSequences(char* modelname); + + void CalcBoneAdj(void); + void CalcBoneQuaternion(int frame, float s, mstudiobone_t* pbone, mstudioanim_t* panim, float* q); + void CalcBonePosition(int frame, float s, mstudiobone_t* pbone, mstudioanim_t* panim, float* pos); + void CalcRotations(vec3_t* pos, vec4_t* q, mstudioseqdesc_t* pseqdesc, mstudioanim_t* panim, float f); + mstudioanim_t* GetAnim(mstudioseqdesc_t* pseqdesc); + void SlerpBones(vec4_t q1[], vec3_t pos1[], vec4_t q2[], vec3_t pos2[], float s); + void SetUpBones(void); + + void DrawPoints(void); + + void Lighting(float* lv, int bone, int flags, vec3_t normal); + void Chrome(int* chrome, int bone, vec3_t normal); + + void SetupLighting(void); + + void SetupModel(int bodypart); + + void UploadTexture(mstudiotexture_t* ptexture, byte* data, byte* pal); +}; + +extern vec3_t g_vright; // needs to be set to viewer's right in order for chrome to work +extern float g_lambert; // modifier for pseudo-hemispherical lighting + +#endif diff --git a/Bsp2Rbn/trace.cpp b/Bsp2Rbn/trace.cpp index a132597..34a11f4 100644 --- a/Bsp2Rbn/trace.cpp +++ b/Bsp2Rbn/trace.cpp @@ -40,95 +40,94 @@ typedef vec_t vec3_t[3]; #define MAX(a,b) (a > b ? a : b) #define MIN(a,b) (a < b ? a : b) -dnode_t *gNode; +dnode_t* gNode; // return the leaf node for a point in 3D space... for model rooted on nodenum -static dleaf_t *TracePointInHull0Leaf(const int num, const vec3_t point) +static dleaf_t* TracePointInHull0Leaf(const int num, const vec3_t point) { - vec_t d; - dnode_t *node; - dplane_t *plane; - int nodenum ; - - nodenum = num ; - // walk through the tree to find the leaf for the point... - while (nodenum >= 0) - { - node = &dnodes[nodenum]; - plane = &dplanes[node->planenum]; - d = DotProduct(point, plane->normal) - plane->dist; - if (d > 0) - nodenum = node->children[0]; - else - nodenum = node->children[1]; - } - - gNode = node; - return &dleafs[-nodenum - 1]; + vec_t d; + dnode_t* node; + dplane_t* plane; + int nodenum; + + nodenum = num; + // walk through the tree to find the leaf for the point... + while (nodenum >= 0) + { + node = &dnodes[nodenum]; + plane = &dplanes[node->planenum]; + d = DotProduct(point, plane->normal) - plane->dist; + if (d > 0) + nodenum = node->children[0]; + else + nodenum = node->children[1]; + } + + gNode = node; + return &dleafs[-nodenum - 1]; } // return contents of a coordinate in 3D space... using hull > 0 (collision hulls) static int TracePointInLeaf(const int hullNumber, const vec3_t point) { - int nodenum; - vec_t d; - dclipnode_t *node; - dplane_t *plane; - - nodenum = dmodels[0].headnode[hullNumber] ; - // walk through the tree to find the leaf for the point... - while (nodenum >= 0) - { - node = &dclipnodes[nodenum]; - plane = &dplanes[node->planenum]; - d = DotProduct(point, plane->normal) - plane->dist; - if (d > 0) - nodenum = node->children[0]; - else - nodenum = node->children[1]; - } - return nodenum ; + int nodenum; + vec_t d; + dclipnode_t* node; + dplane_t* plane; + + nodenum = dmodels[0].headnode[hullNumber]; + // walk through the tree to find the leaf for the point... + while (nodenum >= 0) + { + node = &dclipnodes[nodenum]; + plane = &dplanes[node->planenum]; + d = DotProduct(point, plane->normal) - plane->dist; + if (d > 0) + nodenum = node->children[0]; + else + nodenum = node->children[1]; + } + return nodenum; } // find the contents of a coordinate in 3D space... using hull > 0 (collision hulls) // Just a wrapper for TracePointInLeaf -int BotmanPointContentsInHull(const int hullNumber,const vec3_t coord) +int BotmanPointContentsInHull(const int hullNumber, const vec3_t coord) { - if (hullNumber == point_hull) - return BotmanPointContents(dmodels[0].headnode[0],coord) ; + if (hullNumber == point_hull) + return BotmanPointContents(dmodels[0].headnode[0], coord); - return TracePointInLeaf(hullNumber,coord); + return TracePointInLeaf(hullNumber, coord); } // find the contents of a coordinate in 3D space... using hull0 (visibility) int BotmanPointContents(const int nodenum, const vec3_t coord) { - dleaf_t *leaf; + dleaf_t* leaf; - leaf = TracePointInHull0Leaf(nodenum,coord) ; + leaf = TracePointInHull0Leaf(nodenum, coord); - // return contents (CONTENTS_EMPTY, CONTENTS_SOLID, CONTENTS_WATER, etc.) - return leaf->contents; + // return contents (CONTENTS_EMPTY, CONTENTS_SOLID, CONTENTS_WATER, etc.) + return leaf->contents; } - -int UTIL_PointContents(const Vector & x) +int UTIL_PointContents(const Vector& x) { - vec3_t point ; - int WorldContent, ModelContent ; - int ent_index, model_index ; - char * value ; + vec3_t point; + int WorldContent, ModelContent; + int ent_index, model_index; + char* value; - point[0]=x.x ; - point[1]=x.y ; - point[2]=x.z ; + point[0] = x.x; + point[1] = x.y; + point[2] = x.z; // Find the content in visibility hull of the world (dmodels[0]) - WorldContent = BotmanPointContents(0,point) ; + WorldContent = BotmanPointContents(0, point); // If not empty, return the result if (WorldContent != CONTENTS_EMPTY) - return WorldContent ; + return WorldContent; // If CONTENTS_EMPTY, need to do further check for water... @@ -139,280 +138,277 @@ int UTIL_PointContents(const Vector & x) value = ValueForKey(&entities[ent_index], "model"); if (value[0]) { sscanf(value, "*%d", &model_index); - ModelContent= BotmanPointContents(dmodels[model_index].headnode[0],point); + ModelContent = BotmanPointContents(dmodels[model_index].headnode[0], point); if (ModelContent == CONTENT_WATER) { -//printf("***Found water !!! (%.0f,%.0f,%.0f)\n",point[0],point[1],point[2]) ; - return ModelContent ; + //printf("***Found water !!! (%.0f,%.0f,%.0f)\n",point[0],point[1],point[2]) ; + return ModelContent; } } - } - return WorldContent ; + return WorldContent; } - - // trace a line from start to end, fill in trace_t structure with result... // Should rather use Quake SV_RecursiveHullCheck() -void BotmanTraceLine(vec3_t start, vec3_t end, botman_trace_t *tr) +void BotmanTraceLine(vec3_t start, vec3_t end, botman_trace_t* tr) { -dleaf_t *startleaf, *endleaf; -int numsteps, totalsteps; -vec3_t move, step, position; -float dist, trace_dist; - - memset(tr, 0, sizeof(botman_trace_t)); - - if ((start[0] < -4095) || (start[0] > 4095) || - (start[1] < -4095) || (start[1] > 4095) || - (start[2] < -4095) || (start[2] > 4095)) - { - // start beyond edge of world is INVALID!!! - fprintf(stderr,"TraceLine: start point beyond edge of world!\n"); - } - - if (end[0] > 4095.0f) - { - float percent = 4095.0f / end[0]; - end[1] = end[1] * percent; - end[2] = end[2] * percent; - end[0] = 4095.0f; - } - - if (end[1] > 4095.0f) - { - float percent = 4095.0f / end[1]; - end[0] = end[0] * percent; - end[2] = end[2] * percent; - end[1] = 4095.0f; - } - - if (end[2] > 4095.0f) - { - float percent = 4095.0f / end[2]; - end[0] = end[0] * percent; - end[1] = end[1] * percent; - end[2] = 4095.0f; - } - - if (end[0] < -4095.0f) - { - float percent = 4095.0f / end[0]; - end[1] = end[1] * percent; - end[2] = end[2] * percent; - end[0] = -4095.0f; - } - - if (end[1] < -4095.0f) - { - float percent = 4095.0f / end[1]; - end[0] = end[0] * percent; - end[2] = end[2] * percent; - end[1] = -4095.0f; - } - - if (end[2] < -4095.0f) - { - float percent = 4095.0f / end[2]; - end[0] = end[0] * percent; - end[1] = end[1] * percent; - end[2] = -4095.0f; - } - - // find the starting and ending leafs... - startleaf = TracePointInHull0Leaf(0,start); - endleaf = TracePointInHull0Leaf(0,end); - - // set endpos, fraction and contents to the default (trace completed) - VectorCopy(end, tr->endpos); - tr->fraction = 1.0f; - tr->contents = endleaf->contents; - - if (startleaf->contents == CONTENTS_SOLID) - tr->startsolid = TRUE; - - // is start and end leaf the same (couldn't possibly hit the world)... - if (startleaf == endleaf) { - if (startleaf->contents == CONTENTS_SOLID) - tr->allsolid = TRUE; - return; - } - - // get the length of each interation of the loop... - VectorSubtract(end, start, move); - dist = (float)VectorLength(move); - - // determine the number of steps from start to end... - if (dist > 1.0f) - numsteps = totalsteps = (int)dist + 1; - else - numsteps = totalsteps = 1; - - // calculate the length of the step vector... - VectorScale(move, (float)2/numsteps, step); - - VectorCopy(start, position); - - while (numsteps) - { - VectorAdd(position, step, position); - - endleaf = TracePointInHull0Leaf(0,position); - - if ((endleaf->contents == CONTENTS_SOLID) || // we hit something solid... - (endleaf->contents == CONTENTS_SKY)) // we hit the sky - { - vec3_t hitpos; - - VectorCopy(position, hitpos); - - // store the hit position - VectorCopy(position, tr->hitpos); - - // back off one step before solid - VectorSubtract(position, step, position); - - // store the end position and end position contents - VectorCopy(position, tr->endpos); - tr->contents = endleaf->contents; - - VectorSubtract(position, start, move); - trace_dist = (float)VectorLength(move); - tr->fraction = trace_dist / dist; - - break; // break out of while loop - } - - numsteps--; - } + dleaf_t* startleaf, * endleaf; + int numsteps, totalsteps; + vec3_t move, step, position; + float dist, trace_dist; + + memset(tr, 0, sizeof(botman_trace_t)); + + if ((start[0] < -4095) || (start[0] > 4095) || + (start[1] < -4095) || (start[1] > 4095) || + (start[2] < -4095) || (start[2] > 4095)) + { + // start beyond edge of world is INVALID!!! + fprintf(stderr, "TraceLine: start point beyond edge of world!\n"); + } + + if (end[0] > 4095.0f) + { + float percent = 4095.0f / end[0]; + end[1] = end[1] * percent; + end[2] = end[2] * percent; + end[0] = 4095.0f; + } + + if (end[1] > 4095.0f) + { + float percent = 4095.0f / end[1]; + end[0] = end[0] * percent; + end[2] = end[2] * percent; + end[1] = 4095.0f; + } + + if (end[2] > 4095.0f) + { + float percent = 4095.0f / end[2]; + end[0] = end[0] * percent; + end[1] = end[1] * percent; + end[2] = 4095.0f; + } + + if (end[0] < -4095.0f) + { + float percent = 4095.0f / end[0]; + end[1] = end[1] * percent; + end[2] = end[2] * percent; + end[0] = -4095.0f; + } + + if (end[1] < -4095.0f) + { + float percent = 4095.0f / end[1]; + end[0] = end[0] * percent; + end[2] = end[2] * percent; + end[1] = -4095.0f; + } + + if (end[2] < -4095.0f) + { + float percent = 4095.0f / end[2]; + end[0] = end[0] * percent; + end[1] = end[1] * percent; + end[2] = -4095.0f; + } + + // find the starting and ending leafs... + startleaf = TracePointInHull0Leaf(0, start); + endleaf = TracePointInHull0Leaf(0, end); + + // set endpos, fraction and contents to the default (trace completed) + VectorCopy(end, tr->endpos); + tr->fraction = 1.0f; + tr->contents = endleaf->contents; + + if (startleaf->contents == CONTENTS_SOLID) + tr->startsolid = TRUE; + + // is start and end leaf the same (couldn't possibly hit the world)... + if (startleaf == endleaf) { + if (startleaf->contents == CONTENTS_SOLID) + tr->allsolid = TRUE; + return; + } + + // get the length of each interation of the loop... + VectorSubtract(end, start, move); + dist = (float)VectorLength(move); + + // determine the number of steps from start to end... + if (dist > 1.0f) + numsteps = totalsteps = (int)dist + 1; + else + numsteps = totalsteps = 1; + + // calculate the length of the step vector... + VectorScale(move, (float)2 / numsteps, step); + + VectorCopy(start, position); + + while (numsteps) + { + VectorAdd(position, step, position); + + endleaf = TracePointInHull0Leaf(0, position); + + if ((endleaf->contents == CONTENTS_SOLID) || // we hit something solid... + (endleaf->contents == CONTENTS_SKY)) // we hit the sky + { + vec3_t hitpos; + + VectorCopy(position, hitpos); + + // store the hit position + VectorCopy(position, tr->hitpos); + + // back off one step before solid + VectorSubtract(position, step, position); + + // store the end position and end position contents + VectorCopy(position, tr->endpos); + tr->contents = endleaf->contents; + + VectorSubtract(position, start, move); + trace_dist = (float)VectorLength(move); + tr->fraction = trace_dist / dist; + + break; // break out of while loop + } + + numsteps--; + } } #define TWO_PI 6.2831853f #define DELTA 0.001f // find the face where the traceline hit... -dface_t *TraceLineFindFace(vec3_t start, botman_trace_t *tr) +dface_t* TraceLineFindFace(vec3_t start, botman_trace_t* tr) { - vec3_t v_intersect, v_normalized, v_temp; - dface_t *return_face = NULL; - float min_diff = 9999.9f; + vec3_t v_intersect, v_normalized, v_temp; + dface_t* return_face = NULL; + float min_diff = 9999.9f; - VectorSubtract(tr->endpos, start, v_normalized); - VectorNormalize(v_normalized); + VectorSubtract(tr->endpos, start, v_normalized); + VectorNormalize(v_normalized); - dleaf_t *endleaf = TracePointInHull0Leaf(0,tr->endpos); + dleaf_t* endleaf = TracePointInHull0Leaf(0, tr->endpos); - unsigned short *p = dmarksurfaces + endleaf->firstmarksurface; + unsigned short* p = dmarksurfaces + endleaf->firstmarksurface; - // find a plane with endpos on one side and hitpos on the other side... - for (int i = 0; i < endleaf->nummarksurfaces; i++) - { - int face_idx = *p++; - - dface_t *face = &dfaces[face_idx]; + // find a plane with endpos on one side and hitpos on the other side... + for (int i = 0; i < endleaf->nummarksurfaces; i++) + { + int face_idx = *p++; - dplane_t *plane = &dplanes[face->planenum]; + dface_t* face = &dfaces[face_idx]; - float d1 = DotProduct(tr->endpos, plane->normal) - plane->dist; - float d2 = DotProduct(tr->hitpos, plane->normal) - plane->dist; + dplane_t* plane = &dplanes[face->planenum]; - if ((d1 > 0 && d2 <= 0)||(d1 <= 0 && d2 > 0)) - { - // found a plane, find the intersection point in the plane... + float d1 = DotProduct(tr->endpos, plane->normal) - plane->dist; + float d2 = DotProduct(tr->hitpos, plane->normal) - plane->dist; - vec3_t plane_origin, v_angle1, v_angle2; + if ((d1 > 0 && d2 <= 0) || (d1 <= 0 && d2 > 0)) + { + // found a plane, find the intersection point in the plane... - VectorScale(plane->normal, plane->dist, plane_origin); + vec3_t plane_origin, v_angle1, v_angle2; - float dist = DistanceToIntersection(start, v_normalized, plane_origin, plane->normal); + VectorScale(plane->normal, plane->dist, plane_origin); - if (dist < 0) - return NULL; // can't find intersection + float dist = DistanceToIntersection(start, v_normalized, plane_origin, plane->normal); - VectorScale(v_normalized, dist, v_temp); - VectorAdd(start, v_temp, v_intersect); + if (dist < 0) + return NULL; // can't find intersection - // loop through all of the vertexes of all the edges of this face and - // find the angle between vertex-n, v_intersect and vertex-n+1, then add - // all these angles together. if the sum of these angles is 360 degrees - // (or 2 PI radians), then the intersect point lies within that polygon. + VectorScale(v_normalized, dist, v_temp); + VectorAdd(start, v_temp, v_intersect); - float angle_sum = 0.0f; + // loop through all of the vertexes of all the edges of this face and + // find the angle between vertex-n, v_intersect and vertex-n+1, then add + // all these angles together. if the sum of these angles is 360 degrees + // (or 2 PI radians), then the intersect point lies within that polygon. - // loop though all of the edges, getting the vertexes... - for (int edge_index = 0; edge_index < face->numedges; edge_index++) - { - vec3_t vertex1, vertex2; + float angle_sum = 0.0f; - // get the coordinates of the vertex of this edge... - int edge = dsurfedges[face->firstedge + edge_index]; + // loop though all of the edges, getting the vertexes... + for (int edge_index = 0; edge_index < face->numedges; edge_index++) + { + vec3_t vertex1, vertex2; - if (edge < 0) - { - edge = -edge; - dedge_t* e = &dedges[edge]; - VectorCopy(dvertexes[e->v[1]].point, vertex1); - VectorCopy(dvertexes[e->v[0]].point, vertex2); - } - else - { - dedge_t* e = &dedges[edge]; - VectorCopy(dvertexes[e->v[0]].point, vertex1); - VectorCopy(dvertexes[e->v[1]].point, vertex2); - } + // get the coordinates of the vertex of this edge... + int edge = dsurfedges[face->firstedge + edge_index]; - // now create vectors from the vertexes to the plane intersect point... - VectorSubtract(vertex1, v_intersect, v_angle1); - VectorSubtract(vertex2, v_intersect, v_angle2); + if (edge < 0) + { + edge = -edge; + dedge_t* e = &dedges[edge]; + VectorCopy(dvertexes[e->v[1]].point, vertex1); + VectorCopy(dvertexes[e->v[0]].point, vertex2); + } + else + { + dedge_t* e = &dedges[edge]; + VectorCopy(dvertexes[e->v[0]].point, vertex1); + VectorCopy(dvertexes[e->v[1]].point, vertex2); + } - VectorNormalize(v_angle1); - VectorNormalize(v_angle2); + // now create vectors from the vertexes to the plane intersect point... + VectorSubtract(vertex1, v_intersect, v_angle1); + VectorSubtract(vertex2, v_intersect, v_angle2); - // find the angle between these vectors... - float angle = DotProduct(v_angle1, v_angle2); + VectorNormalize(v_angle1); + VectorNormalize(v_angle2); - angle = (float)acos(angle); + // find the angle between these vectors... + float angle = DotProduct(v_angle1, v_angle2); - angle_sum += angle; + angle = (float)acos(angle); - edge++; - } + angle_sum += angle; - // is the sum of the angles 360 degrees (2 PI)?... - if ((angle_sum >= (TWO_PI - DELTA)) && (angle_sum <= (TWO_PI + DELTA))) - { - // find the difference between the sum and 2 PI... - float diff = (float)fabs(angle_sum - TWO_PI); + edge++; + } - if (diff < min_diff) // is this the BEST so far?... - { - min_diff = diff; - return_face = face; - } - } - } - } + // is the sum of the angles 360 degrees (2 PI)?... + if ((angle_sum >= (TWO_PI - DELTA)) && (angle_sum <= (TWO_PI + DELTA))) + { + // find the difference between the sum and 2 PI... + float diff = (float)fabs(angle_sum - TWO_PI); + + if (diff < min_diff) // is this the BEST so far?... + { + min_diff = diff; + return_face = face; + } + } + } + } - return return_face; + return return_face; } -static void ConvertTraceResult(botman_trace_t * tr, TraceResult * ptr) +static void ConvertTraceResult(botman_trace_t* tr, TraceResult* ptr) { - ptr->fAllSolid = tr->allsolid ; - ptr->fStartSolid = tr->startsolid ; - ptr->fInOpen = tr->contents == CONTENTS_EMPTY ; // TO BE FIXED - ptr->fInWater = tr->contents == CONTENTS_WATER ; // ???? - ptr->flFraction = tr->fraction ; - ptr->vecEndPos[0] = tr->endpos[0] ; - ptr->vecEndPos[1] = tr->endpos[1] ; - ptr->vecEndPos[2] = tr->endpos[2] ; - ptr->flPlaneDist = 0.0f ; // TO BE FIXED ? - ptr->vecPlaneNormal[0] = 0 ; // TO BE FIXED - ptr->vecPlaneNormal[1] = 0 ; // TO BE FIXED - ptr->vecPlaneNormal[2] = 0 ; // TO BE FIXED - ptr->pHit = NULL ; // TO BE FIXED - ptr->iHitgroup = 0 ; + ptr->fAllSolid = tr->allsolid; + ptr->fStartSolid = tr->startsolid; + ptr->fInOpen = tr->contents == CONTENTS_EMPTY; // TO BE FIXED + ptr->fInWater = tr->contents == CONTENTS_WATER; // ???? + ptr->flFraction = tr->fraction; + ptr->vecEndPos[0] = tr->endpos[0]; + ptr->vecEndPos[1] = tr->endpos[1]; + ptr->vecEndPos[2] = tr->endpos[2]; + ptr->flPlaneDist = 0.0f; // TO BE FIXED ? + ptr->vecPlaneNormal[0] = 0; // TO BE FIXED + ptr->vecPlaneNormal[1] = 0; // TO BE FIXED + ptr->vecPlaneNormal[2] = 0; // TO BE FIXED + ptr->pHit = NULL; // TO BE FIXED + ptr->iHitgroup = 0; } /* @@ -421,23 +417,23 @@ SV_HullPointContents ================== */ -static int SV_HullPointContents (int nodenum, vec3_t p) +static int SV_HullPointContents(int nodenum, vec3_t p) { float d; - dclipnode_t *node; - dplane_t *plane; + dclipnode_t* node; + dplane_t* plane; while (nodenum >= 0) { if (nodenum > numclipnodes) { - fprintf(stderr,"SV_HullPointContents: bad node number"); - exit(1) ; + fprintf(stderr, "SV_HullPointContents: bad node number"); + exit(1); } - + node = &dclipnodes[nodenum]; plane = &dplanes[node->planenum]; - d = DotProduct (plane->normal, p) - plane->dist; + d = DotProduct(plane->normal, p) - plane->dist; if (d < 0) nodenum = node->children[1]; else @@ -455,14 +451,14 @@ SV_RecursiveHullCheck ================== */ -qboolean SV_RecursiveHullCheck (int hullNumber, // Id of hull, 1 = human, ... +qboolean SV_RecursiveHullCheck(int hullNumber, // Id of hull, 1 = human, ... int nodenum, // Clip node to analyse from float p1f, float p2f, // Initialized to 0 & 1 vec3_t p1, vec3_t p2, // P1 = start, P2 = end - TraceResult *trace) + TraceResult* trace) { - dclipnode_t *node; - dplane_t *plane; + dclipnode_t* node; + dplane_t* plane; float t1, t2; float frac; int i; @@ -470,12 +466,12 @@ qboolean SV_RecursiveHullCheck (int hullNumber, // Id of hull, 1 = human, ... int side; float midf; - if ((hullNumber < human_hull) || (hullNumber > head_hull)) { - fprintf(stderr,"Wrong hullNumber in SV_RecursiveHullCheck\n") ; - exit(1) ; + if ((hullNumber < human_hull) || (hullNumber > head_hull)) { + fprintf(stderr, "Wrong hullNumber in SV_RecursiveHullCheck\n"); + exit(1); } -// check for empty + // check for empty if (nodenum < 0) { if (nodenum != CONTENTS_SOLID) @@ -492,125 +488,126 @@ qboolean SV_RecursiveHullCheck (int hullNumber, // Id of hull, 1 = human, ... } if (nodenum > numclipnodes) { - fprintf(stderr,"SV_RecursiveHullCheck: bad node number"); - exit(1) ; + fprintf(stderr, "SV_RecursiveHullCheck: bad node number"); + exit(1); } -// -// find the point distances -// + // + // find the point distances + // node = &dclipnodes[nodenum]; if ((node->planenum > numplanes) || (node->planenum < 0)) { - fprintf(stderr,"SV_RecursiveHullCheck: bad plane number"); - exit(1) ; + fprintf(stderr, "SV_RecursiveHullCheck: bad plane number"); + exit(1); } plane = &dplanes[node->planenum]; - t1 = DotProduct (plane->normal, p1) - plane->dist; - t2 = DotProduct (plane->normal, p2) - plane->dist; - + t1 = DotProduct(plane->normal, p1) - plane->dist; + t2 = DotProduct(plane->normal, p2) - plane->dist; + // Check whether P1 & P2 are on the same side of the plane // Then, continue to check in this smaller space if (t1 >= 0 && t2 >= 0) - return SV_RecursiveHullCheck (hullNumber, node->children[0], p1f, p2f, p1, p2, trace); + return SV_RecursiveHullCheck(hullNumber, node->children[0], p1f, p2f, p1, p2, trace); if (t1 < 0 && t2 < 0) - return SV_RecursiveHullCheck (hullNumber, node->children[1], p1f, p2f, p1, p2, trace); + return SV_RecursiveHullCheck(hullNumber, node->children[1], p1f, p2f, p1, p2, trace); // P1 & P2 are on opposite side of the plane... if (t1 == t2) { - fprintf(stderr,"SV_RecursiveHullCheck: same distance from plane"); - exit(1) ; + fprintf(stderr, "SV_RecursiveHullCheck: same distance from plane"); + exit(1); } -// put the crosspoint DIST_EPSILON pixels on the near side + // put the crosspoint DIST_EPSILON pixels on the near side if (t1 < 0.0) - frac = (t1 + DIST_EPSILON)/(t1-t2); + frac = (t1 + DIST_EPSILON) / (t1 - t2); else - frac = (t1 - DIST_EPSILON)/(t1-t2); + frac = (t1 - DIST_EPSILON) / (t1 - t2); if (frac < 0.0) frac = 0.0; if (frac > 1.0) frac = 1.0; - - midf = p1f + (p2f - p1f)*frac; - for (i=0 ; i<3 ; i++) - mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + midf = p1f + (p2f - p1f) * frac; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac * (p2[i] - p1[i]); side = (t1 < 0); -// move up to the node - if (!SV_RecursiveHullCheck (hullNumber, node->children[side], p1f, midf, p1, mid, trace) ) + // move up to the node + if (!SV_RecursiveHullCheck(hullNumber, node->children[side], p1f, midf, p1, mid, trace)) return false; - if (SV_HullPointContents (node->children[side^1], mid) != CONTENTS_SOLID) - // go past the node - return SV_RecursiveHullCheck (hullNumber, node->children[side^1], midf, p2f, mid, p2, trace); - + if (SV_HullPointContents(node->children[side ^ 1], mid) != CONTENTS_SOLID) + // go past the node + return SV_RecursiveHullCheck(hullNumber, node->children[side ^ 1], midf, p2f, mid, p2, trace); + if (trace->fAllSolid) return false; // never got out of the solid area - + //================== // the other side of the node is solid, this is the impact point //================== if (!side) { - VectorCopy (plane->normal, trace->vecPlaneNormal); + VectorCopy(plane->normal, trace->vecPlaneNormal); trace->flPlaneDist = plane->dist; } else { - VectorSubtract (vec3_origin, plane->normal, trace->vecPlaneNormal); + VectorSubtract(vec3_origin, plane->normal, trace->vecPlaneNormal); trace->flPlaneDist = -plane->dist; } // If mid is SOLID, move mid backwards until it is in EMPTY space - while (SV_HullPointContents (dmodels[0].headnode[hullNumber], mid) == CONTENTS_SOLID) + while (SV_HullPointContents(dmodels[0].headnode[hullNumber], mid) == CONTENTS_SOLID) { frac -= 0.1; if (frac < 0.0) // shouldn't really happen, but does occasionally { -// fprintf(stderr,"SV_RecursiveHullCheck(): backup past 0\n"); + // fprintf(stderr,"SV_RecursiveHullCheck(): backup past 0\n"); break; // Exit loop immediately } // Adjust mid to reflect the value of frac - midf = p1f + (p2f - p1f)*frac; - for (i=0 ; i<3 ; i++) - mid[i] = p1[i] + frac*(p2[i] - p1[i]); + midf = p1f + (p2f - p1f) * frac; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac * (p2[i] - p1[i]); } trace->flFraction = midf; - VectorCopy (mid, trace->vecEndPos); + VectorCopy(mid, trace->vecEndPos); return false; } -void UTIL_TraceHull (const Vector & vecStart, const Vector & vecEnd, - IGNORE_MONSTERS igmon, int hullNumber, edict_t * pentIgnore, - TraceResult * ptr) +void UTIL_TraceHull(const Vector& vecStart, const Vector& vecEnd, + IGNORE_MONSTERS igmon, int hullNumber, edict_t* pentIgnore, + TraceResult* ptr) { - vec3_t start, end ; - botman_trace_t tr ; - - start[0]=vecStart.x ; - start[1]=vecStart.y ; - start[2]=vecStart.z ; - end[0]=vecEnd.x ; - end[1]=vecEnd.y ; - end[2]=vecEnd.z ; - memset(&tr,0,sizeof(botman_trace_t)) ; - tr.fraction=1.0 ; + vec3_t start, end; + botman_trace_t tr; + + start[0] = vecStart.x; + start[1] = vecStart.y; + start[2] = vecStart.z; + end[0] = vecEnd.x; + end[1] = vecEnd.y; + end[2] = vecEnd.z; + memset(&tr, 0, sizeof(botman_trace_t)); + tr.fraction = 1.0; if (hullNumber == point_hull) { - BotmanTraceLine(start, end, &tr) ; - ConvertTraceResult(&tr,ptr) ; - } else { - TraceResult EntTrace ; - int ent_index, model_index ; - char * value ; + BotmanTraceLine(start, end, &tr); + ConvertTraceResult(&tr, ptr); + } + else { + TraceResult EntTrace; + int ent_index, model_index; + char* value; // Set the TraceResult default values - memset(ptr,0,sizeof(TraceResult)) ; - ptr->flFraction = 1.0 ; - ptr->fAllSolid = true ; - SV_RecursiveHullCheck(hullNumber, dmodels[0].headnode[hullNumber], + memset(ptr, 0, sizeof(TraceResult)); + ptr->flFraction = 1.0; + ptr->fAllSolid = true; + SV_RecursiveHullCheck(hullNumber, dmodels[0].headnode[hullNumber], 0.0, 1.0, start, end, ptr); // loop through all the entities looking for "func_wall"... @@ -620,18 +617,15 @@ void UTIL_TraceHull (const Vector & vecStart, const Vector & vecEnd, value = ValueForKey(&entities[ent_index], "model"); if (value[0]) { sscanf(value, "*%d", &model_index); - memset(&EntTrace,0,sizeof(TraceResult)) ; - EntTrace.flFraction = 1.0 ; - EntTrace.fAllSolid = true ; + memset(&EntTrace, 0, sizeof(TraceResult)); + EntTrace.flFraction = 1.0; + EntTrace.fAllSolid = true; SV_RecursiveHullCheck(hullNumber, dmodels[model_index].headnode[hullNumber], 0.0, 1.0, start, end, &EntTrace); if (EntTrace.flFraction < ptr->flFraction) - memcpy(ptr,&EntTrace,sizeof(TraceResult)) ; + memcpy(ptr, &EntTrace, sizeof(TraceResult)); } - } - } - return ; -} - + return; +} \ No newline at end of file diff --git a/Bsp2Rbn/trace.h b/Bsp2Rbn/trace.h index f21a049..356e6fd 100644 --- a/Bsp2Rbn/trace.h +++ b/Bsp2Rbn/trace.h @@ -15,7 +15,7 @@ // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License for more details at: // http://www.gnu.org/copyleft/gpl.html @@ -29,18 +29,17 @@ typedef struct { - bool allsolid; /* if true, plane is not valid */ - bool startsolid; /* if true, the initial point was in a solid area */ - float fraction; /* time completed, 1.0 = didn't hit anything */ - vec3_t hitpos; /* surface hit position (in solid) */ - vec3_t endpos; /* final position (not in solid) */ - int contents; /* contents of endpos */ + bool allsolid; /* if true, plane is not valid */ + bool startsolid; /* if true, the initial point was in a solid area */ + float fraction; /* time completed, 1.0 = didn't hit anything */ + vec3_t hitpos; /* surface hit position (in solid) */ + vec3_t endpos; /* final position (not in solid) */ + int contents; /* contents of endpos */ } botman_trace_t; int BotmanPointContents(const int nodenum, const vec3_t coord); -int BotmanPointContentsInHull(const int hullNumber,const vec3_t coord); -void BotmanTraceLine (vec3_t start, vec3_t end, botman_trace_t *trace); -dface_t *TraceLineFindFace(vec3_t start, botman_trace_t *tr); +int BotmanPointContentsInHull(const int hullNumber, const vec3_t coord); +void BotmanTraceLine(vec3_t start, vec3_t end, botman_trace_t* trace); +dface_t* TraceLineFindFace(vec3_t start, botman_trace_t* tr); #endif - diff --git a/Bsp2Rbn/util.cpp b/Bsp2Rbn/util.cpp index 6bc5e45..9a5167d 100644 --- a/Bsp2Rbn/util.cpp +++ b/Bsp2Rbn/util.cpp @@ -21,7 +21,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -33,9 +33,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -49,8 +49,8 @@ #ifdef __linux__ #include #else -char * basename(char *) ; -char * dirname(char *) ; +char* basename(char*); +char* dirname(char*); #endif #include #include @@ -85,475 +85,473 @@ int gmsgShowMenu = 0; #ifndef __linux__ // 21/07/04 Whistler // Handle both \ and / as separator -char * basename(char * s) +char* basename(char* s) { - char * fs ; - - if ((s == NULL) || (*s == 0)) return "." ; - if (strcmp(s,"\\") == 0) return s ; - if (strcmp(s,"/") == 0) return s ; - fs = strrchr(s,'\\') ; - if (fs == NULL) fs = strrchr(s,'/') ; - if (fs == NULL) return s ; - return fs + 1 ; + char* fs; + + if ((s == NULL) || (*s == 0)) return "."; + if (strcmp(s, "\\") == 0) return s; + if (strcmp(s, "/") == 0) return s; + fs = strrchr(s, '\\'); + if (fs == NULL) fs = strrchr(s, '/'); + if (fs == NULL) return s; + return fs + 1; } -char * dirname(char * s) +char* dirname(char* s) { - char * fs ; + char* fs; if ((s == NULL) || (*s == 0)) return "."; - if (strcmp(s,"\\") == 0) return s; - if (strcmp(s,"/") == 0) return s; - fs = strrchr(s,'\\'); - if (fs == NULL) fs = strrchr(s,'/'); + if (strcmp(s, "\\") == 0) return s; + if (strcmp(s, "/") == 0) return s; + fs = strrchr(s, '\\'); + if (fs == NULL) fs = strrchr(s, '/'); if (fs == NULL) return "."; - * fs = 0; - return s ; + *fs = 0; + return s; } #endif -Vector UTIL_VecToAngles (const Vector & vec) +Vector UTIL_VecToAngles(const Vector& vec) { - float rgflVecOut[3]; - VEC_TO_ANGLES (vec, rgflVecOut); - return Vector (rgflVecOut); + float rgflVecOut[3]; + VEC_TO_ANGLES(vec, rgflVecOut); + return Vector(rgflVecOut); } // Overloaded to add IGNORE_GLASS void -UTIL_TraceLine (const Vector & vecStart, const Vector & vecEnd, - IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, - edict_t * pentIgnore, TraceResult * ptr) +UTIL_TraceLine(const Vector& vecStart, const Vector& vecEnd, + IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, + edict_t* pentIgnore, TraceResult* ptr) { - TRACE_LINE (vecStart, vecEnd, - (igmon == - ignore_monsters ? TRUE : FALSE) | (ignoreGlass ? 0x100 : 0), - pentIgnore, ptr); + TRACE_LINE(vecStart, vecEnd, + (igmon == + ignore_monsters ? TRUE : FALSE) | (ignoreGlass ? 0x100 : 0), + pentIgnore, ptr); } void -UTIL_TraceLine (const Vector & vecStart, const Vector & vecEnd, - IGNORE_MONSTERS igmon, edict_t * pentIgnore, - TraceResult * ptr) +UTIL_TraceLine(const Vector& vecStart, const Vector& vecEnd, + IGNORE_MONSTERS igmon, edict_t* pentIgnore, + TraceResult* ptr) { - TRACE_LINE (vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), - pentIgnore, ptr); + TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), + pentIgnore, ptr); } void -UTIL_MakeVectors (const Vector & vecAngles) +UTIL_MakeVectors(const Vector& vecAngles) { - MAKE_VECTORS (vecAngles); + MAKE_VECTORS(vecAngles); } -edict_t * -UTIL_FindEntityInSphere (edict_t * pentStart, const Vector & vecCenter, - float flRadius) +edict_t* +UTIL_FindEntityInSphere(edict_t* pentStart, const Vector& vecCenter, + float flRadius) { - edict_t *pentEntity; + edict_t* pentEntity; - pentEntity = FIND_ENTITY_IN_SPHERE (pentStart, vecCenter, flRadius); + pentEntity = FIND_ENTITY_IN_SPHERE(pentStart, vecCenter, flRadius); - if (!FNullEnt (pentEntity)) - return pentEntity; + if (!FNullEnt(pentEntity)) + return pentEntity; - return NULL; + return NULL; } -edict_t * -UTIL_FindEntityByString (edict_t * pentStart, const char *szKeyword, - const char *szValue) +edict_t* +UTIL_FindEntityByString(edict_t* pentStart, const char* szKeyword, + const char* szValue) { - edict_t *pentEntity; + edict_t* pentEntity; - pentEntity = FIND_ENTITY_BY_STRING (pentStart, szKeyword, szValue); + pentEntity = FIND_ENTITY_BY_STRING(pentStart, szKeyword, szValue); - if (!FNullEnt (pentEntity)) - return pentEntity; - return NULL; + if (!FNullEnt(pentEntity)) + return pentEntity; + return NULL; } -edict_t * -UTIL_FindEntityByClassname (edict_t * pentStart, const char *szName) +edict_t* +UTIL_FindEntityByClassname(edict_t* pentStart, const char* szName) { - return UTIL_FindEntityByString (pentStart, "classname", szName); + return UTIL_FindEntityByString(pentStart, "classname", szName); } -edict_t * -UTIL_FindEntityByTargetname (edict_t * pentStart, const char *szName) +edict_t* +UTIL_FindEntityByTargetname(edict_t* pentStart, const char* szName) { - return UTIL_FindEntityByString (pentStart, "targetname", szName); + return UTIL_FindEntityByString(pentStart, "targetname", szName); } void -UTIL_SetSize (entvars_t * pev, const Vector & vecMin, const Vector & vecMax) +UTIL_SetSize(entvars_t* pev, const Vector& vecMin, const Vector& vecMax) { - SET_SIZE (ENT (pev), vecMin, vecMax); + SET_SIZE(ENT(pev), vecMin, vecMax); } void -UTIL_SetOrigin (entvars_t * pev, const Vector & vecOrigin) +UTIL_SetOrigin(entvars_t* pev, const Vector& vecOrigin) { - SET_ORIGIN (ENT (pev), vecOrigin); + SET_ORIGIN(ENT(pev), vecOrigin); } void -ClientPrint (edict_t * pEntity, int msg_dest, const char *msg_name) +ClientPrint(edict_t* pEntity, int msg_dest, const char* msg_name) { - } void -UTIL_ClientPrintAll (int msg_dest, const char *msg_name, const char *param1, - const char *param2, const char *param3, - const char *param4) +UTIL_ClientPrintAll(int msg_dest, const char* msg_name, const char* param1, + const char* param2, const char* param3, + const char* param4) { } void -UTIL_SayText (const char *pText, edict_t * pEdict) +UTIL_SayText(const char* pText, edict_t* pEdict) { - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG ("SayText", -1); - - MESSAGE_BEGIN (MSG_ONE, gmsgSayText, NULL, pEdict); - WRITE_BYTE (ENTINDEX (pEdict)); - //if (mod_id == FRONTLINE_DLL) - // WRITE_SHORT(0); - WRITE_STRING (pText); - MESSAGE_END (); + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG("SayText", -1); + + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pEdict); + WRITE_BYTE(ENTINDEX(pEdict)); + //if (mod_id == FRONTLINE_DLL) + // WRITE_SHORT(0); + WRITE_STRING(pText); + MESSAGE_END(); } void -UTIL_HostSay (edict_t * pEntity, int teamonly, char *message) +UTIL_HostSay(edict_t* pEntity, int teamonly, char* message) { - int j; - char text[128]; - char *pc; - int sender_team, player_team; - edict_t *client; - - // make sure the text has content - for (pc = message; pc != NULL && *pc != 0; pc++) - { - if (isprint (*pc) && !isspace (*pc)) + int j; + char text[128]; + char* pc; + int sender_team, player_team; + edict_t* client; + + // make sure the text has content + for (pc = message; pc != NULL && *pc != 0; pc++) + { + if (isprint(*pc) && !isspace(*pc)) + { + pc = NULL; // we've found an alphanumeric character, so text is valid + break; + } + } + + if (pc != NULL) + return; // no character found, so say nothing + + // turn on color set 2 (color on, no sound) + if (teamonly) + sprintf(text, "%c(TEAM) %s: ", 2, STRING(pEntity->v.netname)); + else + sprintf(text, "%c%s: ", 2, STRING(pEntity->v.netname)); + + j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator + if ((int)strlen(message) > j) + message[j] = 0; + + strcat(text, message); + strcat(text, "\n"); + + // loop through all players + // Start with the first player. + // This may return the world in single player if the client types something between levels or during spawn + // so check it, or it will infinite loop + + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG("SayText", -1); + + sender_team = UTIL_GetTeam(pEntity); + + client = NULL; + while (((client = UTIL_FindEntityByClassname(client, "player")) != NULL) && + (!FNullEnt(client))) { - pc = NULL; // we've found an alphanumeric character, so text is valid - break; + if (client == pEntity) // skip sender of message + continue; + + player_team = UTIL_GetTeam(client); + + if (teamonly && (sender_team != player_team)) + continue; + + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, client); + WRITE_BYTE(ENTINDEX(pEntity)); + //if (mod_id == FRONTLINE_DLL) + // WRITE_SHORT(0); + WRITE_STRING(text); + MESSAGE_END(); } - } - - if (pc != NULL) - return; // no character found, so say nothing - - // turn on color set 2 (color on, no sound) - if (teamonly) - sprintf (text, "%c(TEAM) %s: ", 2, STRING (pEntity->v.netname)); - else - sprintf (text, "%c%s: ", 2, STRING (pEntity->v.netname)); - - j = sizeof (text) - 2 - strlen (text); // -2 for /n and null terminator - if ((int) strlen (message) > j) - message[j] = 0; - - strcat (text, message); - strcat (text, "\n"); - - // loop through all players - // Start with the first player. - // This may return the world in single player if the client types something between levels or during spawn - // so check it, or it will infinite loop - - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG ("SayText", -1); - - sender_team = UTIL_GetTeam (pEntity); - - client = NULL; - while (((client = UTIL_FindEntityByClassname (client, "player")) != NULL) && - (!FNullEnt (client))) - { - if (client == pEntity) // skip sender of message - continue; - - player_team = UTIL_GetTeam (client); - - if (teamonly && (sender_team != player_team)) - continue; - - MESSAGE_BEGIN (MSG_ONE, gmsgSayText, NULL, client); - WRITE_BYTE (ENTINDEX (pEntity)); - //if (mod_id == FRONTLINE_DLL) - // WRITE_SHORT(0); - WRITE_STRING (text); - MESSAGE_END (); - } - - // print to the sending client - MESSAGE_BEGIN (MSG_ONE, gmsgSayText, NULL, pEntity); - WRITE_BYTE (ENTINDEX (pEntity)); - //if (mod_id == FRONTLINE_DLL) - // WRITE_SHORT(0); - WRITE_STRING (text); - MESSAGE_END (); - - // echo to server console - g_engfuncs.pfnServerPrint (text); + + // print to the sending client + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pEntity); + WRITE_BYTE(ENTINDEX(pEntity)); + //if (mod_id == FRONTLINE_DLL) + // WRITE_SHORT(0); + WRITE_STRING(text); + MESSAGE_END(); + + // echo to server console + g_engfuncs.pfnServerPrint(text); } #ifdef DEBUG -edict_t * -DBG_EntOfVars (const entvars_t * pev) +edict_t* +DBG_EntOfVars(const entvars_t* pev) { - if (pev->pContainingEntity != NULL) - return pev->pContainingEntity; - ALERT (at_console, - "entvars_t pContainingEntity is NULL, calling into engine"); - edict_t *pent = (*g_engfuncs.pfnFindEntityByVars) ((entvars_t *) pev); - if (pent == NULL) - ALERT (at_console, "DAMN! Even the engine couldn't FindEntityByVars!"); - ((entvars_t *) pev)->pContainingEntity = pent; - return pent; + if (pev->pContainingEntity != NULL) + return pev->pContainingEntity; + ALERT(at_console, + "entvars_t pContainingEntity is NULL, calling into engine"); + edict_t* pent = (*g_engfuncs.pfnFindEntityByVars) ((entvars_t*)pev); + if (pent == NULL) + ALERT(at_console, "DAMN! Even the engine couldn't FindEntityByVars!"); + ((entvars_t*)pev)->pContainingEntity = pent; + return pent; } #endif //DEBUG // Is the edict a vip or not? bool -UTIL_IsVip (edict_t * pEntity) +UTIL_IsVip(edict_t* pEntity) { - if (mod_id != CSTRIKE_DLL) - return false; - else - { - char *infobuffer; - char model_name[32]; + if (mod_id != CSTRIKE_DLL) + return false; + else + { + char* infobuffer; + char model_name[32]; - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (pEntity); - strcpy (model_name, (g_engfuncs.pfnInfoKeyValue (infobuffer, "model"))); + infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (pEntity); + strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); - if (strcmp (model_name, "vip") == 0) // VIP - return true; - } + if (strcmp(model_name, "vip") == 0) // VIP + return true; + } - return false; + return false; } // return team number 0 through 3 based what MOD uses for team numbers int -UTIL_GetTeam (edict_t * pEntity) +UTIL_GetTeam(edict_t* pEntity) { - if (mod_id == CSTRIKE_DLL) - { - char *infobuffer; - char model_name[32]; - - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (pEntity); - strcpy (model_name, (g_engfuncs.pfnInfoKeyValue (infobuffer, "model"))); - - if ((strcmp (model_name, "terror") == 0) || // Phoenix Connektion - (strcmp (model_name, "arab") == 0) || // old L337 Krew - (strcmp (model_name, "leet") == 0) || // L337 Krew - (strcmp (model_name, "artic") == 0) || // Artic Avenger - (strcmp (model_name, "arctic") == 0) || // Artic Avenger - fix for arctic? - seemed a typo? - (strcmp (model_name, "guerilla") == 0)) // Gorilla Warfare - { - return 0; - } - else if ((strcmp (model_name, "urban") == 0) || // Seal Team 6 - (strcmp (model_name, "gsg9") == 0) || // German GSG-9 - (strcmp (model_name, "sas") == 0) || // UK SAS - (strcmp (model_name, "gign") == 0) || // French GIGN - (strcmp (model_name, "vip") == 0) || // VIP - (strcmp (model_name, "spetsnatz") == 0)) // CZ Spetsnatz + if (mod_id == CSTRIKE_DLL) { - return 1; + char* infobuffer; + char model_name[32]; + + infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (pEntity); + strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); + + if ((strcmp(model_name, "terror") == 0) || // Phoenix Connektion + (strcmp(model_name, "arab") == 0) || // old L337 Krew + (strcmp(model_name, "leet") == 0) || // L337 Krew + (strcmp(model_name, "artic") == 0) || // Artic Avenger + (strcmp(model_name, "arctic") == 0) || // Artic Avenger - fix for arctic? - seemed a typo? + (strcmp(model_name, "guerilla") == 0)) // Gorilla Warfare + { + return 0; + } + else if ((strcmp(model_name, "urban") == 0) || // Seal Team 6 + (strcmp(model_name, "gsg9") == 0) || // German GSG-9 + (strcmp(model_name, "sas") == 0) || // UK SAS + (strcmp(model_name, "gign") == 0) || // French GIGN + (strcmp(model_name, "vip") == 0) || // VIP + (strcmp(model_name, "spetsnatz") == 0)) // CZ Spetsnatz + { + return 1; + } + return -1; // return zero if team is unknown } - return -1; // return zero if team is unknown - } - return 0; + return 0; } // return class number 0 through N int -UTIL_GetClass (edict_t * pEntity) +UTIL_GetClass(edict_t* pEntity) { - char *infobuffer; - char model_name[32]; + char* infobuffer; + char model_name[32]; - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (pEntity); - strcpy (model_name, (g_engfuncs.pfnInfoKeyValue (infobuffer, "model"))); + infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (pEntity); + strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); - return 0; + return 0; } int -UTIL_GetBotIndex (edict_t * pEdict) +UTIL_GetBotIndex(edict_t* pEdict) { - int index; + int index; - for (index = 0; index < 32; index++) - { - if (bots[index].pEdict == pEdict) + for (index = 0; index < 32; index++) { - return index; + if (bots[index].pEdict == pEdict) + { + return index; + } } - } - return -1; // return -1 if edict is not a bot + return -1; // return -1 if edict is not a bot } -cBot * -UTIL_GetBotPointer (edict_t * pEdict) +cBot* +UTIL_GetBotPointer(edict_t* pEdict) { - int index; + int index; - for (index = 0; index < 32; index++) - { - if (bots[index].pEdict == pEdict) - { - break; - } - } + for (index = 0; index < 32; index++) + { + if (bots[index].pEdict == pEdict) + { + break; + } + } - if (index < 32) - return (&bots[index]); + if (index < 32) + return (&bots[index]); - return NULL; // return NULL if edict is not a bot + return NULL; // return NULL if edict is not a bot } -bool IsAlive (edict_t * pEdict) { - // FIX: Make sure the edict is valid and such, else return false: - return ((pEdict != NULL) && // VALID - (pEdict->v.deadflag == DEAD_NO) && // NOT DEAD - (pEdict->v.health > 0) && // ENOUGHT HEALTH - !(pEdict->v.flags & FL_NOTARGET) && // ? - (pEdict->v.takedamage != 0)); // CAN TAKE DAMAGE +bool IsAlive(edict_t* pEdict) { + // FIX: Make sure the edict is valid and such, else return false: + return ((pEdict != NULL) && // VALID + (pEdict->v.deadflag == DEAD_NO) && // NOT DEAD + (pEdict->v.health > 0) && // ENOUGHT HEALTH + !(pEdict->v.flags & FL_NOTARGET) && // ? + (pEdict->v.takedamage != 0)); // CAN TAKE DAMAGE } bool -FInViewCone (Vector * pOrigin, edict_t * pEdict) +FInViewCone(Vector* pOrigin, edict_t* pEdict) { #ifdef EVYISWRONG -return TRUE ; + return TRUE; #endif - Vector2D vec2LOS; - float flDot; + Vector2D vec2LOS; + float flDot; - UTIL_MakeVectors (pEdict->v.angles); + UTIL_MakeVectors(pEdict->v.angles); - vec2LOS = (*pOrigin - pEdict->v.origin).Make2D (); - vec2LOS = vec2LOS.Normalize (); + vec2LOS = (*pOrigin - pEdict->v.origin).Make2D(); + vec2LOS = vec2LOS.Normalize(); - flDot = DotProduct (vec2LOS, gpGlobals->v_forward.Make2D ()); + flDot = DotProduct(vec2LOS, gpGlobals->v_forward.Make2D()); - if (flDot > 0.50) // 60 degree field of view - { - return TRUE; - } - else - { - return FALSE; - } + if (flDot > 0.50) // 60 degree field of view + { + return TRUE; + } + else + { + return FALSE; + } } // FVisible() bool -FVisible (const Vector & vecOrigin, edict_t * pEdict) +FVisible(const Vector& vecOrigin, edict_t* pEdict) { #ifdef EVYISWRONG -return TRUE ; + return TRUE; #endif - TraceResult tr; - Vector vecLookerOrigin; - - // look through caller's eyes - vecLookerOrigin = pEdict->v.origin + pEdict->v.view_ofs; - - int bInWater = (UTIL_PointContents (vecOrigin) == CONTENTS_WATER); - int bLookerInWater = - (UTIL_PointContents (vecLookerOrigin) == CONTENTS_WATER); - - // don't look through water - if (bInWater != bLookerInWater) - return FALSE; - - UTIL_TraceLine (vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, - pEdict, &tr); - - if (tr.flFraction != 1.0) - { - return FALSE; // Line of sight is not established - } - else - { - return TRUE; // line of sight is valid. - } + TraceResult tr; + Vector vecLookerOrigin; + + // look through caller's eyes + vecLookerOrigin = pEdict->v.origin + pEdict->v.view_ofs; + + int bInWater = (UTIL_PointContents(vecOrigin) == CONTENTS_WATER); + int bLookerInWater = + (UTIL_PointContents(vecLookerOrigin) == CONTENTS_WATER); + + // don't look through water + if (bInWater != bLookerInWater) + return FALSE; + + UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, + pEdict, &tr); + + if (tr.flFraction != 1.0) + { + return FALSE; // Line of sight is not established + } + else + { + return TRUE; // line of sight is valid. + } } Vector -GetGunPosition (edict_t * pEdict) +GetGunPosition(edict_t* pEdict) { - return (pEdict->v.origin + pEdict->v.view_ofs); + return (pEdict->v.origin + pEdict->v.view_ofs); } void -UTIL_SelectItem (edict_t * pEdict, char *item_name) +UTIL_SelectItem(edict_t* pEdict, char* item_name) { - /*BotDebug( item_name); */ - FakeClientCommand (pEdict, item_name, NULL, NULL); + /*BotDebug( item_name); */ + FakeClientCommand(pEdict, item_name, NULL, NULL); } Vector -VecBModelOrigin (edict_t * pEdict) +VecBModelOrigin(edict_t* pEdict) { - return pEdict->v.absmin + (pEdict->v.size * 0.5); + return pEdict->v.absmin + (pEdict->v.size * 0.5); } void -UTIL_ShowMenu (edict_t * pEdict, int slots, int displaytime, bool needmore, - char *pText) +UTIL_ShowMenu(edict_t* pEdict, int slots, int displaytime, bool needmore, + char* pText) { - if (gmsgShowMenu == 0) - gmsgShowMenu = REG_USER_MSG ("ShowMenu", -1); + if (gmsgShowMenu == 0) + gmsgShowMenu = REG_USER_MSG("ShowMenu", -1); - MESSAGE_BEGIN (MSG_ONE, gmsgShowMenu, NULL, pEdict); + MESSAGE_BEGIN(MSG_ONE, gmsgShowMenu, NULL, pEdict); - WRITE_SHORT (slots); - WRITE_CHAR (displaytime); - WRITE_BYTE (needmore); - WRITE_STRING (pText); + WRITE_SHORT(slots); + WRITE_CHAR(displaytime); + WRITE_BYTE(needmore); + WRITE_STRING(pText); - MESSAGE_END (); + MESSAGE_END(); } void -UTIL_BuildFileName (char *filename, char *arg1, char *arg2) +UTIL_BuildFileName(char* filename, char* arg1, char* arg2) { + if (mod_id == VALVE_DLL) + strcpy(filename, "valve/"); + + else if (mod_id == CSTRIKE_DLL) + strcpy(filename, "cstrike/"); - if (mod_id == VALVE_DLL) - strcpy (filename, "valve/"); - - else if (mod_id == CSTRIKE_DLL) - strcpy (filename, "cstrike/"); - - else - { - filename[0] = 0; - return; - } - - if ((arg1) && (*arg1) && (arg2) && (*arg2)) - { - strcat (filename, arg1); - strcat (filename, "/"); - strcat (filename, arg2); - } - else if ((arg1) && (*arg1)) - { - strcat (filename, arg1); - } + else + { + filename[0] = 0; + return; + } + + if ((arg1) && (*arg1) && (arg2) && (*arg2)) + { + strcat(filename, arg1); + strcat(filename, "/"); + strcat(filename, arg2); + } + else if ((arg1) && (*arg1)) + { + strcat(filename, arg1); + } } // added by Tub @@ -561,14 +559,14 @@ UTIL_BuildFileName (char *filename, char *arg1, char *arg2) // Heavily modified in the standalone version void -UTIL_BuildFileNameRB (char *subdir, char *filename) +UTIL_BuildFileNameRB(char* subdir, char* filename) { - char * temp, *temp2 ; + char* temp, * temp2; - temp=strdup(subdir); - temp2=basename(temp) ; - strcpy(filename,temp2) ; - free(temp) ; + temp = strdup(subdir); + temp2 = basename(temp); + strcpy(filename, temp2); + free(temp); } //========================================================= @@ -576,443 +574,438 @@ UTIL_BuildFileNameRB (char *subdir, char *filename) // Preceded by LOG: ( timestamp ) < message > //========================================================= void -UTIL_LogPrintf (char *fmt, ...) +UTIL_LogPrintf(char* fmt, ...) { - va_list argptr; - static char string[1024]; + va_list argptr; + static char string[1024]; - va_start (argptr, fmt); - vsprintf (string, fmt, argptr); - va_end (argptr); + va_start(argptr, fmt); + vsprintf(string, fmt, argptr); + va_end(argptr); - // Print to server console - ALERT (at_logged, "%s", string); + // Print to server console + ALERT(at_logged, "%s", string); } void -UTIL_BotPressKey (cBot * pBot, int type) { - if (type == IN_JUMP || type == IN_DUCK) - if (pBot->f_freeze_time > gpGlobals->time) - return; // do nothing when in freezetime +UTIL_BotPressKey(cBot* pBot, int type) { + if (type == IN_JUMP || type == IN_DUCK) + if (pBot->f_freeze_time > gpGlobals->time) + return; // do nothing when in freezetime - if (type == IN_JUMP) - if (pBot->f_may_jump_time > gpGlobals->time) - return; // do nothing when we may not jump + if (type == IN_JUMP) + if (pBot->f_may_jump_time > gpGlobals->time) + return; // do nothing when we may not jump - if (type == IN_JUMP && pBot->f_camp_time > gpGlobals->time) - return; // Do not jump when camping. + if (type == IN_JUMP && pBot->f_camp_time > gpGlobals->time) + return; // Do not jump when camping. - // don't jump from ladder - if (FUNC_IsOnLadder (pBot->pEdict) && type == IN_JUMP) - return; + // don't jump from ladder + if (FUNC_IsOnLadder(pBot->pEdict) && type == IN_JUMP) + return; - // KEY: Reload - if (type == IN_RELOAD) // when reloading, there is NO zooming (when holding a zoomable weapon or a sniper gun) - { - if (FUNC_BotHoldsZoomWeapon (pBot) - || UTIL_GiveWeaponType (pBot->current_weapon.iId) == SNIPER) + // KEY: Reload + if (type == IN_RELOAD) // when reloading, there is NO zooming (when holding a zoomable weapon or a sniper gun) + { + if (FUNC_BotHoldsZoomWeapon(pBot) + || UTIL_GiveWeaponType(pBot->current_weapon.iId) == SNIPER) - //pBot->zoomed = ZOOM_NONE; // not zoomed anymore + //pBot->zoomed = ZOOM_NONE; // not zoomed anymore - // FIX: Do not let bots do anything with this weapon for 0.7 second. So the engine can - // update the information. - pBot->f_update_weapon_time = gpGlobals->time + 0.7; - } + // FIX: Do not let bots do anything with this weapon for 0.7 second. So the engine can + // update the information. + pBot->f_update_weapon_time = gpGlobals->time + 0.7; + } - // KEY: End - pBot->pEdict->v.button |= type; + // KEY: End + pBot->pEdict->v.button |= type; - if (type == IN_JUMP) - { - if (pBot->f_hold_duck < gpGlobals->time) - pBot->f_hold_duck = gpGlobals->time + 0.35; + if (type == IN_JUMP) + { + if (pBot->f_hold_duck < gpGlobals->time) + pBot->f_hold_duck = gpGlobals->time + 0.35; - pBot->f_may_jump_time = gpGlobals->time + 0.3; - } + pBot->f_may_jump_time = gpGlobals->time + 0.3; + } } int -UTIL_GiveWeaponType (int weapon_id) +UTIL_GiveWeaponType(int weapon_id) { - int kind = NONE; - - // Check 1. Is it a knife? - if (weapon_id == CS_WEAPON_KNIFE) - kind = KNIFE; - - // Check 2, is it a 'tool'? - if (weapon_id == CS_WEAPON_FLASHBANG || - weapon_id == CS_WEAPON_HEGRENADE || weapon_id == CS_WEAPON_SMOKEGRENADE) - kind = GRENADE; - - // Check 3, is it a secondary gun? - if (weapon_id == CS_WEAPON_P228 || - weapon_id == CS_WEAPON_ELITE || - weapon_id == CS_WEAPON_UMP45 || - weapon_id == CS_WEAPON_USP || - weapon_id == CS_WEAPON_GLOCK18 || - weapon_id == CS_WEAPON_DEAGLE || weapon_id == CS_WEAPON_FIVESEVEN) - kind = SECONDARY; - - // Check 4, is it a sniper gun? - if (weapon_id == CS_WEAPON_SCOUT || - weapon_id == CS_WEAPON_SG550 || - weapon_id == CS_WEAPON_AWP || weapon_id == CS_WEAPON_G3SG1) - kind = SNIPER; - - // When the kind of weapon is still not found, its a primary (in CS) - if (kind == NONE) - kind = PRIMARY; - - if (weapon_id < 1) - kind = NONE; - - return kind; + int kind = NONE; + + // Check 1. Is it a knife? + if (weapon_id == CS_WEAPON_KNIFE) + kind = KNIFE; + + // Check 2, is it a 'tool'? + if (weapon_id == CS_WEAPON_FLASHBANG || + weapon_id == CS_WEAPON_HEGRENADE || weapon_id == CS_WEAPON_SMOKEGRENADE) + kind = GRENADE; + + // Check 3, is it a secondary gun? + if (weapon_id == CS_WEAPON_P228 || + weapon_id == CS_WEAPON_ELITE || + weapon_id == CS_WEAPON_UMP45 || + weapon_id == CS_WEAPON_USP || + weapon_id == CS_WEAPON_GLOCK18 || + weapon_id == CS_WEAPON_DEAGLE || weapon_id == CS_WEAPON_FIVESEVEN) + kind = SECONDARY; + + // Check 4, is it a sniper gun? + if (weapon_id == CS_WEAPON_SCOUT || + weapon_id == CS_WEAPON_SG550 || + weapon_id == CS_WEAPON_AWP || weapon_id == CS_WEAPON_G3SG1) + kind = SNIPER; + + // When the kind of weapon is still not found, its a primary (in CS) + if (kind == NONE) + kind = PRIMARY; + + if (weapon_id < 1) + kind = NONE; + + return kind; } // Return weapon ID (depended on mod) int -UTIL_GiveWeaponId (char *name) +UTIL_GiveWeaponId(char* name) { - if (mod_id == CSTRIKE_DLL) - { - if (strcmp (name, "weapon_knife") == 0) - return CS_WEAPON_KNIFE; - - if (strcmp (name, "weapon_c4") == 0) - return CS_WEAPON_C4; - if (strcmp (name, "weapon_mp5navy") == 0) - return CS_WEAPON_MP5NAVY; - if (strcmp (name, "weapon_ak47") == 0) - return CS_WEAPON_AK47; - if (strcmp (name, "weapon_m3") == 0) - return CS_WEAPON_M3; - if (strcmp (name, "weapon_aug") == 0) - return CS_WEAPON_AUG; - if (strcmp (name, "weapon_sg552") == 0) - return CS_WEAPON_SG552; - if (strcmp (name, "weapon_m249") == 0) - return CS_WEAPON_M249; - if (strcmp (name, "weapon_xm1014") == 0) - return CS_WEAPON_XM1014; - if (strcmp (name, "weapon_p90") == 0) - return CS_WEAPON_P90; - if (strcmp (name, "weapon_tmp") == 0) - return CS_WEAPON_TMP; - if (strcmp (name, "weapon_m4a1") == 0) - return CS_WEAPON_M4A1; - if (strcmp (name, "weapon_awp") == 0) - return CS_WEAPON_AWP; - if (strcmp (name, "weapon_fiveseven") == 0) - return CS_WEAPON_FIVESEVEN; - if (strcmp (name, "weapon_ump45") == 0) - return CS_WEAPON_UMP45; - if (strcmp (name, "weapon_sg550") == 0) - return CS_WEAPON_SG550; - if (strcmp (name, "weapon_scout") == 0) - return CS_WEAPON_SCOUT; - if (strcmp (name, "weapon_mac10") == 0) - return CS_WEAPON_MAC10; - if (strcmp (name, "weapon_g3sg1") == 0) - return CS_WEAPON_G3SG1; - if (strcmp (name, "weapon_elite") == 0) - return CS_WEAPON_ELITE; - if (strcmp (name, "weapon_p228") == 0) - return CS_WEAPON_P228; - if (strcmp (name, "weapon_deagle") == 0) - return CS_WEAPON_DEAGLE; - if (strcmp (name, "weapon_usp") == 0) - return CS_WEAPON_USP; - if (strcmp (name, "weapon_glock18") == 0) - return CS_WEAPON_GLOCK18; - // Counter-Strike 1.6 - if (strcmp (name, "weapon_famas") == 0) - return CS_WEAPON_FAMAS; - if (strcmp (name, "weapon_galil") == 0) - return CS_WEAPON_GALIL; - - // TODO: Detect shield carrying. - - } - - return -1; + if (mod_id == CSTRIKE_DLL) + { + if (strcmp(name, "weapon_knife") == 0) + return CS_WEAPON_KNIFE; + + if (strcmp(name, "weapon_c4") == 0) + return CS_WEAPON_C4; + if (strcmp(name, "weapon_mp5navy") == 0) + return CS_WEAPON_MP5NAVY; + if (strcmp(name, "weapon_ak47") == 0) + return CS_WEAPON_AK47; + if (strcmp(name, "weapon_m3") == 0) + return CS_WEAPON_M3; + if (strcmp(name, "weapon_aug") == 0) + return CS_WEAPON_AUG; + if (strcmp(name, "weapon_sg552") == 0) + return CS_WEAPON_SG552; + if (strcmp(name, "weapon_m249") == 0) + return CS_WEAPON_M249; + if (strcmp(name, "weapon_xm1014") == 0) + return CS_WEAPON_XM1014; + if (strcmp(name, "weapon_p90") == 0) + return CS_WEAPON_P90; + if (strcmp(name, "weapon_tmp") == 0) + return CS_WEAPON_TMP; + if (strcmp(name, "weapon_m4a1") == 0) + return CS_WEAPON_M4A1; + if (strcmp(name, "weapon_awp") == 0) + return CS_WEAPON_AWP; + if (strcmp(name, "weapon_fiveseven") == 0) + return CS_WEAPON_FIVESEVEN; + if (strcmp(name, "weapon_ump45") == 0) + return CS_WEAPON_UMP45; + if (strcmp(name, "weapon_sg550") == 0) + return CS_WEAPON_SG550; + if (strcmp(name, "weapon_scout") == 0) + return CS_WEAPON_SCOUT; + if (strcmp(name, "weapon_mac10") == 0) + return CS_WEAPON_MAC10; + if (strcmp(name, "weapon_g3sg1") == 0) + return CS_WEAPON_G3SG1; + if (strcmp(name, "weapon_elite") == 0) + return CS_WEAPON_ELITE; + if (strcmp(name, "weapon_p228") == 0) + return CS_WEAPON_P228; + if (strcmp(name, "weapon_deagle") == 0) + return CS_WEAPON_DEAGLE; + if (strcmp(name, "weapon_usp") == 0) + return CS_WEAPON_USP; + if (strcmp(name, "weapon_glock18") == 0) + return CS_WEAPON_GLOCK18; + // Counter-Strike 1.6 + if (strcmp(name, "weapon_famas") == 0) + return CS_WEAPON_FAMAS; + if (strcmp(name, "weapon_galil") == 0) + return CS_WEAPON_GALIL; + + // TODO: Detect shield carrying. + } + + return -1; } // Return weapon ID (depended on mod) -char * -UTIL_GiveWeaponName (int id) +char* +UTIL_GiveWeaponName(int id) { - if (mod_id == CSTRIKE_DLL) - { - if (id == CS_WEAPON_C4) - return "weapon_c4"; - if (id == CS_WEAPON_MP5NAVY) - return "weapon_mp5navy"; - if (id == CS_WEAPON_AK47) - return "weapon_ak47"; - if (id == CS_WEAPON_M3) - return "weapon_m3"; - if (id == CS_WEAPON_AUG) - return "weapon_aug"; - if (id == CS_WEAPON_SG552) - return "weapon_sg552"; - if (id == CS_WEAPON_M249) - return "weapon_m249"; - if (id == CS_WEAPON_XM1014) - return "weapon_xm1014"; - if (id == CS_WEAPON_P90) - return "weapon_p90"; - if (id == CS_WEAPON_TMP) - return "weapon_tmp"; - if (id == CS_WEAPON_M4A1) - return "weapon_m4a1"; - if (id == CS_WEAPON_AWP) - return "weapon_awp"; - if (id == CS_WEAPON_FIVESEVEN) - return "weapon_fiveseven"; - if (id == CS_WEAPON_UMP45) - return "weapon_ump45"; - if (id == CS_WEAPON_SG550) - return "weapon_ag550"; - if (id == CS_WEAPON_SCOUT) - return "weapon_scout"; - if (id == CS_WEAPON_MAC10) - return "weapon_mac10"; - if (id == CS_WEAPON_G3SG1) - return "weapon_g3sg1"; - if (id == CS_WEAPON_ELITE) - return "weapon_elite"; - if (id == CS_WEAPON_P228) - return "weapon_p228"; - if (id == CS_WEAPON_DEAGLE) - return "weapon_deagle"; - if (id == CS_WEAPON_USP) - return "weapon_usp"; - if (id == CS_WEAPON_GLOCK18) - return "weapon_glock18"; - - // Counter-Strike 1.6 - if (id == CS_WEAPON_FAMAS) - return "weapon_famas"; - if (id == CS_WEAPON_GALIL) - return "weapon_galil"; - - // Unconfirmed shield - if (id == CS_WEAPON_SHIELD) - return "weapon_shield"; - - } - - return "weapon_knife"; // return knife, always good ;) + if (mod_id == CSTRIKE_DLL) + { + if (id == CS_WEAPON_C4) + return "weapon_c4"; + if (id == CS_WEAPON_MP5NAVY) + return "weapon_mp5navy"; + if (id == CS_WEAPON_AK47) + return "weapon_ak47"; + if (id == CS_WEAPON_M3) + return "weapon_m3"; + if (id == CS_WEAPON_AUG) + return "weapon_aug"; + if (id == CS_WEAPON_SG552) + return "weapon_sg552"; + if (id == CS_WEAPON_M249) + return "weapon_m249"; + if (id == CS_WEAPON_XM1014) + return "weapon_xm1014"; + if (id == CS_WEAPON_P90) + return "weapon_p90"; + if (id == CS_WEAPON_TMP) + return "weapon_tmp"; + if (id == CS_WEAPON_M4A1) + return "weapon_m4a1"; + if (id == CS_WEAPON_AWP) + return "weapon_awp"; + if (id == CS_WEAPON_FIVESEVEN) + return "weapon_fiveseven"; + if (id == CS_WEAPON_UMP45) + return "weapon_ump45"; + if (id == CS_WEAPON_SG550) + return "weapon_ag550"; + if (id == CS_WEAPON_SCOUT) + return "weapon_scout"; + if (id == CS_WEAPON_MAC10) + return "weapon_mac10"; + if (id == CS_WEAPON_G3SG1) + return "weapon_g3sg1"; + if (id == CS_WEAPON_ELITE) + return "weapon_elite"; + if (id == CS_WEAPON_P228) + return "weapon_p228"; + if (id == CS_WEAPON_DEAGLE) + return "weapon_deagle"; + if (id == CS_WEAPON_USP) + return "weapon_usp"; + if (id == CS_WEAPON_GLOCK18) + return "weapon_glock18"; + + // Counter-Strike 1.6 + if (id == CS_WEAPON_FAMAS) + return "weapon_famas"; + if (id == CS_WEAPON_GALIL) + return "weapon_galil"; + + // Unconfirmed shield + if (id == CS_WEAPON_SHIELD) + return "weapon_shield"; + } + + return "weapon_knife"; // return knife, always good ;) } // Thanks Botman for this code (from forum). void -UTIL_BotSprayLogo (edict_t * pEntity, char *logo_name) +UTIL_BotSprayLogo(edict_t* pEntity, char* logo_name) { - int index; - TraceResult pTrace; - Vector v_src, v_dest; - UTIL_MakeVectors (pEntity->v.v_angle); - v_src = pEntity->v.origin + pEntity->v.view_ofs; - v_dest = v_src + gpGlobals->v_forward * 80; - UTIL_TraceLine (v_src, v_dest, ignore_monsters, - pEntity->v.pContainingEntity, &pTrace); + int index; + TraceResult pTrace; + Vector v_src, v_dest; + UTIL_MakeVectors(pEntity->v.v_angle); + v_src = pEntity->v.origin + pEntity->v.view_ofs; + v_dest = v_src + gpGlobals->v_forward * 80; + UTIL_TraceLine(v_src, v_dest, ignore_monsters, + pEntity->v.pContainingEntity, &pTrace); - index = DECAL_INDEX (logo_name); + index = DECAL_INDEX(logo_name); - if (index < 0) - return; + if (index < 0) + return; - if ((pTrace.pHit) && (pTrace.flFraction < 1.0)) - { - if (pTrace.pHit->v.solid != SOLID_BSP) - return; + if ((pTrace.pHit) && (pTrace.flFraction < 1.0)) + { + if (pTrace.pHit->v.solid != SOLID_BSP) + return; - MESSAGE_BEGIN (MSG_BROADCAST, SVC_TEMPENTITY); + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); - if (index > 255) - { - WRITE_BYTE (TE_WORLDDECALHIGH); - index -= 256; - } - else - WRITE_BYTE (TE_WORLDDECAL); + if (index > 255) + { + WRITE_BYTE(TE_WORLDDECALHIGH); + index -= 256; + } + else + WRITE_BYTE(TE_WORLDDECAL); - WRITE_COORD (pTrace.vecEndPos.x); - WRITE_COORD (pTrace.vecEndPos.y); - WRITE_COORD (pTrace.vecEndPos.z); - WRITE_BYTE (index); + WRITE_COORD(pTrace.vecEndPos.x); + WRITE_COORD(pTrace.vecEndPos.y); + WRITE_COORD(pTrace.vecEndPos.z); + WRITE_BYTE(index); - MESSAGE_END (); + MESSAGE_END(); - EMIT_SOUND_DYN2 (pEntity, CHAN_VOICE, "player/sprayer.wav", 1.0, - ATTN_NORM, 0, 100); - } + EMIT_SOUND_DYN2(pEntity, CHAN_VOICE, "player/sprayer.wav", 1.0, + ATTN_NORM, 0, 100); + } } // Give a radio message botty boy! void -UTIL_BotRadioMessage (cBot * pBot, int radio, char *arg1, char *arg2) +UTIL_BotRadioMessage(cBot* pBot, int radio, char* arg1, char* arg2) { - // To be sure the console will only change when we MAY change. - // The values will only be changed when console_nr is 0 - if (pBot->console_nr == 0) - { - switch (radio) - { - case 1: - strcpy (pBot->arg1, "radio1"); - break; - case 2: - strcpy (pBot->arg1, "radio2"); - break; - case 3: - strcpy (pBot->arg1, "radio3"); - break; - } - - strcpy (pBot->arg2, arg1); - strcpy (pBot->arg3, arg2); - pBot->console_nr = 1; // Begin message - int iExtra = (100/pBot->ipCreateRadio); - if (iExtra > 30) iExtra=30; - pBot->fDoRadio = gpGlobals->time + iExtra; - } + // To be sure the console will only change when we MAY change. + // The values will only be changed when console_nr is 0 + if (pBot->console_nr == 0) + { + switch (radio) + { + case 1: + strcpy(pBot->arg1, "radio1"); + break; + case 2: + strcpy(pBot->arg1, "radio2"); + break; + case 3: + strcpy(pBot->arg1, "radio3"); + break; + } + + strcpy(pBot->arg2, arg1); + strcpy(pBot->arg3, arg2); + pBot->console_nr = 1; // Begin message + int iExtra = (100 / pBot->ipCreateRadio); + if (iExtra > 30) iExtra = 30; + pBot->fDoRadio = gpGlobals->time + iExtra; + } } ////////////////////////////////// // UTIL_getGrenadeType function // - Stefan ////////////////////////////////// int -UTIL_GetGrenadeType (edict_t * pEntity) +UTIL_GetGrenadeType(edict_t* pEntity) { + char model_name[32]; - char model_name[32]; - - strcpy (model_name, STRING (pEntity->v.model)); + strcpy(model_name, STRING(pEntity->v.model)); - if (strcmp (model_name, "models/w_hegrenade.mdl") == 0) - return 1; // He grenade - if (strcmp (model_name, "models/w_flashbang.mdl") == 0) - return 2; // FlashBang - if (strcmp (model_name, "models/w_smokegrenade.mdl") == 0) - return 3; // SmokeGrenade - if (strcmp (model_name, "models/w_c4.mdl") == 0) - return 4; // C4 Explosive - - return 0; + if (strcmp(model_name, "models/w_hegrenade.mdl") == 0) + return 1; // He grenade + if (strcmp(model_name, "models/w_flashbang.mdl") == 0) + return 2; // FlashBang + if (strcmp(model_name, "models/w_smokegrenade.mdl") == 0) + return 3; // SmokeGrenade + if (strcmp(model_name, "models/w_c4.mdl") == 0) + return 4; // C4 Explosive + return 0; } // 2 functions from podbot source unsigned short -FixedUnsigned16 (float value, float scale) +FixedUnsigned16(float value, float scale) { - int output; + int output; - output = value * scale; - if (output < 0) - output = 0; - if (output > 0xFFFF) - output = 0xFFFF; + output = value * scale; + if (output < 0) + output = 0; + if (output > 0xFFFF) + output = 0xFFFF; - return (unsigned short) output; + return (unsigned short)output; } short -FixedSigned16 (float value, float scale) +FixedSigned16(float value, float scale) { - int output; + int output; - output = value * scale; + output = value * scale; - if (output > 32767) - output = 32767; + if (output > 32767) + output = 32767; - if (output < -32768) - output = -32768; + if (output < -32768) + output = -32768; - return (short) output; + return (short)output; } // Using POD/SDK source to print nice messages on the client machine void -HUD_DrawString (int r, int g, int b, char *msg, edict_t * edict) +HUD_DrawString(int r, int g, int b, char* msg, edict_t* edict) { - // FROM PODBOT SOURCE - // Hacked together Version of HUD_DrawString - MESSAGE_BEGIN (MSG_ONE, SVC_TEMPENTITY, NULL, edict); - WRITE_BYTE (TE_TEXTMESSAGE); - WRITE_BYTE (1); - WRITE_SHORT (FixedSigned16 (-1, 1 << 13)); - WRITE_SHORT (FixedSigned16 (0, 1 << 13)); - WRITE_BYTE (2); - WRITE_BYTE (r); //r - WRITE_BYTE (g); //g - WRITE_BYTE (b); //b - WRITE_BYTE (0); - WRITE_BYTE (255); - WRITE_BYTE (255); - WRITE_BYTE (255); - WRITE_BYTE (200); - WRITE_SHORT (FixedUnsigned16 (0.0078125, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (2, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (6, 1 << 8)); - WRITE_SHORT (FixedUnsigned16 (0.1, 1 << 8)); - WRITE_STRING ((const char *) &msg[0]); - MESSAGE_END (); + // FROM PODBOT SOURCE + // Hacked together Version of HUD_DrawString + MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, edict); + WRITE_BYTE(TE_TEXTMESSAGE); + WRITE_BYTE(1); + WRITE_SHORT(FixedSigned16(-1, 1 << 13)); + WRITE_SHORT(FixedSigned16(0, 1 << 13)); + WRITE_BYTE(2); + WRITE_BYTE(r); //r + WRITE_BYTE(g); //g + WRITE_BYTE(b); //b + WRITE_BYTE(0); + WRITE_BYTE(255); + WRITE_BYTE(255); + WRITE_BYTE(255); + WRITE_BYTE(200); + WRITE_SHORT(FixedUnsigned16(0.0078125, 1 << 8)); + WRITE_SHORT(FixedUnsigned16(2, 1 << 8)); + WRITE_SHORT(FixedUnsigned16(6, 1 << 8)); + WRITE_SHORT(FixedUnsigned16(0.1, 1 << 8)); + WRITE_STRING((const char*)&msg[0]); + MESSAGE_END(); } - void -UTIL_FixAngles (Vector * Angles) +UTIL_FixAngles(Vector* Angles) { - if (Angles->x > 180.0) - Angles->x -= 360.0; - if (Angles->x < -180.0) - Angles->x += 360.0; - if (Angles->y > 180.0) - Angles->y -= 360.0; - if (Angles->y < -180.0) - Angles->y += 360.0; - - Angles->z = 0.0; + if (Angles->x > 180.0) + Angles->x -= 360.0; + if (Angles->x < -180.0) + Angles->x += 360.0; + if (Angles->y > 180.0) + Angles->y -= 360.0; + if (Angles->y < -180.0) + Angles->y += 360.0; + + Angles->z = 0.0; } // POD SAYING: -void UTIL_SayTextBot( const char *pText,cBot *pBot) -{ - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG ("SayText", -1); - +void UTIL_SayTextBot(const char* pText, cBot* pBot) +{ + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG("SayText", -1); + char szTemp[160]; - char szName[BOT_NAME_LEN+1]; - int i=0; - - // clear out - memset (szTemp, 0, sizeof (szTemp)); - memset (szName, 0, sizeof (szName)); + char szName[BOT_NAME_LEN + 1]; + int i = 0; + + // clear out + memset(szTemp, 0, sizeof(szTemp)); + memset(szName, 0, sizeof(szName)); // init - szTemp[0]=2; + szTemp[0] = 2; - int entind=ENTINDEX(pBot->pEdict); + int entind = ENTINDEX(pBot->pEdict); - if(IsAlive(pBot->pEdict)) + if (IsAlive(pBot->pEdict)) { - strcpy(szName,pBot->name); + strcpy(szName, pBot->name); for (i = 1; i <= gpGlobals->maxClients; i++) - { - edict_t *pPlayer = INDEXENT (i); + { + edict_t* pPlayer = INDEXENT(i); // valid if (pPlayer) if (IsAlive(pPlayer)) // alive - { - MESSAGE_BEGIN( MSG_ONE, gmsgSayText,NULL,pPlayer); + { + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pPlayer); WRITE_BYTE(entind); - sprintf(&szTemp[1],"%s : %s",szName,pText); + sprintf(&szTemp[1], "%s : %s", szName, pText); WRITE_STRING(&szTemp[0]); MESSAGE_END(); } @@ -1020,36 +1013,34 @@ void UTIL_SayTextBot( const char *pText,cBot *pBot) } else { - strcpy(szName,pBot->name); + strcpy(szName, pBot->name); for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT (i); + edict_t* pPlayer = INDEXENT(i); if (pPlayer) if (!IsAlive(pPlayer)) { - MESSAGE_BEGIN( MSG_ONE, gmsgSayText,NULL,pPlayer); + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pPlayer); WRITE_BYTE(entind); - sprintf(&szTemp[1],"*DEAD*%s : %s",szName,pText); + sprintf(&szTemp[1], "*DEAD*%s : %s", szName, pText); WRITE_STRING(&szTemp[0]); MESSAGE_END(); } } } - // pass through on ChatEngine (not always) - if (RANDOM_LONG(0,100) < 90) + if (RANDOM_LONG(0, 100) < 90) { char chSentence[80]; - memset (chSentence, 0, sizeof (chSentence)); - + memset(chSentence, 0, sizeof(chSentence)); + // copy pText to chSentence strcpy(chSentence, pText); // pass through - ChatEngine.set_sentence(pBot->name,chSentence); + ChatEngine.set_sentence(pBot->name, chSentence); } - } // $Log: util.cpp,v $ @@ -1063,5 +1054,4 @@ void UTIL_SayTextBot( const char *pText,cBot *pBot) // - added two other utilities DrawNodes & DumpNodes // - updated README file // - fixed compilation warnings (thanks dstruct2k) -// - +// \ No newline at end of file diff --git a/Bsp2Rbn/world.cpp b/Bsp2Rbn/world.cpp index ac46011..d0bbc06 100644 --- a/Bsp2Rbn/world.cpp +++ b/Bsp2Rbn/world.cpp @@ -15,7 +15,7 @@ // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License for more details at: // http://www.gnu.org/copyleft/gpl.html @@ -27,7 +27,6 @@ #include #include - #include "cmdlib.h" #include "mathlib.h" #include "bspfile.h" @@ -39,144 +38,140 @@ World::World(void) { } - World::~World(void) { - FreeWorld(); + FreeWorld(); } - void World::FreeWorld(void) { - FreeEntities(); - - if (dmodels) - { - free(dmodels); - dmodels = NULL; - nummodels = 0; - } - - if (dvisdata) - { - free(dvisdata); - dvisdata = NULL; - visdatasize = 0; - } - - if (dlightdata) - { - free(dlightdata); - dlightdata = NULL; - lightdatasize = 0; - } - - if (dtexdata) - { - free(dtexdata); - dtexdata = NULL; - texdatasize = 0; - } - - if (dentdata) - { - free(dentdata); - dentdata = NULL; - entdatasize = 0; - } - - if (dleafs) - { - free(dleafs); - dleafs = NULL; - numleafs = 0; - } - - if (dplanes) - { - free(dplanes); - dplanes = NULL; - numplanes = 0; - } - - if (dvertexes) - { - free(dvertexes); - dvertexes = NULL; - numvertexes = 0; - } - - if (dnodes) - { - free(dnodes); - dnodes = NULL; - numnodes = 0; - } - - if (texinfo) - { - free(texinfo); - texinfo = NULL; - numtexinfo = 0; - } - - if (dfaces) - { - free(dfaces); - dfaces = NULL; - numfaces = 0; - } - - if (dclipnodes) - { - free(dclipnodes); - dclipnodes = NULL; - numclipnodes = 0; - } - - if (dedges) - { - free(dedges); - dedges = NULL; - numedges = 0; - } - - if (dmarksurfaces) - { - free(dmarksurfaces); - dmarksurfaces = NULL; - nummarksurfaces = 0; - } - - if (dsurfedges) - { - free(dsurfedges); - dsurfedges = NULL; - numsurfedges = 0; - } + FreeEntities(); + + if (dmodels) + { + free(dmodels); + dmodels = NULL; + nummodels = 0; + } + + if (dvisdata) + { + free(dvisdata); + dvisdata = NULL; + visdatasize = 0; + } + + if (dlightdata) + { + free(dlightdata); + dlightdata = NULL; + lightdatasize = 0; + } + + if (dtexdata) + { + free(dtexdata); + dtexdata = NULL; + texdatasize = 0; + } + + if (dentdata) + { + free(dentdata); + dentdata = NULL; + entdatasize = 0; + } + + if (dleafs) + { + free(dleafs); + dleafs = NULL; + numleafs = 0; + } + + if (dplanes) + { + free(dplanes); + dplanes = NULL; + numplanes = 0; + } + + if (dvertexes) + { + free(dvertexes); + dvertexes = NULL; + numvertexes = 0; + } + + if (dnodes) + { + free(dnodes); + dnodes = NULL; + numnodes = 0; + } + + if (texinfo) + { + free(texinfo); + texinfo = NULL; + numtexinfo = 0; + } + + if (dfaces) + { + free(dfaces); + dfaces = NULL; + numfaces = 0; + } + + if (dclipnodes) + { + free(dclipnodes); + dclipnodes = NULL; + numclipnodes = 0; + } + + if (dedges) + { + free(dedges); + dedges = NULL; + numedges = 0; + } + + if (dmarksurfaces) + { + free(dmarksurfaces); + dmarksurfaces = NULL; + nummarksurfaces = 0; + } + + if (dsurfedges) + { + free(dsurfedges); + dsurfedges = NULL; + numsurfedges = 0; + } } - -void World::LoadBSP(char *bspfile) +void World::LoadBSP(char* bspfile) { - char bsp_filename[256]; - char pathname[256]; - bool bsp_found; - int index, mod_index; - char modname[256]; - int len; - - bsp_found = FALSE; + char bsp_filename[256]; + char pathname[256]; + bool bsp_found; + int index, mod_index; + char modname[256]; + int len; - strcpy(bspname, bspfile); + bsp_found = FALSE; - if (FileTime(bspname) != -1) // does the specified file exist? - LoadBSPFile(bspname); - else - fprintf(stderr,"Cannot load file %s\n",bspname) ; + strcpy(bspname, bspfile); - ParseEntities(); + if (FileTime(bspname) != -1) // does the specified file exist? + LoadBSPFile(bspname); + else + fprintf(stderr, "Cannot load file %s\n", bspname); - LoadEntVars(); -} + ParseEntities(); + LoadEntVars(); +} \ No newline at end of file diff --git a/Bsp2Rbn/world.h b/Bsp2Rbn/world.h index 18233b2..9b68ae8 100644 --- a/Bsp2Rbn/world.h +++ b/Bsp2Rbn/world.h @@ -15,7 +15,7 @@ // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License for more details at: // http://www.gnu.org/copyleft/gpl.html @@ -31,18 +31,17 @@ #ifndef WORLD_H #define WORLD_H - class World { - public: +public: - char bspname[256]; // name of the currently loaded BSP file + char bspname[256]; // name of the currently loaded BSP file - World(void); - ~World(void); + World(void); + ~World(void); - void FreeWorld(void); - void LoadBSP(char *bspfile); + void FreeWorld(void); + void LoadBSP(char* bspfile); }; #ifndef __linux__ @@ -50,4 +49,3 @@ BOOL CenterWindow(HWND hWnd); #endif #endif - diff --git a/ChatEngine.cpp b/ChatEngine.cpp index 984113d..36ba7b6 100644 --- a/ChatEngine.cpp +++ b/ChatEngine.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +20,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -28,12 +30,15 @@ **/ // Chatting Engine -#include -#include +#include +#include // Some tests by EVYNCKE #include +#include #include -#include +#include +#include + #include #include @@ -48,30 +53,48 @@ #include "ChatEngine.h" -extern edict_t *pHostEdict; +extern edict_t* pHostEdict; extern cGame Game; extern cBot bots[32]; +namespace { + edict_t* findPlayerEdictByName(const char* playerName) { + if (!playerName || *playerName == '\0') { + return nullptr; + } + + for (int i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + if (pPlayer && !pPlayer->free) { + if (std::strcmp(STRING(pPlayer->v.netname), playerName) == 0) { + return pPlayer; + } + } + } + return nullptr; + } +} + // initialize all -void -cChatEngine::init() { +void cChatEngine::init() { // clear all blocks - for (int iB = 0; iB < MAX_BLOCKS; iB++) { - for (int iBs = 0; iBs < 50; iBs++) - ReplyBlock[iB].sentence[iBs][0] = '\0'; + for (tReplyBlock& iB : ReplyBlock) + { + for (char(&iBs)[128] : iB.sentence) + iBs[0] = '\0'; - for (int iBw = 0; iBw < 10; iBw++) - ReplyBlock[iB].word[iBw][0] = '\0'; + for (char(&iBw)[25] : iB.word) + iBw[0] = '\0'; - ReplyBlock[iB].bUsed = false; + iB.bUsed = false; } iLastBlock = -1; iLastSentence = -1; // init sentence - memset(sentence, 0, sizeof(sentence)); - memset(sender, 0, sizeof(sender)); + std::memset(sentence, 0, sizeof(sentence)); + std::memset(sender, 0, sizeof(sender)); } // load @@ -84,7 +107,7 @@ void cChatEngine::initAndload() { // think void cChatEngine::think() { - if (fThinkTimer + 1.0 > gpGlobals->time) return; // not time yet to think + if (fThinkTimer + 1.0f > gpGlobals->time) return; // not time yet to think // decrease over time to avoid flooding if (Game.iProducedSentences > 1) { @@ -94,47 +117,15 @@ void cChatEngine::think() { // if no sender is set, do nothing if (sender[0] == '\0') return; - // 29/08/2019 Stefan: by using string compare on the name of the sender (ie sender[] is the name) we retrieve - // the edict pointer - edict_t *pSender = NULL; - int i; - for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - - if (pPlayer && (!pPlayer->free)) { - char name[30], name2[30]; - // clear - memset(name, 0, sizeof(name)); - memset(name2, 0, sizeof(name2)); - - // copy - strcpy(name, STRING(pPlayer->v.netname)); - strcpy(name2, sender); - - if (strcmp(name, name2) == 0) { - pSender = pPlayer; - break; - } - } - } - // Edict pointer established + edict_t* pSender = findPlayerEdictByName(sender); // Scan the message so we know in what block we should be to reply: - char word[20]; - memset(word, 0, sizeof(word)); - - int c = 0; - int wc = 0; - - int sentenceLength = strlen(sentence); + std::string_view sentence_sv(sentence); - // When length is not valid, get out. - // 29/08/2019: Stefan, so let me get this. We declare the sentence to be max 128 chars, but then we still could end up with a longer one? - // how did we allow for this to happen? - if (sentenceLength == 0 || sentenceLength >= (MAX_SENTENCE_LENGTH-1)) { + if (sentence_sv.empty() || sentence_sv.length() >= MAX_SENTENCE_LENGTH - 1) { // clear out sentence and sender - memset(sentence, 0, sizeof(sentence)); - memset(sender, 0, sizeof(sender)); + std::memset(sentence, 0, sizeof(sentence)); + std::memset(sender, 0, sizeof(sender)); // reset timer fThinkTimer = gpGlobals->time; @@ -142,79 +133,24 @@ void cChatEngine::think() { return; } - int WordBlockScore[MAX_BLOCKS]; + std::vector WordBlockScore(MAX_BLOCKS, 0); - // Init, none of the block has a score yet (set to -1) - for (int wbs = 0; wbs < MAX_BLOCKS; wbs++) { - WordBlockScore[wbs] = -1; - } - - // loop over the sentence character by character - while (c < sentenceLength) { - // protection matters: - // Stefan: this is weird, this can never happen?! - if (c > sentenceLength) - break; - - if (c < 0) - break; - // End of protection matters - - // Step: Check character to identify the end of a word. - if (sentence[c] == ' ' || sentence[c] == '\n' || - sentence[c] == '.' || sentence[c] == '?' || - sentence[c] == '!' || c == sentenceLength) { - // Now find the word and add up scors on the proper score blocks. - - if (c == sentenceLength) - word[wc] = sentence[c]; - - // not a good word (too small) - if (strlen(word) <= 0) { - //SERVER_PRINT("This is not a good word!\n"); - } else { - for (int iB = 0; iB < MAX_BLOCKS; iB++) { - if (ReplyBlock[iB].bUsed) { - for (int iBw = 0; iBw < 10; iBw++) { - // skip any word in the reply block that is not valid - if (ReplyBlock[iB].word[iBw][0] == '\0') - continue; // not filled in - - if (strlen(ReplyBlock[iB].word[iBw]) <= 0) - continue; // not long enough (a space?) - - // 03/07/04 - // add score to matching word (evy: ignoring case) - if (strcmpi(ReplyBlock[iB].word[iBw], word) == 0) - WordBlockScore[iB]++; - } // all words in this block - } // any used block - } // for all blocks - } // good word - - // clear out entire word. - //for (int cw=0; cw < 20; cw++) - // word[cw] = '\0'; - memset(word, 0, sizeof(word)); - - wc = 0; // reset WC position (start writing 'word[WC]' at 0 again) - c++; // next position in sentence - continue; // go to top again. + std::stringstream ss(sentence); + std::string word_str; + while (ss >> word_str) { + for (int iB = 0; iB < MAX_BLOCKS; iB++) { + if (ReplyBlock[iB].bUsed) { + for (const char (&iBw)[25] : ReplyBlock[iB].word) { + if (iBw[0] != '\0' && word_str == iBw) { + WordBlockScore[iB]++; + } + } + } } - // when we end up here, we are still reading a 'non finishing word' character. - // we will fill that in word[wc]. Then add up wc and c, until we find a character - // that marks the end of a word again. - - // fill in the word: - word[wc] = sentence[c]; - - // add up. - c++; - wc++; - } // end of loop + } // now loop through all blocks and find the one with the most score: - int iMaxScore = -1; + int iMaxScore = 0; int iTheBlock = -1; // for all blocks @@ -232,34 +168,28 @@ void cChatEngine::think() { int iMax = -1; // now choose a sentence to reply with - for (int iS = 0; iS < 50; iS++) { + for (const char(&iS)[128] : ReplyBlock[iTheBlock].sentence) + { // Find max sentences of this reply block - if (ReplyBlock[iTheBlock].sentence[iS][0] != '\0') + if (iS[0] != '\0') iMax++; } // loop through all bots: for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); + edict_t* pPlayer = INDEXENT(i); // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free) && pSender != pPlayer) { - - // only reply to the living when alive, and otherwise - bool bSenderAlive = false; - bool bPlayerAlive = false; - - bSenderAlive = IsAlive(pSender); // CRASH : it sometimes crashes here - bPlayerAlive = IsAlive(pPlayer); - - if (bSenderAlive != bPlayerAlive) + if (pPlayer && !pPlayer->free && pSender != pPlayer) + { + if (!IsAlive(pSender) || !IsAlive(pPlayer)) continue; - cBot *pBotPointer = UTIL_GetBotPointer(pPlayer); + cBot* pBotPointer = UTIL_GetBotPointer(pPlayer); - if (pBotPointer != NULL) + if (pBotPointer != nullptr) if (RANDOM_LONG(0, 100) < - (pBotPointer->ipChatRate + 25)) { + pBotPointer->ipChatRate + 25) { // When we have at least 1 sentence... if (iMax > -1) { // choose randomly a reply @@ -269,93 +199,31 @@ void cChatEngine::think() { the_c == iLastSentence) { // when this is the same, avoid it. Try to change again if (iMax > 0) - the_c++; + the_c = (the_c + 1) % (iMax + 1); else continue; // do not reply double - - if (the_c > iMax) - the_c = 0; } // the_c is choosen, it is the sentence we reply with. // do a check if its valid: if (ReplyBlock[iTheBlock]. - sentence[the_c][0] != '\0') { + sentence[the_c][0] != '\0') { // chSentence is eventually what the bot will say. char chSentence[128]; - char temp[80]; - - memset(chSentence, 0, sizeof(chSentence)); - memset(temp, 0, sizeof(temp)); - - // get character position - char *name_pos = - strstr(ReplyBlock[iTheBlock]. - sentence[the_c], "%n"); - - // when name_pos var is found, fill it in. - if (name_pos != NULL) { - // when name is in this one: - int name_offset = - name_pos - - ReplyBlock[iTheBlock].sentence[the_c]; - name_offset--; - - // copy every character till name_offset - int nC; - for (nC = 0; nC < name_offset; nC++) { - //chSentence[nC] = ReplyBlock[iTheBlock].sentence[the_c][nC]; - temp[nC] = - ReplyBlock[iTheBlock]. - sentence[the_c][nC]; - } - - temp[nC] = ' '; - - // copy senders name to chSentence - strcat(temp, sender); - - // From here us 'tc' to keep track of chSentence and use - // nC to keep reading from ReplyBlock - int tc = nC; - - // Skip %n part in ReplyBlock - nC = name_offset + 3; - - // we just copied a name to chSentence - // set our cursor after the name now (name length + 1) - tc = strlen(temp); - - // now finish the sentence - // get entire length of ReplyBlock and go until we reach the end - int length = - strlen(ReplyBlock[iTheBlock]. - sentence[the_c]); - - - // for every nC , read character from ReplyBlock - for (; nC <= length; nC++) { - // ... and copy it into chSentence - temp[tc] = - ReplyBlock[iTheBlock]. - sentence[the_c][nC]; - //char tmsg[80]; - //sprintf(tmsg,"Copying char %c , tc = %d, nC = %d\n", temp[tc], tc, nC); - //SERVER_PRINT(tmsg); - - tc++; // add up tc. - } - - // terminate - temp[tc] = '\n'; - - sprintf(chSentence, "%s \n", temp); + std::string_view reply_template(ReplyBlock[iTheBlock].sentence[the_c]); + size_t name_pos = reply_template.find("%n"); + + if (name_pos != std::string_view::npos) { + std::string final_sentence = std::string(reply_template.substr(0, name_pos)); + final_sentence += sender; + final_sentence += reply_template.substr(name_pos + 2); + snprintf(chSentence, sizeof(chSentence), "%s \n", final_sentence.c_str()); + } + // when no name pos is found, we just copy the string and say that (works ok) + else { + snprintf(chSentence, sizeof(chSentence), "%s \n", + ReplyBlock[iTheBlock].sentence[the_c]); } - // when no name pos is found, we just copy the string and say that (works ok) - else - sprintf(chSentence, "%s \n", - ReplyBlock[iTheBlock]. - sentence[the_c]); // reply: pBotPointer->PrepareChat(chSentence); @@ -374,41 +242,59 @@ void cChatEngine::think() { } // clear sentence and such - memset(sentence, 0, sizeof(sentence)); - memset(sender, 0, sizeof(sender)); + std::memset(sentence, 0, sizeof(sentence)); + std::memset(sender, 0, sizeof(sender)); - fThinkTimer = gpGlobals->time + RANDOM_FLOAT(0.0, 0.5); + fThinkTimer = gpGlobals->time + RANDOM_FLOAT(0.0f, 0.5f); } -// -void cChatEngine::set_sentence(char csender[30], char csentence[128]) { - - if (sender[0] == ' ' || sender[0] == '\0') { - // SERVER_PRINT("Sender & sentence set.\nSender="); - // SERVER_PRINT(csender); - // SERVER_PRINT("\nSentence="); - // SERVER_PRINT(csentence); - // SERVER_PRINT("--\n"); - - strcpy(sender, csender); -#ifdef _WIN32 - - _strupr(csentence); - // #elseif did not compile in MSVC - stefan (26/04/04) -#else - //for linux by ok: - //further changed back by evyncke as these are normal string not CString - //Hence, hardcoding the strupr inline... - char *pString; - pString = csentence; - while (*pString) { - *pString = toupper(*pString); - pString++; +void cChatEngine::handle_sentence() { + // Define a constant for the chat rate threshold + constexpr int CHAT_RATE_THRESHOLD = 25; + + // Check if there is a sentence to process + if (sentence[0] == '\0') { + return; + } + + // Loop through all clients + for (int i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + + // Skip invalid players + if (pPlayer && !pPlayer->free) { + cBot* pBotPointer = UTIL_GetBotPointer(pPlayer); + + // Check if bot pointer is valid and decide to prepare chat + if (pBotPointer != nullptr && RANDOM_LONG(0, 100) < pBotPointer->ipChatRate + CHAT_RATE_THRESHOLD) { + pBotPointer->PrepareChat(sentence); + } } -#endif - strcpy(sentence, csentence); } + + // Clear sentence and sender buffers + std::memset(sentence, 0, sizeof(sentence)); + std::memset(sender, 0, sizeof(sender)); + + // Set the think timer + fThinkTimer = gpGlobals->time + RANDOM_FLOAT(0.0f, 0.5f); +} + + +void cChatEngine::set_sentence(char csender[MAX_NAME_LENGTH], char csentence[MAX_SENTENCE_LENGTH]) { + if (sender[0] == ' ' || sender[0] == '\0') { + strncpy(sender, csender, sizeof(sender) - 1); + sender[sizeof(sender) - 1] = '\0'; + +#ifdef _WIN32 + _strupr(csentence); +#else + std::transform(csentence, csentence + std::strlen(csentence), csentence, ::toupper); +#endif + strncpy(sentence, csentence, sizeof(sentence) - 1); + sentence[sizeof(sentence) - 1] = '\0'; + } } diff --git a/ChatEngine.h b/ChatEngine.h index 227e0bd..8f5884d 100644 --- a/ChatEngine.h +++ b/ChatEngine.h @@ -28,10 +28,14 @@ **/ // Chatting Engine -#define MAX_BLOCKS 100 -#define BLOCK_DEATHS MAX_BLOCKS-1 +#ifndef CHATENGINE_H +#define CHATENGINE_H -static const int MAX_SENTENCE_LENGTH = 128; +constexpr int MAX_BLOCKS = 100; + +#define BLOCK_DEATHS (MAX_BLOCKS-1) + +static constexpr int MAX_SENTENCE_LENGTH = 128; // Reply block typedef struct { // words, hinting that in this block a logical sentence will be to reply with @@ -59,8 +63,10 @@ class cChatEngine { void think(); // make the chat engine think // add sentence from any player/bot into memory to handle - void set_sentence(char csender[30], char csentence[MAX_SENTENCE_LENGTH]); + void set_sentence(char csender[32], char csentence[MAX_SENTENCE_LENGTH]); // handles a sentence, decides to reply on it or not. void handle_sentence(); }; + +#endif // CHATENGINE_H \ No newline at end of file diff --git a/IniParser.cpp b/IniParser.cpp index 0f4bc4c..f65396c 100644 --- a/IniParser.cpp +++ b/IniParser.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,21 +20,22 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. * **/ -/** - * INI PARSER - * COPYRIGHTED BY STEFAN HENDRIKS (C) 2003-2004 - **/ + /** + * INI PARSER + * COPYRIGHTED BY STEFAN HENDRIKS (C) 2003-2004 + **/ -#include +#include +#include #include #include #include @@ -46,1023 +49,907 @@ #include "NodeMachine.h" #include "ChatEngine.h" -#include +#include +#include +#include extern int mod_id; -extern edict_t *pHostEdict; +extern edict_t* pHostEdict; extern cNodeMachine NodeMachine; extern int m_spriteTexture; extern cGame Game; extern cChatEngine ChatEngine; - // Reads out INPUT , will check for a [ at [0] and then checks till ], it will fill section[] // with the chars in between. So : [MAP] -> section = 'MAP'. Use function INI_SectionType(..) // to get the correct ID for that. void INI_Section(char input[80], char section[30]) { - - int pos = 0; - int end_pos = -1; - - // clear out entire string - for (int i = 0; i < 30; i++) - section[i] = '\0'; - - // check if the first character is a '[' - if (input[0] == '[') { - pos = 1; // Begin at character 1 - - while (pos < 79) { - if (input[pos] == ']') { - end_pos = pos - 1; - break; - } - pos++; - } - - if (end_pos > 1 && end_pos < 29) { - for (int wc = 0; wc < end_pos; wc++) - section[wc] = input[wc + 1]; - - section[end_pos] = '\0'; // terminate string - } - } - + int end_pos = -1; + + // clear out entire string + for (int i = 0; i < 30; i++) + section[i] = '\0'; + + // check if the first character is a '[' + if (input[0] == '[') { + int pos = 1; // Begin at character 1 + + while (pos < 79) { + if (input[pos] == ']') { + end_pos = pos - 1; + break; + } + pos++; + } + + if (end_pos > 1 && end_pos < 29) { + for (int wc = 0; wc < end_pos; wc++) + section[wc] = input[wc + 1]; + + section[end_pos] = '\0'; // terminate string + } + } } // Reads out INPUT and will check for an '=' Everything at the left of the // '=' IS a word and will be put in 'word[]'. Use function INI_WordType(char word[25]) to get // the correct ID tag. void INI_Word(char input[80], char word[25]) { - int pos = 0; - int word_pos = -1; - - // clear out entire string - for (int i = 0; i < 25; i++) - word[i] = '\0'; - - while (pos < 79) { - if (input[pos] == '=') { - word_pos = pos; - break; - } - pos++; - } - - if (word_pos > -1 && word_pos < 23) { - for (int wc = 0; wc < word_pos; wc++) - word[wc] = input[wc]; - - word[word_pos] = '\0'; // terminate string - } + int pos = 0; + int word_pos = -1; + + // clear out entire string + for (int i = 0; i < 25; i++) + word[i] = '\0'; + + while (pos < 79) { + if (input[pos] == '=') { + word_pos = pos; + break; + } + pos++; + } + + if (word_pos > -1 && word_pos < 23) { + for (int wc = 0; wc < word_pos; wc++) + word[wc] = input[wc]; + + word[word_pos] = '\0'; // terminate string + } } // Reads out word[], does a string compare and returns type id int INI_WordType(char word[25], int section) { - if (strlen(word) > 0) { - - if (strcmp(word, "Word") == 0) - return WORD_WORD; - if (strcmp(word, "Sentence") == 0) - return WORD_SENTENCE; - - - if (strcmp(word, "X") == 0) - return WORD_AREAX; - if (strcmp(word, "Y") == 0) - return WORD_AREAY; - if (strcmp(word, "Z") == 0) - return WORD_AREAZ; - - // ------ personality stuff ------ - if (strcmp(word, "PrimaryWeapon") == 0) - return WORD_PRIWEAPON; - - if (strcmp(word, "SecondaryWeapon") == 0) - return WORD_SECWEAPON; - - if (strcmp(word, "SaveForWeapon") == 0) - return WORD_SAVEFORWEAP; - - if (strcmp(word, "Grenade") == 0) - return WORD_GRENADE; - - if (strcmp(word, "FlashBang") == 0) - return WORD_FLASHBANG; - - if (strcmp(word, "SmokeGrenade") == 0) - return WORD_SMOKEGREN; - - if (strcmp(word, "DefuseKit") == 0) - return WORD_DEFUSEKIT; - - if (strcmp(word, "Armour") == 0) - return WORD_ARMOUR; - - // ---- skill - - if (strcmp(word, "XOffset") == 0) - return WORD_XOFFSET; - - if (strcmp(word, "YOffset") == 0) - return WORD_YOFFSET; - - if (strcmp(word, "ZOffset") == 0) - return WORD_ZOFFSET; - - if (strcmp(word, "BotSkill") == 0) - return WORD_BOTSKILL; - - if (strcmp(word, "MaxReactionTime") == 0) - return WORD_MAXREACTTIME; - - if (strcmp(word, "MinReactionTime") == 0) - return WORD_MINREACTTIME; - - if (strcmp(word, "Turnspeed") == 0) - return WORD_TURNSPEED; - - // ---- Game - if (strcmp(word, "Hostage") == 0) - return WORD_HOSTAGERATE; - - if (strcmp(word, "BompSpot") == 0) - return WORD_BOMBSPOTRATE; - - if (strcmp(word, "Random") == 0) - return WORD_RANDOMRATE; - if (strcmp(word, "DroppedBomb") == 0) - return WORD_DROPPEDBOMB; - - // ---- Radio - if (strcmp(word, "Reply") == 0) - return WORD_REPLYRADIO; - - if (strcmp(word, "Create") == 0) - return WORD_CREATERADIO; - - // ---- Team - if (strcmp(word, "HelpTeammate") == 0) - return WORD_HELPTEAM; - - // ---- person - if (strcmp(word, "WalkWithKnife") == 0) - return WORD_WALKKNIFE; - if (strcmp(word, "FearRate") == 0) - return WORD_FEARRATE; - if (strcmp(word, "HearRate") == 0) - return WORD_HEARRATE; - if (strcmp(word, "ChatRate") == 0) - return WORD_CHATRATE; - - if (strcmp(word, "CampRate") == 0) - return WORD_CAMPRATE; - - // ------ buy table stuff ------- - if (strcmp(word, "Price") == 0) - return WORD_PRICE; - - if (strcmp(word, "Priority") == 0) - return WORD_PRIORITY; - - if (strcmp(word, "Ammo1Index") == 0) - return WORD_INDEX1; - - if (strcmp(word, "Ammo2Index") == 0) - return WORD_INDEX2; - - if (strcmp(word, "Ammo1Max") == 0) - return WORD_MAXAMMO1; - - if (strcmp(word, "Ammo2Max") == 0) - return WORD_MAXAMMO2; - - } - - return WORD_NONE; + if (word[0] == '\0') { + return WORD_NONE; + } + + static const std::unordered_map wordMap = { + {"Word", WORD_WORD}, + {"Sentence", WORD_SENTENCE}, + {"X", WORD_AREAX}, + {"Y", WORD_AREAY}, + {"Z", WORD_AREAZ}, + {"PrimaryWeapon", WORD_PRIWEAPON}, + {"SecondaryWeapon", WORD_SECWEAPON}, + {"SaveForWeapon", WORD_SAVEFORWEAP}, + {"Grenade", WORD_GRENADE}, + {"FlashBang", WORD_FLASHBANG}, + {"SmokeGrenade", WORD_SMOKEGREN}, + {"DefuseKit", WORD_DEFUSEKIT}, + {"Armour", WORD_ARMOUR}, + {"XOffset", WORD_XOFFSET}, + {"YOffset", WORD_YOFFSET}, + {"ZOffset", WORD_ZOFFSET}, + {"BotSkill", WORD_BOTSKILL}, + {"MaxReactionTime", WORD_MAXREACTTIME}, + {"MinReactionTime", WORD_MINREACTTIME}, + {"Turnspeed", WORD_TURNSPEED}, + {"Hostage", WORD_HOSTAGERATE}, + {"BompSpot", WORD_BOMBSPOTRATE}, + {"Random", WORD_RANDOMRATE}, + {"DroppedBomb", WORD_DROPPEDBOMB}, + {"Reply", WORD_REPLYRADIO}, + {"Create", WORD_CREATERADIO}, + {"HelpTeammate", WORD_HELPTEAM}, + {"WalkWithKnife", WORD_WALKKNIFE}, + {"FearRate", WORD_FEARRATE}, + {"HearRate", WORD_HEARRATE}, + {"ChatRate", WORD_CHATRATE}, + {"CampRate", WORD_CAMPRATE}, + {"Price", WORD_PRICE}, + {"Priority", WORD_PRIORITY}, + {"Ammo1Index", WORD_INDEX1}, + {"Ammo2Index", WORD_INDEX2}, + {"Ammo1Max", WORD_MAXAMMO1}, + {"Ammo2Max", WORD_MAXAMMO2} + }; + + if (const std::unordered_map::const_iterator it = wordMap.find(word); it != wordMap.end()) { + return it->second; + } + + return WORD_NONE; } // Reads out an entire sentence and returns it -void INI_Sentence(FILE * f, char result[80]) { - - char ch; - int pos = 0; - - // clear out entire string - for (int i = 0; i < 80; i++) - result[i] = '\0'; - - while ((feof(f) == 0) && ((ch = fgetc(f)) != '\n')) { - result[pos] = ch; - pos++; - - // do not allow strings greater then 80 characters. This check prevents a crash for - // users who do exceed the limit. - if (pos > 79) - break; - - // remove dedicated server garbage - //putchar (ch); - } +void INI_Sentence(FILE* f, char result[80]) { + char ch; + int pos = 0; + + // clear out entire string + for (int i = 0; i < 80; i++) + result[i] = '\0'; + + while (feof(f) == 0 && (ch = fgetc(f)) != '\n') { + result[pos] = ch; + pos++; + + // do not allow strings greater then 80 characters. This check prevents a crash for + // users who do exceed the limit. + if (pos > 79) + break; + + // remove dedicated server garbage + //putchar (ch); + } } // Reads out section[], does a string compare and returns type id -int INI_SectionType(char section[30], int last) { - - if (strcmp(section, "BLOCK") == 0) - return INI_BLOCK; - - if (strcmp(section, "DEATH") == 0) - return INI_DEATHS; - - if (strcmp(section, "WELCOME") == 0) - return INI_WELCOME; - - if (strcmp(section, "AREA") == 0) - return INI_AREA; - - if (strcmp(section, "WEAPON") == 0) - return INI_WEAPON; - - if (strcmp(section, "SKILL") == 0) - return INI_SKILL; - - if (strcmp(section, "GAME") == 0) - return INI_GAME; - - if (strcmp(section, "RADIO") == 0) - return INI_RADIO; - - if (strcmp(section, "TEAM") == 0) - return INI_TEAM; - - if (strcmp(section, "PERSON") == 0) - return INI_PERSON; - - // When nothing found; we assume its just a new ID tag for some unit or structure - // Therefor we return the last known SECTION ID so we can assign the proper WORD ID's - return last; +int INI_SectionType(char section[30], const int last) { + static const std::unordered_map sectionMap = { + {"BLOCK", INI_BLOCK}, + {"DEATH", INI_DEATHS}, + {"WELCOME", INI_WELCOME}, + {"AREA", INI_AREA}, + {"WEAPON", INI_WEAPON}, + {"SKILL", INI_SKILL}, + {"GAME", INI_GAME}, + {"RADIO", INI_RADIO}, + {"TEAM", INI_TEAM}, + {"PERSON", INI_PERSON} + }; + + if (const std::unordered_map::const_iterator it = sectionMap.find(section); it != sectionMap.end()) { + return it->second; + } + + // When nothing found; we assume its just a new ID tag for some unit or structure + // Therefor we return the last known SECTION ID so we can assign the proper WORD ID's + return last; } // Reads out section[], does a string compare and returns type id // BUYTABLE.INI SPECIFIC! -int INI_SectionType_BUYTABLE(char section[30], int last) { - - if (strcmp(section, "P228") == 0) - return CS_WEAPON_P228; - if (strcmp(section, "HEGRENADE") == 0) - return CS_WEAPON_HEGRENADE; - if (strcmp(section, "AK47") == 0) - return CS_WEAPON_AK47; - if (strcmp(section, "DEAGLE") == 0) - return CS_WEAPON_DEAGLE; - if (strcmp(section, "MAC10") == 0) - return CS_WEAPON_MAC10; - if (strcmp(section, "AUG") == 0) - return CS_WEAPON_AUG; - if (strcmp(section, "SG552") == 0) - return CS_WEAPON_SG552; - if (strcmp(section, "ELITE") == 0) - return CS_WEAPON_ELITE; - if (strcmp(section, "FIVESEVEN") == 0) - return CS_WEAPON_FIVESEVEN; - if (strcmp(section, "UMP45") == 0) - return CS_WEAPON_UMP45; - if (strcmp(section, "SG550") == 0) - return CS_WEAPON_SG550; - if (strcmp(section, "USP") == 0) - return CS_WEAPON_USP; - if (strcmp(section, "GLOCK18") == 0) - return CS_WEAPON_GLOCK18; - if (strcmp(section, "AWP") == 0) - return CS_WEAPON_AWP; - if (strcmp(section, "MP5") == 0) - return CS_WEAPON_MP5NAVY; - if (strcmp(section, "M249") == 0) - return CS_WEAPON_M249; - if (strcmp(section, "M3") == 0) - return CS_WEAPON_M3; - if (strcmp(section, "M4A1") == 0) - return CS_WEAPON_M4A1; - if (strcmp(section, "TMP") == 0) - return CS_WEAPON_TMP; - if (strcmp(section, "G3SG1") == 0) - return CS_WEAPON_G3SG1; - if (strcmp(section, "SCOUT") == 0) - return CS_WEAPON_SCOUT; - if (strcmp(section, "FLASHBANG") == 0) - return CS_WEAPON_FLASHBANG; - if (strcmp(section, "C4") == 0) - return CS_WEAPON_C4; - if (strcmp(section, "SMOKEGRENADE") == 0) - return CS_WEAPON_SMOKEGRENADE; - if (strcmp(section, "XM1014") == 0) - return CS_WEAPON_XM1014; - if (strcmp(section, "KNIFE") == 0) - return CS_WEAPON_KNIFE; - if (strcmp(section, "P90") == 0) - return CS_WEAPON_P90; - - // Counter-Strike 1.6 - if (strcmp(section, "FAMAS") == 0) - return CS_WEAPON_FAMAS; - if (strcmp(section, "GALIL") == 0) - return CS_WEAPON_GALIL; - - // Unconfirmed - if (strcmp(section, "SHIELD") == 0) - return CS_WEAPON_SHIELD; - - // When nothing found; we assume its just a new ID tag for some unit or structure - // Therefor we return the last known SECTION ID so we can assign the proper WORD ID's - return last; +int INI_SectionType_BUYTABLE(char section[30], const int last) { + if (std::strcmp(section, "P228") == 0) + return CS_WEAPON_P228; + if (std::strcmp(section, "HEGRENADE") == 0) + return CS_WEAPON_HEGRENADE; + if (std::strcmp(section, "AK47") == 0) + return CS_WEAPON_AK47; + if (std::strcmp(section, "DEAGLE") == 0) + return CS_WEAPON_DEAGLE; + if (std::strcmp(section, "MAC10") == 0) + return CS_WEAPON_MAC10; + if (std::strcmp(section, "AUG") == 0) + return CS_WEAPON_AUG; + if (std::strcmp(section, "SG552") == 0) + return CS_WEAPON_SG552; + if (std::strcmp(section, "ELITE") == 0) + return CS_WEAPON_ELITE; + if (std::strcmp(section, "FIVESEVEN") == 0) + return CS_WEAPON_FIVESEVEN; + if (std::strcmp(section, "UMP45") == 0) + return CS_WEAPON_UMP45; + if (std::strcmp(section, "SG550") == 0) + return CS_WEAPON_SG550; + if (std::strcmp(section, "USP") == 0) + return CS_WEAPON_USP; + if (std::strcmp(section, "GLOCK18") == 0) + return CS_WEAPON_GLOCK18; + if (std::strcmp(section, "AWP") == 0) + return CS_WEAPON_AWP; + if (std::strcmp(section, "MP5") == 0) + return CS_WEAPON_MP5NAVY; + if (std::strcmp(section, "M249") == 0) + return CS_WEAPON_M249; + if (std::strcmp(section, "M3") == 0) + return CS_WEAPON_M3; + if (std::strcmp(section, "M4A1") == 0) + return CS_WEAPON_M4A1; + if (std::strcmp(section, "TMP") == 0) + return CS_WEAPON_TMP; + if (std::strcmp(section, "G3SG1") == 0) + return CS_WEAPON_G3SG1; + if (std::strcmp(section, "SCOUT") == 0) + return CS_WEAPON_SCOUT; + if (std::strcmp(section, "FLASHBANG") == 0) + return CS_WEAPON_FLASHBANG; + if (std::strcmp(section, "C4") == 0) + return CS_WEAPON_C4; + if (std::strcmp(section, "SMOKEGRENADE") == 0) + return CS_WEAPON_SMOKEGRENADE; + if (std::strcmp(section, "XM1014") == 0) + return CS_WEAPON_XM1014; + if (std::strcmp(section, "KNIFE") == 0) + return CS_WEAPON_KNIFE; + if (std::strcmp(section, "P90") == 0) + return CS_WEAPON_P90; + + // Counter-Strike 1.6 + if (std::strcmp(section, "FAMAS") == 0) + return CS_WEAPON_FAMAS; + if (std::strcmp(section, "GALIL") == 0) + return CS_WEAPON_GALIL; + + if (std::strcmp(section, "SHIELD") == 0) + return CS_WEAPON_SHIELD; + + // When nothing found; we assume its just a new ID tag for some unit or structure + // Therefor we return the last known SECTION ID so we can assign the proper WORD ID's + return last; } // Reads out 'result' and will return the value after the '='. Returns integer. // For CHAR returns see "INI_WordValueCHAR(char result[80]); int INI_WordValueINT(char result[80]) { - int pos = 0; - int is_pos = -1; - - while (pos < 79) { - if (result[pos] == '=') { - is_pos = pos; - break; - } - pos++; - } - - if (is_pos > -1) { - // Whenever the IS (=) position is known, we make a number out of 'IS_POS' till the next empty - // space. - int end_pos = -1; - - while (pos < 79) { - if (result[pos] == '\0') { - end_pos = pos; - break; - } - pos++; - } - - // End position found! - if (end_pos > -1) { - // We know the END position. We will use that piece of string to read out a number. - char number[10]; - - // clear out entire string - for (int i = 0; i < 10; i++) - number[i] = '\0'; - - // Copy the part to 'number', Make sure we won't get outside the array of the character. - int cp = is_pos + 1; - int c = 0; - while (cp < end_pos) { - number[c] = result[cp]; - c++; - cp++; - if (c > 9) - break; - } - - /* - char aa[80]; - sprintf(aa, "Original %s, atoi %d\n", number, atoi(number)); - DebugOut(aa); - */ - - return atoi(number); - } - // nothing here, so we return NULL at the end - } - - return 0; // No value, return 0 (was NULL) + int pos = 0; + int is_pos = -1; + + while (pos < 79) { + if (result[pos] == '=') { + is_pos = pos; + break; + } + pos++; + } + + if (is_pos > -1) { + // Whenever the IS (=) position is known, we make a number out of 'IS_POS' till the next empty + // space. + int end_pos = -1; + + while (pos < 79) { + if (result[pos] == '\0') { + end_pos = pos; + break; + } + pos++; + } + + // End position found! + if (end_pos > -1) { + // We know the END position. We will use that piece of string to read out a number. + char number[10]; + + // clear out entire string + for (char& i : number) + i = '\0'; + + // Copy the part to 'number', Make sure we won't get outside the array of the character. + int cp = is_pos + 1; + int c = 0; + while (cp < end_pos) { + number[c] = result[cp]; + c++; + cp++; + if (c > 9) + break; + } + + /* + char aa[80]; + sprintf(aa, "Original %s, atoi %d\n", number, atoi(number)); + DebugOut(aa); + */ + + return std::atoi(number); + } + // nothing here, so we return NULL at the end + } + + return 0; // No value, return 0 (was NULL) } // Reads out 'result' and will return the value after the '='. Returns integer. // For CHAR returns see "INI_WordValueCHAR(char result[80]); float INI_WordValueFLOAT(char result[80]) { - int pos = 0; - int is_pos = -1; - - while (pos < 79) { - if (result[pos] == '=') { - is_pos = pos; - break; - } - pos++; - } - - if (is_pos > -1) { - // Whenever the IS (=) position is known, we make a number out of 'IS_POS' till the next empty - // space. - int end_pos = -1; - - while (pos < 79) { - if (result[pos] == '\0') { - end_pos = pos; - break; - } - pos++; - } - - // End position found! - if (end_pos > -1) { - // We know the END position. We will use that piece of string to read out a number. - char number[10]; - - // clear out entire string - for (int i = 0; i < 10; i++) - number[i] = '\0'; - - // Copy the part to 'number', Make sure we won't get outside the array of the character. - int cp = is_pos + 1; - int c = 0; - while (cp < end_pos) { - number[c] = result[cp]; - c++; - cp++; - if (c > 9) - break; - } - return atof(number); - } - // nothing here, so we return NULL at the end - } - - return 0.0; // No value, return 0.0 was NULL + int pos = 0; + int is_pos = -1; + + while (pos < 79) { + if (result[pos] == '=') { + is_pos = pos; + break; + } + pos++; + } + + if (is_pos > -1) { + // Whenever the IS (=) position is known, we make a number out of 'IS_POS' till the next empty + // space. + int end_pos = -1; + + while (pos < 79) { + if (result[pos] == '\0') { + end_pos = pos; + break; + } + pos++; + } + + // End position found! + if (end_pos > -1) { + // We know the END position. We will use that piece of string to read out a number. + char number[10]; + + // clear out entire string + for (char& i : number) + i = '\0'; + + // Copy the part to 'number', Make sure we won't get outside the array of the character. + int cp = is_pos + 1; + int c = 0; + while (cp < end_pos) { + number[c] = result[cp]; + c++; + cp++; + if (c > 9) + break; + } + return static_cast(std::atof(number)); + } + // nothing here, so we return NULL at the end + } + + return 0.0; // No value, return 0.0 was NULL } // Reads out 'result' and will return the value after the '='. Returns nothing but will put // the result in 'value[25]'. Max argument may be 25 characters! void INI_WordValueCHAR(char result[80], char value[80]) { - int pos = 0; - int is_pos = -1; - - // clear out entire string - for (int i = 0; i < 25; i++) - value[i] = '\0'; - - while (pos < 79) { - if (result[pos] == '=') { - is_pos = pos; - break; - } - pos++; - } - - if (is_pos > -1) { - // Whenever the IS (=) position is known, we make a number out of 'IS_POS' till the next empty - // space. - int end_pos = -1; - - while (pos < 79) { - if (result[pos] == '\0') { - end_pos = pos; - break; - } - pos++; - } - - // End position found! - if (end_pos > -1) { - // We know the END position. We will use that piece of string to read out a number. - - // Copy the part to 'value', Make sure we won't get outside the array of the character. - int cp = is_pos + 1; - int c = 0; - while (cp < end_pos) { - value[c] = result[cp]; - c++; - cp++; - if (c > 79) - break; - } - } - } + int pos = 0; + int is_pos = -1; + + // clear out entire string + for (int i = 0; i < 25; i++) + value[i] = '\0'; + + while (pos < 79) { + if (result[pos] == '=') { + is_pos = pos; + break; + } + pos++; + } + + if (is_pos > -1) { + // Whenever the IS (=) position is known, we make a number out of 'IS_POS' till the next empty + // space. + int end_pos = -1; + + while (pos < 79) { + if (result[pos] == '\0') { + end_pos = pos; + break; + } + pos++; + } + + // End position found! + if (end_pos > -1) { + // We know the END position. We will use that piece of string to read out a number. + + // Copy the part to 'value', Make sure we won't get outside the array of the character. + int cp = is_pos + 1; + int c = 0; + while (cp < end_pos) { + value[c] = result[cp]; + c++; + cp++; + if (c > 79) + break; + } + } + } } // parses the chat file (loads in blocks) void INI_PARSE_CHATFILE() { - char dirname[256]; - char filename[256]; - - FILE *stream; - int section = INI_NONE; - int wordtype = WORD_NONE; - - - // Set Directory name + file - strcpy(dirname, "data/cstrike/chat.ini"); - - // writes whole path into "filename", Linux compatible - UTIL_BuildFileNameRB(dirname, filename); - - // make sure the engine knows... - REALBOT_PRINT(NULL, "INI_PARSE_CHATFILE", "Loading CHAT.INI\n"); - - int iBlockId = -1; - int iBlockWord = -1; - int iBlockSentence = -1; - - // load it - if ((stream = fopen(filename, "r+t")) != NULL) { - char linefeed[80]; - char lineword[25]; - char linesection[30]; - - // infinite loop baby - while (!feof(stream)) { - INI_Sentence(stream, linefeed); - - // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary - // character (which is "//", ";" or "#"), or an empty line, then skip it - if (linefeed[0] == ';' || - linefeed[0] == '#' || - (linefeed[0] == '/' && linefeed[1] == '/') || - linefeed[0] == '\n' || linefeed[0] == '\0') - continue; // Skip - - wordtype = WORD_NONE; - - // Every line is checked for a new section. - INI_Section(linefeed, linesection); - - if (linesection[0] != '\0' && strlen(linesection) > 1) { - section = INI_SectionType(linesection, section); - - if (section == INI_BLOCK) { - iBlockId++; - if (iBlockId > 97) - iBlockId = 97; - section = INI_NONE; - iBlockWord = -1; - iBlockSentence = -1; - } - - if (section == INI_DEATHS) { - iBlockId = 99; // 99 = death - iBlockWord = -1; - iBlockSentence = -1; - section = INI_NONE; - } - - if (section == INI_WELCOME) { - iBlockId = 98; // 98 = welcome - iBlockWord = -1; - iBlockSentence = -1; - section = INI_NONE; - } - - continue; // next line - } - - if (iBlockId > -1) { - INI_Word(linefeed, lineword); - wordtype = INI_WordType(lineword, section); - - // We load in words - if (wordtype == WORD_WORD) { - iBlockWord++; - if (iBlockWord > 9) - iBlockWord = 9; - - // write the word in the block - char chWord[25]; - memset(chWord, 0, sizeof(chWord)); - INI_WordValueCHAR(linefeed, chWord); - // lower case - //chWord = _strlwr( _strdup( chWord ) ); + char dirname[256]; + char filename[256]; + + int section = INI_NONE; + + // Set Directory name + file + std::strcpy(dirname, "data/cstrike/chat.ini"); + + // writes whole path into "filename", Linux compatible + UTIL_BuildFileNameRB(dirname, filename); + + // make sure the engine knows... + REALBOT_PRINT(nullptr, "INI_PARSE_CHATFILE", "Loading CHAT.INI\n"); + + // load it + if (FILE* stream; (stream = std::fopen(filename, "r+t")) != nullptr) { + + int iBlockId = -1; + int iBlockWord = -1; + int iBlockSentence = -1; + + // infinite loop baby + while (!feof(stream)) { + char linesection[30]; + char linefeed[80]; + INI_Sentence(stream, linefeed); + + // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary + // character (which is "//", ";" or "#"), or an empty line, then skip it + if (linefeed[0] == ';' || + linefeed[0] == '#' || + (linefeed[0] == '/' && linefeed[1] == '/') || + linefeed[0] == '\n' || linefeed[0] == '\0') + continue; // Skip + + // Every line is checked for a new section. + INI_Section(linefeed, linesection); + + if (linesection[0] != '\0' && std::strlen(linesection) > 1) { + section = INI_SectionType(linesection, section); + + if (section == INI_BLOCK) { + iBlockId++; + iBlockId = std::min(iBlockId, 97); + section = INI_NONE; + iBlockWord = -1; + iBlockSentence = -1; + } + + if (section == INI_DEATHS) { + iBlockId = 99; // 99 = death + iBlockWord = -1; + iBlockSentence = -1; + section = INI_NONE; + } + + if (section == INI_WELCOME) { + iBlockId = 98; // 98 = welcome + iBlockWord = -1; + iBlockSentence = -1; + section = INI_NONE; + } + + continue; // next line + } + + if (iBlockId > -1) { + char lineword[25]; + INI_Word(linefeed, lineword); + const int wordtype = INI_WordType(lineword, section); + + // We load in words + if (wordtype == WORD_WORD) { + iBlockWord++; + iBlockWord = std::min(iBlockWord, 9); + + // write the word in the block + char chWord[25] = {}; + INI_WordValueCHAR(linefeed, chWord); + // lower case + //chWord = _strlwr( _strdup( chWord ) ); #ifdef _WIN32 - _strupr(chWord); - // #elseif did not work for MSVC + _strupr(chWord); + // #elseif did not work for MSVC #else - //for linux by ok: - // transform removed by evyncke since it works only on strings and not char array - //further changed back by evyncke as these are normal string not CString - //Hence, hardcoding the strupr inline... - char *pString; - pString = chWord; - while (*pString) { - *pString = toupper(*pString); - pString++; - } + //for linux by ok: + // transform removed by evyncke since it works only on strings and not char array + //further changed back by evyncke as these are normal string not CString + //Hence, hardcoding the strupr inline... + char* pString; + pString = chWord; + while (*pString) { + *pString = toupper(*pString); + pString++; + } #endif - strcpy(ChatEngine.ReplyBlock[iBlockId].word[iBlockWord], - chWord); - } - - if (wordtype == WORD_SENTENCE) { - iBlockSentence++; - if (iBlockSentence > 49) - iBlockSentence = 49; - - // write the word in the block - char chSentence[80]; - memset(chSentence, 0, sizeof(chSentence)); - INI_WordValueCHAR(linefeed, chSentence); - strcpy(ChatEngine.ReplyBlock[iBlockId]. - sentence[iBlockSentence], chSentence); - - // here we say it is used - ChatEngine.ReplyBlock[iBlockId].bUsed = true; - } - } - - } // while - - fclose(stream); - } + std::strcpy(ChatEngine.ReplyBlock[iBlockId].word[iBlockWord], + chWord); + } + + if (wordtype == WORD_SENTENCE) { + iBlockSentence++; + iBlockSentence = std::min(iBlockSentence, 49); + + // write the word in the block + char chSentence[80] = {}; + INI_WordValueCHAR(linefeed, chSentence); + std::strcpy(ChatEngine.ReplyBlock[iBlockId]. + sentence[iBlockSentence], chSentence); + + // here we say it is used + ChatEngine.ReplyBlock[iBlockId].bUsed = true; + } + } + } // while + + std::fclose(stream); + } } // Parse IAD file: // Important Area Definition file void INI_PARSE_IAD() { - char dirname[256]; - char filename[256]; - - FILE *stream; - int section = INI_NONE; - int wordtype = WORD_NONE; - - // Set Directory name - strcpy(dirname, "data/cstrike/ini/"); - - strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, ".ini"); // nodes file - - // writes whole path into "filename", Linux compatible - UTIL_BuildFileNameRB(dirname, filename); - - SERVER_PRINT(filename); - SERVER_PRINT("\n"); - - float AreaX, AreaY, AreaZ; - AreaX = AreaY = AreaZ = 9999; - - if ((stream = fopen(filename, "r+t")) != NULL) { - char linefeed[80]; - char lineword[25]; - char linesection[30]; - - while (!feof(stream)) { - INI_Sentence(stream, linefeed); - - // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary - // character (which is "//", ";" or "#"), or an empty line, then skip it - if (linefeed[0] == ';' || - linefeed[0] == '#' || - (linefeed[0] == '/' && linefeed[1] == '/') || - linefeed[0] == '\n' || linefeed[0] == '\0') - continue; // Skip - - wordtype = WORD_NONE; - - // Every line is checked for a new section. - INI_Section(linefeed, linesection); - - if (linesection[0] != '\0' && strlen(linesection) > 1) { - section = INI_SectionType(linesection, section); - continue; // next line - } - - // Check word only when in a section - if (section != INI_NONE) { - INI_Word(linefeed, lineword); - wordtype = INI_WordType(lineword, section); - - if (section == INI_AREA) { - if (wordtype == WORD_AREAX) - AreaX = INI_WordValueINT(linefeed); - if (wordtype == WORD_AREAY) - AreaY = INI_WordValueINT(linefeed); - if (wordtype == WORD_AREAZ) - AreaZ = INI_WordValueINT(linefeed); - - - if (AreaX != 9999 && AreaY != 9999 && AreaZ != 9999) { - // add this to goal - rblog("IAD: Adding an important area/goal\n"); - NodeMachine.addGoal(NULL, GOAL_IMPORTANT, Vector(AreaX, AreaY, AreaZ)); - - AreaX = AreaY = AreaZ = 9999; - } - } - } - - } // while - - fclose(stream); - } + char dirname[256]; + char filename[256]; + + FILE* stream; + int section = INI_NONE; + + // Set Directory name + std::strcpy(dirname, "data/cstrike/ini/"); + + std::strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, ".ini"); // nodes file + + // writes whole path into "filename", Linux compatible + UTIL_BuildFileNameRB(dirname, filename); + + SERVER_PRINT(filename); + SERVER_PRINT("\n"); + + float AreaY, AreaZ; + float AreaX = AreaY = AreaZ = 9999; + + if ((stream = std::fopen(filename, "r+t")) != nullptr) { + while (!feof(stream)) { + char linesection[30]; + char linefeed[80]; + INI_Sentence(stream, linefeed); + + // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary + // character (which is "//", ";" or "#"), or an empty line, then skip it + if (linefeed[0] == ';' || + linefeed[0] == '#' || + (linefeed[0] == '/' && linefeed[1] == '/') || + linefeed[0] == '\n' || linefeed[0] == '\0') + continue; // Skip + + // Every line is checked for a new section. + INI_Section(linefeed, linesection); + + if (linesection[0] != '\0' && std::strlen(linesection) > 1) { + section = INI_SectionType(linesection, section); + continue; // next line + } + + // Check word only when in a section + if (section != INI_NONE) { + char lineword[25]; + INI_Word(linefeed, lineword); + const int wordtype = INI_WordType(lineword, section); + + if (section == INI_AREA) { + if (wordtype == WORD_AREAX) + AreaX = static_cast(INI_WordValueINT(linefeed)); + if (wordtype == WORD_AREAY) + AreaY = static_cast(INI_WordValueINT(linefeed)); + if (wordtype == WORD_AREAZ) + AreaZ = static_cast(INI_WordValueINT(linefeed)); + + if (AreaX != 9999.0f && AreaY != 9999.0f && AreaZ != 9999.0f) { + // add this to goal + rblog("IAD: Adding an important area/goal\n"); + NodeMachine.addGoal(nullptr, GOAL_IMPORTANT, Vector(AreaX, AreaY, AreaZ)); + + AreaX = AreaY = AreaZ = 9999; + } + } + } + } // while + + std::fclose(stream); + } } // Parse personality file -void INI_PARSE_BOTS(char cBotName[33], cBot * pBot) { - /* - Revisited: 02/07/05 - Stefan - Last bug/issue report: - - seems to overwrite personality file ? (loading does not work?) - RESULT: There is no bug. - - removed any messages sent by this function. - */ - - FILE *stream; - int section = INI_NONE; - int wordtype = WORD_NONE; - - char dirname[256]; - char filename[256]; - - // Set Directory name - if (mod_id == CSTRIKE_DLL) - strcpy(dirname, "data/cstrike/bots/"); - - //strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, cBotName); - strcat(dirname, ".ini"); - - // writes whole path into "filename", Linux compatible - UTIL_BuildFileNameRB(dirname, filename); - - // we open the file here! - if ((stream = fopen(filename, "r+t")) != NULL) { - char linefeed[80]; - char lineword[25]; - char linesection[30]; - - // infinite loop baby - while (!feof(stream)) { - INI_Sentence(stream, linefeed); - - // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary - // character (which is "//", ";" or "#"), or an empty line, then skip it - if (linefeed[0] == ';' || - linefeed[0] == '#' || - (linefeed[0] == '/' && linefeed[1] == '/') || - linefeed[0] == '\n' || linefeed[0] == '\0') - continue; // Skip - - wordtype = WORD_NONE; - - // Every line is checked for a new section. - INI_Section(linefeed, linesection); - - if (linesection[0] != '\0' && strlen(linesection) > 1) { - section = INI_SectionType(linesection, section); - continue; // next line - } - // Check word only when in a section - if (section != INI_NONE) { - INI_Word(linefeed, lineword); - wordtype = INI_WordType(lineword, section); - - // WEAPON - if (section == INI_WEAPON) { - // 30/07/04 Josh - // Code optimization using a case instead of series of IF THEN - switch (wordtype) { - - case WORD_PRIWEAPON: - pBot->ipFavoPriWeapon = INI_WordValueINT(linefeed); - break; - case WORD_SECWEAPON: - pBot->ipFavoSecWeapon = INI_WordValueINT(linefeed); - break; - case WORD_GRENADE: - pBot->ipBuyGrenade = INI_WordValueINT(linefeed); - break; - case WORD_SAVEFORWEAP: - pBot->ipSaveForWeapon = INI_WordValueINT(linefeed); - break; - case WORD_FLASHBANG: - pBot->ipBuyFlashBang = INI_WordValueINT(linefeed); - break; - case WORD_SMOKEGREN: - pBot->ipBuySmokeGren = INI_WordValueINT(linefeed); - break; - case WORD_DEFUSEKIT: - pBot->ipBuyDefuseKit = INI_WordValueINT(linefeed); - break; - case WORD_ARMOUR: - pBot->ipBuyArmour = INI_WordValueINT(linefeed); - break; - } - } - // SKILL - if (section == INI_SKILL) { - switch (wordtype) { - case WORD_MINREACTTIME: - pBot->fpMinReactTime = INI_WordValueFLOAT(linefeed); - break; - case WORD_MAXREACTTIME: - pBot->fpMaxReactTime = INI_WordValueFLOAT(linefeed); - break; - case WORD_XOFFSET: - pBot->fpXOffset = INI_WordValueFLOAT(linefeed); - break; - case WORD_YOFFSET: - pBot->fpYOffset = INI_WordValueFLOAT(linefeed); - break; - case WORD_ZOFFSET: - pBot->fpZOffset = INI_WordValueFLOAT(linefeed); - break; - - } - // default we override bot skill with personality skill - if (Game.iOverrideBotSkill == GAME_YES) - if (wordtype == WORD_BOTSKILL) - pBot->bot_skill = INI_WordValueINT(linefeed); - } - // GAME - if (section == INI_GAME) { - if (wordtype == WORD_HOSTAGERATE) - pBot->ipHostage = INI_WordValueINT(linefeed); - if (wordtype == WORD_BOMBSPOTRATE) - pBot->ipBombspot = INI_WordValueINT(linefeed); - if (wordtype == WORD_RANDOMRATE) - pBot->ipRandom = INI_WordValueINT(linefeed); - if (wordtype == WORD_DROPPEDBOMB) - pBot->ipDroppedBomb = INI_WordValueINT(linefeed); - } - // RADIO - if (section == INI_RADIO) { - if (wordtype == WORD_REPLYRADIO) - pBot->ipReplyToRadio = INI_WordValueINT(linefeed); - if (wordtype == WORD_CREATERADIO) - pBot->ipCreateRadio = INI_WordValueINT(linefeed); - } - // TEAM - if (section == INI_TEAM) { - if (wordtype == WORD_HELPTEAM) - pBot->ipHelpTeammate = INI_WordValueINT(linefeed); - } - // PERSON - if (section == INI_PERSON) { - if (wordtype == WORD_TURNSPEED) - pBot->ipTurnSpeed = INI_WordValueINT(linefeed); - if (wordtype == WORD_WALKKNIFE) - pBot->ipWalkWithKnife = INI_WordValueINT(linefeed); - if (wordtype == WORD_FEARRATE) - pBot->ipFearRate = INI_WordValueINT(linefeed); - if (wordtype == WORD_HEARRATE) - pBot->ipHearRate = INI_WordValueINT(linefeed); - if (wordtype == WORD_CHATRATE) - pBot->ipChatRate = INI_WordValueINT(linefeed); - if (wordtype == WORD_CAMPRATE) - pBot->ipCampRate = INI_WordValueINT(linefeed); - } - } - } - fclose(stream); - } - // When we end up here, there is NO file? - else { - // Create new variables and save them into file. - - // Buy preferences - pBot->ipFavoPriWeapon = -1; - pBot->ipFavoSecWeapon = -1; - pBot->ipBuyFlashBang = RANDOM_LONG(20, 80); - pBot->ipBuyGrenade = RANDOM_LONG(20, 80); - pBot->ipBuySmokeGren = RANDOM_LONG(20, 80); - pBot->ipBuyDefuseKit = RANDOM_LONG(20, 80); - pBot->ipDroppedBomb = RANDOM_LONG(20, 80); - pBot->ipSaveForWeapon = RANDOM_LONG(0, 30); - pBot->ipBuyArmour = RANDOM_LONG(30, 100); - pBot->ipFearRate = RANDOM_LONG(20, 60); - - - // Skill, everything but botskill can change. - - // Determine reaction time based upon botskill here - float fMinReact = 0.0, fMaxReact; - if (pBot->bot_skill == 0) - fMinReact = 0.0; - else - //30.8.04 redefined by frashman - // fMinReact = RANDOM_FLOAT (0.05, (pBot->bot_skill / 10)); - fMinReact = - RANDOM_FLOAT((pBot->bot_skill / 20) + 0.05, - (pBot->bot_skill / 5) + 0.05); - - fMaxReact = fMinReact + RANDOM_FLOAT(0.05, 0.2); - - // SET them - pBot->fpMinReactTime = fMinReact; - pBot->fpMaxReactTime = fMaxReact; - - // Set Offsets (note, they are extra upon current aiming code) - // 30.8.04 redefined by frashman - // float fOffset = RANDOM_FLOAT ((pBot->bot_skill / 5), (pBot->bot_skill / 2)); - float fOffset = RANDOM_FLOAT((pBot->bot_skill / 5) + 0.05, - (pBot->bot_skill / 2) + 0.05); - - // SET - pBot->fpXOffset = pBot->fpYOffset = pBot->fpZOffset = fOffset; - - // Team - pBot->ipHelpTeammate = RANDOM_LONG(10, 70); - - // Game - pBot->ipHostage = RANDOM_LONG(25, 70); - pBot->ipBombspot = RANDOM_LONG(25, 70); - pBot->ipRandom = RANDOM_LONG(25, 70); - - // Radio - pBot->ipReplyToRadio = RANDOM_LONG(10, 60); - pBot->ipCreateRadio = RANDOM_LONG(10, 60); - pBot->ipHearRate = RANDOM_LONG(10, 60); - - // Person - pBot->ipTurnSpeed = RANDOM_LONG(20, 40); - pBot->ipCampRate = RANDOM_LONG(0, 60); - pBot->ipChatRate = RANDOM_LONG(0, 30); - pBot->ipWalkWithKnife = RANDOM_LONG(0, 60); - - // SAVE TO DISK: - char dirname[256]; - char filename[256]; - - // Set Directory name - if (mod_id == CSTRIKE_DLL) - strcpy(dirname, "data/cstrike/bots/"); - - strcat(dirname, cBotName); - strcat(dirname, ".ini"); - - // writes whole path into "filename", Linux compatible - UTIL_BuildFileNameRB(dirname, filename); - - FILE *rbl; - - // Only save if lock type is < 1 - rbl = fopen(filename, "w+t"); - - // Created file - if (rbl != NULL) { - fprintf(rbl, "; RealBot\n"); - fprintf(rbl, "; \n"); - fprintf(rbl, - "; This personality is created with random values. You may\n; change this file to create your own personality.\n\n"); - - // WEAPON - fprintf(rbl, "[WEAPON]\n"); - fprintf(rbl, "PrimaryWeapon=%d\n", pBot->ipFavoPriWeapon); - fprintf(rbl, "SecondaryWeapon=%d\n", pBot->ipFavoSecWeapon); - fprintf(rbl, "SaveForWeapon=%d\n", pBot->ipSaveForWeapon); - fprintf(rbl, "Grenade=%d\n", pBot->ipBuyGrenade); - fprintf(rbl, "Flashbang=%d\n", pBot->ipBuyFlashBang); - fprintf(rbl, "SmokeGrenade=%d\n", pBot->ipBuySmokeGren); - fprintf(rbl, "DefuseKit=%d\n", pBot->ipBuyDefuseKit); - fprintf(rbl, "Armour=%d\n", pBot->ipBuyArmour); - fprintf(rbl, "\n"); - - - // SKILL - fprintf(rbl, "[SKILL]\n"); - fprintf(rbl, "XOffset=%f\n", pBot->fpXOffset); - fprintf(rbl, "YOffset=%f\n", pBot->fpYOffset); - fprintf(rbl, "ZOffset=%f\n", pBot->fpZOffset); - fprintf(rbl, "BotSkill=%d\n", pBot->bot_skill); - fprintf(rbl, "MaxReactionTime=%f\n", pBot->fpMaxReactTime); - fprintf(rbl, "MinReactionTime=%f\n", pBot->fpMinReactTime); - fprintf(rbl, "\n"); - - // GAME - fprintf(rbl, "[GAME]\n"); - fprintf(rbl, "Hostage=%d\n", pBot->ipHostage); - fprintf(rbl, "BombSpot=%d\n", pBot->ipBombspot); - fprintf(rbl, "DroppedBomb=%d\n", pBot->ipDroppedBomb); - fprintf(rbl, "Random=%d\n", pBot->ipRandom); - fprintf(rbl, "\n"); - - // RADIO - fprintf(rbl, "[RADIO]\n"); - fprintf(rbl, "Reply=%d\n", pBot->ipReplyToRadio); - fprintf(rbl, "Create=%d\n", pBot->ipCreateRadio); - fprintf(rbl, "\n"); - - // TEAM - fprintf(rbl, "[TEAM]\n"); - fprintf(rbl, "HelpTeammate=%d\n", pBot->ipHelpTeammate); - fprintf(rbl, "\n"); - - // PERSON - fprintf(rbl, "[PERSON]\n"); - fprintf(rbl, "Turnspeed=%d\n", pBot->ipTurnSpeed); - fprintf(rbl, "WalkWithKnife=%d\n", pBot->ipWalkWithKnife); - fprintf(rbl, "HearRate=%d\n", pBot->ipHearRate); - fprintf(rbl, "FearRate=%d\n", pBot->ipFearRate); - fprintf(rbl, "ChatRate=%d\n", pBot->ipChatRate); - fprintf(rbl, "CampRate=%d\n", pBot->ipCampRate); - fprintf(rbl, "\n"); - - // Close file - fclose(rbl); - } - } +void INI_PARSE_BOTS(char cBotName[33], cBot* pBot) { + /* + Revisited: 02/07/05 - Stefan + Last bug/issue report: + - seems to overwrite personality file ? (loading does not work?) + RESULT: There is no bug. + - removed any messages sent by this function. + */ + + FILE* stream; + int section = INI_NONE; + + char dirname[256]; + char filename[256]; + + // Set Directory name + if (mod_id == CSTRIKE_DLL) + std::strcpy(dirname, "data/cstrike/bots/"); + + //strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, cBotName); + std::strcat(dirname, ".ini"); + + // writes whole path into "filename", Linux compatible + UTIL_BuildFileNameRB(dirname, filename); + + // we open the file here! + if ((stream = std::fopen(filename, "r+t")) != nullptr) { + // infinite loop baby + while (!feof(stream)) { + char linesection[30]; + char linefeed[80]; + INI_Sentence(stream, linefeed); + + // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary + // character (which is "//", ";" or "#"), or an empty line, then skip it + if (linefeed[0] == ';' || + linefeed[0] == '#' || + (linefeed[0] == '/' && linefeed[1] == '/') || + linefeed[0] == '\n' || linefeed[0] == '\0') + continue; // Skip + + // Every line is checked for a new section. + INI_Section(linefeed, linesection); + + if (linesection[0] != '\0' && std::strlen(linesection) > 1) { + section = INI_SectionType(linesection, section); + continue; // next line + } + // Check word only when in a section + if (section != INI_NONE) { + char lineword[25]; + INI_Word(linefeed, lineword); + const int wordtype = INI_WordType(lineword, section); + + // WEAPON + if (section == INI_WEAPON) { + // 30/07/04 Josh + // Code optimization using a case instead of series of IF THEN + switch (wordtype) { + case WORD_PRIWEAPON: + pBot->ipFavoPriWeapon = INI_WordValueINT(linefeed); + break; + case WORD_SECWEAPON: + pBot->ipFavoSecWeapon = INI_WordValueINT(linefeed); + break; + case WORD_GRENADE: + pBot->ipBuyGrenade = INI_WordValueINT(linefeed); + break; + case WORD_SAVEFORWEAP: + pBot->ipSaveForWeapon = INI_WordValueINT(linefeed); + break; + case WORD_FLASHBANG: + pBot->ipBuyFlashBang = INI_WordValueINT(linefeed); + break; + case WORD_SMOKEGREN: + pBot->ipBuySmokeGren = INI_WordValueINT(linefeed); + break; + case WORD_DEFUSEKIT: + pBot->ipBuyDefuseKit = INI_WordValueINT(linefeed); + break; + case WORD_ARMOUR: + pBot->ipBuyArmour = INI_WordValueINT(linefeed); + break; + } + } + // SKILL + if (section == INI_SKILL) { + switch (wordtype) { + case WORD_MINREACTTIME: + pBot->fpMinReactTime = INI_WordValueFLOAT(linefeed); + break; + case WORD_MAXREACTTIME: + pBot->fpMaxReactTime = INI_WordValueFLOAT(linefeed); + break; + case WORD_XOFFSET: + pBot->fpXOffset = INI_WordValueFLOAT(linefeed); + break; + case WORD_YOFFSET: + pBot->fpYOffset = INI_WordValueFLOAT(linefeed); + break; + case WORD_ZOFFSET: + pBot->fpZOffset = INI_WordValueFLOAT(linefeed); + break; + } + // default we override bot skill with personality skill + if (Game.iOverrideBotSkill == GAME_YES) + if (wordtype == WORD_BOTSKILL) + pBot->bot_skill = INI_WordValueINT(linefeed); + } + // GAME + if (section == INI_GAME) { + if (wordtype == WORD_HOSTAGERATE) + pBot->ipHostage = INI_WordValueINT(linefeed); + if (wordtype == WORD_BOMBSPOTRATE) + pBot->ipBombspot = INI_WordValueINT(linefeed); + if (wordtype == WORD_RANDOMRATE) + pBot->ipRandom = INI_WordValueINT(linefeed); + if (wordtype == WORD_DROPPEDBOMB) + pBot->ipDroppedBomb = INI_WordValueINT(linefeed); + } + // RADIO + if (section == INI_RADIO) { + if (wordtype == WORD_REPLYRADIO) + pBot->ipReplyToRadio = INI_WordValueINT(linefeed); + if (wordtype == WORD_CREATERADIO) + pBot->ipCreateRadio = INI_WordValueINT(linefeed); + } + // TEAM + if (section == INI_TEAM) { + if (wordtype == WORD_HELPTEAM) + pBot->ipHelpTeammate = INI_WordValueINT(linefeed); + } + // PERSON + if (section == INI_PERSON) { + if (wordtype == WORD_TURNSPEED) + pBot->ipTurnSpeed = INI_WordValueINT(linefeed); + if (wordtype == WORD_WALKKNIFE) + pBot->ipWalkWithKnife = INI_WordValueINT(linefeed); + if (wordtype == WORD_FEARRATE) + pBot->ipFearRate = INI_WordValueINT(linefeed); + if (wordtype == WORD_HEARRATE) + pBot->ipHearRate = INI_WordValueINT(linefeed); + if (wordtype == WORD_CHATRATE) + pBot->ipChatRate = INI_WordValueINT(linefeed); + if (wordtype == WORD_CAMPRATE) + pBot->ipCampRate = INI_WordValueINT(linefeed); + } + } + } + std::fclose(stream); + } + // When we end up here, there is NO file? + else { + // Create new variables and save them into file. + + // Buy preferences + pBot->ipFavoPriWeapon = -1; + pBot->ipFavoSecWeapon = -1; + pBot->ipBuyFlashBang = RANDOM_LONG(20, 80); + pBot->ipBuyGrenade = RANDOM_LONG(20, 80); + pBot->ipBuySmokeGren = RANDOM_LONG(20, 80); + pBot->ipBuyDefuseKit = RANDOM_LONG(20, 80); + pBot->ipDroppedBomb = RANDOM_LONG(20, 80); + pBot->ipSaveForWeapon = RANDOM_LONG(0, 20); + pBot->ipBuyArmour = RANDOM_LONG(30, 100); + pBot->ipFearRate = RANDOM_LONG(20, 60); + + // Skill, everything but botskill can change. + + // Determine reaction time based upon botskill here + float fMinReact; + if (pBot->bot_skill == 0) + fMinReact = 0.0f; + else + //30.8.04 redefined by frashman + // fMinReact = RANDOM_FLOAT (0.05, (pBot->bot_skill / 10)); + // Reaction Time delay added for realistic gameplay [APG]RoboCop[CL] + fMinReact = + RANDOM_FLOAT(pBot->bot_skill / 20.0f + 0.3f, + pBot->bot_skill / 5.0f + 0.3f); + + const float fMaxReact = fMinReact + RANDOM_FLOAT(0.2f, 0.4f); + + // SET them + pBot->fpMinReactTime = fMinReact; + pBot->fpMaxReactTime = fMaxReact; + + // Set Offsets (note, they are extra upon current aiming code) + // 30.8.04 redefined by frashman + // float fOffset = RANDOM_FLOAT ((pBot->bot_skill / 5), (pBot->bot_skill / 2)); + const float fOffset = RANDOM_FLOAT(pBot->bot_skill / 5.0f + 0.05f, + pBot->bot_skill / 2.0f + 0.05f); + + // SET + pBot->fpXOffset = pBot->fpYOffset = pBot->fpZOffset = fOffset; + + // Team + pBot->ipHelpTeammate = RANDOM_LONG(40, 80); + + // Game + pBot->ipHostage = RANDOM_LONG(25, 70); + pBot->ipBombspot = RANDOM_LONG(25, 70); + pBot->ipRandom = RANDOM_LONG(40, 80); + + // Radio + pBot->ipReplyToRadio = RANDOM_LONG(10, 20); + pBot->ipCreateRadio = RANDOM_LONG(10, 20); + pBot->ipHearRate = RANDOM_LONG(20, 60); + + // Person + pBot->ipTurnSpeed = RANDOM_LONG(20, 40); + pBot->ipCampRate = RANDOM_LONG(0, 40); + pBot->ipChatRate = RANDOM_LONG(10, 20); + pBot->ipWalkWithKnife = RANDOM_LONG(0, 30); + + // SAVE TO DISK: + //char dirname[256]; + //char filename[256]; + + // Set Directory name + if (mod_id == CSTRIKE_DLL) + std::strcpy(dirname, "data/cstrike/bots/"); + + std::strcat(dirname, cBotName); + std::strcat(dirname, ".ini"); + + // writes whole path into "filename", Linux compatible + UTIL_BuildFileNameRB(dirname, filename); + + // Only save if lock type is < 1 + FILE* rbl = std::fopen(filename, "w+t"); + + // Created file + if (rbl != nullptr) { + std::fprintf(rbl, "; RealBot\n"); + std::fprintf(rbl, "; \n"); + std::fprintf(rbl, + "; This personality is created with random values. You may\n; change this file to create your own personality.\n\n"); + + // WEAPON + std::fprintf(rbl, "[WEAPON]\n"); + std::fprintf(rbl, "PrimaryWeapon=%d\n", pBot->ipFavoPriWeapon); + std::fprintf(rbl, "SecondaryWeapon=%d\n", pBot->ipFavoSecWeapon); + std::fprintf(rbl, "SaveForWeapon=%d\n", pBot->ipSaveForWeapon); + std::fprintf(rbl, "Grenade=%d\n", pBot->ipBuyGrenade); + std::fprintf(rbl, "Flashbang=%d\n", pBot->ipBuyFlashBang); + std::fprintf(rbl, "SmokeGrenade=%d\n", pBot->ipBuySmokeGren); + std::fprintf(rbl, "DefuseKit=%d\n", pBot->ipBuyDefuseKit); + std::fprintf(rbl, "Armour=%d\n", pBot->ipBuyArmour); + std::fprintf(rbl, "\n"); + + // SKILL + std::fprintf(rbl, "[SKILL]\n"); + std::fprintf(rbl, "XOffset=%f\n", pBot->fpXOffset); + std::fprintf(rbl, "YOffset=%f\n", pBot->fpYOffset); + std::fprintf(rbl, "ZOffset=%f\n", pBot->fpZOffset); + std::fprintf(rbl, "BotSkill=%d\n", pBot->bot_skill); + std::fprintf(rbl, "MaxReactionTime=%f\n", pBot->fpMaxReactTime); + std::fprintf(rbl, "MinReactionTime=%f\n", pBot->fpMinReactTime); + std::fprintf(rbl, "\n"); + + // GAME + std::fprintf(rbl, "[GAME]\n"); + std::fprintf(rbl, "Hostage=%d\n", pBot->ipHostage); + std::fprintf(rbl, "BombSpot=%d\n", pBot->ipBombspot); + std::fprintf(rbl, "DroppedBomb=%d\n", pBot->ipDroppedBomb); + std::fprintf(rbl, "Random=%d\n", pBot->ipRandom); + std::fprintf(rbl, "\n"); + + // RADIO + std::fprintf(rbl, "[RADIO]\n"); + std::fprintf(rbl, "Reply=%d\n", pBot->ipReplyToRadio); + std::fprintf(rbl, "Create=%d\n", pBot->ipCreateRadio); + std::fprintf(rbl, "\n"); + + // TEAM + std::fprintf(rbl, "[TEAM]\n"); + std::fprintf(rbl, "HelpTeammate=%d\n", pBot->ipHelpTeammate); + std::fprintf(rbl, "\n"); + + // PERSON + std::fprintf(rbl, "[PERSON]\n"); + std::fprintf(rbl, "Turnspeed=%d\n", pBot->ipTurnSpeed); + std::fprintf(rbl, "WalkWithKnife=%d\n", pBot->ipWalkWithKnife); + std::fprintf(rbl, "HearRate=%d\n", pBot->ipHearRate); + std::fprintf(rbl, "FearRate=%d\n", pBot->ipFearRate); + std::fprintf(rbl, "ChatRate=%d\n", pBot->ipChatRate); + std::fprintf(rbl, "CampRate=%d\n", pBot->ipCampRate); + std::fprintf(rbl, "\n"); + + // Close file + std::fclose(rbl); + } + } } // INI parsing /** @@ -1074,93 +961,90 @@ void INI_PARSE_BOTS(char cBotName[33], cBot * pBot) { * The buytable.ini is not in the source file, but in the binary downloads of REALBOT. */ void INI_PARSE_BUYTABLE() { - FILE *stream; - int section = INI_NONE; - int wordtype = WORD_NONE; - int prev_section = section; - int weapon_id = -1; - - char dirname[256]; - char filename[256]; - - // Set Directory name - if (mod_id == CSTRIKE_DLL) - strcpy(dirname, "data/cstrike/"); - - //strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, "buytable.ini"); - - // writes whole path into "filename", Linux compatible - UTIL_BuildFileNameRB(dirname, filename); - - // clear out weapon table completely - for (int cl = 0; cl < 32; cl++) { - weapons_table[cl].iId = -1; - weapons_table[cl].price = -1; - weapons_table[cl].priority = -1; - weapons_table[cl].iIdIndex = -1; - } - - if ((stream = fopen(filename, "r+t")) != NULL) { - char linefeed[80]; - char lineword[25]; - char linesection[30]; - - while (!feof(stream)) { - INI_Sentence(stream, linefeed); - - // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary - // character (which is "//", ";" or "#"), or an empty line, then skip it - if (linefeed[0] == ';' || - linefeed[0] == '#' || - (linefeed[0] == '/' && linefeed[1] == '/') || - linefeed[0] == '\n' || linefeed[0] == '\0') - continue; // Skip - - wordtype = WORD_NONE; - - // Every line is checked for a new section. - INI_Section(linefeed, linesection); - - // Found a new section - if (linesection[0] != '\0' && strlen(linesection) > 1) { - section = INI_SectionType_BUYTABLE(linesection, section); - // Check if its the same as the previous section - if (section != prev_section) { - weapon_id++; // new weapon - if (weapon_id > MAX_WEAPONS - 1) { - // done - fclose(stream); - break; // out of the loop - } - - weapons_table[weapon_id].iId = section; - weapons_table[section].iIdIndex = weapon_id; - } - - prev_section = section; // Equal the sections now - continue; // next line - } - - // Check word only when in a section - if (section != INI_NONE) { - INI_Word(linefeed, lineword); - wordtype = INI_WordType(lineword, section); - if (wordtype != WORD_NONE) { - if (wordtype == WORD_PRICE) { - //BotDebug("Loading price\n"); - weapons_table[weapon_id].price = - INI_WordValueINT(linefeed); - } else if (wordtype == WORD_PRIORITY) { - //BotDebug("Loading priority\n"); - weapons_table[weapons_table[weapon_id].iId].priority = - INI_WordValueINT(linefeed); - } - } - } - } - fclose(stream); - } + FILE* stream; + int section = INI_NONE; + int prev_section = section; + int weapon_id = -1; + + char dirname[256]; + char filename[256]; + + // Set Directory name + if (mod_id == CSTRIKE_DLL) + std::strcpy(dirname, "data/cstrike/"); + + //strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, "buytable.ini"); + + // writes whole path into "filename", Linux compatible + UTIL_BuildFileNameRB(dirname, filename); + + // clear out weapon table completely + for (weapon_price_table& cl : weapons_table) + { + cl.iId = -1; + cl.price = -1; + cl.priority = -1; + cl.iIdIndex = -1; + } + + if ((stream = std::fopen(filename, "r+t")) != nullptr) { + while (!feof(stream)) { + char linesection[30]; + char linefeed[80]; + INI_Sentence(stream, linefeed); + + // Linefeed contains a string of 1 sentence. Whenever the first character is a commentary + // character (which is "//", ";" or "#"), or an empty line, then skip it + if (linefeed[0] == ';' || + linefeed[0] == '#' || + (linefeed[0] == '/' && linefeed[1] == '/') || + linefeed[0] == '\n' || linefeed[0] == '\0') + continue; // Skip + + // Every line is checked for a new section. + INI_Section(linefeed, linesection); + + // Found a new section + if (linesection[0] != '\0' && std::strlen(linesection) > 1) { + section = INI_SectionType_BUYTABLE(linesection, section); + // Check if its the same as the previous section + if (section != prev_section) { + weapon_id++; // new weapon + if (weapon_id > MAX_WEAPONS - 1) { + // done + //std::fclose(stream); + break; // out of the loop + } + + weapons_table[weapon_id].iId = section; + weapons_table[section].iIdIndex = weapon_id; + } + + prev_section = section; // Equal the sections now + continue; // next line + } + + // Check word only when in a section + if (section != INI_NONE) { + char lineword[25]; + INI_Word(linefeed, lineword); + const int wordtype = INI_WordType(lineword, section); + if (wordtype != WORD_NONE) { + if (wordtype == WORD_PRICE) { + //BotDebug("Loading price\n"); + weapons_table[weapon_id].price = + INI_WordValueINT(linefeed); + } else if (wordtype == WORD_PRIORITY) { + //BotDebug("Loading priority\n"); + weapons_table[weapons_table[weapon_id].iId].priority = + INI_WordValueINT(linefeed); + } + } + } + } + std::fclose(stream); + } } // INI parsing // $Log: IniParser.cpp,v $ @@ -1235,4 +1119,4 @@ void INI_PARSE_BUYTABLE() { // - Log() works properly now // - Clearing in dll.cpp of reallog.txt at dll init // - Logging works now, add REALBOT_PRINT() at every point you want to log something. -// +// \ No newline at end of file diff --git a/IniParser.h b/IniParser.h index 3e0284f..4a661ee 100644 --- a/IniParser.h +++ b/IniParser.h @@ -33,88 +33,104 @@ **/ // Sections -#define INI_NONE -1 -#define INI_SKILL 0 // Bot skill -#define INI_WEAPON 1 // Bot weapon preference -#define INI_GAME 2 // Bot general game behaviour -#define INI_RADIO 3 // Bot radio behaviour -#define INI_TEAM 4 // Bot team behaviour -#define INI_PERSON 5 // Bot person itself - -#define INI_AREA 10 -#define INI_BLOCK 11 -#define INI_DEATHS 12 -#define INI_WELCOME 13 +#ifndef INIPARSER_H +#define INIPARSER_H + +enum : std::int8_t +{ + INI_NONE = (-1), + INI_SKILL = 0, // Bot skill + INI_WEAPON = 1, // Bot weapon preference + INI_GAME = 2, // Bot general game behaviour + INI_RADIO = 3, // Bot radio behaviour + INI_TEAM = 4, // Bot team behaviour + INI_PERSON = 5 // Bot person itself +}; + +enum : std::uint8_t +{ + INI_AREA = 10, + INI_BLOCK = 11, + INI_DEATHS = 12, + INI_WELCOME = 13 +}; // 'Weapon Sections' are the same as WEAPON ID in Counter-Strike. // NOTE: For weapon_buy_table.iId! // 'Words' -#define WORD_NONE -1 -#define WORD_WALK 0 -#define WORD_RUN 1 -#define WORD_SHOOT 2 -#define WORD_WAIT 3 -#define WORD_RADIO 4 +enum : std::int8_t +{ + WORD_NONE = (-1), + WORD_WALK = 0, + WORD_RUN = 1, + WORD_SHOOT = 2, + WORD_WAIT = 3, + WORD_RADIO = 4 +}; // BOTPERSONALITY.INI words -#define WORD_PRIWEAPON 31 -#define WORD_SECWEAPON 32 -#define WORD_SAVEFORWEAP 33 -#define WORD_GRENADE 34 -#define WORD_FLASHBANG 35 -#define WORD_SMOKEGREN 36 -#define WORD_DEFUSEKIT 37 -#define WORD_ARMOUR 54 - -#define WORD_XOFFSET 38 -#define WORD_YOFFSET 39 -#define WORD_ZOFFSET 40 -#define WORD_BOTSKILL 41 -#define WORD_MAXREACTTIME 42 -#define WORD_MINREACTTIME 43 -#define WORD_TURNSPEED 44 - -#define WORD_HOSTAGERATE 45 -#define WORD_BOMBSPOTRATE 46 -#define WORD_RANDOMRATE 47 - -#define WORD_REPLYRADIO 48 -#define WORD_CREATERADIO 49 - -#define WORD_HELPTEAM 50 - -#define WORD_CAMPRATE 51 -#define WORD_CHATRATE 52 -#define WORD_WALKKNIFE 53 - -#define WORD_FEARRATE 55 -#define WORD_HEARRATE 56 - -#define WORD_DROPPEDBOMB 57 +enum : std::uint8_t +{ + WORD_PRIWEAPON = 31, + WORD_SECWEAPON = 32, + WORD_SAVEFORWEAP = 33, + WORD_GRENADE = 34, + WORD_FLASHBANG = 35, + WORD_SMOKEGREN = 36, + WORD_DEFUSEKIT = 37, + WORD_ARMOUR = 54, + WORD_XOFFSET = 38, + WORD_YOFFSET = 39, + WORD_ZOFFSET = 40, + WORD_BOTSKILL = 41, + WORD_MAXREACTTIME = 42, + WORD_MINREACTTIME = 43, + WORD_TURNSPEED = 44, + + WORD_HOSTAGERATE = 45, + WORD_BOMBSPOTRATE = 46, + WORD_RANDOMRATE = 47, + + WORD_REPLYRADIO = 48, + WORD_CREATERADIO = 49, + + WORD_HELPTEAM = 50, + + WORD_CAMPRATE = 51, + WORD_CHATRATE = 52, + WORD_WALKKNIFE = 53, + + WORD_FEARRATE = 55, + WORD_HEARRATE = 56, + + WORD_DROPPEDBOMB = 57, // AREA SHIT -#define WORD_AREAX 60 -#define WORD_AREAY 61 -#define WORD_AREAZ 62 + WORD_AREAX = 60, + WORD_AREAY = 61, + WORD_AREAZ = 62, // CHAT -#define WORD_SENTENCE 67 -#define WORD_WORD 68 + WORD_SENTENCE = 67, + WORD_WORD = 68, // BUYTABLE.INI Words (arguments per weapon) -#define WORD_PRIORITY 5 -#define WORD_PRICE 6 -#define WORD_MAXAMMO1 88 -#define WORD_MAXAMMO2 89 -#define WORD_ISLOT 90 -#define WORD_IPOSITION 91 -#define WORD_IFLAGS 92 -#define WORD_INDEX1 93 -#define WORD_INDEX2 94 + WORD_PRIORITY = 5, + WORD_PRICE = 6, + WORD_MAXAMMO1 = 88, + WORD_MAXAMMO2 = 89, + WORD_ISLOT = 90, + WORD_IPOSITION = 91, + WORD_IFLAGS = 92, + WORD_INDEX1 = 93, + WORD_INDEX2 = 94 +}; void INI_PARSE_BOTS(char cBotName[33], cBot * pBot); void INI_PARSE_BUYTABLE(); void INI_PARSE_IAD(); void INI_PARSE_CHATFILE(); + +#endif // INIPARSER_H \ No newline at end of file diff --git a/Makefile b/Makefile index 8f63e21..1431dc9 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,14 @@ CPP = g++ - ARCHFLAG = -m32 -METAMOD_SRCDIR = ./dependencies/metamod-hl1/metamod -HLSDK_BASEDIR = ./dependencies/hlsdk +META_DIR = ./dependencies/metamod-hl1/metamod +HLSDK_DIR = ./dependencies/hlsdk BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -Dlinux=1 -CPPFLAGS = ${BASEFLAGS} ${ARCHFLAG} -O2 -w -I"${METAMOD_SRCDIR}" -I"${HLSDK_BASEDIR}/common" -I"${HLSDK_BASEDIR}/dlls" -I"${HLSDK_BASEDIR}/engine" -I"${HLSDK_BASEDIR}/pm_shared" -I"${HLSDK_BASEDIR}/public" +CPPFLAGS = ${BASEFLAGS} ${ARCHFLAG} -O2 -mtune=generic -march=i686 -mmmx -msse -msse2 -O2 -mfpmath=sse -s \ + -Wno-write-strings -Wno-attributes -std=gnu++14 -static-libstdc++ -shared-libgcc \ + -I"${META_DIR}" -I"${HLSDK_DIR}/common" -I"${HLSDK_DIR}/dlls" \ + -I"${HLSDK_DIR}/engine" -I"${HLSDK_DIR}/pm_shared" -I"${HLSDK_DIR}/public" OBJ = NodeMachine.o \ bot.o \ @@ -30,7 +32,7 @@ ifeq ($(UNAME_S),Darwin) SO_SUFFIX = dylib endif -realbot_mm_i386.${SO_SUFFIX}: ${OBJ} +realbot_mm.${SO_SUFFIX}: ${OBJ} ${CPP} ${ARCHFLAG} -fPIC -shared -o $@ ${OBJ} -ldl mkdir -p Release mv $@ ${OBJ} Release diff --git a/Makefile.debug b/Makefile.debug index 76c7fdb..557630d 100644 --- a/Makefile.debug +++ b/Makefile.debug @@ -5,7 +5,7 @@ ARCHFLAG = -m32 -g METAMOD_SRCDIR = ./dependencies/metamod-hl1/metamod HLSDK_BASEDIR = ./dependencies/hlsdk -BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -Dlinux=1 -g +BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -Dlinux=1 -ggdb3 CPPFLAGS = ${BASEFLAGS} ${ARCHFLAG} -O0 -w -I"${METAMOD_SRCDIR}" -I"${HLSDK_BASEDIR}/common" -I"${HLSDK_BASEDIR}/dlls" -I"${HLSDK_BASEDIR}/engine" -I"${HLSDK_BASEDIR}/pm_shared" -I"${HLSDK_BASEDIR}/public" OBJ = NodeMachine.o \ @@ -30,7 +30,7 @@ ifeq ($(UNAME_S),Darwin) SO_SUFFIX = dylib endif -realbot_mm_i386.${SO_SUFFIX}: ${OBJ} +realbot_mm.${SO_SUFFIX}: ${OBJ} ${CPP} ${ARCHFLAG} -fPIC -shared -o $@ ${OBJ} -ldl mkdir -p Release mv $@ ${OBJ} Release diff --git a/NodeDataTypes.h b/NodeDataTypes.h index dab5cd8..5b00242 100644 --- a/NodeDataTypes.h +++ b/NodeDataTypes.h @@ -6,7 +6,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,178 +18,235 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. * **/ -/** - * NODE MACHINE data types - * COPYRIGHTED BY STEFAN HENDRIKS (C) - **/ + /** + * NODE MACHINE data types + * COPYRIGHTED BY STEFAN HENDRIKS (C) + **/ #ifndef NODEDATATYPES_H #define NODEDATATYPES_H +#include +#include + // player sizes for path_connection_walkable -#define MAX_JUMPHEIGHT 60 // confirmed // 45 without crouching -#define MAX_FALLHEIGHT 130 // not confirmed (200 is to high, adjusted) -#define MAX_STAIRHEIGHT 18 // confirmed -#define HEAD_HEIGHT 72 // confirmed -#define ORIGIN_HEIGHT 36 // confirmed (?) -#define CROUCHED_HEIGHT 37 // confirmed -#define PLAYER_WIDTH 32 // confirmed (?) +enum : std::uint8_t +{ + MAX_JUMPHEIGHT = 60, // confirmed // 45 without crouching + MAX_FALLHEIGHT = 130, // not confirmed (200 is to high, adjusted) + MAX_STAIRHEIGHT = 18, // confirmed + HEAD_HEIGHT = 72, // confirmed + ORIGIN_HEIGHT = 36, // confirmed (?) + CROUCHED_HEIGHT = 37, // confirmed + PLAYER_WIDTH = 32 // confirmed (?) +}; // File version // Version 1.0 -#define FILE_NODE_VER1 1 -#define FILE_EXP_VER1 1 +enum : std::uint8_t +{ + FILE_NODE_VER1 = 1, + FILE_EXP_VER1 = 1 +}; // Version 2.0 -#define FILE_NODE_VER2 2 -#define FILE_EXP_VER2 2 +enum : std::uint8_t +{ + FILE_NODE_VER2 = 2, + FILE_EXP_VER2 = 2 +}; // Node bits (for navigational performance) -#define BIT_LADDER (1 << 0) -#define BIT_WATER (1 << 1) -#define BIT_JUMP (1 << 2) -#define BIT_DUCK (1 << 3) +enum : std::uint8_t +{ + BIT_LADDER = (1 << 0), + BIT_WATER = (1 << 1), + BIT_JUMP = (1 << 2), + BIT_DUCK = (1 << 3), + BIT_DUCKJUMP = (1 << 4) +}; // Path flags -#define PATH_DANGER 39 // Picked a random number here -#define PATH_CONTACT 37 // w0h00 -#define PATH_NONE 32 // - rushing -#define PATH_CAMP 31 // camp path +enum : std::uint8_t +{ + PATH_DANGER = 39, // Picked a random number here + PATH_CONTACT = 37, // w0h00 + PATH_NONE = 32, // - rushing + PATH_CAMP = 31 // camp path +}; // Visibility flags -#define VIS_INVALID 96 // BERKED -#define VIS_UNKNOWN 97 -#define VIS_VISIBLE 98 -#define VIS_BLOCKED 99 +enum : std::uint8_t +{ + VIS_INVALID = 96, // BERKED + VIS_UNKNOWN = 97, + VIS_VISIBLE = 98, + VIS_BLOCKED = 99 +}; // Goal types & info -#define MAX_GOALS 75 +constexpr int MAX_GOALS = 75; // Node types / goal types -#define GOAL_SPAWNCT 1 -#define GOAL_SPAWNT 2 -#define GOAL_BOMBSPOT 3 -#define GOAL_BOMB 4 // updates all the time -#define GOAL_HOSTAGE 5 // updates all the time -#define GOAL_RESCUEZONE 6 // rescue zone (for hostages) -#define GOAL_CONTACT 7 // zones where teams often have contact -#define GOAL_IMPORTANT 8 -#define GOAL_VIP 9 // as_ maps VIP starting point -#define GOAL_VIPSAFETY 10 // as_ maps VIP safety zone -#define GOAL_ESCAPEZONE 11 // es_ maps escape zone -#define GOAL_WEAPON 12 // pre-dropped weapons like in awp_map -#define GOAL_NONE 99 +enum : std::uint8_t +{ + GOAL_SPAWNCT = 1, + GOAL_SPAWNT = 2, + GOAL_BOMBSPOT = 3, + GOAL_BOMB = 4, // updates all the time + GOAL_HOSTAGE = 5, // updates all the time + GOAL_RESCUEZONE = 6, // rescue zone (for hostages) + GOAL_CONTACT = 7, // zones where teams often have contact + GOAL_IMPORTANT = 8, // important goals + GOAL_VIP = 9, // as_ maps VIP starting point + GOAL_VIPSAFETY = 10, // as_ maps VIP safety zone + GOAL_ESCAPEZONE = 11, // es_ maps escape zone + GOAL_WEAPON = 12, // pre-dropped weapons like in awp_map + GOAL_NONE = 99 +}; // Node costs -#define NODE_DANGER 8192 // Value -#define NODE_DANGER_STEP 0.5 // Step to take to get dangerous -#define NODE_DANGER_DIST 512.0 // Distance +enum : std::uint16_t +{ + NODE_DANGER = 8192 // Value +}; + +constexpr float NODE_DANGER_STEP = 0.5f; // Step to take to get dangerous; +constexpr float NODE_DANGER_DIST = 512.0f; // Distance; // Node contact costs -#define NODE_CONTACT 8192 -#define NODE_CONTACT_STEP 0.2 -#define NODE_CONTACT_DIST 128 +enum : std::uint16_t +{ + NODE_CONTACT = 8192 +}; + +constexpr float NODE_CONTACT_STEP = 0.2f; + +enum : std::uint8_t +{ + NODE_CONTACT_DIST = 128 +}; // Node boundries -#define MAX_NODES 4096 -#define MAX_NEIGHBOURS 16 -#define NODE_ZONE 45 +enum : std::uint8_t +{ + NODE_ZONE = 45 // Maybe increase it to 128 or 144 to reduce the amount of excess nodes [APG]RoboCop[CL] +}; + +constexpr int MAX_NODES = 4096; +constexpr int MAX_NEIGHBOURS = 16; // Maybe increase it to 128 or 144 to reduce the amount of excess nodes [APG]RoboCop[CL] + #define MAX_PATH_NODES MAX_NODES // Max troubled node connections we remember -#define MAX_TROUBLE 100 +constexpr int MAX_TROUBLE = 100; // Meridian stuff -#define SIZE_MEREDIAN 256 -#define MAP_MAX_SIZE 16384 -#define MAX_MEREDIANS MAP_MAX_SIZE / SIZE_MEREDIAN // Size of HL map divided by SIZE of a meridian to evenly spread -#define MAX_NODES_IN_MEREDIANS 120 // EVY: higher number, number of nodes per meredian +enum : std::uint16_t +{ + SIZE_MEREDIAN = 256, + MAP_MAX_SIZE = 16384 +}; + +#define MAX_MEREDIANS (MAP_MAX_SIZE / SIZE_MEREDIAN) // Size of HL map divided by SIZE of a meridian to evenly spread +constexpr int MAX_NODES_IN_MEREDIANS = 120; // EVY: higher number, number of nodes per meredian; //#define MAX_NODES_IN_MEREDIANS 80 // (size meredian / zone (~6) times 2 (surface) , rounded to 80 // A* defines OPEN/CLOSED lists -#define OPEN 1 // open, can still re-evaluate -#define CLOSED 2 // closed, do nothing with it -#define AVAILABLE 3 // available, may open +enum : std::uint8_t +{ + OPEN = 1, // open, can still re-evaluate + CLOSED = 2, // closed, do nothing with it + AVAILABLE = 3 // available, may open +}; -const unsigned long g_iMaxVisibilityByte = (MAX_NODES * MAX_NODES) / 8; +constexpr unsigned long g_iMaxVisibilityByte = MAX_NODES * MAX_NODES / 8; // doors (doors.cpp) HLSDK -#define SF_DOOR_ROTATE_Y 0 -#define SF_DOOR_START_OPEN 1 -#define SF_DOOR_ROTATE_BACKWARDS 2 -#define SF_DOOR_PASSABLE 8 -#define SF_DOOR_ONEWAY 16 -#define SF_DOOR_NO_AUTO_RETURN 32 -#define SF_DOOR_ROTATE_Z 64 -#define SF_DOOR_ROTATE_X 128 -#define SF_DOOR_USE_ONLY 256 // door must be opened by player's use button. -#define SF_DOOR_NOMONSTERS 512 // Monster can't open -#define SF_DOOR_SILENT 0x80000000 - +enum : std::uint16_t +{ + SF_DOOR_ROTATE_Y = 0, + SF_DOOR_START_OPEN = 1, + SF_DOOR_ROTATE_BACKWARDS = 2, + SF_DOOR_PASSABLE = 8, + SF_DOOR_ONEWAY = 16, + SF_DOOR_NO_AUTO_RETURN = 32, + SF_DOOR_ROTATE_Z = 64, + SF_DOOR_ROTATE_X = 128, + SF_DOOR_USE_ONLY = 256, // door must be opened by player's use button. + SF_DOOR_NOMONSTERS = 512 // Monster can't open +}; + +constexpr unsigned SF_DOOR_SILENT = 0x80000000; // Player information on map typedef struct { - Vector vPrevPos; // Previous Position - int iNode; // Previous Node + Vector vPrevPos; // Previous Position + int iNode; // Previous Node } - tPlayer; +tPlayer; // Astar Node informaiton -typedef struct { - int state; // OPEN/CLOSED - double cost; // Cost - double danger; - int parent; // Who opened this node? -} - tNodestar; +typedef struct tNodestar { + int state; // OPEN/CLOSED + int parent; // Who opened this node? + float cost; // Cost + double danger; + + // Comparison operator for priority queue + bool operator<(const tNodestar& other) const { + return cost > other.cost; // Note: Use '>' for min-heap (lower cost has higher priority) + } +} tNodestar; // Additional Node Information typedef struct { - float fDanger[2]; // Danger information (0.0 - no danger, 1.0 dangerous). Indexed per team (T/CT) - float fContact[2]; // How many times have contact with enemy (0.0 none, 1.0 , a lot) + float fDanger[2]; // Danger information (0.0 - no danger, 1.0 dangerous). Indexed per team (T/CT) + float fContact[2]; // How many times have contact with enemy (0.0 none, 1.0 , a lot) } - tInfoNode; +tInfoNode; typedef struct { - int iNodes[MAX_NODES_IN_MEREDIANS]; + int iNodes[MAX_NODES_IN_MEREDIANS]; } - tMeredian; +tMeredian; // Trouble connections typedef struct { - int iFrom; // From NODE - int iTo; // To NODE - int iTries; // How many times we had trouble with this connection + int iFrom; // From NODE + int iTo; // To NODE + int iTries; // How many times we had trouble with this connection } - tTrouble; +tTrouble; // Node (stored in RBN file, do not change casually) typedef struct { - Vector origin; // Node origin - int iNeighbour[MAX_NEIGHBOURS]; // Reachable nodes for this node - int iNodeBits; + Vector origin; // Node origin + std::array iNeighbour; // Reachable nodes for this node + int iNodeBits; + int index; } - tNode; +tNode; // Goal Node information typedef struct { - edict_t *pGoalEdict; // edict of goal - int iNode; // index of node attached to it - int iType; // type of goal - int iChecked; // many times checked/visited? - int iBadScore; // bad score for a node (when it seems to be unreachable?) - int index; // the index in the Goals[] array - char name[32]; // name of goal + edict_t* pGoalEdict; // edict of goal + int iNode; // index of node attached to it + int iType; // type of goal + int iChecked; // many times checked/visited? + int iBadScore; // bad score for a node (when it seems to be unreachable?) + int index; // the index in the Goals[] array + char name[32]; // name of goal } - tGoal; +tGoal; #endif // NODEDATATYPES_H diff --git a/NodeMachine.cpp b/NodeMachine.cpp index b65362e..0e6d352 100644 --- a/NodeMachine.cpp +++ b/NodeMachine.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +20,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -32,14 +34,18 @@ * COPYRIGHTED BY STEFAN HENDRIKS (C) 2003-2004 **/ -#include +#include +#include +#include +#include #include #include #include #include // malloc stuff? -#include "stdlib.h" +#include +#include // --- #include "bot.h" @@ -52,9 +58,9 @@ tNodestar astar_list[MAX_NODES]; -const Vector &INVALID_VECTOR = Vector(9999, 9999, 9999); +const Vector& INVALID_VECTOR = Vector(9999, 9999, 9999); -extern edict_t *pHostEdict; +extern edict_t* pHostEdict; extern cGame Game; extern cBot bots[32]; extern int draw_nodepath; @@ -62,72 +68,61 @@ extern int draw_nodepath; //--------------------------------------------------------- //CODE: CHEESEMONSTER -int // BERKED -cNodeMachine::GetVisibilityFromTo(int iFrom, int iTo) { - // prevent negative indexes on iVisChecked below -- BERKED - if (iFrom < 0 || iFrom > MAX_NODES || iTo < 0 || iTo > MAX_NODES) { - return VIS_INVALID; +int cNodeMachine::GetVisibilityFromTo(const int iFrom, const int iTo) const { + if (iFrom < 0 || iFrom >= MAX_NODES || iTo < 0 || iTo >= MAX_NODES) { + rblog("ERROR: Index out of bounds in GetVisibilityFromTo! Returning VIS_BLOCKED\n"); + return VIS_BLOCKED; + } + + if (iVisChecked[iFrom] == 0) { + return VIS_UNKNOWN; } - // -- BY STEFAN -- - if (iVisChecked[iFrom] == 0) - return VIS_UNKNOWN; // we have no clue - // -- END -- // was int // work out the position - long iPosition = (iFrom * MAX_NODES) + iTo; - - long iByte = (int) (iPosition / 8); - unsigned int iBit = iPosition % 8; + const long iPosition = iFrom * MAX_NODES + iTo; + const long iByte = iPosition / 8; + const unsigned int iBit = iPosition % 8; - if (iByte < g_iMaxVisibilityByte) { - // Get the Byte that this is in - unsigned char *ToReturn = (cVisTable + iByte); - // get the bit in the byte - return ((*ToReturn & (1 << iBit)) > 0) ? VIS_VISIBLE : VIS_BLOCKED; // BERKED - } + // Optional assertion + assert(iByte < g_iMaxVisibilityByte); + const unsigned char ToReturn = cVisTable[iByte]; - return VIS_BLOCKED; // BERKED + return (ToReturn & (1 << iBit)) ? VIS_VISIBLE : VIS_BLOCKED; } -void -cNodeMachine::SetVisibilityFromTo(int iFrom, int iTo, bool bVisible) { - // prevent negative indexes on iVisChecked below, fixing SEGV -- BERKED - if (iFrom < 0 || iFrom > MAX_NODES || iTo < 0 || iTo > MAX_NODES) { +void cNodeMachine::SetVisibilityFromTo(const int iFrom, const int iTo, const bool bVisible) { + if (iFrom < 0 || iFrom >= MAX_NODES || iTo < 0 || iTo >= MAX_NODES) { + rblog("ERROR: Index out of bounds in SetVisibilityFromTo!\n"); return; } - // -- STEFAN -- - iVisChecked[iFrom] = 1; // WE HAVE CHECKED THIS ONE - // -- END -- + iVisChecked[iFrom] = 1; - // was int - long iPosition = (iFrom * MAX_NODES) + iTo; - - long iByte = (int) (iPosition / 8); - unsigned int iBit = iPosition % 8; - - if (iByte < g_iMaxVisibilityByte) { - unsigned char *ToChange = (cVisTable + iByte); + // work out the position + const long iPosition = iFrom * MAX_NODES + iTo; + const long iByte = iPosition / 8; + const unsigned int iBit = iPosition % 8; - if (bVisible) - *ToChange |= (1 << iBit); - else - *ToChange &= ~(1 << iBit); + // Optional assertion + assert(iByte < g_iMaxVisibilityByte); + unsigned char& ToChange = cVisTable[iByte]; + if (bVisible) { + ToChange |= (1 << iBit); + } + else { + ToChange &= ~(1 << iBit); } } -void cNodeMachine::ClearVisibilityTable(void) { - if (cVisTable) { - memset(cVisTable, 0, g_iMaxVisibilityByte); - } +void cNodeMachine::ClearVisibilityTable() +{ + std::fill(cVisTable.begin(), cVisTable.end(), 0); } -void cNodeMachine::FreeVisibilityTable(void) { - if (cVisTable) { - free(cVisTable); - } +void cNodeMachine::FreeVisibilityTable() { + cVisTable.clear(); } //--------------------------------------------------------- @@ -137,56 +132,72 @@ void cNodeMachine::init() { rblog("cNodeMachine::init() - START\n"); iMaxUsedNodes = 0; - for (int i = 0; i < MAX_NODES; i++) { - // --- nodes - Nodes[i].origin = Vector(9999, 9999, 9999); - for (int n = 0; n < MAX_NEIGHBOURS; n++) - Nodes[i].iNeighbour[n] = -1; + initNodes(); + initInfoNodes(); + initTroubles(); + initGoals(); + initPaths(); + initVisTable(); + initMeredians(); - // No bits set - Nodes[i].iNodeBits = 0; + rblog("cNodeMachine::init() - END\n"); +} - // --- info nodes - for (int d = 0; d < 2; d++) { - InfoNodes[i].fDanger[d] = 0.0; - } +void cNodeMachine::initNodes() { + int currentIndex = 0; + for (tNode& node : Nodes) { + node.origin = INVALID_VECTOR; + std::fill(node.iNeighbour.begin(), node.iNeighbour.end(), -1); + node.iNodeBits = 0; + node.index = currentIndex++; } +} - // Init trouble - for (int t = 0; t < MAX_TROUBLE; t++) { - Troubles[t].iFrom = -1; - Troubles[t].iTo = -1; - Troubles[t].iTries = -1; +void cNodeMachine::initInfoNodes() { + for (tInfoNode& infoNode : InfoNodes) { + std::fill(std::begin(infoNode.fDanger), std::end(infoNode.fDanger), 0.0f); + std::fill(std::begin(infoNode.fContact), std::end(infoNode.fContact), 0.0f); } +} - initGoals(); +void cNodeMachine::initializeNode(tNode& node) { + node.origin = INVALID_VECTOR; + std::fill(std::begin(node.iNeighbour), std::end(node.iNeighbour), -1); + node.iNodeBits = 0; + std::fill(std::begin(InfoNodes[node.index].fDanger), std::end(InfoNodes[node.index].fDanger), 0.0f); +} - // Init paths - for (int p = 0; p < MAX_BOTS; p++) - path_clear(p); +void cNodeMachine::initTroubles() { + for (tTrouble& Trouble : Troubles) { + Trouble.iFrom = -1; + Trouble.iTo = -1; + Trouble.iTries = -1; + } +} - // Init VisTable - for (int iVx = 0; iVx < MAX_NODES; iVx++) { - iVisChecked[iVx] = 0; // not checked yet +void cNodeMachine::initPaths() { + for (int p = 0; p < MAX_BOTS; p++) { + path_clear(p); } +} + +void cNodeMachine::initVisTable() { + std::fill(std::begin(iVisChecked), std::end(iVisChecked), 0); // CODE: From cheesemonster - unsigned long iSize = g_iMaxVisibilityByte; + constexpr unsigned long iSize = g_iMaxVisibilityByte; //create a heap type thing... - FreeVisibilityTable(); // 16/07/04 - free it first - cVisTable = (unsigned char *) malloc(iSize); - memset(cVisTable, 0, iSize); + cVisTable.resize(iSize); ClearVisibilityTable(); - // END: - - // Init Meredians - for (int iMx = 0; iMx < MAX_MEREDIANS; iMx++) - for (int iMy = 0; iMy < MAX_MEREDIANS; iMy++) - for (int iNode = 0; iNode < MAX_NODES_IN_MEREDIANS; iNode++) - Meredians[iMx][iMy].iNodes[iNode] = -1; +} - rblog("cNodeMachine::init() - END\n"); +void cNodeMachine::initMeredians() { + for (tMeredian (&Meredian)[64] : Meredians) { + for (tMeredian& iMy : Meredian) { + std::fill(std::begin(iMy.iNodes), std::end(iMy.iNodes), -1); + } + } } void cNodeMachine::initGoals() { @@ -196,44 +207,32 @@ void cNodeMachine::initGoals() { } } -void cNodeMachine::initGoal(int g) { +void cNodeMachine::initGoal(const int g) { Goals[g].iNode = -1; - Goals[g].pGoalEdict = NULL; + Goals[g].pGoalEdict = nullptr; Goals[g].iType = GOAL_NONE; Goals[g].index = g; Goals[g].iChecked = 0; Goals[g].iBadScore = 0; // no bad score at init - memset(Goals[g].name, 0, sizeof(Goals[g].name)); + std::memset(Goals[g].name, 0, sizeof(Goals[g].name)); } -int cNodeMachine::GetTroubleIndexForConnection(int iFrom, int iTo) { - char msg[255]; -// sprintf(msg, "GetTroubleIndexForConnection | from %d to %d\n", iFrom, iTo); -// rblog(msg); - // in case of invalid values, return -1 - no need to loop - if (iFrom < -1 || iFrom >= MAX_NODES) { - rblog("GetTroubleIndexForConnection | invalid iFrom\n"); - return -1; - } - if (iTo < -1 || iTo >= MAX_NODES) { - rblog("GetTroubleIndexForConnection | invalid iTo\n"); +int cNodeMachine::GetTroubleIndexForConnection(int iFrom, int iTo) const +{ + if (iFrom < 0 || iFrom >= MAX_NODES || iTo < 0 || iTo >= MAX_NODES) { return -1; } - // seems to be valid, look for troubled connections - int index; - for (index = 0; index < MAX_TROUBLE; index++) { - if (Troubles[index].iFrom == iFrom && - Troubles[index].iTo == iTo) { - memset(msg, 0, sizeof(msg)); - sprintf(msg, "GetTroubleIndexForConnection | Found index [%d] for from %d to %d\n", index, iFrom, iTo); - rblog(msg); - // found troubled connection, return its index - return index; - } + const tTrouble* it = std::find_if(std::begin(Troubles), std::end(Troubles), + [iFrom, iTo](const tTrouble& trouble) + { + return trouble.iFrom == iFrom && trouble.iTo == iTo; + }); + + if (it != std::end(Troubles)) { + return std::distance(std::begin(Troubles), it); } -// rblog("GetTroubleIndexForConnection | found no index matching from/to. Returning -1\n"); return -1; } @@ -244,15 +243,15 @@ int cNodeMachine::GetTroubleIndexForConnection(int iFrom, int iTo) { * @param iTo * @return index of newly created index */ -int cNodeMachine::AddTroubledConnection(int iFrom, int iTo) { - int existingIndex = GetTroubleIndexForConnection(iFrom, iTo); +int cNodeMachine::AddTroubledConnection(const int iFrom, const int iTo) +{ + const int existingIndex = GetTroubleIndexForConnection(iFrom, iTo); if (existingIndex > -1) return existingIndex; // already exists int iNew = -1; - int t; - for (t = 0; t < MAX_TROUBLE; t++) + for (int t = 0; t < MAX_TROUBLE; t++) if (Troubles[t].iFrom < 0 || Troubles[t].iTo < 0) { iNew = t; @@ -270,16 +269,17 @@ int cNodeMachine::AddTroubledConnection(int iFrom, int iTo) { return iNew; } -bool cNodeMachine::hasAttemptedConnectionTooManyTimes(int index) { - if (index < 0) { +bool cNodeMachine::hasAttemptedConnectionTooManyTimes(const int index) const +{ + if (index < 0 || index >= MAX_TROUBLE) { // Use MAX_TROUBLE for bounds checking [APG]RoboCop[CL] rblog("(trouble) hasAttemptedConnectionTooManyTimes | invalid index for hasAttemptedConnectionTooManyTimes()\n"); // deal with invalid connection return false; } - tTrouble &trouble = Troubles[index]; + const tTrouble& trouble = Troubles[index]; char msg[255]; - sprintf(msg, "(trouble) hasAttemptedConnectionTooManyTimes | Connection %d (%d->%d) has %d tries.\n", index, trouble.iFrom, trouble.iTo, trouble.iTries); + snprintf(msg, sizeof(msg), "(trouble) hasAttemptedConnectionTooManyTimes | Connection %d (%d->%d) has %d tries.\n", index, trouble.iFrom, trouble.iTo, trouble.iTries); rblog(msg); if (trouble.iTries > 2) { @@ -298,8 +298,9 @@ bool cNodeMachine::hasAttemptedConnectionTooManyTimes(int index) { * @param iTo * @return */ -bool cNodeMachine::IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded(int iFrom, int iTo) { - int index = AddTroubledConnection(iFrom, iTo); +bool cNodeMachine::IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded(const int iFrom, const int iTo) +{ + const int index = AddTroubledConnection(iFrom, iTo); IncreaseAttemptsForTroubledConnection(index); if (hasAttemptedConnectionTooManyTimes(index)) { rblog("(trouble) IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded | a troubled connection - tried too many times!\n"); @@ -312,37 +313,37 @@ bool cNodeMachine::IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded(int i } return false; - } else { - rblog("(trouble) IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded | may attempt another time\n"); - return true; } + rblog("(trouble) IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded | may attempt another time\n"); + return true; } -void cNodeMachine::IncreaseAttemptsForTroubledConnection(int index) { +void cNodeMachine::IncreaseAttemptsForTroubledConnection(const int index) +{ if (index < 0 || index >= MAX_TROUBLE) return; - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "(trouble) IncreaseAttemptsForTroubledConnection | Increasing trouble for connection [%d]\n", index); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "(trouble) IncreaseAttemptsForTroubledConnection | Increasing trouble for connection [%d]\n", index); rblog(msg); Troubles[index].iTries++; - tTrouble &trouble = Troubles[index]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "(trouble) IncreaseAttemptsForTroubledConnection | Connection %d (%d->%d) has %d tries.\n", index, trouble.iFrom, trouble.iTo, trouble.iTries); + const tTrouble &trouble = Troubles[index]; + std::memset(msg, 0, sizeof(msg)); + snprintf(msg, sizeof(msg), "(trouble) IncreaseAttemptsForTroubledConnection | Connection %d (%d->%d) has %d tries.\n", index, trouble.iFrom, trouble.iTo, trouble.iTries); rblog(msg); } -bool cNodeMachine::ClearTroubledConnection(int iFrom, int iTo) { +bool cNodeMachine::ClearTroubledConnection(const int iFrom, const int iTo) +{ char msg[255]; - sprintf(msg, "(trouble) NodeMachine::ClearTroubledConnection | %d -> %d - START\n", iFrom, iTo); + snprintf(msg, sizeof(msg), "(trouble) NodeMachine::ClearTroubledConnection | %d -> %d - START\n", iFrom, iTo); rblog(msg); - int index = GetTroubleIndexForConnection(iFrom, iTo); + const int index = GetTroubleIndexForConnection(iFrom, iTo); - memset(msg, 0, sizeof(msg)); - sprintf(msg, "(trouble) NodeMachine::ClearTroubledConnection | %d -> %d has index %d\n", iFrom, iTo, index); + std::memset(msg, 0, sizeof(msg)); + snprintf(msg, sizeof(msg), "(trouble) NodeMachine::ClearTroubledConnection | %d -> %d has index %d\n", iFrom, iTo, index); rblog(msg); if (index < 0) { @@ -358,36 +359,40 @@ bool cNodeMachine::ClearTroubledConnection(int iFrom, int iTo) { return true; } -void cNodeMachine::path_clear(int botIndex) { +void cNodeMachine::path_clear(const int botIndex) +{ for (int nodeIndex = 0; nodeIndex < MAX_NODES; nodeIndex++) { iPath[botIndex][nodeIndex] = -1; } } // Return -Vector cNodeMachine::node_vector(int iNode) { +Vector cNodeMachine::node_vector(const int iNode) const +{ if (iNode > -1) { return Nodes[iNode].origin; } - return Vector(9999, 9999, 9999); + return {9999, 9999, 9999}; } // Input: Vector, Output X and Y Meredians -void cNodeMachine::VectorToMeredian(Vector vOrigin, int *iX, int *iY) { +void cNodeMachine::VectorToMeredian(const Vector& vOrigin, int *iX, int *iY) +{ // Called for lookupt and for storing - float iCoordX = vOrigin.x + 8192.0; // map height (converts from - to +) - float iCoordY = vOrigin.y + 8192.0; // map width (converts from - to +) + float iCoordX = vOrigin.x + 8192.0f; // map height (converts from - to +) + float iCoordY = vOrigin.y + 8192.0f; // map width (converts from - to +) // Meredian: - iCoordX = iCoordX / SIZE_MEREDIAN; - iCoordY = iCoordY / SIZE_MEREDIAN; + iCoordX = iCoordX / static_cast(SIZE_MEREDIAN); + iCoordY = iCoordY / static_cast(SIZE_MEREDIAN); - *iX = (int) iCoordX; - *iY = (int) iCoordY; + *iX = static_cast(iCoordX); + *iY = static_cast(iCoordY); } -void cNodeMachine::AddToMeredian(int iX, int iY, int iNode) { +void cNodeMachine::AddToMeredian(const int iX, const int iY, const int iNode) +{ int index = -1; for (int i = 0; i < MAX_NODES_IN_MEREDIANS; i++) if (Meredians[iX][iY].iNodes[i] < 0) { @@ -402,9 +407,10 @@ void cNodeMachine::AddToMeredian(int iX, int iY, int iNode) { } // Does the node float? -bool cNodeMachine::node_float(Vector vOrigin, edict_t *pEdict) { +bool cNodeMachine::node_float(const Vector& vOrigin, edict_t *pEdict) +{ TraceResult tr; - Vector tr_end = vOrigin - Vector(0, 0, (ORIGIN_HEIGHT * 1.2)); + const Vector tr_end = vOrigin - Vector(0, 0, static_cast(ORIGIN_HEIGHT) * 1.2f); //Using TraceHull to detect de_aztec bridge and other entities. (skill self) //UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); @@ -412,50 +418,51 @@ bool cNodeMachine::node_float(Vector vOrigin, edict_t *pEdict) { UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, human_hull, pEdict->v.pContainingEntity, &tr); else - UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, human_hull, NULL, + UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, human_hull, nullptr, &tr); // if nothing hit: floating too high, return false - if (tr.flFraction >= 1.0) + if (tr.flFraction >= 1.0f) return true; // floating // *NOTE*: Actually this check should not be nescesary! - if (tr.flFraction < 1.0) + if (tr.flFraction < 1.0f) if (tr.pHit == pEdict) return true; // if inside wall: return false if (tr.fStartSolid == 1) { - // todo: make sure the node does not start within this wall - return false; // not floating + rblog("(node) node_float | node is inside wall!\n"); + return false; } return false; // not floating } // Does the node stand on a crate? or a steep slope? -bool cNodeMachine::node_on_crate(Vector vOrigin, edict_t *pEdict) { +bool cNodeMachine::node_on_crate(const Vector& vOrigin, edict_t *pEdict) +{ TraceResult tr; - Vector tr_end = vOrigin - Vector(0, 0, (ORIGIN_HEIGHT * 1.2)); + const Vector tr_end = vOrigin - Vector(0, 0, static_cast(ORIGIN_HEIGHT) * 1.2f); //Using TraceHull to detect de_aztec bridge and other entities. (skill self) if (pEdict) UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, human_hull, pEdict->v.pContainingEntity, &tr); else - UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, human_hull, NULL, + UTIL_TraceHull(vOrigin, tr_end, ignore_monsters, human_hull, nullptr, &tr); // if nothing hit: floating too high, return false - if (tr.flFraction >= 1.0) + if (tr.flFraction >= 1.0f) return false; // hit something - if (tr.flFraction < 1.0) { + if (tr.flFraction < 1.0f) { // thanks a million to PMB , so i know what the difference // is between something straight (crate) and steep... although i have // no clue yet how to compute these values myself. - if ( /*tr.vecPlaneNormal.z >= 0.7 && */ tr.vecPlaneNormal.z == 1.0) { + if ( /*tr.vecPlaneNormal.z >= 0.7 && */ tr.vecPlaneNormal.z == 1.0f) { return true; } } @@ -464,6 +471,46 @@ bool cNodeMachine::node_on_crate(Vector vOrigin, edict_t *pEdict) { return false; } +int cNodeMachine::node_dangerous(const int iTeam, const Vector& vOrigin, const float fMaxDistance) //TODO: Experimental & Incomplete [APG]RoboCop[CL] +{ + int iBestNode = -1; + float fMaxDanger = 0.0f; + + // Use Meredians to search for nodes + int iX, iY; + VectorToMeredian(vOrigin, &iX, &iY); + + if (iX < 0 || iY < 0) { + return -1; // Invalid coordinates + } + + // Search in the current and adjacent meridians to ensure we cover the full radius + for (int meredianX = iX - 1; meredianX <= iX + 1; ++meredianX) { + for (int meredianY = iY - 1; meredianY <= iY + 1; ++meredianY) { + // Ensure meridian coordinates are within bounds + if (meredianX < 0 || meredianX >= MAX_MEREDIANS || meredianY < 0 || meredianY >= MAX_MEREDIANS) { + continue; + } + + for (const int iNode : Meredians[meredianX][meredianY].iNodes) { + if (iNode < 0) continue; + + const float fDist = func_distance(vOrigin, Nodes[iNode].origin); + + if (fDist < fMaxDistance) { + const float fDanger = InfoNodes[iNode].fDanger[iTeam]; + if (fDanger > fMaxDanger) { + fMaxDanger = fDanger; + iBestNode = iNode; + } + } + } + } + } + + return iBestNode; +} + /** * Find a node close to vOrigin within distance fDist. Ignoring any pEdict it hits. * @param vOrigin @@ -471,7 +518,8 @@ bool cNodeMachine::node_on_crate(Vector vOrigin, edict_t *pEdict) { * @param pEdict * @return */ -int cNodeMachine::getClosestNode(Vector vOrigin, float fDist, edict_t *pEdict) { +int cNodeMachine::getClosestNode(const Vector& vOrigin, const float fDist, edict_t *pEdict) const +{ // REDO: Need faster method to find a node // TOADD: For secure results all nodes should be checked to figure out the real // 'closest' node. @@ -491,14 +539,15 @@ int cNodeMachine::getClosestNode(Vector vOrigin, float fDist, edict_t *pEdict) { int iCloseNode = -1; // Search in this meredian - for (int i = 0; i < MAX_NODES_IN_MEREDIANS; i++) { - if (Meredians[iX][iY].iNodes[i] < 0) continue; // skip invalid node indexes + for (const int i : Meredians[iX][iY].iNodes) + { + if (i < 0) continue; // skip invalid node indexes - int iNode = Meredians[iX][iY].iNodes[i]; + const int iNode = i; -// if (Nodes[iNode].origin.z > (vOrigin.z + 32)) continue; // do not pick nodes higher than us + //if (Nodes[iNode].origin.z > (vOrigin.z + 32)) continue; // do not pick nodes higher than us - float distanceFromTo = func_distance(vOrigin, Nodes[iNode].origin); + const float distanceFromTo = func_distance(vOrigin, Nodes[iNode].origin); if (distanceFromTo < dist) { dist = distanceFromTo; @@ -515,19 +564,13 @@ int cNodeMachine::getClosestNode(Vector vOrigin, float fDist, edict_t *pEdict) { &tr); } else { UTIL_TraceHull(vOrigin, nodeVector, dont_ignore_monsters, - head_hull, NULL, &tr); + head_hull, nullptr, &tr); } // if nothing hit: - if (tr.flFraction >= 1.0) { - if (pEdict != NULL) { - if (FInViewCone(&nodeVector, pEdict) // in FOV - && FVisible(nodeVector, pEdict)) { - iCloseNode = iNode; - } else { - iCloseNode = iNode; - } - } + if (tr.flFraction >= 1.0f && pEdict != nullptr) { + // Regardless of conditions, set iFarNode to iNode + iCloseNode = iNode; } } } @@ -541,7 +584,8 @@ int cNodeMachine::getClosestNode(Vector vOrigin, float fDist, edict_t *pEdict) { * @param pEdict * @return */ -int cNodeMachine::getFurthestNode(Vector vOrigin, float fDist, edict_t *pEdict) { +int cNodeMachine::getFurthestNode(const Vector& vOrigin, const float fDist, const edict_t *pEdict) const +{ // Use Meredians to search for nodes // TODO: we should take care in the situation where we're at the 'edge' of such a meridian (subspace). So we should // basicly take edging meridians as well when too close to the edge. @@ -557,14 +601,15 @@ int cNodeMachine::getFurthestNode(Vector vOrigin, float fDist, edict_t *pEdict) int iFarNode = -1; // Search in this meredian - for (int i = 0; i < MAX_NODES_IN_MEREDIANS; i++) { - if (Meredians[iX][iY].iNodes[i] < 0) continue; // skip invalid node indexes + for (const int i : Meredians[iX][iY].iNodes) + { + if (i < 0) continue; // skip invalid node indexes - int iNode = Meredians[iX][iY].iNodes[i]; + const int iNode = i; -// if (Nodes[iNode].origin.z > (vOrigin.z + 32)) continue; // do not pick nodes higher than us + // if (Nodes[iNode].origin.z > (vOrigin.z + 32)) continue; // do not pick nodes higher than us - float distanceFromTo = func_distance(vOrigin, Nodes[iNode].origin); + const float distanceFromTo = func_distance(vOrigin, Nodes[iNode].origin); if (distanceFromTo < fDist && // within range distanceFromTo > dist) { // but furthest so far dist = distanceFromTo; @@ -582,19 +627,13 @@ int cNodeMachine::getFurthestNode(Vector vOrigin, float fDist, edict_t *pEdict) &tr); } else { UTIL_TraceHull(vOrigin, nodeVector, dont_ignore_monsters, - head_hull, NULL, &tr); + head_hull, nullptr, &tr); } // if nothing hit: - if (tr.flFraction >= 1.0) { - if (pEdict != NULL) { - if (FInViewCone(&nodeVector, pEdict) // in FOV - && FVisible(nodeVector, pEdict)) { - iFarNode = iNode; - } else { - iFarNode = iNode; - } - } + if (tr.flFraction >= 1.0f && pEdict != nullptr) { + // Regardless of conditions, set iFarNode to iNode + iFarNode = iNode; } } } @@ -602,12 +641,12 @@ int cNodeMachine::getFurthestNode(Vector vOrigin, float fDist, edict_t *pEdict) } // Adds a neighbour connection to a node ID -bool cNodeMachine::add_neighbour_node(int iNode, int iToNode) { +bool cNodeMachine::add_neighbour_node(const int iNode, const int iToNode) { if (iNode < 0) return false; tNode *node = getNode(iNode); - int iNeighbourId = freeNeighbourNodeIndex(node); + const int iNeighbourId = freeNeighbourNodeIndex(node); if (iNeighbourId > -1) { node->iNeighbour[iNeighbourId] = iToNode; return true; @@ -623,29 +662,26 @@ bool cNodeMachine::add_neighbour_node(int iNode, int iToNode) { * @param iTo * @return */ -bool cNodeMachine::removeConnection(int iFrom, int iTo) { +bool cNodeMachine::removeConnection(const int iFrom, const int iTo) { if (iFrom < 0 || iTo < 0) { return false; } - char msg[255]; - memset(msg, 0, sizeof(msg)); + char msg[255] = {}; tNode *node = getNode(iFrom); if (!node) { - sprintf(msg, "(trouble) cNodeMachine::removeConnection | From %d, to %d has no node! (error)\n", iFrom, iTo); + snprintf(msg, sizeof(msg), "(trouble) cNodeMachine::removeConnection | From %d, to %d has no node! (error)\n", iFrom, iTo); rblog(msg); return false; } bool removedOneOrMoreNeighbours = false; - // Find the connection and remove it - int i = 0; - for (i = 0; i < MAX_NEIGHBOURS; i++) { - int neighbourNode = node->iNeighbour[i]; + for (int i = 0; i < MAX_NEIGHBOURS; i++) { + const int neighbourNode = node->iNeighbour[i]; - sprintf(msg, + snprintf(msg, sizeof(msg), "(trouble) removeConnection(from->%d, to->%d), evaluating neighbour [%d] = node %d\n", iFrom, iTo, @@ -669,19 +705,18 @@ bool cNodeMachine::removeConnection(int iFrom, int iTo) { } // Removes ALL neighbour connections on iNode -bool cNodeMachine::remove_neighbour_nodes(int iNode) { +bool cNodeMachine::remove_neighbour_nodes(const int iNode) { if (iNode < 0) return false; - int i = 0; - for (i = 0; i < MAX_NEIGHBOURS; i++) - Nodes[iNode].iNeighbour[i] = -1; + for (int& i : Nodes[iNode].iNeighbour) + i = -1; return true; } // returns the next free 'neighbour id' for that node -int cNodeMachine::freeNeighbourNodeIndex(tNode *Node) { +int cNodeMachine::freeNeighbourNodeIndex(const tNode *Node) { for (int i = 0; i < MAX_NEIGHBOURS; i++) { if (Node->iNeighbour[i] < 0) { return i; @@ -691,8 +726,20 @@ int cNodeMachine::freeNeighbourNodeIndex(tNode *Node) { return -1; } +int cNodeMachine::is_neighbour_node(const tNode& node, const int iNode) +{ + for (int i = 0; i < MAX_NEIGHBOURS; i++) { + if (node.iNeighbour[i] == iNode) { + return i; + } + } + + return -1; +} + // Return the node id from bot path on Index NR -int cNodeMachine::getNodeIndexFromBotForPath(int botIndex, int pathNodeIndex) { +int cNodeMachine::getNodeIndexFromBotForPath(const int botIndex, const int pathNodeIndex) const +{ if (botIndex > -1 && botIndex < MAX_BOTS && pathNodeIndex > -1 && pathNodeIndex < MAX_PATH_NODES) { return iPath[botIndex][pathNodeIndex]; @@ -702,27 +749,25 @@ int cNodeMachine::getNodeIndexFromBotForPath(int botIndex, int pathNodeIndex) { } // Compute the horizontal distance between A and B (ignoring z coordinate) -static float horizontal_distance(Vector a, Vector b) { - return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); +static float horizontal_distance(const Vector& a, const Vector& b) { + return std::sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } -#define STEP 20 //Incremental move +constexpr int STEP = 20; //Incremental move; // Return the floor below V // TO BE IMPROVED use pEntityCOntaining -static Vector FloorBelow(Vector V) { +static Vector FloorBelow(const Vector& V) { static TraceResult tr; // Keep it available even outside of the call - Vector ReallyDown, UpALittle; - int HullNumber, HullHeight; // First use this hull - HullNumber = human_hull; - HullHeight = 36; + int HullNumber = human_hull; + float HullHeight = 36.0f; // Bump V a little higher (to allow for a steep climb) - UpALittle = V + Vector(0, 0, HullHeight); - ReallyDown = V + Vector(0, 0, -500); - UTIL_TraceHull(UpALittle, ReallyDown, ignore_monsters, HullNumber, NULL, &tr); + Vector UpALittle = V + Vector(0, 0, HullHeight); + Vector ReallyDown = V + Vector(0, 0, -500); + UTIL_TraceHull(UpALittle, ReallyDown, ignore_monsters, HullNumber, nullptr, &tr); //printf(" Floor %.0f -> %.0f, TraceHull fraction = %.2f, vecEndPos.z=%.0f %s %s\n", //UpALittle.z,ReallyDown.z,tr.flFraction,tr.vecEndPos.z, //(tr.fAllSolid) ? "AllSolid" : "", @@ -730,7 +775,7 @@ static Vector FloorBelow(Vector V) { if (tr.fStartSolid) { // Perhaps we where too high and hit the ceiling UpALittle = V + Vector(0, 0, 0); ReallyDown = V + Vector(0, 0, -500); - UTIL_TraceHull(UpALittle, ReallyDown, ignore_monsters, HullNumber, NULL, &tr); + UTIL_TraceHull(UpALittle, ReallyDown, ignore_monsters, HullNumber, nullptr, &tr); //printf(" Floor without raising %.0f -> %.0f, TraceHull fraction = %.2f, vecEndPos.z=%.0f %s %s\n", //UpALittle.z,ReallyDown.z,tr.flFraction,tr.vecEndPos.z, //(tr.fAllSolid) ? "AllSolid" : "", @@ -740,7 +785,7 @@ static Vector FloorBelow(Vector V) { HullHeight = 0; UpALittle = V + Vector(0, 0, STEP); ReallyDown = V + Vector(0, 0, -500); - UTIL_TraceHull(UpALittle, ReallyDown, ignore_monsters, HullNumber, NULL, &tr); + UTIL_TraceHull(UpALittle, ReallyDown, ignore_monsters, HullNumber, nullptr, &tr); //printf(" Floor with point hull %.0f -> %.0f, TraceHull fraction = %.2f, vecEndPos.z=%.0f %s %s\n", //UpALittle.z,ReallyDown.z,tr.flFraction,tr.vecEndPos.z, //(tr.fAllSolid) ? "AllSolid" : "", @@ -763,13 +808,14 @@ static Vector FloorBelow(Vector V) { // to a point in water or a point in the air // - ducking and walking (assuming flat ground) // - swimming -int cNodeMachine::Reachable(const int iStart, const int iEnd) { - Vector IncMove, Check, Floor, Start, End; +int cNodeMachine::Reachable(const int iStart, const int iEnd) const +{ + Vector IncMove, Check, Floor; float Dist, Height, PreviousHeight; TraceResult tr; - Start = Nodes[iStart].origin; - End = Nodes[iEnd].origin; + const Vector Start = Nodes[iStart].origin; + const Vector End = Nodes[iEnd].origin; #ifdef DEBUG_REACHABLE printf("Reachable %d(%.0f,%.0f,%.0f)%s", iStart, Start.x, Start.y, Start.z, @@ -778,27 +824,27 @@ int cNodeMachine::Reachable(const int iStart, const int iEnd) { (Nodes[iEnd].iNodeBits & BIT_LADDER) ? "OnLadder" : ""); #endif // Just in case - if (Start.x == 9999 || End.x == 9999) - return false; + if (static_cast(Start.x) == 9999 || static_cast(End.x) == 9999) + return 0; // Quick & dirty check whether we can go through... // This is simply to quickly decide whether the move is impossible - UTIL_TraceHull(Start, End, ignore_monsters, point_hull, NULL, &tr); + UTIL_TraceHull(Start, End, ignore_monsters, point_hull, nullptr, &tr); #ifdef DEBUG_REACHABLE printf("TraceHull --> tr.flFraction = %.2f\n", tr.flFraction); #endif - if (tr.flFraction < 1.0) - return false; + if (tr.flFraction < 1.0f) + return 0; // If either start/end is on ladder, assume we can fly without falling // but still check whether a human hull can go through - if ((Nodes[iStart].iNodeBits & BIT_LADDER) || - (Nodes[iEnd].iNodeBits & BIT_LADDER)) + if (Nodes[iStart].iNodeBits & BIT_LADDER || + Nodes[iEnd].iNodeBits & BIT_LADDER) { - UTIL_TraceHull(Start, End, ignore_monsters, human_hull, NULL, &tr); - return tr.flFraction >= 1.0; + UTIL_TraceHull(Start, End, ignore_monsters, human_hull, nullptr, &tr); + return tr.flFraction >= 1.0f; } // Heurestic for falling @@ -830,16 +876,16 @@ int cNodeMachine::Reachable(const int iStart, const int iEnd) { #endif if (Height > PreviousHeight) { // Going upwards - if (Height - PreviousHeight > MAX_JUMPHEIGHT) { + if (Height - PreviousHeight > static_cast(MAX_JUMPHEIGHT)) { //printf(" too high for upward jump\n") ; - return false; + return 0; } } else { // Going downwards - if (PreviousHeight - Height > MAX_FALLHEIGHT) + if (PreviousHeight - Height > static_cast(MAX_FALLHEIGHT)) if (UTIL_PointContents(Floor + Vector(0, 0, 5)) != CONTENTS_WATER) //{printf(" too high for a downward fall not in water\n") ; - return false; // Falling from too high not in water + return 0; // Falling from too high not in water //} //else printf(" ouf we are in water\n") ; } @@ -856,15 +902,15 @@ int cNodeMachine::Reachable(const int iStart, const int iEnd) { #endif if (Height > PreviousHeight) { // Going upwards - if (Height - PreviousHeight > MAX_JUMPHEIGHT) { + if (Height - PreviousHeight > static_cast(MAX_JUMPHEIGHT)) { //printf(" too high for upward jump\n") ; - return false; + return 0; } } else { // Going downwards - if (PreviousHeight - Height > MAX_FALLHEIGHT) + if (PreviousHeight - Height > static_cast(MAX_FALLHEIGHT)) if (UTIL_PointContents(End) != CONTENTS_WATER) { //printf(" too high for a downward fall not in water\n") ; - return false; // Falling from too high not in water + return 0; // Falling from too high not in water } //else printf(" ouf we are in water\n") ; } @@ -877,24 +923,23 @@ int cNodeMachine::Reachable(const int iStart, const int iEnd) { // TODO // Success ! - return true; + return 1; } // Adding a node: another way... -int cNodeMachine::add2(Vector vOrigin, int iType, edict_t *pEntity) { +int cNodeMachine::add2(const Vector& vOrigin, const int iType, edict_t *pEntity) { // Do not add a node when there is already one close if (getClosestNode(vOrigin, NODE_ZONE, pEntity) > -1) return -1; - int newNodeIndex = getFreeNodeIndex(); + const int newNodeIndex = getFreeNodeIndex(); if (newNodeIndex >= MAX_NODES || newNodeIndex < 0) { return -1; } Nodes[newNodeIndex].origin = vOrigin; - if (newNodeIndex > iMaxUsedNodes) - iMaxUsedNodes = newNodeIndex; + iMaxUsedNodes = std::max(newNodeIndex, iMaxUsedNodes); // Set different flags about the node Nodes[newNodeIndex].iNodeBits = iType; // EVY's extension @@ -944,7 +989,7 @@ int cNodeMachine::add2(Vector vOrigin, int iType, edict_t *pEntity) { // When walking the human player can't pass a certain speed and distance // however, when a human is falling, the distance will be bigger. - int maxDistance = 3 * NODE_ZONE; + constexpr int maxDistance = 3 * NODE_ZONE; if (horizontal_distance(Nodes[newNodeIndex].origin, Nodes[j].origin) > maxDistance) continue; @@ -966,28 +1011,28 @@ int cNodeMachine::add2(Vector vOrigin, int iType, edict_t *pEntity) { } /** - * Returns a free node index, this is not bound to a meredian (subcluster)! + * Returns a free node index, this is not bound to a meridian (subcluster)! * @return */ -int cNodeMachine::getFreeNodeIndex() { - int i = 0; - for (i = 0; i < MAX_NODES; i++) { +int cNodeMachine::getFreeNodeIndex() const +{ + for (int i = 0; i < MAX_NODES; i++) { if (Nodes[i].origin == INVALID_VECTOR) { return i; - break; + //break; } } return -1; // no free node found } // Adding a node -int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { +int cNodeMachine::addNode(const Vector& vOrigin, edict_t *pEntity) { // Do not add a node when there is already one close if (getClosestNode(vOrigin, NODE_ZONE, pEntity) > -1) return -1; - int currentIndex = getFreeNodeIndex(); + const int currentIndex = getFreeNodeIndex(); // failed to find free node, bail if (currentIndex < 0) { @@ -998,8 +1043,8 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // SET BITS: bool bIsInWater = false; - bool indexNodeFloats = false; - bool bIndexOnCrate = false; + bool indexNodeFloats; + bool bIndexOnCrate; if (pEntity) { // Does this thing float? @@ -1023,16 +1068,19 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { if (FUNC_IsOnLadder(pEntity)) indexNodeFloats = false; - if (pEntity->v.button & IN_DUCK) { + if (pEntity->v.button & IN_DUCK) // ?? Nodes[currentIndex].iNodeBits |= BIT_DUCK; - } + + //Experimental - This new node requires for bots to vault onto crates and edges? [APG]RoboCop[CL] + if (pEntity->v.button & BIT_DUCKJUMP) + Nodes[currentIndex].iNodeBits |= BIT_DUCKJUMP; } // do only check pEntity when its not NULL else { // Does this thing float? - indexNodeFloats = node_float(Nodes[currentIndex].origin, NULL); - bIndexOnCrate = node_on_crate(Nodes[currentIndex].origin, NULL); + indexNodeFloats = node_float(Nodes[currentIndex].origin, nullptr); + bIndexOnCrate = node_on_crate(Nodes[currentIndex].origin, nullptr); } // add to subcluster @@ -1065,8 +1113,8 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // When walking the human player can't pass a certain speed and distance // however, when a human is falling (or walking a slope), the distance will be bigger. - float distanceBetweenVectorsWithoutZAxis = func_distance(vNormalizedOrigin, vNormalizedIndex); - if (distanceBetweenVectorsWithoutZAxis > (NODE_ZONE * 3)) { // allow up to 3 times the boundary we use normally + const float distanceBetweenVectorsWithoutZAxis = func_distance(vNormalizedOrigin, vNormalizedIndex); + if (distanceBetweenVectorsWithoutZAxis > NODE_ZONE * 3) { // allow up to 3 times the boundary we use normally continue; } @@ -1076,11 +1124,11 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // Traceline from nodeIndex to index bool bNeighbourFloats = node_float(Nodes[nodeIndex].origin, pEntity); - bool bNeighOnCrate = node_on_crate(Nodes[nodeIndex].origin, pEntity); + const bool bNeighOnCrate = node_on_crate(Nodes[nodeIndex].origin, pEntity); bool bNeighbourWater = false; // when pEntity is on ladder, it is NOT floating! - if (FUNC_IsOnLadder(pEntity)) + if (pEntity && FUNC_IsOnLadder(pEntity)) bNeighbourFloats = false; if (Nodes[nodeIndex].iNodeBits & BIT_LADDER) @@ -1092,25 +1140,24 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // Check if Index is LOWER then J, if so, the height should be jumpable! // When height does not differ to much we can add this connection. // - // // 14/06/04 - fix: Some slopes are not counted in here..., de_dust jump from crates connects now - bool nodeIsHigherThanOrigin = Nodes[nodeIndex].origin.z > Nodes[currentIndex].origin.z; + const bool nodeIsHigherThanOrigin = Nodes[nodeIndex].origin.z > Nodes[currentIndex].origin.z; // this assumes 'our' position is lower, this determines 'slope' distance - vec_t slopeDistance = Nodes[nodeIndex].origin.z - Nodes[currentIndex].origin.z; + const vec_t slopeDistance = Nodes[nodeIndex].origin.z - Nodes[currentIndex].origin.z; // this assumes 'our' position is higher, so we can determine fall distance - vec_t fallDistance = Nodes[currentIndex].origin.z - Nodes[nodeIndex].origin.z; + const vec_t fallDistance = Nodes[currentIndex].origin.z - Nodes[nodeIndex].origin.z; if (indexNodeFloats - && (fallDistance > MAX_FALLHEIGHT) + && fallDistance > static_cast(MAX_FALLHEIGHT) && (!bNeighbourWater && !bIsInWater)) { // skip nodes that are not in water and too low (ie will cause fall damage) continue; } // skip nodes which are to high (fall damage) - if (fallDistance > MAX_FALLHEIGHT) { + if (fallDistance > static_cast(MAX_FALLHEIGHT)) { continue; } @@ -1121,7 +1168,7 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { if (indexNodeFloats == false && // we stand on something nodeIsHigherThanOrigin && // we MUST jump (not fall) bNeighbourWater == false && - fabs(slopeDistance) > MAX_JUMPHEIGHT) // and cannot jump to it + std::fabs(slopeDistance) > static_cast(MAX_JUMPHEIGHT)) // and cannot jump to it { // SERVER_PRINT("Cannot jump to it"); continue; // next neighbour @@ -1131,7 +1178,7 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { if (indexNodeFloats == false && // we stand on something nodeIsHigherThanOrigin && // we MUST jump (not fall) bNeighbourWater == false && - fabs(slopeDistance) > MAX_FALLHEIGHT) // and cannot jump to it + std::fabs(slopeDistance) > static_cast(MAX_FALLHEIGHT)) // and cannot jump to it { // SERVER_PRINT("Insanity jump not possible either!\n"); continue; // next neighbour @@ -1140,13 +1187,13 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { int hull_type = head_hull; - if (FUNC_IsOnLadder(pEntity)) { + if (pEntity && FUNC_IsOnLadder(pEntity)) { hull_type = point_hull; } else { // falling // 14/06/05 - this code does not cause bad connections! - Stefan // 20/06/05 - oops, it does... because it blocks when we are NOT falling... - if (slopeDistance > MAX_FALLHEIGHT) { + if (slopeDistance > static_cast(MAX_FALLHEIGHT)) { hull_type = human_hull; // when we fall, be sure we can freely fall. } @@ -1164,10 +1211,10 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // trace UTIL_TraceHull(Nodes[currentIndex].origin, Nodes[nodeIndex].origin, ignore_monsters, - hull_type, pEntity->v.pContainingEntity, &tr); + hull_type, pEntity ? pEntity->v.pContainingEntity : nullptr, &tr); // if nothing hit: - if (tr.flFraction >= 1.0) { + if (tr.flFraction >= 1.0f) { // Add this to the neighbouring list Nodes[currentIndex].iNeighbour[neighbourId] = nodeIndex; @@ -1179,11 +1226,11 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // Check if J is LOWER then Index, if so, the height should be jumpable! // When height does not differ to much we can add this connection. - if ((indexNodeFloats && bNeighbourFloats) + if (indexNodeFloats && bNeighbourFloats && (bNeighbourWater == false && bIsInWater == false) && Nodes[currentIndex].origin.z >= Nodes[nodeIndex].origin.z) { char msg[80]; - sprintf(msg, "J.Z = %f, INDEX.Z = %f\n", Nodes[nodeIndex].origin.z, + snprintf(msg, sizeof(msg), "J.Z = %f, INDEX.Z = %f\n", Nodes[nodeIndex].origin.z, Nodes[currentIndex].origin.z); // UTIL_ClientPrintAll( HUD_PRINTNOTIFY, msg); bCanConnect = false; // cannot connect @@ -1193,7 +1240,7 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { if (bNeighbourFloats == false && // we stand on something Nodes[currentIndex].origin.z > Nodes[nodeIndex].origin.z && // we MUST jump (not fall) bIsInWater == false && - fabs(fallDistance) > MAX_JUMPHEIGHT) // and cannot jump to it + std::fabs(fallDistance) > static_cast(MAX_JUMPHEIGHT)) // and cannot jump to it bCanConnect = false; // cannot connect // All water stuff can connect to each other @@ -1201,7 +1248,7 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { bCanConnect = true; if (bCanConnect) { - int jNeigh = freeNeighbourNodeIndex(&Nodes[nodeIndex]); + const int jNeigh = freeNeighbourNodeIndex(&Nodes[nodeIndex]); if (jNeigh > -1) Nodes[nodeIndex].iNeighbour[jNeigh] = currentIndex; // reversed also possible } @@ -1218,8 +1265,7 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { } - if (currentIndex > iMaxUsedNodes) - iMaxUsedNodes = currentIndex; + iMaxUsedNodes = std::max(currentIndex, iMaxUsedNodes); //UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "NodeMachine: Succesfully added node\n"); return currentIndex; @@ -1227,9 +1273,10 @@ int cNodeMachine::addNode(Vector vOrigin, edict_t *pEntity) { // Players initialization void cNodeMachine::init_players() { - for (int i = 0; i < 32; i++) { - Players[i].vPrevPos = Vector(9999, 9999, 9999); - Players[i].iNode = -1; + for (tPlayer& Player : Players) + { + Player.vPrevPos = Vector(9999, 9999, 9999); + Player.iNode = -1; } } @@ -1237,10 +1284,10 @@ void cNodeMachine::init_players() { void cNodeMachine::init_round() { //UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "NodeMachine: Roundstart\n"); for (int index = 1; index <= gpGlobals->maxClients; index++) { - edict_t *pPlayer = INDEXENT(index); + const edict_t *pPlayer = INDEXENT(index); // skip invalid players - if ((pPlayer) && (!pPlayer->free)) + if (pPlayer && !pPlayer->free) Players[(index - 1)].vPrevPos = pPlayer->v.origin; } @@ -1249,9 +1296,9 @@ void cNodeMachine::init_round() { path_clear(p); // decrease bad score for bad goal nodes - for (int g = 0; g < MAX_GOALS; g++) - if (Goals[g].iBadScore > 0) - Goals[g].iBadScore--; + for (tGoal& Goal : Goals) + if (Goal.iBadScore > 0) + Goal.iBadScore--; } /** @@ -1259,17 +1306,16 @@ void cNodeMachine::init_round() { */ void cNodeMachine::addNodesForPlayers() { for (int index = 1; index <= gpGlobals->maxClients; index++) { - edict_t *pPlayer = INDEXENT(index); + edict_t* pPlayer = INDEXENT(index); // skip invalid (dead, not playing) players - if ((pPlayer) && (!pPlayer->free)) { - if (pPlayer->free) continue; + if (pPlayer && !pPlayer->free) { if (!IsAlive(pPlayer)) continue; - int iPlayerIndex = index - 1; + const int iPlayerIndex = index - 1; // within a certain distance no node found? add one - if (func_distance(pPlayer->v.origin, Players[iPlayerIndex].vPrevPos) > NODE_ZONE) { + if (func_distance(pPlayer->v.origin, Players[iPlayerIndex].vPrevPos) > static_cast(NODE_ZONE)) { Players[iPlayerIndex].vPrevPos = pPlayer->v.origin; add2(pPlayer->v.origin, 0, pPlayer); } @@ -1278,57 +1324,56 @@ void cNodeMachine::addNodesForPlayers() { } // Draw connections of the node we are standing on -void cNodeMachine::connections(edict_t *pEntity) { - - int closeNode = -1; - char msg[75]; - memset(msg, 0, sizeof(msg)); +void cNodeMachine::connections(edict_t *pEntity) const +{ + int closeNode; + char msg[75] = {}; if (draw_nodepath > -1 && draw_nodepath < 32) { cBot botPointer = bots[draw_nodepath]; if (botPointer.bIsUsed) { closeNode = botPointer.determineCurrentNodeWithTwoAttempts(); if (closeNode > -1) { - Vector &vector = Nodes[closeNode].origin; - sprintf(msg, "Bot [%s|%d] is at node %d (%f,%f,%f)\n", botPointer.name, draw_nodepath, closeNode, vector.x, vector.y, vector.z); + const Vector &vector = Nodes[closeNode].origin; + snprintf(msg, sizeof(msg), "Bot [%s|%d] is at node %d (%f,%f,%f)\n", botPointer.name, draw_nodepath, closeNode, vector.x, vector.y, vector.z); } else { - sprintf(msg, "Bot [%s|%d] is at node %d\n", botPointer.name, draw_nodepath, closeNode); + snprintf(msg, sizeof(msg), "Bot [%s|%d] is at node %d\n", botPointer.name, draw_nodepath, closeNode); } } else { closeNode = getClosestNode(pEntity->v.origin, NODE_ZONE, pEntity); if (closeNode > -1) { - Vector &vector = Nodes[closeNode].origin; - sprintf(msg, "No bot used for slot [%d], YOU are at node %d (%f,%f,%f)\n", draw_nodepath, closeNode, vector.x, vector.y, vector.z); + const Vector &vector = Nodes[closeNode].origin; + snprintf(msg, sizeof(msg), "No bot used for slot [%d], YOU are at node %d (%f,%f,%f)\n", draw_nodepath, closeNode, vector.x, vector.y, vector.z); } else { - sprintf(msg, "No bot used for slot [%d], YOU are at node %d\n", draw_nodepath, closeNode); + snprintf(msg, sizeof(msg), "No bot used for slot [%d], YOU are at node %d\n", draw_nodepath, closeNode); } } } else { closeNode = getClosestNode(pEntity->v.origin, NODE_ZONE, pEntity); - sprintf(msg, "YOU are at node %d\n", closeNode); + snprintf(msg, sizeof(msg), "YOU are at node %d\n", closeNode); } CenterMessage(msg); if (closeNode > -1) { for (int j = 0; j < MAX_NEIGHBOURS; j++) { - tNode &node = Nodes[closeNode]; - int neighbourNode = node.iNeighbour[j]; + const tNode &node = Nodes[closeNode]; + const int neighbourNode = node.iNeighbour[j]; if (neighbourNode > -1) { - Vector start = node.origin; - Vector end = Nodes[neighbourNode].origin; + const Vector start = node.origin; + const Vector end = Nodes[neighbourNode].origin; int red = 0; int green = 255; int blue = 0; - int troubleIndex = GetTroubleIndexForConnection(closeNode, neighbourNode); + const int troubleIndex = GetTroubleIndexForConnection(closeNode, neighbourNode); if (troubleIndex > -1) { - int tries = Troubles[troubleIndex].iTries; + const int tries = Troubles[troubleIndex].iTries; if (tries <= 1) { red = 255; - green = 255; + green = 250; blue = 0; } @@ -1339,7 +1384,7 @@ void cNodeMachine::connections(edict_t *pEntity) { } if (tries > 2) { - red = 128; + red = 150; green = 0; blue = 0; } @@ -1355,37 +1400,48 @@ void cNodeMachine::connections(edict_t *pEntity) { } // Draw -void cNodeMachine::draw(edict_t *pEntity) { - //DebugOut("waypoint: waypoint_draw()\n"); - int i = 0, max_drawn = 0; +void cNodeMachine::draw(edict_t* pEntity) const +{ + // DebugOut("waypoint: waypoint_draw()\n"); + int max_drawn = 0; + + // Declare 'start' vector outside the loop + Vector start; - for (i = 0; i < MAX_NODES; i++) { - if (Nodes[i].origin != Vector(9999, 9999, 9999)) { - Vector start = Nodes[i].origin - Vector(0, 0, 36); - Vector end = Nodes[i].origin; + for (const tNode& Node : Nodes) + { + if (Node.origin != Vector(9999, 9999, 9999)) { + start = Node.origin - Vector(0, 0, 36); + Vector end = Node.origin; - bool good = VectorIsVisibleWithEdict(pEntity, end, "none"); + const bool good = VectorIsVisibleWithEdict(pEntity, end, "none"); - int angle_to_waypoint = - FUNC_InFieldOfView(pEntity, (end - pEntity->v.origin)); + const int angle_to_waypoint = + FUNC_InFieldOfView(pEntity, end - pEntity->v.origin); if (good && angle_to_waypoint < 65) { - int r, g, b, l; - r = g = b = l = 250; - l = 250; - //l = 250; // Normally light is 250 + int g, b, l; + int r = g = b = l = 250; - if (Nodes[i].iNodeBits & BIT_LADDER) + // l = 250; // Normally light is 250 + + if (Node.iNodeBits & BIT_LADDER) b = g = 0; - if (Nodes[i].iNodeBits & BIT_WATER) + if (Node.iNodeBits & BIT_WATER) r = g = 0; - if (Nodes[i].iNodeBits & BIT_DUCK) - r = b = 0; + if (Node.iNodeBits & BIT_DUCK) + r = b = 50; + // Jump and DuckJump were missing for those nodes? [APG]RoboCop[CL] + if (Node.iNodeBits & BIT_JUMP) + r = b = 100; + + if (Node.iNodeBits & BIT_DUCKJUMP) + r = b = 150; - if (Nodes[i].iNeighbour[0] < 0) + if (Node.iNeighbour[0] < 0) r = 0; if (max_drawn < 39) { @@ -1393,32 +1449,43 @@ void cNodeMachine::draw(edict_t *pEntity) { max_drawn++; } } - } // for + } // for + } + + const int iNodeClose = getClosestNode(pEntity->v.origin, NODE_ZONE, pEntity); + if (iNodeClose >= static_cast(std::size(Nodes))) { + char msg[50]; + snprintf(msg, sizeof(msg), "Node %d\nMe: (%.0f,%.0f,%.0f)\n", iNodeClose, + pEntity->v.origin.x, pEntity->v.origin.y, + pEntity->v.origin.z); + CenterMessage(msg); + return; } - int iNodeClose = getClosestNode(pEntity->v.origin, NODE_ZONE, pEntity); char msg[50]; char Flags[10]; Flags[0] = 0; - if (Nodes[iNodeClose].iNodeBits & BIT_LADDER) - strcat(Flags, "L"); - - if (Nodes[iNodeClose].iNodeBits & BIT_WATER) - strcat(Flags, "W"); - - if (Nodes[iNodeClose].iNodeBits & BIT_JUMP) - strcat(Flags, "J"); - - if (Nodes[iNodeClose].iNodeBits & BIT_DUCK) - strcat(Flags, "D"); - - sprintf(msg, "Node %d(%.0f,%.0f,%.0f)%s\nMe: (%.0f,%.0f,%.0f)\n", - iNodeClose, (iNodeClose < 0) ? -1 : Nodes[iNodeClose].origin.x, - (iNodeClose < 0) ? -1 : Nodes[iNodeClose].origin.y, - (iNodeClose < 0) ? -1 : Nodes[iNodeClose].origin.z, Flags, - pEntity->v.origin.x, pEntity->v.origin.y, pEntity->v.origin.z); + if (iNodeClose >= 0) + { + if (Nodes[iNodeClose].iNodeBits & BIT_LADDER) + std::strcat(Flags, "L"); + if (Nodes[iNodeClose].iNodeBits & BIT_WATER) + std::strcat(Flags, "W"); + if (Nodes[iNodeClose].iNodeBits & BIT_JUMP) + std::strcat(Flags, "J"); + if (Nodes[iNodeClose].iNodeBits & BIT_DUCK) + std::strcat(Flags, "D"); + // Experimental DuckJump added for this new node [APG]RoboCop[CL] + if (Nodes[iNodeClose].iNodeBits & BIT_DUCKJUMP) + std::strcat(Flags, "h"); + } + snprintf(msg, sizeof(msg), "Node %d(%.0f,%.0f,%.0f)%s\nMe: (%.0f,%.0f,%.0f)\n", + iNodeClose, iNodeClose < 0 ? -1 : Nodes[iNodeClose].origin.x, + iNodeClose < 0 ? -1 : Nodes[iNodeClose].origin.y, + iNodeClose < 0 ? -1 : Nodes[iNodeClose].origin.z, Flags, + pEntity->v.origin.x, pEntity->v.origin.y, pEntity->v.origin.z); CenterMessage(msg); } @@ -1426,39 +1493,34 @@ void cNodeMachine::draw(edict_t *pEntity) { void cNodeMachine::experience_save() { char dirname[256]; char filename[256]; - int i; // Set Directory name - strcpy(dirname, "data/cstrike/exp/"); - strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, ".rbx"); // nodes file + std::strcpy(dirname, "data/cstrike/exp/"); + std::strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, ".rbx"); // nodes file // writes whole path into "filename", Linux compatible UTIL_BuildFileNameRB(dirname, filename); - FILE *rbl; // Only save if lock type is < 1 - rbl = fopen(filename, "wb"); + FILE* rbl = std::fopen(filename, "wb"); - if (rbl != NULL) { - int iVersion = FILE_EXP_VER2; - fwrite(&iVersion, sizeof(int), 1, rbl); + if (rbl != nullptr) { + constexpr int iVersion = FILE_EXP_VER2; + std::fwrite(&iVersion, sizeof(int), 1, rbl); - for (i = 0; i < MAX_NODES; i++) { - fwrite(&InfoNodes[i].fDanger[0], sizeof(Vector), 1, rbl); - fwrite(&InfoNodes[i].fDanger[1], sizeof(Vector), 1, rbl); - - fwrite(&InfoNodes[i].fContact[0], sizeof(Vector), 1, rbl); - fwrite(&InfoNodes[i].fContact[1], sizeof(Vector), 1, rbl); + // Write the InfoNodes data directly using the correct struct + for (tInfoNode& InfoNode : InfoNodes) + { + std::fwrite(&InfoNode, sizeof(tInfoNode), 1, rbl); } - if (iMaxUsedNodes > MAX_NODES) - iMaxUsedNodes = MAX_NODES; + iMaxUsedNodes = std::min(iMaxUsedNodes, MAX_NODES); // Here write down the MAX amounts of nodes used from vis table! - unsigned long iSize = (iMaxUsedNodes * MAX_NODES) / 8; + const unsigned long iSize = iMaxUsedNodes * MAX_NODES / 8; - fwrite(&iMaxUsedNodes, sizeof(int), 1, rbl); + std::fwrite(&iMaxUsedNodes, sizeof(int), 1, rbl); // Write down 512 bytes chunks, and when a remaining piece is left over // also write that down. Only when 'size' is 0 we quit the loop @@ -1473,7 +1535,7 @@ void cNodeMachine::experience_save() { // While we still have bytes remaining to write to disk. while (iRemaining > 0) { - fwrite(&cVisTable[iPos], iChunk, 1, rbl); + std::fwrite(&cVisTable[iPos], iChunk, 1, rbl); iChunk = 512; // keep the size 512 @@ -1489,69 +1551,58 @@ void cNodeMachine::experience_save() { } // write down the checked vis - fwrite(iVisChecked, sizeof(iVisChecked), 1, rbl); - fclose(rbl); + std::fwrite(iVisChecked, sizeof(iVisChecked), 1, rbl); + std::fclose(rbl); } else - fprintf(stderr, "Cannot write experience file %s\n", filename); + std::fprintf(stderr, "Cannot write experience file %s\n", filename); } // Load Danger void cNodeMachine::experience_load() { char dirname[256]; char filename[256]; - int i; // Set Directory name - strcpy(dirname, "data/cstrike/exp/"); - strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, ".rbx"); // nodes file + std::strcpy(dirname, "data/cstrike/exp/"); + std::strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, ".rbx"); // nodes file // writes whole path into "filename", Linux compatible UTIL_BuildFileNameRB(dirname, filename); - FILE *rbl; - rbl = fopen(filename, "rb"); + FILE* rbl = std::fopen(filename, "rb"); - if (rbl != NULL) { + if (rbl != nullptr) { + int i; int iVersion = FILE_EXP_VER1; - fread(&iVersion, sizeof(int), 1, rbl); + std::fread(&iVersion, sizeof(int), 1, rbl); if (iVersion == FILE_EXP_VER1) { for (i = 0; i < MAX_NODES; i++) { - fread(&InfoNodes[i].fDanger[0], sizeof(Vector), 1, rbl); - fread(&InfoNodes[i].fDanger[1], sizeof(Vector), 1, rbl); - - fread(&InfoNodes[i].fContact[0], sizeof(Vector), 1, rbl); - fread(&InfoNodes[i].fContact[1], sizeof(Vector), 1, rbl); + std::fread(&InfoNodes[i], sizeof(tInfoNode), 1, rbl); } - fread(&iMaxUsedNodes, sizeof(int), 1, rbl); + std::fread(&iMaxUsedNodes, sizeof(int), 1, rbl); // make sure we never exceed the limit - if (iMaxUsedNodes > MAX_NODES) - iMaxUsedNodes = MAX_NODES; + iMaxUsedNodes = std::min(iMaxUsedNodes, MAX_NODES); - unsigned long iSize = (iMaxUsedNodes * MAX_NODES) / 8; + const unsigned long iSize = iMaxUsedNodes * MAX_NODES / 8; // Read table from what we know - fread(cVisTable, iSize, 1, rbl); - fread(iVisChecked, sizeof(iVisChecked), 1, rbl); - } else if (iVersion == FILE_EXP_VER2) { + std::fread(cVisTable.data(), iSize, 1, rbl); + std::fread(iVisChecked, sizeof(iVisChecked), 1, rbl); + } + else if (iVersion == FILE_EXP_VER2) { for (i = 0; i < MAX_NODES; i++) { - fread(&InfoNodes[i].fDanger[0], sizeof(Vector), 1, rbl); - fread(&InfoNodes[i].fDanger[1], sizeof(Vector), 1, rbl); - - fread(&InfoNodes[i].fContact[0], sizeof(Vector), 1, rbl); - fread(&InfoNodes[i].fContact[1], sizeof(Vector), 1, rbl); + std::fread(&InfoNodes[i], sizeof(tInfoNode), 1, rbl); } - fread(&iMaxUsedNodes, sizeof(int), 1, rbl); - + std::fread(&iMaxUsedNodes, sizeof(int), 1, rbl); // make sure we never exceed the limit - if (iMaxUsedNodes > MAX_NODES) - iMaxUsedNodes = MAX_NODES; + iMaxUsedNodes = std::min(iMaxUsedNodes, MAX_NODES); - unsigned long iSize = (iMaxUsedNodes * MAX_NODES) / 8; + const unsigned long iSize = iMaxUsedNodes * MAX_NODES / 8; // Now read the cVisTable from what we know ClearVisibilityTable(); // clear first @@ -1566,7 +1617,7 @@ void cNodeMachine::experience_load() { break; // Read table from what we know - fread(&cVisTable[iPos], iChunk, 1, rbl); + std::fread(&cVisTable[iPos], iChunk, 1, rbl); // When we exceed the size we make sure that the chunk is not to big if (iRemaining < iChunk) @@ -1579,82 +1630,84 @@ void cNodeMachine::experience_load() { break; // escape } - fread(iVisChecked, sizeof(iVisChecked), 1, rbl); + std::fread(iVisChecked, sizeof(iVisChecked), 1, rbl); } - fclose(rbl); + std::fclose(rbl); } } // Save -void cNodeMachine::save() { +void cNodeMachine::save() const +{ char dirname[256]; char filename[256]; - int i, n; // Set Directory name - strcpy(dirname, "data/cstrike/maps/"); - strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, ".rbn"); // nodes file + std::strcpy(dirname, "data/cstrike/maps/"); + std::strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, ".rbn"); // nodes file // writes whole path into "filename", Linux compatible UTIL_BuildFileNameRB(dirname, filename); - FILE *rbl; // Only save if lock type is < 1 - rbl = fopen(filename, "wb"); + FILE* rbl = std::fopen(filename, "wb"); - if (rbl != NULL) { + if (rbl != nullptr) { // Write down version number - int iVersion = FILE_NODE_VER1; - fwrite(&iVersion, sizeof(int), 1, rbl); - for (i = 0; i < MAX_NODES; i++) { - fwrite(&Nodes[i].origin, sizeof(Vector), 1, rbl); - for (n = 0; n < MAX_NEIGHBOURS; n++) - fwrite(&Nodes[i].iNeighbour[n], sizeof(int), 1, rbl); + constexpr int iVersion = FILE_NODE_VER1; + std::fwrite(&iVersion, sizeof(int), 1, rbl); + for (const tNode& Node : Nodes) + { + std::fwrite(&Node.origin, sizeof(Vector), 1, rbl); + for (const int& n : Node.iNeighbour) + std::fwrite(&n, sizeof(int), 1, rbl); // save bit flags - fwrite(&Nodes[i].iNodeBits, sizeof(int), 1, rbl); + std::fwrite(&Node.iNodeBits, sizeof(int), 1, rbl); } - fclose(rbl); + std::fclose(rbl); } else - fprintf(stderr, "Cannot write file %s\n", filename); + std::fprintf(stderr, "Cannot write file %s\n", filename); } -void cNodeMachine::save_important() { +void cNodeMachine::save_important() const +{ char dirname[256]; char filename[256]; // Set Directory name - strcpy(dirname, "data/cstrike/ini/"); - strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, ".ini"); // nodes file + std::strcpy(dirname, "data/cstrike/ini/"); + std::strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, ".ini"); // nodes file // writes whole path into "filename", Linux compatible UTIL_BuildFileNameRB(dirname, filename); // Only save if lock type is < 1 - FILE *rbl = fopen(filename, "w+t"); + FILE *rbl = std::fopen(filename, "w+t"); if (rbl) { - fprintf(rbl, + std::fprintf(rbl, "; RealBot : Important Area Definition file\n; Do not hand-edit, this is _not_ an editable ini file!\n;\n\n"); // save important areas: - for (int iGn = 0; iGn < MAX_GOALS; iGn++) { - if (Goals[iGn].iType == GOAL_IMPORTANT) { + for (const tGoal& Goal : Goals) + { + if (Goal.iType == GOAL_IMPORTANT) { // save this area - fprintf(rbl, "[AREA]\n"); - Vector iGoalVector = node_vector(Goals[iGn].iNode); - fprintf(rbl, "X=%f\n", iGoalVector.x); - fprintf(rbl, "Y=%f\n", iGoalVector.y); - fprintf(rbl, "Z=%f\n\n", iGoalVector.z); + std::fprintf(rbl, "[AREA]\n"); + const Vector iGoalVector = node_vector(Goal.iNode); + std::fprintf(rbl, "X=%f\n", iGoalVector.x); + std::fprintf(rbl, "Y=%f\n", iGoalVector.y); + std::fprintf(rbl, "Z=%f\n\n", iGoalVector.z); } } - fprintf(rbl, "; Eof"); - fclose(rbl); + std::fprintf(rbl, "; Eof"); + std::fclose(rbl); } } @@ -1662,34 +1715,34 @@ void cNodeMachine::save_important() { void cNodeMachine::load() { char dirname[256]; char filename[256]; - int i, n; // Set Directory name - strcpy(dirname, "data/cstrike/maps/"); + std::strcpy(dirname, "data/cstrike/maps/"); - strcat(dirname, STRING(gpGlobals->mapname)); - strcat(dirname, ".rbn"); // nodes file + std::strcat(dirname, STRING(gpGlobals->mapname)); + std::strcat(dirname, ".rbn"); // nodes file // writes whole path into "filename", Linux compatible UTIL_BuildFileNameRB(dirname, filename); - FILE *rbl; - rbl = fopen(filename, "rb"); + FILE* rbl = std::fopen(filename, "rb"); - if (rbl != NULL) { - int iVersion = FILE_NODE_VER1; - fread(&iVersion, sizeof(int), 1, rbl); + if (rbl != nullptr) { + int i; + int iVersion = FILE_NODE_VER1; + std::fread(&iVersion, sizeof(int), 1, rbl); // Version 1.0 if (iVersion == FILE_NODE_VER1) { for (i = 0; i < MAX_NODES; i++) { - fread(&Nodes[i].origin, sizeof(Vector), 1, rbl); - for (n = 0; n < MAX_NEIGHBOURS; n++) { - fread(&Nodes[i].iNeighbour[n], sizeof(int), 1, rbl); + std::fread(&Nodes[i].origin, sizeof(Vector), 1, rbl); + for (int& n : Nodes[i].iNeighbour) + { + std::fread(&n, sizeof(int), 1, rbl); } // save bit flags - fread(&Nodes[i].iNodeBits, sizeof(int), 1, rbl); + std::fread(&Nodes[i].iNodeBits, sizeof(int), 1, rbl); if (Nodes[i].origin != Vector(9999, 9999, 9999)) iMaxUsedNodes = i; @@ -1704,7 +1757,7 @@ void cNodeMachine::load() { AddToMeredian(iX, iY, i); } - fclose(rbl); + std::fclose(rbl); // 04/07/04 // Check for full nodes table @@ -1713,7 +1766,7 @@ void cNodeMachine::load() { rblog("!!! Nodes table is full\n"); char msg[80]; - sprintf(msg, "After NodeMachine::load iMaxUsedNodes=%d\n", + snprintf(msg, sizeof(msg), "After NodeMachine::load iMaxUsedNodes=%d\n", iMaxUsedNodes); rblog(msg); SERVER_PRINT("Going to load IAD file : "); @@ -1726,35 +1779,44 @@ void cNodeMachine::load() { } void cNodeMachine::ClearImportantGoals() { - for (int iGn = 0; iGn < MAX_GOALS; iGn++) { - if (Goals[iGn].iType == GOAL_IMPORTANT && Goals[iGn].iNode > -1) { - Goals[iGn].iType = -1; - Goals[iGn].iNode = -1; - Goals[iGn].pGoalEdict = NULL; - memset(Goals[iGn].name, 0, sizeof(Goals[iGn].name)); + for (tGoal& Goal : Goals) + { + if (Goal.iType == GOAL_IMPORTANT && Goal.iNode > -1) { + Goal.iType = -1; + Goal.iNode = -1; + Goal.pGoalEdict = nullptr; + std::memset(Goal.name, 0, sizeof(Goal.name)); } } } // Draw path 0 (user) -void cNodeMachine::path_draw(edict_t *pEntity) { +void cNodeMachine::path_draw(edict_t* pEntity) const +{ //DebugOut("waypoint: waypoint_draw()\n"); - int i = 0, max_drawn = 0; - - for (i = 0; i < MAX_NODES; i++) { - int iNode = iPath[draw_nodepath][i]; - int iNextNode = iPath[draw_nodepath][(i + 1)]; + int max_drawn = 0; + + // Declare 'start' vector outside the loop + Vector start; + for (int i = 0; i < MAX_NODES; i++) { + const int iNode = iPath[draw_nodepath][i]; + int iNextNode = -1; // Initialize to -1 + + if (i < MAX_NODES - 1) { // Check if i is less than MAX_NODES - 1 + iNextNode = iPath[draw_nodepath][(i + 1)]; + } + if (iNode > -1 && iNextNode > -1) { - Vector start = Nodes[iNode].origin; + start = Nodes[iNode].origin; Vector end = Nodes[iNextNode].origin; - bool good = VectorIsVisibleWithEdict(pEntity, end, "none"); - int angle_to_waypoint = - FUNC_InFieldOfView(pEntity, (end - pEntity->v.origin)); + const bool good = VectorIsVisibleWithEdict(pEntity, end, "none"); + const int angle_to_waypoint = + FUNC_InFieldOfView(pEntity, end - pEntity->v.origin); if (max_drawn < 39 && good && angle_to_waypoint < 50) { - int red = 255; + constexpr int red = 255; int green = 0; int blue = 255; int width = 15; @@ -1783,7 +1845,7 @@ void cNodeMachine::path_draw(edict_t *pEntity) { } // Spread contact areas -void cNodeMachine::contact(int iNode, int iTeam) { +void cNodeMachine::contact(const int iNode, const int iTeam) { if (iNode < 0 || iNode >= MAX_NODES) return; @@ -1801,19 +1863,19 @@ void cNodeMachine::contact(int iNode, int iTeam) { // Go through all valid nodes, except iNode, and increase danger if needed. for (int i = 0; i < MAX_NODES; i++) { if (Nodes[i].origin != Vector(9999, 9999, 9999) && i != iNode) { - float fDist = func_distance(Nodes[i].origin, Nodes[iNode].origin); - if (fDist < NODE_CONTACT_DIST) { + const float fDist = func_distance(Nodes[i].origin, Nodes[iNode].origin); + if (fDist < static_cast(NODE_CONTACT_DIST)) { //Using TraceHull to detect de_aztec bridge and other entities. TraceResult tr; //UTIL_TraceHull(Nodes[iNode].origin, Nodes[i].origin, ignore_monsters, human_hull, NULL, &tr); UTIL_TraceHull(Nodes[iNode].origin, Nodes[i].origin, - ignore_monsters, point_hull, NULL, &tr); + ignore_monsters, point_hull, nullptr, &tr); // within distance and 'reachable' - if (tr.flFraction >= 1.0) { - double costIncrease = (fDist / NODE_CONTACT_DIST) * NODE_CONTACT_STEP; - InfoNodes[i].fContact[iTeam] += costIncrease; + if (tr.flFraction >= 1.0f) { + const double costIncrease = fDist / static_cast(NODE_CONTACT_DIST) * NODE_CONTACT_STEP; + InfoNodes[i].fContact[iTeam] += static_cast(costIncrease); } } } @@ -1821,7 +1883,7 @@ void cNodeMachine::contact(int iNode, int iTeam) { } // Spread danger around -void cNodeMachine::danger(int iNode, int iTeam) { +void cNodeMachine::danger(const int iNode, const int iTeam) { if (iNode < 0 || iNode >= MAX_NODES) return; @@ -1842,16 +1904,16 @@ void cNodeMachine::danger(int iNode, int iTeam) { // Go through all valid nodes, except iNode, and increase danger if needed. for (int i = 0; i < MAX_NODES; i++) { if (Nodes[i].origin != Vector(9999, 9999, 9999) && i != iNode) { - float fDist = func_distance(Nodes[i].origin, Nodes[iNode].origin); + const float fDist = func_distance(Nodes[i].origin, Nodes[iNode].origin); if (fDist < NODE_DANGER_DIST) { //Using TraceHull to detect de_aztec bridge and other entities. TraceResult tr; UTIL_TraceHull(Nodes[iNode].origin, Nodes[i].origin, - ignore_monsters, point_hull, NULL, &tr); + ignore_monsters, point_hull, nullptr, &tr); // within distance and reachable - if (tr.flFraction >= 1.0) { - double costIncrease = (fDist / NODE_DANGER_DIST) * NODE_DANGER_STEP; + if (tr.flFraction >= 1.0f) { + const float costIncrease = fDist / NODE_DANGER_DIST * NODE_DANGER_STEP; InfoNodes[i].fDanger[iTeam] += costIncrease; } } @@ -1860,7 +1922,7 @@ void cNodeMachine::danger(int iNode, int iTeam) { } // Adds a new goal to the array -void cNodeMachine::addGoal(edict_t *pEdict, int goalType, Vector vVec) { +void cNodeMachine::addGoal(edict_t* pEdict, const int goalType, const Vector& vVec) { // // 14/06/04 // Be carefull with adding SERVER_PRINT messages here @@ -1873,19 +1935,19 @@ void cNodeMachine::addGoal(edict_t *pEdict, int goalType, Vector vVec) { return; // do not add goal that is already in our list } - int index = getFreeGoalIndex(); + const int index = getFreeGoalIndex(); if (index < 0) { return; } - int distance = NODE_ZONE * 2; + float distance = static_cast(NODE_ZONE) * 2.0f; // some goals require very close nodes if (goalType == GOAL_HOSTAGE || goalType == GOAL_VIPSAFETY || goalType == GOAL_RESCUEZONE || goalType == GOAL_BOMBSPOT) { - distance = NODE_ZONE * 0.8; + distance = static_cast(NODE_ZONE) * 0.8f; } int nNode = getClosestNode(vVec, distance, pEdict); @@ -1899,8 +1961,8 @@ void cNodeMachine::addGoal(edict_t *pEdict, int goalType, Vector vVec) { } } - tGoal *goal = getGoal(index); - if (goal == NULL) { + tGoal* goal = getGoal(index); + if (goal == nullptr) { rblog("No valid goal index found - bailing\n"); return; } @@ -1908,18 +1970,17 @@ void cNodeMachine::addGoal(edict_t *pEdict, int goalType, Vector vVec) { goal->index = index; goal->pGoalEdict = pEdict; goal->iType = goalType; - strcpy(goal->name, getGoalTypeAsText(*goal)); + //strcpy(goal->name, getGoalTypeAsText(*goal)); //That appears to trigger crash [APG]RoboCop[CL] - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Adding goal at index %d of type %s, with nearby node %d\n", index, goal->name, nNode); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "Adding goal at index %d of type %s, with nearby node %d\n", index, goal->name, nNode); rblog(msg); } -tGoal *cNodeMachine::getGoal(int index) { +tGoal *cNodeMachine::getGoal(const int index) { if (index < 0 || index >= MAX_GOALS) { rblog("ERROR: Asking to retrieve goal with invalid index! Returning goal NULL\n"); - return NULL; + return nullptr; } // char msg[255]; // sprintf(msg, "Getting goal by index [%d]\n", index); @@ -1934,8 +1995,7 @@ tGoal *cNodeMachine::getGoal(int index) { */ int cNodeMachine::getFreeGoalIndex() const { int index = -1; - int g = 0; // <-- ADDED BY PMB ELSE LINUX COMPILER COMPLAINS (ISO COMPLIANCE) - for (g = 0; g < MAX_GOALS; g++) { + for (int g = 0; g < MAX_GOALS; g++) { if (Goals[g].iType == GOAL_NONE) { index = g; break; @@ -1951,31 +2011,28 @@ int cNodeMachine::getFreeGoalIndex() const { * @param pEdict * @return */ -bool cNodeMachine::hasGoalWithEdict(edict_t *pEdict) { - if (pEdict == NULL) return false; // no edict == by default no - - for (int g = 0; g < MAX_GOALS; g++) { - if (Goals[g].pGoalEdict == pEdict) { - return true; - } - } +bool cNodeMachine::hasGoalWithEdict(edict_t* pEdict) const +{ + if (pEdict == nullptr) return false; // no edict == by default no - // Does not exist - return false; + return std::any_of(std::begin(Goals), std::end(Goals), [pEdict](const tGoal& goal) { + return goal.pGoalEdict == pEdict; + }); } void cNodeMachine::resetCheckedValuesForGoals() { - for (int g = 0; g < MAX_GOALS; g++) { - if (Goals[g].iChecked > 0) - Goals[g].iChecked = 0; + for (tGoal& Goal : Goals) + { + Goal.iChecked = std::min(Goal.iChecked, 0); } } // returns goal type from node, -1 for unknown -int cNodeMachine::getGoalIndexFromNode(int iNode) { - for (int g = 0; g < MAX_GOALS; g++) - if (Goals[g].iNode == iNode) - return Goals[g].iType; +int cNodeMachine::getGoalIndexFromNode(const int iNode) const +{ + for (const tGoal& Goal : Goals) + if (Goal.iNode == iNode) + return Goal.iType; return -1; } @@ -1983,8 +2040,8 @@ int cNodeMachine::getGoalIndexFromNode(int iNode) { void cNodeMachine::updateGoals() { rblog("cNodeMachine::updateGoals - START\n"); for (int i = 0; i < MAX_GOALS; i++) { - tGoal *goal = getGoal(i); - if (goal == NULL || goal->iNode < 0) continue; + const tGoal *goal = getGoal(i); + if (goal == nullptr || goal->iNode < 0) continue; if (goal->iType == GOAL_HOSTAGE) { initGoal(i); @@ -1995,13 +2052,13 @@ void cNodeMachine::updateGoals() { } } - edict_t *pent = NULL; + edict_t *pent = nullptr; // re-add goals for hostages so we have the latest information about them // GOAL #5 - Hostages (this is the 'starting' position): - while ((pent = UTIL_FindEntityByClassname(pent, "hostage_entity")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "hostage_entity")) != nullptr) { // verify hostage is still rescueable - if (isHostageRescued(NULL, pent) || !FUNC_EdictIsAlive(pent)) { + if (isHostageRescued(nullptr, pent) || !FUNC_EdictIsAlive(pent)) { continue; // skip dead or already rescued hostages } @@ -2010,17 +2067,15 @@ void cNodeMachine::updateGoals() { // SEARCH PLAYERS FOR ENEMIES for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - + edict_t* pPlayer = INDEXENT(i); + // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free)) { + if (pPlayer && !pPlayer->free) { // skip this player if not alive (i.e. dead or dying) - if (!IsAlive(pPlayer)) - continue; - } - - if (UTIL_IsVip(pPlayer)) { - addGoal(pPlayer, GOAL_VIP, pPlayer->v.origin + Vector(0,0,32)); + if (!IsAlive(pPlayer)) continue; + if (UTIL_IsVip(pPlayer)) { + addGoal(pPlayer, GOAL_VIP, pPlayer->v.origin + Vector(0, 0, 32)); + } } } rblog("cNodeMachine::updateGoals - FINISHED\n"); @@ -2039,61 +2094,61 @@ void cNodeMachine::setUpInitialGoals() { // because Nodes get expanded all the time the bot should eventually learn // how to reach other goals. - edict_t *pent = NULL; + edict_t *pent = nullptr; // GOAL #1 - Counter Terrorist Spawn points. - while ((pent = UTIL_FindEntityByClassname(pent, "info_player_start")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "info_player_start")) != nullptr) { addGoal(pent, GOAL_SPAWNCT, pent->v.origin); } // GOAL #2 - Terrorist Spawn points. - while ((pent = UTIL_FindEntityByClassname(pent, "info_player_deathmatch")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "info_player_deathmatch")) != nullptr) { addGoal(pent, GOAL_SPAWNT, pent->v.origin); } // GOAL #3 - Hostage rescue zone - while ((pent = UTIL_FindEntityByClassname(pent, "func_hostage_rescue")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "func_hostage_rescue")) != nullptr) { addGoal(pent, GOAL_RESCUEZONE, VecBModelOrigin(pent)); } // rescue zone can also be an entity of info_hostage_rescue - while ((pent = UTIL_FindEntityByClassname(pent, "info_hostage_rescue")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "info_hostage_rescue")) != nullptr) { addGoal(pent, GOAL_RESCUEZONE, VecBModelOrigin(pent)); } // GOAL #4 - Bombspot zone // Bomb spot - while ((pent = UTIL_FindEntityByClassname(pent, "func_bomb_target")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "func_bomb_target")) != nullptr) { addGoal(pent, GOAL_BOMBSPOT, VecBModelOrigin(pent)); } - while ((pent = UTIL_FindEntityByClassname(pent, "info_bomb_target")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "info_bomb_target")) != nullptr) { addGoal(pent, GOAL_BOMBSPOT, VecBModelOrigin(pent)); } // GOAL #5 - Hostages (this is the 'starting' position): - while ((pent = UTIL_FindEntityByClassname(pent, "hostage_entity")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "hostage_entity")) != nullptr) { addGoal(pent, GOAL_HOSTAGE, pent->v.origin + Vector(0,0,32)); } // GOAL #6 - VIP (this is the 'starting' position) (EVY) - while ((pent = UTIL_FindEntityByClassname(pent, "info_vip_start")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "info_vip_start")) != nullptr) { addGoal(pent, GOAL_VIP, VecBModelOrigin(pent)); } // GOAL #7 - VIP safety (this is the 'rescue' position) (EVY) - while ((pent = UTIL_FindEntityByClassname(pent, "func_vip_safetyzone")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "func_vip_safetyzone")) != nullptr) { addGoal(pent, GOAL_VIPSAFETY, VecBModelOrigin(pent)); } // GOAL #8 - Escape zone for es_ (EVY) - while ((pent = UTIL_FindEntityByClassname(pent, "func_escapezone")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "func_escapezone")) != nullptr) { addGoal(pent, GOAL_ESCAPEZONE, VecBModelOrigin(pent)); } // 05/07/04 // GOAL #9 - Free weapons on the ground EVY - while ((pent = UTIL_FindEntityByClassname(pent, "armoury_entity")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "armoury_entity")) != nullptr) { addGoal(pent, GOAL_WEAPON, VecBModelOrigin(pent)); } @@ -2103,37 +2158,29 @@ void cNodeMachine::setUpInitialGoals() { } // Find a goal, and return the node close to it -tGoal *cNodeMachine::getRandomGoalByType(int goalType) { +tGoal* cNodeMachine::getRandomGoalByType(const int goalType) { if (goalType == GOAL_NONE) - return NULL; - - int possibleGoalNodes[MAX_GOALS]; - for (int c = 0; c < MAX_GOALS; c++) { - possibleGoalNodes[c] = -1; - } + return nullptr; - int possibleCandidateIndex = 0; - for (int goalIndex = 0; goalIndex < MAX_GOALS; goalIndex++) { - if (Goals[goalIndex].iType == goalType && // type equals requested type - Goals[goalIndex].iNode > -1) { // and it has a node -// possibleGoalNodes[possibleCandidateIndex] = Goals[goalIndex].iNode; - possibleGoalNodes[possibleCandidateIndex] = goalIndex; - possibleCandidateIndex++; + std::vector possibleGoalIndices; + for (int goalIndex = 0; goalIndex < MAX_GOALS; ++goalIndex) { + if (Goals[goalIndex].iType == goalType && Goals[goalIndex].iNode > -1) { + possibleGoalIndices.push_back(goalIndex); } } - if (possibleCandidateIndex == 0) - return NULL; // nothing found :( + if (possibleGoalIndices.empty()) + return nullptr; - // we have an amount of goals, pick one randomly - int randomGoalIndex = RANDOM_LONG(0, (possibleCandidateIndex - 1)); + const int randomIndex = RANDOM_LONG(0, static_cast(possibleGoalIndices.size()) - 1); + const int chosenGoalIndex = possibleGoalIndices[randomIndex]; char msg[255]; - sprintf(msg, "cNodeMachine::getRandomGoalByType() - Found %d nodes of type %d and picked %d\n", - possibleCandidateIndex, goalType, randomGoalIndex); + snprintf(msg, sizeof(msg), "cNodeMachine::getRandomGoalByType() - Found %zu nodes of type %d and picked index %d (goal %d)\n", + possibleGoalIndices.size(), goalType, randomIndex, chosenGoalIndex); rblog(msg); - return getGoal(randomGoalIndex); + return getGoal(chosenGoalIndex); } // Contact scaler (on round start) @@ -2142,22 +2189,21 @@ void cNodeMachine::scale_contact() { // rescale all other values to stay correct. int iTeam = 0; while (iTeam < 2) { - float fHighest = 0.0; - int i = 0; // <-- ADDED BY PMB ELSE LINUX COMPILER ISNT HAPPY + float fHighest = 0.0f; + int i; // <-- ADDED BY PMB ELSE LINUX COMPILER ISNT HAPPY for (i = 0; i < MAX_NODES; i++) - if (InfoNodes[i].fContact[iTeam] > fHighest) - fHighest = InfoNodes[i].fContact[iTeam]; + fHighest = std::max(InfoNodes[i].fContact[iTeam], fHighest); - if (fHighest < 1.0) { + if (fHighest < 1.0f) { iTeam++; continue; // no need to rescale } // Check how much we passed the limit - float fLimit = 1.0 / fHighest; + const float fLimit = 1.0f / fHighest; // Now rescale all for (i = 0; i < MAX_NODES; i++) - if (InfoNodes[i].fContact[iTeam] > 0.0) + if (InfoNodes[i].fContact[iTeam] > 0.0f) InfoNodes[i].fContact[iTeam] *= fLimit; // rescale iTeam++; @@ -2172,22 +2218,21 @@ void cNodeMachine::scale_danger() { // rescale all other danger values to stay correct. int iTeam = 0; while (iTeam < 2) { - float fHighest = 0.0; - int i = 0; // ADDED BY PMB FOR COMPILING UNDER LINUX + float fHighest = 0.0f; + int i; // ADDED BY PMB FOR COMPILING UNDER LINUX for (i = 0; i < MAX_NODES; i++) - if (InfoNodes[i].fDanger[iTeam] > fHighest) - fHighest = InfoNodes[i].fDanger[iTeam]; + fHighest = std::max(InfoNodes[i].fDanger[iTeam], fHighest); - if (fHighest < 0.8) { + if (fHighest < 0.8f) { iTeam++; continue; // no need to rescale } // Check how much we passed the limit - float fLimit = 0.7 / fHighest; + const float fLimit = 0.7f / fHighest; // Now rescale all for (i = 0; i < MAX_NODES; i++) - if (InfoNodes[i].fDanger[iTeam] > 0.0) + if (InfoNodes[i].fDanger[iTeam] > 0.0f) InfoNodes[i].fDanger[iTeam] *= fLimit; // rescale iTeam++; @@ -2197,109 +2242,91 @@ void cNodeMachine::scale_danger() { } // Pathfinder -bool cNodeMachine::createPath(int nodeStartIndex, int nodeTargetIndex, int botIndex, cBot *pBot, int iFlags) { +bool cNodeMachine::createPath(const int nodeStartIndex, const int nodeTargetIndex, const int botIndex, cBot* pBot, int iFlags) { // Will create a path from nodeStartIndex to nodeTargetIndex, and store it into index number iPathId if (pBot) { - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "createPath(from->%d, to->%d, botIndex->%d)", nodeStartIndex, nodeTargetIndex, botIndex); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "createPath(from->%d, to->%d, botIndex->%d)", nodeStartIndex, nodeTargetIndex, botIndex); pBot->rprint("cNodeMachine::createPath", msg); } - if (nodeStartIndex < 0 || nodeTargetIndex < 0 || botIndex < 0) + if (nodeStartIndex < 0 || nodeTargetIndex < 0 || botIndex < 0 || + nodeStartIndex >= MAX_NODES || nodeTargetIndex >= MAX_NODES) { return false; // do not create a path when invalid params given + } if (nodeStartIndex > iMaxUsedNodes || nodeTargetIndex > iMaxUsedNodes) return false; // do not create a path when invalid params given - int botTeam = -1; if (pBot) { - botTeam = UTIL_GetTeam(pBot->pEdict); // Stefan: yes we use 0-1 based, not 1-2 based + UTIL_GetTeam(pBot->pEdict); // Stefan: yes we use 0-1 based, not 1-2 based } - const Vector &INVALID_VECTOR = Vector(9999, 9999, 9999); - - // start or target vector may not be invalid if (Nodes[nodeStartIndex].origin == INVALID_VECTOR || Nodes[nodeTargetIndex].origin == INVALID_VECTOR) { rblog("Invalid start and target index\n"); return false; } - int path_index = 0, nodeIndex; - path_clear(botIndex); - // INIT: Start - makeAllWaypointsAvailable(); - - // Our start waypoint is open - float gCost = 0.0f; // distance from starting node - float hCost = func_distance(Nodes[nodeStartIndex].origin, - Nodes[nodeTargetIndex].origin); // distance from end node to node - float cost = gCost + hCost; - closeNode(nodeStartIndex, nodeStartIndex, cost); - openNeighbourNodes(nodeStartIndex, nodeStartIndex, nodeTargetIndex, -1); - - bool pathFound = false; // is it still valid to loop through the lists for pathfinding? - - int nodesEvaluated = 0; - // INIT: End - // PATHFINDER: Start - while (!pathFound) { - float lowestScore = 99999999.0f; - int nodeToClose = -1; - - // go through all OPEN waypoints - for (nodeIndex = 0; nodeIndex < MAX_NODES; nodeIndex++) { - tNodestar &nodeStar = astar_list[nodeIndex]; - - if (nodeStar.state == CLOSED || nodeStar.state == AVAILABLE) continue; - - // OPEN waypoint - tNode &node = Nodes[nodeIndex]; - if (node.origin == INVALID_VECTOR) { - rblog("Evaluating an INVALID vector!?!\n"); - nodeStar.state = CLOSED; - continue; // it is an invalid vector - } + std::priority_queue openList; + std::vector nodeData(MAX_NODES); - // nodeIndex is target, so target found - if (nodeIndex == nodeTargetIndex) { - pathFound = true; - rblog("Target found\n"); - break; // Get out of here - } + // Initialize node data + for (int i = 0; i < MAX_NODES; ++i) { + nodeData[i].state = AVAILABLE; + nodeData[i].parent = -1; + nodeData[i].cost = std::numeric_limits::max(); + } - // open is not target, so go over its neighbours (which have been opened) and find the lowest cost - if (nodeStar.cost < lowestScore) { - nodeToClose = nodeIndex; - lowestScore = nodeStar.cost; - } + // Setup start node + nodeData[nodeStartIndex].cost = func_distance(Nodes[nodeStartIndex].origin, Nodes[nodeTargetIndex].origin); + nodeData[nodeStartIndex].state = OPEN; + openList.push({ OPEN, nodeStartIndex, nodeData[nodeStartIndex].cost, 0.0f }); + + bool pathFound = false; + + while (!openList.empty()) { + const int currentNodeIndex = openList.top().parent; + openList.pop(); + + if (nodeData[currentNodeIndex].state == CLOSED) { + continue; } - // a node that should be closed is an evaluated node and the most preferred one. - // open up all neighbouring nodes, and close this one - if (nodeToClose > -1) { -// char msg[255]; -// sprintf(msg, "Found node to close [%d]\n", nodeToClose); -// rblog(msg); - astar_list[nodeToClose].state = CLOSED; - int botTeam = -1; + if (currentNodeIndex == nodeTargetIndex) { + pathFound = true; + break; + } + + nodeData[currentNodeIndex].state = CLOSED; + + const tNode& currentNode = Nodes[currentNodeIndex]; + for (const int neighborIndex : currentNode.iNeighbour) { + if (neighborIndex < 0 || Nodes[neighborIndex].origin == INVALID_VECTOR || nodeData[neighborIndex].state == CLOSED) { + continue; + } + + const float gCost = nodeData[currentNodeIndex].cost + func_distance(currentNode.origin, Nodes[neighborIndex].origin); + const float hCost = func_distance(Nodes[neighborIndex].origin, Nodes[nodeTargetIndex].origin); + float totalCost = gCost + hCost; + if (pBot) { - botTeam = pBot->iTeam; + const float dangerCost = InfoNodes[neighborIndex].fDanger[pBot->iTeam] * totalCost; + totalCost += dangerCost; } - openNeighbourNodes(nodeStartIndex, nodeToClose, nodeTargetIndex, botTeam); - } else { -// rblog("Did not find any open waypoint\n"); - break; + if (totalCost < nodeData[neighborIndex].cost) { + nodeData[neighborIndex].parent = currentNodeIndex; + nodeData[neighborIndex].cost = totalCost; + nodeData[neighborIndex].state = OPEN; + openList.push({ OPEN, neighborIndex, totalCost, 0.0f }); + } } } - // PATHFINDER: End - // RESULT: Success if (!pathFound) { if (pBot) { pBot->rprint("cNodeMachine::createPath", "Failed to create path"); @@ -2307,100 +2334,33 @@ bool cNodeMachine::createPath(int nodeStartIndex, int nodeTargetIndex, int botIn return false; } - for (nodeIndex = 0; nodeIndex < MAX_PATH_NODES; nodeIndex++) { - - tNodestar &nodeStar = astar_list[nodeIndex]; - if (nodeStar.state == AVAILABLE) continue; - -// char msg[255]; -// memset(msg, 0, sizeof(msg)); -// if (nodeStar.state == CLOSED) { -// sprintf(msg, "Node [%d] is CLOSED. Cost = %f. Parent = %d\n", nodeIndex, nodeStar.cost, nodeStar.parent); -// } else if (nodeStar.state == OPEN) { -// sprintf(msg, "Node [%d] is OPEN. Cost = %f. Parent = %d\n", nodeIndex, nodeStar.cost, nodeStar.parent); -// } -// rblog(msg); - } - - // Build path (from goal to start, read out parent waypoint to backtrace) - int temp_path[MAX_PATH_NODES]; - - // INIT: Start - for (nodeIndex = 0; nodeIndex < MAX_PATH_NODES; nodeIndex++) - temp_path[nodeIndex] = -1; - - // The path has been built yet? - bool built = false; - - // The variables needed to backtrace - // wpta = waypoint we use to backtrace (starting at goal) - // p = index for temp_path (the path will be GOAL-START, reversed later) - int wpta = nodeTargetIndex, p = 0; - - // INIT: End - - // START: When path is not built yet - while (!built) { - temp_path[p] = wpta; // Copy the waypoint into temp_path[index] - - // IF: At current (start) waypoint - if (wpta == nodeStartIndex) { - // Check if we did not already had this waypoint before - built = true; // We finished building this path. - } else { - // Whenever wpta is containing bad information... - if (wpta < 0 || wpta > MAX_NODES) - break; // ...get out aswell - } - - // waypoint we use to backtrace will be set to parent waypoint. - wpta = astar_list[wpta].parent; - - // Increase index for temp_path - p++; - - // Whenever we reach the limit, get out. - if (p >= MAX_PATH_NODES) - break; - + // Build path + std::vector temp_path; + int wpta = nodeTargetIndex; + while (wpta != -1) { + temp_path.push_back(wpta); + wpta = nodeData[wpta].parent; } - // INIT: Start - path_index = 0; // done above, but done again to be sure - // INIT: End - - // Now set the path up correctly - for (nodeIndex = (MAX_NODES - 1); nodeIndex > -1; nodeIndex--) { - int node = temp_path[nodeIndex]; - if (node < 0) - continue; - - iPath[botIndex][path_index] = node; + std::reverse(temp_path.begin(), temp_path.end()); - // print out full path so we know what the order is - if (pBot != NULL) { - char pathMsg[255]; - memset(pathMsg, 0, sizeof(pathMsg)); - sprintf(pathMsg, "Bot [%d] path index [%d] has node [%d]", botIndex, path_index, node); + // Copy to bot's path array + for (size_t i = 0; i < temp_path.size() && i < MAX_PATH_NODES; ++i) { + iPath[botIndex][i] = temp_path[i]; + if (pBot) { + char pathMsg[255] = {}; + snprintf(pathMsg, sizeof(pathMsg), "Bot [%d] path index [%zu] has node [%d]", botIndex, i, temp_path[i]); pBot->rprint("cNodeMachine::createPath", pathMsg); } - - path_index++; } - // Finally there is the goal - iPath[botIndex][path_index] = nodeTargetIndex; - - // terminate path - path_index++; - iPath[botIndex][path_index] = -1; - // And set bot in motion - if (pBot != NULL) { + if (pBot != nullptr) { pBot->beginWalkingPath(); pBot->setTimeToMoveToNode(2); // set timer (how much time do we allow ourselves to reach the following node) pBot->rprint("cNodeMachine::createPath", "Path creation finished successfully"); - } else { + } + else { rblog("createPath (without bot) - path creation finished\n"); } @@ -2413,7 +2373,7 @@ bool cNodeMachine::createPath(int nodeStartIndex, int nodeTargetIndex, int botIn * @param parent * @param cost */ -void cNodeMachine::closeNode(int nodeIndex, int parent, float cost) { +void cNodeMachine::closeNode(const int nodeIndex, const int parent, const float cost) { astar_list[nodeIndex].state = CLOSED; astar_list[nodeIndex].parent = parent; astar_list[nodeIndex].cost = cost; @@ -2421,27 +2381,32 @@ void cNodeMachine::closeNode(int nodeIndex, int parent, float cost) { /** * Open all neighbouring nodes, calculate costs for each neighbouring node + * @param startNodeIndex + * @param nodeToOpenNeighboursFrom + * @param destinationNodeIndex + * @param botTeam * @param nodeStartIndex * @param parent * @param cost */ -void cNodeMachine::openNeighbourNodes(int startNodeIndex, int nodeToOpenNeighboursFrom, int destinationNodeIndex, int botTeam) { - tNode &startNode = Nodes[startNodeIndex]; // very start of path - tNode &destNode = Nodes[destinationNodeIndex]; // destination for path +void cNodeMachine::openNeighbourNodes(const int startNodeIndex, const int nodeToOpenNeighboursFrom, const int destinationNodeIndex, const int botTeam) const +{ + const tNode &startNode = Nodes[startNodeIndex]; // very start of path + const tNode &destNode = Nodes[destinationNodeIndex]; // destination for path - tNode &node = Nodes[nodeToOpenNeighboursFrom]; // node evaluating neighbours + const tNode &node = Nodes[nodeToOpenNeighboursFrom]; // node evaluating neighbours - for (int i = 0; i < MAX_NEIGHBOURS; i++) { - int neighbourNode = node.iNeighbour[i]; - if (neighbourNode < 0) continue; // skip invalid nodes + for (const int neighbourNode : node.iNeighbour) + { + if (neighbourNode < 0) continue; // skip invalid nodes if (Nodes[neighbourNode].origin == INVALID_VECTOR) continue; // skip nodes with invalid vector - float gCost = func_distance(startNode.origin, destNode.origin); // distance from starting node - float hCost = func_distance(node.origin, destNode.origin); // distance from end node to node + const float gCost = func_distance(startNode.origin, destNode.origin); // distance from starting node + const float hCost = func_distance(node.origin, destNode.origin); // distance from end node to node float cost = gCost + hCost; if (botTeam > -1) { - double dangerCost = InfoNodes[neighbourNode].fDanger[botTeam] * cost; + const float dangerCost = InfoNodes[neighbourNode].fDanger[botTeam] * cost; // double contactCost = InfoNodes[neighbourNode].fContact[botTeam] * cost; cost += dangerCost; @@ -2465,44 +2430,113 @@ void cNodeMachine::openNeighbourNodes(int startNodeIndex, int nodeToOpenNeighbou // rblog(msg); nodeStar.parent = nodeToOpenNeighboursFrom; nodeStar.cost = cost; - } + } } - } + } } /** * This marks all waypoints to be available to be evaluated again * @param nodeIndex */ -void cNodeMachine::makeAllWaypointsAvailable() const { - int nodeIndex = 0; - for (nodeIndex = 0; nodeIndex < MAX_NODES; nodeIndex++) { - astar_list[nodeIndex].cost = 0; - astar_list[nodeIndex].parent = -1; - astar_list[nodeIndex].state = AVAILABLE; +void cNodeMachine::makeAllWaypointsAvailable() +{ + for (tNodestar& nodeIndex : astar_list) + { + nodeIndex.cost = 0; + nodeIndex.parent = -1; + nodeIndex.state = AVAILABLE; } // rblog("All nodes set to AVAILABLE\n"); } +bool cNodeMachine::isValidNodeIndex(const int index) //TODO: Experimental [APG]RoboCop[CL] +{ + return index >= 0 && index < MAX_NODES; +} + +bool cNodeMachine::isInvalidNode(const int index) //TODO: Experimental [APG]RoboCop[CL] +{ + return !isValidNodeIndex(index); +} + +//TODO: Experimental [APG]RoboCop[CL] +void cNodeMachine::buildPath(const int nodeStartIndex, const int nodeTargetIndex, const int botIndex, cBot* pBot) +{ + if (!isValidNodeIndex(nodeStartIndex) || !isValidNodeIndex(nodeTargetIndex)) { + rblog("Invalid node index provided to buildPath"); + return; + } + + // Clear the current path for the bot + path_clear(botIndex); + + // Initialize the A* algorithm + std::priority_queue openList; + std::unordered_map closedList; + + constexpr tNodestar startNode = { OPEN, -1, 0.0f, 0.0 }; + openList.push(startNode); + + while (!openList.empty()) { + tNodestar currentNode = openList.top(); + openList.pop(); + + if (currentNode.state == CLOSED) { + continue; + } + + if (currentNode.parent == nodeTargetIndex) { + // Path found, reconstruct the path + int pathIndex = 0; + while (currentNode.parent != -1) { + iPath[botIndex][pathIndex++] = currentNode.parent; + currentNode = closedList[currentNode.parent]; + } + std::reverse(iPath[botIndex], iPath[botIndex] + pathIndex); + return; + } + + currentNode.state = CLOSED; + closedList[currentNode.parent] = currentNode; + + // Open neighbor nodes + for (int neighborIndex : Nodes[currentNode.parent].iNeighbour) + { + if (neighborIndex == -1 || closedList.find(neighborIndex) != closedList.end()) { + continue; + } + + const float cost = currentNode.cost + (node_vector(currentNode.parent) - node_vector(neighborIndex)).Length(); + tNodestar neighborNode = { OPEN, currentNode.parent, cost, 0.0 }; + openList.push(neighborNode); + } + } + + rblog("Failed to build path"); +} + // Find a node which has almost no danger! -int cNodeMachine::node_camp(Vector vOrigin, int iTeam) { +int cNodeMachine::node_camp(const Vector& vOrigin, const int iTeam) const +{ // Use Meredians to search for nodes int iX, iY; VectorToMeredian(vOrigin, &iX, &iY); - float fDanger = 2.0; - float fDistance = 9999; - int iVisibility = 9999; int iBestNode = -1; // Theory: // Find a node, close, and less danger... // and with less visibility - if (iX > -1 && iY > -1) { - // Search in this meredian - for (int i = 0; i < MAX_NODES_IN_MEREDIANS; i++) - if (Meredians[iX][iY].iNodes[i] > -1) { - int iNode = Meredians[iX][iY].iNodes[i]; + if (iX > -1 && iY > -1) + { + int iVisibility = 9999; + float fDistance = 9999.0f; + float fDanger = 2.0f; + // Search in this meredian + for (const int i : Meredians[iX][iY].iNodes) + if (i > -1) { + const int iNode = i; if (Nodes[iNode].iNodeBits & BIT_WATER) continue; // next node, do not camp under water! @@ -2531,51 +2565,50 @@ int cNodeMachine::node_camp(Vector vOrigin, int iTeam) { } return iBestNode; - } // Check if iFrom is visible from other nodes (and opposite) -void cNodeMachine::vis_calculate(int iFrom) { +void cNodeMachine::vis_calculate(const int iFrom) { // Check around your area to see what is visible - float fClosest = 1024; - for (int i = 0; i < MAX_NODES; i++) - if ((i != iFrom) && (Nodes[i].origin != Vector(9999, 9999, 9999))) { - float fDistance = func_distance(Nodes[i].origin, Nodes[iFrom].origin); - if (fDistance < fClosest) { - TraceResult tr; + constexpr float MAX_VISIBILITY_DISTANCE = 1024.0f; + for (int i = 0; i < iMaxUsedNodes; ++i) { + if (i == iFrom || Nodes[i].origin == INVALID_VECTOR) { + continue; // Skip the current node or invalid nodes + } - // Visibility is not yet calculated, so determine now - if (GetVisibilityFromTo(iFrom, i) == VIS_UNKNOWN) // BERKED - { - UTIL_TraceHull(Nodes[iFrom].origin, Nodes[i].origin, ignore_monsters, point_hull, NULL, &tr); + const float fDistance = func_distance(Nodes[i].origin, Nodes[iFrom].origin); + if (fDistance >= MAX_VISIBILITY_DISTANCE) { + continue; // Skip if the node is too far + } - if (tr.flFraction < 1.0) { - SetVisibilityFromTo(iFrom, i, false); - SetVisibilityFromTo(i, iFrom, false); - } else { - SetVisibilityFromTo(iFrom, i, true); - SetVisibilityFromTo(i, iFrom, true); - } - } - } + // Visibility is not yet calculated, so determine now + if (GetVisibilityFromTo(iFrom, i) == VIS_UNKNOWN) { + TraceResult tr; + + UTIL_TraceHull(Nodes[iFrom].origin, Nodes[i].origin, ignore_monsters, point_hull, nullptr, &tr); + const bool isVisible = (tr.flFraction >= 1.0f); + SetVisibilityFromTo(iFrom, i, isVisible); + SetVisibilityFromTo(i, iFrom, isVisible); } + } } // Find a node to look at when camping -int cNodeMachine::node_look_camp(Vector vOrigin, int iTeam, - edict_t *pEdict) { +int cNodeMachine::node_look_camp(const Vector& vOrigin, const int iTeam, edict_t *pEdict) { rblog("node_look_camp - start\n"); - float fDanger = -0.1; + float fDanger = -0.1f; float fDistance = 0; int iBestNode = -2; // Theory: // Find a node, far, and a lot danger... - int iFrom = getClosestNode(vOrigin, 75, pEdict); + // and with less visibility + + const int iFrom = getClosestNode(vOrigin, 75, pEdict); // Search in this meredian for (int i = 0; i < MAX_NODES; i++) { - int iNode = i; + const int iNode = i; if (InfoNodes[iNode].fDanger[iTeam] > fDanger) if (func_distance(vOrigin, Nodes[iNode].origin) > fDistance) { // all nodes within this range may be tracelined @@ -2586,16 +2619,12 @@ int cNodeMachine::node_look_camp(Vector vOrigin, int iTeam, UTIL_TraceLine(Nodes[iFrom].origin, Nodes[iNode].origin, ignore_monsters, ignore_glass, pEdict, &tr); //UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "NODE, MEREDIAN: VISIBILITY NOT KNOWN \n"); - if (tr.flFraction < 1.0) { + if (tr.flFraction < 1.0f) { bVisible = false; // Set to false SetVisibilityFromTo(iFrom, iNode, false); SetVisibilityFromTo(iNode, iFrom, false); - } else { - SetVisibilityFromTo(iFrom, iNode, true); - SetVisibilityFromTo(iNode, iFrom, true); } - } else { if (GetVisibilityFromTo(iFrom, iNode) == VIS_BLOCKED) // BERKED { @@ -2614,7 +2643,7 @@ int cNodeMachine::node_look_camp(Vector vOrigin, int iTeam, } } char msg[255]; - sprintf(msg, "Found best node to camp at %d\n", iBestNode); + snprintf(msg, sizeof(msg), "Found best node to camp at %d\n", iBestNode); rblog(msg); return iBestNode; } @@ -2625,9 +2654,9 @@ int cNodeMachine::node_look_camp(Vector vOrigin, int iTeam, * @param pBot * @param distanceMoved */ -void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { +void cNodeMachine::path_walk(cBot *pBot, const float distanceMoved) { pBot->rprint("cNodeMachine::path_walk", "START"); - int BotIndex = pBot->iBotIndex; + const int BotIndex = pBot->iBotIndex; // Check if path is valid if (iPath[BotIndex][0] < 0) { @@ -2656,25 +2685,25 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { pBot->setMoveSpeed(pBot->f_max_speed); // Walk the path - int currentNodeToHeadFor = pBot->getCurrentPathNodeToHeadFor(); // Node we are heading for + const int currentNodeToHeadFor = pBot->getCurrentPathNodeToHeadFor(); // Node we are heading to // possibly end of path reached, overshoot destination? if (currentNodeToHeadFor < 0) { pBot->rprint_trace("cNodeMachine::path_walk", "Finished - there is no current node to head for"); pBot->forgetGoal(); - pBot->forgetPath(); + pBot->stopMoving(); return; } // when pButtonEdict is filled in, we check if we are close! if (pBot->pButtonEdict) { pBot->rprint("cNodeMachine::path_walk", "Interacting with button logic"); - Vector vButtonVector = VecBModelOrigin(pBot->pButtonEdict); + const Vector vButtonVector = VecBModelOrigin(pBot->pButtonEdict); float fDistance = 90; bool bTrigger = false; - if (strcmp(STRING(pBot->pButtonEdict->v.classname), "trigger_multiple") == 0) { + if (std::strcmp(STRING(pBot->pButtonEdict->v.classname), "trigger_multiple") == 0) { fDistance = 32; bTrigger = true; } @@ -2688,19 +2717,18 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { pBot->pEdict, &trb); } else { UTIL_TraceLine(pBot->pEdict->v.origin, vButtonVector, - ignore_monsters, dont_ignore_glass, - pBot->pEdict, &trb); + ignore_monsters, dont_ignore_glass, pBot->pEdict, &trb); } bool isGood = false; // if nothing hit: - if (trb.flFraction >= 1.0) + if (trb.flFraction >= 1.0f) { + isGood = true; + } + // we hit this button we check for + if (trb.pHit == pBot->pButtonEdict) { isGood = true; - else { - // we hit this button we check for - if (trb.pHit == pBot->pButtonEdict) - isGood = true; } if (isGood || bTrigger) { @@ -2708,7 +2736,7 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { pBot->vBody = pBot->vHead; // kill edict in memory - pBot->pButtonEdict = NULL; + pBot->pButtonEdict = nullptr; // press use if (!bTrigger) @@ -2717,8 +2745,8 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { pBot->rprint_trace("cNodeMachine::path_walk", "This is the button I was looking for, it is close and I can use it"); // wait a little - pBot->setTimeToWait(0.5); - pBot->fButtonTime = gpGlobals->time + 5.0; + pBot->setTimeToWait(0.5f); + pBot->fButtonTime = gpGlobals->time + 5.0f; pBot->setTimeToMoveToNode(3); pBot->forgetPath(); return; @@ -2747,11 +2775,11 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { } // Near Node - bool bNearNode = false; + bool bNearNode; //Variable Reassigned [APG]RoboCop[CL] if (pBot->isOnLadder()) { pBot->rprint("Bot is on ladder"); // Set touch radius - pBot->f_strafe_speed = 0.0; // we may not strafe + pBot->f_strafe_speed = 0.0f; // we may not strafe pBot->setMoveSpeed(pBot->f_max_speed / 2); //pBot->pEdict->v.button |= IN_DUCK; // duck @@ -2764,7 +2792,7 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { if (BotShouldDuck(pBot)) { UTIL_BotPressKey(pBot, IN_DUCK); - pBot->f_hold_duck = gpGlobals->time + 0.2; + pBot->f_hold_duck = gpGlobals->time + 0.2f; } bNearNode = pBot->getDistanceToNextNode() < 25; @@ -2783,7 +2811,7 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { tGoal *goalData = pBot->getGoalData(); if (goalData) { char msg[255]; - sprintf(msg, "Heading for goal node of type [%s]", goalData->name); + snprintf(msg, sizeof(msg), "Heading for goal node of type [%s]", goalData->name); pBot->rprint_trace("cNodeMachine::path_walk (bNear)", msg); if (goalData->iType == GOAL_HOSTAGE) { pBot->rprint_normal("cNodeMachine::path_walk (bNear)", "next node is destination and GOAL_HOSTAGE, so need to get really close"); @@ -2799,7 +2827,9 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { } char msg[255]; - sprintf(msg, "Heading for node %d, required distance is %f, actual distance is %f, time remaining %f", pBot->getCurrentPathNodeToHeadFor(), requiredDistance, pBot->getDistanceToNextNode(), pBot->getMoveToNodeTimeRemaining()); + snprintf(msg, sizeof(msg), "Heading for node %d, required distance is %f, actual distance is %f, time remaining %f", + pBot->getCurrentPathNodeToHeadFor(), requiredDistance, pBot->getDistanceToNextNode(), + pBot->getMoveToNodeTimeRemaining()); pBot->rprint_trace("cNodeMachine::path_walk (bNear)", msg); bNearNode = pBot->getDistanceToNextNode() < requiredDistance; @@ -2807,22 +2837,22 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { // If we should duck, duck. if (BotShouldDuck(pBot)) { UTIL_BotPressKey(pBot, IN_DUCK); - pBot->f_hold_duck = gpGlobals->time + 0.2; + pBot->f_hold_duck = gpGlobals->time + 0.2f; } } - bool shouldDrawWaypointBeamsFromBot = false; + // No longer required? [APG]RoboCop[CL] + /*bool shouldDrawWaypointBeamsFromBot = false; + if (shouldDrawWaypointBeamsFromBot) { - tNode *nodeHeadingFor = this->getNode(currentNodeToHeadFor); + const tNode *nodeHeadingFor = this->getNode(currentNodeToHeadFor); - int player_index = 0; - for (player_index = 1; player_index <= gpGlobals->maxClients; - player_index++) { + for (int player_index = 1; player_index <= gpGlobals->maxClients; + player_index++) { edict_t *pPlayer = INDEXENT(player_index); if (pPlayer && !pPlayer->free) { - if (FBitSet(pPlayer->v.flags, FL_CLIENT) && - shouldDrawWaypointBeamsFromBot) { // do not draw for now + if (FBitSet(pPlayer->v.flags, FL_CLIENT)) { // do not draw for now DrawBeam( pPlayer, // player sees beam @@ -2831,9 +2861,9 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { 255, 255, 255 ); - int currentNode = pBot->determineCurrentNodeWithTwoAttempts(); + const int currentNode = pBot->determineCurrentNodeWithTwoAttempts(); if (currentNode > -1) { - tNode *node = getNode(currentNode); + const tNode *node = getNode(currentNode); // close node DrawBeam( @@ -2851,7 +2881,7 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { } } } - } // Draw waypoint beams + }*/ // Draw waypoint beams // reached node if (bNearNode) { @@ -2862,7 +2892,7 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { // TODO TODO TODO Water Navigation - int nextNodeToHeadFor = pBot->getNextPathNode(); // the node we will head for after reaching currentNode + const int nextNodeToHeadFor = pBot->getNextPathNode(); // the node we will head for after reaching currentNode // NO ENEMY, CHECK AROUND AREA // This determines where to look at, no enemy == look at next node @@ -2884,7 +2914,7 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { !isEntityWorldspawn(pEntityHit)) // and it is not worldspawn (ie, the map itself) { char msg[255]; - sprintf(msg, "Entity [%s] between me and next node.", STRING(pEntityHit->v.classname)); + snprintf(msg, sizeof(msg), "Entity [%s] between me and next node.", STRING(pEntityHit->v.classname)); pBot->rprint_trace("cNodeMachine::path_walk", msg); // hit by a door? @@ -2901,10 +2931,14 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { return; } - if (strcmp(STRING(pEntityHit->v.classname), "player") == 0) { + if (std::strcmp(STRING(pEntityHit->v.classname), "player") == 0) { pBot->rprint_trace("cNodeMachine::path_walk", "Another player between me and next node."); - pBot->strafeRight(0.2); - return; + if (pBot->hasTimeToMoveToNode()) { + pBot->strafeRight(0.2f); + pBot->rprint_trace("cNodeMachine::path_walk", "Time left to move to node, so lets try strafing to unstuck."); + return; + } + pBot->rprint_trace("cNodeMachine::path_walk", "No time left to move to node, so we continue and let stuck logic kick in"); } pBot->rprint_trace("cNodeMachine::path_walk", "Finished - entity hit end block"); @@ -2915,24 +2949,27 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { // - unstuck // - go back in path... - float timeEvaluatingMoveSpeed = 0.1; - bool notStuckForAWhile = (pBot->fNotStuckTime + timeEvaluatingMoveSpeed) < gpGlobals->time; + constexpr float timeEvaluatingMoveSpeed = 0.1f; + const bool notStuckForAWhile = pBot->fNotStuckTime + timeEvaluatingMoveSpeed < gpGlobals->time; + + constexpr double fraction = 0.7; // 0.7 is an arbitrary number based on several tests to consider stuck at a more sane moment. + // Else it would trigger stuck logic too soon, too often. - double fraction = 0.7; // 0.7 is an arbitrary number based on several tests to consider stuck at a more sane moment. Else it would trigger stuck logic too soon, too often. - double speedInOneTenthOfASecond = (pBot->f_move_speed * timeEvaluatingMoveSpeed) * fraction; + const double speedInOneTenthOfASecond = static_cast(pBot->f_move_speed * timeEvaluatingMoveSpeed) * fraction; double expectedMoveDistance = speedInOneTenthOfASecond; if (pBot->isFreezeTime()) expectedMoveDistance = 0; + if (pBot->isWalking()) expectedMoveDistance = speedInOneTenthOfASecond / 3.0; if (pBot->isDucking()) expectedMoveDistance = speedInOneTenthOfASecond / 3.0; if (pBot->isJumping()) expectedMoveDistance = speedInOneTenthOfASecond / 3.0; // no need for 'is walking' because walking time influence `f_move_speed` hence it is already taken care of - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Distance moved %f, expected %f, should be able to move yes, notStuck for a while %d", distanceMoved, expectedMoveDistance, notStuckForAWhile); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "Distance moved %f, expected %f, should be able to move yes, notStuck for a while %d", distanceMoved, + expectedMoveDistance, notStuckForAWhile); pBot->rprint_trace("cNodeMachine::path_walk", msg); - bool isStuck = distanceMoved < expectedMoveDistance && pBot->shouldBeAbleToMove() && notStuckForAWhile; // also did not evaluate this logic for 0.5 second - Vector &vector = Nodes[currentNodeToHeadFor].origin; + const bool isStuck = distanceMoved < expectedMoveDistance && pBot->shouldBeAbleToMove() && notStuckForAWhile; // also did not evaluate this logic for 0.5 second + const Vector &vector = Nodes[currentNodeToHeadFor].origin; if (isStuck) { pBot->rprint_trace("cNodeMachine::path_walk", "!!!STUCK STUCK STUCK STUCK STUCK STUCK STUCK!!!"); @@ -2944,8 +2981,8 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { if (pBot->getMoveToNodeTimeRemaining() < 0) { pBot->rprint_trace("cNodeMachine::path_walk/timeRemaining", "Time is up!"); - int iFrom = pBot->getPreviousPathNodeToHeadFor(); - int iTo = currentNodeToHeadFor; + const int iFrom = pBot->getPreviousPathNodeToHeadFor(); + const int iTo = currentNodeToHeadFor; IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded(iFrom, iTo); @@ -2957,71 +2994,81 @@ void cNodeMachine::path_walk(cBot *pBot, float distanceMoved) { pBot->rprint_trace("cNodeMachine::path_walk", "Finished - really the end of the method"); } -void cNodeMachine::ExecuteIsStuckLogic(cBot *pBot, int currentNodeToHeadFor, Vector &vector) { +void cNodeMachine::ExecuteIsStuckLogic(cBot *pBot, const int currentNodeToHeadFor, const Vector& vector) { pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "START"); - pBot->fNotStuckTime = gpGlobals->time + 0.25; // give some time to unstuck + pBot->fNotStuckTime = gpGlobals->time + 0.25f; // give some time to unstuck - int iFrom = pBot->getPreviousPathNodeToHeadFor(); - int iTo = currentNodeToHeadFor; + const int iFrom = pBot->getPreviousPathNodeToHeadFor(); + const int iTo = currentNodeToHeadFor; - // JUMP & DUCK - tNode ¤tNode = Nodes[currentNodeToHeadFor]; - if (BotShouldJumpIfStuck(pBot) || (currentNode.iNodeBits & BIT_JUMP)) { - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck-jump tries increased, increase node time - START"); + // JUMP & DUCK // TODO: Add a proper and reliable DuckJump Node [APG]RoboCop[CL] + const tNode ¤tNode = Nodes[currentNodeToHeadFor]; + if (BotShouldJumpIfStuck(pBot) || currentNode.iNodeBits & BIT_JUMP) { + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Jump tries increased, increase node time - START"); pBot->doJump(vector); pBot->iJumpTries++; - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck-jump tries increased, increase node time - END"); - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Finished!"); - return; - } else if (BotShouldDuck(pBot) || (currentNode.iNodeBits & BIT_DUCK)) { - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck tries increased, increase node time - START"); - pBot->doDuck(); - pBot->iDuckTries++; - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck tries increased, increase node time - END"); + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Jump tries increased, increase node time - END"); pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Finished!"); return; - } else if (pBot->isOnLadder()) { - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Is stuck on ladder, trying to get of the ladder by jumping"); - pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck-jump tries increased"); - pBot->doJump(vector); - pBot->iJumpTries++; + } + if (BotShouldDuck(pBot) || currentNode.iNodeBits & BIT_DUCK) { + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck tries increased, increase node time - START"); + pBot->doDuck(); + pBot->iDuckTries++; + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Duck tries increased, increase node time - END"); + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Finished!"); + return; + } + if (BotShouldDuckJump(pBot) || currentNode.iNodeBits & BIT_DUCKJUMP) { + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "DuckJump tries increased, increase node time - START"); + pBot->doDuckJump(); + pBot->iDuckJumpTries++; + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "DuckJump tries increased, increase node time - END"); pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Finished!"); return; } + if (pBot->isOnLadder()) { + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Is stuck on ladder, trying to get of the ladder by jumping"); + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "DuckJump tries increased"); + pBot->doJump(vector); + pBot->iJumpTries++; + pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Finished!"); + return; + } pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "No need to duck or to jump"); - char msg[255]; - memset(msg, 0, sizeof(msg)); - float timeRemaining = pBot->getMoveToNodeTimeRemaining(); - sprintf(msg, "I still have %f seconds to go to node before considered 'stuck' for connection", timeRemaining); + char msg[255] = {}; + const float timeRemaining = pBot->getMoveToNodeTimeRemaining(); + snprintf(msg, sizeof(msg), "I still have %f seconds to go to node before considered 'stuck' for connection", timeRemaining); pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", msg); - cBot *pBotStuck = getCloseFellowBot(pBot); + const cBot *pBotStuck = getCloseFellowBot(pBot); // edict_t *playerNearbyInFOV = getPlayerNearbyBotInFOV(pBot); edict_t *entityNearbyInFOV = getEntityNearbyBotInFOV(pBot); - edict_t *playerNearbyInFOV = NULL; - edict_t *hostageNearbyInFOV = NULL; + const edict_t *playerNearbyInFOV = nullptr; + edict_t *hostageNearbyInFOV = nullptr; if (entityNearbyInFOV) { - if (strcmp(STRING(entityNearbyInFOV->v.classname), "player") == 0) { + if (std::strcmp(STRING(entityNearbyInFOV->v.classname), "player") == 0) { playerNearbyInFOV = entityNearbyInFOV; pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "A player is in front of me"); } - if (strcmp(STRING(entityNearbyInFOV->v.classname), "hostage_entity") == 0) { + if (std::strcmp(STRING(entityNearbyInFOV->v.classname), "hostage_entity") == 0) { hostageNearbyInFOV = entityNearbyInFOV; pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "A hostage is in front of me"); } } - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Player in FOV? %d, hostage in FOV? %d bot close ? %d, time remaining? %f", playerNearbyInFOV != NULL, hostageNearbyInFOV != NULL, pBotStuck != NULL, timeRemaining); + std::memset(msg, 0, sizeof(msg)); + snprintf(msg, sizeof(msg), "Player in FOV? %d, hostage in FOV? %d bot close ? %d, time remaining? %f", + playerNearbyInFOV != nullptr, hostageNearbyInFOV != nullptr, pBotStuck != nullptr, timeRemaining); pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", msg); if (playerNearbyInFOV) { - if (pBotStuck != NULL) { + if (pBotStuck != nullptr) { if (pBotStuck->pEdict == playerNearbyInFOV) { pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "The player nearby in FOV is the close bot as well: it is a fellow bot that blocks me"); } else { @@ -3063,13 +3110,13 @@ void cNodeMachine::ExecuteIsStuckLogic(cBot *pBot, int currentNodeToHeadFor, Vec // should move, but no nearby bot found that could cause us to get stuck // - when no players are close (could be blocked by them, do not learn stupid things) - if (pBotStuck == NULL) { + if (pBotStuck == nullptr) { pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "There is no other BOT around making me go stuck"); // check if the connection we want is going up // - when going up // - when we should move - bool nodeToHeadForIsHigher = currentNode.origin.z > pBot->pEdict->v.origin.z; + const bool nodeToHeadForIsHigher = currentNode.origin.z > pBot->pEdict->v.origin.z; if (nodeToHeadForIsHigher) { pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "Node to head for is higher than me"); // check if the next node is floating (skip self) @@ -3087,7 +3134,7 @@ void cNodeMachine::ExecuteIsStuckLogic(cBot *pBot, int currentNodeToHeadFor, Vec // make sure we head to the current node pBot->vBody = currentNode.origin; pBot->vHead = currentNode.origin; - pBot->fNotStuckTime = gpGlobals->time + 0.5; // give a bit more time + pBot->fNotStuckTime = gpGlobals->time + 0.5f; // give a bit more time } else { pBot->rprint_trace("cNodeMachine::ExecuteIsStuckLogic", "I can no longer see the current node to head for"); IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded(iFrom, iTo); @@ -3117,10 +3164,10 @@ void cNodeMachine::ExecuteIsStuckLogic(cBot *pBot, int currentNodeToHeadFor, Vec void cNodeMachine::ExecuteNearNodeLogic(cBot *pBot) { pBot->rprint_trace("cNodeMachine::ExecuteNearNodeLogic", "Start"); - int currentNodeToHeadFor = pBot->getCurrentPathNodeToHeadFor(); + const int currentNodeToHeadFor = pBot->getCurrentPathNodeToHeadFor(); // first determine if we where heading for a goal node - bool isHeadingForGoalNode = pBot->isHeadingForGoalNode(); + const bool isHeadingForGoalNode = pBot->isHeadingForGoalNode(); // increase index on path, so we will go to next node pBot->nextPathIndex(); @@ -3133,21 +3180,20 @@ void cNodeMachine::ExecuteNearNodeLogic(cBot *pBot) { if (!isHeadingForGoalNode) { - char msg[255]; - memset(msg, 0, sizeof(msg)); + char msg[255] = {}; - int currentPathNode = pBot->getCurrentPathNodeToHeadFor(); + const int currentPathNode = pBot->getCurrentPathNodeToHeadFor(); if (currentPathNode > -1) { - int troubleIndex = GetTroubleIndexForConnection(pBot->getPreviousPathNodeToHeadFor(), currentNodeToHeadFor); + const int troubleIndex = GetTroubleIndexForConnection(pBot->getPreviousPathNodeToHeadFor(), currentNodeToHeadFor); if (troubleIndex > -1) { - tTrouble &trouble = Troubles[troubleIndex]; - sprintf(msg, "Heading to next node: %d, trouble (tries) %d", currentPathNode, trouble.iTries); + const tTrouble &trouble = Troubles[troubleIndex]; + snprintf(msg, sizeof(msg), "Heading to next node: %d, trouble (tries) %d", currentPathNode, trouble.iTries); } else { - sprintf(msg, "Heading to next node: %d - with no trouble", currentPathNode); + snprintf(msg, sizeof(msg), "Heading to next node: %d - with no trouble", currentPathNode); } } else { - sprintf(msg, "Heading to next node: %d", currentPathNode); + snprintf(msg, sizeof(msg), "Heading to next node: %d", currentPathNode); } pBot->rprint("cNodeMachine::path_walk()", msg); } @@ -3199,7 +3245,7 @@ void cNodeMachine::ExecuteNearNodeLogic(cBot *pBot) { // At destination, bomb is planted and we have not discovered the C4 yet... // TODO: Remember which goals/bomb spots have been visited so bots won't visit this bomb spot again? - int iGoalType = getGoalIndexFromNode(pBot->getGoalNode()); + const int iGoalType = getGoalIndexFromNode(pBot->getGoalNode()); if (pBot->isCounterTerrorist()) { if (Game.bBombPlanted && !Game.isPlantedC4Discovered()) { @@ -3228,32 +3274,36 @@ void cNodeMachine::ExecuteNearNodeLogic(cBot *pBot) { * @param pEntityHit * @return */ -bool cNodeMachine::isDoorThatOpensWhenPressingUseButton(const edict_t *pEntityHit) const { +bool cNodeMachine::isDoorThatOpensWhenPressingUseButton(const edict_t *pEntityHit) +{ return FBitSet(pEntityHit->v.spawnflags, SF_DOOR_USE_ONLY) && !(FBitSet(pEntityHit->v.spawnflags, SF_DOOR_NO_AUTO_RETURN)); } -bool cNodeMachine::isEntityDoor(const edict_t *pEntityHit) const { - return strcmp(STRING(pEntityHit->v.classname), "func_door") == 0 || // normal door (can be used as an elevator) - strcmp(STRING(pEntityHit->v.classname), "func_wall") == 0 || // I am not 100% sure about func_wall, but include it anyway - strcmp(STRING(pEntityHit->v.classname), "func_door_rotating") == 0; // rotating door +bool cNodeMachine::isEntityDoor(const edict_t *pEntityHit) +{ + return std::strcmp(STRING(pEntityHit->v.classname), "func_door") == 0 || // normal door (can be used as an elevator) + std::strcmp(STRING(pEntityHit->v.classname), "func_wall") == 0 || // I am not 100% sure about func_wall, but include it anyway + std::strcmp(STRING(pEntityHit->v.classname), "func_door_rotating") == 0; // rotating door } -bool cNodeMachine::isEntityHostage(const edict_t *pEntityHit) const { - return strcmp(STRING(pEntityHit->v.classname), "hostage_entity") == 0; // hostage +bool cNodeMachine::isEntityHostage(const edict_t *pEntityHit) +{ + return std::strcmp(STRING(pEntityHit->v.classname), "hostage_entity") == 0; // hostage } -bool cNodeMachine::isEntityWorldspawn(const edict_t *pEntityHit) const { - return strcmp(STRING(pEntityHit->v.classname), "worldspawn") == 0; // the world +bool cNodeMachine::isEntityWorldspawn(const edict_t *pEntityHit) +{ + return std::strcmp(STRING(pEntityHit->v.classname), "worldspawn") == 0; // the world } // Think about path creation here -void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { +void cNodeMachine::path_think(cBot *pBot, const float distanceMoved) { pBot->rprint_trace("cNodeMachine::path_think", "START"); if (pBot->shouldBeWandering()) { int currentNode = -1; for (int attempts = 1; attempts < 5; attempts++) { - float distance = NODE_ZONE + (attempts * NODE_ZONE); + const float distance = static_cast(NODE_ZONE) + static_cast(attempts * NODE_ZONE); currentNode = pBot->determineCurrentNode(distance); // this also sets current node in bot state if (currentNode > -1) break; } @@ -3270,9 +3320,8 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { if (pBot->canSeeHostageToRescue()) { pBot->rprint_trace("cNodeMachine::path_think", "has hostage and can see hostage, will not do anything"); return; // bot has hostage, can see hostage - } else { - pBot->rprint_trace("cNodeMachine::path_think", "has hostage to rescue, but can't see it"); } + pBot->rprint_trace("cNodeMachine::path_think", "has hostage to rescue, but can't see it"); } if (pBot->shouldActWithC4()) { @@ -3285,7 +3334,7 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { pBot->rprint("cNodeMachine::path_think", "Time to camp"); if (!pBot->hasGoal()) { pBot->rprint("cNodeMachine::path_think", "Setting goal to look for camping"); - int noteToLookAt = node_look_camp(pBot->pEdict->v.origin, UTIL_GetTeam(pBot->pEdict), pBot->pEdict); + const int noteToLookAt = node_look_camp(pBot->pEdict->v.origin, UTIL_GetTeam(pBot->pEdict), pBot->pEdict); pBot->setGoalNode(noteToLookAt); } return; @@ -3306,14 +3355,14 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { pBot->rprint_trace("cNodeMachine::path_think", "I am not walking a path."); - int thisBotIndex = pBot->iBotIndex; + const int thisBotIndex = pBot->iBotIndex; // No path pBot->stopMoving(); if (pBot->hasGoal()) { // there is no path, but there is a goal to work to - int iCurrentNode = pBot->determineCurrentNodeWithTwoAttempts(); + const int iCurrentNode = pBot->determineCurrentNodeWithTwoAttempts(); if (iCurrentNode < 0) { pBot->forgetGoal(); @@ -3354,40 +3403,36 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { // DETERMINE GOAL / FIND GOAL // Loop through all goals. - float highestScore = 0.0; + float highestScore = 0.0f; int iFinalGoalNode = -1; int iFinalGoalIndex = -1; - int goalIndex = 0; - - float MAX_DISTANCE = 16384.0; // theoretical max distance - float MAX_GOAL_DISTANCE = MAX_DISTANCE / 2.0; + constexpr float MAX_DISTANCE = 16384.0f; // theoretical max distance + constexpr float MAX_GOAL_DISTANCE = MAX_DISTANCE / 2.0f; // 01-07-2008; Instead of using 'scores', use a normalized score. // We do: // (current score + gained score) / 2.0; // Since both scores can be max 1.0, meaning we keep it between 0.0 and 1.0 // A score of 1.0 is max. - int maxCheckedScore = 5; pBot->rprint_normal("cNodeMachine::path_think", "going to choose goal"); - for (goalIndex = 0; goalIndex < MAX_GOALS; goalIndex++) { + for (int goalIndex = 0; goalIndex < MAX_GOALS; ++goalIndex) { + constexpr int maxCheckedScore = 5; - // Make sure this goal is valid + // Make sure this goal is valid if (Goals[goalIndex].iNode < 0) { continue; } - float score = 0.0f; // start with 0 - - float fDistanceToGoal = func_distance( + const float fDistanceToGoal = func_distance( pBot->pEdict->v.origin, node_vector(Goals[goalIndex].iNode) ); // First score is distance, so just set it: - score = fDistanceToGoal / MAX_GOAL_DISTANCE; + float score = fDistanceToGoal / MAX_GOAL_DISTANCE; if (Goals[goalIndex].iChecked > maxCheckedScore) { // it has been very popular, reset @@ -3395,30 +3440,30 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { } // A bit off randomness - float weight = 50 / pBot->ipRandom; // (yes, this will give us 1 or higher score) + float weight = 50.0f / static_cast(pBot->ipRandom); // (yes, this will give us 1 or higher score) weight *= score; score += weight; // Take into consideration how many times this goal has been selected - score = (score + (1.0f - (Goals[goalIndex].iChecked / maxCheckedScore))) / 2.0f; + score = (score + (1.0f - static_cast(Goals[goalIndex].iChecked) / static_cast(maxCheckedScore))) / 2.0f; // Danger (is important) - score = (score + InfoNodes[Goals[goalIndex].iNode].fDanger[UTIL_GetTeam(pBot->pEdict)]) / 1.5; + score = (score + InfoNodes[Goals[goalIndex].iNode].fDanger[UTIL_GetTeam(pBot->pEdict)]) / 1.5f; // How many bots have already taken this goal? - float goalAlreadyUsedScore = 0.0; - float teamMembers = 1.0; // count self by default - - for (int botIndex = 0; botIndex < MAX_BOTS; botIndex++) { + float goalAlreadyUsedScore = 0.0f; + float teamMembers = 1.0f; // count self by default + + for (cBot& bot : bots) + { // not a bot - cBot *botPointer = &bots[botIndex]; - if (botPointer == NULL || - !botPointer->bIsUsed || + const cBot* botPointer = ⊥ + if (!botPointer->bIsUsed || botPointer == pBot) { // skip self continue; } - + // real bots.. if (pBot->isOnSameTeamAs(botPointer)) { teamMembers++; @@ -3429,14 +3474,14 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { } // add favoriteness - score = (score + (1.0 - (goalAlreadyUsedScore / teamMembers))) / 2.0; + score = (score + (1.0f - goalAlreadyUsedScore / teamMembers)) / 2.0f; // Goals regardless of map/game type - int goalType = Goals[goalIndex].iType; + const int goalType = Goals[goalIndex].iType; if (goalType == GOAL_SPAWNCT || goalType == GOAL_SPAWNT) { - float goalscore = fDistanceToGoal / MAX_GOAL_DISTANCE; - score = (score + goalscore) / 2.0; + const float goalscore = fDistanceToGoal / MAX_GOAL_DISTANCE; + score = (score + goalscore) / 2.0f; } if (Game.bHostageRescueMap) { @@ -3446,87 +3491,91 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { if (pBot->isEscortingHostages()) { pBot->rprint("I am escorting hostages - assuming ct spawn is rescue zone and prioritizing"); // highest priority - score = 2.0; + score = 2.0f; } } } if (goalType == GOAL_HOSTAGE) { - // counter-terrorist should - float goalscore = 0.0; + float goalscore = 0.0f; + + // Counter-Terrorist hostage rescue logic if (pBot->isCounterTerrorist()) { if (pBot->isEscortingHostages()) { pBot->rprint("I am escorting hostages - should ignore existing hostages"); - // already escorting hostages, low interest for other hostages - goalscore = 0.5; - } else { - // always go to the most furthest hostage spot, and add some randomness here, else - // all bots go to there. - float mul = MAX_GOAL_DISTANCE / fDistanceToGoal; - goalscore = 1 + mul; + // Already escorting hostages, low interest for other hostages + goalscore = 0.5f; } - } else { - // Terrorist pick randomly this location + else { + // Prioritize closer hostages for a quicker rescue. + // The score is inversely proportional to the distance. + // Adding a small random factor to prevent all bots picking the same hostage. + goalscore = (MAX_GOAL_DISTANCE / std::max(fDistanceToGoal, 1.0f)) + RANDOM_FLOAT(0.1f, 0.2f); + } + } + // Terrorist logic for hostage locations + else { + // Terrorists have a low chance to guard a hostage location. if (RANDOM_LONG(0, 100) < 25) { - goalscore = RANDOM_FLOAT(0.1, 0.6); + goalscore = RANDOM_FLOAT(0.1f, 0.6f); } } - score = (score + goalscore) / 2.0; - } else if (goalType == GOAL_RESCUEZONE) { + score = (score + goalscore) / 2.0f; + } + else if (goalType == GOAL_RESCUEZONE) { if (pBot->isCounterTerrorist()) { if (pBot->isEscortingHostages()) { pBot->rprint("I am escorting hostages - prioritizing for rescue zone"); // highest priority - score = 2.0; + score = 2.0f; } else { - score = 0.2; + score = 0.2f; } } else if (pBot->isTerrorist()) { // TODO: when hostages are being rescued, go to a rescue zone to catch CT's and // prevent rescue - score = 0.2; + score = 0.2f; } } } else { // it is a DE_ map if (goalType == GOAL_BOMBSPOT) { - float goalscore = 0.0; + float goalscore = 0.0f; if (pBot->isTerrorist()) { if (pBot->hasBomb()) { - goalscore = 2.0; // plant it! + goalscore = 2.0f; // plant it! } else { - float mul = fDistanceToGoal / MAX_GOAL_DISTANCE; - goalscore = (0.7 * mul); + const float mul = fDistanceToGoal / MAX_GOAL_DISTANCE; + goalscore = 0.7f * mul; } } else if (pBot->isCounterTerrorist()) { if (Game.bBombPlanted) { if (Game.isPlantedC4Discovered()) { pBot->rprint_trace("path_think/determine goal", "I know where the C4 is planted, evaluating if this is the closest bombspot."); - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "C4 is located at %f, %f, %f", Game.vPlantedC4.x, Game.vPlantedC4.y, Game.vPlantedC4.z); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "C4 is located at %f, %f, %f", Game.vPlantedC4.x, Game.vPlantedC4.y, Game.vPlantedC4.z); pBot->rprint_trace("path_think/determine goal", msg); // find a node close to the C4 - int nodeCloseToC4 = getClosestNode(Game.vPlantedC4, NODE_ZONE * 2, NULL); + const int nodeCloseToC4 = getClosestNode(Game.vPlantedC4, NODE_ZONE * 2, nullptr); if (nodeCloseToC4 > -1) { // measure distance compared to goal node we are evaluating - float distanceToC4FromCloseNode = func_distance( + const float distanceToC4FromCloseNode = func_distance( Game.vPlantedC4, node_vector(Goals[nodeCloseToC4].iNode) ); // the distance from this goal node - float distanceToC4FromThisGoalNode = func_distance( + const float distanceToC4FromThisGoalNode = func_distance( Game.vPlantedC4, node_vector(Goals[goalIndex].iNode) ); - float score = distanceToC4FromCloseNode / distanceToC4FromThisGoalNode; - goalscore = 1.5 + score; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Distance from C4 to closest node is %f, distance from evaluating node to C4 is %f, resulting into score of %f", + score = distanceToC4FromCloseNode / distanceToC4FromThisGoalNode; + goalscore = 1.5f + score; + std::memset(msg, 0, sizeof(msg)); + snprintf(msg, sizeof(msg), "Distance from C4 to closest node is %f, distance from evaluating node to C4 is %f, resulting into score of %f", distanceToC4FromCloseNode, distanceToC4FromThisGoalNode, goalscore); @@ -3534,20 +3583,20 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { } else { pBot->rprint_trace("path_think/determine goal", "We know where the C4 is planted, but unfortunately we can't find a close node to the planted c4."); // we can't find a node close to the c4, so we gamble - goalscore = 2.0; // pick + goalscore = 2.0f; // pick } } else { pBot->rprint_trace("path_think/determine goal", "No clue where bomb is, picking bombspot to evaluate"); - goalscore = 2.0; // pick any bombspot + goalscore = 2.0f; // pick any bombspot } } else { pBot->rprint_trace("path_think/determine goal", "Bomb is not planted"); - float mul = fDistanceToGoal / MAX_GOAL_DISTANCE; - goalscore = (0.7 * mul); + const float mul = fDistanceToGoal / MAX_GOAL_DISTANCE; + goalscore = 0.7f * mul; } } // this is weird? what? - score = (score + goalscore) / 2.0; + score = (score + goalscore) / 2.0f; } // GOAL is a bombspot } // bomb plant map @@ -3560,23 +3609,24 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { if (pBot->isCounterTerrorist()) { if (pBot->vip) { // VIP wants to get out - score = 2.0; + score = 2.0f; } } else { // terrorists pick random - score = (score + RANDOM_FLOAT(0.0, 1.0)) / 2.0; + score = (score + RANDOM_FLOAT(0.0f, 1.0f)) / 2.0f; } } - + if (goalType == GOAL_VIP) { if (pBot->isCounterTerrorist()) { if (pBot->vip) { score = 0; // do not chase yourself } else { - // if distance is too big, go to it. (guard the VIP) - int maxDistanceWeKeepToVIP = 500; - float goalScore = maxDistanceWeKeepToVIP / fDistanceToGoal; - score = (score + goalScore) / 2.0; + score = 0; // don't care about VIP + // if distance is too big, go to it. (guard the VIP) + constexpr int maxDistanceWeKeepToVIP = 500; + const float goalScore = maxDistanceWeKeepToVIP / fDistanceToGoal; + score = (score + goalScore) / 2.0f; } } } @@ -3585,7 +3635,7 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { { // Maximum importance when acting as T if (pBot->iTeam == 1) { - score = 2.0; + score = 2.0f; } } @@ -3604,7 +3654,7 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { // was previous goal as well, don't go there if (pBot->iPreviousGoalNode == goalIndex) { - score *= 0.2; // low chance + score *= 0.2f; // low chance } @@ -3614,14 +3664,11 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { // even though its float comparison, it can h appen since we hard-set it to 2.0 at some places, making // some scores the same - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Evaluating goal %s gives a score of %f, highest score so far is %f", - Goals[goalIndex].name, - score, - highestScore); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "Evaluating goal %s gives a score of %f, highest score so far is %f", + Goals[goalIndex].name, score, highestScore); pBot->rprint_trace("path_think/determine goal", msg); - if (score == highestScore && RANDOM_LONG(0,100) < 50) { + if (static_cast(score) == static_cast(highestScore) && RANDOM_LONG(0,100) < 50) { pBot->rprint_trace("path_think/determine goal", "SCORE == HIGHEST SCORE and chosen to override randomly."); highestScore = score; iFinalGoalNode = Goals[goalIndex].iNode; @@ -3651,11 +3698,11 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { // Well it looks like we override the 'final' goal node (not so final after all huh?) to the dropped c4 if (Game.vDroppedC4 != Vector(9999, 9999, 9999) && // c4 dropped somewhere - pBot->pButtonEdict == NULL) { // not using button + pBot->pButtonEdict == nullptr) { // not using button // randomly, if we 'feel like picking up the bomb' just override the 'final' goal node if (RANDOM_LONG(0, 100) < pBot->ipDroppedBomb) { - iFinalGoalNode = getClosestNode(Game.vDroppedC4, 75, NULL); + iFinalGoalNode = getClosestNode(Game.vDroppedC4, 75, nullptr); } } @@ -3685,11 +3732,10 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { pBot->setGoalNode(iFinalGoalNode, iFinalGoalIndex); tGoal *goalData = pBot->getGoalData(); - char msg[255]; - memset(msg, 0, sizeof(msg)); + char msg[255] = {}; - if (goalData != NULL) { - sprintf(msg, "I have chosen a goal: Node [%d], Goal type [%s], checked [%d], score [%f], distance [%f]", + if (goalData != nullptr) { + snprintf(msg, sizeof(msg), "I have chosen a goal: Node [%d], Goal type [%s], checked [%d], score [%f], distance [%f]", iFinalGoalNode, goalData->name, goalData->iChecked, @@ -3697,7 +3743,7 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { pBot->getDistanceTo(iFinalGoalNode) ); } else { - sprintf(msg, "I have chosen a goal: Node [%d], - NO GOAL DATA - score [%f], distance [%f]", + snprintf(msg, sizeof(msg), "I have chosen a goal: Node [%d], - NO GOAL DATA - score [%f], distance [%f]", iFinalGoalNode, highestScore, pBot->getDistanceTo(iFinalGoalNode) @@ -3722,7 +3768,7 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { // } // create path - bool success = createPath(iCurrentNode, pBot->getGoalNode(), thisBotIndex, pBot, pBot->iPathFlags); + const bool success = createPath(iCurrentNode, pBot->getGoalNode(), thisBotIndex, pBot, pBot->iPathFlags); // If we still did not find a path, we set wander time // for 1 second we wait before a new attempt to find a goal and create a path. @@ -3731,7 +3777,7 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { pBot->rprint("cNodeMachine::path_think()", "Finding node nearby to move to"); pBot->setGoalNode(getClosestNode(pBot->pEdict->v.origin, NODE_ZONE * 5, pBot->pEdict)); - bool successToAlternative = createPath(iCurrentNode, pBot->getGoalNode(), thisBotIndex, pBot, pBot->iPathFlags); + const bool successToAlternative = createPath(iCurrentNode, pBot->getGoalNode(), thisBotIndex, pBot, pBot->iPathFlags); if (!successToAlternative) { pBot->rprint("cNodeMachine::path_think()", "Unable to create path to node nearby."); @@ -3741,64 +3787,48 @@ void cNodeMachine::path_think(cBot *pBot, float distanceMoved) { } } -tNode *cNodeMachine::getNode(int index) { +tNode *cNodeMachine::getNode(const int index) { // safe-guard - if (index < 0 || index >= MAX_NODES) return NULL; + if (index < 0 || index >= MAX_NODES) return nullptr; return &Nodes[index]; } -char *cNodeMachine::getGoalTypeAsText(const tGoal &goal) const { - char typeAsText[32]; - memset(typeAsText, 0, sizeof(typeAsText)); - +std::string cNodeMachine::getGoalTypeAsText(const tGoal& goal) +{ switch (goal.iType) { - case GOAL_SPAWNT: - sprintf(typeAsText, "GOAL_SPAWNT"); - break; - case GOAL_SPAWNCT: - sprintf(typeAsText, "GOAL_SPAWNCT"); - break; + case GOAL_SPAWNT: + return "GOAL_SPAWNT"; + case GOAL_SPAWNCT: + return "GOAL_SPAWNCT"; case GOAL_BOMBSPOT: - sprintf(typeAsText, "GOAL_BOMBSPOT"); - break; + return "GOAL_BOMBSPOT"; case GOAL_BOMB: - sprintf(typeAsText, "GOAL_BOMB"); - break; + return "GOAL_BOMB"; case GOAL_HOSTAGE: - sprintf(typeAsText, "GOAL_HOSTAGE"); - break; + return "GOAL_HOSTAGE"; case GOAL_RESCUEZONE: - sprintf(typeAsText, "GOAL_RESCUEZONE"); - break; + return "GOAL_RESCUEZONE"; case GOAL_CONTACT: - sprintf(typeAsText, "GOAL_CONTACT"); - break; + return "GOAL_CONTACT"; case GOAL_IMPORTANT: - sprintf(typeAsText, "GOAL_IMPORTANT"); - break; + return "GOAL_IMPORTANT"; case GOAL_VIP: - sprintf(typeAsText, "GOAL_VIP"); - break; + return "GOAL_VIP"; case GOAL_VIPSAFETY: - sprintf(typeAsText, "GOAL_VIPSAFETY"); - break; + return "GOAL_VIPSAFETY"; case GOAL_ESCAPEZONE: - sprintf(typeAsText, "GOAL_ESCAPEZONE"); - break; + return "GOAL_ESCAPEZONE"; case GOAL_WEAPON: - sprintf(typeAsText, "GOAL_WEAPON"); - break; + return "GOAL_WEAPON"; case GOAL_NONE: - sprintf(typeAsText, "GOAL_NONE"); - break; + return "GOAL_NONE"; default: - sprintf(typeAsText, "GOAL UNKNOWN"); + return "GOAL UNKNOWN"; } - return typeAsText; } // Find cover -int cNodeMachine::node_cover(int iFrom, int iTo, edict_t *pEdict) { +int cNodeMachine::node_cover(const int iFrom, const int iTo, edict_t *pEdict) { if (iFrom == iTo) return iFrom; // cover at current position @@ -3822,14 +3852,14 @@ int cNodeMachine::node_cover(int iFrom, int iTo, edict_t *pEdict) { // is no option. // Note: this function is only called once for finding some node to take cover from - // Most bad situation would be that 31 bots call this function in 1 frame. + // Most bad scenario would be that 31 bots call this function in 1 frame. // TEMP sollution: float fClosest = 512; int iClosest = -1; for (int i = 0; i < MAX_NODES; i++) - if ((i != iTo) && (Nodes[i].origin != Vector(9999, 9999, 9999))) { - float fDistance = + if (i != iTo && Nodes[i].origin != Vector(9999, 9999, 9999)) { + const float fDistance = func_distance(Nodes[i].origin, Nodes[iFrom].origin); if (fDistance < fClosest) { // all nodes within this range may be tracelined @@ -3841,13 +3871,26 @@ int cNodeMachine::node_cover(int iFrom, int iTo, edict_t *pEdict) { UTIL_TraceLine(Nodes[iFrom].origin, Nodes[i].origin, ignore_monsters, ignore_glass, pEdict, &tr); - if (tr.flFraction < 1.0) + if (tr.flFraction < 1.0f) bVisible = false; - } else { - if (GetVisibilityFromTo(iFrom, i) == VIS_BLOCKED) // BERKED + } + + if (GetVisibilityFromTo(i, iFrom) == VIS_UNKNOWN) // BERKED + { + UTIL_TraceLine(Nodes[i].origin, Nodes[iFrom].origin, + ignore_monsters, ignore_glass, pEdict, &tr); + + if (tr.flFraction < 1.0f) bVisible = false; } + // only when really blocked, count it as false. + if (GetVisibilityFromTo(iFrom, i) == VIS_BLOCKED) + bVisible = false; + + if (GetVisibilityFromTo(i, iFrom) == VIS_BLOCKED) + bVisible = false; + // Hit something if (bVisible == false) { // Update VisTable @@ -3866,8 +3909,7 @@ int cNodeMachine::node_cover(int iFrom, int iTo, edict_t *pEdict) { } -int cNodeMachine::node_look_at_hear(int iFrom, int iOrigin, - edict_t *pEdict) { +int cNodeMachine::node_look_at_hear(const int iFrom, const int iOrigin, edict_t *pEdict) { if (iFrom == iOrigin) return iFrom; // impossible @@ -3892,8 +3934,8 @@ int cNodeMachine::node_look_at_hear(int iFrom, int iOrigin, // we search for a node that can see the sound node (iFrom) and the origin node (iOrigin) for (int i = 0; i < MAX_NODES; i++) - if ((i != iOrigin && i != iFrom) - && (Nodes[i].origin != Vector(9999, 9999, 9999))) { + if (i != iOrigin && i != iFrom + && Nodes[i].origin != Vector(9999, 9999, 9999)) { if (func_distance(Nodes[i].origin, Nodes[iOrigin].origin) > BOT_HEARDISTANCE) continue; @@ -3909,7 +3951,7 @@ int cNodeMachine::node_look_at_hear(int iFrom, int iOrigin, UTIL_TraceLine(Nodes[iFrom].origin, Nodes[i].origin, ignore_monsters, ignore_glass, pEdict, &tr); - if (tr.flFraction < 1.0) { + if (tr.flFraction < 1.0f) { SetVisibilityFromTo(iFrom, i, false); SetVisibilityFromTo(i, iFrom, false); } @@ -3920,7 +3962,7 @@ int cNodeMachine::node_look_at_hear(int iFrom, int iOrigin, UTIL_TraceLine(Nodes[iOrigin].origin, Nodes[i].origin, ignore_monsters, ignore_glass, pEdict, &tr); - if (tr.flFraction < 1.0) { + if (tr.flFraction < 1.0f) { SetVisibilityFromTo(iOrigin, i, false); SetVisibilityFromTo(i, iOrigin, false); } @@ -3943,147 +3985,133 @@ int cNodeMachine::node_look_at_hear(int iFrom, int iOrigin, return iClosest; } -void cNodeMachine::dump_goals(void) { - int i; - char buffer[100]; - Vector v; - - rblog("Dump of all goals\n"); - for (i = 0; (i < MAX_GOALS) && (Goals[i].iNode >= 0); i++) { - v = Nodes[Goals[i].iNode].origin; - sprintf(buffer, - "Goal#%d is at node %d (%.0f, %.0f, %.0f), iChecked= %d, ", - i + 1, Goals[i].iNode, v.x, v.y, v.z, Goals[i].iChecked); +void cNodeMachine::dump_goals() const +{ + rblog("Dump of all goals\n"); + for (int i = 0; i < MAX_GOALS && Goals[i].iNode >= 0; i++) { + char buffer[100]; + const Vector v = Nodes[Goals[i].iNode].origin; + snprintf(buffer, sizeof(buffer), + "Goal#%d is at node %d (%.0f, %.0f, %.0f), iChecked= %d, ", + i + 1, Goals[i].iNode, v.x, v.y, v.z, Goals[i].iChecked); switch (Goals[i].iType) { case GOAL_SPAWNCT: - strcat(buffer, "GOAL_SPAWNCT"); + std::strcat(buffer, "GOAL_SPAWNCT"); break; case GOAL_SPAWNT: - strcat(buffer, "GOAL_SPAWNT"); + std::strcat(buffer, "GOAL_SPAWNT"); break; case GOAL_BOMBSPOT: - strcat(buffer, "GOAL_BOMBSPOT"); + std::strcat(buffer, "GOAL_BOMBSPOT"); break; case GOAL_BOMB: - strcat(buffer, "GOAL_BOMB"); + std::strcat(buffer, "GOAL_BOMB"); break; case GOAL_HOSTAGE: - strcat(buffer, "GOAL_HOSTAGE"); + std::strcat(buffer, "GOAL_HOSTAGE"); break; case GOAL_RESCUEZONE: - strcat(buffer, "GOAL_RESCUEZONE"); + std::strcat(buffer, "GOAL_RESCUEZONE"); break; case GOAL_CONTACT: - strcat(buffer, "GOAL_CONTACT"); + std::strcat(buffer, "GOAL_CONTACT"); break; case GOAL_IMPORTANT: - strcat(buffer, "GOAL_IMPORTANT"); + std::strcat(buffer, "GOAL_IMPORTANT"); break; case GOAL_VIP: - strcat(buffer, "GOAL_VIP"); + std::strcat(buffer, "GOAL_VIP"); break; case GOAL_VIPSAFETY: - strcat(buffer, "GOAL_VIPSAFETY"); + std::strcat(buffer, "GOAL_VIPSAFETY"); break; case GOAL_ESCAPEZONE: - strcat(buffer, "GOAL_ESCAPEZONE"); + std::strcat(buffer, "GOAL_ESCAPEZONE"); break; case GOAL_WEAPON: - strcat(buffer, "GOAL_WEAPON"); + std::strcat(buffer, "GOAL_WEAPON"); break; case GOAL_NONE: - strcat(buffer, "GOAL_NONE"); + std::strcat(buffer, "GOAL_NONE"); break; default: - strcat(buffer, "unknown type"); + std::strcat(buffer, "unknown type"); } - strcat(buffer, "\n"); + std::strcat(buffer, "\n"); rblog(buffer); } } // EVY: another dump -void cNodeMachine::dump_path(int iBot, int CurrentPath) { - char buffer[80]; - int i, j, CurrentNode; - Vector v; - - if (CurrentPath >= 0) - CurrentNode = iPath[iBot][CurrentPath]; - else - CurrentNode = -1; - rblog(" Path is: "); - for (i = 0; (i < MAX_NODES) && (iPath[iBot][i] >= 0); i++) { - if (i == CurrentPath) - sprintf(buffer, "<%d> ", iPath[iBot][i]); - else - sprintf(buffer, "%d ", iPath[iBot][i]); - rblog(buffer); - } - rblog("\n"); - if (CurrentNode < 0) - return; - rblog(" Current direct neighbours are:\n"); - for (i = 0; i < MAX_NEIGHBOURS; i++) - if (Nodes[CurrentNode].iNeighbour[i] >= 0) { - j = Nodes[CurrentNode].iNeighbour[i]; - v = Nodes[j].origin; - sprintf(buffer, " %d (%.0f, %.0f, %.0f)\n", j, v.x, v.y, - v.z); +void cNodeMachine::dump_path(const int iBot, const int CurrentPath) const +{ + char buffer[181]; + snprintf(buffer, 180, " Path for bot %d (current index %d): ", iBot, CurrentPath); + rblog(buffer); + + bool path_exists = false; + for (int i = 0; i < MAX_PATH_NODES; ++i) { + int node_index = iPath[iBot][i]; + if (node_index != -1) { + path_exists = true; + snprintf(buffer, 180, "%d ", node_index); rblog(buffer); } - rblog("\n"); + else { + break; // End of path + } + } + + if (!path_exists) { + rblog("No path.\n"); + } + else { + rblog("\n"); + } } // EVY a lot of things to draw: nodes, neighbours, goals, paths, ... // Graphs from PMB & Botman // width and height of the debug bitmap image -#define DEBUG_BMP_WIDTH 2048 -#define DEBUG_BMP_HEIGHT 2048 +enum : std::uint16_t +{ + DEBUG_BMP_WIDTH = 2048, + DEBUG_BMP_HEIGHT = 2048 +}; -static char *bmp_buffer; +static std::vector bmp_buffer; static float maxx, maxy, minx, miny; static float scale; -static void InitDebugBitmap(void) { +static void InitDebugBitmap() { // this function allocates memory and clears the debug bitmap buffer - - if (bmp_buffer) - free(bmp_buffer); // reliability check, free BMP buffer if already allocated - - bmp_buffer = NULL; - bmp_buffer = (char *) malloc(DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); // allocate memory - if (bmp_buffer == NULL) { - fprintf(stderr, - "InitDebugBitmap(): unable to allocate %d kbytes for BMP buffer!\n", - DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT / 1024); - exit(1); + try { + bmp_buffer.assign(static_cast(DEBUG_BMP_WIDTH) * DEBUG_BMP_HEIGHT, 14); + } + catch (const std::bad_alloc& e) { + std::fprintf(stderr, "InitDebugBitmap(): unable to allocate %d kbytes for BMP buffer! Error: %s\n", + DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT / 1024, e.what()); + bmp_buffer.clear(); // Ensure buffer is in a valid state } - - memset(bmp_buffer, 14, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); // Set all to all white (and allow for darker palette) } // Draw a small cross -static void DrawPoint(const Vector v, unsigned char color) { - int offset, x0, y0; - - if (bmp_buffer == NULL) { - fprintf(stderr, - "DrawLineInDebugBitmap(): function called with NULL BMP buffer!\n"); +static void DrawPoint(const Vector& v, const unsigned char color) { + if (bmp_buffer.empty()) { return; // reliability check: cancel if bmp buffer unallocated } // translate the world coordinates in image pixel coordinates - x0 = (int) ((v.x - minx) / scale); - y0 = (int) ((v.y - miny) / scale); + const int x0 = static_cast((v.x - minx) / scale); + const int y0 = static_cast((v.y - miny) / scale); - offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf(stderr, + const int offset = y0 * DEBUG_BMP_WIDTH + x0; + if (offset < 0 || offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) { + std::fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1); + std::exit(1); } bmp_buffer[offset] = color; // draw the point itself @@ -4099,29 +4127,26 @@ static void DrawPoint(const Vector v, unsigned char color) { // From PMB and Botman's code -static void DrawLineInDebugBitmap(const Vector v_from, const Vector v_to, unsigned char color) { +static void DrawLineInDebugBitmap(const Vector& v_from, const Vector& v_to, const unsigned char color) { // blind copy of botman's Bresenham(). This function prints a vector line into a bitmap dot // matrix. The dot matrix (bmp_buffer) is a global array. The size of the bitmap is always // assumed to be DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT pixels (currently 2000 * 2000 to fit with // the size of the universe, with an adaptative unit scale, up to 1 pixel = 10 vector units). - int x0, y0, x1, y1; - int dx, stepx, dy, stepy; - int offset, fraction; + int stepx, stepy; + int fraction; - if (bmp_buffer == NULL) { - fprintf(stderr, - "DrawLineInDebugBitmap(): function called with NULL BMP buffer!\n"); + if (bmp_buffer.empty()) { return; // reliability check: cancel if bmp buffer unallocated } // translate the world coordinates in image pixel coordinates - x0 = (int) ((v_from.x - minx) / scale); - y0 = (int) ((v_from.y - miny) / scale); - x1 = (int) ((v_to.x - minx) / scale); - y1 = (int) ((v_to.y - miny) / scale); + int x0 = static_cast((v_from.x - minx) / scale); + int y0 = static_cast((v_from.y - miny) / scale); + const int x1 = static_cast((v_to.x - minx) / scale); + const int y1 = static_cast((v_to.y - miny) / scale); - dx = (x1 - x0) * 2; - dy = (y1 - y0) * 2; + int dx = (x1 - x0) * 2; + int dy = (y1 - y0) * 2; if (dx < 0) { dx = -dx; stepx = -1; @@ -4133,12 +4158,12 @@ static void DrawLineInDebugBitmap(const Vector v_from, const Vector v_to, unsign } else stepy = 1; - offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf(stderr, + int offset = y0 * DEBUG_BMP_WIDTH + x0; + if (offset < 0 || offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) { + std::fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1); + std::exit(1); } bmp_buffer[offset] = color; // draw the first point of the line @@ -4162,12 +4187,12 @@ static void DrawLineInDebugBitmap(const Vector v_from, const Vector v_to, unsign // compute the offset in the BMP buffer corresponding to this point offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) - || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf(stderr, + if (offset < 0 + || offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) { + std::fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1); + std::exit(1); } bmp_buffer[offset] = color; // set this point to have the specified color @@ -4190,217 +4215,203 @@ static void DrawLineInDebugBitmap(const Vector v_from, const Vector v_to, unsign // compute the offset in the BMP buffer corresponding to this point offset = y0 * DEBUG_BMP_WIDTH + x0; - if ((offset < 0) - || (offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT)) { - fprintf(stderr, + if (offset < 0 + || offset >= DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT) { + std::fprintf(stderr, "DrawLineInDebugBitmap(): bad BMP buffer index %d (range 0 - %d)\n", offset, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT); - exit(1); + std::exit(1); } bmp_buffer[offset] = color; // set this point to have the specified color } } - return; // finished, segment has been printed into the BMP dot matrix + //return; // finished, segment has been printed into the BMP dot matrix } -// from PMB & Botman code - -static void WriteDebugBitmap(const char *filename) { - // this function writes the debug bitmap image buffer in a .BMP file to disk. The format is - // 256 color and 2000 * 2000 pixels. The center of the world being roughly the center of the - // bitmap. The bitmap is stored in the file specified by 'filename' (which can be a relative - // path from the Half-Life base directory). - FILE *fp; +static void WriteDebugBitmap(const char* filename) { int data_start, file_size; unsigned long dummy; - if (bmp_buffer == NULL) { - fprintf(stderr, - "WriteDebugBitmap(): function called with NULL BMP buffer!\n"); + if (bmp_buffer.empty()) { + std::fprintf(stderr, + "WriteDebugBitmap(): function called with NULL BMP buffer!\n"); return; // reliability check: cancel if bmp buffer unallocated } // open (or create) the .bmp file for writing in binary mode... - fp = fopen(filename, "wb"); - if (fp == NULL) { - fprintf(stderr, "WriteDebugBitmap(): unable to open BMP file!\n"); - if (bmp_buffer) - free(bmp_buffer); // cannot open file, free DXF buffer - bmp_buffer = NULL; + FILE* fp = std::fopen(filename, "wb"); + if (fp == nullptr) { + std::fprintf(stderr, "WriteDebugBitmap(): unable to open BMP file!\n"); + bmp_buffer.clear(); // Ensure buffer is in a valid state return; // cancel if error creating file } // write the BMP header - fwrite("BM", 2, 1, fp); // write the BMP header tag - fseek(fp, sizeof(unsigned long), SEEK_CUR); // skip the file size field (will write it last) - fwrite("\0\0", sizeof(short), 1, fp); // dump zeros in the first reserved field (unused) - fwrite("\0\0", sizeof(short), 1, fp); // dump zeros in the second reserved field (unused) - fseek(fp, sizeof(unsigned long), SEEK_CUR); // skip the data start field (will write it last) + constexpr char bmp_magic[] = { 'B', 'M' }; + std::fwrite(bmp_magic, sizeof(bmp_magic), 1, fp); // write the BMP header tag + std::fseek(fp, sizeof(unsigned long), SEEK_CUR); // skip the file size field (will write it last) + constexpr short reserved = 0; + std::fwrite(&reserved, sizeof(short), 1, fp); // dump zeros in the first reserved field (unused) + std::fwrite(&reserved, sizeof(short), 1, fp); // dump zeros in the second reserved field (unused) + std::fseek(fp, sizeof(unsigned long), SEEK_CUR); // skip the data start field (will write it last) // write the info header dummy = 40; - fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the info header size (does 40 bytes) + std::fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the info header size (does 40 bytes) dummy = DEBUG_BMP_WIDTH; - fwrite(&dummy, sizeof(long), 1, fp); // write the image width (2000 px) + std::fwrite(&dummy, sizeof(long), 1, fp); // write the image width (2000 px) dummy = DEBUG_BMP_HEIGHT; - fwrite(&dummy, sizeof(long), 1, fp); // write the image height (2000 px) + std::fwrite(&dummy, sizeof(long), 1, fp); // write the image height (2000 px) dummy = 1; - fwrite(&dummy, sizeof(short), 1, fp); // write the # of planes (1) + std::fwrite(&dummy, sizeof(short), 1, fp); // write the # of planes (1) dummy = 8; - fwrite(&dummy, sizeof(short), 1, fp); // write the bit count (8) + std::fwrite(&dummy, sizeof(short), 1, fp); // write the bit count (8) dummy = 0; - fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the compression id (no compression) + std::fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the compression id (no compression) dummy = DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT; - fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the image size (2000 * 2000) + std::fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the image size (2000 * 2000) dummy = 0; - fwrite(&dummy, sizeof(long), 1, fp); // write the X pixels per meter (not specified) - fwrite(&dummy, sizeof(long), 1, fp); // write the Y pixels per meter (not specified) + std::fwrite(&dummy, sizeof(long), 1, fp); // write the X pixels per meter (not specified) + std::fwrite(&dummy, sizeof(long), 1, fp); // write the Y pixels per meter (not specified) dummy = 256; - fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the # of colors used (all) - fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the # of important colors (wtf ?) + std::fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the # of colors used (all) + std::fwrite(&dummy, sizeof(unsigned long), 1, fp); // write the # of important colors (wtf ?) // write the color palette (B, G, R, reserved byte) - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 0=BLACK - fputc(0xFF, fp); - fputc(0xFF, fp); - fputc(0xFF, fp); - fputc(0x00, fp); // 1=WHITE - fputc(0x80, fp); - fputc(0x80, fp); - fputc(0x80, fp); - fputc(0x00, fp); // 2=GREY - fputc(0xC0, fp); - fputc(0xC0, fp); - fputc(0xC0, fp); - fputc(0x00, fp); // 3=SILVER - fputc(0x80, fp); - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 4=DARK BLUE - fputc(0xFF, fp); - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 5=BLUE - fputc(0x80, fp); - fputc(0x80, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 6=DARK YELLOW - fputc(0xFF, fp); - fputc(0xFF, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 7=YELLOW ? LIGHT BLUE - fputc(0x00, fp); - fputc(0x80, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 8=DARK GREEN - fputc(0x00, fp); - fputc(0xFF, fp); - fputc(0x00, fp); - fputc(0x00, fp); // 9=GREEN - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x80, fp); - fputc(0x00, fp); // 10=DARK RED - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0xFF, fp); - fputc(0x00, fp); // 11=RED - fputc(0x80, fp); - fputc(0x00, fp); - fputc(0x80, fp); - fputc(0x00, fp); // 12=DARK PURPLE - fputc(0xFF, fp); - fputc(0x00, fp); - fputc(0xFF, fp); - fputc(0x00, fp); // 13=PURPLE - fputc(0xFF, fp); - fputc(0xFF, fp); - fputc(0xFF, fp); - fputc(0x00, fp); // 14=WHITE - fputc(0xEF, fp); - fputc(0xEF, fp); - fputc(0xEF, fp); - fputc(0x00, fp); // 15=WHITE-GREY - fputc(0xDF, fp); - fputc(0xDF, fp); - fputc(0xDF, fp); - fputc(0x00, fp); // 16=GREY - fputc(0xCF, fp); - fputc(0xCF, fp); - fputc(0xCF, fp); - fputc(0x00, fp); // 17=DARKGREY - fputc(0xBF, fp); - fputc(0xBF, fp); - fputc(0xBF, fp); - fputc(0x00, fp); // 18=DARKGREY - fputc(0xAF, fp); - fputc(0xAF, fp); - fputc(0xAF, fp); - fputc(0x00, fp); // 19=DARKGREY + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 0=BLACK + std::fputc(0xFF, fp); + std::fputc(0xFF, fp); + std::fputc(0xFF, fp); + std::fputc(0x00, fp); // 1=WHITE + std::fputc(0x80, fp); + std::fputc(0x80, fp); + std::fputc(0x80, fp); + std::fputc(0x00, fp); // 2=GREY + std::fputc(0xC0, fp); + std::fputc(0xC0, fp); + std::fputc(0xC0, fp); + std::fputc(0x00, fp); // 3=SILVER + std::fputc(0x80, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 4=DARK BLUE + std::fputc(0xFF, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 5=BLUE + std::fputc(0x80, fp); + std::fputc(0x80, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 6=DARK YELLOW + std::fputc(0xFF, fp); + std::fputc(0xFF, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 7=YELLOW ? LIGHT BLUE + std::fputc(0x00, fp); + std::fputc(0x80, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 8=DARK GREEN + std::fputc(0x00, fp); + std::fputc(0xFF, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); // 9=GREEN + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x80, fp); + std::fputc(0x00, fp); // 10=DARK RED + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0xFF, fp); + std::fputc(0x00, fp); // 11=RED + std::fputc(0x80, fp); + std::fputc(0x00, fp); + std::fputc(0x80, fp); + std::fputc(0x00, fp); // 12=DARK PURPLE + std::fputc(0xFF, fp); + std::fputc(0x00, fp); + std::fputc(0xFF, fp); + std::fputc(0x00, fp); // 13=PURPLE + std::fputc(0xFF, fp); + std::fputc(0xFF, fp); + std::fputc(0xFF, fp); + std::fputc(0x00, fp); // 14=WHITE + std::fputc(0xEF, fp); + std::fputc(0xEF, fp); + std::fputc(0xEF, fp); + std::fputc(0x00, fp); // 15=WHITE-GREY + std::fputc(0xDF, fp); + std::fputc(0xDF, fp); + std::fputc(0xDF, fp); + std::fputc(0x00, fp); // 16=GREY + std::fputc(0xCF, fp); + std::fputc(0xCF, fp); + std::fputc(0xCF, fp); + std::fputc(0x00, fp); // 17=DARKGREY + std::fputc(0xBF, fp); + std::fputc(0xBF, fp); + std::fputc(0xBF, fp); + std::fputc(0x00, fp); // 18=DARKGREY + std::fputc(0xAF, fp); + std::fputc(0xAF, fp); + std::fputc(0xAF, fp); + std::fputc(0x00, fp); // 19=DARKGREY for (dummy = 20; dummy < 256; dummy++) { // fill out the rest of the palette with zeros - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); - fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); + std::fputc(0x00, fp); } // write the actual image data data_start = ftell(fp); // get the data start position (that's where we are now) - fwrite(bmp_buffer, DEBUG_BMP_WIDTH * DEBUG_BMP_HEIGHT, 1, fp); // write the image + std::fwrite(bmp_buffer.data(), bmp_buffer.size(), 1, fp); // write the image file_size = ftell(fp); // get the file size now that the image is dumped // now that we've dumped our data, we know the file size and the data start position - fseek(fp, 0, SEEK_SET); // rewind - fseek(fp, 2, SEEK_CUR); // skip the BMP header tag "BM" - fwrite(&file_size, sizeof(unsigned long), 1, fp); // write the file size at its location - fseek(fp, sizeof(short), SEEK_CUR); // skip the first reserved field - fseek(fp, sizeof(short), SEEK_CUR); // skip the second reserved field - fwrite(&data_start, sizeof(unsigned long), 1, fp); // write the data start at its location + std::fseek(fp, 0, SEEK_SET); // rewind + std::fseek(fp, 2, SEEK_CUR); // skip the BMP header tag "BM" + std::fwrite(&file_size, sizeof(unsigned long), 1, fp); // write the file size at its location + std::fseek(fp, sizeof(short), SEEK_CUR); // skip the first reserved field + std::fseek(fp, sizeof(short), SEEK_CUR); // skip the second reserved field + std::fwrite(&data_start, sizeof(unsigned long), 1, fp); // write the data start at its location - fclose(fp); // finished, close the BMP file + std::fclose(fp); // finished, close the BMP file - if (bmp_buffer) - free(bmp_buffer); // and free the BMP buffer - bmp_buffer = NULL; + bmp_buffer.clear(); // and free the BMP buffer + bmp_buffer.shrink_to_fit(); - return; // and return + //return; // and return } // Find the border of all nodes to scale the bitmap -void cNodeMachine::FindMinMax(void) { - int i; - float scalex, scaley; - - minx = miny = 9999.0; - maxx = maxy = -9999.0; - for (i = 0; - (i < MAX_NODES) && (Nodes[i].origin != Vector(9999, 9999, 9999)); +void cNodeMachine::FindMinMax() const +{ + minx = miny = 9999.0f; + maxx = maxy = -9999.0f; + for (int i = 0; + i < MAX_NODES && Nodes[i].origin != Vector(9999, 9999, 9999); i++) { - if (Nodes[i].origin.x > maxx) - maxx = Nodes[i].origin.x; - if (Nodes[i].origin.y > maxy) - maxy = Nodes[i].origin.y; - if (Nodes[i].origin.x < minx) - minx = Nodes[i].origin.x; - if (Nodes[i].origin.y < miny) - miny = Nodes[i].origin.y; + maxx = std::max(Nodes[i].origin.x, maxx); + maxy = std::max(Nodes[i].origin.y, maxy); + minx = std::min(Nodes[i].origin.x, minx); + miny = std::min(Nodes[i].origin.y, miny); } // Avoid having lines/points just on the bitmap border, add some more spaces - maxx += NODE_ZONE; - minx -= NODE_ZONE; - maxy += NODE_ZONE; - miny -= NODE_ZONE; + maxx += static_cast(NODE_ZONE); + minx -= static_cast(NODE_ZONE); + maxy += static_cast(NODE_ZONE); + miny -= static_cast(NODE_ZONE); // first compute the X and Y divider scale, and take the greatest of both - scalex = (1 + maxx - minx) / DEBUG_BMP_WIDTH; - scaley = (1 + maxy - miny) / DEBUG_BMP_WIDTH; + const float scalex = (1 + maxx - minx) / static_cast(DEBUG_BMP_WIDTH); + const float scaley = (1 + maxy - miny) / static_cast(DEBUG_BMP_WIDTH); if (scalex > scaley) scale = scalex + scalex / 100; // add a little offset (margin) for safety else @@ -4412,35 +4423,36 @@ void cNodeMachine::FindMinMax(void) { // Palette is defined such that increasing the palette index // Makes a slightly darker dark -void cNodeMachine::MarkAxis(void) { - int x, y, x0, y0; +// Mark X and Y axis +void cNodeMachine::MarkAxis() +{ + int x, y; - x0 = (int) ((0 - minx) / scale); - y0 = (int) ((0 - miny) / scale); + // Mark X axis + y = static_cast((0 - miny) / scale); + if (y >= 0 && y < DEBUG_BMP_HEIGHT) + for (x = 0; x < DEBUG_BMP_WIDTH; x++) + bmp_buffer[y * DEBUG_BMP_WIDTH + x] = 17; - // Mark X axis by keeping X to 0 and varying Y - if ((minx < 0) && (0 < maxx)) + // Mark Y axis + x = static_cast((0 - minx) / scale); + if (x >= 0 && x < DEBUG_BMP_WIDTH) for (y = 0; y < DEBUG_BMP_HEIGHT; y++) - bmp_buffer[y * DEBUG_BMP_WIDTH + x0] += 2; - - // Mark Y axis by keeping Y to 0 and varying X - if ((miny < 0) && (0 < maxy)) - for (x = 0; x < DEBUG_BMP_WIDTH; x++) - bmp_buffer[y0 * DEBUG_BMP_WIDTH + x] += 2; + bmp_buffer[y * DEBUG_BMP_WIDTH + x] = 17; } // 05/07/04 // Mark each meredians (default 256 units) of alternating color -void cNodeMachine::MarkMeredians(void) { +void cNodeMachine::MarkMeredians() { int x, y; int Meredian; // Mark some meredians for (x = 0; x < DEBUG_BMP_WIDTH; x++) { - Meredian = - (int) ((float) x * scale + minx + - 8192.0) / (float) SIZE_MEREDIAN; + Meredian = static_cast( + (x * scale + minx + + 8192.0f) / static_cast(SIZE_MEREDIAN)); if (Meredian & 0x01) { for (y = 0; y < DEBUG_BMP_HEIGHT; y++) bmp_buffer[y * DEBUG_BMP_WIDTH + x]++; @@ -4449,9 +4461,9 @@ void cNodeMachine::MarkMeredians(void) { // Mark some meredians for (y = 0; y < DEBUG_BMP_HEIGHT; y++) { - Meredian = - (int) ((float) y * scale + miny + - 8192.0) / (float) SIZE_MEREDIAN; + Meredian = static_cast( + (y * scale + miny + + 8192.0f) / static_cast(SIZE_MEREDIAN)); if (Meredian & 0x01) { for (x = 0; x < DEBUG_BMP_HEIGHT; x++) bmp_buffer[y * DEBUG_BMP_WIDTH + x]++; @@ -4461,52 +4473,50 @@ void cNodeMachine::MarkMeredians(void) { // Put a cross on all nodes in RBN + draw lines to all neighbours -void cNodeMachine::PlotNodes(int NeighbourColor, int NodeColor) { - int i, j; +void cNodeMachine::PlotNodes(const unsigned char NeighbourColor, const unsigned char NodeColor) const +{ + int i; // Draw all neighbours for (i = 0; - (i < MAX_NODES) && (Nodes[i].origin != Vector(9999, 9999, 9999)); + i < MAX_NODES && Nodes[i].origin != Vector(9999, 9999, 9999); i++) - for (j = 0; (j < MAX_NEIGHBOURS) && (Nodes[i].iNeighbour[j] >= 0); + for (int j = 0; j < MAX_NEIGHBOURS && Nodes[i].iNeighbour[j] >= 0; j++) DrawLineInDebugBitmap(Nodes[i].origin, Nodes[Nodes[i].iNeighbour[j]].origin, NeighbourColor); // Draw all nodes for (i = 0; - (i < MAX_NODES) && (Nodes[i].origin != Vector(9999, 9999, 9999)); + i < MAX_NODES && Nodes[i].origin != Vector(9999, 9999, 9999); i++) DrawPoint(Nodes[i].origin, NodeColor); } // Put a small cross at all goal points -void cNodeMachine::PlotGoals(int color) { - int i; - Vector v; - - for (i = 0; (i < MAX_GOALS) && (Goals[i].iNode >= 0); i++) { - v = Nodes[Goals[i].iNode].origin; +void cNodeMachine::PlotGoals(const unsigned char color) const +{ + for (int i = 0; i < MAX_GOALS && Goals[i].iNode >= 0; i++) { + const Vector v = Nodes[Goals[i].iNode].origin; DrawPoint(v, color); } } // Plot the computed paths for all life bots -void cNodeMachine::PlotPaths(int Tcolor, int CTcolor) { - int i, iBot, From, To; - - for (iBot = 0; (iBot < 32); iBot++) { +void cNodeMachine::PlotPaths(const unsigned char Tcolor, const unsigned char CTcolor) const +{ + for (int iBot = 0; iBot < 32; iBot++) { if (bots[iBot].bIsUsed) { - From = iPath[iBot][0]; + int From = iPath[iBot][0]; if (From < 0) continue; // This bot has not path - for (i = 1; (i < MAX_NODES) && (iPath[iBot][i] >= 0); i++) { - To = iPath[iBot][i]; + for (int i = 1; i < MAX_NODES && iPath[iBot][i] >= 0; i++) { + const int To = iPath[iBot][i]; DrawLineInDebugBitmap(Nodes[From].origin, Nodes[To].origin, - (bots[iBot].iTeam == - 1) ? Tcolor : CTcolor); + bots[iBot].iTeam == + 1 ? Tcolor : CTcolor); From = To; } } @@ -4519,7 +4529,8 @@ void cNodeMachine::PlotPaths(int Tcolor, int CTcolor) { // 05/07/04 // Marking Axis, Meredians, other colors, other filenames (linked to map names) -void cNodeMachine::Draw(void) { +void cNodeMachine::Draw() const +{ static int Count = 0; // Static to create filenames like cs_siege0000.bmp, cs_siege0001.bmp, ... char Filename[80]; @@ -4530,11 +4541,12 @@ void cNodeMachine::Draw(void) { PlotNodes(0, 5); // 0 = black, 5 = blue PlotPaths(11, 7); // 11 = Red 7 = light blue ? PlotGoals(9); // 9 = green - sprintf(Filename, "%s%4.4d.bmp", STRING(gpGlobals->mapname), Count++); + snprintf(Filename, sizeof(Filename), "%s%4.4d.bmp", STRING(gpGlobals->mapname), Count++); WriteDebugBitmap(Filename); } -void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) { +void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) const +{ pBot->rprint_trace("cNodeMachine::ExecuteDoorInteractionLogic", "Start"); // check if we have to 'use' it @@ -4546,9 +4558,9 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) pBot->vHead = VecBModelOrigin(pEntityHit); pBot->vBody = pBot->vHead; UTIL_BotPressKey(pBot, IN_USE); - pBot->setTimeToWait(0.5); + pBot->setTimeToWait(0.5f); pBot->fButtonTime = gpGlobals->time + 5; - pBot->pButtonEdict = NULL; + pBot->pButtonEdict = nullptr; pBot->rprint_trace("cNodeMachine::ExecuteDoorInteractionLogic", "I have pressed USE to open a door - finished"); // TODO: when this door is opened by a trigger_multiple (on touch) @@ -4560,23 +4572,22 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) const char *doorButtonToLookFor = STRING(pEntityHit->v.targetname); if (doorButtonToLookFor) { - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "There is a target button , named %s, to open this door [%s] - going to search for it.", doorButtonToLookFor, STRING(pEntityHit->v.classname)); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "There is a target button , named %s, to open this door [%s] - going to search for it.", doorButtonToLookFor, STRING(pEntityHit->v.classname)); pBot->rprint_trace("cNodeMachine::ExecuteDoorInteractionLogic", msg); // find this entity - edict_t *pButtonEdict = NULL; - edict_t *pent = NULL; + edict_t *pButtonEdict = nullptr; + edict_t *pent = nullptr; TraceResult trb; // search for all buttons - while ((pent = UTIL_FindEntityByClassname(pent, "func_button")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "func_button")) != nullptr) { // skip anything that could be 'self' (unlikely) if (pent == pEntityHit) continue; // found button entity matching target - if (strcmp(STRING(pent->v.target), doorButtonToLookFor) == 0) { + if (std::strcmp(STRING(pent->v.target), doorButtonToLookFor) == 0) { Vector buttonVector = VecBModelOrigin(pent); UTIL_TraceLine(pBot->pEdict->v.origin, buttonVector, @@ -4584,34 +4595,33 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) pBot->pEdict, &trb); // if nothing hit: - if (trb.flFraction >= 1.0) { + if (trb.flFraction >= 1.0f) { // Button found to head for! pButtonEdict = pent; break; - } else { - // we hit this button we check for - if (trb.pHit == pent) { - // Button found to head for! - pButtonEdict = pent; - break; - } + } + // we hit this button we check for + if (trb.pHit == pent) { + // Button found to head for! + pButtonEdict = pent; + break; } } } // while (func_button) // still nothing found - if (pButtonEdict == NULL) { + if (pButtonEdict == nullptr) { // TOUCH buttons (are not func_button!) - pent = NULL; + pent = nullptr; // search for all buttons - while ((pent = UTIL_FindEntityByClassname(pent, "trigger_multiple")) != NULL) { + while ((pent = UTIL_FindEntityByClassname(pent, "trigger_multiple")) != nullptr) { // skip anything that could be 'self' (unlikely) if (pent == pEntityHit) continue; // found button entity - if (strcmp(STRING(pent->v.target), doorButtonToLookFor) == 0) { + if (std::strcmp(STRING(pent->v.target), doorButtonToLookFor) == 0) { // get vectr Vector vPentVector = VecBModelOrigin(pent); @@ -4622,14 +4632,13 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) bool isGood = false; // if nothing hit: - if (trb.flFraction >= 1.0) { + if (trb.flFraction >= 1.0f) { pButtonEdict = pent; break; - } else { - // we hit this button we check for - if (trb.pHit == pent) - isGood = true; } + // we hit this button we check for + if (trb.pHit == pent) + isGood = true; if (isGood) { // Button found to head for! @@ -4639,13 +4648,13 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) // as most doors have 2 buttons to access it (ie prodigy) // hits by worldspawn here - if (strcmp(STRING(trb.pHit->v.classname), "worldspawn") == 0) { + if (std::strcmp(STRING(trb.pHit->v.classname), "worldspawn") == 0) { // DE_PRODIGY FIX: // Somehow the button is not detectable. Find a node, that is close to it. // then retry the traceline. It should NOT hit a thing now. // On success, it is still our button - int iClose = getClosestNode(vPentVector, NODE_ZONE, pent); + const int iClose = getClosestNode(vPentVector, NODE_ZONE, pent); if (iClose > -1) { // retry the tracehull @@ -4656,7 +4665,7 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) pBot->pEdict, &trb); // if nothing hit: - if (trb.flFraction >= 1.0) { + if (trb.flFraction >= 1.0f) { pButtonEdict = pent; break; } @@ -4670,22 +4679,22 @@ void cNodeMachine::ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pEntityHit) // We have found a button to go to if (pButtonEdict) { - memset(msg, 0, sizeof(msg)); + std::memset(msg, 0, sizeof(msg)); // Get its vector - Vector vButtonVector = VecBModelOrigin(pButtonEdict); + const Vector vButtonVector = VecBModelOrigin(pButtonEdict); // Search a node close to it - int iButtonNode = getClosestNode(vButtonVector, NODE_ZONE, pButtonEdict); + const int iButtonNode = getClosestNode(vButtonVector, NODE_ZONE, pButtonEdict); // When node found, create path to it if (pBot->createPath(iButtonNode, PATH_NONE)) { - sprintf(msg, "Found a button at node %d and created a path to it.", iButtonNode); + snprintf(msg, sizeof(msg), "Found a button at node %d and created a path to it.", iButtonNode); pBot->pButtonEdict = pButtonEdict; } else { if (iButtonNode > -1) { - sprintf(msg, "Found a button at node %d but failed to create a path to it.", iButtonNode); + snprintf(msg, sizeof(msg), "Found a button at node %d but failed to create a path to it.", iButtonNode); } else { - sprintf(msg, "Found a button, but there is no nearby node :/"); + snprintf(msg, sizeof(msg), "Found a button, but there is no nearby node :/"); } } pBot->rprint_trace("cNodeMachine::ExecuteDoorInteractionLogic", msg); diff --git a/NodeMachine.h b/NodeMachine.h index 18e3a0e..604ee82 100644 --- a/NodeMachine.h +++ b/NodeMachine.h @@ -35,7 +35,12 @@ #ifndef NODEMACHINE_H #define NODEMACHINE_H +#include +#include +#include + #include "bot.h" +#include "game.h" #include "NodeDataTypes.h" @@ -43,15 +48,15 @@ class cNodeMachine { public: // ----------------- - int addNode(Vector vOrigin, edict_t *pEntity); + int addNode(const Vector& vOrigin, edict_t *pEntity); - int Reachable(const int iStart, const int iEnd); + int Reachable(int iStart, int iEnd) const; - int add2(Vector vOrigin, int iType, edict_t *pEntity); + int add2(const Vector& vOrigin, int iType, edict_t *pEntity); - int getClosestNode(Vector vOrigin, float fDist, edict_t *pEdict); // returns a close node - int getFurthestNode(Vector vOrigin, float fDist, edict_t *pEdict); // returns a node within dist, but most far away - int getFreeNodeIndex(); + int getClosestNode(const Vector& vOrigin, float fDist, edict_t *pEdict) const; // returns a close node + int getFurthestNode(const Vector& vOrigin, float fDist, const edict_t *pEdict) const; // returns a node within dist, but most far away + int getFreeNodeIndex() const; // ----------------- bool add_neighbour_node(int iNode, int iToNode); @@ -60,28 +65,37 @@ class cNodeMachine { bool remove_neighbour_nodes(int iNode); - int freeNeighbourNodeIndex(tNode *Node); + static int freeNeighbourNodeIndex(const tNode *Node); - int is_neighbour_node(tNode node, int iNode); + static int is_neighbour_node(const tNode& node, int iNode); // ----------------- void init(); // Init (info)nodes - void save(); // Save nodes on disk + + void initNodes(); + void initInfoNodes(); + void initializeNode(tNode& node); + void initTroubles(); + void initPaths(); + void initVisTable(); + void initMeredians(); + + void save() const; // Save nodes on disk void load(); // Load nodes on disk - void save_important(); + void save_important() const; // ----------------- - Vector node_vector(int iNode); + Vector node_vector(int iNode) const; // ----------------- - int GetTroubleIndexForConnection(int iFrom, int iTo); + int GetTroubleIndexForConnection(int iFrom, int iTo) const; int AddTroubledConnection(int iFrom, int iTo); bool IncreaseAttemptsForTroubledConnectionOrRemoveIfExceeded(int iFrom, int iTo); - bool hasAttemptedConnectionTooManyTimes(int index); + bool hasAttemptedConnectionTooManyTimes(int index) const; void IncreaseAttemptsForTroubledConnection(int index); bool ClearTroubledConnection(int iFrom, int iTo); @@ -90,24 +104,24 @@ class cNodeMachine { void setUpInitialGoals(); // find new goals and attach them to the nodes void updateGoals(); // update moving goals (ie hostages) - int getGoalIndexFromNode(int iNode); + int getGoalIndexFromNode(int iNode) const; void resetCheckedValuesForGoals(); void ClearImportantGoals(); - bool hasGoalWithEdict(edict_t *pEdict); + bool hasGoalWithEdict(edict_t *pEdict) const; - void addGoal(edict_t *pEdict, int goalType, Vector vVec); + void addGoal(edict_t *pEdict, int goalType, const Vector& vVec); tGoal * getRandomGoalByType(int goalType); // return a node close to a iType goal (random) - bool node_float(Vector vOrigin, edict_t *pEdict); + static bool node_float(const Vector& vOrigin, edict_t *pEdict); - bool node_on_crate(Vector vOrigin, edict_t *pEdict); + static bool node_on_crate(const Vector& vOrigin, edict_t *pEdict); - int node_dangerous(int iTeam, Vector vOrigin, float fMaxDistance); + int node_dangerous(int iTeam, const Vector& vOrigin, float fMaxDistance); - int node_look_camp(Vector vOrigin, int iTeam, edict_t *pEdict); + int node_look_camp(const Vector& vOrigin, int iTeam, edict_t *pEdict); // ----------------- void danger(int iNode, int iTeam); // Make spot dangerous @@ -125,30 +139,30 @@ class cNodeMachine { // ----------------- int node_cover(int iFrom, int iTo, edict_t *pEdict); - int node_look_at_hear(int iFrom, int iTo, edict_t *pEdict); - - int node_camp(Vector vOrigin, int iTeam); + int node_look_at_hear(int iFrom, int iOrigin, edict_t* pEdict); + + int node_camp(const Vector& vOrigin, int iTeam) const; void vis_calculate(int iFrom); // ----------------- bool createPath(int nodeStartIndex, int nodeTargetIndex, int botIndex, cBot *pBot, int iFlags); // know the path - void path_draw(edict_t *pEntity); // draw the path + void path_draw(edict_t *pEntity) const; // draw the path void path_walk(cBot *pBot, float distanceMoved); // walk the path void path_think(cBot *pBot, float distanceMoved); // think about paths void path_clear(int botIndex); void ExecuteNearNodeLogic(cBot *pBot); - int getNodeIndexFromBotForPath(int botIndex, int pathNodeIndex); + int getNodeIndexFromBotForPath(int botIndex, int pathNodeIndex) const; // ----------------- - void VectorToMeredian(Vector vOrigin, int *iX, int *iY); // Input: origin, output X and Y Meredians + static void VectorToMeredian(const Vector& vOrigin, int *iX, int *iY); // Input: origin, output X and Y Meredians void AddToMeredian(int iX, int iY, int iNode); // ----------------- - void draw(edict_t *pEntity); // Draw nodes - void connections(edict_t *pEntity); // Draw neighbours + void draw(edict_t *pEntity) const; // Draw nodes + void connections(edict_t *pEntity) const; // Draw neighbours // ----------------- void addNodesForPlayers(); // Players plot around! @@ -157,57 +171,64 @@ class cNodeMachine { // ------------------- // From cheesemonster: - int GetVisibilityFromTo(int iFrom, int iTo); // BERKED - void ClearVisibilityTable(void); + int GetVisibilityFromTo(int iFrom, int iTo) const; // BERKED + void ClearVisibilityTable(); void SetVisibilityFromTo(int iFrom, int iTo, bool bVisible); - void FreeVisibilityTable(void); + void FreeVisibilityTable(); // Some debugging by EVY - void dump_goals(void); + void dump_goals() const; - void dump_path(int iBot, int ThisNode); + void dump_path(int iBot, int CurrentPath) const; - void Draw(void); + void Draw() const; tNode *getNode(int index); tGoal *getGoal(int index); private: - tNode Nodes[MAX_NODES]; // Nodes - tInfoNode InfoNodes[MAX_NODES]; // Info for Nodes (metadata) - tPlayer Players[32]; // Players to keep track of, for node plotting - tGoal Goals[MAX_GOALS]; // Goals to pursue in the game - tMeredian Meredians[MAX_MEREDIANS][MAX_MEREDIANS]; // Meredian lookup search for Nodes, squared + tNode Nodes[MAX_NODES] = {}; // Nodes + tInfoNode InfoNodes[MAX_NODES] = {}; // Info for Nodes (metadata) + tPlayer Players[32] = {}; // Players to keep track of, for node plotting + tGoal Goals[MAX_GOALS] = {}; // Goals to pursue in the game + tMeredian Meredians[MAX_MEREDIANS][MAX_MEREDIANS] = {}; // Meredian lookup search for Nodes, squared + + int iPath[MAX_BOTS][MAX_PATH_NODES] = {}; // 32 bots, with max waypoints paths (TODO: move to bot class?) + + int iMaxUsedNodes = 0; + + byte iVisChecked[MAX_NODES] = {}; + std::vector cVisTable; + tTrouble Troubles[MAX_TROUBLE] = {}; + + void FindMinMax() const; - int iPath[MAX_BOTS][MAX_PATH_NODES]; // 32 bots, with max waypoints paths (TODO: move to bot class?) + static void MarkAxis(); - int iMaxUsedNodes; + static void MarkMeredians(); - byte iVisChecked[MAX_NODES]; - unsigned char *cVisTable; - tTrouble Troubles[MAX_TROUBLE]; + void PlotNodes(unsigned char NeighbourColor, unsigned char NodeColor) const; - void FindMinMax(void); + void PlotPaths(unsigned char Tcolor, unsigned char CTcolor) const; - void MarkAxis(void); + void PlotGoals(unsigned char color) const; - void MarkMeredians(void); + static void makeAllWaypointsAvailable(); - void PlotNodes(int NeighbourColor, int NodeColor); + static bool isValidNodeIndex(int index); - void PlotPaths(int Tcolor, int CTcolor); + static bool isInvalidNode(int index); - void PlotGoals(int GoalColor); + void buildPath(int nodeStartIndex, int nodeTargetIndex, int botIndex, cBot* pBot); - void makeAllWaypointsAvailable() const; + static void closeNode(int nodeIndex, int parent, float cost); - void closeNode(int nodeIndex, int parent, float cost); - void openNeighbourNodes(int startNodeIndex, int nodeToOpenNeighboursFrom, int destinationNodeIndex, int botTeam); + void openNeighbourNodes(int startNodeIndex, int nodeToOpenNeighboursFrom, int destinationNodeIndex, int botTeam) const; - char *getGoalTypeAsText(const tGoal &goal) const; + static std::string getGoalTypeAsText(const tGoal& goal); int getFreeGoalIndex() const; @@ -215,15 +236,15 @@ class cNodeMachine { void initGoal(int g); - bool isEntityDoor(const edict_t *pEntityHit) const; - bool isEntityHostage(const edict_t *pEntityHit) const; - bool isEntityWorldspawn(const edict_t *pEntityHit) const; + static bool isEntityDoor(const edict_t *pEntityHit); + static bool isEntityHostage(const edict_t *pEntityHit); + static bool isEntityWorldspawn(const edict_t *pEntityHit); - bool isDoorThatOpensWhenPressingUseButton(const edict_t *pEntityHit) const; + static bool isDoorThatOpensWhenPressingUseButton(const edict_t *pEntityHit); - void ExecuteIsStuckLogic(cBot *pBot, int currentNodeToHeadFor, Vector &vector); + void ExecuteIsStuckLogic(cBot *pBot, int currentNodeToHeadFor, const Vector &vector); - void ExecuteDoorInteractionLogic(cBot *pBot, edict_t *pS); + void ExecuteDoorInteractionLogic(cBot* pBot, edict_t* pEntityHit) const; }; #endif // NODEMACHINE_H diff --git a/bin/data/cstrike/exp/.DS_Store b/bin/data/cstrike/exp/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/bin/data/cstrike/exp/.DS_Store and /dev/null differ diff --git a/bin/data/cstrike/maps/.DS_Store b/bin/data/cstrike/maps/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/bin/data/cstrike/maps/.DS_Store and /dev/null differ diff --git a/bin/dll/.DS_Store b/bin/dll/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/bin/dll/.DS_Store and /dev/null differ diff --git a/bot.cpp b/bot.cpp index 714e380..b81e57f 100644 --- a/bot.cpp +++ b/bot.cpp @@ -1,4418 +1,4601 @@ -/** - * RealBot : Artificial Intelligence - * Version : Work In Progress - * Author : Stefan Hendriks - * Url : http://realbot.bots-united.com - ** - * DISCLAIMER - * - * History, Information & Credits: - * RealBot is based partially upon the HPB-Bot Template #3 by Botman - * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) - * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And - * everybody else who helped me with this project. - * Storage of Visibility Table using BITS by Cheesemonster. - * - * Some portions of code are from other bots, special thanks (and credits) go - * to (in no specific order): - * - * Pierre Marie Baty - * Count - Floyd - * - * !! BOTS-UNITED FOREVER !! - * - * This project is open-source, it is protected under the GPL license; - * By using this source-code you agree that you will ALWAYS release the - * source-code with your project. - * - **/ - - -/* - -//========================================================= -// Returns if enemy can be shoot through some obstacle -//========================================================= -bool CBaseBot::IsShootableThruObstacle(Vector vecDest) -{ - if (!WeaponShootsThru(m_iCurrentWeapon)) - return FALSE; - - Vector vecSrc = EyePosition(); - Vector vecDir = (vecDest - vecSrc).Normalize(); // 1 unit long - Vector vecPoint = g_vecZero; - int iThickness = 0; - int iHits = 0; - - edict_t *pentIgnore = pev->pContainingEntity; - TraceResult tr; - UTIL_TraceLine(vecSrc, vecDest, ignore_monsters, ignore_glass, pentIgnore, &tr); - - while (tr.flFraction != 1.0 && iHits < 3) - { - iHits++; - iThickness++; - vecPoint = tr.vecEndPos + vecDir; - while (POINT_CONTENTS(vecPoint) == CONTENTS_SOLID && iThickness < 64) - { - vecPoint = vecPoint + vecDir; - iThickness++; - } - UTIL_TraceLine(vecPoint, vecDest, ignore_monsters, ignore_glass, pentIgnore, &tr); - } - - if (iHits < 3 && iThickness < 64) - { - if (LengthSquared(vecDest - vecPoint) < 12544) - return TRUE; - } - - return FALSE; -} - -*/ - -#include -#include -#include -#include -#include - -#include "bot.h" -#include "bot_weapons.h" -#include "bot_func.h" - -#include "game.h" -#include "NodeMachine.h" -#include "ChatEngine.h" - -#include -#include - -extern edict_t *pHostEdict; -extern int mod_id; -extern bool internet_play; -extern cGame Game; -extern cNodeMachine NodeMachine; -extern cChatEngine ChatEngine; -extern int counterstrike; -//static FILE *fp; -extern bool autoskill; - -/* Radio issue - Credit by Ditlew (NNBOT - Rest In Peace) */ -bool radio_message = false; -char *message = (char *) malloc(64 * sizeof(char)); -char radio_messenger[30]; - -// random boundries -extern int random_max_skill; -extern int random_min_skill; -cBot bots[32]; // max of 32 bots in a game - -// External added variables -extern bool end_round; // End round - -#ifndef _WIN32 -#define _snprintf snprintf -#endif - -cBot::cBot() { - pBotHostage = NULL; - fMoveToNodeTime = -1; - clearHostages(); -} - -/****************************************************************************** - Function purpose: Initializes bot vars on spawn - ******************************************************************************/ -void cBot::SpawnInit() { - rprint_trace("SpawnInit()", "START"); - - // ------------------------ - // TIMERS - // ------------------------ - fUpdateTime = gpGlobals->time; - fLastRunPlayerMoveTime = gpGlobals->time - 0.1f; - fButtonTime = gpGlobals->time; - fChatTime = gpGlobals->time + RANDOM_FLOAT(0.5, 5); - fMemoryTime = gpGlobals->time; - fDoRadio = gpGlobals->time; - float freezeTimeCVAR = CVAR_GET_FLOAT("mp_freezetime"); - fNotStuckTime = gpGlobals->time + freezeTimeCVAR + 0.5f; - f_shoot_wait_time = gpGlobals->time; - f_goback_time = gpGlobals->time; - f_may_jump_time = gpGlobals->time; - fCheckHostageStatusTimer = gpGlobals->time; - f_defuse = gpGlobals->time; - f_allow_keypress = gpGlobals->time; - f_use_timer = gpGlobals->time; - f_light_time = gpGlobals->time; - f_sec_weapon = gpGlobals->time; - f_prim_weapon = gpGlobals->time; - f_gren_time = gpGlobals->time; - f_walk_time = gpGlobals->time; - f_hear_time = gpGlobals->time; - freezeTime = gpGlobals->time - 1; - f_cover_time = gpGlobals->time; - f_c4_time = gpGlobals->time; - f_update_weapon_time = gpGlobals->time; - f_follow_time = gpGlobals->time; - f_jump_time = 0.0; - f_hold_duck = gpGlobals->time; - f_camp_time = gpGlobals->time; - f_wait_time = gpGlobals->time; - f_bot_see_enemy_time = gpGlobals->time; - f_bot_find_enemy_time = gpGlobals->time; - f_shoot_time = gpGlobals->time; - fMoveToNodeTime = -1; - nodeTimeIncreasedAmount = 0; - distanceMovedTimer = gpGlobals->time; - distanceMoved = 0; - fBlindedTime = gpGlobals->time; - f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.1, 0.9); - fWanderTime = gpGlobals->time; - f_strafe_time = gpGlobals->time; - - // Personality Related (these gets changed when loading personality file) - fpXOffset = 0.0; - fpYOffset = 0.0; - fpZOffset = 0.0; - fpMinReactTime = 0.0; - fpMaxReactTime = 0.0; - - // ------------------------ - // POINTERS - // ------------------------ - pButtonEdict = NULL; - pBotHostage = NULL; - clearHostages(); - pEnemyEdict = NULL; - - // chat - memset(chChatSentence, 0, sizeof(chChatSentence)); - - - // ------------------------ - // INTEGERS - // ------------------------ - iGoalNode = -1; - goalIndex = -1; - iPreviousGoalNode = -1; - iCloseNode = -1; - iDiedNode = -1; - - iTeam = -1; - bot_class = -1; - i_camp_style = 0; - iPrimaryWeapon = -1; - iSecondaryWeapon = -1; - zoomed = ZOOM_NONE; - play_rounds = RANDOM_LONG(Game.GetMinPlayRounds(), Game.GetMaxPlayRounds()); - bot_health = 0; - prev_health = 0; - bot_armor = 0; - bot_weapons = 0; - bot_use_special = 0 + RANDOM_LONG(0, 2); - console_nr = 0; - pathIndex = -1; - iPathFlags = PATH_DANGER; - - // Smarter Stuck stuff - iDuckTries = 0; - iJumpTries = 0; - - // ------------------------ - // BOOLEANS - // ------------------------ - vip = UTIL_IsVip(pEdict); - bWalkKnife = false; - buy_ammo_primary = true; - buy_ammo_secondary = true; - buy_primary = (Game.bPistols ? false : true); //30/07/04: Josh, handle the pistols only mode - buy_secondary = (Game.bPistols ? true : false); - buy_armor = false; - buy_defusekit = false; - bFirstOutOfSight = false; - buy_grenade = false; - buy_smokegrenade = false; - - buy_flashbang = 0; - if (RANDOM_LONG(0, 100) < ipWalkWithKnife) { - bWalkKnife = true; - } - - if (UTIL_GetTeam(pEdict) == 1) { - if (RANDOM_LONG(0, 100) < ipBuyDefuseKit) { - buy_defusekit = true; - } - } - - if (RANDOM_LONG(0, 100) < ipBuyGrenade) { - buy_grenade = true; - } - - // 31.08.04 Frashman added Support for Smoke Grenade - if (RANDOM_LONG(0, 100) < ipBuySmokeGren) { - buy_smokegrenade = true; - } - - if (RANDOM_LONG(0, 100) < ipBuyFlashBang) { - buy_flashbang = 2; - - } - - if (RANDOM_LONG(0, 100) < 15 || Game.bPistols) - buy_secondary = true; - - // ------------------------ - // HUD - // ------------------------ - bHUD_C4_plantable = false; // Get's init'ed anyway... // BERKED - - // ------------------------ - // FLOATS - // ------------------------ - f_strafe_speed = 0.0; - f_max_speed = CVAR_GET_FLOAT("sv_maxspeed"); - - // ------------------------ - // VECTORS - // ------------------------ - prevOrigin = Vector(9999.0, 9999.0, 9999.0); - lastSeenEnemyVector = Vector(0, 0, 0); - vEar = Vector(9999, 9999, 9999); - - // ------------------------ - // CHAR - // ------------------------ - arg1[0] = 0; - arg2[0] = 0; - arg3[0] = 0; - memset(&(current_weapon), 0, sizeof(current_weapon)); - memset(&(m_rgAmmo), 0, sizeof(m_rgAmmo)); - - rprint_trace("SpawnInit()", "END"); -} - -/****************************************************************************** - Function purpose: Initializes bot vars on new round - ******************************************************************************/ -void cBot::NewRound() { - rprint_trace("NewRound()", "START"); - - // ------------------------ - // TIMERS - // ------------------------ - fUpdateTime = gpGlobals->time; - fLastRunPlayerMoveTime = gpGlobals->time; - fCheckHostageStatusTimer = gpGlobals->time; - fButtonTime = gpGlobals->time; - fChatTime = gpGlobals->time + RANDOM_FLOAT(0.5, 5); - fMemoryTime = gpGlobals->time; - fDoRadio = gpGlobals->time; - float freezeTimeCVAR = CVAR_GET_FLOAT("mp_freezetime"); - fNotStuckTime = gpGlobals->time + freezeTimeCVAR + 0.5f; - f_shoot_wait_time = gpGlobals->time; - f_goback_time = gpGlobals->time; - f_may_jump_time = gpGlobals->time; - f_defuse = gpGlobals->time; - f_allow_keypress = gpGlobals->time; - f_use_timer = gpGlobals->time; - f_light_time = gpGlobals->time; - f_sec_weapon = gpGlobals->time; - f_prim_weapon = gpGlobals->time; - f_gren_time = gpGlobals->time; - f_walk_time = gpGlobals->time; - f_hear_time = gpGlobals->time; - freezeTime = gpGlobals->time - 1; - f_cover_time = gpGlobals->time; - f_c4_time = gpGlobals->time; - f_update_weapon_time = gpGlobals->time; - f_follow_time = gpGlobals->time; - f_jump_time = 0.0; - f_hold_duck = gpGlobals->time - 1; - f_camp_time = gpGlobals->time; - f_wait_time = gpGlobals->time; - f_bot_see_enemy_time = gpGlobals->time; - f_bot_find_enemy_time = gpGlobals->time; - f_shoot_time = gpGlobals->time; - fMoveToNodeTime = -1; - nodeTimeIncreasedAmount = 0; - distanceMovedTimer = gpGlobals->time; - distanceMoved = 0; - fBlindedTime = gpGlobals->time; - f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.1, 0.9); - fWanderTime = gpGlobals->time; - f_strafe_time = gpGlobals->time; - - // ------------------------ - // POINTERS - // ------------------------ - pButtonEdict = NULL; - pBotHostage = NULL; - clearHostages(); - pEnemyEdict = NULL; - - // ------------------------ - // INTEGERS - // ------------------------ - i_camp_style = 0; - iPrimaryWeapon = -1; - iSecondaryWeapon = -1; - zoomed = ZOOM_NONE; - bot_health = 0; - prev_health = 0; - bot_armor = 0; -// bot_weapons = 0; // <- stefan: prevent from buying new stuff every round! - console_nr = 0; - pathIndex = -1; - iGoalNode = -1; - goalIndex = -1; - iPreviousGoalNode = -1; - iCloseNode = -1; - - - // Smarter Stuck stuff - iDuckTries = 0; - iJumpTries = 0; - - if (RANDOM_LONG(0, 100) < ipFearRate) - iPathFlags = PATH_DANGER; - else - iPathFlags = PATH_NONE; - - // ------------------------ - // BOOLEANS - // ------------------------ - - // chat - memset(chChatSentence, 0, sizeof(chChatSentence)); - - vip = UTIL_IsVip(pEdict); - - // Every round consider - bWalkKnife = false; - - if (RANDOM_LONG(0, 100) < ipWalkWithKnife) - bWalkKnife = true; - - // Buying - buy_ammo_primary = true; - buy_ammo_secondary = true; - buy_primary = (Game.bPistols ? false : true); - buy_grenade = false; - buy_smokegrenade = false; - buy_flashbang = 0; - buy_secondary = (Game.bPistols ? true : false); - buy_armor = false; - buy_defusekit = false; - - if (UTIL_GetTeam(pEdict) == 1) - if (RANDOM_LONG(0, 100) < ipBuyDefuseKit) - buy_defusekit = true; - - if (RANDOM_LONG(0, 100) < ipBuyArmour) - buy_armor = true; - - if (RANDOM_LONG(0, 100) < ipBuyGrenade) - buy_grenade = true; - - if (RANDOM_LONG(0, 100) < ipBuySmokeGren) - buy_smokegrenade = true; - - if (RANDOM_LONG(0, 100) < ipBuyFlashBang) - buy_flashbang = 2; - - - bFirstOutOfSight = false; - - - f_strafe_speed = 0.0; - - // ------------------------ - // VECTORS - // ------------------------ - prevOrigin = Vector(9999.0, 9999.0, 9999.0); - lastSeenEnemyVector = Vector(0, 0, 0); - vEar = Vector(9999, 9999, 9999); - - // ------------------------ - // CHAR - // ------------------------ - arg1[0] = 0; - arg2[0] = 0; - arg3[0] = 0; - - // initalize a few other stuff - NodeMachine.path_clear(iBotIndex); - iPathFlags = PATH_NONE; - - played_rounds++; - - // hello dudes - if (played_rounds == 1) { - // do some chatting - if (RANDOM_LONG(0, 100) < (ipChatRate + 10)) { - // we should say something now? - int iMax = -1; - - for (int tc = 0; tc < 50; tc++) { - if (ChatEngine.ReplyBlock[98].sentence[tc][0] != '\0') - iMax++; - } - - int the_c = RANDOM_LONG(0, iMax); - - if (the_c > -1 && iMax > -1) { - char chSentence[80]; - memset(chSentence, 0, sizeof(chSentence)); - sprintf(chSentence, "%s ", - ChatEngine.ReplyBlock[98].sentence[the_c]); - PrepareChat(chSentence); - } - } - } - - clearHostages(); - clearHostageToRescueTarget(); - - rprint("NewRound", "Initialization new round finished"); -} - -/****************************************************************************** - Function purpose: Returns a random chat sentence and stores it into 'sentence' - ******************************************************************************/ -void cBot::PrepareChat(char sentence[128]) { - if (Game.iProducedSentences <= Game.iMaxSentences) { - // makes bot chat away - fChatTime = gpGlobals->time + RANDOM_FLOAT(0.1, 2.0); - strcpy(chChatSentence, sentence); // copy this - Game.iProducedSentences++; - } -} - -/****************************************************************************** - Function purpose: Return reaction time based upon skill - ******************************************************************************/ -float cBot::ReactionTime(int iSkill) { - float time = RANDOM_FLOAT(fpMinReactTime, fpMaxReactTime); - if (Game.messageVerbosity > 1) { - char msg[255]; - sprintf(msg, "minReactTime %f, maxReactTime %f, skill %d, results into %f", fpMinReactTime, fpMaxReactTime, iSkill, time); - rprint_trace("ReactionTime()", msg); - } - return time; -} - -/****************************************************************************** - Function purpose: Finds a (new) enemy - ******************************************************************************/ -int cBot::FindEnemy() { - // When on ladder, do not search for enemies - if (isOnLadder()) - return -1; - - // When blinded we cannot search for enemies - if (fBlindedTime > gpGlobals->time) - return -1; - float fNearestDistance = 9999; // Nearest distance - edict_t *pNewEnemy = NULL; // New enemy found - - // SEARCH PLAYERS FOR ENEMIES - for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - - // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { - - // skip this player if not alive (i.e. dead or dying) - if (!IsAlive(pPlayer)) - continue; - - Vector vVecEnd = pPlayer->v.origin + pPlayer->v.view_ofs; - - // if bot can see the player... - if (FInViewCone(&vVecEnd, pEdict) && FVisible(vVecEnd, pEdict)) { - int player_team = UTIL_GetTeam(pPlayer); - int bot_team = UTIL_GetTeam(pEdict); - if (player_team == bot_team) - continue; // do not target teammates - - // Its not a friend, track enemy - float fDistance = - (pPlayer->v.origin - pEdict->v.origin).Length(); - bool bCanSee = true; - - // The further away, the less chance we see this enemy - //if (RANDOM_FLOAT(0,1.0) < (fDistance/4096)) - // bCanSee=false; - if (CarryWeaponType() == SNIPER) - bCanSee = true; - if (fDistance < fNearestDistance && bCanSee) { - fNearestDistance = fDistance; - pNewEnemy = pPlayer; - } - continue; - } - } // valid player - } // FOR - - // We found a new enemy & the new enemy is different then previous pointer - if (pNewEnemy && pNewEnemy != pEnemyEdict) { - int iCurrentNode = determineCurrentNode(); - - // Add 'contact' data - if (iCurrentNode > -1) { - NodeMachine.contact(iCurrentNode, UTIL_GetTeam(pEdict)); - } - - // We have a reaction time to this new enemy - rememberEnemyFound(); - f_shoot_time = gpGlobals->time + ReactionTime(bot_skill); - pEnemyEdict = pNewEnemy; // Update pointer - - // We did not have an enemy before - if (pEnemyEdict == NULL) { - rprint_trace("FindEnemy()", "Found new enemy"); - - // RADIO: When we found a NEW enemy but NOT via a friend - if (FUNC_DoRadio(this)) { - UTIL_BotRadioMessage(this, 3, "2", ""); - } - - // We found a new enemy - return 0; - } else { - // we found an enemy that is newer/more dangerous then previous - rprint_trace("FindEnemy()", "Found 'newer' enemy"); - return 3; - } - } - - // nothing found - return -1; // return result -} - -void cBot::rememberEnemyFound() { - f_bot_find_enemy_time = gpGlobals->time + REMEMBER_ENEMY_TIME; -} - -/****************************************************************************** - Function purpose: Sets vHead to aim at vector - ******************************************************************************/ -void cBot::setHeadAiming(Vector vTarget) { - vHead = vTarget; -} - -/** - * Returns true / false wether enemy is alive. - * @return - */ -bool cBot::isEnemyAlive() { - return IsAlive(pEnemyEdict); -} - -bool cBot::isSeeingEnemy() { - if (!hasEnemy()) { - this->rprint("canSeeEnemy called without having enemy?"); - return false; - } - - if (isBlindedByFlashbang()) { - return false; - } - - Vector vBody = pEnemyEdict->v.origin; - Vector vHead = pEnemyEdict->v.origin + pEnemyEdict->v.view_ofs; - - bool bodyInFOV = FInViewCone(&vBody, pEdict) && FVisible(vBody, pEdict); - bool headInFOV = FInViewCone(&vHead, pEdict) && FVisible(vHead, pEdict); - if (bodyInFOV || headInFOV) { - return true; - } - return false; -} - -/****************************************************************************** - Function purpose: Aims at enemy, only when valid. Based upon skill how it 'aims' - ******************************************************************************/ -void cBot::AimAtEnemy() { - if (!hasEnemy()) - return; - - // We cannot see our enemy? -> bail out - if (isSeeingEnemy()) { - setHeadAiming(lastSeenEnemyVector); // look at last known vector of enemy - return; - } - - // ------------------------ we can see enemy ------------------------- - float fDistance; - - // Distance to enemy - fDistance = (pEnemyEdict->v.origin - pEdict->v.origin).Length() + 1; // +1 to make sure we never divide by zero - - // factor in distance, the further away the more deviation - which is based on skill - int skillReversed = (10 - bot_skill) + 1; - float fScale = 0.5 + (fDistance / (64 * - skillReversed)); // a good skilled bot is less impacted by distance than a bad skilled bot - - if (CarryWeaponType() == SNIPER) fScale *= 0.80; // sniping improves aiming - - // Set target here - Vector vTarget; - if (bot_skill <= 1) - vTarget = pEnemyEdict->v.origin + pEnemyEdict->v.view_ofs * RANDOM_FLOAT(-0.5, 1.1); // aim for the head - else if (bot_skill > 1 && bot_skill < 4) - vTarget = pEnemyEdict->v.origin + - pEnemyEdict->v.view_ofs * RANDOM_FLOAT(-2.5, 2.5); // aim for the head more fuzzy - else - vTarget = pEnemyEdict->v.origin; // aim for body - - // Based upon how far, we make this fuzzy - float fDx, fDy, fDz; - fDx = fDy = fDz = ((bot_skill + 1) * fScale); - - // Example 1: - // Super skilled bot (bot_skill 1), with enemy of 2048 units away. Results into: - // skillReversed = (10 - 0 + 1) == 11 - // fScale = 2048 / (128 * 11) -> 2048 / 1408 => 1.454545 - // fd* = 0.5 + 1 * 1,95 - - // Example 2, less skilled bot (skill = 3) same enemy - // skillReversed = (10 - 3 + 1) == 8 - // fScale = 2048 / (128 * 8) -> 2048 / 1024 => 2 - // fd* = 3 * 2 - - vTarget = vTarget + Vector( - RANDOM_FLOAT(-fDx, fDx), - RANDOM_FLOAT(-fDy, fDy), - RANDOM_FLOAT(-fDz, fDz) - ); - - // Add Offset - fDx = fpXOffset; - fDy = fpYOffset; - fDz = fpZOffset; - - // increase offset with personality x,y,z offsets randomly - vTarget = vTarget + Vector( - RANDOM_FLOAT(-fDx, fDx), - RANDOM_FLOAT(-fDy, fDy), - RANDOM_FLOAT(-fDz, fDz) - ); - - if (isHoldingGrenadeOrFlashbang()) { - // aim a bit higher - vTarget = vTarget + Vector(0, 0, 50); - } - - setHeadAiming(vTarget); -} - -bool cBot::isBlindedByFlashbang() const { - return fBlindedTime > gpGlobals->time; -} - -bool cBot::isHoldingGrenadeOrFlashbang() const { - return current_weapon.iId == CS_WEAPON_HEGRENADE || current_weapon.iId == CS_WEAPON_FLASHBANG; -} - -/****************************************************************************** - Function purpose: Perform fighting actions - ******************************************************************************/ -void cBot::FightEnemy() { - // We can see our enemy - if (!isBlindedByFlashbang() && isSeeingEnemy()) { - - // GET OUT OF CAMP MODE - if (f_camp_time > gpGlobals->time) { - f_camp_time = gpGlobals->time; - } - - // Next time our enemy gets out of sight, it will be the 'first' time - // of all 'frame times'. - bFirstOutOfSight = false; - - // Remember last seen enemy position - lastSeenEnemyVector = pEnemyEdict->v.origin; // last seen enemy position - - // FIXME: Fix the darn zoom bug - // zoom in with sniper gun - if (CarryWeaponType() == SNIPER) { - if (zoomed < ZOOM_TWICE && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); - f_allow_keypress = gpGlobals->time + 0.7; - zoomed++; - - if (zoomed > ZOOM_TWICE) - zoomed = ZOOM_NONE; - } - } else if (FUNC_BotHoldsZoomWeapon(this)) { - if (zoomed < ZOOM_ONCE && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); - f_allow_keypress = gpGlobals->time + 0.7; - zoomed++; - } - } - - // NOT blinded by flashbang, try to find cover? - if (f_cover_time < gpGlobals->time) { - // COVER: Not taking cover now, fight using fightstyles. - - // when vip, we always take cover. - if (vip) { - // Camp, take cover, etc. - BOT_DecideTakeCover(this); - - if (FUNC_DoRadio(this)) { - UTIL_BotRadioMessage(this, 3, "3", ""); // need backup - } - } else { - // DECIDE: Should we take cover or not. - if (FUNC_ShouldTakeCover(this)) { - FindCover(); - } - } - } else { - - } - - // Keep timer updated for enemy - f_bot_find_enemy_time = gpGlobals->time + REMEMBER_ENEMY_TIME; - } - else // ---- CANNOT SEE ENEMY - { - if (f_bot_find_enemy_time < gpGlobals->time) { - pEnemyEdict = NULL; - lastSeenEnemyVector = Vector(0, 0, 0); - rprint_trace("FightEnemy()", "Lost enemy out of sight, forgetting path and goal"); - forgetPath(); - forgetGoal(); - } else { - - // When we have the enemy for the first time out of sight - // we calculate a path to the last seen position - if (!bFirstOutOfSight) { - rprint_trace("FightEnemy()", "Enemy out of sight, calculating path towards it."); - // Only change path when we update our information here - int iGoal = NodeMachine.getClosestNode(lastSeenEnemyVector, NODE_ZONE, pEdict); - if (iGoal > -1) { - setGoalNode(iGoal); - forgetPath(); - } - - bFirstOutOfSight = true; - } else { - if (!hasGoal()) { - rprint("Enemy out of sight and no goal, forgetting enemy"); - forgetEnemy(); - } - } - } - } // visible -} - -void cBot::pickWeapon(int weaponId) { - UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(weaponId)); - f_c4_time = gpGlobals->time - 1; // reset C4 timer data - // give Counter-Strike time to switch weapon (animation, update state, etc) - f_update_weapon_time = gpGlobals->time + 0.7; -} - -bool cBot::ownsFavoritePrimaryWeapon() { - return hasFavoritePrimaryWeaponPreference() && isOwningWeapon(ipFavoPriWeapon); -} - -bool cBot::ownsFavoriteSecondaryWeapon() { - return hasFavoriteSecondaryWeaponPreference() && isOwningWeapon(ipFavoSecWeapon); -} - -/** - * Returns true if bot has weapon (id) in possession - * @param weaponId - * @return - */ -bool cBot::isOwningWeapon(int weaponId) { - return bot_weapons & (1 << weaponId); -} - -/** - * Returns true if bot carries weapon right now - * @param weaponId - * @return - */ -bool cBot::isHoldingWeapon(int weaponId) { - return (current_weapon.iId == weaponId); -} - -bool cBot::hasFavoritePrimaryWeaponPreference() { - return ipFavoPriWeapon > -1; -} - -bool cBot::hasFavoriteSecondaryWeaponPreference() { - return ipFavoSecWeapon > -1; -} - -bool cBot::canAfford(int price) { - return this->bot_money > price; -} - -/****************************************************************************** - Function purpose: Based upon several events pick the best weapon - ******************************************************************************/ -void cBot::PickBestWeapon() { - // does Timer allow to change weapon? (only when f_update_weapon_time < gpGlobals->time - if (f_update_weapon_time > gpGlobals->time) - return; - - // Distance to enemy - float fDistance = func_distance(pEdict->v.origin, lastSeenEnemyVector); - - float knifeDistance = 300; - - // ---------------------------- - // In this function all we do is decide what weapon to pick - // if we don't pick another weapon the current weapon is okay - // ---------------------------- - - // First we handle situations which are bad, no matter the distance - // or any other circumstance. - - // BAD: Carrying C4 or knife - if (CarryWeapon(CS_WEAPON_C4) || // carrying C4 - (CarryWeapon(CS_WEAPON_KNIFE) && fDistance > knifeDistance)) { // carrying knife and too far - if (hasPrimaryWeaponEquiped()) { - pickWeapon(iPrimaryWeapon); - return; - } else if (hasSecondaryWeaponEquiped()) { - pickWeapon(iSecondaryWeapon); - return; - } - } - - // At this point we do not update weapon information. And we did not 'switch back' to primary / secondary - if (hasEnemy() && !isSeeingEnemy()) { - // decision to pull HE grenade - if (isOwningWeapon(CS_WEAPON_HEGRENADE) && // we have a grenade - func_distance(pEdict->v.origin, lastSeenEnemyVector) < 900 && // we are close - func_distance(pEdict->v.origin, lastSeenEnemyVector) > 200 && // but not to close - RANDOM_LONG(0, 100) < 10 && // only randomly we pick a grenade in the heat of the battle - current_weapon.iId != CS_WEAPON_HEGRENADE && current_weapon.iId != CS_WEAPON_FLASHBANG && - f_gren_time + 15 < gpGlobals->time) // and dont hold it yet - { - UTIL_SelectItem(pEdict, "weapon_hegrenade"); // select grenade - f_wait_time = gpGlobals->time + 1; // wait 1 second (stand still 1 sec) - f_gren_time = - gpGlobals->time + (1.0 + RANDOM_FLOAT(0.5, 1.5)); // and determine how long we should hold it - zoomed = ZOOM_NONE; // Counter-Strike resets zooming when choosing another weapon - return; - } - // OR we pull a flashbang? - if (isOwningWeapon(CS_WEAPON_FLASHBANG) && // we have a grenade - func_distance(pEdict->v.origin, lastSeenEnemyVector) < 200 && // we are close - func_distance(pEdict->v.origin, lastSeenEnemyVector) > 300 && // but not to close - RANDOM_LONG(0, 100) < 15 && // only randomly we pick a grenade in the heat of the battle - current_weapon.iId != CS_WEAPON_FLASHBANG && current_weapon.iId != CS_WEAPON_HEGRENADE && - f_gren_time + 15 < gpGlobals->time) // and dont hold it yet - { - UTIL_SelectItem(pEdict, "weapon_flashbang"); // select grenade - f_wait_time = gpGlobals->time + 1; // wait 1 second (stand still 1 sec) - f_gren_time = - gpGlobals->time + (1.0 + RANDOM_FLOAT(0.5, 1.5)); // and determine how long we should hold it - zoomed = ZOOM_NONE; // Counter-Strike resets zooming when choosing another weapon - return; - } - } - - // When we are here, we did not decide to switch to grenade/flashbang. Now we look - // if the bot has to reload or switch weapon based upon ammo. - - // ---------------------------------------- - // More complex bad things that can happen: - // ---------------------------------------- - int iTotalAmmo = current_weapon.iAmmo1; - int iCurrentAmmo = current_weapon.iClip; - - //char msg[80]; - //sprintf(msg, "BOT: ICLIP %d, TOTALAMMO %d\n", iCurrentAmmo, iTotalAmmo); - - // Clip is out of ammo - if (iCurrentAmmo < 1 - && (CarryWeaponType() == PRIMARY || CarryWeaponType() == SECONDARY)) { - // Camp, take cover, etc. - BOT_DecideTakeCover(this); - - // We still have ammo! - if (iTotalAmmo > 0) { - UTIL_BotPressKey(this, IN_RELOAD); - f_update_weapon_time = gpGlobals->time + 0.7; // update timer - return; - } else { - // Thanks to dstruct2k for easy ctrl-c/v, i optimized the code - // a bit though. Btw, distance 600 is too far for slashing :) - - // at here the bot does not have ammo of the current weapon, so - // switch to another weapon. - if (iPrimaryWeapon > -1 && // we have a primary - current_weapon.iId != iPrimaryWeapon && // that's not the current, empty gun - func_distance(pEdict->v.origin, lastSeenEnemyVector) > 300) // and we are not close enough to knife - { - // select primary weapon - UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iPrimaryWeapon)); // select the primary - return; - } else { - - if (iSecondaryWeapon > -1 && current_weapon.iId != iSecondaryWeapon && - // that's not the current, empty gun - func_distance(pEdict->v.origin, lastSeenEnemyVector) > 300) // and we are not close enough to knife - { - UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iSecondaryWeapon)); // select the secondary - return; - } else { - if (isOwningWeapon(CS_WEAPON_KNIFE) && // we have a knife (for non-knife maps) - !isHoldingWeapon(CS_WEAPON_KNIFE)) // but we do not carry it - { - UTIL_SelectItem(pEdict, "weapon_knife"); - return; - } - } - } // end if - } // no ammo - } -} - -/****************************************************************************** - Function purpose: Fire weapon (burst; or do not fire when not allowed) - ******************************************************************************/ -void cBot::FireWeapon() { - // We may not shoot! - if (f_shoot_time > gpGlobals->time || - f_update_weapon_time > gpGlobals->time) - return; - - if (!isSeeingEnemy()) { - return; - } - - // ------------------------------------------------------------ - float fDistance = 50; - - if (hasEnemy()) { - fDistance = func_distance(pEdict->v.origin, pEnemyEdict->v.origin); - } - - // Depending on weapon type - if (CarryWeaponType() == SECONDARY) { - // We may shoot, use shooting rate. - // TODO TODO TODO; Add shooting rates in BUYTABLE.INI - - if (f_sec_weapon < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK); - f_sec_weapon = gpGlobals->time + RANDOM_FLOAT(0.05, 0.2); - } - - } else if (CarryWeaponType() == PRIMARY) { - // We may shoot, use shooting rate. - // TODO TODO TODO: Add shooting rates in BUYTABLE.INI - if (f_prim_weapon < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK); // Hold fire - // All other weapons, the more distance, the more time we add to holding weapon - if (f_shoot_wait_time < gpGlobals->time) { - // AK, COLT, STEYR AUG, only when enough skill! - if ((CarryWeapon(CS_WEAPON_AK47) - || CarryWeapon(CS_WEAPON_M4A1) - || CarryWeapon(CS_WEAPON_AUG)) && (bot_skill < 3)) { - float f_burst = 0.1; - f_burst = (2048 / fDistance) + 0.1; - if (f_burst < 0.1) - f_burst = 0.1; - if (f_burst > 0.4) - f_burst = 0.4; - - // CS 1.6 less burst - if (counterstrike == 1) - if (f_burst > 0.3) - f_burst = 0.3; - - f_prim_weapon = gpGlobals->time + f_burst; - - f_shoot_wait_time = gpGlobals->time + (f_burst * 3); - } else // other weapons - { - float f_burst = 0.1; - if (fDistance > 300 && bot_skill < 6) { - f_burst = ((fDistance - 300) / 550); - if (f_burst < 0.1) - f_burst = 0.0; - if (f_burst > 0.7) - f_burst = 0.7; - - // CS 1.6 less burst - if (counterstrike == 1) - if (f_burst > 0.2) - f_burst = 0.2; - if (f_prim_weapon < gpGlobals->time) - f_prim_weapon = gpGlobals->time + f_burst; - } - f_shoot_wait_time = - gpGlobals->time + f_burst + RANDOM_FLOAT(0.2, 0.7); - } - } - } // give the bot alteast 0.3 seconds to fire its weapon - } // PRIMARY - else if (CarryWeaponType() == GRENADE) { - if (f_gren_time > gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK); // Hold fire - setMoveSpeed(f_max_speed / 2); - - // Set new goal when holding flashbang! - if (current_weapon.iId == CS_WEAPON_FLASHBANG) { - - //tonode ? - // COVER: Take cover, using tracelines all the time! - FindCover(); - } - } else if (f_gren_time + 0.5 < gpGlobals->time) { - // NOTE: Should not happen, a bot cannot 'forget' this... - f_gren_time = gpGlobals->time + 1; - } - } // GRENADE - else if (CarryWeaponType() == KNIFE) { - setMoveSpeed(f_max_speed); - UTIL_BotPressKey(this, IN_ATTACK); // Hold fire - } // KNIFE - else if (CarryWeaponType() == SNIPER) { - setMoveSpeed(f_max_speed / 2); - UTIL_BotPressKey(this, IN_ATTACK); // Hold fire - f_shoot_time = gpGlobals->time + 1.0; - } // SNIPER - else if (CarryWeaponType() == SHIELD) { - if (fDistance > 550) { - if (hasShieldDrawn()) { - // when the enemy is far away, we keep it - } else { - // draw shield! - UTIL_BotPressKey(this, IN_ATTACK2); // secondary attack makes shield draw - f_allow_keypress = gpGlobals->time + 0.7; - } - } else { - // get weapon here. - if (hasShieldDrawn() && f_allow_keypress < gpGlobals->time) { - rblog - ("BOT: Enemy is close enough, i should withdraw shield to attack this enemy\n"); - UTIL_BotPressKey(this, IN_ATTACK2); - f_allow_keypress = gpGlobals->time + 0.7; - } - } - } else { - // debug print - REALBOT_PRINT(this, "FireWeapon()", "Unknown weapon"); - } -} - -/****************************************************************************** - Function purpose: The combat brain of the bot ( called by Think() ) - ******************************************************************************/ -void cBot::Combat() { - if (!hasEnemy()) { - rprint("Unexpected call to Combat because bot has no enemy!"); - return; - } - - // Bot is on ladder - if (isOnLadder()) { - // TODO: Bot fights when on ladder - return; - } - - // We have an enemy and it is now dead - if (!isEnemyAlive()) { - - // radio (Enemy down) - if (FUNC_DoRadio(this)) { - UTIL_BotRadioMessage(this, 3, "9", ""); - } - - // get bot pointer - cBot *checkpointer = UTIL_GetBotPointer(pEnemyEdict); - - // This bot killed a human; adjust skill when 'autoskill' is on. - if (checkpointer == NULL) { - - // increase bot_skill value when autoskill enabled (making bot weaker) - if (autoskill && bot_skill < 10) { - bot_skill++; - } - - if (Game.iDeathsBroadcasting != BROADCAST_DEATHS_NONE) { - // This is a human, we will tell this human he has been killed - // by a bot. - int r = RANDOM_LONG(150, 255); - int g = RANDOM_LONG(30, 155); - int b = RANDOM_LONG(30, 155); - char msg[128]; - if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_FULL) { - sprintf(msg, "A RealBot has killed you!\n\nName:%s\nSkill:%d\n", name, bot_skill); - } else { - sprintf(msg, "A RealBot named %s has killed you!", name); - } - - HUD_DrawString(r, g, b, msg, pEnemyEdict); - } - } - - // clear the pointer for this and other bots that might have the same pEnemyEdict - FUNC_ClearEnemyPointer(pEnemyEdict); - - // from here react after kill... - forgetGoal(); - forgetPath(); - - if (lastSeenEnemyVector != Vector(0, 0, 0)) { - vHead = lastSeenEnemyVector; - } - - lastSeenEnemyVector = Vector(0, 0, 0); - - // random waiting - f_wait_time = gpGlobals->time + (1 + RANDOM_FLOAT(0.0, 0.4)); - - // keep on walking when afraid (perhaps there are more enemies) - if (RANDOM_LONG(0, 100) < ipFearRate) - f_walk_time = gpGlobals->time + (1 + RANDOM_FLOAT(0.0, 2.0)); - - InteractWithPlayers(); // check any new enemy here immediately - - return; - } - - // ----------- combat - - // STEP 1: Pick best weapon to fight with - PickBestWeapon(); - - // STEP 2: Decide how to move to make us a harder target - FightEnemy(); - - // STEP 3: Aim at enemy (skill-based) - AimAtEnemy(); - - // STEP 4: Fire! - FireWeapon(); -} - -/****************************************************************************** - Function purpose: Find cover - Note: Using tracelines to get a cover node. - ******************************************************************************/ -void cBot::FindCover() { - TraceResult tr; - Vector dest = lastSeenEnemyVector; - // Vector start = pEdict->v.origin; - // Vector end; - Vector cover_vect = Vector(9999, 9999, 9999); - - // When vector is visible, then look from that vector to the threat, if then NOT - // Visible, then its cover. - Vector v_src, v_right, v_left; - - // TraceLines in 2 directions to find which way to go... - UTIL_MakeVectors(pEdict->v.v_angle); - v_src = pEdict->v.origin + pEdict->v.view_ofs; - v_right = v_src + gpGlobals->v_right * 90; - v_left = v_src + gpGlobals->v_right * -90; - - // We have now our first 'left' and 'right' - - // First check the right.. - UTIL_TraceLine(v_src, v_right, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); - - if (tr.flFraction >= 1.0) { - // We can see it - // Now trace from that vector to our threat - UTIL_TraceLine(v_right, dest, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); - - // If this is blocking.. then its a good wpt - if (tr.flFraction < 1.0) - cover_vect = v_right; - } - - // Now check at the left - UTIL_TraceLine(v_src, v_left, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); - - if (tr.flFraction >= 1.0) { - // We can see it - // Now trace from that vector to our threat - UTIL_TraceLine(v_left, dest, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); - - // If this is blocking.. then its a good wpt - if (tr.flFraction < 1.0) { - // If we already found a wpt, then randomly pick this one - if (cover_vect != Vector(9999, 9999, 9999)) { - if (RANDOM_LONG(0, 100) < 50) - cover_vect = v_left; - } else - cover_vect = v_left; - } - } - // Now update the V_left and V_right and do the checks again. - // Vector old_right = v_right; - // Vector old_left = v_left; - v_right = v_src + gpGlobals->v_right * 180; - v_left = v_src + gpGlobals->v_right * -180; - - // Now check at the right again - UTIL_TraceLine(v_src, v_right, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - if (tr.flFraction >= 1.0) { - // We can see it - // Now trace from that vector to our threat - UTIL_TraceLine(v_right, dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // If this is blocking.. then its a good wpt - if (tr.flFraction < 1.0) { - // If we already found a wpt, then randomly pick this one - if (cover_vect != Vector(9999, 9999, 9999)) { - if (RANDOM_LONG(0, 100) < 50) - cover_vect = v_right; - } else - cover_vect = v_right; - } - } - - // Now check at the left - UTIL_TraceLine(v_src, v_left, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - if (tr.flFraction >= 1.0) { - // We can see it - // Now trace from that vector to our threat - UTIL_TraceLine(v_left, dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // If this is blocking.. then its a good wpt - if (tr.flFraction < 1.0) { - // If we already found a wpt, then randomly pick this one - if (cover_vect != Vector(9999, 9999, 9999)) { - if (RANDOM_LONG(0, 100) < 50) - cover_vect = v_left; - } else - cover_vect = v_left; - } - } - - int iNodeEnemy = NodeMachine.getClosestNode(pEnemyEdict->v.origin, 60, pEnemyEdict); - int iNodeFrom = NodeMachine.getClosestNode(pEdict->v.origin, NODE_ZONE, pEdict); - - // -------------- - // TEST TEST TEST - // -------------- - int iCoverNode = NodeMachine.node_cover(iNodeFrom, iNodeEnemy, pEdict); - bool bTakenCover = false; - - if (iCoverNode > -1) { - rprint("FindCover()", "cover node found (node based)"); - setGoalNode(iCoverNode); - forgetPath(); - - // Calculate a path to this position and get the heck there. - createPath(iCoverNode); - f_cover_time = gpGlobals->time + 8; - bTakenCover = true; - } else { - - // -------------------------------------------------- - // If cover_vect is found, we find a node close to it - // -------------------------------------------------- - if (cover_vect != Vector(9999, 9999, 9999)) { - rprint("FindCover()", "cover node found (cover_vect based)"); - int iNodeCover = NodeMachine.getClosestNode(cover_vect, 60, pEdict); - if (iNodeCover > -1) { - setGoalNode(iNodeCover); - forgetPath(); - - // Calculate a path to this position and get the heck there. - rprint("createPath -> find cover node"); - NodeMachine.createPath(iNodeFrom, iNodeCover, iBotIndex, this, PATH_NONE); - f_cover_time = gpGlobals->time + 8; - bTakenCover = true; - } - } - } - - // when we have taken cover, and we are leader, command our team to get - // into our position to cover area - if (bTakenCover) { - // do something... - } - -} // FindCover() - -void cBot::InteractWithFriends() { - - - // TODO TODO TODO; make this thing really work - return; - - // We interact with our players in some way - // - // When a bot is camping, another bot can choose to say 'go go go' for example. - // - // - - for (int i = 1; i <= gpGlobals->maxClients; i++) { - - edict_t *pPlayer = INDEXENT(i); - - // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { - // skip this player if not alive (i.e. dead or dying) - if (!IsAlive(pPlayer)) - continue; - - // skip enemies - if (UTIL_GetTeam(pPlayer) != UTIL_GetTeam(pEdict)) - continue; - - bool bCanSeePlayer = false; - bool bClose = false; - - Vector vVecEnd = pPlayer->v.origin + pPlayer->v.view_ofs; - - if (func_distance(pPlayer->v.origin, pEdict->v.origin) < 450) - bClose = true; - - if (FInViewCone(&vVecEnd, pEdict) && FVisible(vVecEnd, pEdict)) - bCanSeePlayer = true; - - // there are tons of cases - cBot *pBotPointer = UTIL_GetBotPointer(pPlayer); - - // It is a fellow bot - if (pBotPointer != NULL) { - if (bClose) { - if (pBotPointer->f_camp_time > gpGlobals->time - && pBotPointer->f_camp_time - 10 < gpGlobals->time - && pBotPointer->pEnemyEdict == NULL - && (RANDOM_LONG(0, 100) < ipCampRate - && FUNC_DoRadio(this))) { - // issue go go go - UTIL_BotRadioMessage(this, 2, "1", ""); // go go go! - } - } - - if (bCanSeePlayer) {} - } else // it is a teammate, but it is human (or a different bot) - { - // when firing - - } - - // any player: - if (bClose) { - // some one is close, need backup? - if (RANDOM_LONG(0, 100) < ipFearRate && pEnemyEdict != NULL) - if (FUNC_DoRadio(this)) { - UTIL_BotRadioMessage(this, 3, "3", ""); // need backup - } - } - } - } - -} - -// BOT: Interact with Players ('find enemy, and how to react upon them') -void cBot::InteractWithPlayers() { - - // friends are important, we are a team dudes! - InteractWithFriends(); - - int result = FindEnemy(); - - // ------------------------------- - // RESULT < 0; NO ENEMY FOUND - // ------------------------------- - - // No enemy found, unzoom - if (result < 0) { - // Keep f_prim_weapon updated, else we do burst immidiatly - if (CarryWeaponType() == SNIPER) { - - // Unzoom (for sniper guns) - if (zoomed > ZOOM_NONE && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); - f_allow_keypress = gpGlobals->time + 0.7; - zoomed++; - } - if (zoomed > ZOOM_TWICE) - zoomed = ZOOM_NONE; - } else if (FUNC_BotHoldsZoomWeapon(this)) { - - // Unzoom (for other guns with only 1 zoom) - if (zoomed > ZOOM_NONE && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); - f_allow_keypress = gpGlobals->time + 0.7; - zoomed = ZOOM_NONE; - } - } else { - - // For any weapon that has a silencer (the colt for example), use it if we want that. - if (isHoldingWeapon(CS_WEAPON_M4A1)) - if (bot_use_special == 0 && zoomed == ZOOM_NONE - && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); - zoomed = ZOOM_ONCE; - } - } - } - // ------------------------------------------------ - // RESULT > -1 ; ENEMY FOUND / NO SPECIFIC REACTION - // ------------------------------------------------ - if (result > -1 && result < 4) { - - // VIP: When we found an enemy, we have a problem. - if (vip) { - - // We do not forget our enemy, but we will try to get the heck out of here. - // TODO TODO TODO: code something here? - } - // Whenever we hold a knife, get our primary weapon - if (CarryWeapon(CS_WEAPON_KNIFE)) { - - // switch back to primary - if (iPrimaryWeapon > -1) - UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iPrimaryWeapon)); - - else // pick secondary - UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iSecondaryWeapon)); - - f_update_weapon_time = gpGlobals->time + 0.7; - } - } - // ------------------------------------------------ - // RESULT = 1 ; ENEMY FOUND, VIA FRIEND! - // ------------------------------------------------ - - // When we have found an enemy via a friend, we simply build a path to it. - if (result == 1) { - - /* - f_prim_weapon = gpGlobals->time; - - // DECIDE: - // Do we go into battle, or do we wait first a few seconds? - - // HEALTH: The less we have, the more we want to wait - int vHealth = 100-bot_health; - - // CAMP: The more we want to camp, the more we want to wait. - int vCamp = ipCampRate; - - if (RANDOM_LONG(0,200) < (vHealth+vCamp)) - { - // depending on how much we want, the longer we wait - float fWaitTime = ((200/(vHealth+vCamp))*5); - f_wait_time = gpGlobals->time + fWaitTime; - - // TODO TODO TODO; we might not even want to wait, but also take 'cover'? - } - - // INITIALIZATION: - int iGoal = NodeMachine.getCloseNode(pBotEnemy->v.origin, NODE_ZONE, pBotEnemy); - if (iGoal > -1) - { - iGoalNode = iGoal; - pathNodeIndex = -1; - } - */ - } - // ------------------------------------------------ - // RESULT = 0 ; NEW ENEMY FOUND - // ------------------------------------------------ - if (result == 0) { - // First Encounter - //f_prim_weapon = gpGlobals->time; - if (CarryWeaponType() == SNIPER) { - if (zoomed < ZOOM_TWICE && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); - f_allow_keypress = gpGlobals->time + 0.7; - zoomed++; - } - } - - // INITIALIZATION: - int iGoal = NodeMachine.getClosestNode(pEnemyEdict->v.origin, NODE_ZONE, pEnemyEdict); - if (iGoal > -1) { - rprint_trace("InteractWithPlayers()", "Found a new enemy, setting goal and forgetting path"); - setGoalNode(iGoal); - forgetPath(); - } - - // Speed our enemy runs - // int run_speed = FUNC_PlayerSpeed(pBot->pBotEnemy); - // Distance between Us and Enemy. - // float f_distance = func_distance(pBot->pEdict->v.origin, - // pBot->pBotEnemy->v.origin); - - // Does our enemy (when a bot) has focus on us? - bool focused = false; - cBot *playerbot = UTIL_GetBotPointer(pEnemyEdict); - if (playerbot) { - if (playerbot->pEnemyEdict == pEdict) - focused = true; - } else // Its a human - { - - // When we are in his 'sight' of 25 degrees , we are pretty - // much focussed for a first encounter. - if (FUNC_InFieldOfView - (pEdict, (pEnemyEdict->v.origin - pEdict->v.origin)) < 25) - focused = true; - } - - /****************************** - At this moment we know: - - The distance between us and enemy - - The focus (are we targetted too?) - - The speed of the enemy (running, standing still? etc) - *******************************/ - } // We have a first encounter - - // ------------------------------------------------ - // RESULT = 3 ; NEWER ENEMY FOUND - // ------------------------------------------------ - if (result == 3) { - // - // Newer enemy found, update goals and such, but thats all! - // - - // INITIALIZATION: - int iGoal = NodeMachine.getClosestNode(pEnemyEdict->v.origin, NODE_ZONE, pEnemyEdict); - - if (iGoal > -1) { - rprint_trace("InteractWithPlayers()", "Found a *newer* enemy, so picking goal node to that"); - setGoalNode(iGoal); - forgetPath(); - } - } -} - -// BOT: INTERACT WITH PLAYERS -void cBot::JoinTeam() { - if (mod_id != CSTRIKE_DLL) return; - // When bot plays Counter-Strike (only Counter-Strike is supported) - - char c_team[32]; - char c_class[32]; - - // Choose team first - if (start_action == MSG_CS_TEAM_SELECT) { - start_action = MSG_CS_IDLE; // switch back to idle - - // in case of bad state/input fall-back to 'pick one for me' - if ((iTeam != 1) && (iTeam != 2) && (iTeam != 5)) { - iTeam = 5; - } - - // select the team the bot wishes to join... - if (iTeam == 1) { - strcpy(c_team, "1"); - } else if (iTeam == 2) { - strcpy(c_team, "2"); - } else { - strcpy(c_team, "5"); - } - - // choose - FakeClientCommand(this->pEdict, "menuselect", c_team, NULL); - - return; - } - - // counter terrorist menu, which class/outfit? - if (start_action == MSG_CS_CT_SELECT) { - start_action = MSG_CS_IDLE; // switch back to idle - - if ((bot_class < 1) || (bot_class > 4)) - bot_class = 5; // use random if invalid - - // Since cs 1.6 does not give us pretty random models - // we do it ourselves - if (bot_class == 5) { - bot_class = RANDOM_LONG(1, 4); - } - - // select the class the bot wishes to use... - if (bot_class == 1) - strcpy(c_class, "1"); - else if (bot_class == 2) - strcpy(c_class, "2"); - else if (bot_class == 3) - strcpy(c_class, "3"); - else if (bot_class == 4) - strcpy(c_class, "4"); - else - strcpy(c_class, "5"); // random - - FakeClientCommand(this->pEdict, "menuselect", c_class, NULL); - - // bot has now joined a team - hasJoinedTeam = true; - - return; - } - - // terrorist select - if (start_action == MSG_CS_T_SELECT) { - start_action = MSG_CS_IDLE; // switch back to idle - - if ((bot_class < 1) || (bot_class > 4)) - bot_class = 5; // use random if invalid - - // Since cs 1.6 does not give us pretty random models - // we do it ourselves - if (bot_class == 5) - bot_class = RANDOM_LONG(1, 4); - - // select the class the bot wishes to use... - if (bot_class == 1) - strcpy(c_class, "1"); - else if (bot_class == 2) - strcpy(c_class, "2"); - else if (bot_class == 3) - strcpy(c_class, "3"); - else if (bot_class == 4) - strcpy(c_class, "4"); - else - strcpy(c_class, "5"); // random - - FakeClientCommand(this->pEdict, "menuselect", c_class, NULL); - - // bot has now joined the game (doesn't need to be started) - hasJoinedTeam = true; - - return; - } -} - -int cBot::ReturnTurnedAngle(float speed, float current, float ideal) { - - // hope this fix the unnescesary turning of bots. - // how? we save the values here, andc alculate the new value. - // this part is copied from botchangeyaw/pitch so it SHOULD work :) - float current_180; // current +/- 180 degrees - float diff; - - // turn from the current v_angle pitch to the idealpitch by selecting - // the quickest way to turn to face that direction - - // find the difference in the current and ideal angle - diff = fabs(current - ideal); - - // check if the bot is already facing the idealpitch direction... - if (diff <= 1.0) - return (int) current; // return number of degrees turned - - // check if difference is less than the max degrees per turn - if (diff < speed) - speed = diff; // just need to turn a little bit (less than max) - - // here we have four cases, both angle positive, one positive and - // the other negative, one negative and the other positive, or - // both negative. handle each case separately... - if ((current >= 0.0) && (ideal >= 0.0)) // both positive - { - if (current > ideal) - current -= speed; - - else - current += speed; - } else if ((current >= 0.0) && (ideal < 0.0)) { - current_180 = current - 180.0; - if (current_180 > ideal) - current += speed; - - else - current -= speed; - } else if ((current < 0) && (ideal >= 0)) { - current_180 = current + 180; - if (current_180 > ideal) - current += speed; - - else - current -= speed; - } else // (current < 0) && (ideal < 0) both negative - { - if (current > ideal) - current -= speed; - - else - current += speed; - } - - // check for wrap around of angle... - if (current > 180) - current -= 360; - if (current < -180) - current += 360; - return (int) current; // return what it should be -} - -// BOT: sub-function (DEFUSE) for ACT() -bool cBot::Defuse() { - if (!isCounterTerrorist()) // non-Counter-Terrorists have no business here - return false; - - // this bot is defusing - if (shouldActWithC4() && keyPressed(IN_USE)) { - setTimeToMoveToNode(3); - return true; - } - - // What i do, i search for the c4 timer, store its origin and check - // if this bot is close. If so, the bot should be defusing the bomb - // if the timers are set. The above check makes sure that no other - // bot will be defusing the bomb. - edict_t *pent = NULL; - bool c4Found = false; - while ((pent = UTIL_FindEntityByClassname(pent, "grenade")) != NULL) { - if (UTIL_GetGrenadeType(pent) == 4) { // It is a C4 - c4Found = true; - break; - } - } - - if (!c4Found) { - rprint_normal("Defuse()", "No C4 planted yet"); - return false; - } - - rprint_normal("Defuse()", "C4 is planted!"); - - // A c4 has been found, oh dear. - // Remember, pent=c4 now! - - // Calculate the distance between our position to the c4 - Vector vC4 = pent->v.origin; - float distance = func_distance(pEdict->v.origin, vC4); - - // can see C4 - bool canSeeC4 = canSeeVector(vC4); - - if (!canSeeC4) { - rprint_trace("Defuse()", "Cannot see planted C4 - bailing"); - return false; - } - - // it can be seen, so it has been discovered - if (!Game.isPlantedC4Discovered()) { - this->rprint_trace("Defuse()", "C4 is discovered, remembering its coordinates"); - Game.vPlantedC4 = vC4; - } - - // We can do 2 things now - // - If we are not close, we check if we can walk to it, and if so we face to the c4 - // - If we are close, we face it and (begin) defuse the bomb. - int distanceForC4ToBeInReach = 70; - if (distance < distanceForC4ToBeInReach) { - vHead = vC4; - vBody = vC4; - - setTimeToMoveToNode(3); // we are going to do non-path-follow stuff, so keep timer updated - int angle_to_c4 = FUNC_InFieldOfView(pEdict, (vC4 - pEdict->v.origin)); - - // if defusion timer has not been set (ie, the bot is not yet going to defuse the bomb) - if (f_defuse < gpGlobals->time && angle_to_c4 < 35) { - this->rprint("Defuse()", "I'll start defusing the bomb"); - // when we are 'about to' defuse, we simply set the timers - f_defuse = gpGlobals->time + 90; // hold as long as you can - f_allow_keypress = gpGlobals->time + 1.5; // And stop any key pressing the first second - // ABOUT TO DEFUSE BOMB - } - - // Defusion timer is set and c4 is within vision - if (f_defuse > gpGlobals->time && angle_to_c4 < 35) { - this->rprint("Defuse()", "I'm defusing the bomb"); - setMoveSpeed(0.0); - f_c4_time = gpGlobals->time + 6; - UTIL_BotPressKey(this, IN_DUCK); - - if (func_distance(pEdict->v.origin, vC4) > 50 - && f_allow_keypress + 0.5 > gpGlobals->time) { - setMoveSpeed(f_max_speed / 2); - } - } - - if (f_allow_keypress < gpGlobals->time && f_defuse > gpGlobals->time) { - UTIL_BotPressKey(this, IN_USE); - } - - } else { - rprint_trace("Defuse()", "I can see C4, but it is out of reach."); - int iGoalNode = NodeMachine.getClosestNode(vC4, distanceForC4ToBeInReach, NULL); - if (iGoalNode < 0) { - rprint_normal("Defuse()", "No node close, so just look at it/body face at it and move towards it."); - vHead = vC4; - vBody = vC4; - } - - if (iGoalNode > -1) { - // we are not heading for this goal yet - if (iGoalNode > -1 && getGoalNode() != iGoalNode) { - rprint_normal("Defuse()", "I don't have a goal towards the C4, overriding it now to C4 destination!"); - forgetPath(); - forgetGoal(); - setGoalNode(iGoalNode); - } else { - rprint_normal("Defuse()", "I already have a goal towards the C4!"); - } - } else { - rprint_normal("Defuse()", "C4 is somewhere without a close node."); - } - setMoveSpeed(f_max_speed); - } // distance < ... - - // we can see the bomb, and we act upon it - return true; -} - -int cBot::keyPressed(int key) const { - return pEdict->v.button & key; -} - -// BOT: Act -void cBot::Act() { - // chat - if (fChatTime < gpGlobals->time) { - if (chChatSentence[0] != '\0') { - UTIL_SayTextBot(chChatSentence, this); - memset(chChatSentence, 0, sizeof(chChatSentence)); - } - } - - // camp - if (f_camp_time > gpGlobals->time) { - // When camping we duck and we don't move - // todo, camping can be done standing too, but this does not look 'cool' atm. - UTIL_BotPressKey(this, IN_DUCK); - - setMoveSpeed(0.0); // do not move - PickBestWeapon(); // pick weapon, do not stare with knife - - // when dropped C4 and CT we look at C4 - if (isCounterTerrorist() && Game.vDroppedC4 != Vector(9999, 9999, 9999)) { - // look at dropped C4 - if (EntityIsVisible(pEdict, Game.vDroppedC4)) - vHead = Game.vDroppedC4; - else { - if (iGoalNode > -1) - vHead = vBody = NodeMachine.node_vector(iGoalNode); - else { - // cannot find a node to the dropped C4, so where do we look at? - // todo : find a node to look at, i.e. something dangerous - } - } - } else { - // Look at iGoalNode - if (iGoalNode > -1) - vHead = vBody = NodeMachine.node_vector(iGoalNode); - else { - // look where to look at? - // todo : find a node to look at, i.e. something dangerous - } - } - } - - // C4 timer is set, this means: - // T -> Is planting bomb - // CT-> Is defusing bomb - if (shouldActWithC4()) { - // make sure we override this, or else we learn that we get stuck or something - // which is not the case. - setTimeToMoveToNode(2); - - // terrorist - if (isTerrorist()) { - // When still having the C4 - setMoveSpeed(0.0f); -// f_strafe_speed = 0.0; - - // When no C4 selected yet, select it - if (!isHoldingWeapon(CS_WEAPON_C4)) { - UTIL_SelectItem(pEdict, "weapon_c4"); - } else { - UTIL_BotPressKey(this, IN_ATTACK); // plant it! - } - - // When we no longer have the C4 , we stop doing this stupid shit - if (!hasBomb() || Game.bBombPlanted) { - rprint_trace("Act()", "I was planting the C4, and it got planted (I no longer have the C4), so find a nearby node to camp/guard the C4"); - f_c4_time = gpGlobals->time; - setGoalNode(NodeMachine.getClosestNode(pEdict->v.origin, 200, pEdict)); - iPathFlags = PATH_CAMP; - forgetPath(); - } - } else { - // counter-terrorist - Defuse(); // old routine from RB AI V1.0 defusing, should get here and more cleaned up - } - } - - if (f_strafe_time < gpGlobals->time) { - f_strafe_speed = 0; - } - - // walk only when NOT holding duck (is same as walking, combination makes bot super slow) - if (f_walk_time > gpGlobals->time && !(pEdict->v.button & IN_DUCK)) { - // From "KickBot": return (float) (((int)flMaxSpeed)/2 + ((int)flMaxSpeed)/50); - //OLD: f_move_speed = f_max_speed / 2.0; // this is not correct - - pEdict->v.button &= (~IN_RUN); // release IN_RUN - rprint("Act", "Walk time > gpGlobals->time"); - setMoveSpeed((float) (((int) f_max_speed) / 2 + ((int) f_max_speed) / 50)); - } - - // When we are at max speed, press IN_RUN to get a running animation - if (f_move_speed == f_max_speed) { - UTIL_BotPressKey(this, IN_RUN); - } - - if (!keyPressed(IN_MOVELEFT) || keyPressed(IN_MOVERIGHT)) { - if (f_strafe_speed > 0.0f) { - UTIL_BotPressKey(this, IN_MOVERIGHT); - } - else if (f_strafe_speed < 0.0f) { - UTIL_BotPressKey(this, IN_MOVELEFT); - } - } - - // When we should go back, we go back - if (f_goback_time > gpGlobals->time) { - setMoveSpeed(-f_max_speed); - } - - // When holding duck, we hold duck - if (f_hold_duck > gpGlobals->time) - UTIL_BotPressKey(this, IN_DUCK); - - // When we wait, we have no move speed - // notice: 'wait' is not 'stuck' nor 'camping'. Wait should only be used to have a bot - // 'do nothing' for a short period of time. - if (f_wait_time > gpGlobals->time) { - rprint("Act", "f_wait_time > gpGlobals->time"); - setMoveSpeed(0.0); - } - - // Button usage, change vBody to a 'trigger multiple' because we have to touch these - if (pButtonEdict) { - if (strcmp(STRING(pButtonEdict->v.classname), "trigger_multiple") == 0) { - if (func_distance(pEdict->v.origin, VecBModelOrigin(pButtonEdict)) < 60) { - vBody = VecBModelOrigin(pButtonEdict); - } - } - } - - // ------------------------------------------- - // MOVE TO : vBody - // calculate the angle we MOVE to. (VecMoveAngles) - // ------------------------------------------- - Vector vTarget = vBody - pEdict->v.origin; - vecMoveAngles = UTIL_VecToAngles(vTarget); - - // Paulo-La-Frite - START bot aiming bug fix - if (vecMoveAngles.x > 180) - vecMoveAngles.x -= 360; - - vecMoveAngles.x = -vecMoveAngles.x; - vecMoveAngles.z = 0; - UTIL_FixAngles(&vecMoveAngles); - - // when filled in, we look to this (overrides) - if (vEar != Vector(9999, 9999, 9999)) - vHead = vEar; - - // button overrides hearing - if (pButtonEdict) - vHead = VecBModelOrigin(pButtonEdict); - - // ------------------------------------------- - // FACE AT: vHead - // calculate the angle we face at. - // - // ------------------------------------------- - vTarget = (vHead - pEdict->v.origin); - pEdict->v.v_angle = UTIL_VecToAngles(vTarget); - if (pEdict->v.v_angle.y > 180) - pEdict->v.v_angle.y -= 360; - - // Paulo-La-Frite - START bot aiming bug fix - if (pEdict->v.v_angle.x > 180) - pEdict->v.v_angle.x -= 360; - - Vector v_shouldbe = pEdict->v.angles; - - // Vector how it should be, however, we don't allow such a fast turn! - v_shouldbe.x = pEdict->v.v_angle.x / 3; - v_shouldbe.y = pEdict->v.v_angle.y; - v_shouldbe.z = 0; - - // set the body angles to point the gun correctly - pEdict->v.angles.x = ReturnTurnedAngle(ipTurnSpeed, pEdict->v.angles.x, v_shouldbe.x); - pEdict->v.angles.y = ReturnTurnedAngle(ipTurnSpeed, pEdict->v.angles.y, v_shouldbe.y); - pEdict->v.angles.z = 0; - - // adjust the view angle pitch to aim correctly (MUST be after body v.angles stuff) - pEdict->v.v_angle.x = -pEdict->v.v_angle.x; - - // Paulo-La-Frite - END - pEdict->v.ideal_yaw = pEdict->v.v_angle.y; - pEdict->v.idealpitch = pEdict->v.v_angle.x; - - botFixIdealYaw(pEdict); - botFixIdealPitch(pEdict); -} - -bool cBot::shouldActWithC4() const { - return f_c4_time > gpGlobals->time; -} - -// BOT: On ladder? -bool cBot::isOnLadder() { - return FUNC_IsOnLadder(pEdict); -} - -// BOT: Check around body and avoid obstacles -void cBot::CheckAround() { - rprint_trace("CheckAround", "Start"); - // Do not act when on ladder - if (isOnLadder()) - return; - - // The principle is to fire 2 tracelines, both forward; one left - // and one right. When one of the 2 gets hit, we know we are 'about' - // to get hit. Therefor we use strafing to keep distance to the coming wall - // when left and right is both hit we have a problem as this should not happen. - - // Note: we use TRACEHULL instead of TRACELINE, because TRACEHULL detects - // the famous 'undetectable' func_walls. - TraceResult tr; - Vector v_source, v_left, v_right, v_forward, v_forwardleft, v_forwardright; - -// v_source = pEdict->v.origin + Vector(0, 0, -CROUCHED_HEIGHT + (MAX_JUMPHEIGHT + 1)); - v_source = pEdict->v.origin + Vector(0, 0, ORIGIN_HEIGHT); - - // Go forward first - float distance = 90; - v_forward = v_source + gpGlobals->v_forward * distance; - - // now really go left/right - v_right = v_source + gpGlobals->v_right * distance; - v_left = v_source + gpGlobals->v_right * -distance; - - // now really go left/right - v_forwardright = v_right + gpGlobals->v_forward * distance; - v_forwardleft = v_left + gpGlobals->v_forward * -distance; - - // TRACELINE: forward - UTIL_TraceHull(v_source, v_forward, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - bool bHitForward = tr.flFraction < 1.0; - - // TRACELINE: Left - UTIL_TraceHull(v_source, v_left, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - bool bHitLeft = tr.flFraction < 1.0; - - // TRACELINE: Right - UTIL_TraceHull(v_source, v_right, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - bool bHitRight = tr.flFraction < 1.0; - - // TRACELINE: Forward left - UTIL_TraceHull(v_source, v_forwardleft, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - bool bHitForwardLeft = tr.flFraction < 1.0; - - // TRACELINE: Forward right - UTIL_TraceHull(v_source, v_forwardright, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - bool bHitForwardRight = tr.flFraction < 1.0; - - - char msg[255]; - sprintf(msg, "HIT results: forward: %d, left: %d, right: %d, forward left: %d, forward right: %d", bHitForward, bHitLeft, bHitRight, bHitForwardLeft, bHitForwardRight); - rprint_trace("CheckAround", msg); - - // Set 'act' properties - - // we are surrounded, so move backwards - if (bHitForward) { - rprint_trace("CheckAround", "Something in front of me blocks, so move back."); -// f_move_speed = -(f_max_speed); - } else { - rprint_trace("CheckAround", "Nothing in front of me"); - } - - if (!bHitForwardLeft && bHitForwardRight) { - strafeLeft(0.5); - rprint_trace("CheckAround", "Can strafe left (forward left)"); - } else if (bHitForwardLeft && !bHitForwardRight) { - strafeRight(0.5); - rprint_trace("CheckAround", "Can strafe right (forward right)"); - } - - if (bHitLeft && bHitRight) { - rprint_trace("CheckAround", "Can't strafe left or right"); - } else if (!bHitLeft && bHitRight) { - strafeLeft(0.5); - rprint_trace("CheckAround", "Can strafe left"); - } else if (bHitLeft && !bHitRight) { - strafeRight(0.5); - rprint_trace("CheckAround", "Can strafe right"); - } - - // ------------------------------------------------------------- - // When checking around a bot also handles breakable stuff. - // ------------------------------------------------------------- - char item_name[40]; - edict_t *pent = NULL; - while ((pent = UTIL_FindEntityInSphere(pent, pEdict->v.origin, 60)) != NULL) { - strcpy(item_name, STRING(pent->v.classname)); - - // See if it matches our object name - if (strcmp("func_breakable", item_name) == 0) { - - // Found a func_breakable - Vector vBreakableOrigin = VecBModelOrigin(pent); - - // Shoot - if ((pent->v.flags & FL_WORLDBRUSH) == 0) // can it be broken? - { - - // Thx for CF by fixing breakable coding - if (pent->v.solid == SOLID_BSP && pent->v.takedamage == DAMAGE_YES && pent->v.impulse == 0 && - pent->v.health < 150) // has it NOT been broken yet? - { - - // trace to vector to be sure we dont get blocked by anything else - if (VectorIsVisibleWithEdict(pEdict, vBreakableOrigin, "func_breakable")) { - setHeadAiming(vBreakableOrigin); - FireWeapon(); - } - return; - } - } - } // CAN BE BROKEN - } // FUNC_BREAKABLE -} - -// BOT: Should be taking cover? -bool cBot::TakeCover() { - - // Its time based. - if (f_cover_time < gpGlobals->time) - return false; - - // And if all went fine, we can return true. - return true; -} - -/** - * Set the node to follow next as the next one (ie, increase index) - */ -void cBot::nextPathIndex() { - this->pathIndex++; -} - -/** - * Set the node to follow next as the previous one (ie, decrease index). Calls forgetPath when index is getting < 0 - */ -void cBot::prevPathIndex() { - rprint("prevPathNodeIndex"); - this->pathIndex--; - if (this->pathIndex < 0) { - forgetPath(); - } -} - -// Returns true if bot has a path to follow -bool cBot::isWalkingPath() { - return this->pathIndex > -1; -} - -// Returns true if bot has goal node -bool cBot::hasGoal() { - return this->iGoalNode > -1; -} - -// Returns true if bot has goal node index (ie referring to Goals[]) -bool cBot::hasGoalIndex() { - return this->goalIndex > -1; -} - -/** - * Returns goal data , if goal data exists - * @return - */ -tGoal *cBot::getGoalData() { - if (!hasGoalIndex()) return NULL; - tGoal *ptr = NodeMachine.getGoal(this->goalIndex); - if (ptr == NULL) return NULL; - - // only goals with a node are valid - if (ptr->iNode > -1) return ptr; - // else not - - return NULL; -} - -// Returns true if bot has an enemy edict -bool cBot::hasEnemy() { - return this->pEnemyEdict != NULL; -} - -/** - * Returns true when given edict == our enemy edict - * @param pEdict - * @return - */ -bool cBot::hasEnemy(edict_t * pEdict) { - return this->pEnemyEdict == pEdict; -} - -// Returns true if bot has a path to follow -bool cBot::shouldBeWandering() { - if (this->fWanderTime > gpGlobals->time) { - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Wander time is %f , globals time is %f, so should still wander", this->fWanderTime, gpGlobals->time); - rprint(msg); - return true; - } - return false; -} - -void cBot::setMoveSpeed(float value) { -// char msg[255]; -// sprintf(msg, "setting to value %f / maxSpeed %f - sv_maxspeed = %f", value, this->f_max_speed, CVAR_GET_FLOAT("sv_maxspeed")); -// rprint_trace("setMoveSpeed", msg); - this->f_move_speed = value; -} - -void cBot::setStrafeSpeed(float value, float time) { - char msg[255]; - sprintf(msg, "%f for %f seconds.", value, time); - rprint_trace("setStrafeSpeed", msg); -// if (f_strafe_time > gpGlobals->time) { -// -// } else { - f_strafe_speed = value; - f_strafe_time = gpGlobals->time + time; -// } -} - -void cBot::strafeLeft(float time) { - setStrafeSpeed(-f_max_speed, time); -} - -void cBot::strafeRight(float time) { - setStrafeSpeed(f_max_speed, time); -} - -void cBot::startWandering(float time) { - this->fWanderTime = gpGlobals->time + time; - setMoveSpeed(f_max_speed); - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Start wandering for %f seconds", time); - rprint("startWandering", msg); -} - -void cBot::stopMoving() { - this->setMoveSpeed(0.0); -} - -void cBot::forgetGoal() { - rprint_trace("forgetGoal"); - this->iGoalNode = -1; - this->goalIndex = -1; -} - -int cBot::getPathIndex() { - return this->pathIndex; -} - -int cBot::getPreviousPathIndex() { - return this->pathIndex - 1; -} - -void cBot::forgetPath() { - rprint("forgetPath"); - this->pathIndex = -1; - NodeMachine.path_clear(this->iBotIndex); -} - -void cBot::forgetEnemy() { - this->pEnemyEdict = NULL; -} - -edict_t * cBot::getEnemyEdict() { - return this->pEnemyEdict; -} - -int cBot::getGoalNode() { - return this->iGoalNode; -} - -void cBot::setGoalNode(int nodeIndex, int iGoalIndex) { - if (nodeIndex < 0) { - rprint("setGoalNode()", "WARN: Setting a goal lower than 0, assuming this is not intentional. If you need to forget a goal, use forgetGoal()"); - } - this->iGoalNode = nodeIndex; - this->goalIndex = iGoalIndex; - - tGoal *goalPtr = getGoalData(); - char msg[255]; - memset(msg, 0, sizeof(msg)); - - if (goalPtr != NULL) { - sprintf(msg, "Setting iGoalNode to [%d] and goalIndex [%d] - GOAL: type [%s], checked [%d]", - nodeIndex, - goalIndex, - goalPtr->name, - goalPtr->iChecked - ); - } else { - sprintf(msg, "Setting iGoalNode to [%d] and goalIndex [%d] - could not retrieve goal data.", nodeIndex, goalIndex); - } - rprint("setGoalNode()", msg); -} - -void cBot::setGoalNode(int nodeIndex) { - this->setGoalNode(nodeIndex, -1); -} - -void cBot::setGoalNode(tGoal *goal) { - if (goal != NULL && goal->iNode > -1) { - rprint("setGoalNode with goal pointer\n"); - this->setGoalNode(goal->iNode, goal->index); - } -} - -/** - * Always printed when debug mode is on - * @param Function - * @param msg - */ -void cBot::rprint(const char *Function, const char *msg) { - REALBOT_PRINT(this, Function, msg); -} - -/** - * Only printed when debug mode is on and verbosity is trace - * @param Function - * @param msg - */ -void cBot::rprint_trace(const char *Function, const char *msg) { - if (Game.messageVerbosity > 1) { - REALBOT_PRINT(this, Function, msg); - } -} - -/** - * Only printed when debug mode is on and verbosity is normal - * @param Function - * @param msg - */ -void cBot::rprint_normal(const char *Function, const char *msg) { - if (Game.messageVerbosity > 1) { - REALBOT_PRINT(this, Function, msg); - } -} - -void cBot::rprint(const char *msg) { - rprint("rprint()", msg); -} - -void cBot::rprint_normal(const char *msg) { - rprint_normal("rprint()", msg); -} - -void cBot::rprint_trace(const char *msg) { - rprint_trace("rprint()", msg); -} - -bool cBot::hasBomb() { - return isOwningWeapon(CS_WEAPON_C4); -} - -bool cBot::isCounterTerrorist() { - return iTeam == 2; -} - -bool cBot::isTerrorist() { - return iTeam == 1; -} - -bool cBot::hasPrimaryWeaponEquiped() { - return iPrimaryWeapon > -1; -} - -bool cBot::hasSecondaryWeaponEquiped() { - return iSecondaryWeapon > -1; -} - -bool cBot::hasSecondaryWeapon(int weaponId) { - return isOwningWeapon(weaponId); -} - -void cBot::performBuyWeapon(const char *menuItem, const char *subMenuItem) { - // To be sure the console will only change when we MAY change. - // The values will only be changed when console_nr is 0 - if (Game.getRoundStartedTime() + 4 < gpGlobals->time) - return; // Not valid to buy - - if (this->console_nr == 0) { - // set up first command and argument - strcpy(this->arg1, "buy"); - strcpy(this->arg2, menuItem); - - if (subMenuItem != NULL) strcpy(this->arg3, subMenuItem); - - this->console_nr = 1; // start console command sequence - } -} - -void cBot::performBuyActions(int weaponIdToBuy) { - if (weaponIdToBuy < 0) { - return; - } - // Buy... - - // TODO - // FRASHMAN 30.08.04 haven't changed the cs 1.5 buycode, maybe there are also errors - - // CS 1.5 only - if (counterstrike == 0) { - switch (weaponIdToBuy) { - case CS_WEAPON_AK47: - performBuyWeapon("4", "1"); - break; - case CS_WEAPON_DEAGLE: - performBuyWeapon("1", "3"); - break; - case CS_WEAPON_P228: - performBuyWeapon("1", "4"); - break; - case CS_WEAPON_SG552: - performBuyWeapon("4", "2"); - break; - case CS_WEAPON_SG550: - performBuyWeapon("4", "8"); - break; - case CS_WEAPON_SCOUT: - performBuyWeapon("4", "5"); - break; - case CS_WEAPON_AWP: - performBuyWeapon("4", "6"); - break; - case CS_WEAPON_MP5NAVY: - performBuyWeapon("3", "1"); - break; - case CS_WEAPON_UMP45: - performBuyWeapon("3", "5"); - break; - case CS_WEAPON_ELITE: - performBuyWeapon("1", "5"); - break; // T only - case CS_WEAPON_MAC10: - performBuyWeapon("3", "4"); - break; // T only - case CS_WEAPON_AUG: - performBuyWeapon("4", "4"); - break; // CT Only - case CS_WEAPON_FIVESEVEN: - performBuyWeapon("1", "6"); - break; // CT only - case CS_WEAPON_M4A1: - performBuyWeapon("4", "3"); - break; // CT Only - case CS_WEAPON_TMP: - performBuyWeapon("3", "2"); - break; // CT only - case CS_WEAPON_HEGRENADE: - performBuyWeapon("8", "4"); - break; - case CS_WEAPON_XM1014: - performBuyWeapon("2", "2"); - break; - case CS_WEAPON_SMOKEGRENADE: - performBuyWeapon("8", "5"); - break; - case CS_WEAPON_USP: - performBuyWeapon("1", "1"); - break; - case CS_WEAPON_GLOCK18: - performBuyWeapon("1", "2"); - break; - case CS_WEAPON_M249: - performBuyWeapon("5", "1"); - break; - case CS_WEAPON_M3: - performBuyWeapon("2", "1"); - break; - - case CS_WEAPON_G3SG1: - performBuyWeapon("4", "7"); - break; - case CS_WEAPON_FLASHBANG: - performBuyWeapon("8", "3"); - break; - case CS_WEAPON_P90: - performBuyWeapon("3", "3"); - break; - - // Armor - case CS_WEAPON_ARMOR_LIGHT: - performBuyWeapon("8", "1"); - break; - case CS_WEAPON_ARMOR_HEAVY: - performBuyWeapon("8", "2"); - break; - - case CS_DEFUSEKIT: - performBuyWeapon("8", "6"); - break; - } - } - - // CS 1.6 only - if (counterstrike == 1) { // FRASHMAN 30/08/04: redone switch block, it was full of errors - switch (weaponIdToBuy) { - //Pistols - case CS_WEAPON_GLOCK18: - performBuyWeapon("1", "1"); - break; - case CS_WEAPON_USP: - performBuyWeapon("1", "2"); - break; - case CS_WEAPON_P228: - performBuyWeapon("1", "3"); - break; - case CS_WEAPON_DEAGLE: - performBuyWeapon("1", "4"); - break; - case CS_WEAPON_ELITE: - performBuyWeapon("1", "5"); - break; - //ShotGUNS - case CS_WEAPON_M3: - performBuyWeapon("2", "1"); - break; - case CS_WEAPON_XM1014: - performBuyWeapon("2", "2"); - break; - //SMG - case CS_WEAPON_MAC10: - performBuyWeapon("3", "1"); - break; - case CS_WEAPON_TMP: - performBuyWeapon("3", "1"); - break; - case CS_WEAPON_MP5NAVY: - performBuyWeapon("3", "2"); - break; - case CS_WEAPON_UMP45: - performBuyWeapon("3", "3"); - break; - case CS_WEAPON_P90: - performBuyWeapon("3", "4"); - break; - //rifles - case CS_WEAPON_GALIL: - performBuyWeapon("4", "1"); - break; - case CS_WEAPON_FAMAS: - performBuyWeapon("4", "1"); - break; - case CS_WEAPON_AK47: - performBuyWeapon("4", "2"); - break; - case CS_WEAPON_M4A1: - performBuyWeapon("4", "3"); - break; - case CS_WEAPON_SG552: - performBuyWeapon("4", "4"); - break; - case CS_WEAPON_AUG: - performBuyWeapon("4", "4"); - break; - case CS_WEAPON_SG550: - performBuyWeapon("4", "5"); - break; - case CS_WEAPON_G3SG1: - performBuyWeapon("4", "6"); - break; - //machinegun - case CS_WEAPON_M249: - performBuyWeapon("5", "1"); - break; - // equipment - case CS_WEAPON_ARMOR_LIGHT: - performBuyWeapon("8", "1"); - break; - case CS_WEAPON_ARMOR_HEAVY: - performBuyWeapon("8", "2"); - break; - case CS_WEAPON_FLASHBANG: - performBuyWeapon("8", "3"); - break; - case CS_WEAPON_HEGRENADE: - performBuyWeapon("8", "4"); - break; - case CS_WEAPON_SMOKEGRENADE: - performBuyWeapon("8", "5"); - break; - case CS_WEAPON_SHIELD: - performBuyWeapon("8", "8"); - break; - - case CS_DEFUSEKIT: - performBuyWeapon("8", "6"); - break; - } - - // This differs per team - // FRASHMAN 30/08/04: all into one ifthen block - if (iTeam == 2) // counter - { - switch (weaponIdToBuy) { - case CS_WEAPON_SCOUT: - performBuyWeapon("4", "2"); - break; - case CS_WEAPON_AWP: - performBuyWeapon("4", "6"); - break; - //whats about nightvision? BuyWeapon (pBot, "8", "7") - } - } else // terror - { - switch (weaponIdToBuy) { - case CS_WEAPON_SCOUT: - performBuyWeapon("4", "3"); - break; - case CS_WEAPON_AWP: - performBuyWeapon("4", "5"); - break; - //whats about nightvision? BuyWeapon (pBot, "8", "6") - } - } - } // end of cs 1.6 part -} // We actually gonna buy this weapon - -// BOT: Memory() -// In this function the bot will receive data; this can be any kind of data. -// For hearing, the bot will check for sounds it should pay attention to and -// store this into its 'hearing vector'. The hearing vector will be used only -// when walking and not when fighting an enemy. Do note that this hearing vector -// is only filled when it is important enough, so all the decisions are made here. -void cBot::Memory() { - - // Skip method when it is too soon. - if (fMemoryTime > gpGlobals->time) { - return; - } - - // Hear players: (loop through all players, determine if they are running and if - // we can hear them (estimated distance)). - if (pEnemyEdict == NULL) { - Vector vHear = Vector(9999, 9999, 9999); - edict_t *pHearPlayer = NULL; - - //f_walk_time = gpGlobals->time + 1; - - for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - - // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { - // skip this player if not alive (i.e. dead or dying) - if (!IsAlive(pPlayer)) - continue; - - // check if we can 'see it on radar' (skip teammates) - if (UTIL_GetTeam(pPlayer) == UTIL_GetTeam(pEdict)) - continue; - - // check if its running - if (FUNC_PlayerRuns(FUNC_PlayerSpeed(pPlayer))) { - // check distance - float fDistance = - (pPlayer->v.origin - pEdict->v.origin).Length(); - - // estimated distance we can hear somebody - if (fDistance < BOT_HEARDISTANCE) { - // check if this 'hearing' vector is closer then our previous one - if (vHear != Vector(9999, 9999, 9999)) { - if (func_distance - (pEdict->v.origin, - pPlayer->v.origin) < - func_distance(pEdict->v.origin, vHear)) { - // this one is closer, thus more important - vHear = pPlayer->v.origin; - pHearPlayer = pPlayer; - } - } else { - vHear = pPlayer->v.origin; - pHearPlayer = pPlayer; - } - } - } - - - if ((pPlayer->v.button & IN_ATTACK) - && (FUNC_EdictHoldsWeapon(pEdict) != CS_WEAPON_HEGRENADE - && FUNC_EdictHoldsWeapon(pEdict) != CS_WEAPON_FLASHBANG - && FUNC_EdictHoldsWeapon(pEdict) != - CS_WEAPON_SMOKEGRENADE)) { - // check distance - float fDistance = - (pPlayer->v.origin - pEdict->v.origin).Length(); - - // estimated distance we can hear somebody - if (fDistance < BOT_HEARFIREDISTANCE) { - // check if this 'hearing' vector is closer then our previous one - if (vHear != Vector(9999, 9999, 9999)) { - if (func_distance - (pEdict->v.origin, - pPlayer->v.origin) < - func_distance(pEdict->v.origin, vHear)) { - // this one is closer, thus more important - vHear = pPlayer->v.origin; - pHearPlayer = pPlayer; - } - } else { - vHear = pPlayer->v.origin; - pHearPlayer = pPlayer; - } - } - } - // zooming of a sniper rifle - if (pPlayer->v.button & IN_ATTACK2) { - // check distance - float fDistance = - (pPlayer->v.origin - pEdict->v.origin).Length(); - - // estimated distance we can hear somebody - if (fDistance < BOT_HEARDISTANCE) { - // check if this 'hearing' vector is closer then our previous one - if (vHear != Vector(9999, 9999, 9999)) { - if (func_distance - (pEdict->v.origin, - pPlayer->v.origin) < - func_distance(pEdict->v.origin, vHear)) { - // this one is closer, thus more important - vHear = pPlayer->v.origin; - pHearPlayer = pPlayer; - } - } else { - vHear = pPlayer->v.origin; - pHearPlayer = pPlayer; - } - } - } - - } - } - - // Fill in hearing vectory if any: - if (pHearPlayer != NULL) { - if (RANDOM_LONG(0, 100) < (ipFearRate + 10)) { - - // determine fuzzyness by distance: - int iFuzz = - (int) (func_distance(pEdict->v.origin, vHear) / - BOT_HEARDISTANCE) * 250; - - // skill depended - iFuzz /= (bot_skill + 1); - - // create 'estimated hear vector' - vHear = - vHear + Vector(RANDOM_LONG(-iFuzz, iFuzz), - RANDOM_LONG(-iFuzz, iFuzz), - RANDOM_LONG(-iFuzz, iFuzz)); - - TraceResult tr; - - UTIL_TraceHull(pEdict->v.origin, vHear, dont_ignore_monsters, - point_hull, pEdict, &tr); - - int iNodeHearPlayer = - NodeMachine.getClosestNode(vHear, NODE_ZONE * 2, pHearPlayer); - - // if nothing hit: - if (tr.flFraction >= 1.0) { - // we can look at this spot - vEar = vHear; - } - // we go to the destination - - float fTime = 5 + (ipFearRate / 7); - - if (RANDOM_LONG(0, 100) < ipFearRate - && f_walk_time + 5 < gpGlobals->time) // last 5 seconds did not walk - f_walk_time = gpGlobals->time + fTime; - - if (RANDOM_LONG(0, 100) < ipCampRate - && f_camp_time + 30 < gpGlobals->time // last 30 seconds did not camp - ) { - f_camp_time = gpGlobals->time + fTime; - } - - } else { - fMemoryTime = gpGlobals->time + 5; - } - - /* - - - int iNodeHearPlayer = NodeMachine.getCloseNode (vHear, NODE_ZONE*2, pHearPlayer); - int iNodeFrom = NodeMachine.getCloseNode (pEdict->v.origin, NODE_ZONE*2, pEdict); - int iHearToNode = NodeMachine.node_look_at_hear(iNodeHearPlayer, iNodeFrom, pEdict); - - // look at hearto node - if (iHearToNode > -1) - { - vHead = NodeMachine.node_vector(iHearToNode); - SERVER_PRINT("found smart look at node\n"); - } - - // only check for new goal when the current goal is way of distance and such - if (ipCampRate > 30 && f_camp_time + 5 < gpGlobals->time) - f_camp_time = gpGlobals->time + 2.5; - */ - - if (f_update_weapon_time + 2 < gpGlobals->time) { - PickBestWeapon(); - } - } else { - vEar = Vector(9999, 9999, 9999); -// -// // check for any 'beeps' of the bomb! -// if (isCounterTerrorist() && Game.bBombPlanted) { -// // find the bomb vector -// edict_t *pent = NULL; -// Vector vC4 = Vector(9999, 9999, 9999); -// while ((pent = UTIL_FindEntityByClassname(pent, "grenade")) != NULL) { -// if (UTIL_GetGrenadeType(pent) == 4) // It is a C4 -// { -// vC4 = pent->v.origin; // store origin -// break; // done our part now -// } -// } // --- find the c4 -// -// if (vC4 != Vector(9999, 9999, 9999)) { -// -// if (func_distance(vC4, NodeMachine.node_vector(iGoalNode)) > 100 && -// func_distance(pEdict->v.origin, vC4) < 1024) { -// // set new goal node -// setGoalNode(NodeMachine.getCloseNode(vC4, NODE_ZONE, NULL)); -// forgetPath(); -// } -// } -// } - } - - } else { - vEar = Vector(9999, 9999, 9999); - } -} - - -// BOT: Do i carry weapon # now? -bool cBot::CarryWeapon(int iType) { - if (current_weapon.iId == iType) - return true; - return false; -} - -// BOT: Do i carry weapon TYPE # now? -int cBot::CarryWeaponType() { - int kind = PRIMARY; - int weapon_id = current_weapon.iId; - - // Check 1. Is it a knife? - if (weapon_id == CS_WEAPON_KNIFE) - kind = KNIFE; - - // Check 2, is it a 'tool'? - if (weapon_id == CS_WEAPON_FLASHBANG || weapon_id == CS_WEAPON_HEGRENADE - || weapon_id == CS_WEAPON_SMOKEGRENADE) - kind = GRENADE; - - // Check 3, is it a secondary gun? - if (weapon_id == CS_WEAPON_P228 || weapon_id == CS_WEAPON_ELITE - || weapon_id == CS_WEAPON_UMP45 || weapon_id == CS_WEAPON_USP - || weapon_id == CS_WEAPON_GLOCK18 || weapon_id == CS_WEAPON_DEAGLE - || weapon_id == CS_WEAPON_FIVESEVEN) - kind = SECONDARY; - - // Check 4, is it a sniper gun? - if (weapon_id == CS_WEAPON_SCOUT || weapon_id == CS_WEAPON_SG550 - || weapon_id == CS_WEAPON_AWP || weapon_id == CS_WEAPON_G3SG1) - kind = SNIPER; - - if (hasShield()) { - kind = SHIELD; - } - //if (weapon_id < 1) - // kind = NONE; - return kind; -} - -// BOT: Think about objectives -// -// This function only takes action when the bot is close a goal. The function -// NodeMachine.path_think() handles WHERE the bot goes. Not WHAT to do at a goal. -void cBot::ThinkAboutGoals() { - //REALBOT_PRINT(this, "thinkAboutGoals()", "start"); - // Depending on bot team we handle goals differently: - // TERRORISTS - if (isTerrorist()) { - // Plant the bomb when the HUD says we can -- BERKED - if (bHUD_C4_plantable) - f_c4_time = gpGlobals->time + 1; // plant bomb - - // A dropped C4 is not a 'goal' (ie. it won't let you win the game - // when you pick up the bomb. Therefor the 'pickup the dropped bomb - // code is in cNodeMachine::path_walk(). - } else if (isCounterTerrorist()) { - // COUNTER-TERRORISTS - if (vip) { - // VIP - } else { - if (Game.bBombPlanted) { - if (isCounterTerrorist()) { - // defuse (or set timers for it) - Defuse(); - } - } else { - if (Game.bHostageRescueMap) { - TryToGetHostageTargetToFollowMe(this); - checkIfHostagesAreRescued(); - checkOfHostagesStillFollowMe(); - } - } - } - } - // in Act() we find the 'acting' code when timers above are set. -} - -void cBot::rememberWhichHostageToRescue(edict_t *pHostage) { - this->pBotHostage = pHostage; -} - -edict_t * cBot::getHostageToRescue() { - return pBotHostage; -} - -edict_t * cBot::findHostageToRescue() { - edict_t *pent = NULL; - - // Search for all hostages in the game - while ((pent = UTIL_FindEntityByClassname(pent, "hostage_entity")) != NULL) { - if (!isHostageRescueable(this, pent)) continue; - if (!canSeeEntity(pent)) continue; - // skip too far hostages, leave it up to the goal picking to get closer - if (getDistanceTo(pent->v.origin) > (NODE_ZONE * 2.5)) continue; - - char msg[255]; - sprintf(msg, "Found hostage to rescue at %f,%f,%f", pent->v.origin.x, pent->v.origin.y, pent->v.origin.z); - this->rprint_trace("findHostageToRescue()", msg); - return pent; - } - - return NULL; -} - -bool cBot::isDefusing() { - return f_defuse > gpGlobals->time; -} - -bool cBot::hasTimeToMoveToNode() { - return fMoveToNodeTime > -1 && getMoveToNodeTimeRemaining() > 0; -} -/** -This function will set the iCloseNode variable, which is the node most closest to -the bot. Returns the closest node it found. -**/ -int cBot::determineCurrentNode() { - iCloseNode = determineCurrentNode(NODE_ZONE); - return iCloseNode; -} - -/** -This function will set the iCloseNode variable, which is the node most closest to -the bot. Returns the closest node it found. -**/ -int cBot::determineCurrentNodeWithTwoAttempts() { - iCloseNode = determineCurrentNode(); - if (iCloseNode < 0) { - iCloseNode = determineCurrentNode(NODE_ZONE * 2); - } - return iCloseNode; -} - -/** -Find node close to bot, given range. Does not cache result. -**/ -int cBot::determineCurrentNode(float range) { - return NodeMachine.getClosestNode(pEdict->v.origin, range, pEdict); -} - -/** - * This returns the current node (iCloseNode) set. Instead of using determineCurrentNode, which is expensive, - * call this to return the cached value. It will however call determineCurrentNode when node is < 0, usually it means - * the state has been set. - * @return - */ -int cBot::getCurrentNode() { - if (iCloseNode < 0) { - determineCurrentNode(); - } - return iCloseNode; -} - -/** - * Aka, the node we are heading for. - */ -int cBot::getCurrentPathNodeToHeadFor() { - return NodeMachine.getNodeIndexFromBotForPath(iBotIndex, pathIndex); -} - -/** - * Aka, the node we were coming from. In case the index is < 0 (ie, there is no previous node yet), this will - * return -1; - */ -int cBot::getPreviousPathNodeToHeadFor() { - return NodeMachine.getNodeIndexFromBotForPath(iBotIndex, getPreviousPathIndex()); -} - -bool cBot::isHeadingForGoalNode() { - return getCurrentPathNodeToHeadFor() == getGoalNode(); -} - -/** - * Aka, the next node after we have arrived at the current path node. - */ -int cBot::getNextPathNode() { - return NodeMachine.getNodeIndexFromBotForPath(iBotIndex, pathIndex + 1); -} - -// Is this bot dead? -bool cBot::isDead() { - return (pEdict->v.health < 1) || (pEdict->v.deadflag != DEAD_NO); -} - -// BOT: Think -void cBot::Think() { - if (mod_id != CSTRIKE_DLL) return; // do not support non-counter-strike mods - - // BOT: If a bot did not join a team yet, then do it - if (!hasJoinedTeam) { - rprint("Need to join team, doing that now"); - JoinTeam(); - return; - } - - // Set closest node - determineCurrentNode(); - - // BOT: If a bot is dead, re-initialize - if (isDead()) { - if (!bInitialize) return; // do nothing when no need to initialize - rprint("Dead, need to re-initialize"); - - // AUTOSKILL - cBot *botPointerOfKiller = UTIL_GetBotPointer(killer_edict); - - // not killed by a fellow bot, presumably a human player - if (botPointerOfKiller == NULL) { - if (autoskill) { - bot_skill--; - if (bot_skill < 0) - bot_skill = 0; - } - - if (Game.iKillsBroadcasting != BROADCAST_KILLS_NONE - && killer_edict != NULL) { - // This is a human, we will tell this human he has been killed - // by a bot. - int r = RANDOM_LONG(150, 255); - int g = RANDOM_LONG(30, 155); - int b = RANDOM_LONG(30, 155); - char msg[128]; - if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_FULL) - sprintf(msg, - "You have killed a RealBot!\n\nName:%s\nSkill:%d\n", - name, bot_skill); - else - sprintf(msg, "You have killed a RealBot named %s!", - name); - - HUD_DrawString(r, g, b, msg, killer_edict); - } - } - - if (iCloseNode > -1 && !end_round) { - iDiedNode = iCloseNode; - NodeMachine.danger(iCloseNode, UTIL_GetTeam(pEdict)); - } - - if (console_nr == 0) { - rprint("NewRound - because console_nr ?!"); - NewRound(); - bInitialize = false; - } - - BotConsole(this); - - // dead messages - if (console_nr == 0) { - rprint("console_nr == 0"); //whatever this means - // do some chatting - if (RANDOM_LONG(0, 200) < ipChatRate) { - if (fChatTime + 0.5 < gpGlobals->time) - if (chChatSentence[0] == '\0') // we did not want to say anything - { - // we should say something now? - int iMax = -1; - - for (int tc = 0; tc < 50; tc++) { - if (ChatEngine.ReplyBlock[99].sentence[tc][0] != '\0') iMax++; - } - - int the_c = RANDOM_LONG(0, iMax); - - if (the_c > -1 && iMax > -1) { - char chSentence[80]; - memset(chSentence, 0, sizeof(chSentence)); - sprintf(chSentence, "%s ", - ChatEngine.ReplyBlock[99].sentence[the_c]); - //strcpy(chSentence, ChatEngine.ReplyBlock[99].sentence[the_c]); - PrepareChat(chSentence); - } - } - } else { - // we missed the chatrate chance - if (fChatTime < gpGlobals->time) // time - if (chChatSentence[0] == '\0') // we did not want to say anything - if (RANDOM_LONG(0, 100) < ipChatRate) // rate - fChatTime = gpGlobals->time + - RANDOM_FLOAT(0.0, ((Game.iProducedSentences + 1) / 2)); // wait - - } - - return; - } - } // isDead(); - - // set this for the next time the bot dies so it will initialize stuff - if (!bInitialize) { - bInitialize = true; - } - - if (end_round) { - rprint("End round"); - MDLL_ClientKill(pEdict); - pEdict->v.frags += 1; - return; - } - - // BOT: Played enough rounds - if (played_rounds > play_rounds && internet_play) { - rprint("Played enough rounds"); - bIsUsed = FALSE; // no longer used - char cmd[80]; - sprintf(cmd, "kick \"%s\"\n", name); - SERVER_COMMAND(cmd); // kick the bot using (kick "name") - return; - } - - // Move speed... moved_distance. - if (distanceMovedTimer <= gpGlobals->time) { - // see how far bot has moved since the previous position... - Vector v_diff = prevOrigin - pEdict->v.origin; - // make distanceMoved an average of this moment and the previous one. - float movedTwoTimes = distanceMoved + v_diff.Length(); - - // prevent division by zero - if (movedTwoTimes > 0.0f) { - distanceMoved = movedTwoTimes / 2; - } else { - distanceMoved = 0; - } - - // save current position as previous - prevOrigin = pEdict->v.origin; - distanceMovedTimer = gpGlobals->time + 0.1; - } - - // NEW ROUND - if (Game.NewRound()) { - rprint_trace("Think", "Game.NewRound"); - } - - // -------------------------------- - // MEMORY STEP - // -------------------------------- - Memory(); - - // -------------------------------- - // IMPORTANT THINKING GOING ON HERE - // -------------------------------- - int healthChange = prev_health - bot_health; - - // handle damage taken - if (prev_health > bot_health - && healthChange > RANDOM_LONG(CSTRIKE_MIN_DAMAGE, CSTRIKE_MAX_DAMAGE) - && hasEnemy()) { - - // need backup! - if (FUNC_DoRadio(this)) { - UTIL_BotRadioMessage(this, 3, "3", ""); - } - - BOT_DecideTakeCover(this); - } - - prev_health = bot_health; - - // Do your console stuff - BotConsole(this); - - // BOT: Blinded - if (isBlindedByFlashbang()) { - // Dude we are messed up. - - // 01/07/04 - Stefan - Pointed out on the forums by Josh Borke... (do not shoot when dontshoot is on) - // shoot randomly - if (!Game.bDoNotShoot) { - if ((RANDOM_LONG(0, 100) < ipFearRate) && RANDOM_LONG(0, 100)) { - UTIL_BotPressKey(this, IN_ATTACK); - } - } - - rprint_trace("Think()", "Blinded"); - return; - } - - - // NEW: When round time is over and still busy playing, kill bots - float roundTimeInSeconds = CVAR_GET_FLOAT("mp_roundtime") * 60; - float freezeTimeCVAR = CVAR_GET_FLOAT("mp_freezetime"); - if (Game.getRoundStartedTime() + 10.0 + roundTimeInSeconds + freezeTimeCVAR < gpGlobals->time) { - end_round = true; - // round is ended - } - - // FREEZETIME: - if (Game.getRoundStartedTime() > gpGlobals->time && freezeTime < gpGlobals->time) { - freezeTime = gpGlobals->time + RANDOM_FLOAT(0.1, 2.0); - } - - // 1 SECOND START OF ROUND - if (Game.getRoundStartedTime() + 1 > gpGlobals->time && - Game.getRoundStartedTime() < gpGlobals->time) { - // TODO: Issue radio command? - this->rprint_trace("cBot::Think()", "First second of round"); - } - - // SITUATION: In freezetime - if (isFreezeTime()) { - stopMoving(); - lastSeenEnemyVector = Vector(0, 0, 0); - setTimeToMoveToNode(2); - vHead = vBody = pEdict->v.origin; - - // find any spawnpoint to look at: - edict_t *pent = NULL; - - if (isCounterTerrorist()) { - while ((pent = UTIL_FindEntityByClassname(pent, "info_player_start")) != NULL) { - if (func_distance(pent->v.origin, pEdict->v.origin) < 200 && - func_distance(pent->v.origin, pEdict->v.origin) > 50) { - break; - } - } - } else { - while ((pent = UTIL_FindEntityByClassname(pent, "info_player_deathmatch")) != NULL) { - if (func_distance(pent->v.origin, pEdict->v.origin) < 200 && - func_distance(pent->v.origin, pEdict->v.origin) > 50) { - break; - } - } - } - - // when pent is filled, look at it - if (pent != NULL) { - vBody = vHead = pent->v.origin; - } - - rprint_trace("Think()", "isFreezeTime"); - return; - } - - // **---**---**---**---**---**---** - // MAIN STATE: We have no enemy... - // **---**---**---**---**---**---** - if (!hasEnemy()) { - - if (!Game.bDoNotShoot) { - InteractWithPlayers(); - } - - bool bMayFromGame = true; - - if (Game.fWalkWithKnife > 0) - if (Game.getRoundStartedTime() + Game.fWalkWithKnife < gpGlobals->time) - bMayFromGame = false; - - if (Game.fWalkWithKnife == 0) - bMayFromGame = false; - - if (hasShield()) { - if (!hasShieldDrawn() && f_allow_keypress < gpGlobals->time) { - UTIL_BotPressKey(this, IN_ATTACK2); // draw shield - f_allow_keypress = gpGlobals->time + 0.7; - } - } - - if (CarryWeapon(CS_WEAPON_KNIFE) == false - && f_camp_time < gpGlobals->time - && freezeTime < gpGlobals->time - && f_c4_time < gpGlobals->time - && f_update_weapon_time < gpGlobals->time && bWalkKnife - && bMayFromGame) { - UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(-1)); // -1 is knife - f_update_weapon_time = gpGlobals->time + 0.7; - } - - // When holding a grenade (and not switching to another weapon) - if (CarryWeaponType() == GRENADE - && f_update_weapon_time < gpGlobals->time) { - if (iPrimaryWeapon > -1) - UTIL_SelectItem(pEdict, - UTIL_GiveWeaponName(iPrimaryWeapon)); - - else // pick secondary - UTIL_SelectItem(pEdict, - UTIL_GiveWeaponName(iSecondaryWeapon)); - f_update_weapon_time = gpGlobals->time + 0.7; - } - - // Think about objectives - ThinkAboutGoals(); - } else { - // **---**---**---**---**---**---** - // MAIN STATE: We have an enemy! - // **---**---**---**---**---**---** - - // Keep interacting with players: - InteractWithPlayers(); - - // And combat enemies - Combat(); - } - - // WALK() - NodeMachine.path_think(this, distanceMoved); - - // SITUATION: Passed Freezetime - -} // THINK() - -bool cBot::isFreezeTime() const { - return freezeTime > gpGlobals->time; -} - -/** -Return true if one of the pointers is not NULL -**/ -bool cBot::isEscortingHostages() { - bool result = getAmountOfHostagesBeingRescued() > 0; - if (result) { - rprint("I am escorting hostages!"); - } - return result; -} - -void cBot::checkOfHostagesStillFollowMe() { - if (fCheckHostageStatusTimer > gpGlobals->time) return; - fCheckHostageStatusTimer = gpGlobals->time + 5; - -//// this->rprint("checkOfHostagesStillFollowMe - START"); -// if (hostage1) { -// if (!isHostageRescued(this, hostage1) && FUNC_EdictIsAlive(hostage1) && !canSeeEntity(hostage1) && getDistanceTo(hostage1->v.origin) > NODE_ZONE*2.5) { -// rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage1"); -// forgetHostage(hostage1); -// } -// } -// if (hostage2) { -// if (!isHostageRescued(this, hostage2) && FUNC_EdictIsAlive(hostage2) && !canSeeEntity(hostage2) && getDistanceTo(hostage2->v.origin) > NODE_ZONE*2.5) { -// rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage2"); -// forgetHostage(hostage2); -// } -// } -// if (hostage3) { -// if (!isHostageRescued(this, hostage3) && FUNC_EdictIsAlive(hostage3) && !canSeeEntity(hostage3) && getDistanceTo(hostage3->v.origin) > NODE_ZONE*2.5) { -// rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage3"); -// forgetHostage(hostage3); -// } -// } -// -// if (hostage4) { -// if (!isHostageRescued(this, hostage4) && FUNC_EdictIsAlive(hostage4) && !canSeeEntity(hostage4) && getDistanceTo(hostage4->v.origin) > NODE_ZONE*2.5) { -// rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage4"); -// forgetHostage(hostage4); -// } -// } -// rprint("checkOfHostagesStillFollowMe - END"); -} - -void cBot::clearHostages() { - rprint_trace("clearHostages"); - hostage1 = NULL; - hostage2 = NULL; - hostage3 = NULL; - hostage4 = NULL; - pBotHostage = NULL; -} - -// BOT: CheckGear, part of UpdateStatus() -void cBot::CheckGear() { - - // PRIMARY - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_mp5navy"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_mp5navy"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_ak47"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_ak47"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_m3"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_m3"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_aug"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_aug"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_sg552"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_sg552"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_m249"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_m249"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_xm1014"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_xm1014"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_p90"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_p90"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_tmp"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_tmp"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_m4a1"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_m4a1"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_awp"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_awp"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_sg550"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_sg550"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_scout"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_scout"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_mac10"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_mac10"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_g3sg1"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_g3sg1"); - - // Counter-Strike 1.6 weapon FAMAS/GALIL - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_famas"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_famas"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_galil"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_galil"); - - // SECONDARY - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_ump45"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_ump45"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_elite"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_elite"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_fiveseven"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_fiveseven"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_p228"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_p228"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_deagle"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_deagle"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_usp"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_usp"); - if (isOwningWeapon(UTIL_GiveWeaponId("weapon_glock18"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_glock18"); - - // Handle shields as primary weapon - if (hasShield()) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_shield"); -} - -// BOT: Update personal status -void cBot::UpdateStatus() { - // name filled in yet? - if (name[0] == 0) - strcpy(name, STRING(pEdict->v.netname)); - - // Set thirdpartybot flag - pEdict->v.flags |= FL_THIRDPARTYBOT; - - // Reset stuff - pEdict->v.button = 0; - setMoveSpeed(f_max_speed); // by default run - - // When its not time to strafe, don't. - if (f_strafe_time < gpGlobals->time) { - if (f_strafe_speed != 0.0f) { - rprint_trace("UpdateStatus", "Strafe speed set to 0!"); - f_strafe_speed = 0.0f; - } - } - - // Update team state when started - if (hasJoinedTeam) { - iTeam = UTIL_GetTeam(pEdict) + 1; // 1 - TERRORIST, 2 - COUNTER-TERRORIST - } - - // Check if we became VIP - vip = UTIL_IsVip(pEdict); - - // Check gear - CheckGear(); - - // Set max speed and such when CS 1.6 - if (counterstrike == 1) { - f_max_speed = pEdict->v.maxspeed; -// char msg[255]; -// sprintf(msg, "f_max_speed set to %f", f_max_speed); -// rprint_trace("UpdateStatus", msg); - bot_health = (int) pEdict->v.health; - bot_armor = (int) pEdict->v.armorvalue; - } -} - -// ---------------------------------- BOT CLASS FUNCTIONS -// ---------------------------------- BOT CLASS FUNCTIONS -// ---------------------------------- BOT CLASS FUNCTIONS - -//////////////////////////////////////////////////////////////////////////////// -/// Radio Action - Response -//////////////////////////////////////////////////////////////////////////////// -bool BotRadioAction() { - char name[64]; - bool unstood = false; - edict_t *plr = NULL; - int team = -1; - int i; - int radios = 0; // Hold amount of replies here, so we don't flood :) - strcpy(name, radio_messenger); - - // First find the team messager name - for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); // Get pEdict - char netname[64]; - if (pPlayer) // If player exists - { - strcpy(netname, STRING(pPlayer->v.netname)); // Copy netname - if (strcmp(netname, name) == 0) // If - { - plr = pPlayer; - team = UTIL_GetTeam(pPlayer); - } - } - } - - // Check players and check if radio message applies to them - for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - char netname[64]; - if (pPlayer) { - - strcpy(netname, STRING(pPlayer->v.netname)); - - if ((strcmp(netname, name) != 0) && // When not the same name - (team == UTIL_GetTeam(pPlayer)) && // .. the same team... - (pPlayer->v.deadflag == DEAD_NO) && // .. not dead .. - ((UTIL_GetBotPointer(pPlayer) != NULL))) // and a RealBot - { - // here are all bots - cBot *BotPointer = UTIL_GetBotPointer(pPlayer); - - if (BotPointer == NULL) - continue; // somehow this fucked up, bail out - - bool want_to_answer = false; // want to answer radio call - bool report_back = false; // for reporting in - float distance = func_distance(plr->v.origin, - BotPointer->pEdict->v.origin); // distance between the 2 - - // Same team, randomly, do we even listen to the radio? - // the more further away, the more chance it will not listen - bool bWantToListen = false; - - // Reply on distance check - if (RANDOM_LONG(0, 8192) > distance) - bWantToListen = true; - - // Hearrate (personality setting) - if (RANDOM_LONG(0, 100) < BotPointer->ipHearRate && - bWantToListen) - want_to_answer = true; - - bool can_do_negative = true; // On some radio commands we can't say negative, thats stupid - - // If we want to listen to the radio... then handle it! - if (bWantToListen) { - - // Report in team! - if (strstr(message, "#Report_in_team") != NULL) { - // gives human knowledge who is on his team - } - - // Regroup team! - if (strstr(message, "#Regroup_team") != NULL) { - // regroup now! - unstood = true; - - // get to the leader position - BotPointer->rprint("Setting goal from regroup team"); - BotPointer->setGoalNode(NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE * 2, plr)); - BotPointer->forgetPath(); - } - - // Hold this position - if (strstr(message, "#Hold_this_position") != NULL || - strstr(message, "#Get_in_position_and_wait") != NULL) { - // do nothing - } - // Follow me!! - if (strstr(message, "#Follow_me") != NULL) {} - - // You take the point! - // 23/06/04 - Stefan - Here the leader should break up his position? - // ie, the leader will be assigned to the bot this human/bot is facing? - if (strstr(message, "#You_take_the_point") != NULL) { - can_do_negative = false; - } - // Enemy Sotted! - if (strstr(message, "#Enemy_spotted") != NULL) { - can_do_negative = false; - - // Find player who issues this message and go to it - int iBackupNode = - NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE, plr); - - // Help this player - if (iBackupNode > -1) { - - unstood = true; - - BotPointer->rprint("Setting goal for backup"); - BotPointer->setGoalNode(iBackupNode); - BotPointer->forgetPath(); - BotPointer->f_camp_time = gpGlobals->time - 1; - BotPointer->f_walk_time = gpGlobals->time; - } - } - // Enemy Down! - if (strstr(message, "#Enemy_down") != NULL) { - - unstood = true; - can_do_negative = false; - } - // Stick together team! - if (strstr(message, "#Stick_together_team") != NULL) { - unstood = true; - // TODO: Find someone to follow. (to stick with) - } - // cover me|| strstr (message, "#Cover_me") != NULL - - // Need backup / taking fire... - if (strstr(message, "#Need_backup") != NULL || strstr(message, "#Taking_fire") != NULL) { - - unstood = true; - - // get source of backup - int iBackupNode = NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE, plr); - - if (iBackupNode > -1) { - BotPointer->rprint("Setting goal for backup"); - BotPointer->setGoalNode(iBackupNode); - BotPointer->forgetPath(); - BotPointer->f_camp_time = gpGlobals->time - 1; - BotPointer->f_walk_time = gpGlobals->time; - } - } - - // Taking fire! - if (strstr(message, "#Taking_fire") != NULL) { - // todo todo todo backup our friend - // unstood = true; - } - // Team fall back! - if (strstr(message, "#Team_fall_back") != NULL) {} - // Go GO Go, stop camping, stop following, get the heck out of there! - if (strstr(message, "#Go_go_go") != NULL) { - unstood = true; - BotPointer->f_camp_time = gpGlobals->time - 30; - BotPointer->f_cover_time = gpGlobals->time - 10; - BotPointer->f_hold_duck = gpGlobals->time - 10; - BotPointer->f_jump_time = gpGlobals->time - 10; - BotPointer->forgetPath(); - BotPointer->forgetGoal(); - } - - if ((FUNC_DoRadio(BotPointer)) && (unstood)) { - if (BotPointer->console_nr == 0 - && radios < (gpGlobals->maxClients / 4)) { - if (!report_back) { - UTIL_BotRadioMessage(BotPointer, 3, "1", ""); // Roger that! - } else { - UTIL_BotRadioMessage(BotPointer, 3, "6", ""); // Reporting in! - } - - BotPointer->f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.8, 2.0); - radios++; - } - } - } // they even listen to the radio command? - else { - /* - // filter out the commands where we cannot reply with negative - // You take the point! - if (strstr (message, "#You_take_the_point") != NULL) - can_do_negative = false; - - // Enemy Sotted! - if (strstr (message, "#Enemy_spotted") != NULL) - can_do_negative = false; - - // Enemy Down! - if (strstr (message, "#Enemy_down") != NULL) - can_do_negative = false; - - if ((FUNC_DoRadio(BotPointer)) - && (unstood) && (can_do_negative)) - - { - if (BotPointer->console_nr == 0 - && radios < (gpGlobals->maxClients / 4)) - - { - if (report_back == false) - - { - UTIL_BotRadioMessage (BotPointer, 3, "8", ""); // Negative! - BotPointer->f_console_timer = gpGlobals->time + RANDOM_FLOAT (0.8, 2.0); - radios++; - } - } - } - */ - } - } // End check! - } // If (Player) - } // FOR Clients - return true; -} - -// Is entity visible? (from Entity view) -bool EntityIsVisible(edict_t *pEntity, Vector dest) { - - //DebugOut("bot: EntityIsVisible()\n"); - TraceResult tr; - - // trace a line from bot's eyes to destination... - UTIL_TraceLine(pEntity->v.origin + pEntity->v.view_ofs, dest, - dont_ignore_monsters, pEntity->v.pContainingEntity, &tr); - - // check if line of sight to object is not blocked (i.e. visible) - if (tr.flFraction >= 1.0) - return true; - - else - return false; -} - -// Can see Edict? -bool cBot::canSeeEntity(edict_t *pEntity) { - if (pEntity == NULL) return false; - - TraceResult tr; - Vector start = pEdict->v.origin + pEdict->v.view_ofs; - Vector vDest = pEntity->v.origin; - - // trace a line from bot's eyes to destination... - UTIL_TraceLine(start, vDest, ignore_monsters, pEdict->v.pContainingEntity, &tr); - - // it hit anything - if (tr.flFraction < 1.0) { - // if it hit the entity we wanted to see, then its ok! - if (tr.pHit == pEntity) return true; - return false; - } - - return true; -} - -/** - * Returns distance from this bot to a given nodeIndex. If the given NodeIndex is invalid, the distance returned is 0. - * @param nodeIndex - * @return - */ -float cBot::getDistanceTo(int nodeIndex) { - tNode *nodePtr = NodeMachine.getNode(nodeIndex); - if (nodePtr != NULL) { - return getDistanceTo(nodePtr->origin); - } - rprint("getDistanceTo(int nodeIndex)", "The given nodeIndex was invalid, returning 9999 distance"); - return 9999; -} - -/** - * Returns distance from this bot position (pEdict->v.origin) to given Vector. - * @param vDest - * @return - */ -float cBot::getDistanceTo(Vector vDest) { - return func_distance(pEdict->v.origin, vDest); -} - -bool cBot::isUsingHostage(edict_t *pHostage) { - if (pHostage == NULL) return false; - - // checks if the current pEdict is already 'in use' - // note: time check only when we have an hostage pointer assigned - if (hostage1 == pHostage) { - rprint("isUsingHostage", "hostage1"); - return true; - } - - if (hostage2 == pHostage) { - rprint("isUsingHostage", "hostage2"); - return true; - } - - if (hostage3 == pHostage) { - rprint("isUsingHostage", "hostage3"); - return true; - } - - if (hostage4 == pHostage) { - rprint("isUsingHostage", "hostage4"); - return true; - } - - return false; -} - -void cBot::forgetHostage(edict_t *pHostage) { - // these are the hostages we are rescueing (ie, they are following this bot) - if (hostage1 == pHostage) { - rprint("forgetHostage", "hostage1"); - hostage1 = NULL; - } - if (hostage2 == pHostage) { - rprint("forgetHostage", "hostage2"); - hostage2 = NULL; - } - if (hostage3 == pHostage) { - rprint("forgetHostage", "hostage3"); - hostage3 = NULL; - } - if (hostage4 == pHostage) { - rprint("forgetHostage", "hostage4"); - hostage4 = NULL; - } - - // this is the hostage we have taken interest in - if (pBotHostage == pHostage) { - rprint("forgetHostage", "pBotHostage"); - pBotHostage = NULL; - } -} - -int cBot::getAmountOfHostagesBeingRescued() { - int count = 0; - - if (hostage1 != NULL) count++; - if (hostage2 != NULL) count++; - if (hostage3 != NULL) count++; - if (hostage4 != NULL) count++; - - return count; -} - -// Will return true when the vector is visible. -// TODO: Make this function more flexible, ie able to hit an entity that it searches -// and return true on that as well. (mix it with the above function) -bool cBot::canSeeVector(Vector vDest) { - TraceResult tr; - Vector start = pEdict->v.origin + pEdict->v.view_ofs; - - // trace a line from bot's eyes to destination... - UTIL_TraceLine(start, vDest, ignore_monsters, pEdict->v.pContainingEntity, &tr); - - if (tr.flFraction < 1.0) - return false; - - return true; -} - -// The coming 2 shield functions where originaly created by Whistler; -// i got them from the JoeBot source though. But... in the end, thank you -// Whistler! -bool cBot::hasShield() { - // Adapted from Wei Mingzhi's YAPB - return (strncmp(STRING(pEdict->v.viewmodel), "models/shield/v_shield_", 23) == 0); -} - -bool cBot::hasShieldDrawn() { - // Adapted from Wei Mingzhi's YAPB - if (!hasShield()) - return false; - - return (pEdict->v.weaponanim == 6 || pEdict->v.weaponanim == 7); -} - -/* - BotThink() - This function is the very general/main/simplified thinking function of the bot. - Do NOT add/remove/change code here! If you want to give the bot information to - work with. Put it in UpdateStatus(). When the bot has to think about it, do it - int Think() and everything else (when all is set, how to 'do' it) in Act(). - */ -void BotThink(cBot *pBot) { - // STEP 1: Update status - pBot->UpdateStatus(); - - // STEP 2: Think - pBot->Think(); - - // STEP 3: Act - pBot->Act(); - - // PASS THROUGH ENGINE - -// float frameInterval = m_lastCommandTime - gpGlobals->time; - float msecval = (gpGlobals->time - pBot->fLastRunPlayerMoveTime) * 1000.0f; - pBot->fLastRunPlayerMoveTime = gpGlobals->time; - - double upMove = 0.0; - char msg[255]; - sprintf(msg, "moveSpeed %f, strafeSpeed %f, msecVal %f", pBot->f_move_speed, pBot->f_strafe_speed, msecval); - pBot->rprint_trace("BotThink/pfnRunPlayerMove", msg); - g_engfuncs.pfnRunPlayerMove(pBot->pEdict, - pBot->vecMoveAngles, - pBot->f_move_speed, - pBot->f_strafe_speed, - upMove, - pBot->pEdict->v.button, - 0, - msecval); - - float fUpdateInterval = 1.0f / 60.0f; // update at 60 fps - pBot->fUpdateTime = gpGlobals->time + fUpdateInterval; -} - -// 17/07/04 -// log important variables of the bot (it will be called from dll.cpp once per active bot) -// they are logged into reallog.txt file - -void cBot::Dump(void) { - char buffer[181]; - int iCurrentNode = - NodeMachine.getClosestNode(pEdict->v.origin, (NODE_ZONE * 2), pEdict); - - _snprintf(buffer, 180, - "%s (#%d %s): timers, now= %.0f, c4_time=%.0f, camp_time=%.0f, wait_time=%.0f, cover_time=%.0f, wander=%.0f, MoveToNodeTime=%.0f\n", - name, iBotIndex, (iTeam == 1) ? "T" : "CT", gpGlobals->time, - f_c4_time, f_camp_time, f_wait_time, f_cover_time, fWanderTime, - fMoveToNodeTime); - rblog(buffer); - _snprintf(buffer, 180, " GoalNode=%d, CurrentNode=%d, iPathFlags=", - iGoalNode, iCurrentNode); - switch (iPathFlags) { - case PATH_NONE: - strncat(buffer, "PATH_NONE ", 180); - break; - case PATH_DANGER: - strncat(buffer, "PATH_DANGER ", 180); - break; - case PATH_CONTACT: - strncat(buffer, "PATH_CONTACT ", 180); - break; - case PATH_CAMP: - strncat(buffer, "PATH_CAMP ", 180); - break; - default: - strncat(buffer, "???", 180); - } - strncat(buffer, "\n", 180); - rblog(buffer); - if (iGoalNode >= 0) - NodeMachine.dump_path(iBotIndex, pathIndex); -} - -/** - * Will begin walk the path by setting pathNodeIndex to 0, which is a valid nr so that - * isWalkingPath returns true. - */ -void cBot::beginWalkingPath() { - this->pathIndex = 0; -} - -bool cBot::hasHostageToRescue() { - return pBotHostage != NULL; -} - -bool cBot::canSeeHostageToRescue() { - return canSeeEntity(pBotHostage); -} - -void cBot::clearHostageToRescueTarget() { - rprint_trace("clearHostageToRescueTarget", "clearing pBotHostage pointer"); - this->pBotHostage = NULL; -} - -// Finds a free hostage pointer and assigns it. -void cBot::rememberHostageIsFollowingMe(edict_t *pHostage) { - if (pHostage == NULL) { - rprint_trace("rememberHostageIsFollowingMe", "ERROR assigning NULL pHostage pointer!?"); - } - if (hostage1 == NULL) { - rprint_trace("rememberHostageIsFollowingMe", "hostage1 slot is free."); - hostage1 = pHostage; - } else if (hostage2 == NULL) { - rprint_trace("rememberHostageIsFollowingMe", "hostage2 slot is free."); - hostage2 = pHostage; - } else if (hostage3 == NULL) { - rprint_trace("rememberHostageIsFollowingMe", "hostage3 slot is free."); - hostage3 = pHostage; - } else if (hostage4 == NULL) { - rprint_trace("rememberHostageIsFollowingMe", "hostage4 slot is free."); - hostage4 = pHostage; - } -} - -void cBot::checkIfHostagesAreRescued() { - if (isHostageRescued(this, hostage1)) forgetHostage(hostage1); - if (isHostageRescued(this, hostage2)) forgetHostage(hostage2); - if (isHostageRescued(this, hostage3)) forgetHostage(hostage3); - if (isHostageRescued(this, hostage4)) forgetHostage(hostage4); -} - -bool cBot::isOnSameTeamAs(cBot *pBot) { - if (pBot == NULL) return false; - return pBot->iTeam == this->iTeam; -} - -bool cBot::wantsToBuyStuff() { - return buy_secondary == true || - buy_primary == true || - buy_ammo_primary == true || - buy_ammo_secondary == true || - buy_armor == true || - buy_defusekit == true || - buy_grenade == true || - buy_flashbang > 0; -} - -bool cBot::isUsingConsole() { - return console_nr > 0; -} - -bool cBot::shouldBeAbleToMove() { - return !isDead() && - !isFreezeTime() && - !shouldCamp() && - !shouldWait() && - !shouldActWithC4(); -// !isDucking() && -// !isJumping(); -} - -edict_t *cBot::getEntityBetweenMeAndCurrentPathNodeToHeadFor() { - TraceResult tr; - Vector vOrigin = pEdict->v.origin; - - tNode *node = NodeMachine.getNode(getCurrentPathNodeToHeadFor()); - - //Using TraceHull to detect de_aztec bridge and other entities. - //DONT_IGNORE_MONSTERS, we reached it only when there are no other bots standing in our way! - //UTIL_TraceHull(vOrigin, vNode, dont_ignore_monsters, point_hull, pBot->pEdict, &tr); - //UTIL_TraceHull(vOrigin, vNode, dont_ignore_monsters, human_hull, pBot->pEdict, &tr); - UTIL_TraceHull(vOrigin, node->origin, dont_ignore_monsters, head_hull, pEdict, &tr); - - // if nothing hit (else a wall is in between and we don't care about that): - if (tr.flFraction < 1.0) { - if (tr.pHit) { - return tr.pHit; - } - } - - return NULL; -} - -/** - * Get distance to the next node we're heading for - * @return - */ -float cBot::getDistanceToNextNode() { - tNode *node = NodeMachine.getNode(getCurrentPathNodeToHeadFor()); - if (node) { - return getDistanceTo(node->origin); - } - return MAP_MAX_SIZE; -} - -void cBot::setBodyToNode(int nodeIndex) { - tNode *node = NodeMachine.getNode(nodeIndex); - if (node) { - vBody = node->origin; - } -} - -void cBot::lookAtNode(int nodeIndex) { - tNode *node = NodeMachine.getNode(nodeIndex); - if (node) { - vHead = node->origin; - } -} - -/** - * Sets timer to allow movement to node, when timer expires we will think about severing the connection - * we used. - * @param timeInSeconds - */ -void cBot::setTimeToMoveToNode(float timeInSeconds) { - char msg[255]; - float endTime = gpGlobals->time + timeInSeconds; - sprintf(msg, "Set to %f so results into end time of %f", timeInSeconds, endTime); - rprint_trace("setTimeToMoveToNode", msg); - - this->nodeTimeIncreasedAmount = 0; - this->fMoveToNodeTime = endTime; -} - -/** - * Whatever was set, increase the time given in function param. This expands the time a bit. - * @param timeInSeconds - */ -void cBot::increaseTimeToMoveToNode(float timeInSeconds) { - if (nodeTimeIncreasedAmount < 2) { - nodeTimeIncreasedAmount++; - this->fMoveToNodeTime += timeInSeconds; - float timeToMoveToNodeRemaining = getMoveToNodeTimeRemaining(); - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "increaseTimeToMoveToNode with %f for the %d time, making time to move to node remaining %f.", timeInSeconds, nodeTimeIncreasedAmount, timeToMoveToNodeRemaining); - rprint_trace("increaseTimeToMoveToNode", msg); - } else { - rprint_trace("increaseTimeToMoveToNode", "Refused to increase time"); - } -} - -float cBot::getMoveToNodeTimeRemaining() { - return fMoveToNodeTime - gpGlobals->time; -} - -bool cBot::shouldCamp() { - return f_camp_time > gpGlobals->time; -} - -bool cBot::shouldWait() { - return f_wait_time > gpGlobals->time; -} - -void cBot::setTimeToWait(float timeInSeconds) { - this->f_wait_time = gpGlobals->time + timeInSeconds; -} - -bool cBot::shouldBeAbleToInteractWithButton() { - return fButtonTime < gpGlobals->time; -} - -bool cBot::hasButtonToInteractWith() { - return pButtonEdict != NULL; -} - -bool cBot::hasCurrentNode() { - return iCloseNode > -1; -} - -/** - * Shorthand method for creating path with flags PATH_NONE. - * @param destinationNode - * @return - */ -bool cBot::createPath(int destinationNode) { - return createPath(destinationNode, PATH_NONE); -} - -/** - * Attempts to create a path from current node to destination. Returns true on success, false on failure. - * @param destinationNode - * @param flags - * @return - */ -bool cBot::createPath(int destinationNode, int flags) { - if (destinationNode < 0 || destinationNode >= MAX_NODES) { - rprint("createPath()", "Unable to create path because destination node provided is < 0 or > MAX_NODES"); - return false; - } - - int currentNode = getCurrentNode(); - if (currentNode < 0) { - rprint("createPath()", "Unable to create path to destination because I am not close to a start node"); - return false; - } - - forgetPath(); - - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "Creating path from currentNode [%d] to destination node [%d]", currentNode, destinationNode); - rprint("createPath()", msg); - - return NodeMachine.createPath(currentNode, destinationNode, iBotIndex, this, flags); -} - -void cBot::doDuck() { - UTIL_BotPressKey(this, IN_DUCK); - this->f_hold_duck = gpGlobals->time + 0.5; - - this->increaseTimeToMoveToNode(0.5); -} - -bool cBot::isDucking() { - bool b = keyPressed(IN_DUCK) || this->f_hold_duck > gpGlobals->time; - if (b) { - rprint_trace("isDucking", "Yes I am ducking"); - } - return b; -} - -void cBot::doJump(Vector &vector) { - rprint_trace("doJump", "With vector"); - // stay focussed with body and head to this vector - this->vHead = vector; - this->vBody = vector; - - doJump(); -} - -void cBot::doJump() { - rprint_trace("doJump", "no vector"); - UTIL_BotPressKey(this, IN_JUMP); - this->f_jump_time = gpGlobals->time + 0.5; - - // duck like this, because doDuck increases node time *again*, so no - UTIL_BotPressKey(this, IN_DUCK); // DUCK jump by default - this->f_hold_duck = gpGlobals->time + 0.5; - - this->increaseTimeToMoveToNode(0.75); -} - -bool cBot::isJumping() { - bool b = keyPressed(IN_JUMP) || f_jump_time > gpGlobals->time; - if (b) { - rprint_trace("isJumping", "Yes I am jumping"); - } - return b; -} - -// $Log: bot.cpp,v $ -// Revision 1.21 2004/09/07 18:23:02 eric -// - bumped version to 3061 -// - adding Frashman code to buy the weapon as selected by Josh's code -// - Realbot is really the result of multiple people :-) -// -// Revision 1.20 2004/09/07 15:44:34 eric -// - bumped build nr to 3060 -// - minor changes in add2 (to add nodes for Bsp2Rbn utilities) -// - if compiled with USE_EVY_ADD, then the add2() function is used when adding -// nodes based on human players instead of add() -// - else, it now compiles mostly without warnings :-) -// -// Revision 1.19 2004/08/07 18:42:56 eric -// - bumped version to 3058 -// - added a cNodeMachine::add2 which should do the same job as ::add -// but it seems to work better. ::add2 is used by Bsp2Rbn only for now. -// - added the display of node flags (water, ladder, jump) next to the -// node position in 'debug nodes draw' -// - suppress the debugging information which displayed the hit entity -// while establishing neighbourhood -// -// Revision 1.18 2004/07/30 15:02:29 eric -// - jumped to version 3057 -// - improved readibility (wapen_tabel -> weapons_table) :-P -// - all Josh Borke modifications to the buying stuff: -// * using a switch() instead of several if -// * better buying code for shield and primary weapons -// * new command 'debug pistols 0/1' -// -// Revision 1.16 2004/07/17 21:32:01 eric -// - bumped version to 3055 -// - handling of es_ and as_ maps with new goals -// - added two debug commands: -// realbot debug goals -// realbot debug bots -// - added two nodes commands (for dedicated servers mainly) -// realbot nodes connect n1 n2 -// realbot nodes disconnect n1 n2 -// - slight modification in goal scoring (only reduced score when two bots of -// the SAME team select the same goal) -// -// Revision 1.15 2004/07/03 15:58:54 eric -// nova test comment for erics account -// -// Revision 1.14 2004/07/02 16:43:35 stefan -// - upped to build 3051 -// - changed log() into rblog() -// - removed BOT.CFG code that interpets old RB V1.0 commands -// - neater respons of the RealBot console -// - more help from RealBot console (ie, type realbot server broadcast ... with no arguments it will tell you what you can do with this, etc) -// - removed message "bot personality loaded from file" -// - in overal; some cleaning done, no extra features added -// -// Revision 1.13 2004/07/01 18:09:46 stefan -// - fixed skill 10 bots not causing memory bugger on re-adding (respawning) -// - added extra check for respawning bots so auto-add function cannot crash -// - fixed 2 nitpicks pointed out on the forums -// -// Revision 1.12 2004/06/25 07:39:00 stefan -// - upped to build 3050 -// - fixed reaction time (instant reaction time) bug -// - added evy's goals, but they are not used yet -// - fixed some radio responses here and there for swat behaviour. -// - swat leader automaticly assigned again when one dies -// - HINT: you can see any changes made by me, by looking at DD/MM/YY - Stefan (ie, 22/06/04 - Stefan, will let you find all changes i made that day) -// -// Revision 1.11 2004/06/23 08:24:14 stefan -// - upped to build 3049 -// - added swat behaviour (team leader assignment, radio response change and leaders command team-mates) - THIS IS EXPERIMENTAL AND DOES NOT ALWAYS WORK AS I WANT IT TO. -// - changed some conditions in nodemachine -// - sorry evy, still not added your new goals ;) will do next time, i promise -// -// Revision 1.10 2004/06/20 10:24:13 stefan -// - fixed another steep/stair thingy -// - changed a bit of the aiming code -// -// Revision 1.9 2004/06/19 21:06:14 stefan -// - changed distance check in nodemachine -// - fixed some 'steep' bug in nodemachine -// -// Revision 1.8 2004/06/17 21:23:23 stefan -// - fixes several connection problems with nodes. Going down from steep + crates (de_dust) PLUS going up/down from very steep slopes on as_oilrig.. 0wnage and thx to PMB and Evy -// - fixed chat bug in CS 1.6, its still CS 1.5 & CS 1.6 compatible though -// -// Revision 1.7 2004/06/13 20:08:21 stefan -// - 'bad score for goals' added -// - bmp dump info (Thanks Evy) -// - added 'realbot server players', so you can keep a server full at NR players at all times -// - slightly adjusted goal selection code -// - wander code disabled -// - lots of debug info introduced, do not use this source for REAL USAGE! -// -// Revision 1.6 2004/06/01 15:30:57 stefan -// *** empty log message *** -// -// Revision 1.5 2004/05/29 19:05:47 stefan -// - upped to BUILD 3044 -// - removed several debug messages on screen -// - changed default 'chatrate (max sentences)' to 3 -// - removed copyright notice, which is not valid due GPL license -// -// i know, nothing special :) -// -// Revision 1.4 2004/05/07 13:33:49 stefan -// added more comments, more neat code now -// -// Revision 1.3 2004/04/18 18:32:36 stefan -// - Restructured code a bit -// -// Revision 1.2 2004/04/18 17:39:19 stefan -// - Upped to build 2043 -// - REALBOT_PRINT() works properly now -// - Log() works properly now -// - Clearing in dll.cpp of reallog.txt at dll init -// - Logging works now, add REALBOT_PRINT() at every point you want to log something. -// +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com +/** + * RealBot : Artificial Intelligence + * Version : Work In Progress + * Author : Stefan Hendriks + * Url : http://realbot.bots-united.com + ** + * DISCLAIMER + * + * History, Information & Credits: + * RealBot is based partially upon the HPB-Bot Template #3 by Botman + * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) + * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And + * everybody else who helped me with this project. + * Storage of Visibility Table using BITS by Cheesemonster. + * + * Some portions of code are from other bots, special thanks (and credits) go + * to (in no specific order): + * + * Pierre Marie Baty + * Count - Floyd + * + * !! BOTS-UNITED FOREVER !! + * + * This project is open-source, it is protected under the GPL license; + * By using this source-code you agree that you will ALWAYS release the + * source-code with your project. + * + **/ + + + /* + + //========================================================= + // Returns if enemy can be shoot through some obstacle + //========================================================= + bool CBaseBot::IsShootableThruObstacle(Vector vecDest) + { + if (!WeaponShootsThru(m_iCurrentWeapon)) + return FALSE; + + Vector vecSrc = EyePosition(); + Vector vecDir = (vecDest - vecSrc).Normalize(); // 1 unit long + Vector vecPoint = g_vecZero; + int iThickness = 0; + int iHits = 0; + + edict_t *pentIgnore = pev->pContainingEntity; + TraceResult tr; + UTIL_TraceLine(vecSrc, vecDest, ignore_monsters, ignore_glass, pentIgnore, &tr); + + while (tr.flFraction != 1.0 && iHits < 3) + { + iHits++; + iThickness++; + vecPoint = tr.vecEndPos + vecDir; + while (POINT_CONTENTS(vecPoint) == CONTENTS_SOLID && iThickness < 64) + { + vecPoint = vecPoint + vecDir; + iThickness++; + } + UTIL_TraceLine(vecPoint, vecDest, ignore_monsters, ignore_glass, pentIgnore, &tr); + } + + if (iHits < 3 && iThickness < 64) + { + if (LengthSquared(vecDest - vecPoint) < 12544) + return TRUE; + } + + return FALSE; + } + + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bot.h" +#include "bot_weapons.h" +#include "bot_func.h" + +#include "game.h" +#include "NodeMachine.h" +#include "ChatEngine.h" + +#include +#include + +extern edict_t* pHostEdict; +extern int mod_id; +extern bool internet_play; +extern cGame Game; +extern cNodeMachine NodeMachine; +extern cChatEngine ChatEngine; +extern int counterstrike; +//static FILE *fp; +extern bool autoskill; + +/* Radio issue + Credit by Ditlew (NNBOT - Rest In Peace) */ +bool radio_message = false; +char* message = static_cast(malloc(64 * sizeof(char))); +char radio_messenger[30]; + +// random boundries +extern int random_max_skill; +extern int random_min_skill; +cBot bots[32]; // max of 32 bots in a game + +// External added variables +extern bool end_round; // End round + +/*#ifndef _WIN32 +#define snprintf std::snprintf //-V1059 +#endif*/ + +cBot::cBot() { + pBotHostage = nullptr; + fMoveToNodeTime = -1; + clearHostages(); +} + +/****************************************************************************** + Function purpose: Initializes bot vars on spawn + ******************************************************************************/ +void cBot::SpawnInit() { + rprint_trace("SpawnInit()", "START"); + + // ------------------------ + // TIMERS + // ------------------------ + fUpdateTime = gpGlobals->time; + fLastRunPlayerMoveTime = gpGlobals->time - 0.1f; + fButtonTime = gpGlobals->time; + fChatTime = gpGlobals->time + RANDOM_FLOAT(2.5f, 5.0f); + fMemoryTime = gpGlobals->time; + fDoRadio = gpGlobals->time; + const float freezeTimeCVAR = CVAR_GET_FLOAT("mp_freezetime"); + fNotStuckTime = gpGlobals->time + freezeTimeCVAR + 0.5f; + f_shoot_wait_time = gpGlobals->time; + f_goback_time = gpGlobals->time; + f_may_jump_time = gpGlobals->time; + fCheckHostageStatusTimer = gpGlobals->time; + f_defuse = gpGlobals->time; + f_allow_keypress = gpGlobals->time; + f_use_timer = gpGlobals->time; + f_light_time = gpGlobals->time; + f_sec_weapon = gpGlobals->time; + f_prim_weapon = gpGlobals->time; + f_gren_time = gpGlobals->time; + f_walk_time = gpGlobals->time; + f_hear_time = gpGlobals->time; + freezeTime = gpGlobals->time - 1; + f_cover_time = gpGlobals->time; + f_c4_time = gpGlobals->time; + f_update_weapon_time = gpGlobals->time; + f_follow_time = gpGlobals->time; + f_jump_time = 0.0f; + f_hold_duck = gpGlobals->time; + f_camp_time = gpGlobals->time; + f_wait_time = gpGlobals->time; + f_bot_see_enemy_time = gpGlobals->time; + f_bot_find_enemy_time = gpGlobals->time; + f_shoot_time = gpGlobals->time; + fMoveToNodeTime = -1; + nodeTimeIncreasedAmount = 0; + distanceMovedTimer = gpGlobals->time; + distanceMoved = 0; + fBlindedTime = gpGlobals->time; + f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.1f, 0.9f); + fWanderTime = gpGlobals->time; + f_strafe_time = gpGlobals->time; + + // Personality Related (these gets changed when loading personality file) + fpXOffset = 0.0f; + fpYOffset = 0.0f; + fpZOffset = 0.0f; + fpMinReactTime = 0.0f; + fpMaxReactTime = 0.0f; + + // ------------------------ + // POINTERS + // ------------------------ + pButtonEdict = nullptr; + pBotHostage = nullptr; + clearHostages(); + pEnemyEdict = nullptr; + pBreakableEdict = nullptr; + + // chat + std::memset(chChatSentence, 0, sizeof(chChatSentence)); + + + // ------------------------ + // INTEGERS + // ------------------------ + iGoalNode = -1; + goalIndex = -1; + iPreviousGoalNode = -1; + iCloseNode = -1; + iDiedNode = -1; + + iTeam = -1; + bot_class = -1; + i_camp_style = 0; + iPrimaryWeapon = -1; + iSecondaryWeapon = -1; + zoomed = ZOOM_NONE; + play_rounds = RANDOM_LONG(Game.GetMinPlayRounds(), Game.GetMaxPlayRounds()); + bot_health = 0; + prev_health = 0; + bot_armor = 0; + bot_weapons = 0; + bot_use_special = 0 + RANDOM_LONG(0, 2); + console_nr = 0; + pathIndex = -1; + iPathFlags = PATH_DANGER; + + // Smarter Stuck stuff + iDuckTries = 0; + iJumpTries = 0; + + // ------------------------ + // BOOLEANS + // ------------------------ + vip = UTIL_IsVip(pEdict); + bWalkKnife = false; + buy_ammo_primary = true; + buy_ammo_secondary = true; + buy_primary = !Game.bPistols; //30/07/04: Josh, handle the pistols only mode + buy_secondary = Game.bPistols; + buy_armor = false; + buy_defusekit = false; + bFirstOutOfSight = false; + buy_grenade = false; + buy_smokegrenade = false; + bIssuedInitialRadio = false; + + buy_flashbang = 0; + if (RANDOM_LONG(0, 100) < ipWalkWithKnife) { + bWalkKnife = true; + } + + if (UTIL_GetTeam(pEdict) == 1) { + if (RANDOM_LONG(0, 100) < ipBuyDefuseKit) { + buy_defusekit = true; + } + } + + if (RANDOM_LONG(0, 100) < ipBuyGrenade) { + buy_grenade = true; + } + + // 31.08.04 Frashman added Support for Smoke Grenade + if (RANDOM_LONG(0, 100) < ipBuySmokeGren) { + buy_smokegrenade = true; + } + + if (RANDOM_LONG(0, 100) < ipBuyFlashBang) { + buy_flashbang = 2; + + } + + if (RANDOM_LONG(0, 100) < 15 || Game.bPistols) + buy_secondary = true; + + // ------------------------ + // HUD + // ------------------------ + bHUD_C4_plantable = false; // Get's init'ed anyway... // BERKED + + // ------------------------ + // FLOATS + // ------------------------ + f_strafe_speed = 0.0f; + f_max_speed = CVAR_GET_FLOAT("sv_maxspeed"); + + // ------------------------ + // VECTORS + // ------------------------ + prevOrigin = Vector(9999.0, 9999.0, 9999.0); + lastSeenEnemyVector = Vector(0, 0, 0); + vEar = Vector(9999, 9999, 9999); + + // ------------------------ + // CHAR + // ------------------------ + arg1[0] = 0; + arg2[0] = 0; + arg3[0] = 0; + std::memset(&(current_weapon), 0, sizeof(current_weapon)); + std::memset(&(m_rgAmmo), 0, sizeof(m_rgAmmo)); + + rprint_trace("SpawnInit()", "END"); +} + +/****************************************************************************** + Function purpose: Initializes bot vars on new round + ******************************************************************************/ +void cBot::NewRound() { + rprint_trace("NewRound()", "START"); + + // ------------------------ + // TIMERS + // ------------------------ + fUpdateTime = gpGlobals->time; + fLastRunPlayerMoveTime = gpGlobals->time; + fCheckHostageStatusTimer = gpGlobals->time; + fButtonTime = gpGlobals->time; + fChatTime = gpGlobals->time + RANDOM_FLOAT(2.5f, 5.0f); + fMemoryTime = gpGlobals->time; + fDoRadio = gpGlobals->time; + const float freezeTimeCVAR = CVAR_GET_FLOAT("mp_freezetime"); + fNotStuckTime = gpGlobals->time + freezeTimeCVAR + 0.5f; + f_shoot_wait_time = gpGlobals->time; + f_goback_time = gpGlobals->time; + f_may_jump_time = gpGlobals->time; + f_defuse = gpGlobals->time; + f_allow_keypress = gpGlobals->time; + f_use_timer = gpGlobals->time; + f_light_time = gpGlobals->time; + f_sec_weapon = gpGlobals->time; + f_prim_weapon = gpGlobals->time; + f_gren_time = gpGlobals->time; + f_walk_time = gpGlobals->time; + f_hear_time = gpGlobals->time; + freezeTime = gpGlobals->time - 1; + f_cover_time = gpGlobals->time; + f_c4_time = gpGlobals->time; + f_update_weapon_time = gpGlobals->time; + f_follow_time = gpGlobals->time; + f_jump_time = 0.0f; + f_hold_duck = gpGlobals->time - 1; + f_camp_time = gpGlobals->time; + f_wait_time = gpGlobals->time; + f_bot_see_enemy_time = gpGlobals->time; + f_bot_find_enemy_time = gpGlobals->time; + f_shoot_time = gpGlobals->time; + fMoveToNodeTime = -1; + nodeTimeIncreasedAmount = 0; + distanceMovedTimer = gpGlobals->time; + distanceMoved = 0; + fBlindedTime = gpGlobals->time; + f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.1f, 0.9f); + fWanderTime = gpGlobals->time; + f_strafe_time = gpGlobals->time; + + // ------------------------ + // POINTERS + // ------------------------ + pButtonEdict = nullptr; + pBotHostage = nullptr; + clearHostages(); + pEnemyEdict = nullptr; + pBreakableEdict = nullptr; + + // ------------------------ + // INTEGERS + // ------------------------ + i_camp_style = 0; + iPrimaryWeapon = -1; + iSecondaryWeapon = -1; + zoomed = ZOOM_NONE; + bot_health = 0; + prev_health = 0; + bot_armor = 0; + // bot_weapons = 0; // <- stefan: prevent from buying new stuff every round! + console_nr = 0; + pathIndex = -1; + iGoalNode = -1; + goalIndex = -1; + iPreviousGoalNode = -1; + iCloseNode = -1; + + + // Smarter Stuck stuff + iDuckTries = 0; + iJumpTries = 0; + + if (RANDOM_LONG(0, 100) < ipFearRate) + iPathFlags = PATH_DANGER; + else + iPathFlags = PATH_NONE; + + // ------------------------ + // BOOLEANS + // ------------------------ + + // chat + std::memset(chChatSentence, 0, sizeof(chChatSentence)); + + vip = UTIL_IsVip(pEdict); + + // Every round consider + bWalkKnife = false; + + if (RANDOM_LONG(0, 100) < ipWalkWithKnife) + bWalkKnife = true; + + // Buying + buy_ammo_primary = true; + buy_ammo_secondary = true; + buy_primary = !Game.bPistols; + buy_grenade = false; + buy_smokegrenade = false; + buy_flashbang = 0; + buy_secondary = Game.bPistols; + buy_armor = false; + buy_defusekit = false; + + if (UTIL_GetTeam(pEdict) == 1) + if (RANDOM_LONG(0, 100) < ipBuyDefuseKit) + buy_defusekit = true; + + if (RANDOM_LONG(0, 100) < ipBuyArmour) + buy_armor = true; + + if (RANDOM_LONG(0, 100) < ipBuyGrenade) + buy_grenade = true; + + if (RANDOM_LONG(0, 100) < ipBuySmokeGren) + buy_smokegrenade = true; + + if (RANDOM_LONG(0, 100) < ipBuyFlashBang) + buy_flashbang = 2; + + + bFirstOutOfSight = false; + bIssuedInitialRadio = false; + + f_strafe_speed = 0.0f; + + // ------------------------ + // VECTORS + // ------------------------ + prevOrigin = Vector(9999.0f, 9999.0f, 9999.0f); + lastSeenEnemyVector = Vector(0, 0, 0); + vEar = Vector(9999, 9999, 9999); + + // ------------------------ + // CHAR + // ------------------------ + arg1[0] = 0; + arg2[0] = 0; + arg3[0] = 0; + + // initalize a few other stuff + NodeMachine.path_clear(iBotIndex); + iPathFlags = PATH_NONE; + + played_rounds++; + + // hello dudes + if (played_rounds == 1) { + // do some chatting + if (RANDOM_LONG(0, 100) < (ipChatRate + 10)) { + // we should say something now? + int iMax = -1; + + for (const char(&tc)[128] : ChatEngine.ReplyBlock[98].sentence) + { + if (tc[0] != '\0') + iMax++; + } + + const int the_c = RANDOM_LONG(0, iMax); + + if (the_c > -1 && iMax > -1) { + char chSentence[80] = {}; + snprintf(chSentence, sizeof(chSentence), "%s ", ChatEngine.ReplyBlock[98].sentence[the_c]); + PrepareChat(chSentence); + } + } + } + + clearHostages(); + clearHostageToRescueTarget(); + + rprint("NewRound", "Initialization new round finished"); +} + +/****************************************************************************** + Function purpose: Returns a random chat sentence and stores it into 'sentence' + ******************************************************************************/ +void cBot::PrepareChat(char sentence[128]) { + if (Game.iProducedSentences <= Game.iMaxSentences) { + // makes bot chat away + fChatTime = gpGlobals->time + RANDOM_FLOAT(0.1f, 2.0f); + std::strcpy(chChatSentence, sentence); // copy this + Game.iProducedSentences++; + } +} + +/****************************************************************************** + Function purpose: Return reaction time based upon skill + ******************************************************************************/ +float cBot::ReactionTime(const int iSkill) { + const float time = RANDOM_FLOAT(fpMinReactTime, fpMaxReactTime); + if (Game.messageVerbosity > 1) { + char msg[255]; + snprintf(msg, sizeof(msg), "minReactTime %f, maxReactTime %f, skill %d, results into %f", fpMinReactTime, fpMaxReactTime, iSkill, time); + rprint_trace("ReactionTime()", msg); + } + return time; +} + +/****************************************************************************** + Function purpose: Finds a (new) enemy + ******************************************************************************/ +int cBot::FindEnemy() { + // When on ladder, do not search for enemies + if (isOnLadder()) + return -1; + + // When blinded we cannot search for enemies + if (fBlindedTime > gpGlobals->time) + return -1; + float fNearestDistance = 9999; // Nearest distance + edict_t* pNewEnemy = nullptr; // New enemy found + + // SEARCH PLAYERS FOR ENEMIES + for (int i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + + // skip invalid players and skip self (i.e. this bot) + if (pPlayer && !pPlayer->free && pPlayer != pEdict) { + + // skip this player if not alive (i.e. dead or dying) + if (!IsAlive(pPlayer)) + continue; + + Vector vVecEnd = pPlayer->v.origin + pPlayer->v.view_ofs; + + // if bot can see the player... + if (FInViewCone(&vVecEnd, pEdict) && FVisible(vVecEnd, pEdict)) { + const int player_team = UTIL_GetTeam(pPlayer); + const int bot_team = UTIL_GetTeam(pEdict); + + if (player_team == bot_team) { + // do not target teammates + continue; + } + + // It's not a friend, track enemy + const float fDistance = (pPlayer->v.origin - pEdict->v.origin).Length(); + bool bCanSee = true; + + // The further away, the less chance we see this enemy + // Uncomment the following lines if you want to add distance-based visibility check + // if (RANDOM_FLOAT(0, 1.0) < (fDistance / 4096)) { + // bCanSee = false; + // } + + // If the bot carries a sniper, always consider the enemy visible + if (CarryWeaponType() != SNIPER) { + // bCanSee = true; + } + + if (fDistance < fNearestDistance && bCanSee) { + fNearestDistance = fDistance; + pNewEnemy = pPlayer; + } + } + } // valid player + } // FOR + + // We found a new enemy & the new enemy is different then previous pointer + if (pNewEnemy && pNewEnemy != pEnemyEdict) { + const int iCurrentNode = determineCurrentNode(); + + // Add 'contact' data + if (iCurrentNode > -1) { + NodeMachine.contact(iCurrentNode, UTIL_GetTeam(pEdict)); + } + + // We have a reaction time to this new enemy + rememberEnemyFound(); + f_shoot_time = gpGlobals->time + ReactionTime(bot_skill); + + const bool hadNoEnemy = pEnemyEdict == nullptr; + pEnemyEdict = pNewEnemy; // Update pointer + + // We did not have an enemy before + if (hadNoEnemy) { + rprint_trace("FindEnemy()", "Found new enemy"); + + // RADIO: When we found a NEW enemy but NOT via a friend + if (FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 3, "2", ""); + } + + // We found a new enemy + return 0; + } + + // we found an enemy that is newer/more dangerous then previous + rprint_trace("FindEnemy()", "Found 'newer' enemy"); + return 3; + } + + // nothing found + return -1; // return result +} + +void cBot::rememberEnemyFound() { + f_bot_find_enemy_time = gpGlobals->time + REMEMBER_ENEMY_TIME; +} + +/****************************************************************************** + Function purpose: Sets vHead to aim at vector + ******************************************************************************/ +void cBot::setHeadAiming(const Vector& vTarget) { + vHead = vTarget; +} + +/** + * Returns true / false whether enemy is alive. + * @return + */ +bool cBot::isEnemyAlive() const +{ + return IsAlive(pEnemyEdict); +} + +bool cBot::isSeeingEnemy() { + if (!hasEnemy()) { + this->rprint("canSeeEnemy called without having enemy?"); + return false; + } + + if (isBlindedByFlashbang()) { + return false; + } + Vector enemyBody = pEnemyEdict->v.origin; // Renamed from vBody to enemyBody - [APG]RoboCop[CL] + Vector enemyHead = pEnemyEdict->v.origin + pEnemyEdict->v.view_ofs; // Renamed from vHead to enemyHead + + const bool bodyInFOV = FInViewCone(&enemyBody, pEdict) && FVisible(enemyBody, pEdict); + const bool headInFOV = FInViewCone(&enemyHead, pEdict) && FVisible(enemyHead, pEdict); + if (bodyInFOV || headInFOV) { + return true; + } + return false; +} + +/****************************************************************************** + Function purpose: Aims at enemy, only when valid. Based upon skill how it 'aims' + ******************************************************************************/ +void cBot::AimAtEnemy() { + if (!hasEnemy()) + return; + + // We cannot see our enemy? -> bail out + if (isSeeingEnemy()) { + setHeadAiming(lastSeenEnemyVector); // look at last known vector of enemy + return; + } + + // Distance to enemy + const float fDistance = (pEnemyEdict->v.origin - pEdict->v.origin).Length() + 1; // +1 to make sure we never divide by zero + + // factor in distance, the further away the more deviation - which is based on skill + const int skillReversed = 10 - bot_skill + 1; + float fScale = 0.5f + fDistance / static_cast(64 * + skillReversed); // a good skilled bot is less impacted by distance than a bad skilled bot + + if (CarryWeaponType() == SNIPER) fScale *= 0.80f; // sniping improves aiming + + // Set target here + Vector vTarget; + if (bot_skill <= 1) + vTarget = pEnemyEdict->v.origin + pEnemyEdict->v.view_ofs * RANDOM_FLOAT(-0.5f, 1.1f); // aim for the head + else if (bot_skill < 4) // bot_skill > 1 is implied here? [APG]RoboCop[CL] + vTarget = pEnemyEdict->v.origin + + pEnemyEdict->v.view_ofs * RANDOM_FLOAT(-2.5f, 2.5f); // aim for the head more fuzzy + else + vTarget = pEdict->v.origin; // aim for body + + // Based upon how far, we make this fuzzy + float fDy, fDz; + float fDx = fDy = fDz = static_cast(bot_skill + 1) * fScale; + + // Example 1: + // Super skilled bot (bot_skill 1), with enemy of 2048 units away. Results into: + // skillReversed = (10 - 0 + 1) == 11 + // fScale = 2048 / (128 * 11) -> 2048 / 1408 => 1.454545 + // fd* = 0.5 + 1 * 1,95 + + // Example 2, less skilled bot (skill = 3) same enemy + // skillReversed = (10 - 3 + 1) == 8 + // fScale = 2048 / (128 * 8) -> 2048 / 1024 => 2 + // fd* = 3 * 2 + + vTarget = vTarget + Vector( + RANDOM_FLOAT(-fDx, fDx), + RANDOM_FLOAT(-fDy, fDy), + RANDOM_FLOAT(-fDz, fDz) + ); + + // Add Offset + fDx = fpXOffset; + fDy = fpYOffset; + fDz = fpZOffset; + + // increase offset with personality x,y,z randomly + vTarget = vTarget + Vector( + RANDOM_FLOAT(-fDx, fDx), + RANDOM_FLOAT(-fDy, fDy), + RANDOM_FLOAT(-fDz, fDz) + ); + + if (isHoldingGrenadeOrFlashbang()) { + // aim a bit higher + vTarget = vTarget + Vector(0, 0, 50); + } + + setHeadAiming(vTarget); +} + +bool cBot::isBlindedByFlashbang() const { + return fBlindedTime > gpGlobals->time; +} + +bool cBot::isHoldingGrenadeOrFlashbang() const { + return current_weapon.iId == CS_WEAPON_HEGRENADE || current_weapon.iId == CS_WEAPON_FLASHBANG; +} + +/****************************************************************************** + Function purpose: Perform fighting actions + ******************************************************************************/ +void cBot::FightEnemy() { + // We can see our enemy + if (!isBlindedByFlashbang() && isSeeingEnemy()) { + + // GET OUT OF CAMP MODE + f_camp_time = std::min(f_camp_time, gpGlobals->time); + + // Next time our enemy gets out of sight, it will be the 'first' time + // of all 'frame times'. + bFirstOutOfSight = false; + + // Remember last seen enemy position + lastSeenEnemyVector = pEnemyEdict->v.origin; // last seen enemy position + + // FIXME: Fix the darn zoom bug + // zoom in with sniper gun + if (CarryWeaponType() == SNIPER) { + if (zoomed < ZOOM_TWICE && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); + f_allow_keypress = gpGlobals->time + 0.7f; + zoomed++; + + if (zoomed > ZOOM_TWICE) + zoomed = ZOOM_NONE; + } + } else if (FUNC_BotHoldsZoomWeapon(this)) { + if (zoomed < ZOOM_ONCE && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); + f_allow_keypress = gpGlobals->time + 0.7f; + zoomed++; + } + } + + // NOT blinded by flashbang, try to find cover? + if (f_cover_time < gpGlobals->time) { + // COVER: Not taking cover now, fight using fightstyles. + + // when vip, we always take cover. + if (vip) { + // Camp, take cover, etc. + BOT_DecideTakeCover(this); + + if (FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 3, "3", ""); // need backup + } + } else { + // DECIDE: Should we take cover or not. + if (FUNC_ShouldTakeCover(this)) { + FindCover(); + } + } + } else { + + } + + // Keep timer updated for enemy + f_bot_find_enemy_time = gpGlobals->time + REMEMBER_ENEMY_TIME; + } + else // ---- CANNOT SEE ENEMY + { + if (f_bot_find_enemy_time < gpGlobals->time) { + pEnemyEdict = nullptr; + lastSeenEnemyVector = Vector(0, 0, 0); + rprint_trace("FightEnemy()", "Lost enemy out of sight, forgetting path and goal"); + forgetPath(); + forgetGoal(); + } else { + + // When we have the enemy for the first time out of sight + // we calculate a path to the last seen position + if (!bFirstOutOfSight) { + rprint_trace("FightEnemy()", "Enemy out of sight, calculating path towards it."); + // Only change path when we update our information here + const int iGoal = NodeMachine.getClosestNode(lastSeenEnemyVector, NODE_ZONE, pEdict); + if (iGoal > -1) { + setGoalNode(iGoal); + forgetPath(); + } + + bFirstOutOfSight = true; + } else { + if (!hasGoal()) { + rprint("Enemy out of sight and no goal, forgetting enemy"); + forgetEnemy(); + } + } + } + } // visible +} + +void cBot::pickWeapon(const int weaponId) { + UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(weaponId)); + f_c4_time = gpGlobals->time - 1; // reset C4 timer data + // give Counter-Strike time to switch weapon (animation, update state, etc) + f_update_weapon_time = gpGlobals->time + 0.7f; +} + +bool cBot::ownsFavoritePrimaryWeapon() const +{ + return hasFavoritePrimaryWeaponPreference() && isOwningWeapon(ipFavoPriWeapon); +} + +bool cBot::ownsFavoriteSecondaryWeapon() const +{ + return hasFavoriteSecondaryWeaponPreference() && isOwningWeapon(ipFavoSecWeapon); +} + +/** + * Returns true if bot has weapon (id) in possession + * @param weaponId + * @return + */ +bool cBot::isOwningWeapon(const int weaponId) const +{ + return bot_weapons & 1 << weaponId; +} + +/** + * Returns true if bot carries weapon right now + * @param weaponId + * @return + */ +bool cBot::isHoldingWeapon(const int weaponId) const +{ + return current_weapon.iId == weaponId; +} + +bool cBot::hasFavoritePrimaryWeaponPreference() const +{ + return ipFavoPriWeapon > -1; +} + +bool cBot::hasFavoriteSecondaryWeaponPreference() const +{ + return ipFavoSecWeapon > -1; +} + +bool cBot::canAfford(const int price) const //price muddled with weaponId? [APG]RoboCop[CL] +{ + return this->bot_money > price; +} + +/****************************************************************************** + Function purpose: Based upon several events pick the best weapon + ******************************************************************************/ +void cBot::PickBestWeapon() { + // does Timer allow to change weapon? (only when f_update_weapon_time < gpGlobals->time + if (f_update_weapon_time > gpGlobals->time) + return; + + // Distance to enemy + const float fDistance = func_distance(pEdict->v.origin, lastSeenEnemyVector); + + constexpr float knifeDistance = 300.0f; + + // ---------------------------- + // In this function all we do is decide what weapon to pick + // if we don't pick another weapon the current weapon is okay + // ---------------------------- + + // First we handle situations which are bad, no matter the distance + // or any other circumstance. + + // BAD: Carrying C4 or knife + if (CarryWeapon(CS_WEAPON_C4) || // carrying C4 + (CarryWeapon(CS_WEAPON_KNIFE) && fDistance > knifeDistance)) + { + // carrying knife and too far + if (hasPrimaryWeaponEquiped()) { + pickWeapon(iPrimaryWeapon); + return; + } + if (hasSecondaryWeaponEquiped()) { + pickWeapon(iSecondaryWeapon); + return; + } + } + + // At this point we do not update weapon information. And we did not 'switch back' to primary / secondary + if (hasEnemy() && !isSeeingEnemy()) { + // decision to pull HE grenade + if (isOwningWeapon(CS_WEAPON_HEGRENADE) && // we have a grenade + func_distance(pEdict->v.origin, lastSeenEnemyVector) < 900 && // we are close + func_distance(pEdict->v.origin, lastSeenEnemyVector) > 200 && // but not to close + RANDOM_LONG(0, 100) < 10 && // only randomly we pick a grenade in the heat of the battle + current_weapon.iId != CS_WEAPON_HEGRENADE && current_weapon.iId != CS_WEAPON_FLASHBANG && + f_gren_time + 15.0f < gpGlobals->time) // and dont hold it yet + { + UTIL_SelectItem(pEdict, "weapon_hegrenade"); // select grenade + f_wait_time = gpGlobals->time + 1.0f; // wait 1 second (stand still 1 sec) + f_gren_time = + gpGlobals->time + (1.0f + RANDOM_FLOAT(0.5f, 1.5f)); // and determine how long we should hold it + zoomed = ZOOM_NONE; // Counter-Strike resets zooming when choosing another weapon + return; + } + // OR we pull a flashbang? + if (isOwningWeapon(CS_WEAPON_FLASHBANG) && // we have a grenade + func_distance(pEdict->v.origin, lastSeenEnemyVector) < 200 && // we are close + func_distance(pEdict->v.origin, lastSeenEnemyVector) > 300 && // but not to close + RANDOM_LONG(0, 100) < 15 && // only randomly we pick a grenade in the heat of the battle + current_weapon.iId != CS_WEAPON_FLASHBANG && current_weapon.iId != CS_WEAPON_HEGRENADE && + f_gren_time + 15 < gpGlobals->time) // and dont hold it yet + { + UTIL_SelectItem(pEdict, "weapon_flashbang"); // select grenade + f_wait_time = gpGlobals->time + 1.0f; // wait 1 second (stand still 1 sec) + f_gren_time = + gpGlobals->time + (1.0f + RANDOM_FLOAT(0.5f, 1.5f)); // and determine how long we should hold it + zoomed = ZOOM_NONE; // Counter-Strike resets zooming when choosing another weapon + return; + } + } + + // When we are here, we did not decide to switch to grenade/flashbang. Now we look + // if the bot has to reload or switch weapon based upon ammo. + + // ---------------------------------------- + // More complex bad things that can happen: + // ---------------------------------------- + const int iTotalAmmo = current_weapon.iAmmo1; + const int iCurrentAmmo = current_weapon.iClip; + + //char msg[80]; + //sprintf(msg, "BOT: ICLIP %d, TOTALAMMO %d\n", iCurrentAmmo, iTotalAmmo); + + // Clip is out of ammo + if (iCurrentAmmo < 1 + && (CarryWeaponType() == PRIMARY || CarryWeaponType() == SECONDARY)) { + // Camp, take cover, etc. + BOT_DecideTakeCover(this); + + // We still have ammo! + if (iTotalAmmo > 0) { + UTIL_BotPressKey(this, IN_RELOAD); + f_update_weapon_time = gpGlobals->time + 0.7f; // update timer + // return; + } else { + // Thanks to dstruct2k for easy ctrl-c/v, i optimized the code + // a bit though. Btw, distance 600 is too far for slashing :) + + // at here the bot does not have ammo of the current weapon, so + // switch to another weapon. + if (iPrimaryWeapon > -1 && // we have a primary + current_weapon.iId != iPrimaryWeapon && // that's not the current, empty gun + func_distance(pEdict->v.origin, lastSeenEnemyVector) > 300) // and we are not close enough to knife + { + // select primary weapon + UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iPrimaryWeapon)); // select the primary + //return; + } else { + + if (iSecondaryWeapon > -1 && current_weapon.iId != iSecondaryWeapon && + // that's not the current, empty gun + func_distance(pEdict->v.origin, lastSeenEnemyVector) > 300) // and we are not close enough to knife + { + UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iSecondaryWeapon)); // select the secondary + //return; + } else { + if (isOwningWeapon(CS_WEAPON_KNIFE) && // we have a knife (for non-knife maps) + !isHoldingWeapon(CS_WEAPON_KNIFE)) // but we do not carry it + { + UTIL_SelectItem(pEdict, "weapon_knife"); + //return; + } + } + } // end if + } // no ammo + } +} + +/****************************************************************************** + Function purpose: Fire weapon (burst; or do not fire when not allowed) + ******************************************************************************/ +void cBot::FireWeapon() { + // We may not shoot! + if (f_shoot_time > gpGlobals->time || + f_update_weapon_time > gpGlobals->time) + return; + + if (pBreakableEdict == nullptr && !isSeeingEnemy()) { + return; + } + + // ------------------------------------------------------------ + float fDistance = 50.0f; + + if (hasEnemy()) { + fDistance = func_distance(pEdict->v.origin, pEnemyEdict->v.origin); + } + else if (pBreakableEdict != nullptr) { + fDistance = func_distance(pEdict->v.origin, VecBModelOrigin(pBreakableEdict)); + } + + // Depending on weapon type + if (CarryWeaponType() == SECONDARY) { + // We may shoot, use shooting rate. + // TODO TODO TODO; Add shooting rates in BUYTABLE.INI + + if (f_sec_weapon < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK); + f_sec_weapon = gpGlobals->time + RANDOM_FLOAT(0.05f, 0.2f); + } + + } else if (CarryWeaponType() == PRIMARY) { + // We may shoot, use shooting rate. + // TODO TODO TODO: Add shooting rates in BUYTABLE.INI + if (f_prim_weapon < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK); // Hold fire + // All other weapons, the more distance, the more time we add to holding weapon + if (f_shoot_wait_time < gpGlobals->time) { + // AK, COLT, STEYR AUG, SIG SG552 only when enough skill! + if ((CarryWeapon(CS_WEAPON_AK47) || CarryWeapon(CS_WEAPON_M4A1) + || CarryWeapon(CS_WEAPON_SG552) || CarryWeapon(CS_WEAPON_AUG)) + && bot_skill < 3) { + float f_burst = (2048 / fDistance) + 0.1f; + f_burst = std::max(f_burst, 0.1f); + f_burst = std::min(f_burst, 0.4f); + + // CS 1.6 less burst + if (counterstrike == 1) + f_burst = std::min(f_burst, 0.3f); + + f_prim_weapon = gpGlobals->time + f_burst; + + f_shoot_wait_time = gpGlobals->time + (f_burst * 3); + } else // other weapons + { + float f_burst = 0.1f; + if (fDistance > 300 && bot_skill < 6) { + f_burst = (fDistance - 300) / 550; + if (f_burst < 0.1f) + f_burst = 0.0f; + f_burst = std::min(f_burst, 0.7f); + + // CS 1.6 less burst + if (counterstrike == 1) + f_burst = std::min(f_burst, 0.2f); + if (f_prim_weapon < gpGlobals->time) + f_prim_weapon = gpGlobals->time + f_burst; + } + f_shoot_wait_time = + gpGlobals->time + f_burst + RANDOM_FLOAT(0.2f, 0.7f); + } + } + } // give the bot alteast 0.3 seconds to fire its weapon + } // PRIMARY + else if (CarryWeaponType() == GRENADE) { + if (f_gren_time > gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK); // Hold fire + setMoveSpeed(f_max_speed / 2); + + // Set new goal when holding flashbang! + if (current_weapon.iId == CS_WEAPON_FLASHBANG) { + + //tonode ? + // COVER: Take cover, using tracelines all the time! + FindCover(); + } + } else if (f_gren_time + 0.5f < gpGlobals->time) { + // NOTE: Should not happen, a bot cannot 'forget' this... + f_gren_time = gpGlobals->time + 1; + } + } // GRENADE + else if (CarryWeaponType() == KNIFE) { + setMoveSpeed(f_max_speed); + UTIL_BotPressKey(this, IN_ATTACK); // Hold fire + } // KNIFE + else if (CarryWeaponType() == SNIPER) { + setMoveSpeed(f_max_speed / 2); + UTIL_BotPressKey(this, IN_ATTACK); // Hold fire + f_shoot_time = gpGlobals->time + 1.0f; + } // SNIPER + else if (CarryWeaponType() == SHIELD) { + if (fDistance > 550) { + if (hasShieldDrawn()) { + // when the enemy is far away, we keep it + } else { + // draw shield! + UTIL_BotPressKey(this, IN_ATTACK2); // secondary attack makes shield draw + f_allow_keypress = gpGlobals->time + 0.7f; + } + } else { + // get weapon here. + if (hasShieldDrawn() && f_allow_keypress < gpGlobals->time) { + rblog + ("BOT: Enemy is close enough, i should withdraw shield to attack this enemy\n"); + UTIL_BotPressKey(this, IN_ATTACK2); + f_allow_keypress = gpGlobals->time + 0.7f; + } + } + } else { + // debug print + REALBOT_PRINT(this, "FireWeapon()", "Unknown weapon"); + } +} + +/****************************************************************************** + Function purpose: The combat brain of the bot ( called by Think() ) + ******************************************************************************/ +void cBot::Combat() { + if (pBreakableEdict != nullptr) { + FUNC_AttackBreakable(this); + return; + } + + if (!hasEnemy()) { + rprint("Unexpected call to Combat because bot has no enemy!"); + return; + } + + // Bot is on ladder + if (isOnLadder()) { + // When on a ladder, the bot is vulnerable. + // It should stop moving and try to fight back if it can see the enemy. + stopMoving(); + + if (isSeeingEnemy()) { + // Switch to a pistol if available, as it's more accurate on ladders. + if (hasSecondaryWeaponEquiped() && CarryWeaponType() != SECONDARY) { + pickWeapon(iSecondaryWeapon); + } + + // Aim and fire at the enemy. + AimAtEnemy(); + FireWeapon(); + } + + // Do not proceed with normal ground combat logic. + return; + } + + // We have an enemy and it is now dead + if (!isEnemyAlive()) { + + // radio (Enemy down) + if (FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 3, "9", ""); + } + + // get bot pointer + const cBot* checkpointer = UTIL_GetBotPointer(pEnemyEdict); + + // This bot killed a human; adjust skill when 'autoskill' is on. + if (checkpointer == nullptr) { + + // increase bot_skill value when autoskill enabled (making bot weaker) + if (autoskill && bot_skill < 10) { + bot_skill++; + } + + if (Game.iDeathsBroadcasting != BROADCAST_DEATHS_NONE) { + // This is a human, we will tell this human he has been killed + // by a bot. + const int r = RANDOM_LONG(150, 255); + const int g = RANDOM_LONG(30, 155); + const int b = RANDOM_LONG(30, 155); + char msg[128]; + if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_FULL) { + snprintf(msg, sizeof(msg), "A RealBot has killed you!\n\nName:%s\nSkill:%d\n", name, bot_skill); + } else { + snprintf(msg, sizeof(msg), "A RealBot named %s has killed you!", name); + } + + HUD_DrawString(r, g, b, msg, pEnemyEdict); + } + } + + // clear the pointer for this and other bots that might have the same pEnemyEdict + FUNC_ClearEnemyPointer(pEnemyEdict); + + // from here react after kill... + forgetGoal(); + forgetPath(); + + if (lastSeenEnemyVector != Vector(0, 0, 0)) { + vHead = lastSeenEnemyVector; + } + + lastSeenEnemyVector = Vector(0, 0, 0); + + // random waiting + f_wait_time = gpGlobals->time + (1 + RANDOM_FLOAT(0.0f, 0.4f)); + + // keep on walking when afraid (perhaps there are more enemies) + if (RANDOM_LONG(0, 100) < ipFearRate) + f_walk_time = gpGlobals->time + (1 + RANDOM_FLOAT(0.0f, 2.0f)); + + InteractWithPlayers(); // check any new enemy here immediately + + return; + } + + // ----------- combat + + // STEP 1: Pick best weapon to fight with + PickBestWeapon(); + + // STEP 2: Decide how to move to make us a harder target + FightEnemy(); + + // STEP 3: Aim at enemy (skill-based) + AimAtEnemy(); + + // STEP 4: Fire! + FireWeapon(); +} + +/****************************************************************************** + Function purpose: Find cover + Note: Using tracelines to get a cover node. + ******************************************************************************/ +void cBot::FindCover() { + TraceResult tr; + const Vector dest = lastSeenEnemyVector; + // Vector start = pEdict->v.origin; + // Vector end; + Vector cover_vect = Vector(9999, 9999, 9999); + + // TraceLines in 2 directions to find which way to go... + UTIL_MakeVectors(pEdict->v.v_angle); + const Vector v_src = pEdict->v.origin + pEdict->v.view_ofs; + Vector v_right = v_src + gpGlobals->v_right * 90; + Vector v_left = v_src + gpGlobals->v_right * -90; + + // We have now our first 'left' and 'right' + + // First check the right.. + UTIL_TraceLine(v_src, v_right, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); + + if (tr.flFraction >= 1.0f) { + // We can see it + // Now trace from that vector to our threat + UTIL_TraceLine(v_right, dest, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); + + // If this is blocking.. then its a good wpt + if (tr.flFraction < 1.0f) + cover_vect = v_right; + } + + // Now check at the left + UTIL_TraceLine(v_src, v_left, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); + + if (tr.flFraction >= 1.0f) { + // We can see it + // Now trace from that vector to our threat + UTIL_TraceLine(v_left, dest, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); + + // If this is blocking.. then its a good wpt + if (tr.flFraction < 1.0f) { + // If we already found a wpt, then randomly pick this one + if (cover_vect != Vector(9999, 9999, 9999)) { + if (RANDOM_LONG(0, 100) < 50) + cover_vect = v_left; + } else + cover_vect = v_left; + } + } + // Now update the V_left and V_right and do the checks again. + // Vector old_right = v_right; + // Vector old_left = v_left; + v_right = v_src + gpGlobals->v_right * 180; + v_left = v_src + gpGlobals->v_right * -180; + + // Now check at the right again + UTIL_TraceLine(v_src, v_right, dont_ignore_monsters, + pEdict->v.pContainingEntity, &tr); + + if (tr.flFraction >= 1.0f) { + // We can see it + // Now trace from that vector to our threat + UTIL_TraceLine(v_right, dest, dont_ignore_monsters, + pEdict->v.pContainingEntity, &tr); + + // If this is blocking.. then its a good wpt + if (tr.flFraction < 1.0f) { + // If we already found a wpt, then randomly pick this one + if (cover_vect != Vector(9999, 9999, 9999)) { + if (RANDOM_LONG(0, 100) < 50) + cover_vect = v_right; + } else + cover_vect = v_right; + } + } + + // Now check at the left + UTIL_TraceLine(v_src, v_left, dont_ignore_monsters, + pEdict->v.pContainingEntity, &tr); + if (tr.flFraction >= 1.0f) { + // We can see it + // Now trace from that vector to our threat + UTIL_TraceLine(v_left, dest, dont_ignore_monsters, + pEdict->v.pContainingEntity, &tr); + + // If this is blocking.. then its a good wpt + if (tr.flFraction < 1.0f) { + // If we already found a wpt, then randomly pick this one + if (cover_vect != Vector(9999, 9999, 9999)) { + if (RANDOM_LONG(0, 100) < 50) + cover_vect = v_left; + } else + cover_vect = v_left; + } + } + + const int iNodeEnemy = NodeMachine.getClosestNode(pEnemyEdict->v.origin, 60, pEnemyEdict); + const int iNodeFrom = NodeMachine.getClosestNode(pEdict->v.origin, NODE_ZONE, pEdict); + + // -------------- + // TEST TEST TEST + // -------------- + const int iCoverNode = NodeMachine.node_cover(iNodeFrom, iNodeEnemy, pEdict); + bool bTakenCover = false; + + if (iCoverNode > -1) { + rprint("FindCover()", "cover node found (node based)"); + setGoalNode(iCoverNode); + forgetPath(); + + // Calculate a path to this position and get the heck there. + createPath(iCoverNode); + f_cover_time = gpGlobals->time + 8.0f; + bTakenCover = true; + } else { + + // -------------------------------------------------- + // If cover_vect is found, we find a node close to it + // -------------------------------------------------- + if (cover_vect != Vector(9999, 9999, 9999)) { + rprint("FindCover()", "cover node found (cover_vect based)"); + const int iNodeCover = NodeMachine.getClosestNode(cover_vect, 60, pEdict); + if (iNodeCover > -1) { + setGoalNode(iNodeCover); + forgetPath(); + + // Calculate a path to this position and get the heck there. + rprint("createPath -> find cover node"); + NodeMachine.createPath(iNodeFrom, iNodeCover, iBotIndex, this, PATH_NONE); + f_cover_time = gpGlobals->time + 8; + bTakenCover = true; + } + } + } + + // when we have taken cover, and we are leader, command our team to get + // into our position to cover area + if (bTakenCover) { + // do something... + } + +} // FindCover() + +void cBot::InteractWithFriends() { + + + // TODO TODO TODO; make this thing really work + //return; + + // We interact with our players in some way + // + // When a bot is camping, another bot can choose to say 'go go go' for example. + // + // + + for (int i = 1; i <= gpGlobals->maxClients; i++) { + + edict_t* pPlayer = INDEXENT(i); + + // skip invalid players and skip self (i.e. this bot) + if (pPlayer && !pPlayer->free && pPlayer != pEdict) { + // skip this player if not alive (i.e. dead or dying) + if (!IsAlive(pPlayer)) + continue; + + // skip enemies + if (UTIL_GetTeam(pPlayer) != UTIL_GetTeam(pEdict)) + continue; + + bool bCanSeePlayer = false; + bool bClose = false; + + Vector vVecEnd = pPlayer->v.origin + pPlayer->v.view_ofs; + + if (func_distance(pPlayer->v.origin, pEdict->v.origin) < 450) + bClose = true; + + if (FInViewCone(&vVecEnd, pEdict) && FVisible(vVecEnd, pEdict)) + bCanSeePlayer = true; + + // there are tons of cases + const cBot* pBotPointer = UTIL_GetBotPointer(pPlayer); + + // It is a fellow bot + if (pBotPointer != nullptr) { + if (bClose) { + if (pBotPointer->f_camp_time > gpGlobals->time + && pBotPointer->f_camp_time - 10 < gpGlobals->time + && pBotPointer->pEnemyEdict == nullptr + && (RANDOM_LONG(0, 100) < ipCampRate + && FUNC_DoRadio(this))) { + // issue go go go + UTIL_BotRadioMessage(this, 2, "1", ""); // go go go! + } + } + + if (bCanSeePlayer) {} + } else // it is a teammate, but it is human (or a different bot) + { + // when firing + + } + + // any player: + if (bClose) { + // some one is close, need backup? + if (RANDOM_LONG(0, 100) < ipFearRate && pEnemyEdict != nullptr) + if (FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 3, "3", ""); // need backup + } + } + } + } + +} + +// BOT: Interact with Players ('find enemy, and how to react upon them') +void cBot::InteractWithPlayers() { + + // friends are important, we are a team dudes! + InteractWithFriends(); + + const int result = FindEnemy(); + + // ------------------------------- + // RESULT < 0; NO ENEMY FOUND + // ------------------------------- + + // No enemy found, unzoom + if (result < 0) { + // Keep f_prim_weapon updated, else we do burst immidiatly + if (CarryWeaponType() == SNIPER) { + + // Unzoom (for sniper guns) + if (zoomed > ZOOM_NONE && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); + f_allow_keypress = gpGlobals->time + 0.7f; + zoomed++; + } + if (zoomed > ZOOM_TWICE) + zoomed = ZOOM_NONE; + } else if (FUNC_BotHoldsZoomWeapon(this)) { + + // Unzoom (for other guns with only 1 zoom) + if (zoomed > ZOOM_NONE && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); + f_allow_keypress = gpGlobals->time + 0.7f; + zoomed = ZOOM_NONE; + } + } else { + + // For any weapon that has a silencer (the colt for example), use it if we want that. + if (isHoldingWeapon(CS_WEAPON_M4A1)) + if (bot_use_special == 0 && zoomed == ZOOM_NONE + && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); + zoomed = ZOOM_ONCE; + } + } + } + // ------------------------------------------------ + // RESULT > -1 ; ENEMY FOUND / NO SPECIFIC REACTION + // ------------------------------------------------ + else { // result > -1 + + // VIP: When we found an enemy, we have a problem. + if (vip) { + + // We do not forget our enemy, but we will try to get the heck out of here. + // A VIP bot should prioritize survival. Upon spotting an enemy, it should + // immediately seek cover and call for backup. This increases the chances of + // the VIP surviving the encounter. + FindCover(); // Find a suitable cover position away from the enemy. + if (FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 3, "3", ""); // Radio for backup. + } + + } + // Whenever we hold a knife, get our primary weapon + if (CarryWeapon(CS_WEAPON_KNIFE)) { + + // switch back to primary + if (iPrimaryWeapon > -1) + UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iPrimaryWeapon)); + + else // pick secondary + UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(iSecondaryWeapon)); + + f_update_weapon_time = gpGlobals->time + 0.7f; + } + } + + // ------------------------------------------------ + // RESULT = 0 ; NEW ENEMY FOUND + // ------------------------------------------------ + if (result == 0) { + // First Encounter + //f_prim_weapon = gpGlobals->time; + if (CarryWeaponType() == SNIPER) { + if (zoomed < ZOOM_TWICE && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); + f_allow_keypress = gpGlobals->time + 0.7f; + zoomed++; + } + } + + // INITIALIZATION: + const int iGoal = NodeMachine.getClosestNode(pEnemyEdict->v.origin, NODE_ZONE, pEnemyEdict); + if (iGoal > -1) { + rprint_trace("InteractWithPlayers()", "Found a new enemy, setting goal and forgetting path"); + setGoalNode(iGoal); + forgetPath(); + } + + // Speed our enemy runs + // int run_speed = FUNC_PlayerSpeed(pBot->pBotEnemy); + // Distance between Us and Enemy. + // float f_distance = func_distance(pBot->pEdict->v.origin, + // pBot->pBotEnemy->v.origin); + + // Does our enemy (when a bot) has focus on us? + bool focused; + const cBot* playerbot = UTIL_GetBotPointer(pEnemyEdict); + if (playerbot) { + if (playerbot->pEnemyEdict == pEdict) + focused = true; + } + else // Its a human + { + + // When we are in his 'sight' of 25 degrees , we are pretty + // much focussed for a first encounter. + if (FUNC_InFieldOfView + (pEdict, (pEnemyEdict->v.origin - pEdict->v.origin)) < 25) + focused = true; + } + + /****************************** + At this moment we know: + - The distance between us and enemy + - The focus (are we targetted too?) + - The speed of the enemy (running, standing still? etc) + *******************************/ + } // We have a first encounter + + // ------------------------------------------------ + // RESULT = 3 ; NEWER ENEMY FOUND + // ------------------------------------------------ + if (result == 3) { + // + // Newer enemy found, update goals and such, but thats all! + // + + // INITIALIZATION: + const int iGoal = NodeMachine.getClosestNode(pEnemyEdict->v.origin, NODE_ZONE, pEnemyEdict); + + if (iGoal > -1) { + rprint_trace("InteractWithPlayers()", "Found a *newer* enemy, so picking goal node to that"); + setGoalNode(iGoal); + forgetPath(); + } + } +} + +// BOT: INTERACT WITH PLAYERS +void cBot::JoinTeam() { + if (mod_id != CSTRIKE_DLL) return; + // When bot plays Counter-Strike (only Counter-Strike is supported) + + char c_class[32]; + + // Choose team first + if (start_action == MSG_CS_TEAM_SELECT) { + char c_team[32]; + start_action = MSG_CS_IDLE; // switch back to idle + + // in case of bad state/input fall-back to 'pick one for me' + if (iTeam != 1 && iTeam != 2 && iTeam != 5) { + iTeam = 5; + } + + // select the team the bot wishes to join... + if (iTeam == 1) { + std::strcpy(c_team, "1"); + } + else if (iTeam == 2) { + std::strcpy(c_team, "2"); + } + else { + std::strcpy(c_team, "5"); + } + + // choose + FakeClientCommand(this->pEdict, "menuselect", c_team, nullptr); + + return; + } + + // counter terrorist menu, which class/outfit? + if (start_action == MSG_CS_CT_SELECT) { + start_action = MSG_CS_IDLE; // switch back to idle + + if (bot_class < 1 || bot_class > 4) + bot_class = 5; // use random if invalid + + // Since cs 1.6 does not give us pretty random models + // we do it ourselves + if (bot_class == 5) { + bot_class = RANDOM_LONG(1, 4); + } + + // select the class the bot wishes to use... + if (bot_class == 1) + std::strcpy(c_class, "1"); + else if (bot_class == 2) + std::strcpy(c_class, "2"); + else if (bot_class == 3) + std::strcpy(c_class, "3"); + else if (bot_class == 4) + std::strcpy(c_class, "4"); + else + std::strcpy(c_class, "5"); // random + + FakeClientCommand(this->pEdict, "menuselect", c_class, nullptr); + + // bot has now joined a team + hasJoinedTeam = true; + + return; + } + + // terrorist select + if (start_action == MSG_CS_T_SELECT) { + start_action = MSG_CS_IDLE; // switch back to idle + + if (bot_class < 1 || bot_class > 4) + bot_class = 5; // use random if invalid + + // Since cs 1.6 does not give us pretty random models + // we do it ourselves + if (bot_class == 5) { + bot_class = RANDOM_LONG(1, 4); + } + + // select the class the bot wishes to use... + if (bot_class == 1) + std::strcpy(c_class, "1"); + else if (bot_class == 2) + std::strcpy(c_class, "2"); + else if (bot_class == 3) + std::strcpy(c_class, "3"); + else if (bot_class == 4) + std::strcpy(c_class, "4"); + else + std::strcpy(c_class, "5"); // random + + FakeClientCommand(this->pEdict, "menuselect", c_class, nullptr); + + // bot has now joined the game (doesn't need to be started) + hasJoinedTeam = true; + + //return; + } +} + +vec_t cBot::ReturnTurnedAngle(float speed, float current, const float ideal) { + + // hope this fix the unnescesary turning of bots. + // how? we save the values here, andc alculate the new value. + // this part is copied from botchangeyaw/pitch so it SHOULD work :) + float current_180; // current +/- 180 degrees + + // turn from the current v_angle pitch to the idealpitch by selecting + // the quickest way to turn to face that direction + + // find the difference in the current and ideal angle + const float diff = std::fabs(current - ideal); + + // check if the bot is already facing the idealpitch direction... + if (diff <= 1.0f) + return current; // return number of degrees turned + + // check if difference is less than the max degrees per turn + speed = std::min(diff, speed); // just need to turn a little bit (less than max) + + // here we have four cases, both angle positive, one positive and + // the other negative, one negative and the other positive, or + // both negative. handle each case separately... + if (current >= 0.0f && ideal >= 0.0f) // both positive + { + if (current > ideal) + current -= speed; + + else + current += speed; + } else if (current >= 0.0f && ideal < 0.0f) { + current_180 = current - 180.0f; + if (current_180 > ideal) + current += speed; + + else + current -= speed; + } else if (current < 0 && ideal >= 0) { + current_180 = current + 180; + if (current_180 > ideal) + current += speed; + + else + current -= speed; + } else // (current < 0) && (ideal < 0) both negative + { + if (current > ideal) + current -= speed; + + else + current += speed; + } + + // check for wrap around of angle... + if (current > 180) + current -= 360; + if (current < -180) + current += 360; + return current; // return what it should be +} + +// BOT: sub-function (DEFUSE) for ACT() +bool cBot::Defuse() { + if (!isCounterTerrorist()) // non-Counter-Terrorists have no business here + return false; + + // this bot is defusing + if (shouldActWithC4() && keyPressed(IN_USE)) { + setTimeToMoveToNode(3); + return true; + } + + // What i do, i search for the c4 timer, store its origin and check + // if this bot is close. If so, the bot should be defusing the bomb + // if the timers are set. The above check makes sure that no other + // bot will be defusing the bomb. + edict_t* pent = nullptr; + while ((pent = UTIL_FindEntityByClassname(pent, "grenade")) != nullptr) { + if (UTIL_GetGrenadeType(pent) == 4) { // It is a C4 + break; + } + } + + if (pent == nullptr) { + rprint_normal("Defuse()", "No C4 planted yet"); + return false; + } + + rprint_normal("Defuse()", "C4 is planted!"); + + // A c4 has been found, oh dear. + // Remember, pent=c4 now! + + // Calculate the distance between our position to the c4 + assert(pent != nullptr); + const Vector vC4 = pent->v.origin; + + const float distance = func_distance(pEdict->v.origin, vC4); + + // can see C4 + const bool canSeeC4 = canSeeVector(vC4, pent); + + if (!canSeeC4) { + rprint_trace("Defuse()", "Cannot see planted C4 - bailing"); + return false; + } + + // it can be seen, so it has been discovered + if (!Game.isPlantedC4Discovered()) { + this->rprint_trace("Defuse()", "C4 is discovered, remembering its coordinates"); + Game.vPlantedC4 = vC4; + } + + // We can do 2 things now + // - If we are not close, we check if we can walk to it, and if so we face to the c4 + // - If we are close, we face it and (begin) defuse the bomb. + constexpr int distanceForC4ToBeInReach = 70; + if (distance < distanceForC4ToBeInReach) { + vHead = vC4; + vBody = vC4; + + setTimeToMoveToNode(3); // we are going to do non-path-follow stuff, so keep timer updated + const int angle_to_c4 = FUNC_InFieldOfView(pEdict, (vC4 - pEdict->v.origin)); + + // if defusion timer has not been set (ie, the bot is not yet going to defuse the bomb) + if (f_defuse < gpGlobals->time && angle_to_c4 < 35) { + this->rprint("Defuse()", "I'll start defusing the bomb"); + // when we are 'about to' defuse, we simply set the timers + f_defuse = gpGlobals->time + 90; // hold as long as you can + f_allow_keypress = gpGlobals->time + 1.5f; // And stop any key pressing the first second + // ABOUT TO DEFUSE BOMB + } + + // Defusion timer is set and c4 is within vision + if (f_defuse > gpGlobals->time && angle_to_c4 < 35) { + this->rprint("Defuse()", "I'm defusing the bomb"); + setMoveSpeed(0.0); + f_c4_time = gpGlobals->time + 6; + UTIL_BotPressKey(this, IN_DUCK); + + if (func_distance(pEdict->v.origin, vC4) > 50 + && f_allow_keypress + 0.5f > gpGlobals->time) { + setMoveSpeed(f_max_speed / 2); + } + } + + if (f_allow_keypress < gpGlobals->time && f_defuse > gpGlobals->time) { + UTIL_BotPressKey(this, IN_USE); + } + + } else { + rprint_trace("Defuse()", "I can see C4, but it is out of reach."); + const int iC4Node = NodeMachine.getClosestNode(vC4, distanceForC4ToBeInReach, nullptr); + if (iC4Node < 0) { + rprint_normal("Defuse()", "No node close, so just look at it/body face at it and move towards it."); + vHead = vC4; + vBody = vC4; + } + + if (iC4Node > -1) { + // we are not heading for this goal yet + if (getGoalNode() != iC4Node) { + rprint_normal("Defuse()", "I don't have a goal towards the C4, overriding it now to C4 destination!"); + forgetPath(); + forgetGoal(); + setGoalNode(iC4Node); + } else { + rprint_normal("Defuse()", "I already have a goal towards the C4!"); + } + } else { + rprint_normal("Defuse()", "C4 is somewhere without a close node."); + } + setMoveSpeed(f_max_speed); + } // distance < ... + + // we can see the bomb, and we act upon it + return true; +} + +int cBot::keyPressed(const int key) const { + return pEdict->v.button & key; +} + +// BOT: Act +void cBot::Act() { + // chat + if (fChatTime < gpGlobals->time) { + if (chChatSentence[0] != '\0') { + UTIL_SayTextBot(chChatSentence, this); + std::memset(chChatSentence, 0, sizeof(chChatSentence)); + } + } + + // camp + if (f_camp_time > gpGlobals->time) { + // When camping we duck and we don't move + UTIL_BotPressKey(this, IN_DUCK); + + + setMoveSpeed(0.0f); // do not move + PickBestWeapon(); // pick weapon, do not stare with knife + + // when dropped C4 and CT we look at C4 + if (isCounterTerrorist() && Game.vDroppedC4 != Vector(9999, 9999, 9999)) { + // look at dropped C4 + if (EntityIsVisible(pEdict, Game.vDroppedC4)) + vHead = Game.vDroppedC4; + else { + if (iGoalNode > -1) + { + forgetPath(); + forgetGoal(); + vHead = vBody = NodeMachine.node_vector(iGoalNode); + } + else { + vHead = vBody = Game.vDroppedC4; + } + } + } + else { + // Look at iGoalNode + if (iGoalNode > -1) + { + forgetPath(); + forgetGoal(); + vHead = vBody = NodeMachine.node_vector(iGoalNode); + } + else { + vHead = vBody = pEdict->v.origin; + } + } + } + + // C4 timer is set, this means: + // T -> Is planting bomb + // CT-> Is defusing bomb + if (shouldActWithC4()) { + // make sure we override this, or else we learn that we get stuck or something + // which is not the case. + setTimeToMoveToNode(2); + + // terrorist + if (isTerrorist()) { + // When still having the C4 + setMoveSpeed(0.0f); + // f_strafe_speed = 0.0f; + + // When no C4 selected yet, select it + if (!isHoldingWeapon(CS_WEAPON_C4)) { + UTIL_SelectItem(pEdict, "weapon_c4"); + } + else { + UTIL_BotPressKey(this, IN_ATTACK); // plant it! + } + + // When we no longer have the C4 , we stop doing this stupid shit + if (!hasBomb() || Game.bBombPlanted) { + rprint_trace("Act()", "I was planting the C4, and it got planted (I no longer have the C4), so find a nearby node to camp/guard the C4"); + f_c4_time = gpGlobals->time; + setGoalNode(NodeMachine.getClosestNode(pEdict->v.origin, 200, pEdict)); + iPathFlags = PATH_CAMP; + forgetPath(); + } + } + else { + // counter-terrorist + Defuse(); // old routine from RB AI V1.0 defusing, should get here and more cleaned up + } + } + + if (f_strafe_time < gpGlobals->time) { + f_strafe_speed = 0.0f; + } + + // walk only when NOT holding duck (is same as walking, combination makes bot super slow) + if (f_walk_time > gpGlobals->time && !(pEdict->v.button & IN_DUCK)) { + // From "KickBot": return (float) (((int)flMaxSpeed)/2 + ((int)flMaxSpeed)/50); + //OLD: f_move_speed = f_max_speed / 2.0; // this is not correct + + pEdict->v.button &= ~IN_RUN; // release IN_RUN + rprint("Act", "Walk time > gpGlobals->time"); + setMoveSpeed(f_max_speed / 2.0f + f_max_speed / 50.0f); + } + + // When we are at max speed, press IN_RUN to get a running animation + if (f_move_speed == f_max_speed) { + UTIL_BotPressKey(this, IN_RUN); + } + + if (!keyPressed(IN_MOVELEFT) || keyPressed(IN_MOVERIGHT)) { + if (f_strafe_speed > 0.0f) { + UTIL_BotPressKey(this, IN_MOVERIGHT); + } + else if (f_strafe_speed < 0.0f) { + UTIL_BotPressKey(this, IN_MOVELEFT); + } + } + + // When we should go back, we go back + if (f_goback_time > gpGlobals->time) { + setMoveSpeed(-f_max_speed); + } + + // When holding duck, we hold duck + if (f_hold_duck > gpGlobals->time) + UTIL_BotPressKey(this, IN_DUCK); + + // When we wait, we have no move speed + // notice: 'wait' is not 'stuck' nor 'camping'. Wait should only be used to have a bot + // 'do nothing' for a short period of time. + if (f_wait_time > gpGlobals->time) { + rprint("Act", "f_wait_time > gpGlobals->time"); + setMoveSpeed(0.0f); + } + + // Button usage, change vBody to a 'trigger multiple' because we have to touch these + if (pButtonEdict) { + if (std::strcmp(STRING(pButtonEdict->v.classname), "trigger_multiple") == 0) { + if (func_distance(pEdict->v.origin, VecBModelOrigin(pButtonEdict)) < 60) { + vBody = VecBModelOrigin(pButtonEdict); + } + } + } + + // ------------------------------------------- + // MOVE TO : vBody + // calculate the angle we MOVE to. (VecMoveAngles) + // ------------------------------------------- + Vector vTarget = vBody - pEdict->v.origin; + vecMoveAngles = UTIL_VecToAngles(vTarget); + + // Paulo-La-Frite - START bot aiming bug fix + if (vecMoveAngles.x > 180) + vecMoveAngles.x -= 360; + + vecMoveAngles.x = -vecMoveAngles.x; + vecMoveAngles.z = 0; + UTIL_FixAngles(&vecMoveAngles); + + // when filled in, we look to this (overrides) + if (vEar != Vector(9999, 9999, 9999)) + vHead = vEar; + + // button overrides hearing + if (pButtonEdict) + vHead = VecBModelOrigin(pButtonEdict); + + // ------------------------------------------- + // FACE AT: vHead + // calculate the angle we face at. + // + // ------------------------------------------- + vTarget = vHead - pEdict->v.origin; + pEdict->v.v_angle = UTIL_VecToAngles(vTarget); + if (pEdict->v.v_angle.y > 180.0f) + pEdict->v.v_angle.y -= 360.0f; + + // Paulo-La-Frite - START bot aiming bug fix + if (pEdict->v.v_angle.x > 180.0f) + pEdict->v.v_angle.x -= 360.0f; + + Vector v_shouldbe; + + // Vector how it should be, however, we don't allow such a fast turn! + v_shouldbe.x = pEdict->v.v_angle.x / 3; + v_shouldbe.y = pEdict->v.v_angle.y; + v_shouldbe.z = 0; //unused? [APG]RoboCop[CL] + + // set the body angles to point the gun correctly + pEdict->v.angles.x = ReturnTurnedAngle(static_cast(ipTurnSpeed), pEdict->v.angles.x, v_shouldbe.x); + pEdict->v.angles.y = ReturnTurnedAngle(static_cast(ipTurnSpeed), pEdict->v.angles.y, v_shouldbe.y); + pEdict->v.angles.z = 0; + + // adjust the view angle pitch to aim correctly (MUST be after body v.angles stuff) + pEdict->v.v_angle.x = -pEdict->v.v_angle.x; + + // Paulo-La-Frite - END + pEdict->v.ideal_yaw = pEdict->v.v_angle.y; + pEdict->v.idealpitch = pEdict->v.v_angle.x; + + botFixIdealYaw(pEdict); + botFixIdealPitch(pEdict); +} + +bool cBot::shouldActWithC4() const +{ + return f_c4_time > gpGlobals->time; +} + +// BOT: On ladder? +bool cBot::isOnLadder() const +{ + return FUNC_IsOnLadder(pEdict); +} + +// BOT: Check around body and avoid obstacles +void cBot::CheckAround() { + rprint_trace("CheckAround", "Start"); + // Do not act when on ladder + if (isOnLadder()) + return; + + // The principle is to fire 2 tracelines, both forward; one left + // and one right. When one of the 2 gets hit, we know we are 'about' + // to get hit. Therefor we use strafing to keep distance to the coming wall + // when left and right is both hit we have a problem as this should not happen. + + // Note: we use TRACEHULL instead of TRACELINE, because TRACEHULL detects + // the famous 'undetectable' func_walls. + TraceResult tr; + + // v_source = pEdict->v.origin + Vector(0, 0, -CROUCHED_HEIGHT + (MAX_JUMPHEIGHT + 1)); + const Vector v_source = pEdict->v.origin + Vector(0, 0, ORIGIN_HEIGHT); + + // Go forward first + constexpr float distance = 90.0f; + const Vector v_forward = v_source + gpGlobals->v_forward * distance; + + // now really go left/right + const Vector v_right = v_source + gpGlobals->v_right * distance; + const Vector v_left = v_source + gpGlobals->v_right * -distance; + + // now really go left/right + const Vector v_forwardright = v_right + gpGlobals->v_forward * distance; + const Vector v_forwardleft = v_left + gpGlobals->v_forward * -distance; + + // TRACELINE: forward + UTIL_TraceHull(v_source, v_forward, dont_ignore_monsters, point_hull, pEdict, &tr); + const bool bHitForward = tr.flFraction < 1.0f; + + // TRACELINE: Left + UTIL_TraceHull(v_source, v_left, dont_ignore_monsters, point_hull, pEdict, &tr); + const bool bHitLeft = tr.flFraction < 1.0f; + + // TRACELINE: Right + UTIL_TraceHull(v_source, v_right, dont_ignore_monsters, point_hull, pEdict, &tr); + const bool bHitRight = tr.flFraction < 1.0f; + + // TRACELINE: Forward left + UTIL_TraceHull(v_source, v_forwardleft, dont_ignore_monsters, point_hull, pEdict, &tr); + const bool bHitForwardLeft = tr.flFraction < 1.0f; + + // TRACELINE: Forward right + UTIL_TraceHull(v_source, v_forwardright, dont_ignore_monsters, point_hull, pEdict, &tr); + const bool bHitForwardRight = tr.flFraction < 1.0f; + + char msg[255]; + snprintf(msg, sizeof(msg), "HIT results: forward: %d, left: %d, right: %d, forward left: %d, forward right: %d", bHitForward, bHitLeft, bHitRight, bHitForwardLeft, bHitForwardRight); + rprint_trace("CheckAround", msg); + + // Set 'act' properties + const float strafeAmount = DetermineStrafe(bHitForwardLeft, bHitForwardRight, bHitLeft, bHitRight); + if (strafeAmount != 0.0f) { + setStrafeSpeed(strafeAmount, 0.5f); + } + + // we are surrounded, so move backwards, but only if we are not strafing + if (bHitForward && strafeAmount == 0.0f) { + rprint_trace("CheckAround", "Something in front of me blocks, so move back."); + setMoveSpeed(-f_max_speed); + } + else { + rprint_trace("CheckAround", "Nothing in front of me"); + } +} + +// BOT: Should be taking cover? +bool cBot::TakeCover() const +{ + // Its time based. + if (f_cover_time < gpGlobals->time) + return false; + + // And if all went fine, we can return true. + return true; +} + +/** + * Set the node to follow next as the next one (ie, increase index) + */ +void cBot::nextPathIndex() +{ + this->pathIndex++; +} + +/** + * Set the node to follow next as the previous one (ie, decrease index). Calls forgetPath when index is getting < 0 + */ +void cBot::prevPathIndex() +{ + rprint("prevPathNodeIndex"); + this->pathIndex--; + if (this->pathIndex < 0) { + forgetPath(); + } +} + +// Returns true if bot has a path to follow +bool cBot::isWalkingPath() const +{ + return this->pathIndex > -1; +} + +// Returns true if bot has goal node +bool cBot::hasGoal() const +{ + return this->iGoalNode > -1; +} + +// Returns true if bot has goal node index (ie referring to Goals[]) +bool cBot::hasGoalIndex() const +{ + return this->goalIndex > -1; +} + +/** + * Returns goal data , if goal data exists + * @return + */ +tGoal* cBot::getGoalData() const +{ + if (!hasGoalIndex()) return nullptr; + tGoal* ptr = NodeMachine.getGoal(this->goalIndex); + if (ptr == nullptr) return nullptr; + + // only goals with a node are valid + if (ptr->iNode > -1) return ptr; + // else not + + return nullptr; +} + +// Returns true if bot has an enemy edict +bool cBot::hasEnemy() const +{ + return this->pEnemyEdict != nullptr; +} + +/** + * Returns true when given edict == our enemy edict + * @param pEntity + * @return + */ +bool cBot::hasEnemy(const edict_t* pEntity) const +{ + return this->pEnemyEdict == pEntity; +} + +// Returns true if bot has a path to follow +bool cBot::shouldBeWandering() { + if (this->fWanderTime > gpGlobals->time) { + char msg[255] = {}; + snprintf(msg, sizeof(msg), "Wander time is %f , globals time is %f, so should still wander", this->fWanderTime, gpGlobals->time); + rprint(msg); + return true; + } + return false; +} + +void cBot::setMoveSpeed(const float value) { + // char msg[255]; + // sprintf(msg, "setting to value %f / maxSpeed %f - sv_maxspeed = %f", value, this->f_max_speed, CVAR_GET_FLOAT("sv_maxspeed")); + // rprint_trace("setMoveSpeed", msg); + this->f_move_speed = value; +} + +void cBot::setStrafeSpeed(const float value, const float time) { + char msg[255]; + snprintf(msg, sizeof(msg), "%f for %f seconds.", value, time); + rprint_trace("setStrafeSpeed", msg); + // if (f_strafe_time > gpGlobals->time) { + // + // } else { + f_strafe_speed = value; + f_strafe_time = gpGlobals->time + time; + // } +} + +void cBot::strafeLeft(const float time) +{ + setStrafeSpeed(-f_max_speed, time); +} + +void cBot::strafeRight(const float time) +{ + setStrafeSpeed(f_max_speed, time); +} + +void cBot::startWandering(const float time) +{ + this->fWanderTime = gpGlobals->time + time; + setMoveSpeed(f_max_speed); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "Start wandering for %f seconds", time); + rprint("startWandering", msg); +} + +void cBot::stopMoving() +{ + this->setMoveSpeed(0.0f); +} + +void cBot::forgetGoal() +{ + rprint_trace("forgetGoal"); + this->iGoalNode = -1; + this->goalIndex = -1; +} + +int cBot::getPathIndex() const +{ + return this->pathIndex; +} + +int cBot::getPreviousPathIndex() const +{ + return this->pathIndex - 1; +} + +void cBot::forgetPath() +{ + rprint("forgetPath"); + this->pathIndex = -1; + NodeMachine.path_clear(this->iBotIndex); +} + +void cBot::forgetEnemy() { + this->pEnemyEdict = nullptr; +} + +edict_t* cBot::getEnemyEdict() const +{ + return this->pEnemyEdict; +} + +int cBot::getGoalNode() const +{ + return this->iGoalNode; +} + +void cBot::setGoalNode(const int nodeIndex, const int iGoalIndex) { + if (nodeIndex < 0) { + rprint("setGoalNode()", "WARN: Setting a goal lower than 0, assuming this is not intentional. If you need to forget a goal, use forgetGoal()"); + } + this->iGoalNode = nodeIndex; + this->goalIndex = iGoalIndex; + + tGoal* goalPtr = getGoalData(); + char msg[255] = {}; + + if (goalPtr != nullptr) { + snprintf(msg, sizeof(msg), "Setting iGoalNode to [%d] and goalIndex [%d] - GOAL: type [%s], checked [%d]", + nodeIndex, + goalIndex, + goalPtr->name, + goalPtr->iChecked + ); + } else { + snprintf(msg, sizeof(msg), "Setting iGoalNode to [%d] and goalIndex [%d] - could not retrieve goal data.", nodeIndex, goalIndex); + } + rprint("setGoalNode()", msg); +} + +void cBot::setGoalNode(const int nodeIndex) +{ + this->setGoalNode(nodeIndex, -1); +} + +void cBot::setGoalNode(tGoal* goal) +{ + if (goal != nullptr && goal->iNode > -1) { + rprint("setGoalNode with goal pointer\n"); + this->setGoalNode(goal->iNode, goal->index); + } +} + +/** + * Always printed when debug mode is on + * @param Function + * @param msg + */ +void cBot::rprint(const char* Function, const char* msg) +{ + REALBOT_PRINT(this, Function, msg); +} + +/** + * Only printed when debug mode is on and verbosity is trace + * @param Function + * @param msg + */ +void cBot::rprint_trace(const char* Function, const char* msg) +{ + if (Game.messageVerbosity > 2) { // Adjust verbosity level for trace + REALBOT_PRINT(this, Function, msg); + } +} + +/** + * Only printed when debug mode is on and verbosity is normal + * @param Function + * @param msg + */ +void cBot::rprint_normal(const char* Function, const char* msg) +{ + if (Game.messageVerbosity > 1) { // Keep verbosity level for normal + REALBOT_PRINT(this, Function, msg); + } +} + +void cBot::rprint(const char* msg) { + rprint("rprint()", msg); +} + +void cBot::rprint_normal(const char* msg) { + rprint_normal("rprint()", msg); +} + +void cBot::rprint_trace(const char* msg) { + rprint_trace("rprint()", msg); +} + +bool cBot::hasBomb() const +{ + return isOwningWeapon(CS_WEAPON_C4); +} + +bool cBot::isCounterTerrorist() const +{ + return iTeam == 2; +} + +bool cBot::isTerrorist() const +{ + return iTeam == 1; +} + +bool cBot::hasPrimaryWeaponEquiped() const +{ + return iPrimaryWeapon > -1; +} + +bool cBot::hasSecondaryWeaponEquiped() const +{ + return iSecondaryWeapon > -1; +} + +/*bool cBot::hasPrimaryWeapon(const int weaponId) const +{ + return isOwningWeapon(weaponId); +} + +bool cBot::hasSecondaryWeapon(const int weaponId) const +{ + return isOwningWeapon(weaponId); +}*/ + +void cBot::performBuyWeapon(const char* menuItem, const char* subMenuItem) { + // To be sure the console will only change when we MAY change. + // The values will only be changed when console_nr is 0 + if (Game.getRoundStartedTime() + 4 < gpGlobals->time) + return; // Not valid to buy + + if (this->console_nr == 0) { + // set up first command and argument + std::strcpy(this->arg1, "buy"); + std::strcpy(this->arg2, menuItem); + + if (subMenuItem != nullptr) std::strcpy(this->arg3, subMenuItem); + + this->console_nr = 1; // start console command sequence + } +} + +void cBot::performBuyActions(const int weaponIdToBuy) { + if (weaponIdToBuy < 0) { + return; + } + // Buy... + + // TODO + // FRASHMAN 30.08.04 haven't changed the cs 1.5 buycode, maybe there are also errors + + // CS 1.5 only + if (counterstrike == 0) { + switch (weaponIdToBuy) { + case CS_WEAPON_AK47: + performBuyWeapon("4", "1"); + break; + case CS_WEAPON_DEAGLE: + performBuyWeapon("1", "3"); + break; + case CS_WEAPON_P228: + performBuyWeapon("1", "4"); + break; + case CS_WEAPON_SG552: + performBuyWeapon("4", "2"); + break; + case CS_WEAPON_SG550: + performBuyWeapon("4", "8"); + break; + case CS_WEAPON_SCOUT: + performBuyWeapon("4", "5"); + break; + case CS_WEAPON_AWP: + performBuyWeapon("4", "6"); + break; + case CS_WEAPON_MP5NAVY: + performBuyWeapon("3", "1"); + break; + case CS_WEAPON_UMP45: + performBuyWeapon("3", "5"); + break; + case CS_WEAPON_ELITE: + performBuyWeapon("1", "5"); + break; // T only + case CS_WEAPON_MAC10: + performBuyWeapon("3", "4"); + break; // T only + case CS_WEAPON_AUG: + performBuyWeapon("4", "4"); + break; // CT Only + case CS_WEAPON_FIVESEVEN: + performBuyWeapon("1", "6"); + break; // CT only + case CS_WEAPON_M4A1: + performBuyWeapon("4", "3"); + break; // CT Only + case CS_WEAPON_TMP: + performBuyWeapon("3", "2"); + break; // CT only + case CS_WEAPON_HEGRENADE: + performBuyWeapon("8", "4"); + break; + case CS_WEAPON_XM1014: + performBuyWeapon("2", "2"); + break; + case CS_WEAPON_SMOKEGRENADE: + performBuyWeapon("8", "5"); + break; + case CS_WEAPON_USP: + performBuyWeapon("1", "1"); + break; + case CS_WEAPON_GLOCK18: + performBuyWeapon("1", "2"); + break; + case CS_WEAPON_M249: + performBuyWeapon("5", "1"); + break; + case CS_WEAPON_M3: + performBuyWeapon("2", "1"); + break; + case CS_WEAPON_G3SG1: + performBuyWeapon("4", "7"); + break; + case CS_WEAPON_FLASHBANG: + performBuyWeapon("8", "3"); + break; + case CS_WEAPON_P90: + performBuyWeapon("3", "3"); + break; + + // Armor + case CS_WEAPON_ARMOR_LIGHT: + performBuyWeapon("8", "1"); + break; + case CS_WEAPON_ARMOR_HEAVY: + performBuyWeapon("8", "2"); + break; + + case CS_DEFUSEKIT: + performBuyWeapon("8", "6"); + break; + } + } + + // CS 1.6 only + else if (counterstrike == 1) { // FRASHMAN 30/08/04: redone switch block, it was full of errors + switch (weaponIdToBuy) { + //Pistols + case CS_WEAPON_GLOCK18: + performBuyWeapon("1", "1"); + break; + case CS_WEAPON_USP: + performBuyWeapon("1", "2"); + break; + case CS_WEAPON_P228: + performBuyWeapon("1", "3"); + break; + case CS_WEAPON_DEAGLE: + performBuyWeapon("1", "4"); + break; + case CS_WEAPON_FIVESEVEN: + performBuyWeapon("1", "5"); + break; // CT Only + case CS_WEAPON_ELITE: + performBuyWeapon("1", "5"); + break; // T Only + //ShotGUNS + case CS_WEAPON_M3: + performBuyWeapon("2", "1"); + break; + case CS_WEAPON_XM1014: + performBuyWeapon("2", "2"); + break; + //SMG + case CS_WEAPON_MAC10: + performBuyWeapon("3", "1"); + break; // T Only + case CS_WEAPON_TMP: + performBuyWeapon("3", "1"); + break; // CT Only + case CS_WEAPON_MP5NAVY: + performBuyWeapon("3", "2"); + break; + case CS_WEAPON_UMP45: + performBuyWeapon("3", "3"); + break; + case CS_WEAPON_P90: + performBuyWeapon("3", "4"); + break; + //rifles + case CS_WEAPON_GALIL: + performBuyWeapon("4", "1"); + break; // T Only + case CS_WEAPON_FAMAS: + performBuyWeapon("4", "1"); + break; // CT Only + case CS_WEAPON_AK47: + performBuyWeapon("4", "2"); + break; // T Only + case CS_WEAPON_M4A1: + performBuyWeapon("4", "3"); + break; // CT Only + case CS_WEAPON_SG552: + performBuyWeapon("4", "4"); + break; // T Only + case CS_WEAPON_AUG: + performBuyWeapon("4", "4"); + break; // CT Only + case CS_WEAPON_SG550: + performBuyWeapon("4", "5"); + break; // CT Only + case CS_WEAPON_G3SG1: + performBuyWeapon("4", "6"); + break; // T Only + //machinegun + case CS_WEAPON_M249: + performBuyWeapon("5", "1"); + break; + // equipment + case CS_WEAPON_ARMOR_LIGHT: + performBuyWeapon("8", "1"); + break; + case CS_WEAPON_ARMOR_HEAVY: + performBuyWeapon("8", "2"); + break; + case CS_WEAPON_FLASHBANG: + performBuyWeapon("8", "3"); + break; + case CS_WEAPON_HEGRENADE: + performBuyWeapon("8", "4"); + break; + case CS_WEAPON_SMOKEGRENADE: + performBuyWeapon("8", "5"); + break; + case CS_WEAPON_SHIELD: + performBuyWeapon("8", "8"); + break; + + case CS_DEFUSEKIT: + performBuyWeapon("8", "6"); + break; + //default: //Just in case they use pistols but buy MP5 [APG]RoboCop[CL] + // performBuyWeapon("3", "2"); + // break; + } + + // This differs per team + // FRASHMAN 30/08/04: all into one ifthen block + if (iTeam == 2) // counter + { + switch (weaponIdToBuy) { + case CS_WEAPON_SCOUT: + performBuyWeapon("4", "2"); + break; + case CS_WEAPON_AWP: + performBuyWeapon("4", "6"); + break; + //whats about nightvision? BuyWeapon (pBot, "8", "7") + } + } else // terror + { + switch (weaponIdToBuy) { + case CS_WEAPON_SCOUT: + performBuyWeapon("4", "3"); + break; + case CS_WEAPON_AWP: + performBuyWeapon("4", "5"); + break; + //whats about nightvision? BuyWeapon (pBot, "8", "6") + } + } + } // end of cs 1.6 part +} // We actually gonna buy this weapon + +// BOT: Memory() +// In this function the bot will receive data; this can be any kind of data. +// For hearing, the bot will check for sounds it should pay attention to and +// store this into its 'hearing vector'. The hearing vector will be used only +// when walking and not when fighting an enemy. Do note that this hearing vector +// is only filled when it is important enough, so all the decisions are made here. +void cBot::Memory() { + + // Skip method when it is too soon. + if (fMemoryTime > gpGlobals->time) { + return; + } + + // Hear players: (loop through all players, determine if they are running and if + // we can hear them (estimated distance)). + if (pEnemyEdict == nullptr) { + Vector vHear = Vector(9999, 9999, 9999); + edict_t* pHearPlayer = nullptr; + + //f_walk_time = gpGlobals->time + 1; + + for (int i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + + // skip invalid players and skip self (i.e. this bot) + if (pPlayer && !pPlayer->free && pPlayer != pEdict) { + // skip this player if not alive (i.e. dead or dying) + if (!IsAlive(pPlayer)) + continue; + + // check if we can 'see it on radar' (skip teammates) + if (UTIL_GetTeam(pPlayer) == UTIL_GetTeam(pEdict)) + continue; + + // check if its running + if (FUNC_PlayerRuns(FUNC_PlayerSpeed(pPlayer))) { + // check distance + const float fDistance = + (pPlayer->v.origin - pEdict->v.origin).Length(); + + // estimated distance we can hear somebody + if (fDistance < BOT_HEARDISTANCE) { + // check if this 'hearing' vector is closer then our previous one + if (vHear != Vector(9999, 9999, 9999)) { + if (func_distance + (pEdict->v.origin, + pPlayer->v.origin) < + func_distance(pEdict->v.origin, vHear)) { + // this one is closer, thus more important + vHear = pPlayer->v.origin; + pHearPlayer = pPlayer; + } + } else { + vHear = pPlayer->v.origin; + pHearPlayer = pPlayer; + } + } + } + + if (pPlayer->v.button & IN_ATTACK + && (FUNC_EdictHoldsWeapon(pEdict) != CS_WEAPON_HEGRENADE + && FUNC_EdictHoldsWeapon(pEdict) != CS_WEAPON_FLASHBANG + && FUNC_EdictHoldsWeapon(pEdict) != + CS_WEAPON_SMOKEGRENADE)) { + // check distance + const float fDistance = + (pPlayer->v.origin - pEdict->v.origin).Length(); + + // estimated distance we can hear somebody + if (fDistance < BOT_HEARFIREDISTANCE) { + // check if this 'hearing' vector is closer then our previous one + if (vHear != Vector(9999, 9999, 9999)) { + if (func_distance + (pEdict->v.origin, + pPlayer->v.origin) < + func_distance(pEdict->v.origin, vHear)) { + // this one is closer, thus more important + vHear = pPlayer->v.origin; + pHearPlayer = pPlayer; + } + } else { + vHear = pPlayer->v.origin; + pHearPlayer = pPlayer; + } + } + } + // zooming of a sniper rifle + if (pPlayer->v.button & IN_ATTACK2) { + // check distance + const float fDistance = + (pPlayer->v.origin - pEdict->v.origin).Length(); + + // estimated distance we can hear somebody + if (fDistance < BOT_HEARDISTANCE) { + // check if this 'hearing' vector is closer then our previous one + if (vHear != Vector(9999, 9999, 9999)) { + if (func_distance + (pEdict->v.origin, + pPlayer->v.origin) < + func_distance(pEdict->v.origin, vHear)) { + // this one is closer, thus more important + vHear = pPlayer->v.origin; + pHearPlayer = pPlayer; + } + } else { + vHear = pPlayer->v.origin; + pHearPlayer = pPlayer; + } + } + } + + } + } + + // Fill in hearing vectory if any: + if (pHearPlayer != nullptr) { + if (RANDOM_LONG(0, 100) < ipFearRate + 10) { + + // determine fuzzyness by distance: + int iFuzz = + static_cast(func_distance(pEdict->v.origin, vHear) / + BOT_HEARDISTANCE) * 250; + + // skill depended + iFuzz /= bot_skill + 1; + + // create 'estimated hear vector' + const float randX = RANDOM_LONG(-iFuzz, iFuzz); + const float randY = RANDOM_LONG(-iFuzz, iFuzz); + const float randZ = RANDOM_LONG(-iFuzz, iFuzz); + + vHear = vHear + Vector(randX, randY, randZ); + + TraceResult tr; + + UTIL_TraceHull(pEdict->v.origin, vHear, dont_ignore_monsters, + point_hull, pEdict, &tr); + + int iNodeHearPlayer = + NodeMachine.getClosestNode(vHear, NODE_ZONE * 2, pHearPlayer); + + // if nothing hit: + if (tr.flFraction >= 1.0f) { + // we can look at this spot + vEar = vHear; + } + // we go to the destination + + const float fTime = 5 + static_cast(ipFearRate) / static_cast(7); + + if (RANDOM_LONG(0, 100) < ipFearRate + && f_walk_time + 5 < gpGlobals->time) // last 5 seconds did not walk + f_walk_time = gpGlobals->time + fTime; + + if (RANDOM_LONG(0, 100) < ipCampRate + && f_camp_time + 30 < gpGlobals->time // last 30 seconds did not camp + ) { + f_camp_time = gpGlobals->time + fTime; + } + + } else { + fMemoryTime = gpGlobals->time + 5.0f; + } + + /* + + + int iNodeHearPlayer = NodeMachine.getCloseNode (vHear, NODE_ZONE*2, pHearPlayer); + int iNodeFrom = NodeMachine.getCloseNode (pEdict->v.origin, NODE_ZONE*2, pEdict); + int iHearToNode = NodeMachine.node_look_at_hear(iNodeHearPlayer, iNodeFrom, pEdict); + + // look at hearto node + if (iHearToNode > -1) + { + vHead = NodeMachine.node_vector(iHearToNode); + SERVER_PRINT("found smart look at node\n"); + } + + // only check for new goal when the current goal is way of distance and such + if (ipCampRate > 30 && f_camp_time + 5 < gpGlobals->time) + f_camp_time = gpGlobals->time + 2.5; + */ + + if (f_update_weapon_time + 2 < gpGlobals->time) { + PickBestWeapon(); + } + } else { + vEar = Vector(9999, 9999, 9999); + // + // // check for any 'beeps' of the bomb! + // if (isCounterTerrorist() && Game.bBombPlanted) { + // // find the bomb vector + // edict_t *pent = NULL; + // Vector vC4 = Vector(9999, 9999, 9999); + // while ((pent = UTIL_FindEntityByClassname(pent, "grenade")) != NULL) { + // if (UTIL_GetGrenadeType(pent) == 4) // It is a C4 + // { + // vC4 = pent->v.origin; // store origin + // break; // done our part now + // } + // } // --- find the c4 + // + // if (vC4 != Vector(9999, 9999, 9999)) { + // + // if (func_distance(vC4, NodeMachine.node_vector(iGoalNode)) > 100 && + // func_distance(pEdict->v.origin, vC4) < 1024) { + // // set new goal node + // setGoalNode(NodeMachine.getCloseNode(vC4, NODE_ZONE, NULL)); + // forgetPath(); + // } + // } + // } + } + } else { + vEar = Vector(9999, 9999, 9999); + } +} + +void cBot::Walk() //Experimental implementation [APG]RoboCop[CL] +{ + if (f_walk_time + 0.1f < gpGlobals->time) { + f_walk_time = gpGlobals->time + 0.1f; + if (f_walk_time + 0.1f < gpGlobals->time) { + f_walk_time = gpGlobals->time + 0.1f; + } + } +} + + +// BOT: Do i carry weapon # now? +bool cBot::CarryWeapon(const int iType) const +{ + if (current_weapon.iId == iType) + return true; + return false; +} + +// BOT: Do i carry weapon TYPE # now? +int cBot::CarryWeaponType() const +{ + int kind = PRIMARY; + const int weapon_id = current_weapon.iId; + + // Check 1. Is it a knife? + if (weapon_id == CS_WEAPON_KNIFE) + kind = KNIFE; + + // Check 2, is it a 'tool'? + if (weapon_id == CS_WEAPON_FLASHBANG || weapon_id == CS_WEAPON_HEGRENADE + || weapon_id == CS_WEAPON_SMOKEGRENADE) + kind = GRENADE; + + // Check 3, is it a secondary gun? + if (weapon_id == CS_WEAPON_P228 || weapon_id == CS_WEAPON_ELITE + || weapon_id == CS_WEAPON_USP || weapon_id == CS_WEAPON_GLOCK18 + || weapon_id == CS_WEAPON_DEAGLE || weapon_id == CS_WEAPON_FIVESEVEN) + kind = SECONDARY; + + // Check 4, is it a sniper gun? + if (weapon_id == CS_WEAPON_SCOUT || weapon_id == CS_WEAPON_SG550 + || weapon_id == CS_WEAPON_AWP || weapon_id == CS_WEAPON_G3SG1) + kind = SNIPER; + + if (hasShield()) { + kind = SHIELD; + } + //if (weapon_id < 1) + // kind = NONE; + return kind; +} + +// BOT: Think about objectives +// +// This function only takes action when the bot is close a goal. The function +// NodeMachine.path_think() handles WHERE the bot goes. Not WHAT to do at a goal. +void cBot::ThinkAboutGoals() { + //REALBOT_PRINT(this, "thinkAboutGoals()", "start"); + // Depending on bot team we handle goals differently: + // TERRORISTS + if (isTerrorist()) { + // Plant the bomb when the HUD says we can -- BERKED + if (bHUD_C4_plantable) + f_c4_time = gpGlobals->time + 1; // plant bomb + + // A dropped C4 is not a 'goal' (ie. it won't let you win the game + // when you pick up the bomb. Therefor the 'pickup the dropped bomb + // code is in cNodeMachine::path_walk(). + } else if (isCounterTerrorist()) { + // COUNTER-TERRORISTS + if (vip) { + // VIP + } else { + if (Game.bBombPlanted) { + if (isCounterTerrorist()) { + // defuse (or set timers for it) + Defuse(); + } + } else { + if (Game.bHostageRescueMap) { + TryToGetHostageTargetToFollowMe(this); + checkIfHostagesAreRescued(); + checkOfHostagesStillFollowMe(); + } + } + } + } + // in Act() we find the 'acting' code when timers above are set. +} + +void cBot::rememberWhichHostageToRescue(edict_t* pHostage) { + this->pBotHostage = pHostage; +} + +edict_t* cBot::getHostageToRescue() const +{ + return pBotHostage; +} + +edict_t* cBot::findHostageToRescue() { + edict_t* pent = nullptr; + + // Search for all hostages in the game + while ((pent = UTIL_FindEntityByClassname(pent, "hostage_entity")) != nullptr) { + if (!isHostageRescueable(this, pent)) continue; + if (!canSeeEntity(pent)) continue; + // skip too far hostages, leave it up to the goal picking to get closer + if (getDistanceTo(pent->v.origin) > static_cast(NODE_ZONE) * 2.5f) continue; + + char msg[255]; + snprintf(msg, sizeof(msg), "Found hostage to rescue at %f,%f,%f", pent->v.origin.x, pent->v.origin.y, pent->v.origin.z); + this->rprint_trace("findHostageToRescue()", msg); + return pent; + } + + return nullptr; +} + +bool cBot::isDefusing() const +{ + return f_defuse > gpGlobals->time; +} + +bool cBot::hasTimeToMoveToNode() const +{ + return fMoveToNodeTime > -1 && getMoveToNodeTimeRemaining() > 0; +} +/** +This function will set the iCloseNode variable, which is the node most closest to +the bot. Returns the closest node it found. +**/ +int cBot::determineCurrentNode() { + iCloseNode = determineCurrentNode(NODE_ZONE); + return iCloseNode; +} + +/** +This function will set the iCloseNode variable, which is the node most closest to +the bot. Returns the closest node it found. +**/ +int cBot::determineCurrentNodeWithTwoAttempts() { + iCloseNode = determineCurrentNode(); + if (iCloseNode < 0) { + iCloseNode = determineCurrentNode(NODE_ZONE * 2); + } + return iCloseNode; +} + +/** +Find node close to bot, given range. Does not cache result. +**/ +int cBot::determineCurrentNode(const float range) const +{ + return NodeMachine.getClosestNode(pEdict->v.origin, range, pEdict); +} + +/** + * This returns the current node (iCloseNode) set. Instead of using determineCurrentNode, which is expensive, + * call this to return the cached value. It will however call determineCurrentNode when node is < 0, usually it means + * the state has been set. + * @return + */ +int cBot::getCurrentNode() { + if (iCloseNode < 0) { + determineCurrentNode(); + } + return iCloseNode; +} + +/** + * Aka, the node we are heading for. + */ +int cBot::getCurrentPathNodeToHeadFor() const +{ + return NodeMachine.getNodeIndexFromBotForPath(iBotIndex, pathIndex); +} + +/** + * Aka, the node we were coming from. In case the index is < 0 (ie, there is no previous node yet), this will + * return -1; + */ +int cBot::getPreviousPathNodeToHeadFor() const +{ + return NodeMachine.getNodeIndexFromBotForPath(iBotIndex, getPreviousPathIndex()); +} + +bool cBot::isHeadingForGoalNode() const +{ + return getCurrentPathNodeToHeadFor() == getGoalNode(); +} + +/** + * Aka, the next node after we have arrived at the current path node. + */ +int cBot::getNextPathNode() const +{ + return NodeMachine.getNodeIndexFromBotForPath(iBotIndex, pathIndex + 1); +} + +// Is this bot dead? +bool cBot::isDead() const +{ + return pEdict->v.health < 1 || pEdict->v.deadflag != DEAD_NO; +} + +// BOT: Think +void cBot::Think() { + if (mod_id != CSTRIKE_DLL) return; // do not support non-counter-strike mods + + // BOT: If a bot did not join a team yet, then do it + if (!hasJoinedTeam) { + rprint("Need to join team, doing that now"); + JoinTeam(); + return; + } + + // Set closest node + determineCurrentNode(); + + // BOT: If a bot is dead, re-initialize + if (isDead()) { + if (!bInitialize) return; // do nothing when no need to initialize + rprint("Dead, need to re-initialize"); + + // AUTOSKILL + const cBot* botPointerOfKiller = UTIL_GetBotPointer(killer_edict); + + // not killed by a fellow bot, presumably a human player + if (botPointerOfKiller == nullptr) { + if (autoskill) { + bot_skill--; + bot_skill = std::max(bot_skill, 0); + } + + if (Game.iKillsBroadcasting != BROADCAST_KILLS_NONE + && killer_edict != nullptr) { + // This is a human, we will tell this human he has been killed + // by a bot. + const int r = RANDOM_LONG(150, 255); + const int g = RANDOM_LONG(30, 155); + const int b = RANDOM_LONG(30, 155); + char msg[128]; + if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_FULL) { + snprintf(msg, sizeof(msg), "A RealBot has killed you!\n\nName:%s\nSkill:%d\n", name, bot_skill); + } + else { + snprintf(msg, sizeof(msg), "A RealBot named %s has killed you!", name); + } + + HUD_DrawString(r, g, b, msg, killer_edict); + } + } + + if (iCloseNode > -1 && !end_round) { + iDiedNode = iCloseNode; + NodeMachine.danger(iCloseNode, UTIL_GetTeam(pEdict)); + } + + if (console_nr == 0) { + rprint("NewRound - because console_nr ?!"); + NewRound(); + bInitialize = false; + } + + BotConsole(this); + + // dead messages + if (console_nr == 0) { + rprint("console_nr == 0"); //whatever this means + // do some chatting + if (RANDOM_LONG(0, 200) < ipChatRate) { + if (fChatTime + 0.5f < gpGlobals->time) + if (chChatSentence[0] == '\0') // we did not want to say anything + { + // we should say something now? + int iMax = -1; + + for (const char(&tc)[128] : ChatEngine.ReplyBlock[99].sentence) + { + if (tc[0] != '\0') iMax++; + } + + const int the_c = RANDOM_LONG(0, iMax); + + if (the_c > -1 && iMax > -1) { + char chSentence[80] = {}; + snprintf(chSentence, sizeof(chSentence), + "%s ", ChatEngine.ReplyBlock[99].sentence[the_c]); + //strcpy(chSentence, ChatEngine.ReplyBlock[99].sentence[the_c]); + PrepareChat(chSentence); + } + } + } + else { + // we missed the chatrate chance + if (fChatTime < gpGlobals->time) // time + if (chChatSentence[0] == '\0') // we did not want to say anything + if (RANDOM_LONG(0, 100) < ipChatRate) // rate + fChatTime = gpGlobals->time + + RANDOM_FLOAT(0.0f, (Game.iProducedSentences + 1.0f) / 2.0f); // wait + + } + + return; + } + } // isDead(); + + // set this for the next time the bot dies so it will initialize stuff + if (!bInitialize) { + bInitialize = true; + } + + if (end_round) { + rprint("End round"); + MDLL_ClientKill(pEdict); + pEdict->v.frags += 1; + return; + } + + // BOT: Played enough rounds + if (played_rounds > play_rounds && internet_play) { + rprint("Played enough rounds"); + bIsUsed = false; // no longer used + char cmd[80]; + snprintf(cmd, sizeof(cmd), "kick \"%s\"\n", name); + SERVER_COMMAND(cmd); // kick the bot using (kick "name") + return; + } + + // Move speed... moved_distance. + if (distanceMovedTimer <= gpGlobals->time) { + // see how far bot has moved since the previous position... + const Vector v_diff = prevOrigin - pEdict->v.origin; + // make distanceMoved an average of this moment and the previous one. + const float movedTwoTimes = distanceMoved + v_diff.Length(); + + // prevent division by zero + if (movedTwoTimes > 0.0f) { + distanceMoved = movedTwoTimes / 2.0f; + } + else { + distanceMoved = 0.0f; + } + + // save current position as previous + prevOrigin = pEdict->v.origin; + distanceMovedTimer = gpGlobals->time + 0.1f; + } + + // NEW ROUND + if (Game.NewRound()) { + rprint_trace("Think", "Game.NewRound"); + } + + // -------------------------------- + // MEMORY STEP + // -------------------------------- + Memory(); + + // -------------------------------- + // IMPORTANT THINKING GOING ON HERE + // -------------------------------- + const int healthChange = prev_health - bot_health; + + // handle damage taken + if (prev_health > bot_health + && healthChange > RANDOM_LONG(CSTRIKE_MIN_DAMAGE, CSTRIKE_MAX_DAMAGE) + && hasEnemy()) { + + // need backup! + if (FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 3, "3", ""); + } + + BOT_DecideTakeCover(this); + } + + prev_health = bot_health; + + // Do your console stuff + BotConsole(this); + + // BOT: Blinded + if (isBlindedByFlashbang()) { + // Dude we are messed up. + + // 01/07/04 - Stefan - Pointed out on the forums by Josh Borke... (do not shoot when dontshoot is on) + // shoot randomly + if (!Game.bDoNotShoot) { + if (RANDOM_LONG(0, 100) < ipFearRate && RANDOM_LONG(0, 100)) { + UTIL_BotPressKey(this, IN_ATTACK); + } + } + + rprint_trace("Think()", "Blinded"); + return; + } + + // NEW: When round time is over and still busy playing, kill bots + const float roundTimeInSeconds = CVAR_GET_FLOAT("mp_roundtime") * 60.0f; + const float freezeTimeCVAR = CVAR_GET_FLOAT("mp_freezetime"); + if (Game.getRoundStartedTime() + 10.0f + roundTimeInSeconds + freezeTimeCVAR < gpGlobals->time) { + end_round = true; + // round is ended + } + + // FREEZETIME: + if (Game.getRoundStartedTime() > gpGlobals->time && freezeTime < gpGlobals->time) { + freezeTime = gpGlobals->time + RANDOM_FLOAT(0.1f, 2.0f); + } + + // 1 SECOND START OF ROUND + if (!bIssuedInitialRadio && + Game.getRoundStartedTime() < gpGlobals->time && + Game.getRoundStartedTime() + 1.0f > gpGlobals->time) { + if (RANDOM_LONG(0, 100) < 30 && FUNC_DoRadio(this)) { + UTIL_BotRadioMessage(this, 2, "1", ""); // "Go, Go, Go!" + } + bIssuedInitialRadio = true; + this->rprint_trace("cBot::Think()", "First second of round, issued initial radio."); + } + + // SITUATION: In freezetime + if (isFreezeTime()) { + stopMoving(); + lastSeenEnemyVector = Vector(0, 0, 0); + setTimeToMoveToNode(2); + vHead = vBody = pEdict->v.origin; + + // find any spawnpoint to look at: + edict_t* pent = nullptr; + + if (isCounterTerrorist()) { + while ((pent = UTIL_FindEntityByClassname(pent, "info_player_start")) != nullptr) { + if (func_distance(pent->v.origin, pEdict->v.origin) < 200 && + func_distance(pent->v.origin, pEdict->v.origin) > 50) { + break; + } + } + } + else { + while ((pent = UTIL_FindEntityByClassname(pent, "info_player_deathmatch")) != nullptr) { + if (func_distance(pent->v.origin, pEdict->v.origin) < 200 && + func_distance(pent->v.origin, pEdict->v.origin) > 50) { + break; + } + } + } + + // when pent is filled, look at it + if (pent != nullptr) { + vBody = vHead = pent->v.origin; + } + + rprint_trace("Think()", "isFreezeTime"); + return; + } + + // Check if bot is stuck + if (fNotStuckTime < gpGlobals->time) + { + if (isStuck()) + { + // The bot is stuck, find a breakable if it's in the way + FUNC_FindBreakable(this); + if (pBreakableEdict != nullptr) + { + // Found a breakable, attack it + FUNC_AttackBreakable(this); + // Reset stuck timer + fNotStuckTime = gpGlobals->time + 2.0f; + return; + } + } + else + { + // Bot is not stuck, reset stuck timer + fNotStuckTime = gpGlobals->time + 2.0f; + } + } + + // If the bot has a breakable target, attack it immediately. + if (pBreakableEdict != nullptr) { + FUNC_AttackBreakable(this); + return; + } + + // **---**---**---**---**---**---** + // MAIN STATE: We have no enemy... + // **---**---**---**---**---**---** + if (!hasEnemy()) { + + if (!Game.bDoNotShoot) { + FUNC_FindBreakable(this); + // If a breakable is found, the next think cycle will handle it + if (pBreakableEdict == nullptr) { + InteractWithPlayers(); + } + } + + bool bMayFromGame = true; + + if (Game.fWalkWithKnife > 0.0f) + if (Game.getRoundStartedTime() + Game.fWalkWithKnife < gpGlobals->time) + bMayFromGame = false; + + if (Game.fWalkWithKnife == 0.0f) + bMayFromGame = false; + + if (hasShield()) { + if (!hasShieldDrawn() && f_allow_keypress < gpGlobals->time) { + UTIL_BotPressKey(this, IN_ATTACK2); // draw shield + f_allow_keypress = gpGlobals->time + 0.7f; + } + } + + if (CarryWeapon(CS_WEAPON_KNIFE) == false + && f_camp_time < gpGlobals->time + && freezeTime < gpGlobals->time + && f_c4_time < gpGlobals->time + && f_update_weapon_time < gpGlobals->time && bWalkKnife + && bMayFromGame) { + UTIL_SelectItem(pEdict, UTIL_GiveWeaponName(-1)); // -1 is knife + f_update_weapon_time = gpGlobals->time + 0.7f; + } + + // When holding a grenade (and not switching to another weapon) + if (CarryWeaponType() == GRENADE + && f_update_weapon_time < gpGlobals->time) { + if (iPrimaryWeapon > -1) + UTIL_SelectItem(pEdict, + UTIL_GiveWeaponName(iPrimaryWeapon)); + + else // pick secondary + UTIL_SelectItem(pEdict, + UTIL_GiveWeaponName(iSecondaryWeapon)); + f_update_weapon_time = gpGlobals->time + 0.7f; + } + + // Think about objectives + ThinkAboutGoals(); + } + else { + // **---**---**---**---**---**---** + // MAIN STATE: We have an enemy! + // **---**---**---**---**---**---** + + // Keep interacting with players: + InteractWithPlayers(); + + // And combat enemies + Combat(); + } + + // WALK() + NodeMachine.path_think(this, distanceMoved); + + // SITUATION: Passed Freezetime + +} // THINK() + +bool cBot::isFreezeTime() const { + return freezeTime > gpGlobals->time; +} + +/** +Return true if one of the pointers is not NULL +**/ +bool cBot::isEscortingHostages() { + const bool result = getAmountOfHostagesBeingRescued() > 0; + if (result) { + rprint("I am escorting hostages!"); + } + return result; +} + +void cBot::checkOfHostagesStillFollowMe() { + if (fCheckHostageStatusTimer > gpGlobals->time) return; + fCheckHostageStatusTimer = gpGlobals->time + 5.0f; + + //// this->rprint("checkOfHostagesStillFollowMe - START"); + // if (hostage1) { + // if (!isHostageRescued(this, hostage1) && FUNC_EdictIsAlive(hostage1) && !canSeeEntity(hostage1) && getDistanceTo(hostage1->v.origin) > NODE_ZONE*2.5) { + // rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage1"); + // forgetHostage(hostage1); + // } + // } + // if (hostage2) { + // if (!isHostageRescued(this, hostage2) && FUNC_EdictIsAlive(hostage2) && !canSeeEntity(hostage2) && getDistanceTo(hostage2->v.origin) > NODE_ZONE*2.5) { + // rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage2"); + // forgetHostage(hostage2); + // } + // } + // if (hostage3) { + // if (!isHostageRescued(this, hostage3) && FUNC_EdictIsAlive(hostage3) && !canSeeEntity(hostage3) && getDistanceTo(hostage3->v.origin) > NODE_ZONE*2.5) { + // rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage3"); + // forgetHostage(hostage3); + // } + // } + // + // if (hostage4) { + // if (!isHostageRescued(this, hostage4) && FUNC_EdictIsAlive(hostage4) && !canSeeEntity(hostage4) && getDistanceTo(hostage4->v.origin) > NODE_ZONE*2.5) { + // rprint_trace("checkOfHostagesStillFollowMe", "lost track of hostage4"); + // forgetHostage(hostage4); + // } + // } + // rprint("checkOfHostagesStillFollowMe - END"); +} + +void cBot::clearHostages() { + rprint_trace("clearHostages"); + hostage1 = nullptr; + hostage2 = nullptr; + hostage3 = nullptr; + hostage4 = nullptr; + pBotHostage = nullptr; +} + +// BOT: CheckGear, part of UpdateStatus() +void cBot::CheckGear() { + + // PRIMARY + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_mp5navy"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_mp5navy"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_ak47"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_ak47"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_m3"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_m3"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_aug"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_aug"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_sg552"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_sg552"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_m249"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_m249"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_xm1014"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_xm1014"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_p90"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_p90"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_tmp"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_tmp"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_m4a1"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_m4a1"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_awp"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_awp"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_sg550"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_sg550"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_scout"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_scout"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_mac10"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_mac10"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_g3sg1"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_g3sg1"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_ump45"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_ump45"); + + // Counter-Strike 1.6 weapon FAMAS/GALIL + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_famas"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_famas"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_galil"))) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_galil"); + + // SECONDARY + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_elite"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_elite"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_fiveseven"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_fiveseven"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_p228"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_p228"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_deagle"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_deagle"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_usp"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_usp"); + if (isOwningWeapon(UTIL_GiveWeaponId("weapon_glock18"))) iSecondaryWeapon = UTIL_GiveWeaponId("weapon_glock18"); + + // Handle shields as primary weapon + if (hasShield()) iPrimaryWeapon = UTIL_GiveWeaponId("weapon_shield"); +} + +// BOT: Update personal status +void cBot::UpdateStatus() { + // name filled in yet? + if (name[0] == 0) + std::strcpy(name, STRING(pEdict->v.netname)); + + // Set thirdpartybot flag + pEdict->v.flags |= FL_THIRDPARTYBOT; + + // Reset stuff + pEdict->v.button = 0; + setMoveSpeed(f_max_speed); // by default run + + // When its not time to strafe, don't. + if (f_strafe_time < gpGlobals->time) { + if (f_strafe_speed != 0.0f) { + rprint_trace("UpdateStatus", "Strafe speed set to 0!"); + f_strafe_speed = 0.0f; + } + } + + // Update team state when started + if (hasJoinedTeam) { + iTeam = UTIL_GetTeam(pEdict) + 1; // 1 - TERRORIST, 2 - COUNTER-TERRORIST + } + + // Check if we became VIP + vip = UTIL_IsVip(pEdict); + + // Check gear + CheckGear(); + + // Set max speed and such when CS 1.6 + if (counterstrike == 1) { + f_max_speed = pEdict->v.maxspeed; + // char msg[255]; + // sprintf(msg, "f_max_speed set to %f", f_max_speed); + // rprint_trace("UpdateStatus", msg); + bot_health = static_cast(pEdict->v.health); + bot_armor = static_cast(pEdict->v.armorvalue); + } +} + +// ---------------------------------- BOT CLASS FUNCTIONS +// ---------------------------------- BOT CLASS FUNCTIONS +// ---------------------------------- BOT CLASS FUNCTIONS + +//////////////////////////////////////////////////////////////////////////////// +/// Radio Action - Response +//////////////////////////////////////////////////////////////////////////////// +bool BotRadioAction() { + char name[64]; + bool unstood = false; + edict_t* plr = nullptr; + int team = -1; + int i; + int radios = 0; // Hold amount of replies here, so we don't flood :) + std::strcpy(name, radio_messenger); + + // First find the team messager name + for (i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); // Get pEdict + if (pPlayer) // If player exists + { + char netname[64]; + std::strcpy(netname, STRING(pPlayer->v.netname)); // Copy netname + if (std::strcmp(netname, name) == 0) // If + { + plr = pPlayer; + team = UTIL_GetTeam(pPlayer); + } + } + } + + // Check players and check if radio message applies to them + for (i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + if (pPlayer) { + char netname[64]; + + std::strcpy(netname, STRING(pPlayer->v.netname)); + + if (std::strcmp(netname, name) != 0 && // When not the same name + team == UTIL_GetTeam(pPlayer) && // .. the same team... + pPlayer->v.deadflag == DEAD_NO && // .. not dead .. + UTIL_GetBotPointer(pPlayer) != nullptr) // and a RealBot + { + // here are all bots + cBot* BotPointer = UTIL_GetBotPointer(pPlayer); + + if (BotPointer == nullptr) + continue; // somehow this fucked up, bail out + + const float distance = func_distance(plr->v.origin, + BotPointer->pEdict->v.origin); // distance between the 2 + + // Same team, randomly, do we even listen to the radio? + // the more further away, the more chance it will not listen + bool bWantToListen = false; + + // Reply on distance check + if (RANDOM_LONG(0, 8192) > distance) + bWantToListen = true; + + // Hearrate (personality setting) + if (RANDOM_LONG(0, 100) < BotPointer->ipHearRate && + bWantToListen) + bool want_to_answer = true; + + // If we want to listen to the radio... then handle it! + if (bWantToListen) { + bool can_do_negative = false; + + // Report in team! + if (std::strstr(message, "#Report_in_team") != nullptr) { + // gives human knowledge who is on his team + } + + // Regroup team! + if (std::strstr(message, "#Regroup_team") != nullptr) { + // regroup now! + unstood = true; + + // get to the leader position + BotPointer->rprint("Setting goal from regroup team"); + BotPointer->setGoalNode(NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE * 2, plr)); + BotPointer->forgetPath(); + } + + // Hold this position + if (std::strstr(message, "#Hold_this_position") != nullptr || + std::strstr(message, "#Get_in_position_and_wait") != nullptr) { + // do nothing + } + // Follow me!! + if (std::strstr(message, "#Follow_me") != nullptr) {} + + // You take the point! + // 23/06/04 - Stefan - Here the leader should break up his position? + // ie, the leader will be assigned to the bot this human/bot is facing? + if (std::strstr(message, "#You_take_the_point") != nullptr) { + can_do_negative = false; + } + // Enemy Sotted! + if (std::strstr(message, "#Enemy_spotted") != nullptr) { + can_do_negative = false; + + // Find player who issues this message and go to it + const int iBackupNode = + NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE, plr); + + // Help this player + if (iBackupNode > -1) { + + unstood = true; + + BotPointer->rprint("Setting goal for backup"); + BotPointer->setGoalNode(iBackupNode); + BotPointer->forgetPath(); + BotPointer->f_camp_time = gpGlobals->time - 1; + BotPointer->f_walk_time = gpGlobals->time; + } + } + // Enemy Down! + if (std::strstr(message, "#Enemy_down") != nullptr) { + BotPointer->rprint_trace("BotRadioAction", "Understood Enemy down - no logic"); + + unstood = true; + can_do_negative = false; + } + // Stick together team! + if (std::strstr(message, "#Stick_together_team") != nullptr) { + BotPointer->rprint_trace("BotRadioAction", "Understood Stick together team - no logic"); + unstood = true; + can_do_negative = false; + } + // cover me|| strstr (message, "#Cover_me") != NULL + + // Need backup / taking fire... + if (std::strstr(message, "#Need_backup") != nullptr || std::strstr(message, "#Taking_fire") != nullptr) { + BotPointer->rprint_trace("BotRadioAction", "Understood Need backup or Taking fire"); + + unstood = true; + + // get source of backup + const int iBackupNode = NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE, plr); + + if (iBackupNode > -1) { + BotPointer->rprint_trace("BotRadioAction", "Found node nearby player who requested backup/reported taking fire."); + BotPointer->setGoalNode(iBackupNode); + BotPointer->forgetPath(); + BotPointer->f_camp_time = gpGlobals->time - 1; + BotPointer->f_walk_time = gpGlobals->time; + } + } + + // Taking fire! + if (std::strstr(message, "#Taking_fire") != nullptr) { + BotPointer->rprint_trace("BotRadioAction", "Understood Taking fire"); + unstood = true; + + // Find player who issued this message and go to it + const int iBackupNode = + NodeMachine.getClosestNode(plr->v.origin, NODE_ZONE, plr); + + if (iBackupNode > -1) { + BotPointer->rprint_trace("BotRadioAction", "Found node nearby player who requested backup/reported taking fire."); + BotPointer->setGoalNode(iBackupNode); + BotPointer->forgetPath(); + BotPointer->f_camp_time = gpGlobals->time - 1; + BotPointer->f_walk_time = gpGlobals->time; + } + + } + // Team fall back! + if (std::strstr(message, "#Team_fall_back") != nullptr) { + + } + // Go Go Go, stop camping, stop following, get the heck out of there! + if (std::strstr(message, "#Go_go_go") != nullptr) { + BotPointer->rprint_trace("BotRadioAction", "Understood Go Go Go"); + unstood = true; + BotPointer->f_camp_time = gpGlobals->time - 30; + BotPointer->f_walk_time = gpGlobals->time; + BotPointer->f_cover_time = gpGlobals->time - 10; + BotPointer->f_hold_duck = gpGlobals->time - 10; + BotPointer->f_jump_time = gpGlobals->time - 10; + BotPointer->forgetPath(); + BotPointer->forgetGoal(); + } + + if (FUNC_DoRadio(BotPointer) && unstood) { + const int maxAllowedRadios = gpGlobals->maxClients / 4; + if (BotPointer->console_nr == 0 && radios < maxAllowedRadios) { + constexpr bool report_back = false; + + if constexpr (!report_back) { + UTIL_BotRadioMessage(BotPointer, 3, "1", ""); // Roger that! + } + else { + UTIL_BotRadioMessage(BotPointer, 3, "6", ""); // Reporting in! + } + + BotPointer->f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.8f, 2.0f); + radios++; + } + } + } // they even listen to the radio command? + else { + /* + // filter out the commands where we cannot reply with negative + // You take the point! + if (strstr (message, "#You_take_the_point") != NULL) + can_do_negative = false; + + // Enemy Sotted! + if (strstr (message, "#Enemy_spotted") != NULL) + can_do_negative = false; + + // Enemy Down! + if (strstr (message, "#Enemy_down") != NULL) + can_do_negative = false; + + if ((FUNC_DoRadio(BotPointer)) + && (unstood) && (can_do_negative)) + + { + if (BotPointer->console_nr == 0 + && radios < (gpGlobals->maxClients / 4)) + + { + if (report_back == false) + + { + UTIL_BotRadioMessage (BotPointer, 3, "8", ""); // Negative! + BotPointer->f_console_timer = gpGlobals->time + RANDOM_FLOAT (0.8, 2.0); + radios++; + } + } + } + */ + } + } // End check! + } // If (Player) + } // FOR Clients + return true; +} + +// Is entity visible? (from Entity view) +bool EntityIsVisible(edict_t* pEntity, const Vector& dest) { + + //DebugOut("bot: EntityIsVisible()\n"); + TraceResult tr; + + // trace a line from bot's eyes to destination... + UTIL_TraceLine(pEntity->v.origin + pEntity->v.view_ofs, dest, + dont_ignore_monsters, pEntity->v.pContainingEntity, &tr); + + // check if line of sight to object is not blocked (i.e. visible) + if (tr.flFraction >= 1.0f) + return true; + + else + return false; +} + +// Can see Edict? +bool cBot::canSeeEntity(edict_t* pEntity) const +{ + if (pEntity == nullptr) return false; + + TraceResult tr; + const Vector start = pEdict->v.origin + pEdict->v.view_ofs; + const Vector vDest = pEntity->v.origin; + + // trace a line from bot's eyes to destination... + UTIL_TraceLine(start, vDest, ignore_monsters, pEdict->v.pContainingEntity, &tr); + + // it hit anything + if (tr.flFraction < 1.0f) { + // if it hit the entity we wanted to see, then its ok! + if (tr.pHit == pEntity) return true; + return false; + } + + return true; +} + +/** + * Returns distance from this bot to a given nodeIndex. If the given NodeIndex is invalid, the distance returned is 0. + * @param nodeIndex + * @return + */ +float cBot::getDistanceTo(const int nodeIndex) { + const tNode* nodePtr = NodeMachine.getNode(nodeIndex); + if (nodePtr != nullptr) { + return getDistanceTo(nodePtr->origin); + } + rprint("getDistanceTo(int nodeIndex)", "The given nodeIndex was invalid, returning 9999 distance"); + return 9999; +} + +/** + * Returns distance from this bot position (pEdict->v.origin) to given Vector. + * @param vDest + * @return + */ +float cBot::getDistanceTo(const Vector& vDest) const +{ + return func_distance(pEdict->v.origin, vDest); +} + +bool cBot::isUsingHostage(edict_t* pHostage) { + if (pHostage == nullptr) return false; + + // checks if the current pEdict is already 'in use' + // note: time check only when we have an hostage pointer assigned + if (hostage1 == pHostage) { + rprint("isUsingHostage", "hostage1"); + return true; + } + + if (hostage2 == pHostage) { + rprint("isUsingHostage", "hostage2"); + return true; + } + + if (hostage3 == pHostage) { + rprint("isUsingHostage", "hostage3"); + return true; + } + + if (hostage4 == pHostage) { + rprint("isUsingHostage", "hostage4"); + return true; + } + + return false; +} + +void cBot::forgetHostage(edict_t* pHostage) { + // these are the hostages we are rescueing (ie, they are following this bot) + if (hostage1 == pHostage) { + rprint("forgetHostage", "hostage1"); + hostage1 = nullptr; + } + if (hostage2 == pHostage) { + rprint("forgetHostage", "hostage2"); + hostage2 = nullptr; + } + if (hostage3 == pHostage) { + rprint("forgetHostage", "hostage3"); + hostage3 = nullptr; + } + if (hostage4 == pHostage) { + rprint("forgetHostage", "hostage4"); + hostage4 = nullptr; + } + + // this is the hostage we have taken interest in + if (pBotHostage == pHostage) { + rprint("forgetHostage", "pBotHostage"); + pBotHostage = nullptr; + } +} + +int cBot::getAmountOfHostagesBeingRescued() const +{ + int count = 0; + + if (hostage1 != nullptr) count++; + if (hostage2 != nullptr) count++; + if (hostage3 != nullptr) count++; + if (hostage4 != nullptr) count++; + + return count; +} + +// Will return true when the vector is visible. +bool cBot::canSeeVector(const Vector& vDest, edict_t* pTargetEntity) const +{ + TraceResult tr; + const Vector start = pEdict->v.origin + pEdict->v.view_ofs; + + // trace a line from bot's eyes to destination... + UTIL_TraceLine(start, vDest, ignore_monsters, pEdict->v.pContainingEntity, &tr); + + // if trace is not blocked, the vector is visible + if (tr.flFraction >= 1.0f) + { + return true; + } + + // if we have a target entity, check if we hit it + if (pTargetEntity != nullptr && tr.pHit == pTargetEntity) + { + return true; // we hit our target, so we can "see" it + } + + return false; // something is blocking the view +} + +// The coming 2 shield functions where originaly created by Whistler; +// i got them from the JoeBot source though. But... in the end, thank you +// Whistler! +bool cBot::hasShield() const +{ + // Adapted from Wei Mingzhi's YAPB + return strncmp(STRING(pEdict->v.viewmodel), "models/shield/v_shield_", 23) == 0; +} + +bool cBot::hasShieldDrawn() const +{ + // Adapted from Wei Mingzhi's YAPB + if (!hasShield()) + return false; + + return pEdict->v.weaponanim == 6 || pEdict->v.weaponanim == 7; +} + +/* + BotThink() + This function is the very general/main/simplified thinking function of the bot. + Do NOT add/remove/change code here! If you want to give the bot information to + work with. Put it in UpdateStatus(). When the bot has to think about it, do it + int Think() and everything else (when all is set, how to 'do' it) in Act(). + */ +void BotThink(cBot* pBot) { + // STEP 1: Update status + pBot->UpdateStatus(); + + // STEP 2: Think + pBot->Think(); + + // STEP 3: Act + pBot->Act(); + + // PASS THROUGH ENGINE + + // float frameInterval = m_lastCommandTime - gpGlobals->time; + const float msecval = (gpGlobals->time - pBot->fLastRunPlayerMoveTime) * 1000.0f; + pBot->fLastRunPlayerMoveTime = gpGlobals->time; + + constexpr double upMove = 0.0; + char msg[255]; + snprintf(msg, sizeof(msg), "moveSpeed %f, strafeSpeed %f, msecVal %f", pBot->f_move_speed, pBot->f_strafe_speed, msecval); + pBot->rprint_trace("BotThink/pfnRunPlayerMove", msg); + g_engfuncs.pfnRunPlayerMove(pBot->pEdict, pBot->vecMoveAngles, pBot->f_move_speed, pBot->f_strafe_speed, + upMove, pBot->pEdict->v.button, 0, msecval); + + constexpr float fUpdateInterval = 1.0f / 60.0f; // update at 60 fps + pBot->fUpdateTime = gpGlobals->time + fUpdateInterval; +} + +// 17/07/04 +// log important variables of the bot (it will be called from dll.cpp once per active bot) +// they are logged into reallog.txt file + +void cBot::Dump() { + char buffer[181]; + const int iCurrentNode = + NodeMachine.getClosestNode(pEdict->v.origin, (NODE_ZONE * 2), pEdict); + + snprintf(buffer, 180, + "%s (#%d %s): timers, now= %.0f, c4_time=%.0f, camp_time=%.0f, wait_time=%.0f, cover_time=%.0f, wander=%.0f, MoveToNodeTime=%.0f\n", + name, iBotIndex, (iTeam == 1) ? "T" : "CT", gpGlobals->time, + f_c4_time, f_camp_time, f_wait_time, f_cover_time, fWanderTime, fMoveToNodeTime); + rblog(buffer); + snprintf(buffer, 180, " GoalNode=%d, CurrentNode=%d, iPathFlags=", + iGoalNode, iCurrentNode); + switch (iPathFlags) { + case PATH_NONE: + std::strncat(buffer, "PATH_NONE ", 180); + break; + case PATH_DANGER: + std::strncat(buffer, "PATH_DANGER ", 180); + break; + case PATH_CONTACT: + std::strncat(buffer, "PATH_CONTACT ", 180); + break; + case PATH_CAMP: + std::strncat(buffer, "PATH_CAMP ", 180); + break; + default: + std::strncat(buffer, "???", 180); + } + std::strncat(buffer, "\n", 180); + rblog(buffer); + if (iGoalNode >= 0) + NodeMachine.dump_path(iBotIndex, pathIndex); +} + +/** + * Will begin walk the path by setting pathNodeIndex to 0, which is a valid nr so that + * isWalkingPath returns true. + */ +void cBot::beginWalkingPath() { + this->pathIndex = 0; +} + +bool cBot::hasHostageToRescue() const +{ + return pBotHostage != nullptr; +} + +bool cBot::canSeeHostageToRescue() const +{ + return canSeeEntity(pBotHostage); +} + +void cBot::clearHostageToRescueTarget() { + rprint_trace("clearHostageToRescueTarget", "clearing pBotHostage pointer"); + this->pBotHostage = nullptr; +} + +// Finds a free hostage pointer and assigns it. +void cBot::rememberHostageIsFollowingMe(edict_t* pHostage) { + if (pHostage == nullptr) { + rprint_trace("rememberHostageIsFollowingMe", "ERROR assigning NULL pHostage pointer!?"); + } + if (hostage1 == nullptr) { + rprint_trace("rememberHostageIsFollowingMe", "hostage1 slot is free."); + hostage1 = pHostage; + } + else if (hostage2 == nullptr) { + rprint_trace("rememberHostageIsFollowingMe", "hostage2 slot is free."); + hostage2 = pHostage; + } + else if (hostage3 == nullptr) { + rprint_trace("rememberHostageIsFollowingMe", "hostage3 slot is free."); + hostage3 = pHostage; + } + else if (hostage4 == nullptr) { + rprint_trace("rememberHostageIsFollowingMe", "hostage4 slot is free."); + hostage4 = pHostage; + } +} + +void cBot::checkIfHostagesAreRescued() { + if (isHostageRescued(this, hostage1)) forgetHostage(hostage1); + if (isHostageRescued(this, hostage2)) forgetHostage(hostage2); + if (isHostageRescued(this, hostage3)) forgetHostage(hostage3); + if (isHostageRescued(this, hostage4)) forgetHostage(hostage4); +} + +bool cBot::isOnSameTeamAs(const cBot* pBot) const +{ + if (pBot == nullptr) return false; + return pBot->iTeam == this->iTeam; +} + +bool cBot::wantsToBuyStuff() const +{ + return buy_secondary == true || + buy_primary == true || + buy_ammo_primary == true || + buy_ammo_secondary == true || + buy_armor == true || + buy_defusekit == true || + buy_grenade == true || + buy_flashbang > 0; +} + +bool cBot::isUsingConsole() const +{ + return console_nr > 0; +} + +bool cBot::shouldBeAbleToMove() const +{ + return !isDead() && + !isFreezeTime() && + !shouldCamp() && + !shouldWait() && + !shouldActWithC4(); + // !isDucking() && + // !isJumping(); +} + +edict_t* cBot::getEntityBetweenMeAndCurrentPathNodeToHeadFor() const +{ + TraceResult tr; + const Vector vOrigin = pEdict->v.origin; + + const tNode* node = NodeMachine.getNode(getCurrentPathNodeToHeadFor()); + + //Using TraceHull to detect de_aztec bridge and other entities. + //DONT_IGNORE_MONSTERS, we reached it only when there are no other bots standing in our way! + //UTIL_TraceHull(vOrigin, vNode, dont_ignore_monsters, point_hull, pBot->pEdict, &tr); + //UTIL_TraceHull(vOrigin, vNode, dont_ignore_monsters, human_hull, pBot->pEdict, &tr); + UTIL_TraceHull(vOrigin, node->origin, dont_ignore_monsters, head_hull, pEdict, &tr); + + // if nothing hit (else a wall is in between and we don't care about that): + if (tr.flFraction < 1.0f) { + if (tr.pHit) { + return tr.pHit; + } + } + + return nullptr; +} + +/** + * Get distance to the next node we're heading for + * @return + */ +float cBot::getDistanceToNextNode() const +{ + const tNode* node = NodeMachine.getNode(getCurrentPathNodeToHeadFor()); + if (node) { + return getDistanceTo(node->origin); + } + return MAP_MAX_SIZE; +} + +void cBot::setBodyToNode(const int nodeIndex) { + const tNode* node = NodeMachine.getNode(nodeIndex); + if (node) { + vBody = node->origin; + } +} + +void cBot::lookAtNode(const int nodeIndex) { + const tNode* node = NodeMachine.getNode(nodeIndex); + if (node) { + vHead = node->origin; + } +} + +/** + * Sets timer to allow movement to node, when timer expires we will think about severing the connection + * we used. + * @param timeInSeconds + */ +void cBot::setTimeToMoveToNode(const float timeInSeconds) { + char msg[255]; + const float endTime = gpGlobals->time + timeInSeconds; + snprintf(msg, sizeof(msg), "Set to %f so results into end time of %f", timeInSeconds, endTime); + rprint_trace("setTimeToMoveToNode", msg); + + this->nodeTimeIncreasedAmount = 0; + this->fMoveToNodeTime = endTime; +} + +/** + * Whatever was set, increase the time given in function param. This expands the time a bit. + * @param timeInSeconds + */ +void cBot::increaseTimeToMoveToNode(const float timeInSeconds) { + if (nodeTimeIncreasedAmount < 2) { + nodeTimeIncreasedAmount++; + this->fMoveToNodeTime += timeInSeconds; + const float timeToMoveToNodeRemaining = getMoveToNodeTimeRemaining(); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "increaseTimeToMoveToNode with %f for the %d time, making time to move to node remaining %f.", + timeInSeconds, nodeTimeIncreasedAmount, timeToMoveToNodeRemaining); + rprint_trace("increaseTimeToMoveToNode", msg); + } + else { + rprint_trace("increaseTimeToMoveToNode", "Refused to increase time"); + } +} + +float cBot::getMoveToNodeTimeRemaining() const +{ + return fMoveToNodeTime - gpGlobals->time; +} + +bool cBot::shouldCamp() const +{ + return f_camp_time > gpGlobals->time; +} + +bool cBot::shouldWait() const +{ + return f_wait_time > gpGlobals->time; +} + +void cBot::setTimeToWait(const float timeInSeconds) +{ + this->f_wait_time = gpGlobals->time + timeInSeconds; +} + +bool cBot::shouldBeAbleToInteractWithButton() const +{ + return fButtonTime < gpGlobals->time; +} + +bool cBot::hasButtonToInteractWith() const +{ + return pButtonEdict != nullptr; +} + +bool cBot::hasCurrentNode() const +{ + return iCloseNode > -1; +} + +/** + * Shorthand method for creating path with flags PATH_NONE. + * @param destinationNode + * @return + */ +bool cBot::createPath(const int destinationNode) { + return createPath(destinationNode, PATH_NONE); +} + +/** + * Attempts to create a path from current node to destination. Returns true on success, false on failure. + * @param destinationNode + * @param flags + * @return + */ +bool cBot::createPath(const int destinationNode, const int flags) { + if (destinationNode < 0 || destinationNode >= MAX_NODES) { + rprint("createPath()", "Unable to create path because destination node provided is < 0 or > MAX_NODES"); + return false; + } + + const int currentNode = getCurrentNode(); + if (currentNode < 0) { + rprint("createPath()", "Unable to create path to destination because I am not close to a start node"); + return false; + } + + forgetPath(); + + char msg[255] = {}; + snprintf(msg, sizeof(msg), "Creating path from currentNode [%d] to destination node [%d]", currentNode, destinationNode); + rprint("createPath()", msg); + + return NodeMachine.createPath(currentNode, destinationNode, iBotIndex, this, flags); +} + +void cBot::doDuck() { + UTIL_BotPressKey(this, IN_DUCK); + this->f_hold_duck = gpGlobals->time + 0.5f; + + this->increaseTimeToMoveToNode(0.5f); +} + +bool cBot::isDucking() { + const bool b = keyPressed(IN_DUCK) || this->f_hold_duck > gpGlobals->time; + if (b) { + rprint_trace("isDucking", "Yes I am ducking"); + } + return b; +} + +bool cBot::isWalking() { + const bool b = !keyPressed(IN_RUN) || this->f_walk_time > gpGlobals->time; + if (b) { + rprint_trace("isWalking", "Yes I am walking"); + } + return b; +} + +void cBot::doJump(const Vector& vector) { + rprint_trace("doJump", "With vector"); + // stay focussed with body and head to this vector + this->vHead = vector; + this->vBody = vector; + + doJump(); +} + +void cBot::doJump() { + rprint_trace("doJump", "no vector"); + UTIL_BotPressKey(this, IN_JUMP); + this->f_jump_time = gpGlobals->time + 0.75f; + + // duck like this, because doDuck increases node time *again*, so no + UTIL_BotPressKey(this, IN_DUCK); // DUCK jump by default + this->f_hold_duck = gpGlobals->time + 0.5f; + + this->increaseTimeToMoveToNode(0.75f); +} + +bool cBot::isJumping() { + const bool b = keyPressed(IN_JUMP) || this->f_jump_time > gpGlobals->time; + if (b) { + rprint_trace("isJumping", "Yes I am jumping"); + } + return b; +} + +float cBot::DetermineStrafe(const bool bHitForwardLeft, const bool bHitForwardRight, const bool bHitLeft, const bool bHitRight) +{ + // Prioritize avoiding diagonal obstacles + if (!bHitForwardLeft && bHitForwardRight) { + rprint_trace("CheckAround", "Strafing left to avoid forward-right obstacle."); + return -f_max_speed; + } + + if (bHitForwardLeft && !bHitForwardRight) { + rprint_trace("CheckAround", "Strafing right to avoid forward-left obstacle."); + return f_max_speed; + } + + // If diagonals are clear, check for side obstacles + if (!bHitLeft && bHitRight) { + rprint_trace("CheckAround", "Strafing left to avoid right obstacle."); + return -f_max_speed; + } + + if (bHitLeft && !bHitRight) { + rprint_trace("CheckAround", "Strafing right to avoid left obstacle."); + return f_max_speed; + } + + rprint_trace("CheckAround", "No strafing needed or path is blocked on both sides."); + return 0.0f; +} + +// Experimental DuckJump added for the NodeMachine [APG]RoboCop[CL] +// +void cBot::doDuckJump(const Vector& vector) { + rprint_trace("doDuckJump", "With vector"); + // stay focussed with body and head to this vector + this->vHead = vector; + this->vBody = vector; + + doDuckJump(); +} + +void cBot::doDuckJump() { + rprint_trace("doDuckJump", "no vector"); + UTIL_BotPressKey(this, IN_DUCK); + this->f_hold_duck = gpGlobals->time + 0.75f; + + UTIL_BotPressKey(this, IN_JUMP); + this->f_jump_time = gpGlobals->time + 0.75f; + + this->increaseTimeToMoveToNode(0.75f); +} + +// Bots require both the combination of the (IN_DUCK) and (IN_JUMP) key to be pressed +// in order to properly duck jump. +bool cBot::isDuckJumping() { + const bool b = keyPressed(IN_JUMP) && keyPressed(IN_DUCK) || + this->f_hold_duck > gpGlobals->time && this->f_jump_time > gpGlobals->time; + if (b) { + rprint_trace("isDuckJumping", "Yes I am DuckJumping"); + } + return b; +} + +bool cBot::isStuck() const +{ + if (distanceMoved < 1.0f && f_move_speed > 0.0f) { + return true; + } + return false; +} + +// $Log: bot.cpp,v $ +// Revision 1.21 2004/09/07 18:23:02 eric +// - bumped version to 3061 +// - adding Frashman code to buy the weapon as selected by Josh's code +// - Realbot is really the result of multiple people :-) +// +// Revision 1.20 2004/09/07 15:44:34 eric +// - bumped build nr to 3060 +// - minor changes in add2 (to add nodes for Bsp2Rbn utilities) +// - if compiled with USE_EVY_ADD, then the add2() function is used when adding +// nodes based on human players instead of add() +// - else, it now compiles mostly without warnings :-) +// +// Revision 1.19 2004/08/07 18:42:56 eric +// - bumped version to 3058 +// - added a cNodeMachine::add2 which should do the same job as ::add +// but it seems to work better. ::add2 is used by Bsp2Rbn only for now. +// - added the display of node flags (water, ladder, jump) next to the +// node position in 'debug nodes draw' +// - suppress the debugging information which displayed the hit entity +// while establishing neighbourhood +// +// Revision 1.18 2004/07/30 15:02:29 eric +// - jumped to version 3057 +// - improved readibility (wapen_tabel -> weapons_table) :-P +// - all Josh Borke modifications to the buying stuff: +// * using a switch() instead of several if +// * better buying code for shield and primary weapons +// * new command 'debug pistols 0/1' +// +// Revision 1.16 2004/07/17 21:32:01 eric +// - bumped version to 3055 +// - handling of es_ and as_ maps with new goals +// - added two debug commands: +// realbot debug goals +// realbot debug bots +// - added two nodes commands (for dedicated servers mainly) +// realbot nodes connect n1 n2 +// realbot nodes disconnect n1 n2 +// - slight modification in goal scoring (only reduced score when two bots of +// the SAME team select the same goal) +// +// Revision 1.15 2004/07/03 15:58:54 eric +// nova test comment for erics account +// +// Revision 1.14 2004/07/02 16:43:35 stefan +// - upped to build 3051 +// - changed log() into rblog() +// - removed BOT.CFG code that interpets old RB V1.0 commands +// - neater respons of the RealBot console +// - more help from RealBot console (ie, type realbot server broadcast ... with no arguments it will tell you what you can do with this, etc) +// - removed message "bot personality loaded from file" +// - in overal; some cleaning done, no extra features added +// +// Revision 1.13 2004/07/01 18:09:46 stefan +// - fixed skill 10 bots not causing memory bugger on re-adding (respawning) +// - added extra check for respawning bots so auto-add function cannot crash +// - fixed 2 nitpicks pointed out on the forums +// +// Revision 1.12 2004/06/25 07:39:00 stefan +// - upped to build 3050 +// - fixed reaction time (instant reaction time) bug +// - added evy's goals, but they are not used yet +// - fixed some radio responses here and there for swat behaviour. +// - swat leader automaticly assigned again when one dies +// - HINT: you can see any changes made by me, by looking at DD/MM/YY - Stefan (ie, 22/06/04 - Stefan, will let you find all changes i made that day) +// +// Revision 1.11 2004/06/23 08:24:14 stefan +// - upped to build 3049 +// - added swat behaviour (team leader assignment, radio response change and leaders command team-mates) - THIS IS EXPERIMENTAL AND DOES NOT ALWAYS WORK AS I WANT IT TO. +// - changed some conditions in nodemachine +// - sorry evy, still not added your new goals ;) will do next time, i promise +// +// Revision 1.10 2004/06/20 10:24:13 stefan +// - fixed another steep/stair thingy +// - changed a bit of the aiming code +// +// Revision 1.9 2004/06/19 21:06:14 stefan +// - changed distance check in nodemachine +// - fixed some 'steep' bug in nodemachine +// +// Revision 1.8 2004/06/17 21:23:23 stefan +// - fixes several connection problems with nodes. Going down from steep + crates (de_dust) PLUS going up/down from very steep slopes on as_oilrig.. 0wnage and thx to PMB and Evy +// - fixed chat bug in CS 1.6, its still CS 1.5 & CS 1.6 compatible though +// +// Revision 1.7 2004/06/13 20:08:21 stefan +// - 'bad score for goals' added +// - bmp dump info (Thanks Evy) +// - added 'realbot server players', so you can keep a server full at NR players at all times +// - slightly adjusted goal selection code +// - wander code disabled +// - lots of debug info introduced, do not use this source for REAL USAGE! +// +// Revision 1.6 2004/06/01 15:30:57 stefan +// *** empty log message *** +// +// Revision 1.5 2004/05/29 19:05:47 stefan +// - upped to BUILD 3044 +// - removed several debug messages on screen +// - changed default 'chatrate (max sentences)' to 3 +// - removed copyright notice, which is not valid due GPL license +// +// i know, nothing special :) +// +// Revision 1.4 2004/05/07 13:33:49 stefan +// added more comments, more neat code now +// +// Revision 1.3 2004/04/18 18:32:36 stefan +// - Restructured code a bit +// +// Revision 1.2 2004/04/18 17:39:19 stefan +// - Upped to build 2043 +// - REALBOT_PRINT() works properly now +// - Log() works properly now +// - Clearing in dll.cpp of reallog.txt at dll init +// - Logging works now, add REALBOT_PRINT() at every point you want to log something. +// \ No newline at end of file diff --git a/bot.h b/bot.h index b3a6ec9..63576f8 100644 --- a/bot.h +++ b/bot.h @@ -6,7 +6,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +18,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -46,99 +46,99 @@ typedef struct { extern weapon_price_table weapons_table[32]; // BOT SPECIFIC / AUDIOABLE / VISUAL PROPERTIES -#define BOT_HEARDISTANCE 1500 -#define BOT_HEARFIREDISTANCE 2500 +constexpr int BOT_HEARDISTANCE = 1500; +constexpr int BOT_HEARFIREDISTANCE = 2500; // define constants used to identify the MOD we are playing... -#define VALVE_DLL 1 // Added "Half-Life DeathMatch" -#define CSTRIKE_DLL 3 // Added "Counter-Strike" +constexpr int VALVE_DLL = 1; // Added "Half-Life DeathMatch"; +constexpr int CSTRIKE_DLL = 3; // Added "Counter-Strike"; // max names in rb_names.txt -#define MAX_BOT_NAMES 100 +constexpr int MAX_BOT_NAMES = 100; // Define Join states for other mods -#define JOIN_TEAM 1 -#define JOIN_CLASS 2 -#define JOIN_NONE 9999 +constexpr int JOIN_TEAM = 1; +constexpr int JOIN_CLASS = 2; +constexpr int JOIN_NONE = 9999; // fix for steam (cs 1.6 bot will crash when FL_FAKECLIENT; // this is fixed later again in STEAM, but leave it in anway). -#define FL_THIRDPARTYBOT (1 << 27) +constexpr int FL_THIRDPARTYBOT = (1 << 27); // "Own predefined" item types (attached with buying code) -#define CS_WEAPON_ARMOR_LIGHT 199 -#define CS_WEAPON_ARMOR_HEAVY 299 +constexpr int CS_WEAPON_ARMOR_LIGHT = 199; +constexpr int CS_WEAPON_ARMOR_HEAVY = 299; // damage tolerance -#define CSTRIKE_MIN_DAMAGE 7 -#define CSTRIKE_MAX_DAMAGE 14 +constexpr int CSTRIKE_MIN_DAMAGE = 7; +constexpr int CSTRIKE_MAX_DAMAGE = 14; // The type of a weapon, not mod related. You can use these prefixed names // for your functions -#define SNIPER 10 // Sniper gun -#define RIFLE 20 // Rifle -#define HANDGUN 30 // Handgun -#define ARMOR 40 // Armor -#define PRIMARY 50 // Its a primary weapon (for CS Mods) -#define SECONDARY 60 // Its a secondary weapon (for CS Mods) -#define GRENADE 70 // Grenade (aka, HE, Flashbang, C4, etc) -#define KNIFE 80 // Knife / Crowbar etc -#define SHIELD 90 // Shield (CS 1.6) -#define NONE 99 // No type +constexpr int SNIPER = 10; // Sniper gun; +constexpr int RIFLE = 20; // Rifle; +constexpr int HANDGUN = 30; // Handgun; +constexpr int ARMOR = 40; // Armor; +constexpr int PRIMARY = 50; // Its a primary weapon (for CS Mods); +constexpr int SECONDARY = 60; // Its a secondary weapon (for CS Mods) +constexpr int GRENADE = 70; // Grenade (aka, HE, Flashbang, C4, etc); +constexpr int KNIFE = 80; // Knife / Crowbar etc +constexpr int SHIELD = 90; // Shield (CS 1.6) +constexpr int NONE = 99; // No type // Strafe //#define STRAFE_LEFT 0 //#define STRAFE_RIGHT 1 // zooming -#define ZOOM_NONE 0 -#define ZOOM_ONCE 1 -#define ZOOM_TWICE 2 +constexpr int ZOOM_NONE = 0; +constexpr int ZOOM_ONCE = 1; +constexpr int ZOOM_TWICE = 2; // instant damage (from cbase.h) -#define DMG_CRUSH (1 << 0) // crushed by falling or moving object -#define DMG_BURN (1 << 3) // heat burned -#define DMG_FREEZE (1 << 4) // frozen -#define DMG_FALL (1 << 5) // fell too far -#define DMG_SHOCK (1 << 8) // electric shock -#define DMG_DROWN (1 << 14) // Drowning -#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad -#define DMG_RADIATION (1 << 18) // radiation exposure -#define DMG_DROWNRECOVER (1 << 19) // drowning recovery -#define DMG_ACID (1 << 20) // toxic chemicals or acid burns -#define DMG_SLOWBURN (1 << 21) // in an oven -#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer +constexpr int DMG_CRUSH = (1 << 0); // crushed by falling or moving object +constexpr int DMG_BURN = (1 << 3); // heat burned +constexpr int DMG_FREEZE = (1 << 4); // frozen +constexpr int DMG_FALL = (1 << 5); // fell too far +constexpr int DMG_SHOCK = (1 << 8); // electric shock +constexpr int DMG_DROWN = (1 << 14); // Drowning +constexpr int DMG_NERVEGAS = (1 << 16); // nerve toxins, very bad +constexpr int DMG_RADIATION = (1 << 18); // radiation exposure +constexpr int DMG_DROWNRECOVER = (1 << 19); // drowning recovery +constexpr int DMG_ACID = (1 << 20); // toxic chemicals or acid burns +constexpr int DMG_SLOWBURN = (1 << 21); // in an oven +constexpr int DMG_SLOWFREEZE = (1 << 22); // in a subzero freezer; // define some function prototypes... -void FakeClientCommand(edict_t *pBot, char *arg1, char *arg2, char *arg3); +void FakeClientCommand(edict_t* pBot, const char* arg1, const char* arg2, const char* arg3); -#define LADDER_UNKNOWN 0 -#define LADDER_UP 1 -#define LADDER_DOWN 2 +constexpr int LADDER_UNKNOWN = 0; +constexpr int LADDER_UP = 1; +constexpr int LADDER_DOWN = 2; -#define WANDER_LEFT 1 -#define WANDER_RIGHT 2 +constexpr int WANDER_LEFT = 1; +constexpr int WANDER_RIGHT = 2; -#define BOT_PITCH_SPEED 20 -#define BOT_YAW_SPEED 20 +constexpr int BOT_PITCH_SPEED = 30; +constexpr int BOT_YAW_SPEED = 30; -#define RESPAWN_NONE 0 // Added by Stefan -#define RESPAWN_IDLE 1 -#define RESPAWN_NEED_TO_RESPAWN 2 -#define RESPAWN_IS_RESPAWNING 3 +constexpr int RESPAWN_NONE = 0; // Added by Stefan; +constexpr int RESPAWN_IDLE = 1; +constexpr int RESPAWN_NEED_TO_RESPAWN = 2; +constexpr int RESPAWN_IS_RESPAWNING = 3; // game start messages for CS... -#define MSG_CS_IDLE 1 -#define MSG_CS_TEAM_SELECT 2 -#define MSG_CS_CT_SELECT 3 -#define MSG_CS_T_SELECT 4 +constexpr int MSG_CS_IDLE = 1; +constexpr int MSG_CS_TEAM_SELECT = 2; +constexpr int MSG_CS_CT_SELECT = 3; +constexpr int MSG_CS_T_SELECT = 4; -#define BOT_SKIN_LEN 32 -#define BOT_NAME_LEN 32 +constexpr int BOT_SKIN_LEN = 32; +constexpr int BOT_NAME_LEN = 32; -#define MAX_BOT_WHINE 100 +constexpr int MAX_BOT_WHINE = 100; -#define REMEMBER_ENEMY_TIME 20 // remember for 20 seconds +constexpr int REMEMBER_ENEMY_TIME = 20; // remember for 20 seconds typedef struct { int iId; // weapon ID @@ -146,10 +146,10 @@ typedef struct { int iAmmo1; // amount of ammo in primary reserve int iAmmo2; // amount of ammo in secondary reserve } - bot_current_weapon_t; +bot_current_weapon_t; /* - Bot is a class now + Bot is a class now */ class cBot { @@ -159,13 +159,13 @@ class cBot { int pathIndex; // Index on theWhich node we want to move to, derived from Path // Hostages edict - edict_t *pBotHostage; // the Hostage we will go after! - edict_t *hostage1; // we do not - edict_t *hostage2; // use - edict_t *hostage3; // any array - edict_t *hostage4; // here + edict_t* pBotHostage; // the Hostage we will go after! + edict_t* hostage1; // we do not + edict_t* hostage2; // use + edict_t* hostage3; // any array + edict_t* hostage4; // here - edict_t *pEnemyEdict; // Enemy edict + edict_t* pEnemyEdict; // Enemy edict float fMoveToNodeTime; // How long we should take to move to next node. int nodeTimeIncreasedAmount; // how many times did we increase the time to reach node? @@ -182,19 +182,19 @@ class cBot { cBot(); // Hostage related - bool isUsingHostage(edict_t *pHostage); - void forgetHostage(edict_t *pHostage); - void rememberHostageIsFollowingMe(edict_t *pHostage); - void rememberWhichHostageToRescue(edict_t *pHostage); + bool isUsingHostage(edict_t* pHostage); + void forgetHostage(edict_t* pHostage); + void rememberHostageIsFollowingMe(edict_t* pHostage); + void rememberWhichHostageToRescue(edict_t* pHostage); void clearHostageToRescueTarget(); - int getAmountOfHostagesBeingRescued(); - edict_t * findHostageToRescue(); // finds a hostage to rescue - edict_t * getHostageToRescue(); // returns hostage state pointer + int getAmountOfHostagesBeingRescued() const; + edict_t* findHostageToRescue(); // finds a hostage to rescue + edict_t* getHostageToRescue() const; // returns hostage state pointer bool isEscortingHostages(); // Does the bot has used any hostages yet? void checkOfHostagesStillFollowMe(); - bool hasTimeToMoveToNode(); - bool isDefusing(); + bool hasTimeToMoveToNode() const; + bool isDefusing() const; // ------------------------ // TIMERS @@ -307,24 +307,26 @@ class cBot { // Remember stuck stuff int iJumpTries; int iDuckTries; + int iDuckJumpTries; // Experimental DuckJump added for this new node [APG]RoboCop[CL] // ------------------------ // BOOLEANS // ------------------------ - bool buy_primary; - bool buy_secondary; + bool vip; + bool bWalkKnife; bool buy_ammo_primary; bool buy_ammo_secondary; + bool buy_primary; + bool buy_secondary; bool buy_armor; - bool buy_grenade; - bool buy_smokegrenade; //31.08.04 Frashman added support for Smoke Grenade - bool bIsUsed; // Bot is 'used'/'playing' (if set to true, the bot is active) - bool bInitialize; bool buy_defusekit; - bool bWalkKnife; // likes to walk around with knife - bool vip; - bool bFirstOutOfSight; + bool buy_grenade; + bool buy_smokegrenade; + bool bIssuedInitialRadio; + + bool bInitialize; // Has the bot been initialized yet? + bool bIsUsed; // Is this bot slot used? // ------------------------ // HUD @@ -335,10 +337,12 @@ class cBot { char chChatSentence[160]; // ------------------------ - // - // ------------------------ - - edict_t *pEdict; // Edict_t of the bot + // POINTERS + // ------------------------ + edict_t* pBreakableEdict; // Breakable edict + edict_t* pButtonEdict; // button edict + edict_t* killer_edict; // Killer edict + edict_t* pEdict; // Edict_t of the bot char name[BOT_NAME_LEN + 1]; char skin[BOT_SKIN_LEN + 1]; @@ -369,10 +373,6 @@ class cBot { float f_bot_see_enemy_time; float f_bot_find_enemy_time; - edict_t *pButtonEdict; // button edict - - edict_t *killer_edict; // Killer edict - Vector vecMoveAngles; // Vector we move to @@ -394,29 +394,29 @@ class cBot { void CheckGear(); void ThinkAboutGoals(); // Think about goals - bool TakeCover(); + bool TakeCover() const; - bool CarryWeapon(int iType); + bool CarryWeapon(int iType) const; - int CarryWeaponType(); + int CarryWeaponType() const; - void setHeadAiming(Vector vTarget); // Aim at vector + void setHeadAiming(const Vector& vTarget); // Aim at vector void AimAtEnemy(); void PickBestWeapon(); void FireWeapon(); - int ReturnTurnedAngle(float speed, float current, float ideal); + static vec_t ReturnTurnedAngle(float speed, float current, float ideal); int FindEnemy(); float ReactionTime(int iSkill); // Reaction time based upon skill void FindCover(); - bool canSeeVector(Vector vDest); + bool canSeeVector(const Vector& vDest, edict_t* pTargetEntity = nullptr) const; - bool canSeeEntity(edict_t *pEntity); + bool canSeeEntity(edict_t* pEntity) const; void InteractWithFriends(); @@ -427,40 +427,40 @@ class cBot { // Set methods int determineCurrentNode(); int determineCurrentNodeWithTwoAttempts(); - int determineCurrentNode(float range); + int determineCurrentNode(float range) const; // Get methods float getDistanceTo(int nodeIndex); - float getDistanceTo(Vector vDest); + float getDistanceTo(const Vector& vDest) const; // "is" Methods (booleans, statements, etc) - bool isDead(); - bool isHeadingForGoalNode(); - bool isEnemyAlive(); // If enemy set, is it alive? (else returns false) - bool isOnLadder(); // Bot on ladder or not? + bool isDead() const; + bool isHeadingForGoalNode() const; + bool isEnemyAlive() const; // If enemy set, is it alive? (else returns false) + bool isOnLadder() const; // Bot on ladder or not? bool isSeeingEnemy(); // If enemy set, can we see it? (takes blinded by flashbang into account) - bool isCounterTerrorist(); - bool isTerrorist(); + bool isCounterTerrorist() const; + bool isTerrorist() const; // "is" methods, related to weapons - bool wantsToBuyStuff(); - bool isUsingConsole(); - bool isOwningWeapon(int weaponId); - bool isHoldingWeapon(int weaponId); - bool ownsFavoritePrimaryWeapon(); - bool ownsFavoriteSecondaryWeapon(); - bool hasFavoritePrimaryWeaponPreference(); - bool hasFavoriteSecondaryWeaponPreference(); - bool hasPrimaryWeaponEquiped(); - bool hasSecondaryWeaponEquiped(); - bool hasPrimaryWeapon(int weaponId); - bool hasSecondaryWeapon(int weaponId); - bool canAfford(int weaponId); + bool wantsToBuyStuff() const; + bool isUsingConsole() const; + bool isOwningWeapon(int weaponId) const; + bool isHoldingWeapon(int weaponId) const; + bool ownsFavoritePrimaryWeapon() const; + bool ownsFavoriteSecondaryWeapon() const; + bool hasFavoritePrimaryWeaponPreference() const; + bool hasFavoriteSecondaryWeaponPreference() const; + bool hasPrimaryWeaponEquiped() const; + bool hasSecondaryWeaponEquiped() const; + //bool hasPrimaryWeapon(int weaponId) const; + //bool hasSecondaryWeapon(int weaponId) const; + bool canAfford(int price) const; //price muddled with weaponId? [APG]RoboCop[CL] // ------------------- // 20/06/04 - CS 1.6 shield functions - bool hasShield(); - bool hasShieldDrawn(); + bool hasShield() const; + bool hasShieldDrawn() const; bool isHoldingGrenadeOrFlashbang() const; bool isBlindedByFlashbang() const; @@ -468,26 +468,24 @@ class cBot { // void / action methods void pickWeapon(int weaponId); void performBuyActions(int weaponIdToBuy); - void performBuyWeapon(const char *menuItem, const char *subMenuItem); + void performBuyWeapon(const char* menuItem, const char* subMenuItem); // ------------------- void CheckAround(); // Check around body - // ------------------- - // - bool hasEnemy(); - bool hasEnemy(edict_t * pEdict); - edict_t * getEnemyEdict(); + bool hasEnemy() const; + bool hasEnemy(const edict_t* pEntity) const; + edict_t* getEnemyEdict() const; - bool hasGoal(); - bool hasGoalIndex(); - tGoal *getGoalData(); + bool hasGoal() const; + bool hasGoalIndex() const; + tGoal* getGoalData() const; bool shouldBeWandering(); - bool hasBomb(); + bool hasBomb() const; // ------------ - bool isWalkingPath(); + bool isWalkingPath() const; void beginWalkingPath(); void nextPathIndex(); // increases index so we know which node to walk on path @@ -496,20 +494,18 @@ class cBot { void forgetGoal(); void forgetPath(); void forgetEnemy(); - void setGoalNode(tGoal *goal); + void setGoalNode(tGoal* goal); void setGoalNode(int nodeIndex); void setGoalNode(int nodeIndex, int iGoalIndex); int getCurrentNode(); // the current (closest) node we are at - int getCurrentPathNodeToHeadFor(); // get Node from path - int getPreviousPathNodeToHeadFor(); // get previous Node from path - int getNextPathNode(); // get next Node from path - int getPathIndex(); - int getPreviousPathIndex(); - - int getGoalNode(); - + int getCurrentPathNodeToHeadFor() const; // get Node from path + int getPreviousPathNodeToHeadFor() const; // get previous Node from path + int getNextPathNode() const; // get next Node from path + int getPathIndex() const; + int getPreviousPathIndex() const; + int getGoalNode() const; void setMoveSpeed(float value); void setStrafeSpeed(float value, float time); @@ -522,28 +518,28 @@ class cBot { void NewRound(); // When bot enters new round // Debug ------- - void rprint(const char *Function, const char *msg); // low - void rprint_normal(const char *Function, const char *msg); // normal - void rprint_trace(const char *Function, const char *msg); // trace + void rprint(const char* Function, const char* msg); // low + void rprint_normal(const char* Function, const char* msg); // normal + void rprint_trace(const char* Function, const char* msg); // trace - void rprint(const char *msg); - void rprint_normal(const char *msg); - void rprint_trace(const char *msg); + void rprint(const char* msg); + void rprint_normal(const char* msg); + void rprint_trace(const char* msg); void Dump(); - bool hasHostageToRescue(); + bool hasHostageToRescue() const; - bool canSeeHostageToRescue(); + bool canSeeHostageToRescue() const; void checkIfHostagesAreRescued(); - bool isOnSameTeamAs(cBot *pBot); + bool isOnSameTeamAs(const cBot* pBot) const; - bool shouldBeAbleToMove(); + bool shouldBeAbleToMove() const; - edict_t *getEntityBetweenMeAndCurrentPathNodeToHeadFor(); + edict_t* getEntityBetweenMeAndCurrentPathNodeToHeadFor() const; - float getDistanceToNextNode(); + float getDistanceToNextNode() const; void setBodyToNode(int nodeIndex); @@ -552,99 +548,144 @@ class cBot { void setTimeToMoveToNode(float timeInSeconds); void increaseTimeToMoveToNode(float timeInSeconds); - float getMoveToNodeTimeRemaining(); + float getMoveToNodeTimeRemaining() const; bool shouldActWithC4() const; int keyPressed(int key) const; - bool shouldCamp(); - bool shouldWait(); + bool shouldCamp() const; + bool shouldWait() const; void setTimeToWait(float timeInSeconds); - bool shouldBeAbleToInteractWithButton(); + bool shouldBeAbleToInteractWithButton() const; - bool hasButtonToInteractWith(); - bool hasCurrentNode(); + bool hasButtonToInteractWith() const; + bool hasCurrentNode() const; bool createPath(int destinationNode, int flags); bool createPath(int destinationNode); - void doJump(Vector &vector); + void doJump(const Vector& vector); void doJump(); bool isJumping(); + // Experimental DuckJump added for the NodeMachine [APG]RoboCop[CL] + void doDuckJump(const Vector& vector); + void doDuckJump(); + bool isDuckJumping(); + + bool isStuck() const; + void doDuck(); bool isDucking(); + bool isWalking(); bool isFreezeTime() const; void rememberEnemyFound(); +private: + float DetermineStrafe(bool bHitForwardLeft, bool bHitForwardRight, bool bHitLeft, bool bHitRight); }; // new UTIL.CPP functions... -edict_t *UTIL_FindEntityInSphere(edict_t *pentStart, - const Vector &vecCenter, float flRadius); +edict_t* UTIL_FindEntityInSphere(edict_t* pentStart, + const Vector& vecCenter, float flRadius); -edict_t *UTIL_FindEntityByString(edict_t *pentStart, - const char *szKeyword, - const char *szValue); +edict_t* UTIL_FindEntityByString(edict_t* pentStart, + const char* szKeyword, + const char* szValue); -edict_t *UTIL_FindEntityByClassname(edict_t *pentStart, - const char *szName); +edict_t* UTIL_FindEntityByClassname(edict_t* pentStart, + const char* szName); -edict_t *UTIL_FindEntityByTargetname(edict_t *pentStart, - const char *szName); +edict_t* UTIL_FindEntityByTargetname(edict_t* pentStart, + const char* szName); -void ClientPrint(edict_t *pEdict, int msg_dest, const char *msg_name); +void ClientPrint(edict_t* pEdict, int msg_dest, const char* msg_name); -void UTIL_SayText(const char *pText, edict_t *pEdict); +void UTIL_SayText(const char* pText, edict_t* pEdict); -void UTIL_HostSay(edict_t *pEntity, int teamonly, char *message); +void UTIL_HostSay(edict_t* pEntity, int teamonly, char* message); -int UTIL_GetTeam(edict_t *pEntity); +int UTIL_GetTeam(edict_t* pEntity); -int UTIL_GetClass(edict_t *pEntity); +int UTIL_GetClass(edict_t* pEntity); -bool IsAlive(edict_t *pEdict); +bool IsAlive(edict_t* pEdict); -bool FInViewCone(Vector *pOrigin, edict_t *pEdict); +bool FInViewCone(Vector* pOrigin, edict_t* pEdict); -bool FVisible(const Vector &vecOrigin, edict_t *pEdict); +bool FVisible(const Vector& vecOrigin, edict_t* pEdict); -Vector Center(edict_t *pEdict); +Vector Center(edict_t* pEdict); -Vector GetGunPosition(edict_t *pEdict); +Vector GetGunPosition(edict_t* pEdict); -Vector VecBModelOrigin(edict_t *pEdict); +Vector VecBModelOrigin(edict_t* pEdict); -void UTIL_ShowMenu(edict_t *pEdict, int slots, int displaytime, bool needmore, char *pText); +void UTIL_ShowMenu(edict_t* pEdict, int slots, int displaytime, bool needmore, const char* pText); -void UTIL_SelectItem(edict_t *pEdict, char *item_name); +void UTIL_SelectItem(edict_t* pEdict, const char* item_name); -void UTIL_BuildFileName(char *filename, char *arg1, char *arg2); +void UTIL_BuildFileName(char* filename, const char* arg1, const char* arg2); -void UTIL_BuildFileNameRB(char *subdir, char *filename); +void UTIL_BuildFileNameRB(const char* subdir, char* filename); -unsigned short FixedUnsigned16(float value, float scale); +unsigned short fixed_unsigned16(float value, float scale); //redundant declaration? [APG]RoboCop[CL] -short FixedSigned16(float value, float scale); +short fixed_signed16(float value, float scale); //redundant declaration? [APG]RoboCop[CL] -void HUD_DrawString(int r, int g, int b, char *msg, edict_t *edict); +void HUD_DrawString(int r, int g, int b, const char* msg, edict_t* edict); -void UTIL_FixAngles(Vector *Angles); +void UTIL_FixAngles(Vector* Angles); -void UTIL_SayTextBot(const char *pText, cBot *pBot); +void UTIL_SayTextBot(const char* pText, cBot* pBot); // combat.h // Bot specific -int UTIL_GetBotIndex(edict_t *pEdict); +int UTIL_GetBotIndex(edict_t* pEdict); + +cBot* UTIL_GetBotPointer(edict_t* pEdict); + +// IniParser.ini +void INI_Section(char input[80], char section[30]); +void INI_Word(char input[80], char word[25]); +int INI_WordType(char word[25], int section); +void INI_Sentence(FILE* f, char result[80]); -cBot *UTIL_GetBotPointer(edict_t *pEdict); +int INI_SectionType(char section[30], int last); +int INI_SectionType_BUYTABLE(char section[30], int last); +int INI_WordValueINT(char result[80]); + +float INI_WordValueFLOAT(char result[80]); + +void INI_WordValueCHAR(char result[80], char value[80]); // dll.cpp +void UpdateClientData(const edict_s* ent, int sendweapons, clientdata_s* cd); +void ProcessBotCfgFile(); bool BotRadioAction(); +void GameDLLInit(); + +#ifdef _WIN32 +int WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); +#endif + +int ClientConnect(edict_t* pEntity, const char* pszName, const char* pszAddress, char szRejectReason[128]); + +void ClientDisconnect(edict_t* pEntity); +void ConsoleThink(cBot* pBot); +void ClientPutInServer(edict_t* pEntity); +void StartFrame(); +void RealBot_ServerCommand(); + +int Spawn(edict_t* pent); +int Spawn_Post(edict_t* pent); + +// util.cpp +void UTIL_BotSprayLogo(edict_t* pEntity, const char* logo_name); #endif // BOT_H diff --git a/bot_buycode.cpp b/bot_buycode.cpp index 4f2934c..6038bb3 100644 --- a/bot_buycode.cpp +++ b/bot_buycode.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -27,7 +29,9 @@ * **/ -#include +#include +#include +#include #include #include #include @@ -52,17 +56,17 @@ void LoadDefaultWeaponTable() { /* REMOVED */ } // Returns price of a weapon, checking on WEAPON_ID (of CS) -int PriceWeapon(int weapon_id) { +int PriceWeapon(const int weapon_id) { return weapons_table[weapons_table[weapon_id].iIdIndex].price; } // Returns the ID in the list, checking on WEAPON_ID (of CS) -int ListIdWeapon(int weapon_id) { +int ListIdWeapon(const int weapon_id) { return weapons_table[weapon_id].iIdIndex; } // The bot will be buying this weapon -void BotPrepareConsoleCommandsToBuyWeapon(cBot *pBot, const char *arg1, const char *arg2) { +void BotPrepareConsoleCommandsToBuyWeapon(cBot* pBot, const char* arg1, const char* arg2) { // To be sure the console will only change when we MAY change. // The values will only be changed when console_nr is 0 if (Game.getRoundStartedTime() + 4 < gpGlobals->time) @@ -70,12 +74,16 @@ void BotPrepareConsoleCommandsToBuyWeapon(cBot *pBot, const char *arg1, const ch if (pBot->console_nr == 0) { // set up first command and argument - strcpy(pBot->arg1, "buy"); - strcpy(pBot->arg2, arg1); + snprintf(pBot->arg1, sizeof(pBot->arg1), "buy"); + snprintf(pBot->arg2, sizeof(pBot->arg2), "%s", arg1); // add argument - if (arg2 != NULL) - strcpy(pBot->arg3, arg2); + if (arg2 != nullptr) { + snprintf(pBot->arg3, sizeof(pBot->arg3), "%s", arg2); + } + else { + pBot->arg3[0] = '\0'; + } pBot->console_nr = 1; // start console command sequence } @@ -88,69 +96,35 @@ void BotPrepareConsoleCommandsToBuyWeapon(cBot *pBot, const char *arg1, const ch * @param team * @return */ -bool GoodWeaponForTeam(int weapon, int team) { +bool GoodWeaponForTeam(const int weapon, const int team) { // Mod CS: if (mod_id == CSTRIKE_DLL) { - - // When not playing Counter-Strike 1.6, these weapons are automaticly wrong for all teams. - if (counterstrike == 0) - if (weapon == CS_WEAPON_GALIL || weapon == CS_WEAPON_FAMAS - || weapon == CS_WEAPON_SHIELD) + // When not playing Counter-Strike 1.6, these weapons are automatically wrong for all teams. + if (counterstrike == 0) { + if (weapon == CS_WEAPON_GALIL || weapon == CS_WEAPON_FAMAS || weapon == CS_WEAPON_SHIELD) { return false; + } + } + + // Define team-specific restricted weapons + static const std::unordered_set ct_restricted = { + CS_WEAPON_SG552, CS_WEAPON_AK47, CS_WEAPON_DEAGLE, CS_WEAPON_MP5NAVY, + CS_WEAPON_GALIL, CS_WEAPON_P90, CS_WEAPON_G3SG1 + }; - // Check the real thing - if (team == 2) // Counter-Terrorist - { - // 30/07/04 by Josh - // Use a switch instead of multiple if - switch (weapon) { - case CS_WEAPON_SG552: - return false; - break; - case CS_WEAPON_AK47: - return false; - break; - case CS_WEAPON_ELITE: - return false; - break; - case CS_WEAPON_MAC10: - return false; - break; - case CS_WEAPON_GALIL: - return false; - break; - // 30.8.04 added by frashman - case CS_WEAPON_G3SG1: - return false; - break; + static const std::unordered_set t_restricted = { + CS_WEAPON_AUG, CS_WEAPON_DEAGLE, CS_WEAPON_M4A1, CS_WEAPON_MP5NAVY, + CS_WEAPON_FAMAS, CS_WEAPON_P90, CS_WEAPON_SG550, CS_DEFUSEKIT + }; + + if (team == 2) { // Counter-Terrorist + if (ct_restricted.count(weapon)) { + return false; } - } else { - switch (weapon) { - case CS_WEAPON_AUG: - return false; - break; - case CS_WEAPON_FIVESEVEN: - return false; - break; - case CS_WEAPON_M4A1: - return false; - break; - case CS_WEAPON_TMP: - return false; - break; - case CS_WEAPON_FAMAS: - return false; - break; - case CS_WEAPON_SHIELD: - return false; - break; - //30.8.04 added by Frashman - case CS_WEAPON_SG550: - return false; - break; - case CS_DEFUSEKIT: - return false; - break; + } + else { // Terrorist + if (t_restricted.count(weapon)) { + return false; } } } @@ -160,273 +134,195 @@ bool GoodWeaponForTeam(int weapon, int team) { /* BotDecideWhatToBuy() - + In this function the bot will choose what weapon to buy from the table. */ -void BotDecideWhatToBuy(cBot *pBot) { - /** - * Stefan 02/09/2018 - * - * This function is called multiple frames. And it basically checks boolean flags (buy_*) method, if true - * then a weapon to buy is choosen. Once choosen it is bought. The flag which was 'true' now became 'false'. - * - * Then the next frame, the boolean flag before will become 'false' and thus another if block is executed. - * - * Effectively doing: - * - buy primary weapon first - * - then secondary - * - ammo for primary - * - ammo for secondary - * - armor - * - defuse kit - * - etc. - * - * the order of if statements is leading - * - * In other words, it needs refactoring... - * - */ - int money = pBot->bot_money; // Money - int team = pBot->iTeam; // Team - +int BotBuyPrimaryWeapon(cBot *pBot) { + const int money = pBot->bot_money; + const int team = pBot->iTeam; int buy_weapon = -1; - // Buy a primary weapon, think of the best choice - if (pBot->buy_primary) { - pBot->rprint("BotDecideWhatToBuy()", "buy_primary"); - // Buy primary - - // Personality related: - // Check if we can buy our favorite weapon - if (pBot->hasFavoritePrimaryWeaponPreference()) { - pBot->rprint("BotDecideWhatToBuy()", "I have a primary weapon preference"); - - if (!pBot->ownsFavoritePrimaryWeapon()) { - pBot->rprint("BotDecideWhatToBuy()", "I do not own my primary weapon preference"); - - if (GoodWeaponForTeam(pBot->ipFavoPriWeapon, pBot->iTeam)) { // can we buy it for this team? - - if (pBot->canAfford(PriceWeapon(pBot->ipFavoPriWeapon))) { // can we afford it? - // Buy favorite weapon! - pBot->rprint("BotDecideWhatToBuy()", "Can buy my favorite primary weapon, doing it"); - buy_weapon = pBot->ipFavoPriWeapon; - } else { - pBot->rprint("BotDecideWhatToBuy()", "Can't afford my favorite primary weapon."); - - // bot personality: if we want to save money for our favorite weapon, then set other values to false - if (RANDOM_LONG(0, 100) < pBot->ipSaveForWeapon) { // 31.08.04 Frashman forgotten brace - pBot->rprint("Decided to save extra money"); - pBot->buy_primary = false; - pBot->buy_secondary = false; // don't buy a secondary - return; // get out of function, don't buy anything - } - } + // Personality related: + // Check if we can buy our favorite weapon + if (pBot->hasFavoritePrimaryWeaponPreference()) { + if (!pBot->ownsFavoritePrimaryWeapon()) { + if (GoodWeaponForTeam(pBot->ipFavoPriWeapon, pBot->iTeam)) { // can we buy it for this team? + if (pBot->canAfford(PriceWeapon(pBot->ipFavoPriWeapon))) { // can we afford it? + return pBot->ipFavoPriWeapon; + } + // bot personality: if we want to save money for our favorite weapon, then set other values to false + if (RANDOM_LONG(0, 100) < pBot->ipSaveForWeapon) { + pBot->rprint("Decided to save extra money"); + pBot->buy_secondary = false; // don't buy a secondary + return -2; // Special value to indicate "stop buying" } - } else { - pBot->rprint("BotDecideWhatToBuy()", "I already have my favorite primary weapon"); - // already have my favorite weapon - pBot->buy_primary = false; // do not buy a primary weapon - return; } } + else { + // already have my favorite weapon + return -2; // Stop buying + } + } - // not decided what to buy yet - if (buy_weapon < 0) { - pBot->rprint("BotDecideWhatToBuy()", "I have no primary weapon preference, deciding what to buy."); - - // Find weapon we can buy in the list of weapons - for (int i = 0; i < MAX_WEAPONS; i++) { + // Find weapon we can buy in the list of weapons + for (const weapon_price_table& i : weapons_table) { + if (UTIL_GiveWeaponType(i.iId) != PRIMARY && UTIL_GiveWeaponType(i.iId) != SHIELD) + continue; - // 31.08.04 Frashman Filter Out all except PRIMARY and SHIELD - // SHIELD is used as primary weapon + if (!GoodWeaponForTeam(i.iId, team)) + continue; - if ((UTIL_GiveWeaponType(weapons_table[i].iId) != PRIMARY) - && (UTIL_GiveWeaponType(weapons_table[i].iId) != SHIELD)) - continue; - - // must be a weapon that the team can buy (CT/T weapon) - if (!GoodWeaponForTeam(weapons_table[i].iId, team)) + if (i.price <= money) { + if (pBot->iPrimaryWeapon > -1) { + if (weapons_table[ListIdWeapon(pBot->iPrimaryWeapon)].priority >= i.priority) continue; + } - // can afford it - if (weapons_table[i].price <= money) { - - // owns a primary weapon - if (pBot->iPrimaryWeapon > -1) { - // and the primary weapon has a higher priority than the other primary weapon - if (weapons_table[ListIdWeapon(pBot->iPrimaryWeapon)].priority >= weapons_table[i].priority) - continue; - } - - // nothing to buy yet, so chose this one - if (buy_weapon == -1) { - buy_weapon = weapons_table[i].iId; - } else { - // randomly overrule it based on priority. The higher priority the more chance - // it will be bought. - if (RANDOM_LONG(0, 100) < weapons_table[i].priority) { - buy_weapon = weapons_table[i].iId; // randomly buy a different weapon - } - } + if (buy_weapon == -1) { + buy_weapon = i.iId; + } + else { + if (RANDOM_LONG(0, 100) < i.priority) { + buy_weapon = i.iId; } } } + } - pBot->buy_primary = false; - - // has decided which weapon to buy - if (buy_weapon != -1) { - pBot->rprint("BotDecideWhatToBuy()", "Found a primary weapon to buy"); + return buy_weapon; +} - // depending on amount of money we have left buy *also* secondary weapon - int iMoneyLeft = money - PriceWeapon(buy_weapon); +int BotBuySecondaryWeapon(cBot *pBot) { + const int money = pBot->bot_money; + const int team = pBot->iTeam; + int buy_weapon = -1; - // TODO: this should be dependant on something else... not only money - // 01.09.04 Frashman if buyed a Shield, try to buy a good Pistol - if (iMoneyLeft >= 600) - if ((RANDOM_LONG(0, 100) < 15) - || (pBot->iPrimaryWeapon == CS_WEAPON_SHIELD)) - pBot->buy_secondary = true; - } - } else if (pBot->buy_secondary) { - pBot->rprint("BotDecideWhatToBuy()", "buy_secondary"); - // Buy secondary - - // Personality related: - // Check if we can buy our favorite weapon - if (pBot->hasFavoriteSecondaryWeaponPreference()) { - pBot->rprint("BotDecideWhatToBuy()", "I have a secondary weapon preference"); - - if (!pBot->ownsFavoriteSecondaryWeapon()) { - pBot->rprint("BotDecideWhatToBuy()", "I do not own my secondary weapon preference"); - - if (GoodWeaponForTeam(pBot->ipFavoSecWeapon, pBot->iTeam)) { - if (pBot->canAfford(pBot->ipFavoSecWeapon)) { - pBot->rprint("BotDecideWhatToBuy()", "I can afford my favorite secondary weapon, buying it now"); - // Buy favorite weapon - buy_weapon = pBot->ipFavoPriWeapon; - } else { - pBot->rprint("BotDecideWhatToBuy()", "I cannot afford my favorite secondary weapon"); - - // do not buy a random secondary weapon - rather save money for it. - // We do here something to 'save' for our favorite weapon - if (RANDOM_LONG(0, 100) < pBot->ipSaveForWeapon) { // 31.08.04 Frashman forgotten brace - pBot->rprint("I have decided to save money for my favorite secondary weapon"); - pBot->buy_secondary = false; // don't buy a secondary - return; // get out of function, don't buy anything - } - } + if (pBot->hasFavoriteSecondaryWeaponPreference()) { + if (!pBot->ownsFavoriteSecondaryWeapon()) { + if (GoodWeaponForTeam(pBot->ipFavoSecWeapon, pBot->iTeam)) { + if (pBot->canAfford(pBot->ipFavoSecWeapon)) { + return pBot->ipFavoSecWeapon; + } + if (RANDOM_LONG(0, 100) < pBot->ipSaveForWeapon) { + return -2; // Stop buying } - } else { - pBot->rprint("BotDecideWhatToBuy()", "I already own my favorite secondary weapon"); - // we already own it, do nothing - return; } } + else { + return -2; // Stop buying + } + } - // no weapon choosen to buy yet - and no preference - if (buy_weapon < 0) { - pBot->rprint("BotDecideWhatToBuy()", "Deciding which secondary weapon to buy"); - // Buy secondary - // Find weapon we can buy in the list of weapons - for (int i = 0; i < MAX_WEAPONS; i++) { + for (const weapon_price_table& i : weapons_table) { + if (UTIL_GiveWeaponType(i.iId) != SECONDARY) + continue; - // When enough money and the priority is high enough.. - // Filter out Secondary and Grenades - if (UTIL_GiveWeaponType(weapons_table[i].iId) != SECONDARY) - continue; + if (!GoodWeaponForTeam(i.iId, team)) + continue; - if (GoodWeaponForTeam(weapons_table[i].iId, team) == false) + if (i.price <= money) { + if (pBot->iSecondaryWeapon > -1) { + const int index = weapons_table[pBot->iSecondaryWeapon].iIdIndex; + if (weapons_table[index].priority >= i.priority) continue; + } - if (weapons_table[i].price <= money) { - if (pBot->iSecondaryWeapon > -1) { - int index = - weapons_table[pBot->iSecondaryWeapon].iIdIndex; - // 31.08.04 Frashman > corrected to >= , - // else the bot will buy another weapon with the same priority - if (weapons_table[index].priority >= - weapons_table[i].priority) - continue; - } - - if (buy_weapon == -1) - buy_weapon = weapons_table[i].iId; - else { - if (RANDOM_LONG(0, 100) < weapons_table[i].priority) - buy_weapon = weapons_table[i].iId; - } - if (RANDOM_LONG(0, 100) < weapons_table[i].priority) - break; + if (buy_weapon == -1) { + buy_weapon = i.iId; + } + else { + if (RANDOM_LONG(0, 100) < i.priority) { + buy_weapon = i.iId; } } + if (RANDOM_LONG(0, 100) < i.priority) + break; } + } + return buy_weapon; +} - pBot->buy_secondary = false; - - // found a secondary weapon to buy - if (buy_weapon != -1) { - pBot->rprint("Found a secondary weapon to buy"); - } - } else if (pBot->buy_ammo_primary == true) { - pBot->rprint("BotDecideWhatToBuy()", "buy_ammo_primary"); - // Buy primary ammo - BotPrepareConsoleCommandsToBuyWeapon(pBot, "6", NULL); - pBot->buy_ammo_primary = false; - return; +int BotBuyEquipment(cBot *pBot) { + const int money = pBot->bot_money; - } else if (pBot->buy_ammo_secondary == true) { - pBot->rprint("BotDecideWhatToBuy()", "buy_ammo_secondary"); - // Buy secondary ammo - BotPrepareConsoleCommandsToBuyWeapon(pBot, "7", NULL); - pBot->buy_ammo_secondary = false; - return; - } else if (pBot->buy_defusekit) { - pBot->rprint("BotDecideWhatToBuy()", "buy_defusekit"); + if (pBot->buy_defusekit) { pBot->buy_defusekit = false; - if (money >= 200) { - buy_weapon = CS_DEFUSEKIT; - } - } else if (pBot->buy_armor) { - pBot->rprint("BotDecideWhatToBuy()", "buy_armor"); - if (money < 1000 && money >= 650) { - // Buy light armor - buy_weapon = CS_WEAPON_ARMOR_LIGHT; - } else if (money >= 1000) { - // Buy heavy armor - buy_weapon = CS_WEAPON_ARMOR_HEAVY; - } + if (money >= 200) return CS_DEFUSEKIT; + } + + if (pBot->buy_armor) { pBot->buy_armor = false; - } else if (pBot->buy_grenade) { - pBot->rprint("BotDecideWhatToBuy()", "buy_grenade"); - // Buy grenade - if (money >= weapons_table[ListIdWeapon(CS_WEAPON_HEGRENADE)].price) { - buy_weapon = CS_WEAPON_HEGRENADE; - } + if (money >= 1000) return CS_WEAPON_ARMOR_HEAVY; + if (money >= 650) return CS_WEAPON_ARMOR_LIGHT; + } + if (pBot->buy_grenade) { pBot->buy_grenade = false; - } else if (pBot->buy_flashbang > 0) { - pBot->rprint("BotDecideWhatToBuy()", "buy_flashbang"); - // Buy flashbang + if (money >= weapons_table[ListIdWeapon(CS_WEAPON_HEGRENADE)].price) return CS_WEAPON_HEGRENADE; + } + + if (pBot->buy_flashbang > 0) { if (money >= weapons_table[ListIdWeapon(CS_WEAPON_FLASHBANG)].price) { - buy_weapon = CS_WEAPON_FLASHBANG; pBot->buy_flashbang--; - } else { - pBot->buy_flashbang = 0; // do not buy - } - } else if (pBot->buy_smokegrenade) //31.08.04 Frashman added Smoke Grenade support - { - pBot->rprint("BotDecideWhatToBuy()", "buy_smokegrenade"); - // Buy SmokeGrenade - if (money >= weapons_table[ListIdWeapon(CS_WEAPON_SMOKEGRENADE)].price) { - buy_weapon = CS_WEAPON_SMOKEGRENADE; + return CS_WEAPON_FLASHBANG; } + pBot->buy_flashbang = 0; + } + if (pBot->buy_smokegrenade) { pBot->buy_smokegrenade = false; + if (money >= weapons_table[ListIdWeapon(CS_WEAPON_SMOKEGRENADE)].price) return CS_WEAPON_SMOKEGRENADE; + } + + return -1; +} + + +/* + BotDecideWhatToBuy() + + In this function the bot will choose what weapon to buy from the table. + */ +void BotDecideWhatToBuy(cBot *pBot) { + const int money = pBot->bot_money; + int buy_weapon; + + if (pBot->buy_primary) { + buy_weapon = BotBuyPrimaryWeapon(pBot); + pBot->buy_primary = false; + + if (buy_weapon == -2) return; // Stop buying + + if (buy_weapon != -1) { + const int iMoneyLeft = money - PriceWeapon(buy_weapon); + if (iMoneyLeft >= 600 && (RANDOM_LONG(0, 100) < 15 || pBot->iPrimaryWeapon == CS_WEAPON_SHIELD)) { + pBot->buy_secondary = true; + } + } + } + else if (pBot->buy_secondary) { + buy_weapon = BotBuySecondaryWeapon(pBot); + pBot->buy_secondary = false; + if (buy_weapon == -2) return; + } + else if (pBot->buy_ammo_primary) { + BotPrepareConsoleCommandsToBuyWeapon(pBot, "6", nullptr); + pBot->buy_ammo_primary = false; + return; + } + else if (pBot->buy_ammo_secondary) { + BotPrepareConsoleCommandsToBuyWeapon(pBot, "7", nullptr); + pBot->buy_ammo_secondary = false; + return; + } + else { + buy_weapon = BotBuyEquipment(pBot); } // Perform the actual buy commands to acquire weapon - pBot->performBuyActions(buy_weapon); + if (buy_weapon != -1) { + pBot->performBuyActions(buy_weapon); + } } /* @@ -442,7 +338,7 @@ void ConsoleThink(cBot *pBot) { // buy time is in minutes, we need // gpGlobals->time is in seconds, so we need to translate the minutes into seconds - float buyTime = CVAR_GET_FLOAT("mp_buytime") * 60; + const float buyTime = CVAR_GET_FLOAT("mp_buytime") * 60; if (Game.getRoundStartedTime() + buyTime > gpGlobals->time && pBot->wantsToBuyStuff()) { BotDecideWhatToBuy(pBot); @@ -467,33 +363,32 @@ void BotConsole(cBot *pBot) { // Here the bot will excecute the console commands if the console counter has been set/changed if (pBot->console_nr != 0 && pBot->f_console_timer < gpGlobals->time) { // safety net - if (pBot->console_nr < 0) - pBot->console_nr = 0; // Set it to 0 + pBot->console_nr = std::max(pBot->console_nr, 0); // Set it to 0 // issue command (buy/radio) if (pBot->console_nr == 1) - FakeClientCommand(pBot->pEdict, pBot->arg1, NULL, NULL); + FakeClientCommand(pBot->pEdict, pBot->arg1, nullptr, nullptr); // do menuselect if (pBot->console_nr == 2) - FakeClientCommand(pBot->pEdict, "menuselect", pBot->arg2, NULL); + FakeClientCommand(pBot->pEdict, "menuselect", pBot->arg2, nullptr); // do menuselect if (pBot->console_nr == 3) { // When the last parameter is not null, we will perform that action. if (pBot->arg3[0] != 0) FakeClientCommand(pBot->pEdict, "menuselect", pBot->arg3, - NULL); + nullptr); // reset pBot->console_nr = -1; - pBot->f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.2, 0.5); + pBot->f_console_timer = gpGlobals->time + RANDOM_FLOAT(0.2f, 0.5f); } if (pBot->console_nr > 0) pBot->console_nr++; // Increase command - return; + //return; } } // BotConsole() diff --git a/bot_client.cpp b/bot_client.cpp index 3bc1c71..3c91511 100644 --- a/bot_client.cpp +++ b/bot_client.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -27,8 +29,9 @@ * **/ - -#include +#include +#include +#include #include #include #include @@ -56,18 +59,18 @@ bot_weapon_t weapon_defs[MAX_WEAPONS]; // array of weapon definitions static FILE *fp; // This message is sent when the Counter-Strike VGUI menu is displayed. -void BotClient_CS_VGUI(void *p, int bot_index) { +void BotClient_CS_VGUI(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_VGUI()\n"); - if ((*(int *) p) == 2) // is it a team select menu? + if (*static_cast(p) == 2) // is it a team select menu? bots[bot_index].start_action = MSG_CS_TEAM_SELECT; - else if ((*(int *) p) == 26) // is is a terrorist model select menu? + else if (*static_cast(p) == 26) // is is a terrorist model select menu? bots[bot_index].start_action = MSG_CS_T_SELECT; - else if ((*(int *) p) == 27) // is is a counter-terrorist model select menu? + else if (*static_cast(p) == 27) // is is a counter-terrorist model select menu? bots[bot_index].start_action = MSG_CS_CT_SELECT; } // This message is sent when a menu is being displayed in Counter-Strike. -void BotClient_CS_ShowMenu(void *p, int bot_index) { +void BotClient_CS_ShowMenu(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_ShowMenu()\n"); static int state = 0; // current state machine state @@ -76,18 +79,18 @@ void BotClient_CS_ShowMenu(void *p, int bot_index) { return; } - if (strcmp((char *) p, "#Team_Select") == 0 || - strcmp((char *) p, "#Team_Select_Spect") == 0 || - strcmp((char *) p, "#IG_Team_Select_Spect") == 0 || - strcmp((char *) p, "#IG_Team_Select") == 0 || - strcmp((char *) p, "#IG_VIP_Team_Select") == 0 || - strcmp((char *) p, "#IG_VIP_Team_Select_Spect") == 0) { + if (std::strcmp(static_cast(p), "#Team_Select") == 0 || + std::strcmp(static_cast(p), "#Team_Select_Spect") == 0 || + std::strcmp(static_cast(p), "#IG_Team_Select_Spect") == 0 || + std::strcmp(static_cast(p), "#IG_Team_Select") == 0 || + std::strcmp(static_cast(p), "#IG_VIP_Team_Select") == 0 || + std::strcmp(static_cast(p), "#IG_VIP_Team_Select_Spect") == 0) { // team select menu? bots[bot_index].start_action = MSG_CS_TEAM_SELECT; - } else if (strcmp((char *) p, "#Terrorist_Select") == 0) { + } else if (std::strcmp(static_cast(p), "#Terrorist_Select") == 0) { // T model select? bots[bot_index].start_action = MSG_CS_T_SELECT; - } else if (strcmp((char *) p, "#CT_Select") == 0) { + } else if (std::strcmp(static_cast(p), "#CT_Select") == 0) { // CT model select menu? bots[bot_index].start_action = MSG_CS_CT_SELECT; } @@ -104,32 +107,32 @@ void BotClient_Valve_WeaponList(void *p, int bot_index) { if (state == 0) { state++; - strcpy(bot_weapon.szClassname, (char *) p); + std::strcpy(bot_weapon.szClassname, static_cast(p)); } else if (state == 1) { state++; - bot_weapon.iAmmo1 = *(int *) p; // ammo index 1 + bot_weapon.iAmmo1 = *static_cast(p); // ammo index 1 } else if (state == 2) { state++; - bot_weapon.iAmmo1Max = *(int *) p; // max ammo1 + bot_weapon.iAmmo1Max = *static_cast(p); // max ammo1 } else if (state == 3) { state++; - bot_weapon.iAmmo2 = *(int *) p; // ammo index 2 + bot_weapon.iAmmo2 = *static_cast(p); // ammo index 2 } else if (state == 4) { state++; - bot_weapon.iAmmo2Max = *(int *) p; // max ammo2 + bot_weapon.iAmmo2Max = *static_cast(p); // max ammo2 } else if (state == 5) { state++; - bot_weapon.iSlot = *(int *) p; // slot for this weapon + bot_weapon.iSlot = *static_cast(p); // slot for this weapon } else if (state == 6) { state++; - bot_weapon.iPosition = *(int *) p; // position in slot + bot_weapon.iPosition = *static_cast(p); // position in slot } else if (state == 7) { state++; - bot_weapon.iId = *(int *) p; // weapon ID + bot_weapon.iId = *static_cast(p); // weapon ID } else if (state == 8) { state = 0; - bot_weapon.iFlags = *(int *) p; // flags for weapon (WTF???) + bot_weapon.iFlags = *static_cast(p); // flags for weapon (WTF???) // store away this weapon with it's ammo information... weapon_defs[bot_weapon.iId] = bot_weapon; @@ -150,19 +153,19 @@ void BotClient_Valve_WeaponList(void *p, int bot_index) { } } -void BotClient_CS_WeaponList(void *p, int bot_index) { +void BotClient_CS_WeaponList(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_WeaponList()\n"); // this is just like the Valve Weapon List message BotClient_Valve_WeaponList(p, bot_index); } -void BotClient_Gearbox_WeaponList(void *p, int bot_index) { +void BotClient_Gearbox_WeaponList(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_WeaponList()\n"); // this is just like the Valve Weapon List message BotClient_Valve_WeaponList(p, bot_index); } -void BotClient_FLF_WeaponList(void *p, int bot_index) { +void BotClient_FLF_WeaponList(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_WeaponList()\n"); // this is just like the Valve Weapon List message BotClient_Valve_WeaponList(p, bot_index); @@ -170,25 +173,25 @@ void BotClient_FLF_WeaponList(void *p, int bot_index) { // 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). -void BotClient_Valve_CurrentWeapon(void *p, int bot_index) { +void BotClient_Valve_CurrentWeapon(void *p, const int bot_index) { static int state = 0; // current state machine state static int iState; static int iId; - static int iClip; if (state == 0) { state++; - iState = *(int *) p; // state of the current weapon + iState = *static_cast(p); // state of the current weapon } else if (state == 1) { state++; - iId = *(int *) p; // weapon ID of current weapon + iId = *static_cast(p); // weapon ID of current weapon } else if (state == 2) { - state = 0; + static int iClip; + state = 0; - iClip = *(int *) p; // ammo currently in the clip for this weapon + iClip = *static_cast(p); // ammo currently in the clip for this weapon - if (iId <= 31) { - bots[bot_index].bot_weapons |= (1 << iId); // set this weapon bit + if (iId < static_cast(std::size(weapon_defs))) { + bots[bot_index].bot_weapons |= 1 << iId; // set this weapon bit if (iState == 1) { bots[bot_index].current_weapon.iId = iId; // weapon id @@ -205,46 +208,44 @@ void BotClient_Valve_CurrentWeapon(void *p, int bot_index) { } } } - } -void BotClient_CS_CurrentWeapon(void *p, int bot_index) { +void BotClient_CS_CurrentWeapon(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_CurrentWeapon()\n"); // this is just like the Valve Current Weapon message BotClient_Valve_CurrentWeapon(p, bot_index); } -void BotClient_Gearbox_CurrentWeapon(void *p, int bot_index) { +void BotClient_Gearbox_CurrentWeapon(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_CurrentWeapon()\n"); // this is just like the Valve Current Weapon message BotClient_Valve_CurrentWeapon(p, bot_index); } -void BotClient_FLF_CurrentWeapon(void *p, int bot_index) { +void BotClient_FLF_CurrentWeapon(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_CurrentWeapon()\n"); // this is just like the Valve Current Weapon message BotClient_Valve_CurrentWeapon(p, bot_index); } // This message is sent whenever ammo ammounts are adjusted (up or down). -void BotClient_Valve_AmmoX(void *p, int bot_index) { +void BotClient_Valve_AmmoX(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Valve_AmmoX()\n"); static int state = 0; // current state machine state static int index; - static int ammount; - int ammo_index; if (state == 0) { state++; - index = *(int *) p; // ammo index (for type of ammo) + index = *static_cast(p); // ammo index (for type of ammo) } else if (state == 1) { - state = 0; + static int ammount; + state = 0; - ammount = *(int *) p; // the ammount of ammo currently available + ammount = *static_cast(p); // the ammount of ammo currently available bots[bot_index].m_rgAmmo[index] = ammount; // store it away - ammo_index = bots[bot_index].current_weapon.iId; + const int ammo_index = bots[bot_index].current_weapon.iId; // update the ammo counts for this weapon... bots[bot_index].current_weapon.iAmmo1 = @@ -256,19 +257,19 @@ void BotClient_Valve_AmmoX(void *p, int bot_index) { } } -void BotClient_CS_AmmoX(void *p, int bot_index) { +void BotClient_CS_AmmoX(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_AmmoX()\n"); // this is just like the Valve AmmoX message BotClient_Valve_AmmoX(p, bot_index); } -void BotClient_Gearbox_AmmoX(void *p, int bot_index) { +void BotClient_Gearbox_AmmoX(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_AmmoX()\n"); // this is just like the Valve AmmoX message BotClient_Valve_AmmoX(p, bot_index); } -void BotClient_FLF_AmmoX(void *p, int bot_index) { +void BotClient_FLF_AmmoX(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_AmmoX()\n"); // this is just like the Valve AmmoX message BotClient_Valve_AmmoX(p, bot_index); @@ -278,24 +279,23 @@ void BotClient_FLF_AmmoX(void *p, int bot_index) { // also sent so this message is probably not really necessary except it // allows the HUD to draw pictures of ammo that have been picked up. The // bots don't really need pictures since they don't have any eyes anyway. -void BotClient_Valve_AmmoPickup(void *p, int bot_index) { +void BotClient_Valve_AmmoPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Valve_AmmoPickup()\n"); static int state = 0; // current state machine state static int index; - static int ammount; - int ammo_index; if (state == 0) { state++; - index = *(int *) p; + index = *static_cast(p); } else if (state == 1) { - state = 0; + static int ammount; + state = 0; - ammount = *(int *) p; + ammount = *static_cast(p); bots[bot_index].m_rgAmmo[index] = ammount; - ammo_index = bots[bot_index].current_weapon.iId; + const int ammo_index = bots[bot_index].current_weapon.iId; // update the ammo counts for this weapon... bots[bot_index].current_weapon.iAmmo1 = @@ -305,48 +305,45 @@ void BotClient_Valve_AmmoPickup(void *p, int bot_index) { } } -void BotClient_CS_AmmoPickup(void *p, int bot_index) { +void BotClient_CS_AmmoPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_AmmoPickup()\n"); // this is just like the Valve Ammo Pickup message BotClient_Valve_AmmoPickup(p, bot_index); } -void BotClient_Gearbox_AmmoPickup(void *p, int bot_index) { +void BotClient_Gearbox_AmmoPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_AmmoPickup()\n"); // this is just like the Valve Ammo Pickup message BotClient_Valve_AmmoPickup(p, bot_index); } -void BotClient_FLF_AmmoPickup(void *p, int bot_index) { +void BotClient_FLF_AmmoPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_AmmoPickup()\n"); // this is just like the Valve Ammo Pickup message BotClient_Valve_AmmoPickup(p, bot_index); } // This message gets sent when the bot picks up a weapon. -void BotClient_Valve_WeaponPickup(void *p, int bot_index) { - //DebugOut("bot_client: BotClient_Valve_WeaponPickup()\n"); - int index; - - index = *(int *) p; +void BotClient_Valve_WeaponPickup(void *p, const int bot_index) { + const int index = *static_cast(p); // set this weapon bit to indicate that we are carrying this weapon bots[bot_index].bot_weapons |= (1 << index); } -void BotClient_CS_WeaponPickup(void *p, int bot_index) { +void BotClient_CS_WeaponPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_WeaponPickup()\n"); // this is just like the Valve Weapon Pickup message BotClient_Valve_WeaponPickup(p, bot_index); } -void BotClient_Gearbox_WeaponPickup(void *p, int bot_index) { +void BotClient_Gearbox_WeaponPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_WeaponPickup()\n"); // this is just like the Valve Weapon Pickup message BotClient_Valve_WeaponPickup(p, bot_index); } -void BotClient_FLF_WeaponPickup(void *p, int bot_index) { +void BotClient_FLF_WeaponPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_WeaponPickup()\n"); // this is just like the Valve Weapon Pickup message BotClient_Valve_WeaponPickup(p, bot_index); @@ -358,74 +355,74 @@ void BotClient_Valve_ItemPickup(void *p, int bot_index) { //DebugOut("bot_client: BotClient_Valve_ItemPickup()\n"); } -void BotClient_CS_ItemPickup(void *p, int bot_index) { +void BotClient_CS_ItemPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_ItemPickup()\n"); // this is just like the Valve Item Pickup message BotClient_Valve_ItemPickup(p, bot_index); } -void BotClient_Gearbox_ItemPickup(void *p, int bot_index) { +void BotClient_Gearbox_ItemPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_ItemPickup()\n"); // this is just like the Valve Item Pickup message BotClient_Valve_ItemPickup(p, bot_index); } -void BotClient_FLF_ItemPickup(void *p, int bot_index) { +void BotClient_FLF_ItemPickup(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_ItemPickup()\n"); // this is just like the Valve Item Pickup message BotClient_Valve_ItemPickup(p, bot_index); } // This message gets sent when the bots health changes. -void BotClient_Valve_Health(void *p, int bot_index) { +void BotClient_Valve_Health(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Valve_Health()\n"); - bots[bot_index].bot_health = *(int *) p; // health ammount + bots[bot_index].bot_health = *static_cast(p); // health ammount } -void BotClient_CS_Health(void *p, int bot_index) { +void BotClient_CS_Health(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_Health()\n"); // this is just like the Valve Health message BotClient_Valve_Health(p, bot_index); } -void BotClient_Gearbox_Health(void *p, int bot_index) { +void BotClient_Gearbox_Health(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_Health()\n"); // this is just like the Valve Health message BotClient_Valve_Health(p, bot_index); } -void BotClient_FLF_Health(void *p, int bot_index) { +void BotClient_FLF_Health(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_Health()\n"); // this is just like the Valve Health message BotClient_Valve_Health(p, bot_index); } // This message gets sent when the bots armor changes. -void BotClient_Valve_Battery(void *p, int bot_index) { +void BotClient_Valve_Battery(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Valve_Battery()\n"); - bots[bot_index].bot_armor = *(int *) p; // armor ammount + bots[bot_index].bot_armor = *static_cast(p); // armor ammount } -void BotClient_CS_Battery(void *p, int bot_index) { +void BotClient_CS_Battery(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_Battery()\n"); // this is just like the Valve Battery message BotClient_Valve_Battery(p, bot_index); } -void BotClient_Gearbox_Battery(void *p, int bot_index) { +void BotClient_Gearbox_Battery(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_Battery()\n"); // this is just like the Valve Battery message BotClient_Valve_Battery(p, bot_index); } -void BotClient_FLF_Battery(void *p, int bot_index) { +void BotClient_FLF_Battery(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_Battery()\n"); // this is just like the Valve Battery message BotClient_Valve_Battery(p, bot_index); } // This message gets sent when the bots are getting damaged. -void BotClient_Valve_Damage(void *p, int bot_index) { +void BotClient_Valve_Damage(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Valve_Damage()\n"); static int state = 0; // current state machine state static int damage_armor; @@ -435,24 +432,24 @@ void BotClient_Valve_Damage(void *p, int bot_index) { if (state == 0) { state++; - damage_armor = *(int *) p; + damage_armor = *static_cast(p); } else if (state == 1) { state++; - damage_taken = *(int *) p; + damage_taken = *static_cast(p); } else if (state == 2) { state++; - damage_bits = *(int *) p; + damage_bits = *static_cast(p); } else if (state == 3) { state++; - damage_origin.x = *(float *) p; + damage_origin.x = *static_cast(p); } else if (state == 4) { state++; - damage_origin.y = *(float *) p; + damage_origin.y = *static_cast(p); } else if (state == 5) { state = 0; - damage_origin.z = *(float *) p; - if ((damage_armor > 0) || (damage_taken > 0)) { + damage_origin.z = *static_cast(p); + if (damage_armor > 0 || damage_taken > 0) { // Damage recieved: // - when the prev node was higher (so we are sure we do FIX the correct nodes!) @@ -462,13 +459,13 @@ void BotClient_Valve_Damage(void *p, int bot_index) { if (damage_bits & (DMG_FALL | DMG_CRUSH)) { pBot->rprint_trace("BotClient_Valve_Damage", "Taken fall damage!"); if (pBot->getPathIndex() > 0) { // was one node further, so we can use previous node! - int iNode = NodeMachine.getNodeIndexFromBotForPath(pBot->iBotIndex, pBot->getPreviousPathIndex()); + const int iNode = NodeMachine.getNodeIndexFromBotForPath(pBot->iBotIndex, pBot->getPreviousPathIndex()); - float fDist = fabs(damage_origin.z - NodeMachine.node_vector(iNode).z); + const float fDist = std::fabs(damage_origin.z - NodeMachine.node_vector(iNode).z); if (fDist > 90) { // we know where we came from, and we know where we went to - int iNodeTo = NodeMachine.getNodeIndexFromBotForPath(pBot->iBotIndex, - pBot->getPathIndex()); + const int iNodeTo = NodeMachine.getNodeIndexFromBotForPath(pBot->iBotIndex, + pBot->getPathIndex()); // remove connection? @@ -484,7 +481,7 @@ void BotClient_Valve_Damage(void *p, int bot_index) { return; // depending on bot skill slow this bot down a bit -// pBot->f_move_speed *= (((10 - pBot->bot_skill) + 1) / 10); + // pBot->f_move_speed *= (((10 - pBot->bot_skill) + 1) / 10); // if the bot doesn't have an enemy and someone is shooting at it then // turn in the attacker's direction... @@ -492,7 +489,7 @@ void BotClient_Valve_Damage(void *p, int bot_index) { // face the attacker... edict_t *damageInflictor = pBot->pEdict->v.dmg_inflictor; if (damageInflictor) { - if (strcmp(STRING(damageInflictor->v.classname), "player") == 0) { + if (std::strcmp(STRING(damageInflictor->v.classname), "player") == 0) { // Face danger vector pBot->vHead = damage_origin; pBot->vBody = damage_origin; @@ -502,7 +499,7 @@ void BotClient_Valve_Damage(void *p, int bot_index) { pBot->rprint("BotClient_Valve_Damage", "Damage taken, by player, change goal to damage origin."); char msg[255]; - sprintf(msg, "damage_origin (x,y,z) => (%f,%f,%f) | damageInflictor->v.origin (x,y,z) => (%f,%f,%f)", + snprintf(msg, sizeof(msg), "damage_origin (x,y,z) => (%f,%f,%f) | damageInflictor->v.origin (x,y,z) => (%f,%f,%f)", damage_origin.x, damage_origin.y, damage_origin.z, @@ -518,7 +515,7 @@ void BotClient_Valve_Damage(void *p, int bot_index) { pBot->forgetPath(); } else { char msg[255]; - sprintf(msg, "I have a damage inflictor! -> %s", STRING(damageInflictor->v.classname)); + snprintf(msg, sizeof(msg), "I have a damage inflictor! -> %s", STRING(damageInflictor->v.classname)); pBot->rprint("BotClient_Valve_Damage", msg); } } else { @@ -531,13 +528,14 @@ void BotClient_Valve_Damage(void *p, int bot_index) { } } -void BotClient_CS_Damage(void *p, int bot_index) { +void BotClient_CS_Damage(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_Damage()\n"); // this is just like the Valve Battery message BotClient_Valve_Damage(p, bot_index); } -void BotClient_Gearbox_Damage(void *p, int bot_index) { +// Not required? [APG]RoboCop[CL] +/*void BotClient_Gearbox_Damage(void* p, int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_Damage()\n"); // this is just like the Valve Battery message BotClient_Valve_Damage(p, bot_index); @@ -547,10 +545,10 @@ void BotClient_FLF_Damage(void *p, int bot_index) { //DebugOut("bot_client: BotClient_FLF_Damage()\n"); // this is just like the Valve Battery message BotClient_Valve_Damage(p, bot_index); -} +}*/ -void BotClient_CS_SayText(void *p, int bot_index) { +void BotClient_CS_SayText(void *p, const int bot_index) { static unsigned char ucEntIndex; /** @@ -566,36 +564,37 @@ void BotClient_CS_SayText(void *p, int bot_index) { // handling of this "SayText" thingy. if (counterstrike == 0) { if (state == 0) { - ucEntIndex = *(unsigned char *) p; + ucEntIndex = *static_cast(p); } else if (state == 1) { - cBot *pBot = &bots[bot_index]; + const cBot *pBot = &bots[bot_index]; if (ENTINDEX(pBot->pEdict) != ucEntIndex) { char sentence[MAX_SENTENCE_LENGTH]; char chSentence[MAX_SENTENCE_LENGTH]; char netname[30]; - memset(sentence, 0, sizeof(sentence)); - memset(chSentence, 0, sizeof(chSentence)); - memset(netname, 0, sizeof(netname)); + std::memset(sentence, 0, sizeof(sentence)); + std::memset(chSentence, 0, sizeof(chSentence)); + std::memset(netname, 0, sizeof(netname)); - strcpy(sentence, (char *) p); // the actual sentence + std::strcpy(sentence, static_cast(p)); // the actual sentence - int length = 0; + unsigned int length = 0; // FIXED: In any case that this might return NULL, do not crash the server - if (strstr(sentence, " : ")) - length = strlen(sentence) - strlen(strstr(sentence, " : ")); + const char* found = std::strstr(sentence, " : "); + if (found != nullptr) + length = std::strlen(sentence) - std::strlen(found); - int tc = 0, c; + int tc = 0; - for (c = length; c < MAX_SENTENCE_LENGTH; c++) { + for (unsigned int c = length; c < MAX_SENTENCE_LENGTH; c++) { chSentence[tc] = sentence[c]; tc++; } - edict_t *pPlayer = INDEXENT(ucEntIndex); - strcpy(netname, STRING(pPlayer->v.netname)); + const edict_t *pPlayer = INDEXENT(ucEntIndex); + std::strcpy(netname, STRING(pPlayer->v.netname)); ChatEngine.set_sentence(netname, chSentence); state = -1; @@ -605,15 +604,22 @@ void BotClient_CS_SayText(void *p, int bot_index) { // CS 1.6 if (state == 0) { // who sent this message? - ucEntIndex = *(unsigned char *) p; + ucEntIndex = *static_cast(p); } // to who? else if (state == 1) { // here must be some team check so we do not let bots // of the other team react to this somehow.. - // - after playing with the dll i did not notice any weird stuff yet... - // - so only when people discover some bugs with this we are going to fix this - // - thing... ie "Only fix it when its broken". + edict_t* pPlayer = INDEXENT(ucEntIndex); + if (pPlayer) + { + const cBot* pBot = &bots[bot_index]; + if (UTIL_GetTeam(pPlayer) != pBot->iTeam - 1) + { + // message is from another team, don't process it + state = -1; // reset state machine + } + } } // don't know? else if (state == 2) { @@ -626,15 +632,15 @@ void BotClient_CS_SayText(void *p, int bot_index) { char netname[30]; // init - memset(sentence, 0, sizeof(sentence)); - memset(netname, 0, sizeof(netname)); + std::memset(sentence, 0, sizeof(sentence)); + std::memset(netname, 0, sizeof(netname)); // copy in memory - strcpy(sentence, (char *) p); + std::strcpy(sentence, static_cast(p)); // copy netname - edict_t *pPlayer = INDEXENT(ucEntIndex); - strcpy(netname, STRING(pPlayer->v.netname)); + const edict_t *pPlayer = INDEXENT(ucEntIndex); + std::strcpy(netname, STRING(pPlayer->v.netname)); // and give chatengine something to do ChatEngine.set_sentence(netname, sentence); @@ -652,7 +658,7 @@ void BotClient_CS_SayText(void *p, int bot_index) { // Converted to use switches. // This message gets sent when the bot enters a buyzone -void BotClient_CS_StatusIcon(void *p, int bot_index) { +void BotClient_CS_StatusIcon(void *p, const int bot_index) { /* FROM SDK 2.3 // Message handler for StatusIcon message @@ -664,20 +670,20 @@ void BotClient_CS_StatusIcon(void *p, int bot_index) { // byte : blue */ static int state = 0; // current state machine state - static int EnableIcon; - if (p == 0) // A bandaid. Make this whole thing more robust! + if (p == nullptr) // A bandaid. Make this whole thing more robust! { state = 0; return; } switch (state++) { - case 0: // Enable or not? - EnableIcon = *(int *) p; // check the byte + static int EnableIcon; + case 0: // Enable or not? + EnableIcon = *static_cast(p); // check the byte break; case 1: // Which icon - if (strcmp((char *) p, "buyzone") == 0) { + if (std::strcmp(static_cast(p), "buyzone") == 0) { switch (EnableIcon) { case 0: // Not in buy zone state = 0; @@ -687,7 +693,7 @@ void BotClient_CS_StatusIcon(void *p, int bot_index) { default: break; } - } else if (strcmp((char *) p, "c4") == 0) { + } else if (std::strcmp(static_cast(p), "c4") == 0) { switch (EnableIcon) { case 0: // No C4 bots[bot_index].bHUD_C4_plantable = false; @@ -702,7 +708,7 @@ void BotClient_CS_StatusIcon(void *p, int bot_index) { default: break; } - } else if (strcmp((char *) p, "defuser") == 0) { + } else if (std::strcmp(static_cast(p), "defuser") == 0) { switch (EnableIcon) { case 0: // No defuser state = 0; @@ -712,7 +718,7 @@ void BotClient_CS_StatusIcon(void *p, int bot_index) { default: break; } - } else if (strcmp((char *) p, "rescue") == 0) { + } else if (std::strcmp(static_cast(p), "rescue") == 0) { switch (EnableIcon) { case 0: // Not in rescue zone state = 0; @@ -742,14 +748,14 @@ void BotClient_CS_StatusIcon(void *p, int bot_index) { } // This message gets sent when the bots money ammount changes (for CS) -void BotClient_CS_Money(void *p, int bot_index) { +void BotClient_CS_Money(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_Money()\n"); static int state = 0; // current state machine state if (state == 0) { state++; - bots[bot_index].bot_money = *(int *) p; // amount of money + bots[bot_index].bot_money = *static_cast(p); // amount of money } else { state = 0; // ingore this field } @@ -761,17 +767,17 @@ void BotClient_Valve_DeathMsg(void *p, int bot_index) { static int state = 0; // current state machine state static int killer_index; static int victim_index; - static edict_t *victim_edict; - static int index; if (state == 0) { state++; - killer_index = *(int *) p; // ENTINDEX() of killer + killer_index = *static_cast(p); // ENTINDEX() of killer } else if (state == 1) { state++; - victim_index = *(int *) p; // ENTINDEX() of victim + victim_index = *static_cast(p); // ENTINDEX() of victim } else if (state == 2) { - state = 0; + static int index; + static edict_t *victim_edict; + state = 0; victim_edict = INDEXENT(victim_index); @@ -781,7 +787,7 @@ void BotClient_Valve_DeathMsg(void *p, int bot_index) { if (index != -1) { if ((killer_index == 0) || (killer_index == victim_index)) { // bot killed by world (worldspawn) or bot killed self... - bots[index].killer_edict = NULL; + bots[index].killer_edict = nullptr; } else { // store edict of player that killed this bot... bots[index].killer_edict = INDEXENT(killer_index); @@ -790,64 +796,60 @@ void BotClient_Valve_DeathMsg(void *p, int bot_index) { } } -void BotClient_CS_DeathMsg(void *p, int bot_index) { +void BotClient_CS_DeathMsg(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_DeathMsg()\n"); // this is just like the Valve DeathMsg message BotClient_Valve_DeathMsg(p, bot_index); } -void BotClient_Gearbox_DeathMsg(void *p, int bot_index) { +void BotClient_Gearbox_DeathMsg(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_DeathMsg()\n"); // this is just like the Valve DeathMsg message BotClient_Valve_DeathMsg(p, bot_index); } -void BotClient_FLF_DeathMsg(void *p, int bot_index) { +void BotClient_FLF_DeathMsg(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_FLF_DeathMsg()\n"); // this is just like the Valve DeathMsg message BotClient_Valve_DeathMsg(p, bot_index); } -void BotClient_Valve_ScreenFade(void *p, int bot_index) { +void BotClient_Valve_ScreenFade(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_Valve_ScreenFade()\n"); static int state = 0; // current state machine state static int duration; static int hold_time; - static int fade_flags; - float length; if (state == 0) { state++; - duration = *(int *) p; + duration = *static_cast(p); } else if (state == 1) { state++; - hold_time = *(int *) p; + hold_time = *static_cast(p); } else if (state == 2) { - state++; - fade_flags = *(int *) p; + static int fade_flags; + state++; + fade_flags = *static_cast(p); } else if (state == 6) { state = 0; - length = (duration + hold_time) / 4096.0; + float length = (static_cast(duration + hold_time)) / 4096.0f; int iDevide = bots[bot_index].bot_skill; - if (iDevide < 1) - iDevide = 1; + iDevide = std::max(iDevide, 1); - length -= ((10 / iDevide) * 0.5); + length -= (static_cast(10) / static_cast(iDevide) * 0.5f); bots[bot_index].fBlindedTime = gpGlobals->time + length; - // Get pointer and do some radio stuff here - Added by Stefan - cBot *pBot; - pBot = &bots[bot_index]; + cBot* pBot = &bots[bot_index]; - int iCurrentNode = NodeMachine.getClosestNode(pBot->pEdict->v.origin, NODE_ZONE, pBot->pEdict); - int iCoverNode = NodeMachine.node_cover(iCurrentNode, iCurrentNode, pBot->pEdict); + const int iCurrentNode = NodeMachine.getClosestNode(pBot->pEdict->v.origin, NODE_ZONE, pBot->pEdict); + const int iCoverNode = NodeMachine.node_cover(iCurrentNode, iCurrentNode, pBot->pEdict); if (iCoverNode > -1) { -// pBot->forgetPath(); -// pBot->rprint("Setting goal from screenfade"); -// pBot->setGoalNode(iCoverNode); + // pBot->forgetPath(); + // pBot->rprint("Setting goal from screenfade"); + // pBot->setGoalNode(iCoverNode); pBot->rprint("TODO: Make bot react upon screenfade/flashbang\n"); } @@ -856,13 +858,14 @@ void BotClient_Valve_ScreenFade(void *p, int bot_index) { } } -void BotClient_CS_ScreenFade(void *p, int bot_index) { +void BotClient_CS_ScreenFade(void *p, const int bot_index) { //DebugOut("bot_client: BotClient_CS_ScreenFade()\n"); // this is just like the Valve ScreenFade message BotClient_Valve_ScreenFade(p, bot_index); } -void BotClient_Gearbox_ScreenFade(void *p, int bot_index) { +// Not required? [APG]RoboCop[CL] +/*void BotClient_Gearbox_ScreenFade(void* p, int bot_index) { //DebugOut("bot_client: BotClient_Gearbox_ScreenFade()\n"); // this is just like the Valve ScreenFade message BotClient_Valve_ScreenFade(p, bot_index); @@ -872,7 +875,7 @@ void BotClient_FLF_ScreenFade(void *p, int bot_index) { //DebugOut("bot_client: BotClient_FLF_ScreenFade()\n"); // this is just like the Valve ScreenFade message BotClient_Valve_ScreenFade(p, bot_index); -} +}*/ // STEFAN // This message gets sent for HLTV, according to Alfred (Valve) also @@ -899,7 +902,7 @@ void BotClient_CS_HLTV(void *p, int bot_index) { */ if (state == 0) { - players = *(int *) p; // check the byte + players = *static_cast(p); // check the byte state++; } else if (state == 1) { // I check here on purpose. Multiple HLTV messages get sent within the game, diff --git a/bot_client.h b/bot_client.h index 437ca9d..9608fa2 100644 --- a/bot_client.h +++ b/bot_client.h @@ -28,6 +28,10 @@ **/ // COUNTER-STRIKE + +#ifndef BOT_CLIENT_H +#define BOT_CLIENT_H + void BotClient_CS_VGUI(void *p, int bot_index); void BotClient_CS_ShowMenu(void *p, int bot_index); void BotClient_CS_WeaponList(void *p, int bot_index); @@ -60,3 +64,5 @@ void BotClient_Valve_Battery(void *p, int bot_index); void BotClient_Valve_Damage(void *p, int bot_index); void BotClient_Valve_DeathMsg(void *p, int bot_index); void BotClient_Valve_ScreenFade(void *p, int bot_index); + +#endif // BOT_CLIENT_H \ No newline at end of file diff --git a/bot_func.cpp b/bot_func.cpp index 86de85e..518c191 100644 --- a/bot_func.cpp +++ b/bot_func.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +20,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -28,11 +30,12 @@ **/ -#include +#include #include #include #include #include +#include #include "bot.h" #include "game.h" @@ -46,85 +49,65 @@ extern int mod_id; extern int m_spriteTexture; // defined in dll.cpp -extern FILE *fpRblog; +extern FILE* fpRblog; // extern cNodeMachine NodeMachine; // For taking cover decision -#define TOTAL_SCORE 16300 // 16000 money + 100 health + 100 fear + 100 camp desire +constexpr int TOTAL_SCORE = 16300; // 16000 money + 100 health + 100 fear + 100 camp desire; -bool -VectorIsVisibleWithEdict(edict_t *pEdict, Vector dest, char *checkname) { +bool VectorIsVisibleWithEdict(edict_t* pEdict, const Vector& dest, const char* checkname) { TraceResult tr; - Vector start = pEdict->v.origin + pEdict->v.view_ofs; + const Vector start = pEdict->v.origin + pEdict->v.view_ofs; // trace a line from bot's eyes to destination... UTIL_TraceLine(start, dest, ignore_monsters, - pEdict->v.pContainingEntity, &tr); + pEdict->v.pContainingEntity, &tr); // When our check string is not "none" and the traceline has a hit... - if (strcmp("none", checkname) != 0 && tr.flFraction < 1.0) { + if (std::strcmp("none", checkname) != 0 && tr.flFraction < 1.0f) { // Check if the blocking entity is same as checkname.. - char entity_blocker[128]; - edict_t *pent = tr.pHit; // Ok now retrieve the entity - strcpy(entity_blocker, STRING(pent->v.classname)); // the classname + const edict_t* pent = tr.pHit; // Ok now retrieve the entity + const std::string entity_blocker = STRING(pent->v.classname); // the classname - if (strcmp(entity_blocker, checkname) == 0) + if (entity_blocker == checkname) return true; // We are blocked by our string, this means its ok. - else { - return false; // We are blocked, but by something differernt then 'checkname' its not ok - } - } else { - // check if line of sight to object is not blocked (i.e. visible) - if (tr.flFraction >= 1.0) - return true; - else - return false; + return false; // We are blocked, but by something differernt then 'checkname' its not ok } - return false; - + // check if line of sight to object is not blocked (i.e. visible) + return tr.flFraction >= 1.0f; } -bool VectorIsVisible(Vector start, Vector dest, char *checkname) { +bool VectorIsVisible(const Vector& start, const Vector& dest, const char* checkname) { TraceResult tr; // trace a line from bot's eyes to destination... - UTIL_TraceLine(start, dest, dont_ignore_monsters, NULL, &tr); + UTIL_TraceLine(start, dest, dont_ignore_monsters, nullptr, &tr); // Als we geblokt worden EN we checken voor een naam - if (strcmp("none", checkname) != 0 && tr.flFraction < 1.0) { + if (std::strcmp("none", checkname) != 0 && tr.flFraction < 1.0f) { // Check if the blocking entity is same as checkname.. - char entity_blocker[128]; - edict_t *pent = tr.pHit; // Ok now retrieve the entity - strcpy(entity_blocker, STRING(pent->v.classname)); // the classname + const edict_t* pent = tr.pHit; // Ok now retrieve the entity + const std::string entity_blocker = STRING(pent->v.classname); // the classname - if (strcmp(entity_blocker, checkname) == 0) + if (entity_blocker == checkname) return false; // We worden geblokt door die naam.. - else - return true; // We worden NIET geblokt door die naam (dus we worden niet geblokt). - - } else { - // check if line of sight to object is not blocked (i.e. visible) - // Als er NONE wordt opgegeven dan checken we gewoon of we worden geblokt - if (tr.flFraction >= 1.0) - return TRUE; - else - return FALSE; + return true; // We worden NIET geblokt door die naam (dus we worden niet geblokt). } + // check if line of sight to object is not blocked (i.e. visible) + // Als er NONE wordt opgegeven dan checken we gewoon of we worden geblokt + return tr.flFraction >= 1.0f; } -float func_distance(Vector v1, Vector v2) { +float func_distance(const Vector& v1, const Vector& v2) { // Returns distance between 2 vectors - if (v1 && v2) - return (v1 - v2).Length(); - else - return 0.0; + return (v1 - v2).Length(); } /** @@ -135,7 +118,7 @@ float func_distance(Vector v1, Vector v2) { * @param dest * @return */ -int FUNC_InFieldOfView(edict_t *pEntity, Vector dest) { +int FUNC_InFieldOfView(edict_t* pEntity, const Vector& dest) { // NOTE: Copy from Botman's BotInFieldOfView() routine. // find angles from source to destination... Vector entity_angles = UTIL_VecToAngles(dest); @@ -156,7 +139,7 @@ int FUNC_InFieldOfView(edict_t *pEntity, Vector dest) { // 45 degrees to the right is the limit of the normal view angle // rsm - START angle bug fix - int angle = abs((int) view_angle - (int) entity_angles.y); + int angle = std::abs(static_cast(view_angle) - static_cast(entity_angles.y)); if (angle > 180) angle = 360 - angle; @@ -171,7 +154,7 @@ int FUNC_InFieldOfView(edict_t *pEntity, Vector dest) { * @param start * @param end */ -void DrawBeam(edict_t *visibleForWho, Vector start, Vector end) { +void DrawBeam(edict_t* visibleForWho, const Vector& start, const Vector& end) { DrawBeam(visibleForWho, start, end, 25, 1, 255, 255, 255, 255, 1); } @@ -180,8 +163,11 @@ void DrawBeam(edict_t *visibleForWho, Vector start, Vector end) { * @param visibleForWho * @param start * @param end + * @param r + * @param g + * @param b */ -void DrawBeam(edict_t *visibleForWho, Vector start, Vector end, int r, int g, int b) { +void DrawBeam(edict_t* visibleForWho, const Vector& start, const Vector& end, const int r, const int g, const int b) { DrawBeam(visibleForWho, start, end, 25, 1, r, g, b, 255, 1); } @@ -198,10 +184,10 @@ void DrawBeam(edict_t *visibleForWho, Vector start, Vector end, int r, int g, in * @param brightness * @param speed */ -void DrawBeam(edict_t *visibleForWho, Vector start, Vector end, int width, - int noise, int red, int green, int blue, int brightness, - int speed) { - MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, visibleForWho); +void DrawBeam(edict_t* visibleForWho, const Vector& start, const Vector& end, const int width, const int noise, + const int red, const int green, const int blue, const int brightness, const int speed) +{ + MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, visibleForWho); WRITE_BYTE(TE_BEAMPOINTS); WRITE_COORD(start.x); WRITE_COORD(start.y); @@ -230,27 +216,30 @@ void DrawBeam(edict_t *visibleForWho, Vector start, Vector end, int width, * @param pBot * @return */ -cBot *getCloseFellowBot(cBot *pBot) { - edict_t *pEdict = pBot->pEdict; - cBot *closestBot = NULL; +cBot* getCloseFellowBot(cBot* pBot) { + const edict_t* pEdict = pBot->pEdict; + cBot* closestBot = nullptr; + float minDistance = NODE_ZONE; // Loop through all clients for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); + edict_t* pPlayer = INDEXENT(i); // skip invalid players - if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { + if (pPlayer && !pPlayer->free && pPlayer != pEdict) { // skip this player if not alive (i.e. dead or dying) if (!IsAlive(pPlayer)) continue; - cBot *pBotPointer = UTIL_GetBotPointer(pPlayer); + cBot* pBotPointer = UTIL_GetBotPointer(pPlayer); // skip anything that is not a RealBot - if (pBotPointer == NULL) // not using FL_FAKECLIENT here so it is multi-bot compatible + if (pBotPointer == nullptr) // not using FL_FAKECLIENT here so it is multi-bot compatible continue; - if (func_distance(pBot->pEdict->v.origin, pPlayer->v.origin) < NODE_ZONE) { + const float distance = func_distance(pBot->pEdict->v.origin, pPlayer->v.origin); + if (distance < minDistance) { closestBot = pBotPointer; // set pointer + minDistance = distance; } } } @@ -263,34 +252,33 @@ cBot *getCloseFellowBot(cBot *pBot) { * @param pBot * @return */ -edict_t * getPlayerNearbyBotInFOV(cBot *pBot) { - edict_t *pEdict = pBot->pEdict; - int FOV = 90; // TODO: use server var "default_fov" ? +edict_t* getPlayerNearbyBotInFOV(cBot* pBot) { + const edict_t* pEdict = pBot->pEdict; for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); + edict_t* pPlayer = INDEXENT(i); // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { + if (pPlayer && !pPlayer->free && pPlayer != pEdict) { + constexpr int fov = 90;// TODO: use server var "default_fov" ? // skip this player if not alive (i.e. dead or dying) if (!IsAlive(pPlayer)) continue; - if (!((pPlayer->v.flags & FL_THIRDPARTYBOT) - || (pPlayer->v.flags & FL_FAKECLIENT) - || (pPlayer->v.flags & FL_CLIENT))) + if (!(pPlayer->v.flags & FL_THIRDPARTYBOT + || pPlayer->v.flags & FL_FAKECLIENT + || pPlayer->v.flags & FL_CLIENT)) continue; - int angleToPlayer = FUNC_InFieldOfView(pBot->pEdict, (pPlayer->v.origin - pBot->pEdict->v.origin)); + const int angleToPlayer = FUNC_InFieldOfView(pBot->pEdict, pPlayer->v.origin - pBot->pEdict->v.origin); - int distance = NODE_ZONE; - if (func_distance(pBot->pEdict->v.origin, pPlayer->v.origin) < distance && angleToPlayer < FOV) { + constexpr int distance = NODE_ZONE; + if (func_distance(pBot->pEdict->v.origin, pPlayer->v.origin) < distance && angleToPlayer < fov) { return pPlayer; } - } } - return NULL; + return nullptr; } /** @@ -298,18 +286,18 @@ edict_t * getPlayerNearbyBotInFOV(cBot *pBot) { * @param pBot * @return */ -edict_t * getEntityNearbyBotInFOV(cBot *pBot) { - edict_t *pEdict = pBot->pEdict; +edict_t* getEntityNearbyBotInFOV(cBot* pBot) { + edict_t* pEdict = pBot->pEdict; - edict_t *pent = NULL; - while ((pent = UTIL_FindEntityInSphere(pent, pEdict->v.origin, 45)) != NULL) { + edict_t* pent = nullptr; + while ((pent = UTIL_FindEntityInSphere(pent, pEdict->v.origin, 45)) != nullptr) { if (pent == pEdict) continue; // skip self if (FInViewCone(&pent->v.origin, pEdict)) { return pent; // yes it is the case } } - return NULL; + return nullptr; } /** @@ -317,31 +305,27 @@ edict_t * getEntityNearbyBotInFOV(cBot *pBot) { * @param pBot * @return */ -bool isAnyPlayerNearbyBot(cBot *pBot) { - edict_t *pEdict = pBot->pEdict; - int FOV = 120; +bool isAnyPlayerNearbyBot(cBot* pBot) { + const edict_t* pEdict = pBot->pEdict; for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); + edict_t* pPlayer = INDEXENT(i); // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { + if (pPlayer && !pPlayer->free && pPlayer != pEdict) { // skip this player if not alive (i.e. dead or dying) if (!IsAlive(pPlayer)) continue; - if (!((pPlayer->v.flags & FL_THIRDPARTYBOT) - || (pPlayer->v.flags & FL_FAKECLIENT) - || (pPlayer->v.flags & FL_CLIENT))) + if (!(pPlayer->v.flags & FL_THIRDPARTYBOT + || pPlayer->v.flags & FL_FAKECLIENT + || pPlayer->v.flags & FL_CLIENT)) continue; - int angleToPlayer = FUNC_InFieldOfView(pBot->pEdict, (pPlayer->v.origin - pBot->pEdict->v.origin)); - - int distance = NODE_ZONE; + constexpr int distance = NODE_ZONE; if (func_distance(pBot->pEdict->v.origin, pPlayer->v.origin) < distance) { return true; } - } } return false; @@ -352,7 +336,7 @@ bool isAnyPlayerNearbyBot(cBot *pBot) { * @param pBot * @return */ -bool BotShouldJumpIfStuck(cBot *pBot) { +bool BotShouldJumpIfStuck(cBot* pBot) { if (pBot->isDefusing()) { pBot->rprint_trace("BotShouldJumpIfStuck", "Returning false because defusing."); return false; @@ -360,23 +344,23 @@ bool BotShouldJumpIfStuck(cBot *pBot) { if (pBot->isJumping()) return false; // already jumping - if (pBot->iJumpTries > 3) { + if (pBot->iJumpTries > 5) { char msg[255]; - sprintf(msg, "Returning false because jumped too many times (%d)", pBot->iJumpTries); + snprintf(msg, sizeof(msg), "Returning false because jumped too many times (%d)", pBot->iJumpTries); pBot->rprint_trace("BotShouldJumpIfStuck", msg); return false; } - bool result = BotShouldJump(pBot); + const bool result = BotShouldJump(pBot); if (result) { pBot->rprint_trace("BotShouldJumpIfStuck", "BotShouldJump returns true, so returning that"); return true; } // should not jump, perhaps its a func_illusionary causing that we're stuck? - edict_t *entityInFov = getEntityNearbyBotInFOV(pBot); + const edict_t* entityInFov = getEntityNearbyBotInFOV(pBot); - if (entityInFov && strcmp("func_illusionary", STRING(entityInFov->v.classname)) == 0) { + if (entityInFov && std::strcmp("func_illusionary", STRING(entityInFov->v.classname)) == 0) { return true; // yes it is the case } @@ -388,8 +372,8 @@ bool BotShouldJumpIfStuck(cBot *pBot) { * @param pBot * @return */ -bool BotShouldJump(cBot *pBot) { - // WHen a bot should jump, something is blocking his way. +bool BotShouldJump(cBot* pBot) { + // When a bot should jump, something is blocking his way. // Most of the time it is a fence, or a 'half wall' that reaches from body to feet // However, the body most of the time traces above this wall. // What i do: @@ -403,15 +387,15 @@ bool BotShouldJump(cBot *pBot) { return false; } - if (pBot->isJumping()) return false; // already jumping + if (pBot->isJumping()) + return false; // already jumping TraceResult tr; - Vector v_jump, v_source, v_dest; - edict_t *pEdict = pBot->pEdict; + const edict_t* pEdict = pBot->pEdict; // convert current view angle to vectors for TraceLine math... - v_jump = FUNC_CalculateAngles(pBot); + Vector v_jump = FUNC_CalculateAngles(pBot); v_jump.x = 0; // reset pitch to 0 (level horizontally) v_jump.z = 0; // reset roll to 0 (straight up and down) @@ -419,19 +403,18 @@ bool BotShouldJump(cBot *pBot) { // Check if we can jump onto something with maximum jump height: // maximum jump height, so check one unit above that (MAX_JUMPHEIGHT) - v_source = pEdict->v.origin + Vector(0, 0, -CROUCHED_HEIGHT + (MAX_JUMPHEIGHT + 1)); - v_dest = v_source + gpGlobals->v_forward * 90; + Vector v_source = pEdict->v.origin + Vector(0, 0, -CROUCHED_HEIGHT + (MAX_JUMPHEIGHT + 1)); + Vector v_dest = v_source + gpGlobals->v_forward * 90; // trace a line forward at maximum jump height... UTIL_TraceHull(v_source, v_dest, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); // if trace hit something, return FALSE - if (tr.flFraction < 1.0) { + if (tr.flFraction < 1.0f) { pBot->rprint_trace("BotShouldJump", "I cannot jump because something is blocking the max jump height"); return false; - } else { - pBot->rprint_trace("BotShouldJump", "I can make the jump, nothing blocking the jump height"); } + pBot->rprint_trace("BotShouldJump", "I can make the jump, nothing blocking the jump height"); // Ok the body is clear v_source = pEdict->v.origin + Vector(0, 0, ORIGIN_HEIGHT); @@ -440,39 +423,38 @@ bool BotShouldJump(cBot *pBot) { // trace a line forward at maximum jump height... UTIL_TraceHull(v_source, v_dest, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - if (tr.flFraction < 1.0) { - pBot->rprint_trace("BotShouldJump", "cannot jump because body is blocked"); + if (tr.flFraction < 1.0f) { + pBot->rprint_trace("BotShouldJump", "Cannot jump because body is blocked"); return false; - } else { - pBot->rprint_trace("BotShouldJump", "Jump body is not blocked"); } + pBot->rprint_trace("BotShouldJump", "Jump body is not blocked"); // Ok the body is clear v_source = pEdict->v.origin + Vector(0, 0, -14); // 14 downwards (from center) ~ roughly the kneecaps v_dest = v_source + gpGlobals->v_forward * 40; // -// int player_index = 0; -// for (player_index = 1; player_index <= gpGlobals->maxClients; -// player_index++) { -// edict_t *pPlayer = INDEXENT(player_index); -// -// if (pPlayer && !pPlayer->free) { -// if (FBitSet(pPlayer->v.flags, FL_CLIENT)) { // do not draw for now -// -// DrawBeam( -// pPlayer, // player sees beam -// v_source, // + Vector(0, 0, 32) (head?) -// v_dest, -// 255, 255, 255 -// ); -// } -// } -// } + // int player_index = 0; + // for (player_index = 1; player_index <= gpGlobals->maxClients; + // player_index++) { + // edict_t *pPlayer = INDEXENT(player_index); + // + // if (pPlayer && !pPlayer->free) { + // if (FBitSet(pPlayer->v.flags, FL_CLIENT)) { // do not draw for now + // + // DrawBeam( + // pPlayer, // player sees beam + // v_source, // + Vector(0, 0, 32) (head?) + // v_dest, + // 255, 255, 255 + // ); + // } + // } + // } UTIL_TraceHull(v_source, v_dest, dont_ignore_monsters, point_hull, pEdict->v.pContainingEntity, &tr); - if (tr.flFraction < 1.0) { + if (tr.flFraction < 1.0f) { pBot->rprint_trace("BotShouldJump", "Yes should jump, kneecaps hit something, so it is jumpable"); return true; } @@ -480,9 +462,9 @@ bool BotShouldJump(cBot *pBot) { // "func_illusionary" - although on cs_italy this is not detected, and probably in a lot of other cases as well if (tr.pHit) { pBot->rprint_trace("trace pHit", STRING(tr.pHit->v.classname)); - if (strcmp("func_illusionary", STRING(tr.pHit->v.classname)) == 0) { - pBot->rprint_trace("BotShouldJump", "#1 Hit a func_illusionary, its a hit as well! (even though trace hit results no)"); - return true; + if (std::strcmp("func_illusionary", STRING(tr.pHit->v.classname)) == 0) { + pBot->rprint_trace("BotShouldJump", "#1 Hit a func_illusionary, its a hit as well! (even though trace hit results no)"); + return true; } } @@ -491,9 +473,9 @@ bool BotShouldJump(cBot *pBot) { } // FUNCTION: Calculates angles as pEdict->v.v_angle should be when checking for body -Vector FUNC_CalculateAngles(cBot *pBot) { +Vector FUNC_CalculateAngles(const cBot* pBot) { // aim for the head and/or body - Vector v_target = pBot->vBody - pBot->pEdict->v.origin; + const Vector v_target = pBot->vBody - pBot->pEdict->v.origin; Vector v_body = UTIL_VecToAngles(v_target); if (v_body.y > 180) @@ -510,10 +492,7 @@ Vector FUNC_CalculateAngles(cBot *pBot) { return v_body; } -bool BotShouldDuck(cBot *pBot) { - // temp - // TODO: Deal with this, is it good code? remove the other stuff below the return statement? - +bool BotShouldDuck(cBot* pBot) { if (pBot->iDuckTries > 3) { // tried to duck 3 times, so no longer! pBot->rprint_trace("BotShouldDuck", "Returning false because ducked too many times."); @@ -521,62 +500,48 @@ bool BotShouldDuck(cBot *pBot) { } return BotCanDuckUnder(pBot); +} - // WHen a bot should jump, something is blocking his way. - // Most of the time it is a fence, or a 'half wall' that reaches from body to feet - // However, the body most of the time traces above this wall. - // What i do: - // Trace line from head - // When head blocked and waist is free, then we should duck... - - TraceResult tr; - Vector v_duck, v_source, v_dest; - edict_t *pEdict = pBot->pEdict; - - // convert current view angle to vectors for TraceLine math... - - v_duck = FUNC_CalculateAngles(pBot); - v_duck.x = 0; // reset pitch to 0 (level horizontally) - v_duck.z = 0; // reset roll to 0 (straight up and down) - - UTIL_MakeVectors(v_duck); - - // Check if head is blocked - v_source = pEdict->v.origin + Vector(0, 0, +37); - v_dest = v_source + gpGlobals->v_forward * 24; +bool BotShouldDuckJump(cBot* pBot) //Experimental DuckJump Incomplete [APG]RoboCop[CL] +{ + // This is crucial for bots to sneak inside vents and tight areas in order + // to inflitrate and prooceed on ahead. DuckJump is required for vaulting + // on top of crates, window ledges and edges as an important method. - // trace a line forward at maximum jump height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - if (tr.flFraction >= 1.0) + if (pBot->isDefusing()) { + pBot->rprint_trace("BotShouldDuckJump", "Returning false because defusing."); return false; + } - v_source = pEdict->v.origin; - v_dest = v_source + gpGlobals->v_forward * 24; - - // trace a line forward at maximum jump height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - if (tr.flFraction < 1.0) + if (pBot->iDuckJumpTries > 5) { + // tried to duck 5 times, so no longer! + pBot->rprint_trace("BotShouldDuck", "Returning false because ducked too many times."); return false; + } - return true; + if (pBot->isDuckJumping()) + return false; // already duckjumping + + return false; } -bool FUNC_DoRadio(cBot *pBot) { +/** + * Returns if a bot can and wants to do radio. Wanting is based on personality flag. + * @param pBot + * @return + */ +bool FUNC_DoRadio(const cBot* pBot) { - if (pBot->fDoRadio > gpGlobals->time) + if (pBot->fDoRadio > gpGlobals->time) // allowed? return false; - int iRadio = pBot->ipCreateRadio; + const int iRadio = pBot->ipCreateRadio; - return RANDOM_LONG(0, 100) < iRadio; + return RANDOM_LONG(0, 100) < iRadio; // want? } // DECIDE: Take cover or not -bool FUNC_ShouldTakeCover(cBot *pBot) { +bool FUNC_ShouldTakeCover(cBot* pBot) { // Do not allow taking cover within 3 seconds again. if (pBot->f_cover_time + 3 > gpGlobals->time) return false; @@ -588,18 +553,27 @@ bool FUNC_ShouldTakeCover(cBot *pBot) { pBot->f_cover_time = gpGlobals->time; // MONEY: The less we have, the more we want to take cover - int vMoney = 16000 - pBot->bot_money; + const int vMoney = 16000 - pBot->bot_money; // HEALTH: The less we have, the more we want to take cover - int vHealth = 100 - pBot->bot_health; + const int vHealth = 100 - pBot->bot_health; // CAMP: The more we want, the more we want to take cover - int vCamp = pBot->ipCampRate; + const int vCamp = pBot->ipCampRate; + + return RANDOM_LONG(0, TOTAL_SCORE) < vMoney + vHealth + vCamp; +} + +bool FUNC_TakeCover(cBot* pBot) //Experimental [APG]RoboCop[CL] +{ + // If we are not allowed to take cover, return false + if (!FUNC_ShouldTakeCover(pBot)) + return false; - return RANDOM_LONG(0, TOTAL_SCORE) < (vMoney + vHealth + vCamp); + return true; } -int FUNC_BotEstimateHearVector(cBot *pBot, Vector v_sound) { +int FUNC_BotEstimateHearVector(cBot* pBot, const Vector& v_sound) { // here we normally figure out where to look at when we hear an enemy, RealBot AI PR 2 lagged a lot on this so we need another approach return -1; @@ -607,56 +581,134 @@ int FUNC_BotEstimateHearVector(cBot *pBot, Vector v_sound) { // Added Stefan // 7 November 2001 -int FUNC_PlayerSpeed(edict_t *edict) { - if (edict != NULL) - return (int) edict->v.velocity.Length2D(); // Return speed of any edict given +int FUNC_PlayerSpeed(const edict_t* edict) { + if (edict != nullptr) + return static_cast(edict->v.velocity.Length2D()); // Return speed of any edict given return 0; } -bool FUNC_PlayerRuns(int speed) { - if (speed < 200) - return false; // We make no sound - else - return true; // We make sound +bool FUNC_PlayerRuns(const int speed) { + return speed >= 200; } // return weapon type of edict. // only when 'important enough'. -int FUNC_EdictHoldsWeapon(edict_t *pEdict) { +int FUNC_EdictHoldsWeapon(const edict_t* pEdict) { + const std::string weaponModel = STRING(pEdict->v.weaponmodel); // sniper guns - if (strcmp("models/p_awp.mdl", STRING(pEdict->v.weaponmodel)) == 0) - return CS_WEAPON_AWP; - if (strcmp("models/p_scout.mdl", STRING(pEdict->v.weaponmodel)) == 0) + //if (weaponModel == "models/p_awp.mdl") //Excluded for high prices and accuracy [APG]RoboCop[CL] + // return CS_WEAPON_AWP; + if (weaponModel == "models/p_scout.mdl") return CS_WEAPON_SCOUT; - // good weapons (ak, m4a1) - if (strcmp("models/p_ak47.mdl", STRING(pEdict->v.weaponmodel)) == 0) + // good weapons (ak, m4a1, mp5) + if (weaponModel == "models/p_ak47.mdl") return CS_WEAPON_AK47; - if (strcmp("models/p_m4a1.mdl", STRING(pEdict->v.weaponmodel)) == 0) + if (weaponModel == "models/p_m4a1.mdl") return CS_WEAPON_M4A1; + if (weaponModel == "models/p_mp5navy.mdl") + return CS_WEAPON_MP5NAVY; // grenade types - if (strcmp("models/p_smokegrenade.mdl", STRING(pEdict->v.weaponmodel)) - == 0) + if (weaponModel == "models/p_smokegrenade.mdl") return CS_WEAPON_SMOKEGRENADE; - if (strcmp("models/p_hegrenade.mdl", STRING(pEdict->v.weaponmodel)) == - 0) + if (weaponModel == "models/p_hegrenade.mdl") return CS_WEAPON_HEGRENADE; - if (strcmp("models/p_flashbang.mdl", STRING(pEdict->v.weaponmodel)) == - 0) + if (weaponModel == "models/p_flashbang.mdl") return CS_WEAPON_FLASHBANG; - // shield - if (strcmp("models/p_shield.mdl", STRING(pEdict->v.weaponmodel)) == 0) - return CS_WEAPON_SHIELD; + // shield types //Most CS Veterans dislikes the shield [APG]RoboCop[CL] + //if (weaponModel == "models/p_shield.mdl") + // return CS_WEAPON_SHIELD; // unknown return -1; } +int FUNC_FindFarWaypoint(cBot* pBot, const Vector& avoid, const bool safest) //Experimental [APG]RoboCop[CL] +{ + // Find a waypoint that is far away from the enemy. + // If safest is true, then we want the safest waypoint. + // If safest is false, then we want the farthest waypoint. + + // Find the farthest waypoint + int farthest = -1; + float farthest_distance = 0.0f; + + for (int i = 0; i < gpGlobals->maxEntities; i++) { + const edict_t* pEdict = INDEXENT(i); + + if (pEdict == nullptr) + continue; + + if (pEdict->v.flags & FL_DORMANT) + continue; + + if (pEdict->v.classname != 0 && std::strcmp(STRING(pEdict->v.classname), "info_waypoint") == 0) { + if (farthest == -1) { + farthest = i; + farthest_distance = (pEdict->v.origin - pBot->pEdict->v.origin).Length(); + } else { + const float distance = (pEdict->v.origin - pBot->pEdict->v.origin).Length(); + + if (safest) { + if (distance < farthest_distance) { + farthest = i; + farthest_distance = distance; + } + } else { + if (distance > farthest_distance) { + farthest = i; + farthest_distance = distance; + } + } + } + } + } + + return farthest; +} + +int FUNC_FindCover(const cBot* pBot) //Experimental [APG]RoboCop[CL] +{ + // Find a waypoint that is far away from the enemy. + // If safest is true, then we want the safest waypoint. + // If safest is false, then we want the farthest waypoint. + + // Find the farthest waypoint + int farthest = -1; + float farthest_distance = 0.0f; + + for (int i = 0; i < gpGlobals->maxEntities; i++) { + const edict_t* pEdict = INDEXENT(i); + + if (pEdict == nullptr) + continue; + + if (pEdict->v.flags & FL_DORMANT) + continue; + + if (pEdict->v.classname != 0 && std::strcmp(STRING(pEdict->v.classname), "info_waypoint") == 0) { + if (farthest == -1) { + farthest = i; + farthest_distance = (pEdict->v.origin - pBot->pEdict->v.origin).Length(); + } else { + const float distance = (pEdict->v.origin - pBot->pEdict->v.origin).Length(); + + if (distance > farthest_distance) { + farthest = i; + farthest_distance = distance; + } + } + } + } + + return farthest; +} + // Function to let a bot react on some sound which he cannot see -void FUNC_HearingTodo(cBot *pBot) { +void FUNC_HearingTodo(cBot* pBot) { // This is called every frame. if (pBot->f_hear_time > gpGlobals->time) return; // Do nothing, we need more time to think @@ -669,31 +721,33 @@ void FUNC_HearingTodo(cBot *pBot) { // I HEAR SOMETHING // More chance on getting to true - int health = pBot->bot_health; + const int health = pBot->bot_health; - int action = 0; - int etime = 0; + int action; + float etime; if (health < 25) action = 2; - else if (health >= 25 && health < 75) + else if (/*health >= 25 &&*/ health < 75) action = 1; else - action = -1; + action = 0; if (action == 0) { etime = RANDOM_LONG(2, 6); pBot->f_camp_time = gpGlobals->time + etime; pBot->forgetGoal(); - } else if (action == 1) { + } + else if (action == 1) { etime = RANDOM_LONG(1, 7); pBot->f_walk_time = gpGlobals->time + etime; - } else if (action == 2) { + } + else { etime = RANDOM_LONG(1, 5); pBot->f_hold_duck = gpGlobals->time + etime; } - pBot->f_hear_time = gpGlobals->time + 6; // Always keep a 6 seconds + pBot->f_hear_time = gpGlobals->time + 6.0f; // Always keep a 6 seconds // think time. } @@ -704,14 +758,14 @@ void FUNC_HearingTodo(cBot *pBot) { * Created : 16/11/2001 * Changed : 16/11/2001 */ -void FUNC_ClearEnemyPointer(edict_t *pPtr) { +void FUNC_ClearEnemyPointer(edict_t* pPtr) { //pPtr muddled with c_pointer? [APG]RoboCop[CL] // Go through all bots and remove their enemy pointer that matches the given // pointer pPtr for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); + edict_t* pPlayer = INDEXENT(i); // Skip invalid players. - if ((pPlayer) && (!pPlayer->free)) { + if (pPlayer && !pPlayer->free) { // skip this player if not alive (i.e. dead or dying) if (!IsAlive(pPlayer)) @@ -722,29 +776,133 @@ void FUNC_ClearEnemyPointer(edict_t *pPtr) { continue; // check if it is a bot known to us (ie, not another metamod supported bot) - cBot *botpointer = UTIL_GetBotPointer(pPlayer); + cBot* botpointer = UTIL_GetBotPointer(pPlayer); if (botpointer && // Is a bot managed by us botpointer->hasEnemy(pPtr) // and has the pointer we want to get rid of - ) { + ) { botpointer->forgetEnemy(); // Clear its pointer } } - } } // Returns true/false if an entity is on a ladder -bool FUNC_IsOnLadder(edict_t *pEntity) { - if (pEntity == NULL) +bool FUNC_IsOnLadder(const edict_t* pEntity) { + if (pEntity == nullptr) return false; - if (pEntity->v.movetype == MOVETYPE_FLY) + return pEntity->v.movetype == MOVETYPE_FLY; +} + +bool IsShootableBreakable(edict_t* pent) +{ + if (!pent) { + return false; + } + + const char* classname = STRING(pent->v.classname); + + if (FStrEq("func_breakable", classname)) + { + // Not shootable if it's trigger-only or already broken (health <= 0) + if ((pent->v.spawnflags & 1) || pent->v.health <= 0) // SF_BREAK_TRIGGER_ONLY + return false; + return true; + } + + if (FStrEq("func_pushable", classname)) + { + // Shootable if it's a breakable pushable + return (pent->v.spawnflags & 2) != 0; // SF_PUSH_BREAKABLE + } return false; } +void FUNC_FindBreakable(cBot* pBot) +{ + // The "func_breakable" entity is required for bots to recognize and attack + // breakable objects like glass or weak doors that block their path. + if (pBot == nullptr || pBot->pEdict == nullptr) { + return; // Ensure pBot and its edict are not null + } + + edict_t* pEntity = pBot->pEdict; + edict_t* pent = nullptr; + + // Search for entities within a 256-unit radius around the bot + while ((pent = UTIL_FindEntityInSphere(pent, pEntity->v.origin, 256.0f)) != nullptr) + { + if (pent == pEntity || (pent->v.flags & FL_DORMANT) || pent->v.health <= 0) { + continue; // Skip self, dormant, and already broken entities + } + + if (IsShootableBreakable(pent)) { + if (pBot->canSeeEntity(pent)) { + pBot->pBreakableEdict = pent; + return; + } + } + } +} + +void FUNC_AttackBreakable(cBot* pBot) +{ + if (pBot == nullptr || pBot->pBreakableEdict == nullptr) { + return; + } + + edict_t* pBreakable = pBot->pBreakableEdict; + + // If the breakable is no longer valid, forget it + if (pBreakable->v.health <= 0 || (pBreakable->v.flags & FL_DORMANT)) { + pBot->pBreakableEdict = nullptr; + return; + } + + const Vector vBreakableOrigin = VecBModelOrigin(pBreakable); + pBot->setHeadAiming(vBreakableOrigin); + + const float distance = (pBot->pEdict->v.origin - vBreakableOrigin).Length(); + + // Use knife if close enough, otherwise use the current weapon + if (distance < 64.0f) { + if (!pBot->isHoldingWeapon(CS_WEAPON_KNIFE)) { + pBot->pickWeapon(CS_WEAPON_KNIFE); + } + pBot->FireWeapon(); + } + else { + // If holding a knife but the breakable is not close, switch to a better weapon + if (pBot->isHoldingWeapon(CS_WEAPON_KNIFE)) { + pBot->PickBestWeapon(); + } + pBot->FireWeapon(); + } +} + +void FUNC_CheckForBombPlanted(edict_t* pEntity) //Experimental [APG]RoboCop[CL] +{ + // Check if the bot has a bomb planted. + // If so, then we need to go to the bomb site. + // If not, then we need to go to the waypoint. + + // "models/w_c4.mdl" needed for CTs to see the bomb? [APG]RoboCop[CL] + if (pEntity->v.model != 0 && std::strcmp(STRING(pEntity->v.model), "models/w_c4.mdl") == 0) { + // Bot has a bomb planted. + // Go to the bomb site. + pEntity->v.button |= IN_USE; + pEntity->v.button |= IN_ATTACK; + } + else { + // Bot does not have a bomb planted. + // Go to the waypoint. + pEntity->v.button |= IN_USE; + } +} + /** * Returns true when hostage is not marked as being rescued by any other alive bot. * @@ -752,15 +910,13 @@ bool FUNC_IsOnLadder(edict_t *pEntity) { * @param pHostage * @return */ -bool isHostageFree(cBot *pBotWhoIsAsking, edict_t *pHostage) { - if (pHostage == NULL) return false; - if (pBotWhoIsAsking == NULL) return false; +bool isHostageFree(cBot* pBotWhoIsAsking, edict_t* pHostage) { + if (pHostage == nullptr) return false; + if (pBotWhoIsAsking == nullptr) return false; for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - - if ((!pPlayer) || // NULL - (pPlayer && pPlayer->free)) // free - ie no client + edict_t* pPlayer = INDEXENT(i); + if (!pPlayer || pPlayer->free) // free - ie no client continue; // next // skip this player if not alive (i.e. dead or dying) @@ -772,9 +928,9 @@ bool isHostageFree(cBot *pBotWhoIsAsking, edict_t *pHostage) { continue; // Only check other bots (do not check self) - cBot *botpointer = UTIL_GetBotPointer(pPlayer); + cBot* botpointer = UTIL_GetBotPointer(pPlayer); if (botpointer && // a bot - (botpointer != pBotWhoIsAsking) && // not self + botpointer != pBotWhoIsAsking && // not self !botpointer->isDead()) { // not dead // other bot uses hostage, so hostage is not 'free' @@ -789,7 +945,7 @@ bool isHostageFree(cBot *pBotWhoIsAsking, edict_t *pHostage) { return true; } -void TryToGetHostageTargetToFollowMe(cBot *pBot) { +void TryToGetHostageTargetToFollowMe(cBot* pBot) { if (pBot->hasEnemy()) { return; // enemy, do not check } @@ -799,32 +955,31 @@ void TryToGetHostageTargetToFollowMe(cBot *pBot) { return; } - edict_t *pHostage = pBot->getHostageToRescue(); + edict_t* pHostage = pBot->getHostageToRescue(); - if (pHostage == NULL) { + if (pHostage == nullptr) { pHostage = pBot->findHostageToRescue(); } // still NULL - if (pHostage == NULL) { + if (pHostage == nullptr) { // Note: this means a hostage that is near and visible and rescueable etc. return; // nothing to do yet } // Whenever we have a hostage to go after, verify it is still rescueable - bool isRescueable = isHostageRescueable(pBot, pHostage); + const bool isRescueable = isHostageRescueable(pBot, pHostage); if (!isRescueable) { pBot->rprint_trace("TryToGetHostageTargetToFollowMe", "Hostage found, but not rescueable, forgetting..."); pBot->forgetHostage(pHostage); return; - } else { - pBot->rprint_trace("TryToGetHostageTargetToFollowMe", "Remembering hostage (target) to rescue"); - pBot->rememberWhichHostageToRescue(pHostage); } + pBot->rprint_trace("TryToGetHostageTargetToFollowMe", "Remembering hostage (target) to rescue"); + pBot->rememberWhichHostageToRescue(pHostage); // Prevent bots getting to close here - float distanceToHostage = func_distance(pBot->pEdict->v.origin, pHostage->v.origin); + const float distanceToHostage = func_distance(pBot->pEdict->v.origin, pHostage->v.origin); // From here, we should get the hostage when still visible if (pBot->canSeeEntity(pHostage)) { @@ -839,7 +994,7 @@ void TryToGetHostageTargetToFollowMe(cBot *pBot) { pBot->setMoveSpeed(0.0f); // too close, do not move // only use hostage when facing - int angle_to_hostage = FUNC_InFieldOfView(pBot->pEdict, (pBot->vBody - pBot->pEdict->v.origin)); + const int angle_to_hostage = FUNC_InFieldOfView(pBot->pEdict, (pBot->vBody - pBot->pEdict->v.origin)); if (angle_to_hostage <= 30 && (pBot->f_use_timer < gpGlobals->time)) { @@ -862,71 +1017,108 @@ void TryToGetHostageTargetToFollowMe(cBot *pBot) { } } -bool isHostageRescued(cBot *pBot, edict_t *pHostage) { - if (pHostage == NULL) return false; +bool isHostageRescued(cBot* pBot, const edict_t* pHostage) //pBot not used [APG]RoboCop[CL] +{ + if (pHostage == nullptr) return false; if (FBitSet(pHostage->v.effects, EF_NODRAW)) { -// pBot->rprint("isHostageRescued()", "Hostage is rescued"); + // pBot->rprint("isHostageRescued()", "Hostage is rescued"); return true; } -// pBot->rprint("isHostageRescued()", "Hostage is NOT rescued"); + // pBot->rprint("isHostageRescued()", "Hostage is NOT rescued"); return false; } -bool isHostageRescueable(cBot *pBot, edict_t *pHostage) { - if (pHostage == NULL) return false; -// pBot->rprint("isHostageRescueable"); +int FUNC_GiveHostage(cBot* pBot) //Experimental [APG]RoboCop[CL] +{ + if (pBot->isTerrorist()) { + return 0; + } - // Already rescued? - if (isHostageRescued(pBot, pHostage)) { - return false; + // find a hostage to rescue + edict_t* pHostage = pBot->getHostageToRescue(); + + if (pHostage == nullptr) { + pHostage = pBot->findHostageToRescue(); } - // dead - if (!FUNC_EdictIsAlive(pHostage)) { - return false; + // still NULL + if (pHostage == nullptr) { + // Note: this means a hostage that is near and visible and rescueable etc. + return 0; // nothing to do yet } - // Already moving? (used by human player?) - if (FUNC_PlayerSpeed(pHostage) > 2) { - return false; + // Whenever we have a hostage to go after, verify it is still rescueable + const bool isRescueable = isHostageRescueable(pBot, pHostage); + + if (!isRescueable) { + pBot->rprint_trace("GiveHostage", "Hostage found, but not rescueable, forgetting..."); + pBot->forgetHostage(pHostage); + return 0; } - // Already used by bot? + pBot->rprint_trace("GiveHostage", "Remembering hostage (target) to rescue"); + pBot->rememberWhichHostageToRescue(pHostage); - if (pBot != NULL) { -// rblog("isHostageRescueable - pBot is != NULL\n"); - if (pBot->isUsingHostage(pHostage)) return false; - // Is the hostage not used by *any other* bot? - if (!isHostageFree(pBot, pHostage)) { - rblog("isHostageRescueable - Hostage is not free"); - return false; + // Prevent bots getting to close here + const float distanceToHostage = func_distance(pBot->pEdict->v.origin, pHostage->v.origin); + + // From here, we should get the hostage when still visible + if (pBot->canSeeEntity(pHostage)) + { + pBot->rprint_trace("GiveHostage", "I can see the hostage to rescue!"); + // set body to hostage! + pBot->vBody = pBot->vHead = pHostage->v.origin + Vector(0, 0, 36); + // by default run + pBot->setMoveSpeed(pBot->f_max_speed); + + if (distanceToHostage <= 80.0f) + { + pBot->rprint_trace("GiveHostage", "I can see hostage AND really close!"); + pBot->setMoveSpeed(0.0f); // too close, do not move } } + return 1; //gives any hostage we still have to go for +} + +bool isHostageRescueable(cBot* pBot, edict_t* pHostage) { + if (pHostage == nullptr || pBot == nullptr) { + return false; + } + + // A hostage is not rescueable if it has already been rescued, is dead, + // is already being moved by a human player, or is being rescued by another bot. + if (isHostageRescued(pBot, pHostage) || + !FUNC_EdictIsAlive(pHostage) || + FUNC_PlayerSpeed(pHostage) > 2 || + pBot->isUsingHostage(pHostage) || + !isHostageFree(pBot, pHostage)) { + return false; + } - // yes we can rescue this hostage + // If all checks pass, the hostage is rescueable return true; } -bool FUNC_EdictIsAlive(edict_t *pEdict) { - if (pEdict == NULL) return false; +bool FUNC_EdictIsAlive(edict_t* pEdict) { + if (pEdict == nullptr) return false; return pEdict->v.health > 0; } // HostageNear() -bool FUNC_BotHoldsZoomWeapon(cBot *pBot) { +bool FUNC_BotHoldsZoomWeapon(cBot* pBot) { // Check if the bot holds a weapon that can zoom, but is not a sniper gun. return pBot->isHoldingWeapon(CS_WEAPON_AUG) || pBot->isHoldingWeapon(CS_WEAPON_SG552); } -void FUNC_BotChecksFalling(cBot *pBot) { +void FUNC_BotChecksFalling(cBot* pBot) { // This routine should never be filled with code. // - Bots should simply never fall // - If bots do fall, check precalculation routine. } // New function to display a message on the center of the screen -void CenterMessage(char *buffer) { +void CenterMessage(const char* buffer) { //DebugOut("waypoint: CenterMessage():\n"); //DebugOut(buffer); //DebugOut("\n"); @@ -934,7 +1126,7 @@ void CenterMessage(char *buffer) { } // Bot Takes Cover -bool BOT_DecideTakeCover(cBot *pBot) { +bool BOT_DecideTakeCover(cBot* pBot) { /* UTIL_ClientPrintAll( HUD_PRINTCENTER, "DECISION TO TAKE COVER\n" ); @@ -956,13 +1148,13 @@ bool BOT_DecideTakeCover(cBot *pBot) { } // logs into a file -void rblog(char *txt) { +void rblog(const char* txt) { // output to stdout - printf(txt); + //printf("%s", txt); // Excessive log spewing [APG]RoboCop[CL] // and to reallog file if (fpRblog) { - fprintf(fpRblog, "%s", txt); // print the text into the file + std::fprintf(fpRblog, "%s", txt); // print the text into the file // this way we make sure we have all latest info - even with crashes fflush(fpRblog); diff --git a/bot_func.h b/bot_func.h index e3bdc13..ecb399e 100644 --- a/bot_func.h +++ b/bot_func.h @@ -6,7 +6,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +18,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -30,97 +30,122 @@ #ifndef BOT_FUNC_H #define BOT_FUNC_H -//prototypes of bot functions... -void BotThink(cBot * pBot); + //prototypes of bot functions... +void BotThink(cBot* pBot); -void botFixIdealPitch(edict_t * pEdict); -void botFixIdealYaw(edict_t * pEdict); -bool BotCanJumpUp(cBot * pBot); -bool BotCanDuckUnder(cBot * pBot); +float fixAngle(float angle); -bool EntityIsVisible(edict_t * pEntity, Vector dest); +void botFixIdealPitch(edict_t* pEdict); +void botFixIdealYaw(edict_t* pEdict); +bool traceLine(const Vector& v_source, const Vector& v_dest, const edict_t* pEdict, TraceResult& tr); +bool BotCanJumpUp(cBot* pBot); +bool BotCanDuckUnder(cBot* pBot); -// bot_func.cpp -bool VectorIsVisible(Vector start, Vector dest, char *checkname); -float func_distance(Vector v1, Vector v2); +bool isBotNearby(const cBot* pBot, float radius); +void adjustBotAngle(cBot* pBot, float angle); +bool isPathBlocked(const cBot* pBot, const Vector& v_dest); +void BotNavigate(cBot* pBot); -void DrawBeam(edict_t * visibleForWho, Vector start, Vector end); -void DrawBeam(edict_t * visibleForWho, Vector start, Vector end, int red, int green, int blue); -void DrawBeam(edict_t * visibleForWho, Vector start, Vector end, - int width, int noise, int red, int green, int blue, - int brightness, int speed); +bool EntityIsVisible(edict_t* pEntity, const Vector& dest); -cBot *getCloseFellowBot(cBot * pBot); -edict_t * getPlayerNearbyBotInFOV(cBot * pBot); -edict_t * getEntityNearbyBotInFOV(cBot * pBot); - -bool BotShouldJump(cBot * pBot); -bool BotShouldJumpIfStuck(cBot * pBot); -bool BotShouldDuck(cBot * pBot); -void TryToGetHostageTargetToFollowMe(cBot * pBot); -Vector FUNC_CalculateAngles(cBot * pBot); +// bot_func.cpp +bool VectorIsVisible(const Vector& start, const Vector& dest, const char* checkname); +float func_distance(const Vector& v1, const Vector& v2); + +void DrawBeam(edict_t* visibleForWho, const Vector& start, const Vector& end); +void DrawBeam(edict_t* visibleForWho, const Vector& start, const Vector& end, int red, int green, int blue); +void DrawBeam(edict_t* visibleForWho, const Vector& start, const Vector& end, + int width, int noise, int red, int green, int blue, + int brightness, int speed); + +cBot* getCloseFellowBot(cBot* pBot); +edict_t* getPlayerNearbyBotInFOV(cBot* pBot); +edict_t* getEntityNearbyBotInFOV(cBot* pBot); + +bool isAnyPlayerNearbyBot(cBot* pBot); +bool BotShouldJump(cBot* pBot); +bool BotShouldJumpIfStuck(cBot* pBot); +bool BotShouldDuck(cBot* pBot); +bool BotShouldDuckJump(cBot* pBot); +void TryToGetHostageTargetToFollowMe(cBot* pBot); +Vector FUNC_CalculateAngles(const cBot* pBot); // New funcs -bool FUNC_DoRadio(cBot * pBot); +bool FUNC_DoRadio(const cBot* pBot); -bool FUNC_ShouldTakeCover(cBot * pBot); -bool FUNC_TakeCover(cBot * pBot); +bool FUNC_ShouldTakeCover(cBot* pBot); +bool FUNC_TakeCover(cBot* pBot); -int FUNC_EdictHoldsWeapon(edict_t * pEdict); +int FUNC_EdictHoldsWeapon(const edict_t* pEdict); -int FUNC_FindFarWaypoint(cBot * pBot, Vector avoid, bool safest); -int FUNC_FindCover(int from, int to); -int FUNC_PlayerSpeed(edict_t * edict); +int FUNC_FindFarWaypoint(cBot* pBot, const Vector& avoid, bool safest); +int FUNC_FindCover(const cBot* pBot); +int FUNC_PlayerSpeed(const edict_t* edict); bool FUNC_PlayerRuns(int speed); -void FUNC_HearingTodo(cBot * pBot); -void FUNC_ClearEnemyPointer(edict_t * c_pointer); +void FUNC_HearingTodo(cBot* pBot); +void FUNC_ClearEnemyPointer(edict_t* pPtr); //pPtr muddled with c_pointer? [APG]RoboCop[CL] -bool FUNC_IsOnLadder(edict_t * pEntity); -void FUNC_FindBreakable(cBot * pBot); -void FUNC_CheckForBombPlanted(); +bool FUNC_IsOnLadder(const edict_t* pEntity); +bool IsShootableBreakable(edict_t* pent); +void FUNC_FindBreakable(cBot* pBot); +void FUNC_AttackBreakable(cBot* pBot); +void FUNC_CheckForBombPlanted(edict_t* pEntity); -int FUNC_GiveHostage(cBot * pBot); // gives any hostage we still have to go for +int FUNC_GiveHostage(cBot* pBot); // gives any hostage we still have to go for -bool isHostageRescueable(cBot *pBot, edict_t *pHostage); -bool isHostageRescued(cBot *pBot, edict_t *pHostage); -bool isHostageFree(cBot * pBotWhoIsAsking, edict_t * pHostage); // is this hostage not used by any other bot? +bool isHostageRescueable(cBot* pBot, edict_t* pHostage); +bool isHostageRescued(cBot* pBot, const edict_t* pHostage); +bool isHostageFree(cBot* pBotWhoIsAsking, edict_t* pHostage); // is this hostage not used by any other bot? -int FUNC_BotEstimateHearVector(cBot * pBot, Vector v_sound); +int FUNC_BotEstimateHearVector(cBot* pBot, const Vector& v_sound); -bool FUNC_EdictIsAlive(edict_t *pEdict); +bool FUNC_EdictIsAlive(edict_t* pEdict); -bool FUNC_BotHoldsZoomWeapon(cBot * pBot); +bool FUNC_BotHoldsZoomWeapon(cBot* pBot); -int FUNC_InFieldOfView(edict_t * pEntity, Vector dest); +int FUNC_InFieldOfView(edict_t* pEntity, const Vector& dest); -bool VectorIsVisibleWithEdict(edict_t * pEdict, Vector dest, - char *checkname); +bool VectorIsVisibleWithEdict(edict_t* pEdict, const Vector& dest, + const char* checkname); -bool BOT_DecideTakeCover(cBot * pBot); +bool BOT_DecideTakeCover(cBot* pBot); // bot_buycode.cpp -void BotConsole(cBot * pBot); +int PriceWeapon(int weapon_id); +int ListIdWeapon(int weapon_id); + +void BotPrepareConsoleCommandsToBuyWeapon(cBot* pBot, const char* arg1, const char* arg2); + +bool GoodWeaponForTeam(int weapon, int team); + +void BotConsole(cBot* pBot); +void BotDecideWhatToBuy(cBot* pBot); + +// Buying sub-functions +int BotBuyPrimaryWeapon(cBot* pBot); +int BotBuySecondaryWeapon(cBot* pBot); +int BotBuyEquipment(cBot* pBot); -void rblog(char *txt); +void rblog(const char* txt); // bot.cpp // util.cpp -int UTIL_GiveWeaponId(const char *name); +int UTIL_GiveWeaponId(const char* name); int UTIL_GiveWeaponType(int weapon_id); -int UTIL_GetGrenadeType(edict_t * pEntity); +int UTIL_GetGrenadeType(edict_t* pEntity); -bool UTIL_IsVip(edict_t * pEntity); +bool UTIL_IsVip(edict_t* pEntity); -char *UTIL_GiveWeaponName(int id); +const char* UTIL_GiveWeaponName(int id); -void UTIL_SpeechSynth(edict_t * pEdict, char *szMessage); -void UTIL_BotRadioMessage(cBot * pBot, int radio, char *arg1, char *arg2); -void UTIL_BotPressKey(cBot * pBot, int type); +void UTIL_SpeechSynth(edict_t* pEdict, const char* szMessage); +void UTIL_BotRadioMessage(cBot* pBot, int radio, const char* arg1, const char* arg2); +void UTIL_BotPressKey(cBot* pBot, int type); // bot_navigate.cpp // .. -void CenterMessage(char *buffer); +void CenterMessage(const char* buffer); -#endif // BOT_FUNC_H +#endif // BOT_FUNC_H \ No newline at end of file diff --git a/bot_navigate.cpp b/bot_navigate.cpp index c98386c..7ac64ca 100644 --- a/bot_navigate.cpp +++ b/bot_navigate.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +20,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -28,7 +30,7 @@ **/ -#include +#include #include #include #include @@ -39,262 +41,215 @@ #include "bot_func.h" #include "game.h" #include "NodeMachine.h" -extern int mod_id; -extern edict_t *pHostEdict; - -#define SCAN_RADIUS 45 // Radius to scan to prevent blocking with players +extern int mod_id; +extern edict_t* pHostEdict; + +// Obstacle Avoidance +constexpr float TURN_ANGLE = 75.0f; // Degrees to turn when avoiding obstacles +constexpr float MOVE_DISTANCE = 24.0f; // Distance to move forward +constexpr std::uint8_t SCAN_RADIUS = 60; // Radius to scan to prevent blocking with players + +// Bot dimensions and movement capabilities +constexpr float BODY_SIDE_OFFSET = 16.0f; // Offset from center to check for body clearance +constexpr float FORWARD_CHECK_DISTANCE = 24.0f; // How far forward to check for obstacles +constexpr float STAND_VIEW_HEIGHT_OFFSET = -36.0f; // Eye level offset from origin when standing +constexpr float DUCK_VIEW_HEIGHT_OFFSET = -36.0f; // Eye level offset from origin when ducking +constexpr float DUCK_HEIGHT = 36.0f; // Height of the bot when ducking +constexpr float HEAD_CLEARANCE_CHECK_HEIGHT = 108.0f; // Height from origin to check for head clearance +constexpr float JUMP_CLEARANCE_CHECK_DROP = -81.0f; // Distance to trace down for jump clearance +constexpr float DUCK_CLEARANCE_CHECK_RISE = 72.0f; // Distance to trace up for duck clearance +constexpr float FEET_OFFSET = -35.0f; // Offset from origin to near the bot's feet /** * Given an angle, makes sure it wraps around properly * @param angle * @return */ -float fixAngle(float angle) { - if (angle > 180) return (angle - 360); - if (angle < -180) return (angle + 360); +float fixAngle(const float angle) { + if (angle > 180.0f) return angle - 360.0f; + if (angle < -180.0f) return angle + 360.0f; return angle; } -void botFixIdealPitch(edict_t * pEdict) { +void botFixIdealPitch(edict_t* pEdict) { pEdict->v.idealpitch = fixAngle(pEdict->v.idealpitch); } -void botFixIdealYaw(edict_t * pEdict) { +void botFixIdealYaw(edict_t* pEdict) { pEdict->v.ideal_yaw = fixAngle(pEdict->v.ideal_yaw); } -bool BotCanJumpUp(cBot * pBot) { - // What I do here is trace 3 lines straight out, one unit higher than - // the highest normal jumping distance. I trace once at the center of - // the body, once at the right side, and once at the left side. If all - // three of these TraceLines don't hit an obstruction then I know the - // area to jump to is clear. I then need to trace from head level, - // above where the bot will jump to, downward to see if there is anything - // blocking the jump. There could be a narrow opening that the body - // will not fit into. These horizontal and vertical TraceLines seem - // to catch most of the problems with falsely trying to jump on something - // that the bot can not get onto. - - TraceResult tr; - Vector v_jump, v_source, v_dest; - edict_t *pEdict = pBot->pEdict; - - // convert current view angle to vectors for TraceLine math... - - v_jump = pEdict->v.v_angle; - v_jump.x = 0; // reset pitch to 0 (level horizontally) - v_jump.z = 0; // reset roll to 0 (straight up and down) - - UTIL_MakeVectors(v_jump); - - // use center of the body first... - - // maximum jump height is 45, so check one unit above that (46) - v_source = pEdict->v.origin + Vector(0, 0, -36 + MAX_JUMPHEIGHT); - v_dest = v_source + gpGlobals->v_forward * 24; - - // trace a line forward at maximum jump height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return FALSE; - - // now check same height to one side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * 16 + Vector(0, 0, - -36 + - MAX_JUMPHEIGHT); - v_dest = v_source + gpGlobals->v_forward * 24; - - // trace a line forward at maximum jump height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return FALSE; - - // now check same height on the other side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * -16 + Vector(0, 0, - -36 + - MAX_JUMPHEIGHT); - v_dest = v_source + gpGlobals->v_forward * 24; - - // trace a line forward at maximum jump height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return FALSE; - - // now trace from head level downward to check for obstructions... - - // start of trace is 24 units in front of bot, 72 units above head... - v_source = pEdict->v.origin + gpGlobals->v_forward * 24; - - // offset 72 units from top of head (72 + 36) - v_source.z = v_source.z + 108; - - // end point of trace is 99 units straight down from start... - // (99 is 108 minus the jump limit height which is 45 - 36 = 9) - // fix by stefan, max jump height is 63 , not 45! (using duck-jump) - // 108 - (63-36) = 81 - v_dest = v_source + Vector(0, 0, -81); - - // trace a line straight down toward the ground... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return FALSE; - - // now check same height to one side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * 16 + - gpGlobals->v_forward * 24; - v_source.z = v_source.z + 108; - v_dest = v_source + Vector(0, 0, -81); - - // trace a line straight down toward the ground... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return FALSE; - - // now check same height on the other side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * -16 + - gpGlobals->v_forward * 24; - v_source.z = v_source.z + 108; - v_dest = v_source + Vector(0, 0, -81); - - // trace a line straight down toward the ground... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); - - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return FALSE; - - return TRUE; +bool traceLine(const Vector& v_source, const Vector& v_dest, const edict_t* pEdict, TraceResult& tr) { + UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, pEdict->v.pContainingEntity, &tr); + return tr.flFraction >= 1.0f; } -bool BotCanDuckUnder(cBot * pBot) { - // What I do here is trace 3 lines straight out, one unit higher than - // the ducking height. I trace once at the center of the body, once - // at the right side, and once at the left side. If all three of these - // TraceLines don't hit an obstruction then I know the area to duck to - // is clear. I then need to trace from the ground up, 72 units, to make - // sure that there is something blocking the TraceLine. Then we know - // we can duck under it. - - TraceResult tr; - Vector v_duck, v_source, v_dest; - edict_t *pEdict = pBot->pEdict; - - // convert current view angle to vectors for TraceLine math... - - v_duck = pEdict->v.v_angle; - v_duck.x = 0; // reset pitch to 0 (level horizontally) - v_duck.z = 0; // reset roll to 0 (straight up and down) - - UTIL_MakeVectors(v_duck); +// Helper function to perform a series of traces (center, left, right) +bool traceArea(const edict_t* pEdict, const Vector& base_source, const Vector& forward, const Vector& right, const bool check_hit) { + TraceResult tr; - // use center of the body first... + // Center trace + if (traceLine(base_source, base_source + forward, pEdict, tr) == check_hit) return false; - // duck height is 36, so check one unit above that (37) - v_source = pEdict->v.origin + Vector(0, 0, -36 + 37); - v_dest = v_source + gpGlobals->v_forward * 24; + // Left trace + Vector left_source = base_source - right * BODY_SIDE_OFFSET; + if (traceLine(left_source, left_source + forward, pEdict, tr) == check_hit) return false; - // trace a line forward at duck height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); + // Right trace + Vector right_source = base_source + right * BODY_SIDE_OFFSET; + if (traceLine(right_source, right_source + forward, pEdict, tr) == check_hit) return false; - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return false; - - // now check same height to one side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * 16 + Vector(0, 0, -36 + 37); - v_dest = v_source + gpGlobals->v_forward * 24; + return true; +} - // trace a line forward at duck height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); +// Helper function to set up vectors for movement checks +void setupMovementVectors(const edict_t* pEdict, Vector& forward, Vector& right) { + Vector angle = pEdict->v.v_angle; + angle.x = 0; + angle.z = 0; + UTIL_MakeVectors(angle); + forward = gpGlobals->v_forward; + right = gpGlobals->v_right; +} - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return false; +bool BotCanJumpUp(cBot* pBot) { + // What I do here is trace 3 lines straight out, one unit higher than + // the highest normal jumping distance. I trace once at the center of + // the body, once at the right side, and once at the left side. If all + // three of these TraceLines don't hit an obstruction then I know the + // area to jump to is clear. I then need to trace from head level, + // above where the bot will jump to, downward to see if there is anything + // blocking the jump. There could be a narrow opening that the body + // will not fit into. These horizontal and vertical TraceLines seem + // to catch most of the problems with falsely trying to jump on something + // that the bot can not get onto. + + const edict_t* pEdict = pBot->pEdict; + Vector v_forward, v_right; + setupMovementVectors(pEdict, v_forward, v_right); + + // Horizontal check at jump height + Vector v_source_horizontal = pEdict->v.origin + Vector(0, 0, STAND_VIEW_HEIGHT_OFFSET + static_cast(MAX_JUMPHEIGHT)); + if (!traceArea(pEdict, v_source_horizontal, v_forward * FORWARD_CHECK_DISTANCE, v_right, true)) { + return false; + } + + // Vertical check for head clearance + Vector v_source_vertical = pEdict->v.origin + v_forward * FORWARD_CHECK_DISTANCE; + v_source_vertical.z += HEAD_CLEARANCE_CHECK_HEIGHT; + if (!traceArea(pEdict, v_source_vertical, Vector(0, 0, JUMP_CLEARANCE_CHECK_DROP), v_right, true)) { + return false; + } + + return true; +} - // now check same height on the other side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * -16 + Vector(0, 0, - -36 + 37); - v_dest = v_source + gpGlobals->v_forward * 24; +bool BotCanDuckUnder(cBot* pBot) { + // What I do here is trace 3 lines straight out, one unit higher than + // the ducking height. I trace once at the center of the body, once + // at the right side, and once at the left side. If all three of these + // TraceLines don't hit an obstruction then I know the area to duck to + // is clear. I then need to trace from the ground up, 72 units, to make + // sure that there is something blocking the TraceLine. Then we know + // we can duck under it. + + const edict_t* pEdict = pBot->pEdict; + Vector v_forward, v_right; + setupMovementVectors(pEdict, v_forward, v_right); + + // Horizontal check at duck height + Vector v_source_horizontal = pEdict->v.origin + Vector(0, 0, DUCK_VIEW_HEIGHT_OFFSET + DUCK_HEIGHT + 1); + if (!traceArea(pEdict, v_source_horizontal, v_forward * FORWARD_CHECK_DISTANCE, v_right, true)) { + return false; + } + + // Vertical check for something to duck under + Vector v_source_vertical = pEdict->v.origin + v_forward * FORWARD_CHECK_DISTANCE; + v_source_vertical.z += FEET_OFFSET; + if (!traceArea(pEdict, v_source_vertical, Vector(0, 0, DUCK_CLEARANCE_CHECK_RISE), v_right, false)) { + return false; + } + + return true; +} - // trace a line forward at duck height... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); +bool isBotNearby(const cBot* pBot, const float radius) { + if (!pBot || !pBot->pEdict) { + return false; // Validate input + } - // if trace hit something, return FALSE - if (tr.flFraction < 1.0) - return false; + for (int i = 0; i < gpGlobals->maxClients; ++i) { + edict_t* pPlayer = INDEXENT(i + 1); - // now trace from the ground up to check for object to duck under... + if (pPlayer && !pPlayer->free && pPlayer != pBot->pEdict) { + float distance = (pPlayer->v.origin - pBot->pEdict->v.origin).Length(); + if (distance < radius) { + return true; + } + } + } - // start of trace is 24 units in front of bot near ground... - v_source = pEdict->v.origin + gpGlobals->v_forward * 24; - v_source.z = v_source.z - 35; // offset to feet + 1 unit up + return false; +} - // end point of trace is 72 units straight up from start... - v_dest = v_source + Vector(0, 0, 72); +void adjustBotAngle(cBot* pBot, const float angle) { + if (!pBot || !pBot->pEdict) { + return; + } - // trace a line straight up in the air... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); + pBot->pEdict->v.v_angle.y += angle; + UTIL_MakeVectors(pBot->pEdict->v.v_angle); +} - // if trace didn't hit something, return FALSE - if (tr.flFraction >= 1.0) - return false; +void avoidClustering(cBot* pBot) { + if (!pBot) { + return; + } - // now check same height to one side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * 16 + - gpGlobals->v_forward * 24; - v_source.z = v_source.z - 35; // offset to feet + 1 unit up - v_dest = v_source + Vector(0, 0, 72); + if (isBotNearby(pBot, SCAN_RADIUS)) { + adjustBotAngle(pBot, TURN_ANGLE); + } +} - // trace a line straight up in the air... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); +bool isPathBlocked(const cBot* pBot, const Vector& v_dest) { + if (!pBot || !pBot->pEdict) { + return true; // Assume blocked if input is invalid + } - // if trace didn't hit something, return FALSE - if (tr.flFraction >= 1.0) - return false; + TraceResult tr; + return !traceLine(pBot->pEdict->v.origin, v_dest, pBot->pEdict, tr); +} - // now check same height on the other side of the bot... - v_source = - pEdict->v.origin + gpGlobals->v_right * -16 + - gpGlobals->v_forward * 24; - v_source.z = v_source.z - 35; // offset to feet + 1 unit up - v_dest = v_source + Vector(0, 0, 72); - - // trace a line straight up in the air... - UTIL_TraceLine(v_source, v_dest, dont_ignore_monsters, - pEdict->v.pContainingEntity, &tr); +void adjustPathIfBlocked(cBot* pBot) { + if (!pBot) { + return; + } - // if trace didn't hit something, return FALSE - if (tr.flFraction >= 1.0) - return false; + Vector v_dest = pBot->pEdict->v.origin + gpGlobals->v_forward * MOVE_DISTANCE; - return true; + if (isPathBlocked(pBot, v_dest)) { + adjustBotAngle(pBot, TURN_ANGLE); + } } + +void BotNavigate(cBot* pBot) { + if (!pBot || !pBot->pEdict) { + return; + } + + // Avoid clustering with other bots + avoidClustering(pBot); + + // Check if the path is blocked and adjust angle if necessary + Vector v_dest = pBot->pEdict->v.origin + gpGlobals->v_forward * MOVE_DISTANCE; + if (isPathBlocked(pBot, v_dest)) { + adjustBotAngle(pBot, TURN_ANGLE); + v_dest = pBot->pEdict->v.origin + gpGlobals->v_forward * MOVE_DISTANCE; // Recalculate destination + } + + // If the path is clear, move the bot + if (!isPathBlocked(pBot, v_dest)) { + pBot->pEdict->v.origin = v_dest; + } +} \ No newline at end of file diff --git a/bot_weapons.h b/bot_weapons.h index ce072e3..a062537 100644 --- a/bot_weapons.h +++ b/bot_weapons.h @@ -31,7 +31,7 @@ #define BOT_WEAPONS_H // weapon ID values for Valve's Team Fortress Classic & 1.5 -#define TF_WEAPON_UNKNOWN1 1 +/*#define TF_WEAPON_UNKNOWN1 1 #define TF_WEAPON_UNKNOWN2 2 #define TF_WEAPON_MEDIKIT 3 #define TF_WEAPON_SPANNER 4 @@ -54,48 +54,52 @@ #define TF_WEAPON_RAILGUN 21 #define TF_WEAPON_PL 22 #define TF_WEAPON_KNIFE 23 +*/ // weapon ID values for Counter-Strike -#define CS_WEAPON_P228 1 -#define CS_WEAPON_UNKNOWN2 2 -#define CS_WEAPON_SCOUT 3 -#define CS_WEAPON_HEGRENADE 4 -#define CS_WEAPON_XM1014 5 -#define CS_WEAPON_C4 6 -#define CS_WEAPON_MAC10 7 -#define CS_WEAPON_AUG 8 -#define CS_WEAPON_SMOKEGRENADE 9 -#define CS_WEAPON_ELITE 10 -#define CS_WEAPON_FIVESEVEN 11 -#define CS_WEAPON_UMP45 12 -#define CS_WEAPON_SG550 13 -#define CS_WEAPON_GALIL 14 // CS 1.6 -#define CS_WEAPON_FAMAS 15 // CS 1.6 -#define CS_WEAPON_USP 16 -#define CS_WEAPON_GLOCK18 17 -#define CS_WEAPON_AWP 18 -#define CS_WEAPON_MP5NAVY 19 -#define CS_WEAPON_M249 20 -#define CS_WEAPON_M3 21 -#define CS_WEAPON_M4A1 22 -#define CS_WEAPON_TMP 23 -#define CS_WEAPON_G3SG1 24 -#define CS_WEAPON_FLASHBANG 25 -#define CS_WEAPON_DEAGLE 26 -#define CS_WEAPON_SG552 27 -#define CS_WEAPON_AK47 28 -#define CS_WEAPON_KNIFE 29 -#define CS_WEAPON_P90 30 +enum : std::uint8_t +{ + CS_WEAPON_P228 = 1, + CS_WEAPON_SHIELD = 2, + CS_WEAPON_SCOUT = 3, + CS_WEAPON_HEGRENADE = 4, + CS_WEAPON_XM1014 = 5, + CS_WEAPON_C4 = 6, + CS_WEAPON_MAC10 = 7, + CS_WEAPON_AUG = 8, + CS_WEAPON_SMOKEGRENADE = 9, + CS_WEAPON_ELITE = 10, + CS_WEAPON_FIVESEVEN = 11, + CS_WEAPON_UMP45 = 12, + CS_WEAPON_SG550 = 13, + CS_WEAPON_GALIL = 14, // CS 1.6 + CS_WEAPON_FAMAS = 15, // CS 1.6 + CS_WEAPON_USP = 16, + CS_WEAPON_GLOCK18 = 17, + CS_WEAPON_AWP = 18, + CS_WEAPON_MP5NAVY = 19, + CS_WEAPON_M249 = 20, + CS_WEAPON_M3 = 21, + CS_WEAPON_M4A1 = 22, + CS_WEAPON_TMP = 23, + CS_WEAPON_G3SG1 = 24, + CS_WEAPON_FLASHBANG = 25, + CS_WEAPON_DEAGLE = 26, + CS_WEAPON_SG552 = 27, + CS_WEAPON_AK47 = 28, + CS_WEAPON_KNIFE = 29, + CS_WEAPON_P90 = 30, //30.8.04 redefined by frashman -#define CS_DEFUSEKIT 98 // old value was 99, same as SHIELD -> Bug?? + CS_DEFUSEKIT = 98 // old value was 99, same as SHIELD -> Bug?? +}; // NOT CONFIRMED -#define CS_WEAPON_SHIELD 99 // Not used for detecting, only for +//#define CS_WEAPON_SHIELD 99 // Not used for detecting, only for // bot.dll // Woah, i rule! :D, figured out all Earth Special Forces Weapon ID's.. -#define ESF_WEAPON_MELEE 1 +/*#define ESF_WEAPON_MELEE 1 #define ESF_KIBLAST 2 #define ESF_GALLITGUN 3 #define ESF_KAMEHAMEHA 4 @@ -112,6 +116,7 @@ #define ESF_DEATHBALL 15 #define ESF_BURNINGATTACK 16 #define ESF_SENSU 17 +*/ typedef struct { char szClassname[64]; diff --git a/build.cpp b/build.cpp index 2cfe18e..4831227 100644 --- a/build.cpp +++ b/build.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com // 14/11/04 // Updated Build nr; it compiles in MSVC again, and should compile in Linux... i did not // change that much. @@ -8,4 +10,4 @@ // 07/07/04 - Comment changed, it was moved from dll.cpp, not bot.cpp ;) (nitpicking) :D // BUILD NR & Version -char *rb_version_nr = "4.0.1"; +const char *rb_version_nr = "4.0.5-beta5"; diff --git a/configure.py b/configure.py new file mode 100644 index 0000000..f3b03d1 --- /dev/null +++ b/configure.py @@ -0,0 +1,33 @@ +# AMBuild Configuration Script for RealBot, written by Anonymous Player +# vim: set sts=4 ts=8 sw=4 tw=99 et: +API_VERSION = '2.2.3' + +import sys +try: + from ambuild2 import run + if not run.HasAPI(API_VERSION): + raise Exception() +except: + sys.stderr.write('AMBuild {0} must be installed to build this project.\n'.format(API_VERSION)) + sys.stderr.write('http://www.alliedmods.net/ambuild\n') + sys.exit(1) + +def make_objdir_name(p): + return 'obj-' + util.Platform() + '-' + p.target_arch + +builder = run.BuildParser(sourcePath = sys.path[0], api=API_VERSION) +builder.default_arch = 'x86' +builder.default_build_folder = make_objdir_name +# builder.options.add_argument('--hl1sdk', type=str, dest='hl1sdk_path', default=None, + # help='Half-Life 1 SDK source tree folder') +# builder.options.add_argument('--mm-path', type=str, dest='mm_path', default=None, + # help='Metamod source tree folder') +builder.options.add_argument('--enable-optimize', action='store_const', const='1', dest='optimize', + help='Enable optimization') +builder.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug', + help='Enable debug') +builder.options.add_argument('--enable-static-lib', action='store_const', const='1', dest='staticlib', + help='Enable statically link the sanitizer runtime') +builder.options.add_argument('--enable-shared-lib', action='store_const', const='1', dest='sharedlib', + help='Enable dynamically link the sanitizer runtime') +builder.Configure() \ No newline at end of file diff --git a/dependencies/hlsdk/dlls/extdll.h b/dependencies/hlsdk/dlls/extdll.h index 2e75d54..342dccc 100644 --- a/dependencies/hlsdk/dlls/extdll.h +++ b/dependencies/hlsdk/dlls/extdll.h @@ -56,6 +56,10 @@ #define NOMCX #define NOIME #include "windows.h" + +#undef max +#undef min + #else // _WIN32 #ifndef FALSE #define FALSE 0 @@ -69,24 +73,28 @@ typedef int BOOL; #define MAX_PATH PATH_MAX #include #include -#include // memset +#include // memset +//Fix for GCC 8 - [APG]RoboCop[CL] #include -using namespace std; -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif + #ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) #endif -#ifndef _vsnprintf -#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) #endif + +#undef max +#undef min + +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) #endif //_WIN32 // Misc C-runtime library headers -#include "stdio.h" -#include "stdlib.h" -#include "math.h" +#include +#include +#include // Header file containing definition of globalvars_t and entvars_t typedef unsigned int func_t; // diff --git a/dependencies/hlsdk/dlls/util.h b/dependencies/hlsdk/dlls/util.h index 42e1667..a9c7541 100644 --- a/dependencies/hlsdk/dlls/util.h +++ b/dependencies/hlsdk/dlls/util.h @@ -74,7 +74,7 @@ inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) typedef int EOFFSET; // In case it's not alread defined -typedef int BOOL; +typedef int BOOL; //-V677 // In case this ever changes #ifndef M_PI @@ -134,7 +134,7 @@ inline entvars_t *VARS(entvars_t *pev) { return pev; } inline entvars_t *VARS(edict_t *pent) { if ( !pent ) - return NULL; + return nullptr; return &pent->v; } @@ -149,8 +149,8 @@ inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, ent // Testing the three types of "entity" for nullity #define eoNullEntity 0 inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } -inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); } -inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } +inline BOOL FNullEnt(const edict_t* pent) { return pent == nullptr || FNullEnt(OFFSET(pent)); } +inline BOOL FNullEnt(entvars_t* pev) { return pev == nullptr || FNullEnt(OFFSET(pev)); } // Testing strings for nullity #define iStringNull 0 @@ -159,13 +159,13 @@ inline BOOL FStringNull(int iString) { return iString == iStringNull; } #define cchMapNameMost 32 // 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 (-1.0f) // +-180 degrees +#define VIEW_FIELD_WIDE (-0.7f) // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks +#define VIEW_FIELD_NARROW 0.7f // +-45 degrees, more narrow check used to set up ranged attacks +#define VIEW_FIELD_ULTRA_NARROW 0.9f // +-25 degrees, more narrow check used to set up ranged attacks // All monsters need this data -#define DONT_BLEED -1 +#define DONT_BLEED (-1) #define BLOOD_COLOR_RED (BYTE)247 #define BLOOD_COLOR_YELLOW (BYTE)195 #define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW @@ -197,12 +197,21 @@ typedef enum } TOGGLE_STATE; // Misc useful -inline BOOL FStrEq(const char*sz1, const char*sz2) - { return (strcmp(sz1, sz2) == 0); } +inline BOOL FStrEq(const char* sz1, const char* sz2) +{ + return (strcmp(sz1, sz2) == 0); +} inline BOOL FClassnameIs(edict_t* pent, const char* szClassname) - { return FStrEq(STRING(VARS(pent)->classname), szClassname); } +{ + if (FNullEnt(pent)) + return FALSE; + + return FStrEq(STRING(VARS(pent)->classname), szClassname); +} inline BOOL FClassnameIs(entvars_t* pev, const char* szClassname) - { return FStrEq(STRING(pev->classname), szClassname); } +{ + return FStrEq(STRING(pev->classname), szClassname); +} class CBaseEntity; diff --git a/dependencies/hlsdk/dlls/vector.h b/dependencies/hlsdk/dlls/vector.h index 08148de..58c441e 100644 --- a/dependencies/hlsdk/dlls/vector.h +++ b/dependencies/hlsdk/dlls/vector.h @@ -15,6 +15,8 @@ #ifndef VECTOR_H #define VECTOR_H +#include + //========================================================= // 2DVector - used for many pathfinding and many other // operations that are treated as planar rather than 3d. @@ -29,7 +31,7 @@ class Vector2D inline Vector2D operator*(float fl) const { return Vector2D(x*fl, y*fl); } inline Vector2D operator/(float fl) const { return Vector2D(x/fl, y/fl); } - inline float Length(void) const { return sqrt(x*x + y*y ); } + inline float Length(void) const { return std::sqrt(x*x + y*y ); } inline Vector2D Normalize ( void ) const { @@ -77,7 +79,7 @@ class Vector // same data-layout as engine's vec3_t, // Methods inline void CopyToArray(float* rgfl) const { rgfl[0] = x, rgfl[1] = y, rgfl[2] = z; } - inline float Length(void) const { return sqrt(x*x + y*y + z*z); } + inline float Length(void) const { return std::sqrt(x*x + y*y + z*z); } operator float *() { return &x; } // Vectors will now automatically convert to float * when needed operator const float *() const { return &x; } // Vectors will now automatically convert to float * when needed inline Vector Normalize(void) const @@ -97,7 +99,7 @@ class Vector // same data-layout as engine's vec3_t, return Vec2; } - inline float Length2D(void) const { return sqrt(x*x + y*y); } + inline float Length2D(void) const { return std::sqrt(x*x + y*y); } // Members vec_t x, y, z; diff --git a/dependencies/hlsdk/linux/vgui.so b/dependencies/hlsdk/linux/vgui.so deleted file mode 120000 index 3b3dc1b..0000000 --- a/dependencies/hlsdk/linux/vgui.so +++ /dev/null @@ -1 +0,0 @@ -release/vgui.so \ No newline at end of file diff --git a/dependencies/metamod-hl1/metamod/osdep.h b/dependencies/metamod-hl1/metamod/osdep.h index f20d773..b60507c 100644 --- a/dependencies/metamod-hl1/metamod/osdep.h +++ b/dependencies/metamod-hl1/metamod/osdep.h @@ -216,7 +216,7 @@ mBOOL os_safe_call(REG_CMD_FN pfn); #elif defined(_WIN32) #define snprintf _snprintf #define vsnprintf _vsnprintf - #define sleep(x) Sleep(x*1000) + #define sleep(x) Sleep((x)*1000) #define strcasecmp stricmp #define strncasecmp _strnicmp #include diff --git a/dll.cpp b/dll.cpp index 5cca9f7..ca7c9a0 100644 --- a/dll.cpp +++ b/dll.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,21 +20,28 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. * **/ -#include +#include +#include +#include +#include +#include + #include #include #include #include +#include "globals.h" + #include "bot.h" #include "bot_func.h" #include "IniParser.h" @@ -43,20 +52,20 @@ // this makes sure function `min` is available (instead of fmin). #include -using namespace std; +//using namespace std; DLL_FUNCTIONS gFunctionTable; DLL_FUNCTIONS gFunctionTable_post; enginefuncs_t g_engfuncs; -globalvars_t *gpGlobals; +globalvars_t* gpGlobals; char g_argv[1024]; extern cBot bots[32]; extern bool radio_message; -extern char *rb_version_nr; -extern char *message; +extern const char* rb_version_nr; +extern char* message; // DLL specific variables DLL_GLOBAL const Vector g_vecZero = Vector(0, 0, 0); @@ -65,31 +74,31 @@ DLL_GLOBAL const Vector g_vecZero = Vector(0, 0, 0); cGame Game; cNodeMachine NodeMachine; cChatEngine ChatEngine; -FILE *fpRblog = NULL; +FILE* fpRblog = nullptr; -float f_load_time = 0.0; -float f_minplayers_think = 0.0; // timer used to add realbots if internet play enabled +float f_load_time = 0.0f; +float f_minplayers_think = 0.0f; // timer used to add realbots if internet play enabled int mod_id = CSTRIKE_DLL; // should be changed to 0 when we are going to do multi-mod stuff int m_spriteTexture = 0; -bool isFakeClientCommand = FALSE; +bool isFakeClientCommand = false; int fake_arg_count; -float bot_check_time = 30.0; +float bot_check_time = 30.0f; int min_bots = -1; int max_bots = -1; int min_players = -1; // minimum amount of players that should be in the server all the time int num_bots = 0; int prev_num_bots = 0; -bool g_GameRules = FALSE; -edict_t *clients[32]; -edict_t *pHostEdict = NULL; -float welcome_time = 0.0; +bool g_GameRules = false; +edict_t* clients[32]; +edict_t* pHostEdict = nullptr; +float welcome_time = 0.0f; bool welcome_sent = false; -FILE *bot_cfg_fp = NULL; -bool need_to_open_cfg = TRUE; -float bot_cfg_pause_time = 0.0; -float respawn_time = 0.0; -bool spawn_time_reset = FALSE; +FILE* bot_cfg_fp = nullptr; +bool need_to_open_cfg = true; +float bot_cfg_pause_time = 0.0f; +float respawn_time = 0.0f; +bool spawn_time_reset = false; // Interval between joining bots. int internet_max_interval = 30; @@ -99,11 +108,6 @@ int internet_min_interval = 10; // Counter-Strike 1.6 or 1.5 int counterstrike = 0; // Default 1.5 -void UpdateClientData(const struct edict_s *ent, int sendweapons, - struct clientdata_s *cd); - -void ProcessBotCfgFile(void); - // External added variables bool end_round = false; @@ -119,40 +123,41 @@ bool internet_addbot = false; // Add a bot? float add_timer = -1; // Timer for adding bots bool internet_play = false; -void RealBot_ServerCommand(void); - // START of Metamod stuff enginefuncs_t meta_engfuncs; -gamedll_funcs_t *gpGamedllFuncs; -mutil_funcs_t *gpMetaUtilFuncs; -meta_globals_t *gpMetaGlobals; +gamedll_funcs_t* gpGamedllFuncs; +mutil_funcs_t* gpMetaUtilFuncs; +meta_globals_t* gpMetaGlobals; META_FUNCTIONS gMetaFunctionTable = { - NULL, // pfnGetEntityAPI() - NULL, // pfnGetEntityAPI_Post() + nullptr, // pfnGetEntityAPI() + nullptr, // pfnGetEntityAPI_Post() GetEntityAPI2, // pfnGetEntityAPI2() GetEntityAPI2_Post, // pfnGetEntityAPI2_Post() - NULL, // pfnGetNewDLLFunctions() - NULL, // pfnGetNewDLLFunctions_Post() + nullptr, // pfnGetNewDLLFunctions() + nullptr, // pfnGetNewDLLFunctions_Post() GetEngineFunctions, // pfnGetEngineFunctions() - NULL, // pfnGetEngineFunctions_Post() + nullptr, // pfnGetEngineFunctions_Post() }; +constexpr const char* REALBOT_AUTHOR = "Stefan Hendriks"; +constexpr const char* REALBOT_URL = "http://realbot.bots-united.com/"; + plugin_info_t Plugin_info = { META_INTERFACE_VERSION, // interface version "RealBot", // plugin name - rb_version_nr, // plugin version + rb_version_nr, // plugin version __DATE__, // date of creation - "Stefan Hendriks", // plugin author - "http://realbot.bots-united.com/", // plugin URL + REALBOT_AUTHOR, // plugin author + REALBOT_URL, // plugin URL "REALBOT", // plugin logtag PT_CHANGELEVEL, // when loadable <-- FIX PT_ANYTIME, // when unloadable }; -C_DLLEXPORT int Meta_Query(const char *ifvers, plugin_info_t **pPlugInfo, - mutil_funcs_t *pMetaUtilFuncs) { +C_DLLEXPORT int Meta_Query(const char* ifvers, 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. @@ -162,42 +167,42 @@ C_DLLEXPORT int Meta_Query(const char *ifvers, plugin_info_t **pPlugInfo, *pPlugInfo = &Plugin_info; // check for interface version compatibility - if (strcmp(ifvers, Plugin_info.ifvers) != 0) { + if (std::strcmp(ifvers, Plugin_info.ifvers) != 0) { int mmajor = 0, mminor = 0, pmajor = 0, pminor = 0; LOG_CONSOLE(PLID, - "%s: meta-interface version mismatch (metamod: %s, %s: %s)", - Plugin_info.name, ifvers, Plugin_info.name, - Plugin_info.ifvers); + "%s: meta-interface version mismatch (metamod: %s, %s: %s)", + Plugin_info.name, ifvers, Plugin_info.name, + Plugin_info.ifvers); LOG_MESSAGE(PLID, - "%s: meta-interface version mismatch (metamod: %s, %s: %s)", - Plugin_info.name, ifvers, Plugin_info.name, - Plugin_info.ifvers); + "%s: meta-interface version mismatch (metamod: %s, %s: %s)", + Plugin_info.name, ifvers, Plugin_info.name, + Plugin_info.ifvers); // if plugin has later interface version, it's incompatible (update metamod) - sscanf(ifvers, "%d:%d", &mmajor, &mminor); - sscanf(META_INTERFACE_VERSION, "%d:%d", &pmajor, &pminor); - if ((pmajor > mmajor) || ((pmajor == mmajor) && (pminor > mminor))) { + std::sscanf(ifvers, "%d:%d", &mmajor, &mminor); + std::sscanf(META_INTERFACE_VERSION, "%d:%d", &pmajor, &pminor); + if (pmajor > mmajor || (pmajor == mmajor && pminor > mminor)) { LOG_CONSOLE(PLID, - "metamod version is too old for this plugin; update metamod"); + "metamod version is too old for this plugin; update metamod"); LOG_ERROR(PLID, - "metamod version is too old for this plugin; update metamod"); - return (FALSE); + "metamod version is too old for this plugin; update metamod"); + return 0; } - // if plugin has older major interface version, it's incompatible (update plugin) - else if (pmajor < mmajor) { + // if plugin has older major interface version, it's incompatible (update plugin) + if (pmajor < mmajor) { LOG_CONSOLE(PLID, - "metamod version is incompatible with this plugin; please find a newer version of this plugin"); + "metamod version is incompatible with this plugin; please find a newer version of this plugin"); LOG_ERROR(PLID, - "metamod version is incompatible with this plugin; please find a newer version of this plugin"); - return (FALSE); + "metamod version is incompatible with this plugin; please find a newer version of this plugin"); + return 0; } } - return (TRUE); // tell metamod this plugin looks safe + return 1; // tell metamod this plugin looks safe } C_DLLEXPORT int -Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, - meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) { +Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS* pFunctionTable, + 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. @@ -205,16 +210,16 @@ Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, // are we allowed to load this plugin now ? if (now > Plugin_info.loadable) { LOG_CONSOLE(PLID, - "%s: plugin NOT attaching (can't load plugin right now)", - Plugin_info.name); + "%s: plugin NOT attaching (can't load plugin right now)", + Plugin_info.name); LOG_ERROR(PLID, - "%s: plugin NOT attaching (can't load plugin right now)", - Plugin_info.name); - return (FALSE); // returning FALSE prevents metamod from attaching this plugin + "%s: plugin NOT attaching (can't load plugin right now)", + Plugin_info.name); + return 0; // returning FALSE prevents metamod from attaching this plugin } // keep track of the pointers to engine function tables metamod gives us gpMetaGlobals = pMGlobals; - memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS)); + std::memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS)); gpGamedllFuncs = pGamedllFuncs; // print a message to notify about plugin attaching @@ -227,7 +232,7 @@ Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, // Notify user that 'realbot' command is regged LOG_CONSOLE(PLID, "realbot - command prefix is now reserved."); LOG_MESSAGE(PLID, "realbot - command prefix is now reserved."); - return (TRUE); // returning TRUE enables metamod to attach this plugin + return 1; // returning TRUE enables metamod to attach this plugin } C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { @@ -237,78 +242,75 @@ C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { // is metamod allowed to unload the plugin ? if ((now > Plugin_info.unloadable) && (reason != PNL_CMD_FORCED)) { LOG_CONSOLE(PLID, - "%s: plugin NOT detaching (can't unload plugin right now)", - Plugin_info.name); + "%s: plugin NOT detaching (can't unload plugin right now)", + Plugin_info.name); LOG_ERROR(PLID, - "%s: plugin NOT detaching (can't unload plugin right now)", - Plugin_info.name); - return (FALSE); // returning FALSE prevents metamod from unloading this plugin + "%s: plugin NOT detaching (can't unload plugin right now)", + Plugin_info.name); + return 0; // returning FALSE prevents metamod from unloading this plugin } NodeMachine.FreeVisibilityTable(); free(message); - return (TRUE); // returning TRUE enables metamod to unload this plugin + return 1; // returning TRUE enables metamod to unload this plugin } // END of Metamod stuff #ifdef _WIN32 // Required DLL entry point -int WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - return TRUE; +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + return TRUE; } #endif /* */ C_DLLEXPORT void WINAPI -GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine, - globalvars_t *pGlobals) { +GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, + globalvars_t* pGlobals) { // get the engine functions from the engine... - memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); + std::memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); gpGlobals = pGlobals; mod_id = CSTRIKE_DLL; // so far RealBot works only for CS, eh Stefan ? :) // @PMB -> Yes so far it does ;) working on this MOD-GAME independent structure... grmbl } -void GameDLLInit(void) { - // clear log.txt - FILE *fplog; - fplog = fopen("reallog.txt", "wt"); +void GameDLLInit() { + FILE* fplog = std::fopen("reallog.txt", "wt"); if (fplog) { - fprintf(fplog, "Realbot Logbook\n"); - fprintf(fplog, "Version %s\n\n", rb_version_nr); - fclose(fplog); + std::fprintf(fplog, "Realbot Logbook\n"); + std::fprintf(fplog, "Version %s\n\n", rb_version_nr); + std::fclose(fplog); } // and now open it for the entire bot-dll-lifetime - fpRblog = fopen("reallog.txt", "at"); + fpRblog = std::fopen("reallog.txt", "at"); rblog("Initializing clients.."); - for (int i = 0; i < 32; i++) - clients[i] = NULL; + for (edict_t*& client : clients) + client = nullptr; rblog("OK\n"); // initialize the bots array of structures... rblog("Initializing memory for bots array.."); - memset(bots, 0, sizeof(bots)); + std::memset(bots, 0, sizeof(bots)); rblog("OK\n"); rblog("Verifying realbot is installed correctly.."); - FILE *fp; bool bInstalledCorrectly = false; - fp = fopen("realbot/dll/realbot_mm.dll", "rb"); - if (fp != NULL) { + FILE* fp = std::fopen("realbot/dll/realbot_mm.dll", "rb"); + if (fp != nullptr) { bInstalledCorrectly = true; - fclose(fp); + std::fclose(fp); } - fp = fopen("realbot/dll/realbot_mm_i386.so", "rb"); - if (fp != NULL) { + fp = std::fopen("realbot/dll/realbot_mm.so", "rb"); + if (fp != nullptr) { bInstalledCorrectly = true; - fclose(fp); + std::fclose(fp); } if (bInstalledCorrectly) @@ -318,15 +320,15 @@ void GameDLLInit(void) { // When installed correctly let the user know if (bInstalledCorrectly) - REALBOT_PRINT(NULL, "GAMEDLLINIT", - "Notice: RealBot is installed in the correct directory."); + REALBOT_PRINT(nullptr, "GAMEDLLINIT", + "Notice: RealBot is installed in the correct directory."); else - REALBOT_PRINT(NULL, "GAMEDLLINIT", - "WARNING: RealBot is NOT installed in the correct directory."); + REALBOT_PRINT(nullptr, "GAMEDLLINIT", + "WARNING: RealBot is NOT installed in the correct directory."); Game.Init(); Game.LoadNames(); - Game.LoadBuyTable(); + cGame::LoadBuyTable(); // NodeMachine NodeMachine.init(); @@ -349,15 +351,15 @@ void GameDLLInit(void) { // if you know a more orthodox way of doing this, please tell me. // test file, if found = STEAM Linux/Win32 dedicated server - fp = fopen("valve/steam.inf", "rb"); - if (fp != NULL) { - fclose(fp); // test was successful, close it + fp = std::fopen("valve/steam.inf", "rb"); + if (fp != nullptr) { + std::fclose(fp); // test was successful, close it counterstrike = 1; // its cs 1.6 } // test file, if found = STEAM Win32 listenserver - fp = fopen("FileSystem_Steam.dll", "rb"); - if (fp != NULL) { - fclose(fp); // test was successful, close it + fp = std::fopen("FileSystem_Steam.dll", "rb"); + if (fp != nullptr) { + std::fclose(fp); // test was successful, close it counterstrike = 1; // its cs 1.6 } @@ -370,11 +372,11 @@ void GameDLLInit(void) { } // INITIALIZATION -int Spawn(edict_t *pent) { +int Spawn(edict_t* pent) { if (gpGlobals->deathmatch) { - char *pClassname = (char *) STRING(pent->v.classname); + const char* pClassname = STRING(pent->v.classname); - if (strcmp(pClassname, "worldspawn") == 0) { + if (std::strcmp(pClassname, "worldspawn") == 0) { // do level initialization stuff here... draw_nodes = false; draw_nodepath = -1; @@ -389,13 +391,13 @@ int Spawn(edict_t *pent) { // sound PRECACHE_SOUND("misc/imgood12.wav"); - g_GameRules = TRUE; + g_GameRules = true; //bot_cfg_pause_time = 0.0; - respawn_time = 0.0; - spawn_time_reset = FALSE; + respawn_time = 0.0f; + spawn_time_reset = false; prev_num_bots = num_bots; num_bots = 0; - bot_check_time = gpGlobals->time + 30.0; + bot_check_time = gpGlobals->time + 30.0f; rblog("SPAWN : f_load_time is set\n"); f_load_time = gpGlobals->time + 6; @@ -405,7 +407,8 @@ int Spawn(edict_t *pent) { NodeMachine.experience_load(); ChatEngine.fThinkTimer = gpGlobals->time; - } else if (strcmp(pClassname, "trigger_multiple") == 0) { + } + else if (std::strcmp(pClassname, "trigger_multiple") == 0) { // make it a func_button? //sprintf(STRING(pent->v.classname), "func_button"); @@ -415,15 +418,11 @@ int Spawn(edict_t *pent) { RETURN_META_VALUE(MRES_IGNORED, 0); } -BOOL -ClientConnect(edict_t *pEntity, const char *pszName, - const char *pszAddress, char szRejectReason[128]) { +int ClientConnect(edict_t* pEntity, const char* pszName, + const char* pszAddress, char szRejectReason[128]) { if (gpGlobals->deathmatch) { - int i; - int count = 0; - // check if this client is the listen server client - if (strcmp(pszAddress, "loopback") == 0) { + if (std::strcmp(pszAddress, "loopback") == 0) { // save the edict of the listen server client... pHostEdict = pEntity; } @@ -433,9 +432,13 @@ ClientConnect(edict_t *pEntity, const char *pszName, // check if this is NOT a bot joining the server... - if (strcmp(pszAddress, "127.0.0.1") != 0) { + if (std::strcmp(pszAddress, "127.0.0.1") != 0) { + + int count = 0; + int i; + // don't try to add bots for 60 seconds, give client time to get added - bot_check_time = gpGlobals->time + 60.0; + bot_check_time = gpGlobals->time + 60.0f; for (i = 0; i < 32; i++) { if (bots[i].bIsUsed) // count the number of bots in use @@ -443,36 +446,35 @@ ClientConnect(edict_t *pEntity, const char *pszName, } // if there are currently more than the minimum number of bots running // then kick one of the bots off the server... - if ((count > min_bots) && (min_bots != -1)) { + if (count > min_bots && min_bots != -1) { for (i = 0; i < 32; i++) { if (bots[i].bIsUsed) // is this slot used? { char cmd[80]; - sprintf(cmd, "kick \"%s\"\n", bots[i].name); + snprintf(cmd, sizeof(cmd), "kick \"%s\"\n", bots[i].name); SERVER_COMMAND(cmd); // kick the bot using (kick "name") break; } } } - } else {} + } + else {} } RETURN_META_VALUE(MRES_IGNORED, 0); } -void ClientDisconnect(edict_t *pEntity) { +void ClientDisconnect(edict_t* pEntity) { if (gpGlobals->deathmatch) { - int i; - - i = 0; - while ((i < 32) && (clients[i] != pEntity)) + int i = 0; + while (i < 32 && clients[i] != pEntity) i++; if (i < 32) - clients[i] = NULL; + clients[i] = nullptr; // when a bot... for (i = 0; i < 32; i++) { @@ -481,7 +483,7 @@ void ClientDisconnect(edict_t *pEntity) { // someone kicked this bot off of the server... bots[i].bIsUsed = false; // this slot is now free to use bots[i].fKickTime = gpGlobals->time; // save the kicked time - bots[i].pEdict = NULL; // make NULL + bots[i].pEdict = nullptr; // make NULL break; } } @@ -490,10 +492,10 @@ void ClientDisconnect(edict_t *pEntity) { RETURN_META(MRES_IGNORED); } -void ClientPutInServer(edict_t *pEntity) { +void ClientPutInServer(edict_t* pEntity) { int i = 0; - while ((i < 32) && (clients[i] != NULL)) + while (i < 32 && (clients[i] != nullptr)) i++; if (i < 32) @@ -503,7 +505,7 @@ void ClientPutInServer(edict_t *pEntity) { } // CLIENT / CONSOLE / COMMANDS -void ClientCommand(edict_t *pEntity) { +void ClientCommand(edict_t* pEntity) { /* @@ -527,24 +529,27 @@ void ClientCommand(edict_t *pEntity) { RETURN_META(MRES_IGNORED); } - -// TODO: Revise this method -void StartFrame(void) { +void StartFrame() { if (!gpGlobals->deathmatch) return; // bots only work in 'deathmatch mode' -// REALBOT_PRINT("StartFrame", "BEGIN"); + //REALBOT_PRINT("StartFrame", "BEGIN"); - edict_t *pPlayer; - static float check_server_cmd = 0.0; - static int i, index, player_index, bot_index; - static float previous_time = -1.0; - static float client_update_time = 0.0; + edict_t* pPlayer; + static int i, player_index, bot_index; + static float previous_time = -1.0f; + static float client_update_time = 0.0f; clientdata_s cd; - char msg[256]; int count = 0; int waits = 0; // How many bots had to wait. - // When a user - or anything else - specified a higher number of 0 to - // kick bots, then we will do as told. + // TODO: Add Autovacate regulation with bot count loop + // Experimental autovacate script from POD-Bot Incomplete [APG]RoboCop[CL] + /*max_bots = max_bots > gpGlobals->maxClients ? gpGlobals->maxClients : max_bots < 0 ? 0 : max_bots; + min_bots = min_bots > gpGlobals->maxClients ? gpGlobals->maxClients : min_bots < 0 ? 0 : min_bots; + if (max_bots < min_bots) + min_bots = max_bots;*/ + + // When a user - or anything else - specified a higher number of 0 to + // kick bots, then we will do as told. if (kick_amount_bots > 0) { int kicking_team = 0; // What team should we kick? @@ -555,10 +560,12 @@ void StartFrame(void) { { kicking_team = 1; kick_bots_team++; - } else if (kick_bots_team == 7) { + } + else if (kick_bots_team == 7) { kicking_team = 2; kick_bots_team = 6; - } else + } + else kicking_team = kick_bots_team; if (kick_bots_team > 7) @@ -573,10 +580,9 @@ void StartFrame(void) { for (i = 0; i < 32; i++) { if (bots[i].bIsUsed) // is this slot used? { - char cmd[80]; - if (bots[i].iTeam == kicking_team) { - sprintf(cmd, "kick \"%s\"\n", bots[i].name); + char cmd[80]; + snprintf(cmd, sizeof(cmd), "kick \"%s\"\n", bots[i].name); SERVER_COMMAND(cmd); // kick the bot using (kick "name") break; } @@ -585,7 +591,8 @@ void StartFrame(void) { } // I kick_amount_bots--; // next frame we kick another bot - } else { + } + else { kick_bots_team = 0; // its always 0 when we have no one to kick } @@ -593,19 +600,21 @@ void StartFrame(void) { // which is determined by comparing the previously recorded time (at the end of this function) // with the current time. If the current time somehow was less (before) the previous time, then we // assume a reset/restart/reload of a map. - if ((gpGlobals->time + 0.1) < previous_time) { + if (gpGlobals->time + 0.1f < previous_time) { + static int index; + static float check_server_cmd = 0.0f; rblog("NEW MAP because time is reset #1\n"); - check_server_cmd = 0.0; // reset at start of map + check_server_cmd = 0.0f; // reset at start of map count = 0; // mark the bots as needing to be respawned... for (index = 0; index < 32; index++) { - cBot *pBot = &bots[index]; + cBot* pBot = &bots[index]; if (count >= prev_num_bots) { pBot->bIsUsed = false; pBot->respawn_state = RESPAWN_NONE; - pBot->fKickTime = 0.0; + pBot->fKickTime = 0.0f; } if (pBot->bIsUsed) // is this slot used? @@ -615,23 +624,23 @@ void StartFrame(void) { } // check for any bots that were very recently kicked... - if ((pBot->fKickTime + 5.0) > previous_time) { + if (pBot->fKickTime + 5.0f > previous_time) { pBot->respawn_state = RESPAWN_NEED_TO_RESPAWN; count++; } else { - pBot->fKickTime = 0.0; // reset to prevent false spawns later + pBot->fKickTime = 0.0f; // reset to prevent false spawns later } // set the respawn time if (IS_DEDICATED_SERVER()) { - respawn_time = gpGlobals->time + 5.0; + respawn_time = gpGlobals->time + 5.0f; } else { - respawn_time = gpGlobals->time + 20.0; + respawn_time = gpGlobals->time + 20.0f; } // Send welcome message welcome_sent = false; - welcome_time = 0.0; + welcome_time = 0.0f; } NodeMachine.init_players(); @@ -640,9 +649,9 @@ void StartFrame(void) { NodeMachine.resetCheckedValuesForGoals(); //ChatEngine.fThinkTimer = gpGlobals->time; - client_update_time = gpGlobals->time + 10.0; // start updating client data again + client_update_time = gpGlobals->time + 10.0f; // start updating client data again - bot_check_time = gpGlobals->time + 30.0; + bot_check_time = gpGlobals->time + 30.0f; } // New map/reload/restart // @@ -650,24 +659,24 @@ void StartFrame(void) { // if (!welcome_sent) { int iIndex = 0; - if (welcome_time == 0.0) { + if (welcome_time == 0.0f) { // go through all clients (except bots) for (iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { - edict_t *pPlayer = INDEXENT(iIndex); + edict_t* p_player = INDEXENT(iIndex); // skip invalid players - if ((pPlayer) && (!pPlayer->free)) { + if (p_player && !p_player->free) { // we found a player which is alive. w00t - welcome_time = gpGlobals->time + 10.0; + welcome_time = gpGlobals->time + 10.0f; break; } } } - if ((welcome_time > 0.0) && (welcome_time < gpGlobals->time)) { + if (welcome_time > 0.0f && welcome_time < gpGlobals->time) { // let's send a welcome message to this client... char total_welcome[256]; - sprintf(total_welcome, "RealBot - Version %s\nBy Stefan Hendriks\n", rb_version_nr); + snprintf(total_welcome, sizeof(total_welcome), "RealBot - Version %s\nBy Stefan Hendriks\n", rb_version_nr); int r, g, b; /* r = RANDOM_LONG(30, 255); @@ -680,41 +689,41 @@ void StartFrame(void) { */ for (iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { - edict_t *pPlayer = INDEXENT(iIndex); + edict_t* p_player = INDEXENT(iIndex); // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free)) { + if (p_player && !p_player->free) { // skip bots! - if (UTIL_GetBotPointer(pPlayer)) + if (UTIL_GetBotPointer(p_player)) continue; // skip fake clients - if ((pPlayer->v.flags & FL_THIRDPARTYBOT) - || (pPlayer->v.flags & FL_FAKECLIENT)) + if (p_player->v.flags & FL_THIRDPARTYBOT + || p_player->v.flags & FL_FAKECLIENT) continue; // random color r = RANDOM_LONG(30, 255); g = RANDOM_LONG(30, 255); b = RANDOM_LONG(30, 255); - HUD_DrawString(r, g, b, total_welcome, pPlayer); + HUD_DrawString(r, g, b, total_welcome, p_player); // use speak command - if (pPlayer == pHostEdict) + if (p_player == pHostEdict) UTIL_SpeechSynth(pHostEdict, Game.RandomSentence()); } } welcome_sent = true; // clear this so we only do it once - welcome_time = 0.0; + welcome_time = 0.0f; } } if (client_update_time <= gpGlobals->time) { - client_update_time = gpGlobals->time + 1.0; + client_update_time = gpGlobals->time + 1.0f; for (i = 0; i < 32; i++) { if (bots[i].bIsUsed) { - memset(&cd, 0, sizeof(cd)); + std::memset(&cd, 0, sizeof(cd)); MDLL_UpdateClientData(bots[i].pEdict, 1, &cd); @@ -727,8 +736,8 @@ void StartFrame(void) { } // a few seconds after map load we assign goals - if (f_load_time < gpGlobals->time && f_load_time != 0.0) { - f_load_time = 0.0; // do not load again + if (f_load_time < gpGlobals->time && f_load_time != 0.0f) { + f_load_time = 0.0f; // do not load again rblog("NEW MAP because time is reset #2\n"); Game.DetermineMapGoal(); Game.resetRoundTime(); @@ -741,7 +750,7 @@ void StartFrame(void) { // Send a message to clients about RealBot every round if (Game.iVersionBroadcasting == BROADCAST_ROUND) { welcome_sent = false; - welcome_time = gpGlobals->time + 2; + welcome_time = gpGlobals->time + 2.0f; } NodeMachine.init_round(); @@ -757,15 +766,15 @@ void StartFrame(void) { int iHumans = 0, iBots = 0; // Search for human players, simple method... - for (int i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); + for (int j = 1; j <= gpGlobals->maxClients; j++) { + edict_t* p_player = INDEXENT(j); // skip invalid players and skip self (i.e. this bot) - if ((pPlayer) && (!pPlayer->free)) { + if (p_player && !p_player->free) { // a bot - if (UTIL_GetBotPointer(pPlayer) != NULL) + if (UTIL_GetBotPointer(p_player) != nullptr) iBots++; else { - if (pPlayer->v.flags & FL_CLIENT) + if (p_player->v.flags & FL_CLIENT) iHumans++; // it is 'human' (well unless some idiot uses another bot, i cannot detect that!) } } @@ -783,11 +792,11 @@ void StartFrame(void) { SERVER_PRINT("RBSERVER: Too many players, kicking one bot.\n"); kick_amount_bots = 1; } - // total players is lower then min_players due BOTS, add a bot + // total players is lower then min_players due BOTS, add a bot else if (iTotal < min_players) { // add a bot SERVER_PRINT("RBSERVER: Too few player slots filled, adding one bot.\n"); - Game.createBot(NULL, NULL, NULL, NULL, NULL); + Game.createBot(nullptr, nullptr, nullptr, nullptr, nullptr); } } else { if (num_bots > 0) { @@ -796,11 +805,11 @@ void StartFrame(void) { } } // 2 seconds thinking - f_minplayers_think = gpGlobals->time + 2; + f_minplayers_think = gpGlobals->time + 2.0f; } // INTERNET MODE: if (internet_play == false) - add_timer = gpGlobals->time + 2.0; + add_timer = gpGlobals->time + 2.0f; else // ------------ INTERNET MODE IS ON ------------ { bool max_serverbots = true; // Reached MAX bots? @@ -822,8 +831,8 @@ void StartFrame(void) { if (max_serverbots == false) { if (add_timer < gpGlobals->time) { add_timer = - gpGlobals->time + RANDOM_LONG(internet_min_interval, - internet_max_interval); + gpGlobals->time + RANDOM_LONG(internet_min_interval, + internet_max_interval); internet_addbot = true; // add a bot! w00t } } @@ -831,10 +840,10 @@ void StartFrame(void) { if (internet_addbot) { // When timer is set, create a new bot. - if (add_timer > gpGlobals->time && internet_addbot) { + if (add_timer > gpGlobals->time) { internet_addbot = false; - Game.createBot(NULL, NULL, NULL, NULL, NULL); - bot_check_time = gpGlobals->time + 5.0; + Game.createBot(nullptr, nullptr, nullptr, nullptr, nullptr); + bot_check_time = gpGlobals->time + 8.0f; } } @@ -850,9 +859,9 @@ void StartFrame(void) { Game.UpdateGameStatus(); for (bot_index = 0; bot_index < gpGlobals->maxClients; bot_index++) { - cBot &bot = bots[bot_index]; - if ((bot.bIsUsed) && // is this slot used AND - (bot.respawn_state == RESPAWN_IDLE)) // not respawning + cBot& bot = bots[bot_index]; + if (bot.bIsUsed && // is this slot used AND + bot.respawn_state == RESPAWN_IDLE) // not respawning { if (bot.fUpdateTime < gpGlobals->time) { BotThink(&bot); @@ -868,8 +877,7 @@ void StartFrame(void) { ChatEngine.think(); // Keep number of bots up-to-date. - if (count > num_bots) - num_bots = count; + num_bots = std::max(count, num_bots); // handle radio commands if (radio_message) { @@ -879,7 +887,7 @@ void StartFrame(void) { // FIX: Suggested by Greg, unnescesary loops. if (draw_nodes) { for (player_index = 1; player_index <= gpGlobals->maxClients; - player_index++) { + player_index++) { pPlayer = INDEXENT(player_index); if (pPlayer && !pPlayer->free) { @@ -893,7 +901,7 @@ void StartFrame(void) { if (draw_connodes) { for (player_index = 1; player_index <= gpGlobals->maxClients; - player_index++) { + player_index++) { pPlayer = INDEXENT(player_index); if (pPlayer && !pPlayer->free) { @@ -921,11 +929,11 @@ void StartFrame(void) { } // are we currently respawning bots and is it time to spawn one yet? - if ((respawn_time > 1.0) && (respawn_time <= gpGlobals->time)) { + if (respawn_time > 1.0f && respawn_time <= gpGlobals->time) { int index = 0; // find bot needing to be respawned... - while ((index < 32) - && (bots[index].respawn_state != RESPAWN_NEED_TO_RESPAWN)) + while (index < 32 + && bots[index].respawn_state != RESPAWN_NEED_TO_RESPAWN) index++; if (index < 32) { @@ -934,69 +942,71 @@ void StartFrame(void) { // respawn 1 bot then wait a while (otherwise engine crashes) // 01/07/04 - Stefan - Thanks Evy for pointing out this one: On skill 10 - // the c_skill (was [2]) would be messed up (perhaps this is some memory related bug later on?) + // the c_skill (was [2]) would be messed up (perhaps this is some memory related bug later on?) char c_skill[3]; char c_team[2]; char c_class[3]; - sprintf(c_skill, "%d", bots[index].bot_skill); - sprintf(c_team, "%d", bots[index].iTeam); - sprintf(c_class, "%d", bots[index].bot_class); + snprintf(c_skill, sizeof(c_skill), "%d", bots[index].bot_skill); + snprintf(c_team, sizeof(c_team), "%d", bots[index].iTeam); + snprintf(c_class, sizeof(c_class), "%d", bots[index].bot_class); - Game.createBot(NULL, c_team, c_skill, c_class, - bots[index].name); + Game.createBot(nullptr, c_team, c_skill, c_class, + bots[index].name); // 01/07/04 - Stefan - make 100% sure we do not crash on this part with the auto-add function f_minplayers_think = gpGlobals->time + 15; // do not check this for 15 seconds from now - respawn_time = gpGlobals->time + 2.0; // set next respawn time - bot_check_time = gpGlobals->time + 5.0; + // Added more delay to prevent possible "Tried to write to uninitialized sizebuf_t" crashes - [APG]RoboCop[CL] + respawn_time = gpGlobals->time + 7.0f; // set next respawn time + bot_check_time = gpGlobals->time + 8.0f; } else { - respawn_time = 0.0; + respawn_time = 0.0f; } } if (g_GameRules) { if (need_to_open_cfg) // have we open bot.cfg file yet? { + char msg[256]; char filename[256]; - need_to_open_cfg = FALSE; // only do this once!!! + need_to_open_cfg = false; // only do this once!!! UTIL_BuildFileNameRB("bot.cfg", filename); - sprintf(msg, "Executing %s\n", filename); + snprintf(msg, sizeof(msg), "Executing %s\n", filename); ALERT(at_console, msg); - bot_cfg_fp = fopen(filename, "r"); + bot_cfg_fp = std::fopen(filename, "r"); - if (bot_cfg_fp == NULL) + if (bot_cfg_fp == nullptr) ALERT(at_console, "bot.cfg file not found\n"); if (IS_DEDICATED_SERVER()) - bot_cfg_pause_time = gpGlobals->time + 5.0; + bot_cfg_pause_time = gpGlobals->time + 5.0f; else - bot_cfg_pause_time = gpGlobals->time + 20.0; + bot_cfg_pause_time = gpGlobals->time + 20.0f; } if (!IS_DEDICATED_SERVER() && !spawn_time_reset) { - if (pHostEdict != NULL) { + if (pHostEdict != nullptr) { if (IsAlive(pHostEdict)) { - spawn_time_reset = TRUE; + spawn_time_reset = true; - if (respawn_time >= 1.0) - respawn_time = min(respawn_time, gpGlobals->time + (float) 1.0); + if (respawn_time >= 1.0f) + respawn_time = std::min(respawn_time, gpGlobals->time + 1.0f); - if (bot_cfg_pause_time >= 1.0) + if (bot_cfg_pause_time >= 1.0f) bot_cfg_pause_time = - min(bot_cfg_pause_time, - gpGlobals->time + (float) 1.0); + std::min(bot_cfg_pause_time, + gpGlobals->time + 1.0f); } } } // DO BOT.CFG crap here - if ((bot_cfg_fp) && (bot_cfg_pause_time >= 1.0) - && (bot_cfg_pause_time <= gpGlobals->time)) { + if (bot_cfg_fp && bot_cfg_pause_time >= 1.0f + && bot_cfg_pause_time <= gpGlobals->time) { // process bot.cfg file options... ProcessBotCfgFile(); } @@ -1006,187 +1016,188 @@ void StartFrame(void) { // remember the time previous_time = gpGlobals->time; -// REALBOT_PRINT("StartFrame", "END"); + // REALBOT_PRINT("StartFrame", "END"); RETURN_META(MRES_IGNORED); } -void FakeClientCommand(edict_t *pBot, char *arg1, char *arg2, char *arg3) { +void FakeClientCommand(edict_t* pBot, const char* arg1, const char* arg2, const char* arg3) { int length; - memset(g_argv, 0, sizeof(g_argv)); + std::memset(g_argv, 0, sizeof(g_argv)); - isFakeClientCommand = TRUE; + isFakeClientCommand = true; - if ((arg1 == NULL) || (*arg1 == 0)) + if (arg1 == nullptr || *arg1 == 0) return; - if ((arg2 == NULL) || (*arg2 == 0)) { - length = sprintf(&g_argv[0], "%s", arg1); + if (arg2 == nullptr || *arg2 == 0) { + length = snprintf(&g_argv[0], sizeof(g_argv), "%s", arg1); fake_arg_count = 1; - } else if ((arg3 == NULL) || (*arg3 == 0)) { - length = sprintf(&g_argv[0], "%s %s", arg1, arg2); + } + else if (arg3 == nullptr || *arg3 == 0) { + length = snprintf(&g_argv[0], sizeof(g_argv), "%s %s", arg1, arg2); fake_arg_count = 2; - } else { - length = sprintf(&g_argv[0], "%s %s %s", arg1, arg2, arg3); + } + else { + length = snprintf(&g_argv[0], sizeof(g_argv), "%s %s %s", arg1, arg2, arg3); fake_arg_count = 3; } g_argv[length] = 0; // null terminate just in case - strcpy(&g_argv[64], arg1); + std::strcpy(&g_argv[64], arg1); if (arg2) - strcpy(&g_argv[128], arg2); + std::strcpy(&g_argv[128], arg2); if (arg3) - strcpy(&g_argv[192], arg3); + std::strcpy(&g_argv[192], arg3); // allow the MOD DLL to execute the ClientCommand... MDLL_ClientCommand(pBot); - isFakeClientCommand = FALSE; + isFakeClientCommand = false; } -void ProcessBotCfgFile(void) { - int ch; - char cmd_line[256]; - int cmd_index; - static char server_cmd[80]; - char *cmd, *arg1, *arg2, *arg3, *arg4; - char msg[80]; - - if (bot_cfg_pause_time > gpGlobals->time) - return; - - if (bot_cfg_fp == NULL) - return; - - cmd_index = 0; - cmd_line[cmd_index] = 0; - - ch = fgetc(bot_cfg_fp); - - // skip any leading blanks - while (ch == ' ') - ch = fgetc(bot_cfg_fp); +/*void UpdateClientData(const edict_s* ent, int sendweapons, clientdata_s* cd) // KWo 02.03.2010 - thanks to MeRcyLeZZ +{ + static int sending; + static int bot_index; + static edict_t* pPlayer; - while ((ch != EOF) && (ch != '\r') && (ch != '\n')) { - if (ch == '\t') // convert tabs to spaces - ch = ' '; + if (g_i_cv_latencybot != 2) + RETURN_META(MRES_IGNORED); - cmd_line[cmd_index] = ch; + sending = 0; - ch = fgetc(bot_cfg_fp); - - // skip multiple spaces in input file - while ((cmd_line[cmd_index] == ' ') && (ch == ' ')) - ch = fgetc(bot_cfg_fp); - - cmd_index++; - } - - if (ch == '\r') // is it a carriage return? + // Scoreboard key being pressed? + if (!FNullEnt(ent) && ent->v.flags & FL_CLIENT + && (ent->v.button & IN_SCORE || ent->v.oldbuttons & IN_SCORE)) { - ch = fgetc(bot_cfg_fp); // skip the linefeed - } - // if reached end of file, then close it - if (ch == EOF) { - fclose(bot_cfg_fp); - - bot_cfg_fp = NULL; + pPlayer = INDEXENT(ENTINDEX(ent)); - bot_cfg_pause_time = 0.0; + for (bot_index = 0; bot_index < gpGlobals->maxClients; bot_index++) + { + if (bots[bot_index].is_used && !FNullEnt(bots[bot_index].pEdict)) + { + // Send message with the weird arguments + switch (sending) + { + case 0: + { + // Start a new message + MESSAGE_BEGIN(MSG_ONE_UNRELIABLE, SVC_PINGS, NULL, pPlayer); + WRITE_BYTE(bots[bot_index].iOffsetPing[0] * 64 + (1 + 2 * bot_index)); + WRITE_SHORT(bots[bot_index].iArgPing[0]); + sending++; + } + case 1: + { + // Append additional data + WRITE_BYTE(bots[bot_index].iOffsetPing[1] * 128 + (2 + 4 * bot_index)); + WRITE_SHORT(bots[bot_index].iArgPing[1]); + sending++; + } + case 2: + { + // Append additional data and end message + WRITE_BYTE(4 + 8 * bot_index); + WRITE_SHORT(bots[bot_index].iArgPing[2]); + WRITE_BYTE(0); + MESSAGE_END(); + sending = 0; + } + } + } + } + // End message if not yet sent + if (sending) + { + WRITE_BYTE(0); + MESSAGE_END(); + } } + RETURN_META(MRES_IGNORED); +}*/ - cmd_line[cmd_index] = 0; // terminate the command line - - // copy the command line to a server command buffer... - strcpy(server_cmd, cmd_line); - strcat(server_cmd, "\n"); - - cmd_index = 0; - cmd = cmd_line; - arg1 = arg2 = arg3 = arg4 = NULL; - - // skip to blank or end of string... - while ((cmd_line[cmd_index] != ' ') && (cmd_line[cmd_index] != 0)) - cmd_index++; - - if (cmd_line[cmd_index] == ' ') { - cmd_line[cmd_index++] = 0; - arg1 = &cmd_line[cmd_index]; - - // skip to blank or end of string... - while ((cmd_line[cmd_index] != ' ') && (cmd_line[cmd_index] != 0)) - cmd_index++; +void UpdateClientData(const edict_s* ent, int sendweapons, clientdata_s* cd) //TODO: Needed implemented +{ +} - if (cmd_line[cmd_index] == ' ') { - cmd_line[cmd_index++] = 0; - arg2 = &cmd_line[cmd_index]; +void ProcessBotCfgFile() { + if (bot_cfg_pause_time > gpGlobals->time) + return; + if (bot_cfg_fp == nullptr) + return; - // skip to blank or end of string... - while ((cmd_line[cmd_index] != ' ') && (cmd_line[cmd_index] != 0)) - cmd_index++; + char line_buffer[256]; + if (fgets(line_buffer, sizeof(line_buffer), bot_cfg_fp) == nullptr) { + // End of file or error + std::fclose(bot_cfg_fp); + bot_cfg_fp = nullptr; + bot_cfg_pause_time = 8.0f; // wait 8 seconds before starting + return; + } - if (cmd_line[cmd_index] == ' ') { - cmd_line[cmd_index++] = 0; - arg3 = &cmd_line[cmd_index]; + std::string cmd_line(line_buffer); - // skip to blank or end of string... - while ((cmd_line[cmd_index] != ' ') - && (cmd_line[cmd_index] != 0)) - cmd_index++; + // Trim leading and trailing whitespace, and remove carriage returns + cmd_line.erase(0, cmd_line.find_first_not_of(" \t\n\r")); + cmd_line.erase(cmd_line.find_last_not_of(" \t\n\r") + 1); - if (cmd_line[cmd_index] == ' ') { - cmd_line[cmd_index++] = 0; - arg4 = &cmd_line[cmd_index]; - } - } - } + if (cmd_line.empty() || cmd_line[0] == '#') { + return; // return if comment or blank line } - if ((cmd_line[0] == '#') || (cmd_line[0] == 0)) - return; // return if comment or blank line + std::stringstream ss(cmd_line); + std::string command; + ss >> command; - if (strcmp(cmd, "pause") == 0) { - bot_cfg_pause_time = gpGlobals->time + atoi(arg1); + if (command == "pause") { + float pause_duration = 0.0f; + if (ss >> pause_duration) { + bot_cfg_pause_time = gpGlobals->time + pause_duration; + } return; } + // 07/02/04 - This gives a user theoreticly the power as in the console // use 'realbot addbot' to add a bot, etc. I dont think we need more // it would double the work. - sprintf(msg, "BOT.CFG >> Executing command: %s", server_cmd); // removed \n + char msg[256]; + snprintf(msg, sizeof(msg), "BOT.CFG >> Executing command: %s", cmd_line.c_str()); ALERT(at_console, msg); - if (IS_DEDICATED_SERVER()) - printf(msg); + if (IS_DEDICATED_SERVER()) { + printf("%s\n", msg); + } - SERVER_COMMAND(server_cmd); + std::string server_cmd = cmd_line + "\n"; + SERVER_COMMAND(server_cmd.c_str()); } // REALBOT COMMAND // SERVER Command: RealBot -void RealBot_ServerCommand(void) { - const char *pcmd = CMD_ARGV(1); - const char *arg1 = CMD_ARGV(2); - const char *arg2 = CMD_ARGV(3); - const char *arg3 = CMD_ARGV(4); - const char *arg4 = CMD_ARGV(5); +void RealBot_ServerCommand() { + const char* pcmd = CMD_ARGV(1); + const char* arg1 = CMD_ARGV(2); + const char* arg2 = CMD_ARGV(3); + const char* arg3 = CMD_ARGV(4); + const char* arg4 = CMD_ARGV(5); char cMessage[256]; bool bSendMessage = true; - bool bValidCommand = true; // When we need an edict to send messages to, we do that to the pHostEdict - edict_t *pEntity = pHostEdict; + edict_t* pEntity = pHostEdict; // Handle command here if (FStrEq(pcmd, "help")) { // Give information bSendMessage = false; // do not use 'standard' stuff - SERVER_PRINT("=============================================================================\n"); + SERVER_PRINT("=====================================\n"); SERVER_PRINT("Syntax: realbot [command] [arg1/subcommand] [arg2] [arg3] [arg4]\n\n"); SERVER_PRINT("List of most-used commands; for full command list read the readme.\n\n"); SERVER_PRINT("realbot add (team) (skill) (model) (name)\n"); @@ -1196,42 +1207,41 @@ void RealBot_ServerCommand(void) { SERVER_PRINT("realbot remove (amount) ((optional) of team 1(T)/2(CT))\n"); SERVER_PRINT("realbot skill (-1(random), 0(godlike)-10(newbie)\n"); SERVER_PRINT("realbot server [subcommand]\n"); - SERVER_PRINT("=============================================================================\n"); + SERVER_PRINT("=====================================\n"); } else if (FStrEq(pcmd, "chatrate")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - Game.iMaxSentences = atoi(arg1); - if (Game.iMaxSentences < -1) - Game.iMaxSentences = -1; + if (arg1 != nullptr && *arg1 != 0) { + Game.iMaxSentences = std::atoi(arg1); + Game.iMaxSentences = std::max(Game.iMaxSentences, -1); - if (Game.iMaxSentences > 10) - Game.iMaxSentences = 10; + Game.iMaxSentences = std::min(Game.iMaxSentences, 10); - sprintf(cMessage, "REALBOT: Chat-rate set to %d", - Game.iMaxSentences); - } else { - sprintf(cMessage, - "REALBOT: No argument given, current chat-rate is %d", - Game.iMaxSentences); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Chat-rate set to %d", + Game.iMaxSentences); + } + else { + snprintf(cMessage, sizeof(cMessage), + "REALBOT: No argument given, current chat-rate is %d", + Game.iMaxSentences); } } else if (FStrEq(pcmd, "sound")) { - EMIT_SOUND_DYN2(pEntity, CHAN_VOICE, "misc/imgood12.wav", 1.0, - ATTN_NORM, 0, 100); + EMIT_SOUND_DYN2(pEntity, CHAN_VOICE, "misc/imgood12.wav", 1.0f, + ATTN_NORM, 0, 100); } else if (FStrEq(pcmd, "add")) { - int iStatus = Game.createBot(pEntity, arg1, arg2, arg3, arg4); - bot_check_time = gpGlobals->time + 5.0; + const int iStatus = Game.createBot(pEntity, arg1, arg2, arg3, arg4); + bot_check_time = gpGlobals->time + 8.0f; if (iStatus == GAME_MSG_SUCCESS) - sprintf(cMessage, "REALBOT: Successfully created bot."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Successfully created bot."); else if (iStatus == GAME_MSG_FAILURE) - sprintf(cMessage, "REALBOT: Failed creating bot."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Failed creating bot."); else if (iStatus == GAME_MSG_FAIL_SERVERFULL) - sprintf(cMessage, - "REALBOT: Failed creating bot, server is full."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Failed creating bot, server is full."); } else if (FStrEq(pcmd, "walkwithknife")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - float fVar = atof(arg1); + if (arg1 != nullptr && *arg1 != 0) { + const float fVar = static_cast(std::atof(arg1)); // Only set when valid if (fVar < 0) @@ -1242,142 +1252,138 @@ void RealBot_ServerCommand(void) { // Show amount set if (Game.fWalkWithKnife > 0) - sprintf(cMessage, - "REALBOT: Bots may walk with knife for %f seconds.", - Game.fWalkWithKnife); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Bots may walk with knife for %f seconds.", + Game.fWalkWithKnife); else - sprintf(cMessage, - "REALBOT: Bots may not walk with knife (value=0)"); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Bots may not walk with knife (value=0)"); } else - sprintf(cMessage, "REALBOT: No valid argument given."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: No valid argument given."); } else if (FStrEq(pcmd, "max")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - max_bots = atoi(arg1); - if ((max_bots < 0) || (max_bots > 31)) + if (arg1 != nullptr && *arg1 != 0) { + max_bots = std::atoi(arg1); + if (max_bots < 0 || max_bots > 31) max_bots = -1; // Show amount set - sprintf(cMessage, "REALBOT: Max amount of bots is set to %d.", - max_bots); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Max amount of bots is set to %d.", + max_bots); } else { // sprintf (cMessage, "REALBOT: No valid argument given."); - sprintf(cMessage, - "REALBOT: Max amount of bots is %d -- no valid argument given.", - max_bots); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Max amount of bots is %d -- no valid argument given.", + max_bots); } } else if (FStrEq(pcmd, "important")) { // Broadcast if (FStrEq(arg1, "add")) { - if (pHostEdict != NULL) { - NodeMachine.addGoal(NULL, GOAL_IMPORTANT, pHostEdict->v.origin); - sprintf(cMessage, "REALBOT: Added important area/goal."); + if (pHostEdict != nullptr) { + NodeMachine.addGoal(nullptr, GOAL_IMPORTANT, pHostEdict->v.origin); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Added important area/goal."); } else - sprintf(cMessage, "REALBOT: Only a listen server can execute this command!"); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Only a listen server can execute this command!"); } else if (FStrEq(arg1, "save")) { NodeMachine.save_important(); - sprintf(cMessage, - "REALBOT: Important Area Definitions written to INI file"); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Important Area Definitions written to INI file"); } else if (FStrEq(arg1, "init")) { // clear all goals that are 'goal_important' NodeMachine.ClearImportantGoals(); - sprintf(cMessage, - "REALBOT: All important goals have been removed."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: All important goals have been removed."); } else - sprintf(cMessage, - "REALBOT: 'important' sub-commands are: add, save, init"); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: 'important' sub-commands are: add, save, init"); } else if (FStrEq(pcmd, "killall")) { - sprintf(cMessage, "REALBOT: Killing all bots."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Killing all bots."); end_round = true; } else if (FStrEq(pcmd, "csversion")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - int temp = atoi(arg1); + if (arg1 != nullptr && *arg1 != 0) { + const int temp = std::atoi(arg1); if (temp <= 0) counterstrike = 0; // cs 1.5 else counterstrike = 1; // cs 1.6 if (counterstrike == 0) - sprintf(cMessage, - "REALBOT: Set bot-rules for Counter-Strike 1.5."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Set bot-rules for Counter-Strike 1.5."); else - sprintf(cMessage, - "REALBOT: Set bot-rules for Counter-Strike 1.6."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Set bot-rules for Counter-Strike 1.6."); } else { if (counterstrike == 0) - sprintf(cMessage, - "REALBOT: bot-rules are set for Counter-Strike 1.5."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: bot-rules are set for Counter-Strike 1.5."); else - sprintf(cMessage, - "REALBOT: bot-rules are set for Counter-Strike 1.6."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: bot-rules are set for Counter-Strike 1.6."); } } else if (FStrEq(pcmd, "internet")) { - if ((arg1 != NULL) && (*arg1 != 0)) { + if (arg1 != nullptr && *arg1 != 0) { // switch on/off internet mode - int temp = atoi(arg1); + const int temp = std::atoi(arg1); if (temp == 0) internet_play = false; else internet_play = true; if (internet_play) - sprintf(cMessage, "REALBOT: Internet simulation - enabled."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Internet simulation - enabled."); else - sprintf(cMessage, "REALBOT: Internet simulation - disabled."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Internet simulation - disabled."); } else - sprintf(cMessage, "REALBOT: No valid argument given."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: No valid argument given."); } else if (FStrEq(pcmd, "internet_interval")) { // 1st argument - if ((arg1 != NULL) && (*arg1 != 0)) { + if (arg1 != nullptr && *arg1 != 0) { // switch on/off internet mode - int temp = atoi(arg1); + int temp = std::atoi(arg1); if (temp > -1) { - if (temp < 1) - temp = 1; + temp = std::max(temp, 1); internet_min_interval = temp; } } // 2nd argument - if ((arg2 != NULL) && (*arg2 != 0)) { + if (arg2 != nullptr && *arg2 != 0) { // switch on/off internet mode - int temp = atoi(arg2); + int temp = std::atoi(arg2); if (temp > -1) { - if (temp < internet_min_interval) - temp = internet_min_interval; + temp = std::max(temp, internet_min_interval); internet_max_interval = temp; } } // Create message - sprintf(cMessage, - "REALBOT: Internet simulation - Interval set to, MIN %d - MAX %d", - internet_min_interval, internet_max_interval); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Internet simulation - Interval set to, MIN %d - MAX %d", + internet_min_interval, internet_max_interval); } else if (FStrEq(pcmd, "remove") && kick_amount_bots == 0) { // ARG1 - Amount - if ((arg1 != NULL) && (*arg1 != 0)) { + if (arg1 != nullptr && *arg1 != 0) { // get amount - int temp = atoi(arg1); + const int temp = std::atoi(arg1); kick_amount_bots = temp; - if (kick_amount_bots > 31) - kick_amount_bots = 31; - if (kick_amount_bots < 0) - kick_amount_bots = 0; + kick_amount_bots = std::min(kick_amount_bots, 31); + kick_amount_bots = std::max(kick_amount_bots, 0); } // ARG 2 - Team - if ((arg2 != NULL) && (*arg2 != 0)) { + if (arg2 != nullptr && *arg2 != 0) { // get team int team = 0; - int temp = atoi(arg2); + const int temp = std::atoi(arg2); if (temp == 1) team = 1; if (temp == 2) @@ -1385,78 +1391,70 @@ void RealBot_ServerCommand(void) { kick_bots_team = team; } if (kick_bots_team < 1) - sprintf(cMessage, "REALBOT: Removing randomly %d bots.", - kick_amount_bots); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Removing randomly %d bots.", + kick_amount_bots); else { if (kick_bots_team == 1) - sprintf(cMessage, "REALBOT: Removing %d terrorist bots.", - kick_amount_bots); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Removing %d terrorist bots.", + kick_amount_bots); else - sprintf(cMessage, - "REALBOT: Removing %d counter-terrorist bots.", - kick_amount_bots); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Removing %d counter-terrorist bots.", + kick_amount_bots); } - } else if (FStrEq(pcmd, "roundlimit")) { - + } + else if (FStrEq(pcmd, "roundlimit")) { // Check if Arguments are valid - if (((arg2 != NULL) && (*arg2 != 0)) - && ((arg1 != NULL) && (*arg1 != 0))) { - int s1 = -1, s2 = -1; - + if (arg2 != nullptr && *arg2 != 0 && arg1 != nullptr && *arg1 != 0) { // argument 1 - if ((arg1 != NULL) && (*arg1 != 0)) - s1 = atoi(arg1); - + const int s1 = std::atoi(arg1); // argument 2 - if ((arg2 != NULL) && (*arg2 != 0)) - s2 = atoi(arg2); + const int s2 = std::atoi(arg2); Game.SetPlayingRounds(s1, s2); - sprintf(cMessage, - "REALBOT: Bots play at minimum %d and at maximum %d rounds.\n", - Game.GetMinPlayRounds(), Game.GetMaxPlayRounds()); - } else - sprintf(cMessage, - "REALBOT: No(t) (enough) valid arguments given."); - } else if (FStrEq(pcmd, "setrandom")) { + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Bots play at minimum %d and at maximum %d rounds.\n", + Game.GetMinPlayRounds(), Game.GetMaxPlayRounds()); + } + else { + snprintf(cMessage, sizeof(cMessage), + "REALBOT: No(t) (enough) valid arguments given."); + } + } + else if (FStrEq(pcmd, "setrandom")) { int s1 = -2, s2 = -2; // argument 1 - if ((arg1 != NULL) && (*arg1 != 0)) - s1 = atoi(arg1); + if (arg1 != nullptr && *arg1 != 0) + s1 = std::atoi(arg1); // argument 2 - if ((arg2 != NULL) && (*arg2 != 0)) - s2 = atoi(arg2); + if (arg2 != nullptr && *arg2 != 0) + s2 = std::atoi(arg2); // When first argument is invalid if (s1 < -1) { - sprintf(cMessage, - "REALBOT: No valid argument(s) given. (minimum random skill=%d, maximum random skill=%d).", - Game.iRandomMinSkill, Game.iRandomMaxSkill); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: No valid argument(s) given. (minimum random skill=%d, maximum random skill=%d).", + Game.iRandomMinSkill, Game.iRandomMaxSkill); } else { if (s2 < -1) s2 = Game.iRandomMaxSkill; - if (s1 > 10) - s1 = 10; - if (s1 < 0) - s1 = 0; - if (s2 > 10) - s2 = 10; - if (s2 < 0) - s2 = 0; - if (s2 < s1) // be sure s1 is lower then s2 or atleast the same - s2 = s1; + s1 = std::min(s1, 10); + s1 = std::max(s1, 0); + s2 = std::min(s2, 10); + s2 = std::max(s2, 0); + s2 = std::max(s2, s1); Game.iRandomMinSkill = s1; Game.iRandomMaxSkill = s2; - sprintf(cMessage, - "REALBOT: minimum random skill=%d, maximum random skill=%d.", - Game.iRandomMinSkill, Game.iRandomMaxSkill); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: minimum random skill=%d, maximum random skill=%d.", + Game.iRandomMinSkill, Game.iRandomMaxSkill); } } else if (FStrEq(pcmd, "autoskill")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - int temp = atoi(arg1); + if (arg1 != nullptr && *arg1 != 0) { + const int temp = std::atoi(arg1); if (temp == 1) autoskill = true; @@ -1464,13 +1462,13 @@ void RealBot_ServerCommand(void) { autoskill = false; } if (autoskill) - sprintf(cMessage, "REALBOT: Auto adjust skill - enabled."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Auto adjust skill - enabled."); else - sprintf(cMessage, "REALBOT: Auto adjust skill - disabled."); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Auto adjust skill - disabled."); } else if (FStrEq(pcmd, "override_skill")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - int temp = atoi(arg1); + if (arg1 != nullptr && *arg1 != 0) { + const int temp = std::atoi(arg1); if (temp == 1) Game.iOverrideBotSkill = GAME_YES; @@ -1478,90 +1476,84 @@ void RealBot_ServerCommand(void) { Game.iOverrideBotSkill = GAME_NO; } if (Game.iOverrideBotSkill == GAME_YES) - sprintf(cMessage, - "REALBOT: Using personality skill (if present) instead of default bot skill."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Using personality skill (if present) instead of default bot skill."); else - sprintf(cMessage, - "REALBOT: Using default bot skill at all times."); + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Using default bot skill at all times."); } else if (FStrEq(pcmd, "skill")) { - if ((arg1 != NULL) && (*arg1 != 0)) { - int temp = atoi(arg1); - if ((temp < -1) || (temp > 10)) { - sprintf(cMessage, - "REALBOT: Invalid argument given - default skill = %d.", - Game.iDefaultBotSkill); + if (arg1 != nullptr && *arg1 != 0) { + const int temp = std::atoi(arg1); + if (temp < -1 || temp > 10) { + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Invalid argument given - default skill = %d.", + Game.iDefaultBotSkill); } else { Game.iDefaultBotSkill = temp; - sprintf(cMessage, "REALBOT: Default skill = %d", - Game.iDefaultBotSkill); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Default skill = %d", + Game.iDefaultBotSkill); } } else { - sprintf(cMessage, "REALBOT: Default skill = %d", - Game.iDefaultBotSkill); + snprintf(cMessage, sizeof(cMessage), "REALBOT: Default skill = %d", + Game.iDefaultBotSkill); } } - // ----------------------------------------- - // SERVER SUPPORT - // ----------------------------------------- + // ----------------------------------------- + // SERVER SUPPORT + // ----------------------------------------- else if (FStrEq(pcmd, "server")) { // Minimum amount of playing players... bots or not if (FStrEq(arg1, "players")) { - if ((arg2 != NULL) && (*arg2 != 0)) { - int temp = atoi(arg2); // argument - if (temp > 31) - temp = 31; - if (temp < -1) - temp = -1; + if (arg2 != nullptr && *arg2 != 0) { + int temp = std::atoi(arg2); // argument + temp = std::min(temp, 31); + temp = std::max(temp, -1); min_players = temp; f_minplayers_think = gpGlobals->time; } - sprintf(cMessage, "RBSERVER: Minimum playing forced to %d.", - min_players); + snprintf(cMessage, sizeof(cMessage), "RBSERVER: Minimum playing forced to %d.", + min_players); } - // Broadcast + // Broadcast else if (FStrEq(arg1, "broadcast")) { // Broadcast what? if (FStrEq(arg2, "version")) { // How do we broadcast version message? - if ((arg3 != NULL) && (*arg3 != 0)) { - int temp = atoi(arg3); // argument - if (temp <= 0) - temp = 0; - if (temp >= 1) - temp = 1; + if (arg3 != nullptr && *arg3 != 0) { + int temp = std::atoi(arg3); // argument + temp = std::max(temp, 0); + temp = std::min(temp, 1); Game.iVersionBroadcasting = temp; if (Game.iVersionBroadcasting == BROADCAST_ROUND) - sprintf(cMessage, - "RBSERVER: Broadcasting RealBot version every round and map change.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting RealBot version every round and map change.\n"); else - sprintf(cMessage, - "RBSERVER: Broadcasting RealBot version every map change only.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting RealBot version every map change only.\n"); } } else if (FStrEq(arg2, "speech")) { - if (arg2 != NULL) { - if (atoi(arg2) == 1) + if (arg2 != nullptr) { + if (std::atoi(arg2) == 1) Game.bSpeechBroadcasting = true; else Game.bSpeechBroadcasting = false; } if (Game.bSpeechBroadcasting) - sprintf(cMessage, "RBSERVER: Broadcasting speech is ON"); + snprintf(cMessage, sizeof(cMessage), "RBSERVER: Broadcasting speech is ON"); else - sprintf(cMessage, "RBSERVER: Broadcasting speech is OFF"); + snprintf(cMessage, sizeof(cMessage), "RBSERVER: Broadcasting speech is OFF"); } else if (FStrEq(arg2, "kills")) { // How do we broadcast kills by bots? - if ((arg3 != NULL) && (*arg3 != 0)) { - int temp = atoi(arg3); // argument - if (temp <= 0) - temp = 0; - if (temp >= 2) - temp = 2; + if (arg3 != nullptr && *arg3 != 0) { + int temp = std::atoi(arg3); // argument + temp = std::max(temp, 0); + temp = std::min(temp, 2); if (temp == 0) Game.iKillsBroadcasting = BROADCAST_KILLS_FULL; if (temp == 1) @@ -1569,34 +1561,32 @@ void RealBot_ServerCommand(void) { if (temp == 2) Game.iKillsBroadcasting = BROADCAST_KILLS_NONE; if (Game.iKillsBroadcasting == BROADCAST_KILLS_FULL) - sprintf(cMessage, - "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); else if (Game.iKillsBroadcasting == BROADCAST_KILLS_MIN) - sprintf(cMessage, - "RBSERVER: Broadcasting name of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name of bot who killed human player.\n"); else - sprintf(cMessage, - "RBSERVER: Nothing will be sent to player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Nothing will be sent to player.\n"); } else { if (Game.iKillsBroadcasting == BROADCAST_KILLS_FULL) - sprintf(cMessage, - "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); else if (Game.iKillsBroadcasting == BROADCAST_KILLS_MIN) - sprintf(cMessage, - "RBSERVER: Broadcasting name of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name of bot who killed human player.\n"); else - sprintf(cMessage, - "RBSERVER: Nothing will be sent to player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Nothing will be sent to player.\n"); } } else if (FStrEq(arg2, "deaths")) { // How do we broadcast deaths by bots. - if ((arg3 != NULL) && (*arg3 != 0)) { - int temp = atoi(arg3); // argument - if (temp <= 0) - temp = 0; - if (temp >= 2) - temp = 2; + if (arg3 != nullptr && *arg3 != 0) { + int temp = std::atoi(arg3); // argument + temp = std::max(temp, 0); + temp = std::min(temp, 2); if (temp == 0) Game.iDeathsBroadcasting = BROADCAST_DEATHS_FULL; if (temp == 1) @@ -1604,40 +1594,40 @@ void RealBot_ServerCommand(void) { if (temp == 2) Game.iDeathsBroadcasting = BROADCAST_DEATHS_NONE; if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_FULL) - sprintf(cMessage, - "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); else if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_MIN) - sprintf(cMessage, - "RBSERVER: Broadcasting name of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name of bot who killed human player.\n"); else - sprintf(cMessage, - "RBSERVER: Nothing will be sent to player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Nothing will be sent to player.\n"); } else { if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_FULL) - sprintf(cMessage, - "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name and skill of bot who killed human player.\n"); else if (Game.iDeathsBroadcasting == BROADCAST_DEATHS_MIN) - sprintf(cMessage, - "RBSERVER: Broadcasting name of bot who killed human player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcasting name of bot who killed human player.\n"); else - sprintf(cMessage, - "RBSERVER: Nothing will be sent to player.\n"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Nothing will be sent to player.\n"); } } else { - sprintf(cMessage, - "RBSERVER: Broadcast what?\nversion\nspeech\nkills\ndeaths"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Broadcast what?\nversion\nspeech\nkills\ndeaths"); } } else - sprintf(cMessage, - "RBSERVER: Invalid sub-command.\nValid commands are:\nbroadcast (version/kill)\nplayers (keep ## player slots full)"); + snprintf(cMessage, sizeof(cMessage), + "RBSERVER: Invalid sub-command.\nValid commands are:\nbroadcast (version/kill)\nplayers (keep ## player slots full)"); } - // ----------------------------------------- - // NODE EDITOR COMMANDS - // ----------------------------------------- + // ----------------------------------------- + // NODE EDITOR COMMANDS + // ----------------------------------------- else if (FStrEq(pcmd, "nodes")) { int iOnNode = -1; - if (pHostEdict != NULL) { + if (pHostEdict != nullptr) { iOnNode = NodeMachine.getClosestNode(pHostEdict->v.origin, 35, pHostEdict); } @@ -1647,75 +1637,75 @@ void RealBot_ServerCommand(void) { bool bValidArg = false; // check for valid argument - if ((arg2 != NULL) && (*arg2 != 0)) { - int iTo = atoi(arg2); // add connection TO + if (arg2 != nullptr && *arg2 != 0) { + const int iTo = std::atoi(arg2); // add connection TO // Add this connection if (iTo > -1) { - bool bSuccess = - NodeMachine.add_neighbour_node(iOnNode, iTo); + const bool bSuccess = + NodeMachine.add_neighbour_node(iOnNode, iTo); if (bSuccess) - sprintf(cMessage, - "NODES EDITOR: Added connection from node %d to node %d.", - iOnNode, iTo); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Added connection from node %d to node %d.", + iOnNode, iTo); else - sprintf(cMessage, - "NODES EDITOR: Connection could not be added, max amount of connections reached or connection already exists."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Connection could not be added, max amount of connections reached or connection already exists."); bValidArg = true; } } if (bValidArg == false) - sprintf(cMessage, - "NODES EDITOR: Give argument to which node this connection is valid!"); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Give argument to which node this connection is valid!"); } else if (FStrEq(arg1, "removeto")) { // removes connection TO bool bValidArg = false; // check for valid argument - if ((arg2 != NULL) && (*arg2 != 0)) { - int iTo = atoi(arg2); // remove connection TO + if (arg2 != nullptr && *arg2 != 0) { + const int iTo = std::atoi(arg2); // remove connection TO // remove this connection if (iTo > -1) { - bool bSuccess = - NodeMachine.removeConnection(iOnNode, iTo); + const bool bSuccess = + NodeMachine.removeConnection(iOnNode, iTo); if (bSuccess) - sprintf(cMessage, - "NODES EDITOR: Removed connection from node %d to node %d.", - iOnNode, iTo); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Removed connection from node %d to node %d.", + iOnNode, iTo); else - sprintf(cMessage, - "NODES EDITOR: Could not remove connection, connection does not exist."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Could not remove connection, connection does not exist."); bValidArg = true; } } if (bValidArg == false) - sprintf(cMessage, - "NODES EDITOR: Give argument to which node this connection is valid!"); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Give argument to which node this connection is valid!"); } else if (FStrEq(arg1, "removeall")) { - bool bSuccess = NodeMachine.remove_neighbour_nodes(iOnNode); + const bool bSuccess = NodeMachine.remove_neighbour_nodes(iOnNode); if (bSuccess) - sprintf(cMessage, - "NODES EDITOR: Removed all connections from node %d.", - iOnNode); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Removed all connections from node %d.", + iOnNode); } else if (FStrEq(arg1, "draw")) { if (draw_nodes == false) draw_nodes = true; else draw_nodes = false; if (draw_nodes) - sprintf(cMessage, "NODES EDITOR: Drawing nodes - enabled."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Drawing nodes - enabled."); else - sprintf(cMessage, - "NODES EDITOR: Drawing nodes - disabled."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Drawing nodes - disabled."); } else if (FStrEq(arg1, "connections")) { if (draw_connodes == false) draw_connodes = true; @@ -1723,23 +1713,23 @@ void RealBot_ServerCommand(void) { draw_connodes = false; if (draw_connodes) - sprintf(cMessage, - "NODES EDITOR: Drawing nodes connections - enabled."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Drawing nodes connections - enabled."); else - sprintf(cMessage, - "NODES EDITOR: Drawing nodes connections - disabled."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Drawing nodes connections - disabled."); } else if (FStrEq(arg1, "init")) { NodeMachine.init(); - sprintf(cMessage, "NODES EDITOR: Nodes initialized."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Nodes initialized."); } else if (FStrEq(arg1, "save")) { NodeMachine.save(); - sprintf(cMessage, "NODES EDITOR: Nodes saved."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Nodes saved."); } else if (FStrEq(arg1, "load")) { NodeMachine.load(); - sprintf(cMessage, "NODES EDITOR: Nodes loaded."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Nodes loaded."); } else { - sprintf(cMessage, - "NODES EDITOR: Unknown command\n Valid commands are:\naddto,removeto,removeall,draw,connections,init,save,load."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Unknown command\n Valid commands are:\naddto,removeto,removeall,draw,connections,init,save,load."); } } else { @@ -1750,10 +1740,10 @@ void RealBot_ServerCommand(void) { else draw_nodes = false; if (draw_nodes) - sprintf(cMessage, "NODES EDITOR: Drawing nodes - enabled."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Drawing nodes - enabled."); else - sprintf(cMessage, - "NODES EDITOR: Drawing nodes - disabled."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Drawing nodes - disabled."); } else if (FStrEq(arg1, "connections")) { if (draw_connodes == false) draw_connodes = true; @@ -1761,23 +1751,23 @@ void RealBot_ServerCommand(void) { draw_connodes = false; if (draw_connodes) - sprintf(cMessage, - "NODES EDITOR: Drawing nodes connections - enabled."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Drawing nodes connections - enabled."); else - sprintf(cMessage, - "NODES EDITOR: Drawing nodes connections - disabled."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Drawing nodes connections - disabled."); } else if (FStrEq(arg1, "init")) { NodeMachine.init(); - sprintf(cMessage, "NODES EDITOR: Nodes initialized."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Nodes initialized."); } else if (FStrEq(arg1, "save")) { NodeMachine.save(); - sprintf(cMessage, "NODES EDITOR: Nodes saved."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Nodes saved."); } else if (FStrEq(arg1, "load")) { NodeMachine.load(); - sprintf(cMessage, "NODES EDITOR: Nodes loaded."); + snprintf(cMessage, sizeof(cMessage), "NODES EDITOR: Nodes loaded."); } else - sprintf(cMessage, - "NODES EDITOR: Not close enough to a node to edit."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Not close enough to a node to edit."); } // commands not needed for a node to be close // 17/07/04 @@ -1785,50 +1775,50 @@ void RealBot_ServerCommand(void) { // Don't care whether we are close to a node... if (FStrEq(arg1, "connect")) { // check for valid argument - if ((arg2 != NULL) && (*arg2 != 0) && (arg3 != NULL) - && (*arg3 != 0)) { - int Node1 = atoi(arg2); // add connection TO - int Node2 = atoi(arg3); // add connection TO - if ((Node1 >= 0) && (Node2 >= 0) + if (arg2 != nullptr && *arg2 != 0 && arg3 != nullptr + && *arg3 != 0) { + const int Node1 = std::atoi(arg2); // add connection TO + const int Node2 = std::atoi(arg3); // add connection TO + if (Node1 >= 0 && Node2 >= 0 && NodeMachine.add_neighbour_node(Node1, Node2)) - sprintf(cMessage, - "NODES EDITOR: Added connection from node %d to node %d.", - Node1, Node2); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Added connection from node %d to node %d.", + Node1, Node2); else - sprintf(cMessage, - "NODES EDITOR: Connection could not be added, max amount of connections reached or connection already exists."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Connection could not be added, max amount of connections reached or connection already exists."); } else - sprintf(cMessage, - "NODES EDITOR: this command requires TWO numeric arguments!"); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: this command requires TWO numeric arguments!"); } else if (FStrEq(arg1, "disconnect")) { // check for valid argument - if ((arg2 != NULL) && (*arg2 != 0) && (arg3 != NULL) - && (*arg3 != 0)) { - int Node1 = atoi(arg2); - int Node2 = atoi(arg3); - if ((Node1 >= 0) && (Node2 >= 0) + if (arg2 != nullptr && *arg2 != 0 && arg3 != nullptr + && *arg3 != 0) { + const int Node1 = std::atoi(arg2); + const int Node2 = std::atoi(arg3); + if (Node1 >= 0 && Node2 >= 0 && NodeMachine.removeConnection(Node1, Node2)) - sprintf(cMessage, - "NODES EDITOR: Removed connection from node %d to node %d.", - Node1, Node2); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Removed connection from node %d to node %d.", + Node1, Node2); else - sprintf(cMessage, - "NODES EDITOR: Connection could not be removed..."); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: Connection could not be removed..."); } else - sprintf(cMessage, - "NODES EDITOR: this command requires TWO numeric arguments!"); + snprintf(cMessage, sizeof(cMessage), + "NODES EDITOR: this command requires TWO numeric arguments!"); } } - // ----------------------------------------- - // DEBUG COMMANDS - // ----------------------------------------- + // ----------------------------------------- + // DEBUG COMMANDS + // ----------------------------------------- else if (FStrEq(pcmd, "debug")) { // Arg 1 is command: if (FStrEq(arg1, "dontshoot")) { // realbot debug dontshoot [1/0] // check for valid argument - if ((arg2 != NULL) && (*arg2 != 0)) { - int temp = atoi(arg2); + if (arg2 != nullptr && *arg2 != 0) { + const int temp = std::atoi(arg2); if (temp) Game.bDoNotShoot = true; else @@ -1836,14 +1826,14 @@ void RealBot_ServerCommand(void) { } if (Game.bDoNotShoot) - sprintf(cMessage, "RBDEBUG: Bots will not shoot."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Bots will not shoot."); else - sprintf(cMessage, "RBDEBUG: Bots will shoot."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Bots will shoot."); } - // 17/07/04 + // 17/07/04 else if (FStrEq(arg1, "pistols")) { // realbot debug pistols [1/0] - if ((arg2 != NULL) && (*arg2 != 0)) { - int temp = atoi(arg2); + if (arg2 != nullptr && *arg2 != 0) { + const int temp = std::atoi(arg2); if (temp) Game.bPistols = true; else @@ -1851,54 +1841,53 @@ void RealBot_ServerCommand(void) { } if (Game.bPistols) - sprintf(cMessage, "RBDEBUG: Bots will only use pistols."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Bots will only use pistols."); else - sprintf(cMessage, "RBDEBUG: Bots will use all weapons."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Bots will use all weapons."); } else if (FStrEq(arg1, "goals")) // Print all current goals { NodeMachine.dump_goals(); - strcpy(cMessage, "RBDEBUG: Dumping goals."); + std::strcpy(cMessage, "RBDEBUG: Dumping goals."); } else if (FStrEq(arg1, "bots")) // realbot debug bots // Print information about all current bots { - int iBot; rblog("Dumping information about all bots:\n"); - for (iBot = 0; (iBot < 32) && (bots[iBot].bIsUsed == true); iBot++) { + for (int iBot = 0; iBot < 32 && bots[iBot].bIsUsed == true; iBot++) { bots[iBot].Dump(); } NodeMachine.dump_goals(); - strcpy(cMessage, "RBDEBUG: Dumping bots' information."); + std::strcpy(cMessage, "RBDEBUG: Dumping bots' information."); } else if (FStrEq(arg1, "print")) { // realbot debug print (toggles, and last argument is bot index) if (Game.bDebug > -2) { Game.bDebug = -2; - sprintf(cMessage, "RBDEBUG: Debug messages off."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Debug messages off."); } else { - if ((arg2 != NULL) && (*arg2 != 0)) { - int temp = atoi(arg2); + if (arg2 != nullptr && *arg2 != 0) { + const int temp = std::atoi(arg2); Game.bDebug = temp; - sprintf(cMessage, "RBDEBUG: Debug messages on for bot [%d].", Game.bDebug); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Debug messages on for bot [%d].", Game.bDebug); } else { Game.bDebug = -1; - sprintf(cMessage, "RBDEBUG: Debug messages on for all bots", Game.bDebug); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Debug messages on for all bots"/*, Game.bDebug*/); } } } else if (FStrEq(arg1, "verbosity")) { // realbot verbosity if (FStrEq(arg2, "low")) { - sprintf(cMessage, "RBDEBUG: Message verbosity low."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Message verbosity low."); Game.messageVerbosity = 0; } else if (FStrEq(arg2, "normal")) { - sprintf(cMessage, "RBDEBUG: Message verbosity normal."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Message verbosity normal."); Game.messageVerbosity = 1; } else if (FStrEq(arg2, "trace")) { - sprintf(cMessage, "RBDEBUG: Message verbosity trace."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Message verbosity trace."); Game.messageVerbosity = 2; // extreme verbose } else { - sprintf(cMessage, "RBDEBUG: Usage: realbot debug verbosity [low][normal][trace]"); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Usage: realbot debug verbosity [low][normal][trace]"); } } else if (FStrEq(arg1, "nodes")) { // realbot debug nodes if (FStrEq(arg2, "dumpbmp")) { // realbot debug nodes dumpbmp - sprintf(cMessage, "RBDEBUG: Dumping Nodes information into bitmap file"); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Dumping Nodes information into bitmap file"); NodeMachine.Draw(); } else if (FStrEq(arg2, "draw")) { if (draw_nodes == false) @@ -1906,36 +1895,36 @@ void RealBot_ServerCommand(void) { else draw_nodes = false; if (draw_nodes) - sprintf(cMessage, "RBDEBUG: Drawing nodes - enabled."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Drawing nodes - enabled."); else - sprintf(cMessage, "RBDEBUG: Drawing nodes - disabled."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Drawing nodes - disabled."); } else if (FStrEq(arg2, "path")) { int iFrom = -1, iTo = -1; - if ((arg3 != NULL) && (*arg3 != 0)) - iFrom = atoi(arg3); - if ((arg4 != NULL) && (*arg4 != 0)) - iTo = atoi(arg4); + if (arg3 != nullptr && *arg3 != 0) + iFrom = std::atoi(arg3); + if (arg4 != nullptr && *arg4 != 0) + iTo = std::atoi(arg4); if (iFrom > -1) { if (iTo < 0) { - tGoal *ptr = NodeMachine.getRandomGoalByType(GOAL_SPAWNT); + const tGoal* ptr = NodeMachine.getRandomGoalByType(GOAL_SPAWNT); if (ptr) iTo = ptr->iNode; } - sprintf(cMessage, "RBDEBUG: Creating path from [%d] to [%d].", iFrom, iTo); - NodeMachine.createPath(iFrom, iTo, 0, NULL, PATH_DANGER); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Creating path from [%d] to [%d].", iFrom, iTo); + NodeMachine.createPath(iFrom, iTo, 0, nullptr, PATH_DANGER); } else { - sprintf(cMessage, "RBDEBUG: Usage: realbot debug nodes path "); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Usage: realbot debug nodes path "); } } else if (FStrEq(arg2, "drawpath")) { - if ((arg3 != NULL) && (*arg3 != 0)) - draw_nodepath = atoi(arg3); + if (arg3 != nullptr && *arg3 != 0) + draw_nodepath = std::atoi(arg3); if (draw_nodepath > -1) { - sprintf(cMessage, "RBDEBUG: Drawing path of bot id [%d].", draw_nodepath); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Drawing path of bot id [%d].", draw_nodepath); } else - sprintf(cMessage, - "RBDEBUG: Drawing path of bot id [%d] - no better or valid argument given.", - draw_nodepath); + snprintf(cMessage, sizeof(cMessage), + "RBDEBUG: Drawing path of bot id [%d] - no better or valid argument given.", + draw_nodepath); } else if (FStrEq(arg2, "connections")) { if (draw_connodes == false) draw_connodes = true; @@ -1943,47 +1932,47 @@ void RealBot_ServerCommand(void) { draw_connodes = false; if (draw_connodes) - sprintf(cMessage, - "RBDEBUG: Drawing nodes connections - enabled."); + snprintf(cMessage, sizeof(cMessage), + "RBDEBUG: Drawing nodes connections - enabled."); else - sprintf(cMessage, - "RBDEBUG: Drawing nodes connections - disabled."); + snprintf(cMessage, sizeof(cMessage), + "RBDEBUG: Drawing nodes connections - disabled."); } else if (FStrEq(arg2, "init")) { NodeMachine.init(); - sprintf(cMessage, "RBDEBUG: Nodes initialized."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Nodes initialized."); } else if (FStrEq(arg2, "save")) { NodeMachine.save(); - sprintf(cMessage, "RBDEBUG: Nodes saved."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Nodes saved."); } else if (FStrEq(arg2, "load")) { NodeMachine.load(); - sprintf(cMessage, "RBDEBUG: Nodes loaded."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: Nodes loaded."); } else - sprintf(cMessage, "RBDEBUG: No argument given."); + snprintf(cMessage, sizeof(cMessage), "RBDEBUG: No argument given."); } else { - sprintf(cMessage, - "RBDEBUG: Unknown debug command.\n\nKnown commands are:\ndontshoot, pistols, nodes, print"); + snprintf(cMessage, sizeof(cMessage), + "RBDEBUG: Unknown debug command.\n\nKnown commands are:\ndontshoot, pistols, nodes, print"); } } else { // Not a valid command - sprintf(cMessage, - "REALBOT: Unknown command.\nValid commands are:\nhelp, add, remove, skill, max, debug, server"); - bValidCommand = false; + snprintf(cMessage, sizeof(cMessage), + "REALBOT: Unknown command.\nValid commands are:\nhelp, add, remove, skill, max, debug, server"); + bool bValidCommand = false; //Unused variable boolean [APG]RoboCop[CL] } // Send message if (bSendMessage) { // Adding return carriage - strcat(cMessage, "\n"); + std::strcat(cMessage, "\n"); // Put a carriage return when using dedicated server. if (IS_DEDICATED_SERVER()) { SERVER_PRINT("\n"); } - SERVER_PRINT("============================================================================\n"); + SERVER_PRINT("=====================================\n"); SERVER_PRINT(cMessage); - SERVER_PRINT("============================================================================\n"); + SERVER_PRINT("=====================================\n"); // Put an extra carriage return when using dedicated server. if (IS_DEDICATED_SERVER()) { @@ -1996,31 +1985,31 @@ void RealBot_ServerCommand(void) { } } -int Spawn_Post(edict_t *pent) { +int Spawn_Post(edict_t* pent) { // solves the bots unable to see through certain types of glass bug. // MAPPERS: NEVER ALLOW A TRANSPARENT ENTITY TO WEAR THE FL_WORLDBRUSH FLAG !!! // is this a transparent entity ? if (pent->v.rendermode == kRenderTransTexture //|| -// (pent->v.rendermode == kRenderTransAlpha && strcmp("func_illusionary", STRING(pent->v.classname)) == 0) + // (pent->v.rendermode == kRenderTransAlpha && strcmp("func_illusionary", STRING(pent->v.classname)) == 0) ) { // func_illusionary on cs_italy uses this rendermode pent->v.flags |= FL_WORLDBRUSH; // set WORLD BRUSH -// // this seems to enforce solidness, and hence the tracehull/line will detect it now. -// pent->v.solid = SOLID_BSP; -// pent->v.movetype = MOVETYPE_PUSHSTEP; // this seemed to be the best choice, does not do much -// pent->v.rendermode == kRenderNormal; + // this seems to enforce solidness, and hence the tracehull/line will detect it now. + // pent->v.solid = SOLID_BSP; + // pent->v.movetype = MOVETYPE_PUSHSTEP; // this seemed to be the best choice, does not do much + // pent->v.rendermode == kRenderNormal; } else { } char msg[255]; - sprintf(msg, "Found an entity %s - %s - %s with rendermode %d!\n", STRING(pent->v.classname), STRING(pent->v.netname), STRING(pent->v.model), pent->v.rendermode); + snprintf(msg, sizeof(msg), "Found an entity %s - %s - %s with rendermode %d!\n", STRING(pent->v.classname), STRING(pent->v.netname), STRING(pent->v.model), pent->v.rendermode); rblog(msg); // is this a trigger_multiple ? - if (strcmp(STRING(pent->v.classname), "trigger_multiple") == 0) { + if (std::strcmp(STRING(pent->v.classname), "trigger_multiple") == 0) { //pent->v.flags &= ~FL_WORLDBRUSH; // then clear the FL_WORLDBRUSH flag pent->v.flags |= FL_WORLDBRUSH; // make it detectable! pent->v.rendermode = kRenderNormal; @@ -2028,12 +2017,12 @@ int Spawn_Post(edict_t *pent) { // Perhaps this fixes also the as_oilrig problem where a traceline goes through the button without // detecting?? // this seems to enforce solidness, and hence the tracehull/line will detect it now. -// pent->v.solid = SOLID_BSP; -// pent->v.movetype = MOVETYPE_PUSHSTEP; // this seemed to be the best choice, does not do much + // pent->v.solid = SOLID_BSP; + // pent->v.movetype = MOVETYPE_PUSHSTEP; // this seemed to be the best choice, does not do much -// pent->v.solid = SOLID_TRIGGER; -// pent->v.solid = SOLID_BSP; -// SERVER_PRINT("Adjusted a trigger_multiple!!\n"); + // pent->v.solid = SOLID_TRIGGER; + // pent->v.solid = SOLID_BSP; + // SERVER_PRINT("Adjusted a trigger_multiple!!\n"); } RETURN_META_VALUE(MRES_IGNORED, 0); @@ -2041,7 +2030,7 @@ int Spawn_Post(edict_t *pent) { C_DLLEXPORT int -GetEntityAPI2(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion) { +GetEntityAPI2(DLL_FUNCTIONS* pFunctionTable, int* interfaceVersion) { gFunctionTable.pfnGameInit = GameDLLInit; gFunctionTable.pfnSpawn = Spawn; gFunctionTable.pfnClientConnect = ClientConnect; @@ -2049,16 +2038,16 @@ GetEntityAPI2(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion) { gFunctionTable.pfnClientPutInServer = ClientPutInServer; gFunctionTable.pfnClientCommand = ClientCommand; gFunctionTable.pfnStartFrame = StartFrame; - memcpy(pFunctionTable, &gFunctionTable, sizeof(DLL_FUNCTIONS)); - return (TRUE); + std::memcpy(pFunctionTable, &gFunctionTable, sizeof(DLL_FUNCTIONS)); + return 1; } // Whistler: C_DLLEXPORT int -GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion) { +GetEntityAPI2_Post(DLL_FUNCTIONS* pFunctionTable, int* interfaceVersion) { gFunctionTable_post.pfnSpawn = Spawn_Post; // need to declare another gFunctionTable_post in the top of the dll.cpp file - memcpy(pFunctionTable, &gFunctionTable_post, sizeof(DLL_FUNCTIONS)); - return (TRUE); + std::memcpy(pFunctionTable, &gFunctionTable_post, sizeof(DLL_FUNCTIONS)); + return 1; } // Stefan says: You where right, i did not understand it properly. But now i see the little diff --git a/engine.cpp b/engine.cpp index 5010e51..bbffa34 100644 --- a/engine.cpp +++ b/engine.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,29 +20,31 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. * **/ -#include +#include #include #include #include +#include + #include "bot.h" #include "bot_client.h" #include "bot_func.h" #include "ChatEngine.h" -//#include "engine.h" +#include "engine.h" #include "game.h" -extern enginefuncs_t g_engfuncs; +extern enginefuncs_t g_engfuncs; //Redundant? [APG]RoboCop[CL] extern cBot bots[32]; extern cGame Game; @@ -54,28 +58,29 @@ extern int fake_arg_count; // Ditlew's Radio extern char radio_messenger[30]; extern bool radio_message; -extern char *message; +extern char* message; bool radio_message_start = false; bool radio_message_from = false; bool show_beginmessage = true; // End Ditlew's Radio -void (*botMsgFunction)(void *, int) = NULL; +void (*botMsgFunction)(void*, int) = nullptr; -void (*botMsgEndFunction)(void *, int) = NULL; +void (*botMsgEndFunction)(void*, int) = nullptr; int botMsgIndex; -void pfnChangeLevel(const char *s1, const char *s2) { +void pfnChangeLevel(const char* s1, const char* s2) { // kick any bot off of the server after time/frag limit... - for (int index = 0; index < 32; index++) { - if (bots[index].bIsUsed) // is this slot used? + for (cBot& bot : bots) + { + if (bot.bIsUsed) // is this slot used? { char cmd[40]; - sprintf(cmd, "kick \"%s\"\n", bots[index].name); + snprintf(cmd, sizeof(cmd), "kick \"%s\"\n", bot.name); - bots[index].respawn_state = RESPAWN_NEED_TO_RESPAWN; + bot.respawn_state = RESPAWN_NEED_TO_RESPAWN; SERVER_COMMAND(cmd); // kick the bot using (kick "name") } @@ -87,11 +92,11 @@ void pfnChangeLevel(const char *s1, const char *s2) { RETURN_META(MRES_IGNORED); } -edict_t *pfnFindEntityByString(edict_t *pEdictStartSearchAfter, - const char *pszField, const char *pszValue) { +edict_t* pfnFindEntityByString(edict_t* pEdictStartSearchAfter, + const char* pszField, const char* pszValue) { // Counter-Strike - New Round Started - if (strcmp(pszValue, "info_map_parameters") == 0) { + if (std::strcmp(pszValue, "info_map_parameters") == 0) { rblog("pfnFindEntityByString: Game new round\n"); // New round started. @@ -107,29 +112,29 @@ edict_t *pfnFindEntityByString(edict_t *pEdictStartSearchAfter, RETURN_META_VALUE(MRES_IGNORED, NULL); } -void pfnRemoveEntity(edict_t *e) { +void pfnRemoveEntity(edict_t* e) { #if DO_DEBUG == 2 { - fp = fopen("!rbdebug.txt", "a"); - fprintf(fp, "pfnRemoveEntity: %x\n", e); - if (e->v.model != 0) - fprintf(fp, " model=%s\n", STRING(e->v.model)); - fclose(fp); + fp = std::fopen("!rbdebug.txt", "a"); + std::fprintf(fp, R"(pfnRemoveEntity: %x)", e); + if (e->v.model != 0) + std::fprintf(fp, " model=%s\n", STRING(e->v.model)); + std::fclose(fp); } #endif if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnRemoveEntity() - model -> '%s'\n", - STRING(e->v.model)); + snprintf(msg, sizeof(msg), "ENGINE: pfnRemoveEntity() - model -> '%s'\n", + STRING(e->v.model)); rblog(msg); } RETURN_META(MRES_IGNORED); } -void pfnClientCommand(edict_t *pEdict, const char *szFmt, ...) { +void pfnClientCommand(edict_t* pEdict, const char* szFmt, ...) { // new? if (pEdict->v.flags & (FL_FAKECLIENT | FL_THIRDPARTYBOT)) RETURN_META(MRES_SUPERCEDE); @@ -140,127 +145,126 @@ void pfnClientCommand(edict_t *pEdict, const char *szFmt, ...) { RETURN_META(MRES_IGNORED); } -void -pfnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *edict) { +void pfnMessageBegin(const int msg_dest, const int msg_type, const float* pOrigin, edict_t* edict) { if (Game.bEngineDebug) { char dmsg[256]; - sprintf(dmsg, "ENGINE: pfnMessageBegin(), dest=%d, msg_type=%d\n", msg_dest, msg_type); + snprintf(dmsg, sizeof(dmsg), "ENGINE: pfnMessageBegin(), dest=%d, msg_type=%d\n", msg_dest, msg_type); rblog(dmsg); } if (gpGlobals->deathmatch) { - int index = -1; - // Fix this up for CS 1.6 weaponlists // 01/07/04 - Stefan - Thanks to Whistler for pointing this out! - if (msg_type == GET_USER_MSG_ID(PLID, "WeaponList", NULL)) + if (msg_type == GET_USER_MSG_ID(PLID, "WeaponList", nullptr)) botMsgFunction = BotClient_CS_WeaponList; if (edict) { - index = UTIL_GetBotIndex(edict); + const int index = UTIL_GetBotIndex(edict); // is this message for a bot? if (index != -1) { - botMsgFunction = NULL; // no msg function until known otherwise - botMsgEndFunction = NULL; // no msg end function until known otherwise + botMsgFunction = nullptr; // no msg function until known otherwise + botMsgEndFunction = nullptr; // no msg end function until known otherwise botMsgIndex = index; // index of bot receiving message if (mod_id == VALVE_DLL) { - if (msg_type == GET_USER_MSG_ID(PLID, "WeaponList", NULL)) + if (msg_type == GET_USER_MSG_ID(PLID, "WeaponList", nullptr)) botMsgFunction = BotClient_Valve_WeaponList; else if (msg_type == - GET_USER_MSG_ID(PLID, "CurWeapon", NULL)) + GET_USER_MSG_ID(PLID, "CurWeapon", nullptr)) botMsgFunction = BotClient_Valve_CurrentWeapon; - else if (msg_type == GET_USER_MSG_ID(PLID, "AmmoX", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "AmmoX", nullptr)) botMsgFunction = BotClient_Valve_AmmoX; else if (msg_type == - GET_USER_MSG_ID(PLID, "AmmoPickup", NULL)) + GET_USER_MSG_ID(PLID, "AmmoPickup", nullptr)) botMsgFunction = BotClient_Valve_AmmoPickup; else if (msg_type == - GET_USER_MSG_ID(PLID, "WeapPickup", NULL)) + GET_USER_MSG_ID(PLID, "WeapPickup", nullptr)) botMsgFunction = BotClient_Valve_WeaponPickup; else if (msg_type == - GET_USER_MSG_ID(PLID, "ItemPickup", NULL)) + GET_USER_MSG_ID(PLID, "ItemPickup", nullptr)) botMsgFunction = BotClient_Valve_ItemPickup; - else if (msg_type == GET_USER_MSG_ID(PLID, "Health", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "Health", nullptr)) botMsgFunction = BotClient_Valve_Health; - else if (msg_type == GET_USER_MSG_ID(PLID, "Battery", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "Battery", nullptr)) botMsgFunction = BotClient_Valve_Battery; else if (msg_type == - GET_USER_MSG_ID(PLID, "DeathMsg", NULL)) + GET_USER_MSG_ID(PLID, "DeathMsg", nullptr)) botMsgFunction = BotClient_Valve_Damage; else if (msg_type == - GET_USER_MSG_ID(PLID, "ScreenFade", NULL)) + GET_USER_MSG_ID(PLID, "ScreenFade", nullptr)) botMsgFunction = BotClient_Valve_ScreenFade; - } else if (mod_id == CSTRIKE_DLL) { - if (msg_type == GET_USER_MSG_ID(PLID, "VGUIMenu", NULL)) + } + else if (mod_id == CSTRIKE_DLL) { + if (msg_type == GET_USER_MSG_ID(PLID, "VGUIMenu", nullptr)) botMsgFunction = BotClient_CS_VGUI; else if (msg_type == - GET_USER_MSG_ID(PLID, "ShowMenu", NULL)) + GET_USER_MSG_ID(PLID, "ShowMenu", nullptr)) botMsgFunction = BotClient_CS_ShowMenu; else if (msg_type == - GET_USER_MSG_ID(PLID, "WeaponList", NULL)) { + GET_USER_MSG_ID(PLID, "WeaponList", nullptr)) { botMsgFunction = BotClient_CS_WeaponList; //DebugOut("BUGBUG: WEAPONLIST FUNCTION CALLED\n"); - } else if (msg_type == - GET_USER_MSG_ID(PLID, "CurWeapon", NULL)) + } + else if (msg_type == + GET_USER_MSG_ID(PLID, "CurWeapon", nullptr)) botMsgFunction = BotClient_CS_CurrentWeapon; - else if (msg_type == GET_USER_MSG_ID(PLID, "AmmoX", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "AmmoX", nullptr)) botMsgFunction = BotClient_CS_AmmoX; else if (msg_type == - GET_USER_MSG_ID(PLID, "WeapPickup", NULL)) + GET_USER_MSG_ID(PLID, "WeapPickup", nullptr)) botMsgFunction = BotClient_CS_WeaponPickup; else if (msg_type == - GET_USER_MSG_ID(PLID, "AmmoPickup", NULL)) + GET_USER_MSG_ID(PLID, "AmmoPickup", nullptr)) botMsgFunction = BotClient_CS_AmmoPickup; else if (msg_type == - GET_USER_MSG_ID(PLID, "ItemPickup", NULL)) + GET_USER_MSG_ID(PLID, "ItemPickup", nullptr)) botMsgFunction = BotClient_CS_ItemPickup; - else if (msg_type == GET_USER_MSG_ID(PLID, "Health", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "Health", nullptr)) botMsgFunction = BotClient_CS_Health; - else if (msg_type == GET_USER_MSG_ID(PLID, "Battery", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "Battery", nullptr)) botMsgFunction = BotClient_CS_Battery; - else if (msg_type == GET_USER_MSG_ID(PLID, "Damage", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "Damage", nullptr)) botMsgFunction = BotClient_CS_Damage; else if (msg_type == - GET_USER_MSG_ID(PLID, "StatusIcon", NULL)) { - BotClient_CS_StatusIcon(0, -1); // clear state -- redo this -- BERKED + GET_USER_MSG_ID(PLID, "StatusIcon", nullptr)) { + BotClient_CS_StatusIcon(nullptr, -1); // clear state -- redo this -- BERKED botMsgFunction = BotClient_CS_StatusIcon; } - if (msg_type == GET_USER_MSG_ID(PLID, "SayText", NULL)) { + if (msg_type == GET_USER_MSG_ID(PLID, "SayText", nullptr)) { botMsgFunction = BotClient_CS_SayText; - } else if (msg_type == GET_USER_MSG_ID(PLID, "Money", NULL)) + } else if (msg_type == GET_USER_MSG_ID(PLID, "Money", nullptr)) botMsgFunction = BotClient_CS_Money; else if (msg_type == - GET_USER_MSG_ID(PLID, "ScreenFade", NULL)) + GET_USER_MSG_ID(PLID, "ScreenFade", nullptr)) botMsgFunction = BotClient_CS_ScreenFade; } } } else if (msg_dest == MSG_ALL) { - botMsgFunction = NULL; // no msg function until known otherwise + botMsgFunction = nullptr; // no msg function until known otherwise botMsgIndex = -1; // index of bot receiving message (none) if (mod_id == VALVE_DLL) { - if (msg_type == GET_USER_MSG_ID(PLID, "DeathMsg", NULL)) + if (msg_type == GET_USER_MSG_ID(PLID, "DeathMsg", nullptr)) botMsgFunction = BotClient_Valve_DeathMsg; } else if (mod_id == CSTRIKE_DLL) { - if (msg_type == GET_USER_MSG_ID(PLID, "DeathMsg", NULL)) + if (msg_type == GET_USER_MSG_ID(PLID, "DeathMsg", nullptr)) botMsgFunction = BotClient_CS_DeathMsg; - else if (msg_type == GET_USER_MSG_ID(PLID, "SayText", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "SayText", nullptr)) botMsgFunction = BotClient_CS_SayText; } } - // STEFAN + // STEFAN else if (msg_dest == MSG_SPEC) { - botMsgFunction = NULL; // no msg function until known otherwise + botMsgFunction = nullptr; // no msg function until known otherwise botMsgIndex = -1; // index of bot receiving message (none) if (mod_id == CSTRIKE_DLL) { - if (msg_type == GET_USER_MSG_ID(PLID, "HLTV", NULL)) { + if (msg_type == GET_USER_MSG_ID(PLID, "HLTV", nullptr)) { botMsgFunction = BotClient_CS_HLTV; } - else if (msg_type == GET_USER_MSG_ID(PLID, "SayText", NULL)) + else if (msg_type == GET_USER_MSG_ID(PLID, "SayText", nullptr)) botMsgFunction = BotClient_CS_SayText; } @@ -271,14 +275,14 @@ pfnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *edict RETURN_META(MRES_IGNORED); } -void pfnMessageEnd(void) { +void pfnMessageEnd() { if (gpGlobals->deathmatch) { if (botMsgEndFunction) - (*botMsgEndFunction)(NULL, botMsgIndex); // NULL indicated msg end + (*botMsgEndFunction)(nullptr, botMsgIndex); // NULL indicated msg end // clear out the bot message function pointers... - botMsgFunction = NULL; - botMsgEndFunction = NULL; + botMsgFunction = nullptr; + botMsgEndFunction = nullptr; } if (Game.bEngineDebug) @@ -291,12 +295,12 @@ void pfnWriteByte(int iValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &iValue, botMsgIndex); + (*botMsgFunction)(static_cast(&iValue), botMsgIndex); } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteByte() - '%d'\n", iValue); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteByte() - '%d'\n", iValue); rblog(msg); } @@ -307,12 +311,12 @@ void pfnWriteChar(int iValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &iValue, botMsgIndex); + (*botMsgFunction)(static_cast(&iValue), botMsgIndex); } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteChar() - '%d'\n", iValue); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteChar() - '%d'\n", iValue); rblog(msg); } RETURN_META(MRES_IGNORED); @@ -322,12 +326,12 @@ void pfnWriteShort(int iValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &iValue, botMsgIndex); + (*botMsgFunction)(static_cast(&iValue), botMsgIndex); } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteShort() - '%d'\n", iValue); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteShort() - '%d'\n", iValue); rblog(msg); } @@ -338,12 +342,12 @@ void pfnWriteLong(int iValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &iValue, botMsgIndex); + (*botMsgFunction)(static_cast(&iValue), botMsgIndex); } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteLong() - '%d'\n", iValue); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteLong() - '%d'\n", iValue); rblog(msg); } @@ -355,12 +359,12 @@ void pfnWriteAngle(float flValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &flValue, botMsgIndex); + (*botMsgFunction)(static_cast(&flValue), botMsgIndex); } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteAngle() - '%f'\n", flValue); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteAngle() - '%f'\n", flValue); rblog(msg); } @@ -372,12 +376,12 @@ void pfnWriteCoord(float flValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &flValue, botMsgIndex); + (*botMsgFunction)(static_cast(&flValue), botMsgIndex); } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteCoord() - '%f'\n", flValue); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteCoord() - '%f'\n", flValue); rblog(msg); } @@ -386,141 +390,74 @@ void pfnWriteCoord(float flValue) { } void pfnWriteString(const char *sz) { + if (sz == nullptr) + { + if (Game.bEngineDebug) + rblog("ENGINE: pfnWriteString() - sz is null\n"); + RETURN_META(MRES_IGNORED); + } if (Game.bEngineDebug) { char msg[256]; - sprintf(msg, "ENGINE: pfnWriteByte() - '%s'\n", sz); + snprintf(msg, sizeof(msg), "ENGINE: pfnWriteByte() - '%s'\n", sz); rblog(msg); } - if (gpGlobals->deathmatch) { // Ditlew's Radio - if ((strstr(sz, "(RADIO):") != NULL) && !radio_message) { - // We found an old radio message, we should convert the strings... - radio_message = true; // we found a radio message - - // Thank god Ditlew you already coded this... - int length = strlen(sz) - strlen(strstr(sz, " (RADIO)")); - strncpy(radio_messenger, sz, length); - - // Now search for any compatible radio command (old string). - // if found then convert the message in the new way so the code - // thinks its CS 1.1 and thus every version lower then CS 1.1 should work too... - if ((strstr(sz, "Follow Me") != NULL)) { - // convert string + std::string_view sz_sv(sz); + if (sz_sv.find("(RADIO):") != std::string_view::npos && !radio_message) { + radio_message = true; + + if (size_t radio_ptr_pos = sz_sv.find(" (RADIO)"); radio_ptr_pos != std::string_view::npos) { + std::string_view messenger_sv = sz_sv.substr(0, radio_ptr_pos); + strncpy(radio_messenger, messenger_sv.data(), messenger_sv.length()); + radio_messenger[messenger_sv.length()] = '\0'; + } + + if (sz_sv.find("Follow Me") != std::string_view::npos) { strcpy(message, "#Follow me"); - } else if ((strstr(sz, "You Take the Point") != NULL)) { - // convert string + } + else if (sz_sv.find("You Take the Point") != std::string_view::npos) { strcpy(message, "#You_take_the_point"); - } else if ((strstr(sz, "Need backup") != NULL)) { - // convert string + } + else if (sz_sv.find("Need backup") != std::string_view::npos) { strcpy(message, "#Need_backup"); - } else if ((strstr(sz, "Taking Fire.. Need Assistance!") != NULL)) { - // convert string + } + else if (sz_sv.find("Enemy spotted") != std::string_view::npos) { + strcpy(message, "#Enemy_spotted"); + } + else if (sz_sv.find("Taking Fire.. Need Assistance!") != std::string_view::npos) { strcpy(message, "#Taking_fire"); - } else if ((strstr(sz, "Team, fall back!") != NULL)) { - // convert string + } + else if (sz_sv.find("Team, fall back!") != std::string_view::npos) { strcpy(message, "#Team_fall_back"); - } else if ((strstr(sz, "Go go go") != NULL)) { - // convert string + } + else if (sz_sv.find("Go go go") != std::string_view::npos) { strcpy(message, "#Go_go_go"); } - } - /* - else - { - - // normal text message - if (strstr(sz, " : ")) - { - - // Get sender - // Thank god Ditlew you already coded this... - int length = strlen (sz) - strlen (strstr (sz, " : ")); - - char messenger[30]; - char chMessage[80]; - for (int clear=0; clear < 80; clear ++) - { - if (clear < 30) - messenger[clear]='\0'; - - chMessage[clear]='\0'; - } - - // Find the sender (old fasioned way) - int iM=0; - bool SkippedFirst=false; - for (int scan=1; scan < 80; scan++) - { - if (sz[scan] == ':') - { - // now cut one from the messenger - //messenger[iM] = '\0'; - length=scan-1; - scan++; - break; - } - else if (sz[scan] != ' ' && SkippedFirst) - { - //messenger[iM] = sz[scan]; - iM++; - } - else if (sz[scan] == ' ') - { - if(SkippedFirst==false) - SkippedFirst=true; - else - { - // messenger[iM] = sz[scan]; - iM++; - } - } - } - - strncpy (messenger, sz, length); - SERVER_PRINT("MESSENGER:"); - SERVER_PRINT(messenger); - - - - // Everything else is just the sentence: - int chM=0; - for (scan; scan < 80; scan++) - { - chMessage[chM] = sz[scan]; - chM++; - } - - - ChatEngine.set_sentence(messenger, chMessage); - - } - } - */ + if (radio_message_start) { - strcpy(radio_messenger, sz); // the messenger of the radio + std::strcpy(radio_messenger, sz); radio_message_start = false; radio_message_from = true; - } else if (radio_message_from) { - strcpy(message, sz); // copy message and handle at bot.cpp radio routine. + } + else if (radio_message_from) { + std::strcpy(message, sz); radio_message = true; radio_message_from = false; - } else if (strcmp(sz, "#Game_radio") == 0) + } + else if (sz_sv == "#Game_radio") { radio_message_start = true; + } // End Ditlew's Radio - // here it is not radio - // here it is not radio - - - // if this message is for a bot, call the client message function... - if (botMsgFunction) - (*botMsgFunction)((void *) sz, botMsgIndex); + if (botMsgFunction) { + (*botMsgFunction)((void*)sz, botMsgIndex); + } } RETURN_META(MRES_IGNORED); @@ -530,53 +467,49 @@ void pfnWriteEntity(int iValue) { if (gpGlobals->deathmatch) { // if this message is for a bot, call the client message function... if (botMsgFunction) - (*botMsgFunction)((void *) &iValue, botMsgIndex); + (*botMsgFunction)(static_cast(&iValue), botMsgIndex); } RETURN_META(MRES_IGNORED); } -void pfnClientPrintf(edict_t *pEdict, PRINT_TYPE ptype, const char *szMsg) { +void pfnClientPrintf(edict_t* pEdict, PRINT_TYPE ptype, const char* szMsg) { // prevent bots sending these kind of messages if (pEdict->v.flags & (FL_FAKECLIENT | FL_THIRDPARTYBOT)) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } -const char *pfnCmd_Args(void) { +const char* pfnCmd_Args() { if (isFakeClientCommand) RETURN_META_VALUE(MRES_SUPERCEDE, &g_argv[0]); RETURN_META_VALUE(MRES_IGNORED, NULL); } -const char *pfnCmd_Argv(int argc) { +const char* pfnCmd_Argv(const int argc) { if (isFakeClientCommand) { if (argc == 0) RETURN_META_VALUE(MRES_SUPERCEDE, &g_argv[64]); - - else if (argc == 1) + if (argc == 1) RETURN_META_VALUE(MRES_SUPERCEDE, &g_argv[128]); - - else if (argc == 2) + if (argc == 2) RETURN_META_VALUE(MRES_SUPERCEDE, &g_argv[192]); - - else - RETURN_META_VALUE(MRES_SUPERCEDE, NULL); + RETURN_META_VALUE(MRES_SUPERCEDE, NULL); } RETURN_META_VALUE(MRES_IGNORED, NULL); } -int pfnCmd_Argc(void) { +int pfnCmd_Argc() { if (isFakeClientCommand) RETURN_META_VALUE(MRES_SUPERCEDE, fake_arg_count); RETURN_META_VALUE(MRES_IGNORED, 0); } -void pfnSetClientMaxspeed(const edict_t *pEdict, float fNewMaxspeed) { +void pfnSetClientMaxspeed(const edict_t* pEdict, const float fNewMaxspeed) { // Set client max speed (CS / All mods) // Check if edict_t is a bot, then set maxspeed - cBot *pPlayerBot = NULL; + cBot* pPlayerBot = nullptr; int index; for (index = 0; index < 32; index++) { @@ -594,7 +527,7 @@ void pfnSetClientMaxspeed(const edict_t *pEdict, float fNewMaxspeed) { RETURN_META(MRES_IGNORED); } -int pfnGetPlayerUserId(edict_t *e) { +int pfnGetPlayerUserId(edict_t* e) { if (gpGlobals->deathmatch) { //if (mod_id == GEARBOX_DLL) //{ @@ -608,8 +541,8 @@ int pfnGetPlayerUserId(edict_t *e) { } C_DLLEXPORT int -GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, - int *interfaceVersion) { +GetEngineFunctions(enginefuncs_t* pengfuncsFromEngine, + int* interfaceVersion) { meta_engfuncs.pfnChangeLevel = pfnChangeLevel; meta_engfuncs.pfnFindEntityByString = pfnFindEntityByString; meta_engfuncs.pfnRemoveEntity = pfnRemoveEntity; @@ -630,6 +563,6 @@ GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, meta_engfuncs.pfnCmd_Argc = pfnCmd_Argc; meta_engfuncs.pfnSetClientMaxspeed = pfnSetClientMaxspeed; meta_engfuncs.pfnGetPlayerUserId = pfnGetPlayerUserId; - memcpy(pengfuncsFromEngine, &meta_engfuncs, sizeof(enginefuncs_t)); - return TRUE; + std::memcpy(pengfuncsFromEngine, &meta_engfuncs, sizeof(enginefuncs_t)); + return 1; } diff --git a/engine.h b/engine.h index 4733976..d400272 100644 --- a/engine.h +++ b/engine.h @@ -30,6 +30,9 @@ #ifndef ENGINE_H #define ENGINE_H +#include +#include + // engine prototypes (from engine\eiface.h)... int pfnPrecacheModel(char *s); int pfnPrecacheSound(char *s); @@ -56,7 +59,7 @@ 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); +edict_t *pfnCreateEntity(); void pfnRemoveEntity(edict_t * e); edict_t *pfnCreateNamedEntity(int className); void pfnMakeStatic(edict_t * ent); @@ -86,7 +89,7 @@ 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 pfnServerExecute(); void pfnClientCommand(edict_t * pEdict, const char *szFmt, ...); void pfnParticleEffect(const float *org, const float *dir, float color, float count); @@ -95,7 +98,7 @@ int pfnDecalIndex(const char *name); int pfnPointContents(const float *rgflVector); void pfnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t * edict); -void pfnMessageEnd(void); +void pfnMessageEnd(); void pfnWriteByte(int iValue); void pfnWriteChar(int iValue); void pfnWriteShort(int iValue); @@ -136,9 +139,9 @@ const char *pfnNameForFunction(uint32 function); void pfnClientPrintf(edict_t * pEdict, PRINT_TYPE ptype, const char *szMsg); void pfnServerPrint(const char *szMsg); -const char *pfnCmd_Args(void); +const char *pfnCmd_Args(); const char *pfnCmd_Argv(int argc); -int pfnCmd_Argc(void); +int pfnCmd_Argc(); void pfnGetAttachment(const edict_t * pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles); void pfnCRC32_Init(CRC32_t * pulCRC); @@ -148,7 +151,7 @@ CRC32_t pfnCRC32_Final(CRC32_t pulCRC); int32 pfnRandomLong(int32 lLow, int32 lHigh); float pfnRandomFloat(float flLow, float flHigh); void pfnSetView(const edict_t * pClient, const edict_t * pViewent); -float pfnTime(void); +float pfnTime(); void pfnCrosshairAngle(const edict_t * pClient, float pitch, float yaw); byte *pfnLoadFileForMe(char *filename, int *pLength); void pfnFreeFile(void *buffer); @@ -164,7 +167,7 @@ edict_t *pfnCreateFakeClient(const char *netname); void pfnRunPlayerMove(edict_t * fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, byte msec); -int pfnNumberOfEntities(void); +int pfnNumberOfEntities(); char *pfnGetInfoKeyBuffer(edict_t * e); char *pfnInfoKeyValue(char *infobuffer, char *key); void pfnSetKeyValue(char *infobuffer, char *key, char *value); @@ -179,7 +182,7 @@ void pfnBuildSoundMsg(edict_t * entity, int channel, const char *sample, /*int */ float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t * ed); -int pfnIsDedicatedServer(void); +int pfnIsDedicatedServer(); cvar_t *pfnCVarGetPointer(const char *szVarName); unsigned int pfnGetPlayerWONId(edict_t * e); const char *pfnGetPlayerAuthId(edict_t * e); // new @@ -208,7 +211,7 @@ void pfnDeltaAddEncoder(char *name, *from, const unsigned char *to)); -int pfnGetCurrentPlayer(void); +int pfnGetCurrentPlayer(); int pfnCanSkipPlayer(const edict_t * player); int pfnDeltaFindField(struct delta_s *pFields, const char *fieldname); void pfnDeltaSetFieldByIndex(struct delta_s *pFields, int fieldNumber); diff --git a/game.cpp b/game.cpp index baeae4e..f47dd4a 100644 --- a/game.cpp +++ b/game.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /** * RealBot : Artificial Intelligence * Version : Work In Progress @@ -6,7 +8,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,16 +20,23 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. * **/ -#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -79,7 +88,7 @@ void cGame::Init() { vDroppedC4 = Vector(9999, 9999, 9999); // May we walk with knife (when bots want to), default = yes (3600 seconds) - fWalkWithKnife = 3600; + fWalkWithKnife = 3600.0f; // Chat related iMaxSentences = 1; // max sentences produced by chatengine per second (1=default) @@ -93,22 +102,24 @@ void cGame::Init() { bPistols = false; // pistols only mode // Speech sentences (from POD and a *few* own made) - strcpy(cSpeechSentences[0], "hello user,communication is acquired"); - strcpy(cSpeechSentences[1], "your presence is acknowledged"); - strcpy(cSpeechSentences[2], "high man, your in command now"); - strcpy(cSpeechSentences[3], "blast your hostile for good"); - strcpy(cSpeechSentences[4], "high man, kill some idiot here"); - strcpy(cSpeechSentences[5], "is there a doctor in the area"); - strcpy(cSpeechSentences[6], "warning, experimental materials detected"); - strcpy(cSpeechSentences[7], "high amigo, shoot some but"); - strcpy(cSpeechSentences[8], "attention, hours of work software, detected"); - strcpy(cSpeechSentences[9], "time for some bad ass explosion"); - strcpy(cSpeechSentences[10], "bad ass son of a breach device activated"); - strcpy(cSpeechSentences[11], "high, do not question this great service"); - strcpy(cSpeechSentences[12], "engine is operative, hello and goodbye"); - strcpy(cSpeechSentences[13], "high amigo, your administration has been great last day"); - strcpy(cSpeechSentences[14], "attention, expect experimental armed hostile presence"); - strcpy(cSpeechSentences[15], "warning,medical attention required"); + cSpeechSentences = { { + "hello user,communication is acquired", + "your presence is acknowledged", + "high man, your in command now", + "blast your hostile for good", + "high man, kill some idiot here", + "is there a doctor in the area", + "warning, experimental materials detected", + "high amigo, shoot some but", + "attention, hours of work software, detected", + "time for some bad ass explosion", + "bad ass son of a breach device activated", + "high, do not question this great service", + "engine is operative, hello and goodbye", + "high amigo, your administration has been great last day", + "attention, expect experimental armed hostile presence", + "warning,medical attention required" + } }; InitNewRound(); } // Init() @@ -126,22 +137,23 @@ void cGame::InitNewRound() { DetermineMapGoal(); // initialize bots for new round - for (int i = 0; i < MAX_BOTS; i++) { - cBot &bot = bots[i]; + for (cBot& bot : bots) + { if (bot.bIsUsed) { bot.NewRound(); } } iProducedSentences = RANDOM_LONG(0, Game.iMaxSentences); - ChatEngine.fThinkTimer = gpGlobals->time + RANDOM_FLOAT(0.0, 0.5); + ChatEngine.fThinkTimer = gpGlobals->time + RANDOM_FLOAT(0.0f, 0.5f); } /** * If the c4 is dropped (not planted), then return true. * @return */ -bool cGame::isC4Dropped() { +bool cGame::isC4Dropped() const +{ return vDroppedC4 != Vector(9999, 9999, 9999); } @@ -149,43 +161,44 @@ bool cGame::isC4Dropped() { * Is the C4 - when planted - discovered? * @return */ -bool cGame::isPlantedC4Discovered() { +bool cGame::isPlantedC4Discovered() const +{ return vPlantedC4 != Vector(9999, 9999, 9999); } // Returns random sentence for speech -char *cGame::RandomSentence() { - return cSpeechSentences[RANDOM_LONG(0, 15)]; +const char* cGame::RandomSentence() const +{ + return cSpeechSentences[RANDOM_LONG(0, cSpeechSentences.size() - 1)].c_str(); } -void cGame::DetermineMapGoal() { +void cGame::DetermineMapGoal() +{ rblog("DetermineMapGoal called\n"); - edict_t *pEnt = NULL; + edict_t* pEnt = nullptr; int hostagesFound = 0; - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "hostage_entity")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "hostage_entity")) != nullptr) { hostagesFound++; } - char msg[255]; - memset(msg, 0, sizeof(msg)); - sprintf(msg, "DetermineMapGoal: There are %d hostages found to rescue\n", hostagesFound); + char msg[255] = {}; + snprintf(msg, sizeof(msg), "DetermineMapGoal: There are %d hostages found to rescue\n", hostagesFound); rblog(msg); Game.bHostageRescueMap = hostagesFound > 0; int rescueZonesFound = 0; // GOAL #3 - Hostage rescue zone - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "func_hostage_rescue")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "func_hostage_rescue")) != nullptr) { rescueZonesFound++; } // rescue zone can also be an entity of info_hostage_rescue - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "info_hostage_rescue")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "info_hostage_rescue")) != nullptr) { rescueZonesFound++; } - memset(msg, 0, sizeof(msg)); - sprintf(msg, "DetermineMapGoal: There are %d rescue zones found\n", rescueZonesFound); + snprintf(msg, sizeof(msg), "DetermineMapGoal: There are %d rescue zones found\n", rescueZonesFound); rblog(msg); Game.bHostageRescueZoneFound = rescueZonesFound > 0; @@ -193,15 +206,14 @@ void cGame::DetermineMapGoal() { int bombSpots = 0; // GOAL #4 - Bombspot zone // Bomb spot - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "func_bomb_target")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "func_bomb_target")) != nullptr) { bombSpots++; } - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "info_bomb_target")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "info_bomb_target")) != nullptr) { bombSpots++; } - memset(msg, 0, sizeof(msg)); - sprintf(msg, "DetermineMapGoal: There are %d bomb spots in this level\n", bombSpots); + snprintf(msg, sizeof(msg), "DetermineMapGoal: There are %d bomb spots in this level\n", bombSpots); Game.bBombPlantMap = bombSpots > 0; rblog(msg); } @@ -212,47 +224,47 @@ void cGame::resetRoundTime() { } // GAME: Set round time -void cGame::SetRoundTime(float fTime) { +void cGame::SetRoundTime(const float fTime) { fRoundTime = fTime; } // GAME: Get round time // NOTE: This is the time when a round has just started! -float cGame::getRoundStartedTime() { +float cGame::getRoundStartedTime() const +{ return fRoundTime; } -float cGame::getRoundTimeElapsed() { +float cGame::getRoundTimeElapsed() const +{ if (getRoundStartedTime() > -1) { return gpGlobals->time - getRoundStartedTime(); - } else { - return -1; } + return -1; } // GAME: Set new round flag -void cGame::SetNewRound(bool bState) { +void cGame::SetNewRound(const bool bState) { bNewRound = bState; } // GAME: Get new round status -bool cGame::NewRound() { +bool cGame::NewRound() const +{ return bNewRound; } // GAME: Set min and max playing rounds -void cGame::SetPlayingRounds(int iMin, int iMax) { +void cGame::SetPlayingRounds(const int iMin, const int iMax) { if (iMin > -1) iMinPlayRounds = iMin; if (iMax > -1) iMaxPlayRounds = iMax; // Boundries - if (iMinPlayRounds < 1) - iMinPlayRounds = 1; - if (iMaxPlayRounds < 2) - iMaxPlayRounds = 2; + iMinPlayRounds = std::max(iMinPlayRounds, 1); + iMaxPlayRounds = std::max(iMaxPlayRounds, 2); // Make sure MAX never is lower then MIN if (iMinPlayRounds > iMaxPlayRounds) @@ -261,12 +273,14 @@ void cGame::SetPlayingRounds(int iMin, int iMax) { } // SetPlayingRounds() // GAME: GET Max playing rounds -int cGame::GetMaxPlayRounds() { +int cGame::GetMaxPlayRounds() const +{ return iMaxPlayRounds; } // GAME: GET Min playing rounds -int cGame::GetMinPlayRounds() { +int cGame::GetMinPlayRounds() const +{ return iMinPlayRounds; } @@ -277,114 +291,70 @@ void cGame::LoadCFG() { } // LoadCFG() // GAME: Load names file -// NOTE: This function is just a copy/paste stuff from Botmans template, nothing more, nothing less -// TODO: Rewrite this, can be done much cleaner. void cGame::LoadNames() { - FILE *bot_name_fp; - int str_index; - char name_buffer[80]; - int length, index; char filename[256]; UTIL_BuildFileNameRB("rb_names.txt", filename); - bot_name_fp = fopen(filename, "r"); - if (bot_name_fp != NULL) { - while ((iAmountNames < MAX_BOT_NAMES) && - (fgets(name_buffer, 80, bot_name_fp) != NULL)) { - length = strlen(name_buffer); - if (name_buffer[length - 1] == '\n') { - name_buffer[length - 1] = 0; // remove '\n' - length--; - } - str_index = 0; - while (str_index < length) { - if ((name_buffer[str_index] < ' ') - || (name_buffer[str_index] > '~') - || (name_buffer[str_index] == '"')) - for (index = str_index; index < length; index++) - name_buffer[index] = name_buffer[index + 1]; - str_index++; - } + std::ifstream file(filename); + if (!file.is_open()) { + return; + } - if (name_buffer[0] != 0) { - strncpy(cBotNames[iAmountNames], name_buffer, BOT_NAME_LEN); - iAmountNames++; - } + cBotNames.clear(); + std::string line; + while (std::getline(file, line) && cBotNames.size() < MAX_BOT_NAMES) { + // Trim whitespace and remove invalid characters + line.erase(line.find_last_not_of(" \n\r\t") + 1); + line.erase(0, line.find_first_not_of(" \n\r\t")); + line.erase(std::remove_if(line.begin(), line.end(), [](const char c) { + return c < ' ' || c > '~' || c == '"'; + }), line.end()); + + if (!line.empty()) { + cBotNames.push_back(line); } - fclose(bot_name_fp); } + iAmountNames = cBotNames.size(); } // LoadNames() // Any names available? -bool cGame::NamesAvailable() { - if (iAmountNames > 0) - return true; - - return false; +bool cGame::NamesAvailable() const +{ + return !cBotNames.empty(); } // NamesAvailable() - // Picks a random name // rewritten on april 10th 2004 -void cGame::SelectName(char *name) { - int iNameIndex, iIndex; - bool bUsed; - edict_t *pPlayer; - iNameIndex = 0; // zero based (RANDOM_LONG (0, iAmountNames-1)) - - bool iNameUsed[MAX_BOT_NAMES]; - for (int i = 0; i < MAX_BOT_NAMES; i++) { - iNameUsed[i] = false; +std::string cGame::SelectName() const +{ + if (cBotNames.empty()) { + return "RealBot"; } - // check make sure this name isn't used - bUsed = true; - while (bUsed) { - iNameIndex = RANDOM_LONG(0, iAmountNames - 1); // pick random one - int iLimit = iNameIndex; // remember this. + // Get all names of players currently in the game. + std::vector usedNames; + for (int i = 1; i <= gpGlobals->maxClients; i++) { + const edict_t* pPlayer = INDEXENT(i); + if (pPlayer && !pPlayer->free) { + usedNames.emplace_back(STRING(pPlayer->v.netname)); + } + } - // make sure it is not checked yet - while (iNameUsed[iNameIndex]) { - // check again - if (iNameUsed[iNameIndex] == false) + // Find a name that is not in use. + for (const std::string& botName : cBotNames) { + bool isNameInUse = false; + for (const std::string& usedName : usedNames) { + if (botName == usedName) { + isNameInUse = true; break; - - // add up - iNameIndex++; - - // make sure that it does not check out of range - if (iNameIndex == iAmountNames) - iNameIndex = 0; // go to 0 (will be set to 0 next 'name') - - // when we are back to where we came from, get the fuck outta here - if (iNameIndex == iLimit) { - strcpy(name, "RealBot"); - return; } } - // so far we did not find evidence that this name has been used already - bUsed = false; - - // check if this name is used - for (iIndex = 1; iIndex <= gpGlobals->maxClients; iIndex++) { - pPlayer = INDEXENT(iIndex); - if (pPlayer && !pPlayer->free) { - if (strcmp(cBotNames[iNameIndex], STRING(pPlayer->v.netname)) - == 0) { - // atten tion, this namehas been used. - bUsed = true; - break; - } - } + if (!isNameInUse) { + return botName; } - - if (bUsed) - iNameUsed[iNameIndex] = true; // set on true - } - // copy name into the name_buffer - strcpy(name, cBotNames[iNameIndex]); + return "RealBot"; // Fallback if all names are taken } // SelectName() // GAME: Load BUYTABLE.INI file @@ -394,9 +364,7 @@ void cGame::LoadBuyTable() { // GAME: Update global vars (called by StartFrame) void cGame::UpdateGameStatus() { - // Used variables - edict_t *pEnt; - pEnt = NULL; + edict_t* pEnt = nullptr; // ------------------ // Update: Dropped C4 @@ -405,7 +373,7 @@ void cGame::UpdateGameStatus() { vDroppedC4 = Vector(9999, 9999, 9999); // Find the dropped bomb - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "weaponbox")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "weaponbox")) != nullptr) { // when DROPPED C4 if ((FStrEq(STRING(pEnt->v.model), "models/w_backpack.mdl"))) { vDroppedC4 = pEnt->v.origin; // this is the origin. @@ -417,10 +385,10 @@ void cGame::UpdateGameStatus() { // Update: Is the bomb planted? // ------------------ // Same as dropped c4, its NOT, unless stated otherwise. - pEnt = NULL; + pEnt = nullptr; bool bPlanted = bBombPlanted; - while ((pEnt = UTIL_FindEntityByClassname(pEnt, "grenade")) != NULL) { + while ((pEnt = UTIL_FindEntityByClassname(pEnt, "grenade")) != nullptr) { if (UTIL_GetGrenadeType(pEnt) == 4) { bPlanted = true; // Found planted bomb! break; @@ -431,19 +399,17 @@ void cGame::UpdateGameStatus() { // all counter-terrorists should know this, and they should head for the bomb if (bPlanted && // found a planted bomb bPlanted != bBombPlanted // and previously we didn't know that - ) { - - int i; - for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - cBot *bot = UTIL_GetBotPointer(pPlayer); + ) { + for (int i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + cBot* bot = UTIL_GetBotPointer(pPlayer); // valid bot if (bot) { if (bot->isCounterTerrorist()) { bot->forgetPath(); bot->rprint("Setting goal for bombspot"); - tGoal *bombSpotGoal = NodeMachine.getRandomGoalByType(GOAL_BOMBSPOT); + tGoal* bombSpotGoal = NodeMachine.getRandomGoalByType(GOAL_BOMBSPOT); if (bombSpotGoal) { bot->setGoalNode(bombSpotGoal); // picks a random bomb spot } @@ -452,12 +418,12 @@ void cGame::UpdateGameStatus() { } // through all clients // Now update bBombPlanted - bBombPlanted = bPlanted; + bBombPlanted = true; } // planted, and not planted before // Every 3 seconds update the goals - if (gpGlobals->time > (fUpdateGoalTimer + 3)) { -// rblog("cGame::UpdateGameStatus - updateGoals\n"); + if (gpGlobals->time > fUpdateGoalTimer + 3) { + // rblog("cGame::UpdateGameStatus - updateGoals\n"); NodeMachine.updateGoals(); fUpdateGoalTimer = gpGlobals->time; } @@ -474,47 +440,40 @@ void cGame::UpdateGameStatus() { * @param nameArg * @return */ -int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg, const char *modelArg, const char *nameArg) { +int cGame::createBot(edict_t* pPlayer, const char* teamArg, const char* skillArg, const char* modelArg, const char* nameArg) const +{ // NAME - char botName[BOT_NAME_LEN + 1]; - memset(botName, 0, sizeof(botName)); - // if name given, use that - if ((nameArg != NULL) && (*nameArg != 0)) { - strncpy(botName, nameArg, BOT_NAME_LEN - 1); - botName[BOT_NAME_LEN] = 0; // make sure botName is null terminated - } else { // else pick random one or fallback to default "RealBot" - if (NamesAvailable()) { - SelectName(botName); - } else { - strcpy(botName, "RealBot"); - } + std::string botNameStr; + if (nameArg != nullptr && *nameArg != 0) { + botNameStr = nameArg; + } + else { + botNameStr = SelectName(); } - // length of name - int lengthOfBotName = strlen(botName); + // Sanitize name + botNameStr.erase(std::remove_if(botNameStr.begin(), botNameStr.end(), [](const char c) { + return c <= ' ' || c > '~' || c == '"'; + }), botNameStr.end()); - // remove any illegal characters from name... - int i, j; - for (i = 0; i < lengthOfBotName; i++) { - if ((botName[i] <= ' ') || (botName[i] > '~') || (botName[i] == '"')) { - // move chars to the left (and null) - for (j = i; j < lengthOfBotName; j++) { - botName[j] = botName[j + 1]; - } - lengthOfBotName--; - } + if (botNameStr.length() > BOT_NAME_LEN) { + botNameStr.resize(BOT_NAME_LEN); } + char botName[BOT_NAME_LEN + 1]; + std::strncpy(botName, botNameStr.c_str(), BOT_NAME_LEN); + botName[BOT_NAME_LEN] = '\0'; + int botSkill = -2; // -2, not valid // Skill argument provided - if ((skillArg != NULL) && (*skillArg != 0)) { - botSkill = atoi(skillArg); // set to given skill + if ((skillArg != nullptr) && (*skillArg != 0)) { + botSkill = std::atoi(skillArg); // set to given skill } // when not valid (-2), it has default skill - if ((botSkill < -1) || (botSkill > 10)) { + if (botSkill < -1 || botSkill > 10) { botSkill = iDefaultBotSkill; } @@ -523,22 +482,20 @@ int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg botSkill = RANDOM_LONG(iRandomMinSkill, iRandomMaxSkill); } + // Clamp skill value between 0 and 10 + botSkill = std::clamp(botSkill, 0, 10); + // CREATE fake client! edict_t *pBotEdict = (*g_engfuncs.pfnCreateFakeClient)(botName); if (FNullEnt(pBotEdict)) { - REALBOT_PRINT(NULL, "cGame::CreateBot", "Cannot create bot, server is full"); + REALBOT_PRINT(nullptr, "cGame::CreateBot", "Cannot create bot, server is full"); return GAME_MSG_FAIL_SERVERFULL; // failed } - char ptr[128]; // allocate space for message from ClientConnect - char *infobuffer; - int clientIndex; - // find empty bot index - int freeBotIndex; - freeBotIndex = 0; - while ((bots[freeBotIndex].bIsUsed) && (freeBotIndex < MAX_BOTS)) + int freeBotIndex = 0; + while (freeBotIndex < MAX_BOTS && bots[freeBotIndex].bIsUsed) freeBotIndex++; if (freeBotIndex == MAX_BOTS) { // failure @@ -549,16 +506,16 @@ int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg // (from LINK_ENTITY_TO_CLASS for player object) // FIX: Free data for bot, so we can fill in new - if (pBotEdict->pvPrivateData != NULL) + if (pBotEdict->pvPrivateData != nullptr) FREE_PRIVATE(pBotEdict); - pBotEdict->pvPrivateData = NULL; + pBotEdict->pvPrivateData = nullptr; pBotEdict->v.frags = 0; // END OF FIX: --- score resetted CALL_GAME_ENTITY(PLID, "player", VARS(pBotEdict)); - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pBotEdict); - clientIndex = ENTINDEX(pBotEdict); + char* infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pBotEdict); + const int clientIndex = ENTINDEX(pBotEdict); (*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "model", ""); (*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "rate", "3500.000000"); @@ -586,7 +543,7 @@ int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg // initialize all the variables for this bot... // Retrieve Pointer - cBot *pBot = &bots[freeBotIndex]; + cBot* pBot = &bots[freeBotIndex]; // TODO: Stefan 05/09/2019 - init function? (re-use, so much duplication here) // Set variables @@ -594,14 +551,13 @@ int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg pBot->bIsUsed = true; pBot->respawn_state = RESPAWN_IDLE; pBot->fCreateTime = gpGlobals->time; - pBot->fKickTime = 0.0; + pBot->fKickTime = 0.0f; pBot->name[0] = 0; // name not set by server yet pBot->bot_money = 0; // clear - char c_skin[BOT_SKIN_LEN + 1]; - memset(c_skin, 0, sizeof(c_skin)); - strcpy(pBot->skin, c_skin); + constexpr char c_skin[BOT_SKIN_LEN + 1] = {}; + std::strcpy(pBot->skin, c_skin); pBot->pEdict = pBotEdict; pBot->hasJoinedTeam = false; // hasn't joined game yet @@ -644,12 +600,12 @@ int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg pBot->ipBuyArmour = 0; // here we set team - if ((teamArg != NULL) && (teamArg[0] != 0)) { - pBot->iTeam = atoi(teamArg); + if ((teamArg != nullptr) && (teamArg[0] != 0)) { + pBot->iTeam = std::atoi(teamArg); // and class - if ((modelArg != NULL) && (modelArg[0] != 0)) { - pBot->bot_class = atoi(modelArg); + if ((modelArg != nullptr) && (modelArg[0] != 0)) { + pBot->bot_class = std::atoi(modelArg); } } @@ -664,40 +620,30 @@ int cGame::createBot(edict_t *pPlayer, const char *teamArg, const char *skillArg } // CreateBot() // Debug message (without BOT) -void REALBOT_PRINT(const char *Function, const char *msg) { - REALBOT_PRINT(NULL, Function, msg); +void REALBOT_PRINT(const char* Function, const char* msg) { + REALBOT_PRINT(nullptr, Function, msg); } // Debug message void REALBOT_PRINT(cBot *pBot, const char *Function, const char *msg) { - char cMessage[512]; - char team[9]; - char name[MAX_NAME_LENGTH]; - char mapName[32]; + std::string team = "NONE"; + std::string name = "FUNCTION"; + std::string mapName = "NA"; int botIndex = -1; - memset(team, 0, sizeof(team)); // clear - memset(name, 0, sizeof(name)); // clear - memset(mapName, 0, sizeof(mapName)); // clear - - strcpy(team, "NONE"); - strcpy(name, "FUNCTION"); - if (gpGlobals->mapname) { - strcpy(mapName, STRING(gpGlobals->mapname)); - } else { - strcpy(mapName, "NA"); + mapName = STRING(gpGlobals->mapname); } if (pBot) { botIndex = pBot->iBotIndex; - memset(name, 0, sizeof(name)); // clear - strcpy(name, pBot->name); // copy name + name = pBot->name; if (pBot->isCounterTerrorist()) { - strcpy(team, "COUNTER"); - } else if (pBot->isTerrorist()) { - strcpy(team, "TERROR"); + team = "COUNTER"; + } + else if (pBot->isTerrorist()) { + team = "TERROR"; } } @@ -707,19 +653,19 @@ void REALBOT_PRINT(cBot *pBot, const char *Function, const char *msg) { float roundTimeInMinutes = CVAR_GET_FLOAT("mp_roundtime"); if (roundTimeInMinutes < 0) roundTimeInMinutes = 4; // sensible default - float roundTimeInSeconds = roundTimeInMinutes * 60; - float roundTimeRemaining = roundTimeInSeconds - roundElapsedTimeInSeconds; - int minutesLeft = roundTimeRemaining / 60; - int secondsLeft = (int)roundTimeRemaining % 60; - - sprintf(cMessage, "[rb] [%s] [%0d:%02d] - [%s|%d] [%s] [%s] : %s\n", mapName, minutesLeft, secondsLeft, name, botIndex, team, Function, msg); + const float roundTimeInSeconds = roundTimeInMinutes * 60; + const float roundTimeRemaining = roundTimeInSeconds - roundElapsedTimeInSeconds; + const int minutesLeft = static_cast(roundTimeRemaining) / 60; + const int secondsLeft = static_cast(roundTimeRemaining) % 60; // print in console only when on debug print if (Game.bDebug > -2) { - if (Game.bDebug == -1) { - rblog(cMessage); - } else if (Game.bDebug == botIndex && botIndex > -1) { - rblog(cMessage); + if (Game.bDebug == -1 || Game.bDebug == botIndex) { + std::ostringstream oss; + oss << "[rb] [" << mapName << "] [" << minutesLeft << ":" << std::setw(2) << std::setfill('0') << secondsLeft << "] - [" + << name << "|" << botIndex << "] [" << team << "] [" << Function << "] : " << msg << "\n"; + const std::string cMessage = oss.str(); + rblog(cMessage.c_str()); } } } // REALBOT_PRINT() @@ -800,4 +746,4 @@ void REALBOT_PRINT(cBot *pBot, const char *Function, const char *msg) { // - Log() works properly now // - Clearing in dll.cpp of reallog.txt at dll init // - Logging works now, add REALBOT_PRINT() at every point you want to log something. -// +// \ No newline at end of file diff --git a/game.h b/game.h index 53befe7..168ceed 100644 --- a/game.h +++ b/game.h @@ -6,7 +6,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -18,9 +18,9 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. @@ -30,122 +30,131 @@ #ifndef GAME_H #define GAME_H +#include +#include +#include -/** - * GAME "handler" CLASS - * COPYRIGHTED BY STEFAN HENDRIKS (C) - **/ + /** + * GAME "handler" CLASS + * COPYRIGHTED BY STEFAN HENDRIKS (C) + **/ -// GAME MESSAGES -#define GAME_MSG_SUCCESS 0 // complete success -#define GAME_MSG_FAILURE 1 // complete failure + // GAME MESSAGES +enum : std::uint8_t +{ + GAME_MSG_SUCCESS = 0, // complete success + GAME_MSG_FAILURE = 1, // complete failure -#define GAME_MSG_FAIL_SERVERFULL 2 // failure + reason -//#define GAME_MSG_WHATEVERYOUWANTTOPUTHERE + GAME_MSG_FAIL_SERVERFULL = 2, // failure + reason + //#define GAME_MSG_WHATEVERYOUWANTTOPUTHERE -// SITUATIONS -#define GAME_YES 99 -#define GAME_NO 98 + // SITUATIONS + GAME_YES = 99, + GAME_NO = 98 +}; // BROADCASTING -#define BROADCAST_ROUND 0 -#define BROADCAST_MAP 1 -#define BROADCAST_KILLS_FULL 3 // killed, show full info (name+skill) -#define BROADCAST_KILLS_MIN 4 // killed, show min info (name) -#define BROADCAST_KILLS_NONE 5 // killed, show no information -#define BROADCAST_DEATHS_FULL 6 // died, show full info (name+skill) -#define BROADCAST_DEATHS_MIN 7 // died, show min info (name) -#define BROADCAST_DEATHS_NONE 8 // died, show no information - -static const int MAX_BOTS = 32; -static const int MAX_NAME_LENGTH = 32; +enum : std::uint8_t +{ + BROADCAST_ROUND = 0, + BROADCAST_MAP = 1, + BROADCAST_KILLS_FULL = 3, // killed, show full info (name+skill) + BROADCAST_KILLS_MIN = 4, // killed, show min info (name) + BROADCAST_KILLS_NONE = 5, // killed, show no information + BROADCAST_DEATHS_FULL = 6, // died, show full info (name+skill) + BROADCAST_DEATHS_MIN = 7, // died, show min info (name) + BROADCAST_DEATHS_NONE = 8 // died, show no information +}; + +static constexpr int MAX_BOTS = 32; +static constexpr int MAX_NAME_LENGTH = 32; // Debug messages for realbot -void REALBOT_PRINT(cBot * pBot, const char *Function, const char *msg); -void REALBOT_PRINT(const char *Function, const char *msg); +void REALBOT_PRINT(cBot* pBot, const char* Function, const char* msg); +void REALBOT_PRINT(const char* Function, const char* msg); class cGame { public: - void Init(); - void InitNewRound(); - - // --------------------- - void LoadNames(); - void LoadCFG(); - void LoadBuyTable(); - - // --------------------- - void SelectName(char *name); - bool NamesAvailable(); - void SetPlayingRounds(int iMin, int iMax); - void SetNewRound(bool bState); - void resetRoundTime(); - void SetRoundTime(float fTime); - void DetermineMapGoal(); - - // --------------------- - char *RandomSentence(); - - // --------------------- - int GetMinPlayRounds(); - int GetMaxPlayRounds(); - - bool NewRound(); // New round? - float getRoundStartedTime(); // When did the round start? (time) - float getRoundTimeElapsed(); // difference between now and round started time - - int createBot(edict_t * pPlayer, const char *teamArg, const char *skillArg, - const char *modelArg, const char *nameArg); - - // --------------------- - void UpdateGameStatus(); // Updates global game variables - bool isC4Dropped(); - bool isPlantedC4Discovered(); - - // --------------------- - // public variables - int iDefaultBotSkill; - int iRandomMinSkill; - int iRandomMaxSkill; - int iOverrideBotSkill; // Override "game botskill" with personality skill? - float fWalkWithKnife; // May bots walk with knife - - // Game related variables: - Vector vDroppedC4; // Dropped C4? - bool bBombPlanted; // Bomb planted? - bool bHostageRescueMap; // Hostage rescue map? (CS_...) - bool bHostageRescueZoneFound; // Is any rescue zone found? (CS_...) - bool bBombPlantMap; // Bomb plant map? (DE_...) - Vector vPlantedC4; // Is the bomb discovered? - - // Server vars - int iVersionBroadcasting; // 0 = every round , 1 = every new map - int iKillsBroadcasting; // 0 = full, 1 = min, 2 = none - int iDeathsBroadcasting; // 0 = full, 1 = min, 2 = none - bool bInstalledCorrectly; // false = RB is not in the correct directory - bool bSpeechBroadcasting; // true/false - - // DEBUG variables - bool bDoNotShoot; // Bots not shooting - int bDebug; // Print debug messages (if > -1, it prints messages for bot index...) - int messageVerbosity; // Print debug messages (verbosity) - bool bEngineDebug; // Print engine debug messages - bool bPistols; // 30/07/04 by Josh: bots will only use pistols - - // ChatEngine related - int iMaxSentences; // how many sentences may there be at max? - int iProducedSentences; + void Init(); + void InitNewRound(); + + // --------------------- + void LoadNames(); + static void LoadCFG(); + static void LoadBuyTable(); + + // --------------------- + std::string SelectName() const; + bool NamesAvailable() const; + void SetPlayingRounds(int iMin, int iMax); + void SetNewRound(bool bState); + void resetRoundTime(); + void SetRoundTime(float fTime); + static void DetermineMapGoal(); + + // --------------------- + const char* RandomSentence() const; + + // --------------------- + int GetMinPlayRounds() const; + int GetMaxPlayRounds() const; + + bool NewRound() const; // New round? + float getRoundStartedTime() const; // When did the round start? (time) + float getRoundTimeElapsed() const; // difference between now and round started time + + int createBot(edict_t* pPlayer, const char* teamArg, const char* skillArg, + const char* modelArg, const char* nameArg) const; + + // --------------------- + void UpdateGameStatus(); // Updates global game variables + bool isC4Dropped() const; + bool isPlantedC4Discovered() const; + + // --------------------- + // public variables + int iDefaultBotSkill; + int iRandomMinSkill; + int iRandomMaxSkill; + int iOverrideBotSkill; // Override "game botskill" with personality skill? + float fWalkWithKnife; // May bots walk with knife + + // Game related variables: + Vector vDroppedC4; // Dropped C4? + bool bBombPlanted; // Bomb planted? + bool bHostageRescueMap; // Hostage rescue map? (CS_...) + bool bHostageRescueZoneFound; // Is any rescue zone found? (CS_...) + bool bBombPlantMap; // Bomb plant map? (DE_...) + Vector vPlantedC4; // Is the bomb discovered? + + // Server vars + int iVersionBroadcasting; // 0 = every round , 1 = every new map + int iKillsBroadcasting; // 0 = full, 1 = min, 2 = none + int iDeathsBroadcasting; // 0 = full, 1 = min, 2 = none + bool bInstalledCorrectly; // false = RB is not in the correct directory + bool bSpeechBroadcasting; // true/false + + // DEBUG variables + bool bDoNotShoot; // Bots not shooting + int bDebug; // Print debug messages (if > -1, it prints messages for bot index...) + int messageVerbosity; // Print debug messages (verbosity) + bool bEngineDebug; // Print engine debug messages + bool bPistols; // 30/07/04 by Josh: bots will only use pistols + + // ChatEngine related + int iMaxSentences; // how many sentences may there be at max? + int iProducedSentences; private: - // --------------------- - // private variables - char cSpeechSentences[16][80]; - int iAmountNames; - char cBotNames[MAX_BOT_NAMES][BOT_NAME_LEN + 1]; - int iMinPlayRounds, iMaxPlayRounds; // Min/Max playable rounds - bool bNewRound; // New round triggered? - float fRoundTime; // Round time - float fUpdateGoalTimer; + // --------------------- + // private variables + std::array cSpeechSentences; + int iAmountNames = 0; + std::vector cBotNames; + int iMinPlayRounds = 0, iMaxPlayRounds = 0; // Min/Max playable rounds + bool bNewRound = false; // New round triggered? + float fRoundTime = 0.0f; // Round time + float fUpdateGoalTimer = 0.0f; }; #endif // GAME_H \ No newline at end of file diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..712f16e --- /dev/null +++ b/globals.h @@ -0,0 +1,60 @@ +// globals.h + +#ifndef GLOBALS_H +#define GLOBALS_H + +#include + +#include "bot.h" +#include "NodeMachine.h" +#include "ChatEngine.h" +#include "game.h" + +extern cGame Game; +extern cNodeMachine NodeMachine; +extern cChatEngine ChatEngine; +extern FILE* fpRblog; + +extern float f_load_time; +extern float f_minplayers_think; +extern int mod_id; +extern int m_spriteTexture; +extern bool isFakeClientCommand; +extern int fake_arg_count; +extern float bot_check_time; +extern int min_bots; +extern int max_bots; +extern int min_players; +extern int num_bots; +extern int prev_num_bots; +extern bool g_GameRules; +extern edict_t* clients[32]; +extern edict_t* pHostEdict; +extern float welcome_time; +extern bool welcome_sent; + +extern FILE* bot_cfg_fp; +extern bool need_to_open_cfg; +extern float bot_cfg_pause_time; +extern float respawn_time; +extern bool spawn_time_reset; + +extern int internet_max_interval; +extern int internet_min_interval; + +extern int counterstrike; + +extern bool end_round; + +extern bool autoskill; +extern bool draw_nodes; +extern int draw_nodepath; +extern bool draw_connodes; +extern int kick_amount_bots; +extern int kick_bots_team; + +extern bool internet_addbot; +extern float add_timer; +extern bool internet_play; + +#endif // GLOBALS_H diff --git a/makeAndCopyAndRun.sh b/makeAndCopyAndRun.sh index c64178c..817fda1 100755 --- a/makeAndCopyAndRun.sh +++ b/makeAndCopyAndRun.sh @@ -17,7 +17,9 @@ touch "/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half- # run #"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map cs_italy +maxplayers 32 #"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map de_prodigy +maxplayers 32 -"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map de_dust +maxplayers 32 -#"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map as_oilrig +maxplayers 32 +#"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map de_dust +maxplayers 32 + +"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map as_oilrig +maxplayers 32 + #"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map fy_iceworld +maxplayers 32 #"/Users/shendriks/Library/Application Support/Steam/SteamApps/common/Half-Life/hl.sh" -game cstrike --listen +map cs_assault +maxplayers 32 diff --git a/realbot_mm.dsp b/realbot_mm.dsp deleted file mode 100644 index 661e798..0000000 --- a/realbot_mm.dsp +++ /dev/null @@ -1,187 +0,0 @@ -# Microsoft Developer Studio Project File - Name="realbot_mm" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=realbot_mm - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "realbot_mm.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "realbot_mm.mak" CFG="realbot_mm - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "realbot_mm - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName ""$/SDKSrc/Public/dlls", NVGBAAAA" -# PROP Scc_LocalPath "." -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\Release" -# PROP BASE Intermediate_Dir ".\Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\Release" -# PROP Intermediate_Dir ".\Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /G5 /MT /W3 /WX /GX /Zi /O2 /I "..\metamod" /I "..\..\devtools\sdk\Single-Player Source\common" /I "..\..\devtools\sdk\Single-Player Source\dlls" /I "..\..\devtools\sdk\Single-Player Source\engine" /I "..\..\devtools\sdk\Single-Player Source\pm_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Fr /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /def:".\realbot_mm.def" -# SUBTRACT LINK32 /profile /incremental:yes /map -# Begin Custom Build -TargetPath=.\Release\realbot_mm.dll -TargetName=realbot_mm -InputPath=.\Release\realbot_mm.dll -SOURCE="$(InputPath)" - -"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetPath) c:\progra~1\steam\steamapps\s.hendriks2@chello.nl\counter-strike\realbot\dll\realbot_mm.dll - copy $(TargetPath) c:\progra~1\steam\steamapps\s.hendriks2@chello.nl\dedica~1\realbot\dll\realbot_mm.dll - copy $(TargetPath) c:\progra~1\half-life\realbot\dll\realbot_mm.dll - copy $(TargetPath) d:\games\half-life\realbot\dll\realbot_mm.dll - -# End Custom Build -# Begin Target - -# Name "realbot_mm - Win32 Release" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90" -# Begin Group "BAL" - -# PROP Default_Filter "" -# Begin Group "Halflife" - -# PROP Default_Filter "" -# Begin Group "Metamod" - -# PROP Default_Filter "" -# End Group -# End Group -# End Group -# Begin Source File - -SOURCE=.\bot.cpp -# End Source File -# Begin Source File - -SOURCE=.\bot_buycode.cpp -# End Source File -# Begin Source File - -SOURCE=.\bot_client.cpp -# End Source File -# Begin Source File - -SOURCE=.\bot_func.cpp -# End Source File -# Begin Source File - -SOURCE=.\bot_navigate.cpp -# End Source File -# Begin Source File - -SOURCE=.\build.cpp -# End Source File -# Begin Source File - -SOURCE=.\ChatEngine.cpp -# End Source File -# Begin Source File - -SOURCE=.\dll.cpp -# End Source File -# Begin Source File - -SOURCE=.\engine.cpp -# End Source File -# Begin Source File - -SOURCE=.\game.cpp -# End Source File -# Begin Source File - -SOURCE=.\IniParser.cpp -# End Source File -# Begin Source File - -SOURCE=.\NodeMachine.cpp -# End Source File -# Begin Source File - -SOURCE=.\util.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" -# Begin Source File - -SOURCE=.\BAL\BAL_Bot.h -# End Source File -# Begin Source File - -SOURCE=.\bot.h -# End Source File -# Begin Source File - -SOURCE=.\bot_client.h -# End Source File -# Begin Source File - -SOURCE=.\bot_func.h -# End Source File -# Begin Source File - -SOURCE=.\bot_weapons.h -# End Source File -# Begin Source File - -SOURCE=.\ChatEngine.h -# End Source File -# Begin Source File - -SOURCE=.\engine.h -# End Source File -# Begin Source File - -SOURCE=.\game.h -# End Source File -# Begin Source File - -SOURCE=.\IniParser.h -# End Source File -# Begin Source File - -SOURCE=.\NodeMachine.h -# End Source File -# End Group -# Begin Source File - -SOURCE=.\todo.txt -# End Source File -# End Target -# End Project diff --git a/realbot_mm.dsw b/realbot_mm.dsw deleted file mode 100644 index 1645c6f..0000000 --- a/realbot_mm.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "realbot_mm"=".\realbot_mm.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/realbot_mm.vcproj b/realbot_mm.vcproj deleted file mode 100644 index 8356a27..0000000 --- a/realbot_mm.vcproj +++ /dev/null @@ -1,345 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/realbot_mm.vcxproj b/realbot_mm.vcxproj index 288bf2d..1ef8956 100644 --- a/realbot_mm.vcxproj +++ b/realbot_mm.vcxproj @@ -8,14 +8,15 @@ {0AE0F290-F98C-402E-9F35-329A88708328} - 8.1 + 10.0 DynamicLibrary - v140 + v145 false MultiByte + false @@ -50,8 +51,8 @@ MaxSpeed OnlyExplicitInline - C:\projects\RealBot\dependencies\hlsdk\dlls;C:\projects\RealBot\dependencies\hlsdk\common;C:\projects\RealBot\dependencies\hlsdk\pm_shared;C:\projects\RealBot\dependencies\hlsdk\engine;C:\projects\RealBot\dependencies\hlsdk\public;C:\projects\RealBot\dependencies\metamod-hl1\metamod;%(AdditionalIncludeDirectories) - NDEBUG;WIN32;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + .\dependencies\hlsdk\dlls;.\dependencies\hlsdk\common;.\dependencies\hlsdk\pm_shared;.\dependencies\hlsdk\engine;.\dependencies\hlsdk\public;.\dependencies\metamod-hl1\metamod;%(AdditionalIncludeDirectories) + NDEBUG;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true MultiThreaded true @@ -61,10 +62,11 @@ .\Release/ true .\Release/ - Level2 + Level4 false true ProgramDatabase + stdcpp17 NDEBUG;%(PreprocessorDefinitions) @@ -110,6 +112,7 @@ + diff --git a/realbot_mm.vcxproj.filters b/realbot_mm.vcxproj.filters index 500cd92..ffb14df 100644 --- a/realbot_mm.vcxproj.filters +++ b/realbot_mm.vcxproj.filters @@ -82,6 +82,9 @@ Header Files + + Header Files + diff --git a/todo.txt b/todo.txt index 396be67..eadc9cf 100644 --- a/todo.txt +++ b/todo.txt @@ -1,11 +1,12 @@ - Bots may not camp when last in team (or second last?) -- Bots should know when others are near its waypoint, so it won't run into each other -- Bots should have better knowledge about goals -- Improve pathfinder -- Chat rate should be lower @ default -- Improve code in overall, ugly! - - -- improve the way bots walk path and decide it is a bad connection - - improve 'unstucking'... when not stuck by other players. - \ No newline at end of file +- Bots should know when others are near its waypoint, so it won't run into each other - Done but not fully tested! +- Bots should have better knowledge about goals (mainly for investigating which bomb site got the live C4 in and hostages) - Almost done! +- Improve pathfinder by properly removing excessive connections, especially on ones that have blocked pathways - Almost done! +- Allow bots to duck jump for vaulting upon crates or ledges, cs_italy, de_nuke and de_inferno are good examples - Almost done! +- Improve the way bots walk path and decide it is a bad connection +- Improve 'unstucking'... when not stuck by hostages and how to attack glass or vent grills in order to proceed. +- Allow bots to autovacate for human players +- To allow bots to really, I mean REALLY ignore nodes from VERY hard to reach places - including non-vaultable edges and tall crates - Almost done! +- Find an automated solution for removing redundant or excessive nodes that can make the connections less messy (if it doesn't already exist?) +- To add a cvar to toggle Reallog.txt on or off to reduce printing too many texts in the console and to prevent that txt file building up +- Ensure that the RealBot personality directories are correctly assigned \ No newline at end of file diff --git a/util.cpp b/util.cpp index 55fa17b..ca8ff9d 100644 --- a/util.cpp +++ b/util.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com /*** * * Copyright (c) 1999, Valve LLC. All rights reserved. @@ -21,7 +23,7 @@ ** * DISCLAIMER * - * History, Information & Credits: + * History, Information & Credits: * RealBot is based partially upon the HPB-Bot Template #3 by Botman * Thanks to Ditlew (NNBot), Pierre Marie Baty (RACCBOT), Tub (RB AI PR1/2/3) * Greg Slocum & Shivan (RB V1.0), Botman (HPB-Bot) and Aspirin (JOEBOT). And @@ -33,32 +35,35 @@ * * Pierre Marie Baty * Count-Floyd - * + * * !! BOTS-UNITED FOREVER !! - * + * * This project is open-source, it is protected under the GPL license; * By using this source-code you agree that you will ALWAYS release the * source-code with your project. * **/ -#include +#include +#include #include #include #include #include +#include "globals.h" + #include "bot.h" #include "bot_weapons.h" #include "bot_func.h" #include "game.h" #include "ChatEngine.h" -#include +#include -#ifndef _WIN32 -#define _snprintf snprintf -#endif +/*#ifndef _WIN32 +#define snprintf std::snprintf //-V1059 +#endif*/ extern int mod_id; extern cBot bots[32]; @@ -69,296 +74,281 @@ int gmsgTextMsg = 0; int gmsgSayText = 0; int gmsgShowMenu = 0; -Vector UTIL_VecToAngles(const Vector &vec) { - float rgflVecOut[3]; - VEC_TO_ANGLES(vec, rgflVecOut); - return Vector(rgflVecOut); +Vector UTIL_VecToAngles(const Vector& vec) { + float rgflVecOut[3]; + VEC_TO_ANGLES(vec, rgflVecOut); + return { rgflVecOut }; } // Overloaded to add IGNORE_GLASS void -UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, - IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, - edict_t *pentIgnore, TraceResult *ptr) { - TRACE_LINE(vecStart, vecEnd, - (igmon == - ignore_monsters ? TRUE : FALSE) | (ignoreGlass ? 0x100 : 0), - pentIgnore, ptr); +UTIL_TraceLine(const Vector& vecStart, const Vector& vecEnd, + const IGNORE_MONSTERS igmon, const IGNORE_GLASS ignoreGlass, + edict_t* pentIgnore, TraceResult* ptr) { + TRACE_LINE(vecStart, vecEnd, + (igmon == + ignore_monsters) | (ignoreGlass ? 0x100 : 0), + pentIgnore, ptr); } void -UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, - IGNORE_MONSTERS igmon, edict_t *pentIgnore, - TraceResult *ptr) { - TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), - pentIgnore, ptr); +UTIL_TraceLine(const Vector& vecStart, const Vector& vecEnd, + const IGNORE_MONSTERS igmon, edict_t* pentIgnore, + TraceResult* ptr) { + TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters), + pentIgnore, ptr); } void -UTIL_TraceHull(const Vector &vecStart, const Vector &vecEnd, - IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, - TraceResult *ptr) { - TRACE_HULL(vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), hullNumber, pentIgnore, ptr); +UTIL_TraceHull(const Vector& vecStart, const Vector& vecEnd, + const IGNORE_MONSTERS igmon, const int hullNumber, edict_t* pentIgnore,TraceResult* ptr) { + TRACE_HULL(vecStart, vecEnd, (igmon == ignore_monsters), hullNumber, pentIgnore, ptr); } -void UTIL_MakeVectors(const Vector &vecAngles) { - MAKE_VECTORS(vecAngles); +void UTIL_MakeVectors(const Vector& vecAngles) { + MAKE_VECTORS(vecAngles); } -edict_t *UTIL_FindEntityInSphere(edict_t *pentStart, - const Vector &vecCenter, float flRadius) { - edict_t *pentEntity; +edict_t* UTIL_FindEntityInSphere(edict_t* pentStart, + const Vector& vecCenter, const float flRadius) { + edict_t* pentEntity = FIND_ENTITY_IN_SPHERE(pentStart, vecCenter, flRadius); - pentEntity = FIND_ENTITY_IN_SPHERE(pentStart, vecCenter, flRadius); + if (!FNullEnt(pentEntity)) + return pentEntity; - if (!FNullEnt(pentEntity)) - return pentEntity; - - return NULL; + return nullptr; } -edict_t *UTIL_FindEntityByString(edict_t *pentStart, - const char *szKeyword, - const char *szValue) { - edict_t *pentEntity; - - pentEntity = FIND_ENTITY_BY_STRING(pentStart, szKeyword, szValue); +edict_t* UTIL_FindEntityByString(edict_t* pentStart, + const char* szKeyword, + const char* szValue) { + edict_t* pentEntity = FIND_ENTITY_BY_STRING(pentStart, szKeyword, szValue); - if (!FNullEnt(pentEntity)) - return pentEntity; - return NULL; + if (!FNullEnt(pentEntity)) + return pentEntity; + return nullptr; } -edict_t *UTIL_FindEntityByClassname(edict_t *pentStart, - const char *szName) { - return UTIL_FindEntityByString(pentStart, "classname", szName); +edict_t* UTIL_FindEntityByClassname(edict_t* pentStart, + const char* szName) { + return UTIL_FindEntityByString(pentStart, "classname", szName); } -edict_t *UTIL_FindEntityByTargetname(edict_t *pentStart, - const char *szName) { - return UTIL_FindEntityByString(pentStart, "targetname", szName); +edict_t* UTIL_FindEntityByTargetname(edict_t* pentStart, + const char* szName) { + return UTIL_FindEntityByString(pentStart, "targetname", szName); } -int UTIL_PointContents(const Vector &vec) { - return POINT_CONTENTS(vec); +int UTIL_PointContents(const Vector& vec) { + return POINT_CONTENTS(vec); } void -UTIL_SetSize(entvars_t *pev, const Vector &vecMin, const Vector &vecMax) { - SET_SIZE(ENT(pev), vecMin, vecMax); +UTIL_SetSize(entvars_t* pev, const Vector& vecMin, const Vector& vecMax) { + SET_SIZE(ENT(pev), vecMin, vecMax); } -void UTIL_SetOrigin(entvars_t *pev, const Vector &vecOrigin) { - SET_ORIGIN(ENT(pev), vecOrigin); +void UTIL_SetOrigin(entvars_t* pev, const Vector& vecOrigin) { + SET_ORIGIN(ENT(pev), vecOrigin); } -void ClientPrint(edict_t *pEntity, int msg_dest, const char *msg_name) { +void ClientPrint(edict_t* pEntity, const int msg_dest, const char* msg_name) { - if (gmsgTextMsg == 0) - gmsgTextMsg = REG_USER_MSG("TextMsg", -1); + if (gmsgTextMsg == 0) + gmsgTextMsg = REG_USER_MSG("TextMsg", -1); - MESSAGE_BEGIN(MSG_ONE, gmsgTextMsg, NULL, pEntity); + MESSAGE_BEGIN(MSG_ONE, gmsgTextMsg, nullptr, pEntity); - WRITE_BYTE(msg_dest); - WRITE_STRING(msg_name); - MESSAGE_END(); + WRITE_BYTE(msg_dest); + WRITE_STRING(msg_name); + MESSAGE_END(); } -void -UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1, - const char *param2, const char *param3, - const char *param4) { - if (gmsgTextMsg == 0) - gmsgTextMsg = REG_USER_MSG("TextMsg", -1); - - MESSAGE_BEGIN(MSG_ALL, gmsgTextMsg); - WRITE_BYTE(msg_dest); - WRITE_STRING(msg_name); - - if (param1) - WRITE_STRING(param1); - if (param2) - WRITE_STRING(param2); - if (param3) - WRITE_STRING(param3); - if (param4) - WRITE_STRING(param4); - - MESSAGE_END(); +void UTIL_ClientPrintAll(const int msg_dest, const char* msg_name, const char* param1, + const char* param2, const char* param3, + const char* param4) { + if (gmsgTextMsg == 0) + gmsgTextMsg = REG_USER_MSG("TextMsg", -1); + + MESSAGE_BEGIN(MSG_ALL, gmsgTextMsg); + WRITE_BYTE(msg_dest); + WRITE_STRING(msg_name); + + if (param1) + WRITE_STRING(param1); + if (param2) + WRITE_STRING(param2); + if (param3) + WRITE_STRING(param3); + if (param4) + WRITE_STRING(param4); + + MESSAGE_END(); } -void UTIL_SayText(const char *pText, edict_t *pEdict) { - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG("SayText", -1); +void UTIL_SayText(const char* pText, edict_t* pEdict) { + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG("SayText", -1); - MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pEdict); - WRITE_BYTE(ENTINDEX(pEdict)); - //if (mod_id == FRONTLINE_DLL) - // WRITE_SHORT(0); - WRITE_STRING(pText); - MESSAGE_END(); + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pEdict); + WRITE_BYTE(ENTINDEX(pEdict)); + //if (mod_id == FRONTLINE_DLL) + // WRITE_SHORT(0); + WRITE_STRING(pText); + MESSAGE_END(); } -void UTIL_HostSay(edict_t *pEntity, int teamonly, char *message) { - int j; - char text[128]; - char *pc; - int sender_team, player_team; - edict_t *client; - - // make sure the text has content - for (pc = message; pc != NULL && *pc != 0; pc++) { - if (isprint(*pc) && !isspace(*pc)) { - pc = NULL; // we've found an alphanumeric character, so text is valid - break; - } - } - - if (pc != NULL) - return; // no character found, so say nothing - - // turn on color set 2 (color on, no sound) - if (teamonly) - sprintf(text, "%c(TEAM) %s: ", 2, STRING(pEntity->v.netname)); - else - sprintf(text, "%c%s: ", 2, STRING(pEntity->v.netname)); - - j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator - if ((int) strlen(message) > j) - message[j] = 0; - - strcat(text, message); - strcat(text, "\n"); - - // loop through all players - // Start with the first player. - // This may return the world in single player if the client types something between levels or during spawn - // so check it, or it will infinite loop - - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG("SayText", -1); - - sender_team = UTIL_GetTeam(pEntity); - - client = NULL; - while (((client = UTIL_FindEntityByClassname(client, "player")) != NULL) - && (!FNullEnt(client))) { - if (client == pEntity) // skip sender of message - continue; - - player_team = UTIL_GetTeam(client); - - if (teamonly && (sender_team != player_team)) - continue; - - MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, client); - WRITE_BYTE(ENTINDEX(pEntity)); - //if (mod_id == FRONTLINE_DLL) - // WRITE_SHORT(0); - WRITE_STRING(text); - MESSAGE_END(); - } - - // print to the sending client - MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pEntity); - WRITE_BYTE(ENTINDEX(pEntity)); - //if (mod_id == FRONTLINE_DLL) - // WRITE_SHORT(0); - WRITE_STRING(text); - MESSAGE_END(); - - // echo to server console - g_engfuncs.pfnServerPrint(text); +void UTIL_HostSay(edict_t* pEntity, const int teamonly, char* message) { + char text[128]; + char* pc; + + // make sure the text has content + for (pc = message; pc != nullptr && *pc != 0; pc++) { + if (isprint(*pc) && !isspace(*pc)) { + pc = nullptr; // we've found an alphanumeric character, so text is valid + break; + } + } + + if (pc != nullptr) + return; // no character found, so say nothing + + // turn on color set 2 (color on, no sound) + if (teamonly) + snprintf(text, sizeof(text), "%c(TEAM) %s: %s\n", 2, STRING(pEntity->v.netname), message); + else + snprintf(text, sizeof(text), "%c%s: %s\n", 2, STRING(pEntity->v.netname), message); + + if (const unsigned j = sizeof(text) - 2 - std::strlen(text); std::strlen(message) > j) + message[j] = 0; + + std::strcat(text, message); + std::strcat(text, "\n"); + + // loop through all players + // Start with the first player. + // This may return the world in single player if the client types something between levels or during spawn + // so check it, or it will infinite loop + + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG("SayText", -1); + + const int sender_team = UTIL_GetTeam(pEntity); + + edict_t* client = nullptr; + while ((client = UTIL_FindEntityByClassname(client, "player")) != nullptr + && !FNullEnt(client)) { + if (client == pEntity) // skip sender of message + continue; + + const int player_team = UTIL_GetTeam(client); + + if (teamonly && sender_team != player_team) + continue; + + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, client); + WRITE_BYTE(ENTINDEX(pEntity)); + //if (mod_id == FRONTLINE_DLL) + // WRITE_SHORT(0); + WRITE_STRING(text); + MESSAGE_END(); + } + + // print to the sending client + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pEntity); + WRITE_BYTE(ENTINDEX(pEntity)); + //if (mod_id == FRONTLINE_DLL) + // WRITE_SHORT(0); + WRITE_STRING(text); + MESSAGE_END(); + + // echo to server console + g_engfuncs.pfnServerPrint(text); } #ifdef DEBUG -edict_t *DBG_EntOfVars(const entvars_t * pev) { - if (pev->pContainingEntity != NULL) - return pev->pContainingEntity; - ALERT(at_console, - "entvars_t pContainingEntity is NULL, calling into engine"); - edict_t *pent = (*g_engfuncs.pfnFindEntityByVars) ((entvars_t *) pev); - if (pent == NULL) - ALERT(at_console, - "DAMN! Even the engine couldn't FindEntityByVars!"); - ((entvars_t *) pev)->pContainingEntity = pent; - return pent; +edict_t* DBG_EntOfVars(const entvars_t* pev) { + if (pev->pContainingEntity != NULL) + return pev->pContainingEntity; + ALERT(at_console, + "entvars_t pContainingEntity is NULL, calling into engine"); + edict_t* pent = (*g_engfuncs.pfnFindEntityByVars) ((entvars_t*)pev); + if (pent == NULL) + ALERT(at_console, + "DAMN! Even the engine couldn't FindEntityByVars!"); + ((entvars_t*)pev)->pContainingEntity = pent; + return pent; } #endif //DEBUG // Is the edict a vip or not? -bool UTIL_IsVip(edict_t *pEntity) { - if (mod_id != CSTRIKE_DLL) - return false; - else { - char *infobuffer; - char model_name[32]; +bool UTIL_IsVip(edict_t* pEntity) { + if (mod_id != CSTRIKE_DLL) + return false; + char model_name[32]; - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pEntity); - strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); + char* infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pEntity); + std::strcpy(model_name, g_engfuncs.pfnInfoKeyValue(infobuffer, "model")); - if (strcmp(model_name, "vip") == 0) // VIP - return true; - } + if (std::strcmp(model_name, "vip") == 0) // VIP + return true; - return false; + return false; } // return team number 0 through 3 based what MOD uses for team numbers -int UTIL_GetTeam(edict_t *pEntity) { - if (mod_id == CSTRIKE_DLL) { - char *infobuffer; - char model_name[32]; - - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pEntity); - strcpy(model_name, - (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); - - if ((strcmp(model_name, "terror") == 0) || // Phoenix Connektion - (strcmp(model_name, "arab") == 0) || // old L337 Krew - (strcmp(model_name, "leet") == 0) || // L337 Krew - (strcmp(model_name, "artic") == 0) || // Artic Avenger - (strcmp(model_name, "arctic") == 0) || // Artic Avenger - fix for arctic? - seemed a typo? - (strcmp(model_name, "guerilla") == 0)) // Gorilla Warfare - { - return 0; // team Terrorists - } else if ((strcmp(model_name, "urban") == 0) || // Seal Team 6 - (strcmp(model_name, "gsg9") == 0) || // German GSG-9 - (strcmp(model_name, "sas") == 0) || // UK SAS - (strcmp(model_name, "gign") == 0) || // French GIGN - (strcmp(model_name, "vip") == 0) || // VIP - (strcmp(model_name, "spetsnatz") == 0)) // CZ Spetsnatz - { - return 1; // team Counter-Terrorists - } - - return -1; // unknown team - } - - return -2; // unknown mod +int UTIL_GetTeam(edict_t* pEntity) { + if (mod_id == CSTRIKE_DLL) { + char model_name[32]; + + char* infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pEntity); + std::strcpy(model_name, + g_engfuncs.pfnInfoKeyValue(infobuffer, "model")); + + if (std::strcmp(model_name, "terror") == 0 || // Phoenix Connektion + std::strcmp(model_name, "arab") == 0 || // old L337 Krew + std::strcmp(model_name, "leet") == 0 || // L337 Krew + std::strcmp(model_name, "artic") == 0 || // Artic Avenger + std::strcmp(model_name, "arctic") == 0 || // Arctic Avenger - fix for arctic? - seemed a typo? + std::strcmp(model_name, "guerilla") == 0 || // Gorilla Warfare + std::strcmp(model_name, "militia") == 0) // CZ Militia + { + return 0; // team Terrorists + } + if (std::strcmp(model_name, "urban") == 0 || // Seal Team 6 + std::strcmp(model_name, "gsg9") == 0 || // German GSG-9 + std::strcmp(model_name, "sas") == 0 || // UK SAS + std::strcmp(model_name, "gign") == 0 || // French GIGN + std::strcmp(model_name, "vip") == 0 || // VIP + std::strcmp(model_name, "spetsnaz") == 0) // CZ Spetsnaz + { + return 1; // team Counter-Terrorists + } + + return -1; // unknown team + } + + return -2; // unknown mod } // return class number 0 through N -int UTIL_GetClass(edict_t *pEntity) { - char *infobuffer; - char model_name[32]; +int UTIL_GetClass(edict_t* pEntity) { + char model_name[32]; - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pEntity); - strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); + char* infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(pEntity); + std::strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); - return 0; + return 0; } -int UTIL_GetBotIndex(edict_t *pEdict) { - int index; - - for (index = 0; index < 32; index++) { - if (bots[index].pEdict == pEdict) { - return index; - } - } +int UTIL_GetBotIndex(edict_t* pEdict) { + for (int index = 0; index < 32; index++) { + if (bots[index].pEdict == pEdict) { + return index; + } + } - return -1; // return -1 if edict is not a bot + return -1; // return -1 if edict is not a bot } /** @@ -366,128 +356,121 @@ int UTIL_GetBotIndex(edict_t *pEdict) { * @param pEdict * @return */ -cBot *UTIL_GetBotPointer(edict_t *pEdict) { - int index; - - for (index = 0; index < 32; index++) { - if (bots[index].pEdict == pEdict) { - return (&bots[index]); - } - } +cBot* UTIL_GetBotPointer(edict_t* pEdict) { + for (cBot& bot : bots) + { + if (bot.pEdict == pEdict) { + return ⊥ + } + } - return NULL; // return NULL if edict is not a bot + return nullptr; // return NULL if edict is not a bot } -bool IsAlive(edict_t *pEdict) { - // FIX: Make sure the edict is valid and such, else return false: - return ((pEdict != NULL) && // VALID - (pEdict->v.deadflag == DEAD_NO) && // NOT DEAD - (pEdict->v.health > 0) && // ENOUGHT HEALTH - !(pEdict->v.flags & FL_NOTARGET) && // ? - (pEdict->v.takedamage != 0)); // CAN TAKE DAMAGE +bool IsAlive(edict_t* pEdict) { + // FIX: Make sure the edict is valid and such, else return false: + return pEdict != nullptr && // VALID + pEdict->v.deadflag == DEAD_NO && // NOT DEAD + pEdict->v.health > 0 && // ENOUGHT HEALTH + !(pEdict->v.flags & FL_NOTARGET) && // ? + pEdict->v.takedamage != 0.0f; // CAN TAKE DAMAGE } -bool FInViewCone(Vector *pOrigin, edict_t *pEdict) { - Vector2D vec2LOS; - float flDot; +bool FInViewCone(Vector* pOrigin, edict_t* pEdict) { + UTIL_MakeVectors(pEdict->v.angles); - UTIL_MakeVectors(pEdict->v.angles); + Vector2D vec2LOS = (*pOrigin - pEdict->v.origin).Make2D(); + vec2LOS = vec2LOS.Normalize(); - vec2LOS = (*pOrigin - pEdict->v.origin).Make2D(); - vec2LOS = vec2LOS.Normalize(); + const float flDot = DotProduct(vec2LOS, gpGlobals->v_forward.Make2D()); - flDot = DotProduct(vec2LOS, gpGlobals->v_forward.Make2D()); - - if (flDot > 0.50) // 60 degree field of view - { - return TRUE; - } else { - return FALSE; - } + if (flDot > 0.50f) // 60 degree field of view + { + return true; + } + return false; } // FVisible() -bool FVisible(const Vector &vecOrigin, edict_t *pEdict) { - TraceResult tr; - Vector vecLookerOrigin; +bool FVisible(const Vector& vecOrigin, edict_t* pEdict) { + TraceResult tr; - // look through caller's eyes - vecLookerOrigin = pEdict->v.origin + pEdict->v.view_ofs; + // look through caller's eyes + const Vector vecLookerOrigin = pEdict->v.origin + pEdict->v.view_ofs; - int bInWater = (UTIL_PointContents(vecOrigin) == CONTENTS_WATER); - int bLookerInWater = - (UTIL_PointContents(vecLookerOrigin) == CONTENTS_WATER); + const int bInWater = UTIL_PointContents(vecOrigin) == CONTENTS_WATER; + const int bLookerInWater = + UTIL_PointContents(vecLookerOrigin) == CONTENTS_WATER; - // don't look through water - if (bInWater != bLookerInWater) - return FALSE; + // don't look through water + if (bInWater != bLookerInWater) + return false; - UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, - ignore_glass, pEdict, &tr); + UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, + ignore_glass, pEdict, &tr); - if (tr.flFraction != 1.0) { - return FALSE; // Line of sight is not established - } else { - return TRUE; // line of sight is valid. - } + if (tr.flFraction != 1.0f) { + return false; // Line of sight is not established + } + return true; // line of sight is valid. } -Vector GetGunPosition(edict_t *pEdict) { - return (pEdict->v.origin + pEdict->v.view_ofs); +Vector GetGunPosition(edict_t* pEdict) { + return pEdict->v.origin + pEdict->v.view_ofs; } -void UTIL_SelectItem(edict_t *pEdict, char *item_name) { - /*BotDebug( item_name); */ - FakeClientCommand(pEdict, item_name, NULL, NULL); +void UTIL_SelectItem(edict_t* pEdict, const char* item_name) { + /*BotDebug( item_name); */ + FakeClientCommand(pEdict, item_name, nullptr, nullptr); } -Vector VecBModelOrigin(edict_t *pEdict) { - return pEdict->v.absmin + (pEdict->v.size * 0.5); +Vector VecBModelOrigin(edict_t* pEdict) { + return pEdict->v.absmin + (pEdict->v.size * 0.5f); } -void -UTIL_ShowMenu(edict_t *pEdict, int slots, int displaytime, bool needmore, - char *pText) { - if (gmsgShowMenu == 0) - gmsgShowMenu = REG_USER_MSG("ShowMenu", -1); +void UTIL_ShowMenu(edict_t* pEdict, const int slots, const int displaytime, const bool needmore, + const char* pText) { + if (gmsgShowMenu == 0) + gmsgShowMenu = REG_USER_MSG("ShowMenu", -1); - MESSAGE_BEGIN(MSG_ONE, gmsgShowMenu, NULL, pEdict); + MESSAGE_BEGIN(MSG_ONE, gmsgShowMenu, nullptr, pEdict); - WRITE_SHORT(slots); - WRITE_CHAR(displaytime); - WRITE_BYTE(needmore); - WRITE_STRING(pText); + WRITE_SHORT(slots); + WRITE_CHAR(displaytime); + WRITE_BYTE(needmore); + WRITE_STRING(pText); - MESSAGE_END(); + MESSAGE_END(); } -void UTIL_BuildFileName(char *filename, char *arg1, char *arg2) { +void UTIL_BuildFileName(char* filename, const char* arg1, const char* arg2) { - if (mod_id == VALVE_DLL) - strcpy(filename, "valve/"); + if (mod_id == VALVE_DLL) + std::strcpy(filename, "valve/"); - else if (mod_id == CSTRIKE_DLL) - strcpy(filename, "cstrike/"); + else if (mod_id == CSTRIKE_DLL) + std::strcpy(filename, "cstrike/"); - else { - filename[0] = 0; - return; - } + else { + filename[0] = 0; + return; + } - if ((arg1) && (*arg1) && (arg2) && (*arg2)) { - strcat(filename, arg1); - strcat(filename, "/"); - strcat(filename, arg2); - } else if ((arg1) && (*arg1)) { - strcat(filename, arg1); - } + if (arg1 && *arg1 && arg2 && *arg2) { + std::strcat(filename, arg1); + std::strcat(filename, "/"); + std::strcat(filename, arg2); + } + else if (arg1 && *arg1) { + std::strcat(filename, arg1); + } } // added by Tub // copies subdir/file to filename -void UTIL_BuildFileNameRB(char *subdir, char *filename) { +void UTIL_BuildFileNameRB(const char* subdir, char* filename) { #if DO_DEBUG != 0 - char *filenamedebug = filename; + char* filenamedebug = filename; #endif #ifdef _WIN32 #define S '\\' @@ -495,15 +478,15 @@ void UTIL_BuildFileNameRB(char *subdir, char *filename) { #define S '/' #endif - strcpy(filename, "realbot/"); - strcat(filename, subdir); - while (*filename) { - if ((*filename == '/') && (*filename == '\\')) - *filename = S; - filename++; - } + std::strcpy(filename, "realbot/"); + std::strcat(filename, subdir); + while (*filename) { + if ((*filename == '/') || (*filename == '\\')) + *filename = S; + filename++; + } #if DO_DEBUG != 0 - DebugOut(filenamedebug); + DebugOut(filenamedebug); #endif #undef S } @@ -512,529 +495,463 @@ void UTIL_BuildFileNameRB(char *subdir, char *filename) { // UTIL_LogPrintf - Prints a logged message to console. // Preceded by LOG: ( timestamp ) < message > //========================================================= -void UTIL_LogPrintf(char *fmt, ...) { - va_list argptr; - static char string[1024]; - - va_start(argptr, fmt); - vsprintf(string, fmt, argptr); - va_end(argptr); - - // Print to server console - ALERT(at_logged, "%s", string); -} - -void UTIL_BotPressKey(cBot *pBot, int type) { - if (type == IN_JUMP || type == IN_DUCK) { - if (pBot->isFreezeTime()) { - pBot->rprint_trace("UTIL_BotPressKey", "IN_JUMP or IN_DUCK press requested, but in freezetime - IGNORING"); - return; // do nothing when in freezetime - } - } - - if (type == IN_JUMP) { - if (pBot->f_may_jump_time > gpGlobals->time || // do nothing when we may not jump - pBot->f_camp_time > gpGlobals->time || // when camping do not jump - FUNC_IsOnLadder(pBot->pEdict) // don't jump from ladder - ) { - pBot->rprint_trace("UTIL_BotPressKey", - "IN_JUMP requested, but either camping, still jumping or on ladder - IGNORING"); - return; - } - } - - // KEY: Reload - if (type == IN_RELOAD) // when reloading, there is NO zooming (when holding a zoomable weapon or a sniper gun) - { - pBot->rprint_trace("UTIL_BotPressKey", "IN_RELOAD"); - if (FUNC_BotHoldsZoomWeapon(pBot) || UTIL_GiveWeaponType(pBot->current_weapon.iId) == SNIPER) - //pBot->zoomed = ZOOM_NONE; // not zoomed anymore - - // FIX: Do not let bots do anything with this weapon for 0.7 second. So the engine can - // update the information. - pBot->f_update_weapon_time = gpGlobals->time + 0.7; - } - // KEY: End - - switch (type) { - case IN_DUCK: - pBot->rprint_trace("UTIL_BotPressKey", "IN_DUCK"); - break; - case IN_JUMP: - pBot->rprint_trace("UTIL_BotPressKey", "IN_JUMP"); - break; - case IN_USE: - pBot->rprint_trace("UTIL_BotPressKey", "IN_USE"); - break; - case IN_RELOAD: - pBot->rprint_trace("UTIL_BotPressKey", "IN_RELOAD"); - break; - case IN_ATTACK: - pBot->rprint_trace("UTIL_BotPressKey", "IN_ATTACK"); - break; - case IN_RUN: - pBot->rprint_trace("UTIL_BotPressKey", "IN_RUN"); - break; - case IN_MOVELEFT: - pBot->rprint_trace("UTIL_BotPressKey", "IN_MOVELEFT"); - break; - case IN_MOVERIGHT: - pBot->rprint_trace("UTIL_BotPressKey", "IN_MOVERIGHT"); - break; - default: - char msg[255]; - sprintf(msg, "unknown key to print [%d]", type); - pBot->rprint_trace("UTIL_BotPressKey", msg); - break; - } - - pBot->pEdict->v.button |= type; -} - -int UTIL_GiveWeaponType(int weapon_id) { - int kind = NONE; - // 30/07/04 by Josh - // Use switch instead of multiple if - switch (weapon_id) { - case CS_WEAPON_KNIFE: - kind = KNIFE; - break; // Check1. Is it a knife? - case CS_WEAPON_FLASHBANG: - case CS_WEAPON_HEGRENADE: - case CS_WEAPON_SMOKEGRENADE: - kind = GRENADE; - break; // Check 2, is it a 'tool'? - case CS_WEAPON_P228: - case CS_WEAPON_ELITE: - case CS_WEAPON_USP: - case CS_WEAPON_GLOCK18: - case CS_WEAPON_DEAGLE: - case CS_WEAPON_FIVESEVEN: - kind = SECONDARY; - break; // Check 3, is it a secondary gun? - case CS_WEAPON_SCOUT: - case CS_WEAPON_SG550: - case CS_WEAPON_AWP: - case CS_WEAPON_G3SG1: - kind = SNIPER; - break; // Check 4, is it a sniper gun? - //31.08.04 Frashman: added to switch construct - case CS_WEAPON_SHIELD: - kind = SHIELD; // 08/07/04 - Also take shield into account - } - - // When the kind of weapon is still not found, its a primary (in CS) - if (kind == NONE) - kind = PRIMARY; - - if (weapon_id < 1) - kind = NONE; - - return kind; +void UTIL_LogPrintf(const char* fmt, ...) { + va_list argptr; + static char string[1024]; + + va_start(argptr, fmt); + vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + // Print to server console + ALERT(at_logged, "%s", string); +} + +void UTIL_BotPressKey(cBot* pBot, const int type) { + if (type == IN_JUMP || type == IN_DUCK) { + if (pBot->isFreezeTime()) { + pBot->rprint_trace("UTIL_BotPressKey", "IN_JUMP or IN_DUCK press requested, but in freezetime - IGNORING"); + return; // do nothing when in freezetime + } + } + + if (type == IN_JUMP) { + if (pBot->f_may_jump_time > gpGlobals->time || // do nothing when we may not jump + pBot->f_camp_time > gpGlobals->time || // when camping do not jump + FUNC_IsOnLadder(pBot->pEdict) // don't jump from ladder + ) { + pBot->rprint_trace("UTIL_BotPressKey", + "IN_JUMP requested, but either camping, still jumping or on ladder - IGNORING"); + return; + } + } + + // KEY: Reload + if (type == IN_RELOAD) // when reloading, there is NO zooming (when holding a zoomable weapon or a sniper gun) + { + pBot->rprint_trace("UTIL_BotPressKey", "IN_RELOAD"); + if (FUNC_BotHoldsZoomWeapon(pBot) || UTIL_GiveWeaponType(pBot->current_weapon.iId) == SNIPER) + //pBot->zoomed = ZOOM_NONE; // not zoomed anymore + + // FIX: Do not let bots do anything with this weapon for 0.7 second. So the engine can + // update the information. + pBot->f_update_weapon_time = gpGlobals->time + 0.7f; + } + // KEY: End + + switch (type) { + case IN_DUCK: + pBot->rprint_trace("UTIL_BotPressKey", "IN_DUCK"); + break; + case IN_JUMP: + pBot->rprint_trace("UTIL_BotPressKey", "IN_JUMP"); + break; + case IN_USE: + pBot->rprint_trace("UTIL_BotPressKey", "IN_USE"); + break; + case IN_RELOAD: + pBot->rprint_trace("UTIL_BotPressKey", "IN_RELOAD"); + break; + case IN_ATTACK: + pBot->rprint_trace("UTIL_BotPressKey", "IN_ATTACK"); + break; + case IN_RUN: + pBot->rprint_trace("UTIL_BotPressKey", "IN_RUN"); + break; + case IN_MOVELEFT: + pBot->rprint_trace("UTIL_BotPressKey", "IN_MOVELEFT"); + break; + case IN_MOVERIGHT: + pBot->rprint_trace("UTIL_BotPressKey", "IN_MOVERIGHT"); + break; + default: + char msg[255]; + snprintf(msg, sizeof(msg), "unknown key to print [%d]", type); + pBot->rprint_trace("UTIL_BotPressKey", msg); + break; + } + + pBot->pEdict->v.button |= type; +} + +int UTIL_GiveWeaponType(const int weapon_id) { + int kind = NONE; + // 30/07/04 by Josh + // Use switch instead of multiple if + switch (weapon_id) { + case CS_WEAPON_KNIFE: + kind = KNIFE; + break; // Check1. Is it a knife? + case CS_WEAPON_FLASHBANG: + case CS_WEAPON_HEGRENADE: + case CS_WEAPON_SMOKEGRENADE: + kind = GRENADE; + break; // Check 2, is it a 'tool'? + case CS_WEAPON_P228: + case CS_WEAPON_ELITE: + case CS_WEAPON_USP: + case CS_WEAPON_GLOCK18: + case CS_WEAPON_DEAGLE: + case CS_WEAPON_FIVESEVEN: + kind = SECONDARY; + break; // Check 3, is it a secondary gun? + case CS_WEAPON_SCOUT: + case CS_WEAPON_SG550: + case CS_WEAPON_AWP: + case CS_WEAPON_G3SG1: + kind = SNIPER; + break; // Check 4, is it a sniper gun? + //31.08.04 Frashman: added to switch construct + case CS_WEAPON_SHIELD: + kind = SHIELD; // 08/07/04 - Also take shield into account + } + + // When the kind of weapon is still not found, its a primary (in CS) + if (kind == NONE) + kind = PRIMARY; + + if (weapon_id < 1) + kind = NONE; + + return kind; } // Return weapon ID (depended on mod) -int UTIL_GiveWeaponId(const char *name) { - if (mod_id == CSTRIKE_DLL) { - if (strcmp(name, "weapon_knife") == 0) - return CS_WEAPON_KNIFE; - if (strcmp(name, "weapon_c4") == 0) - return CS_WEAPON_C4; - if (strcmp(name, "weapon_mp5navy") == 0) - return CS_WEAPON_MP5NAVY; - if (strcmp(name, "weapon_ak47") == 0) - return CS_WEAPON_AK47; - if (strcmp(name, "weapon_m3") == 0) - return CS_WEAPON_M3; - if (strcmp(name, "weapon_aug") == 0) - return CS_WEAPON_AUG; - if (strcmp(name, "weapon_sg552") == 0) - return CS_WEAPON_SG552; - if (strcmp(name, "weapon_m249") == 0) - return CS_WEAPON_M249; - if (strcmp(name, "weapon_xm1014") == 0) - return CS_WEAPON_XM1014; - if (strcmp(name, "weapon_p90") == 0) - return CS_WEAPON_P90; - if (strcmp(name, "weapon_tmp") == 0) - return CS_WEAPON_TMP; - if (strcmp(name, "weapon_m4a1") == 0) - return CS_WEAPON_M4A1; - if (strcmp(name, "weapon_awp") == 0) - return CS_WEAPON_AWP; - if (strcmp(name, "weapon_fiveseven") == 0) - return CS_WEAPON_FIVESEVEN; - if (strcmp(name, "weapon_ump45") == 0) - return CS_WEAPON_UMP45; - if (strcmp(name, "weapon_sg550") == 0) - return CS_WEAPON_SG550; - if (strcmp(name, "weapon_scout") == 0) - return CS_WEAPON_SCOUT; - if (strcmp(name, "weapon_mac10") == 0) - return CS_WEAPON_MAC10; - if (strcmp(name, "weapon_g3sg1") == 0) - return CS_WEAPON_G3SG1; - if (strcmp(name, "weapon_elite") == 0) - return CS_WEAPON_ELITE; - if (strcmp(name, "weapon_p228") == 0) - return CS_WEAPON_P228; - if (strcmp(name, "weapon_deagle") == 0) - return CS_WEAPON_DEAGLE; - if (strcmp(name, "weapon_usp") == 0) - return CS_WEAPON_USP; - if (strcmp(name, "weapon_glock18") == 0) - return CS_WEAPON_GLOCK18; - // Counter-Strike 1.6 - if (strcmp(name, "weapon_famas") == 0) - return CS_WEAPON_FAMAS; - if (strcmp(name, "weapon_galil") == 0) - return CS_WEAPON_GALIL; - - // TODO: Detect shield carrying. - // 06/07/04 - // Unconfirmed for handling shield - // 31.08.04 Frashman: moved shield string before unknown weapon, not after it - if (strcmp(name, "weapon_shield") == 0) - return CS_WEAPON_SHIELD; - - char buffer[80]; - _snprintf(buffer, 79, "UTIL_GiveWeaponId: Unknown weapon name %s\n", name); - rblog(buffer); - } - - return -1; +int UTIL_GiveWeaponId(const char* name) { + if (mod_id == CSTRIKE_DLL) { + if (name == nullptr) { + rblog("UTIL_GiveWeaponId: Unknown weapon name (null)\n"); + return -1; + } + + const std::string_view weaponName(name); + + if (weaponName == "weapon_knife") return CS_WEAPON_KNIFE; + if (weaponName == "weapon_c4") return CS_WEAPON_C4; + if (weaponName == "weapon_mp5navy") return CS_WEAPON_MP5NAVY; + if (weaponName == "weapon_ak47") return CS_WEAPON_AK47; + if (weaponName == "weapon_m3") return CS_WEAPON_M3; + if (weaponName == "weapon_aug") return CS_WEAPON_AUG; + if (weaponName == "weapon_sg552") return CS_WEAPON_SG552; + if (weaponName == "weapon_m249") return CS_WEAPON_M249; + if (weaponName == "weapon_xm1014") return CS_WEAPON_XM1014; + if (weaponName == "weapon_p90") return CS_WEAPON_P90; + if (weaponName == "weapon_tmp") return CS_WEAPON_TMP; + if (weaponName == "weapon_m4a1") return CS_WEAPON_M4A1; + if (weaponName == "weapon_awp") return CS_WEAPON_AWP; + if (weaponName == "weapon_fiveseven") return CS_WEAPON_FIVESEVEN; + if (weaponName == "weapon_ump45") return CS_WEAPON_UMP45; + if (weaponName == "weapon_sg550") return CS_WEAPON_SG550; + if (weaponName == "weapon_scout") return CS_WEAPON_SCOUT; + if (weaponName == "weapon_mac10") return CS_WEAPON_MAC10; + if (weaponName == "weapon_g3sg1") return CS_WEAPON_G3SG1; + if (weaponName == "weapon_elite") return CS_WEAPON_ELITE; + if (weaponName == "weapon_p228") return CS_WEAPON_P228; + if (weaponName == "weapon_deagle") return CS_WEAPON_DEAGLE; + if (weaponName == "weapon_usp") return CS_WEAPON_USP; + if (weaponName == "weapon_glock18") return CS_WEAPON_GLOCK18; + if (weaponName == "weapon_famas") return CS_WEAPON_FAMAS; + if (weaponName == "weapon_galil") return CS_WEAPON_GALIL; + if (weaponName == "weapon_shield") return CS_WEAPON_SHIELD; + + char buffer[80]; + snprintf(buffer, sizeof(buffer) - 1, "UTIL_GiveWeaponId: Unknown weapon name %s\n", name); + rblog(buffer); + } + + return -1; } // Return weapon ID (depended on mod) -char *UTIL_GiveWeaponName(int id) { - if (mod_id == CSTRIKE_DLL) { - switch (id) { - case CS_WEAPON_C4: - return "weapon_c4"; - break; - case CS_WEAPON_MP5NAVY: - return "weapon_mp5navy"; - break; - case CS_WEAPON_AK47: - return "weapon_ak47"; - break; - case CS_WEAPON_M3: - return "weapon_m3"; - break; - case CS_WEAPON_AUG: - return "weapon_aug"; - break; - case CS_WEAPON_SG552: - return "weapon_sg552"; - break; - case CS_WEAPON_M249: - return "weapon_m249"; - break; - case CS_WEAPON_XM1014: - return "weapon_xm1014"; - break; - case CS_WEAPON_P90: - return "weapon_p90"; - break; - case CS_WEAPON_TMP: - return "weapon_tmp"; - break; - case CS_WEAPON_M4A1: - return "weapon_m4a1"; - break; - case CS_WEAPON_AWP: - return "weapon_awp"; - break; - case CS_WEAPON_FIVESEVEN: - return "weapon_fiveseven"; - break; - case CS_WEAPON_UMP45: - return "weapon_ump45"; - break; - case CS_WEAPON_SG550: - return "weapon_ag550"; - break; - case CS_WEAPON_SCOUT: - return "weapon_scout"; - break; - case CS_WEAPON_MAC10: - return "weapon_mac10"; - break; - case CS_WEAPON_G3SG1: - return "weapon_g3sg1"; - break; - case CS_WEAPON_ELITE: - return "weapon_elite"; - break; - case CS_WEAPON_P228: - return "weapon_p228"; - break; - case CS_WEAPON_DEAGLE: - return "weapon_deagle"; - break; - case CS_WEAPON_USP: - return "weapon_usp"; - break; - case CS_WEAPON_GLOCK18: - return "weapon_glock18"; - break; - - // Counter-Strike 1.6 - case CS_WEAPON_FAMAS: - return "weapon_famas"; - break; - case CS_WEAPON_GALIL: - return "weapon_galil"; - break; - - // Unconfirmed shield - case CS_WEAPON_SHIELD: - return "weapon_shield"; - break; - } - } - - return "weapon_knife"; // return knife, always good ;) +const char* UTIL_GiveWeaponName(const int id) { + if (mod_id == CSTRIKE_DLL) { + switch (id) { + case CS_WEAPON_C4: + return "weapon_c4"; + case CS_WEAPON_MP5NAVY: + return "weapon_mp5navy"; + case CS_WEAPON_AK47: + return "weapon_ak47"; + case CS_WEAPON_M3: + return "weapon_m3"; + case CS_WEAPON_AUG: + return "weapon_aug"; + case CS_WEAPON_SG552: + return "weapon_sg552"; + case CS_WEAPON_M249: + return "weapon_m249"; + case CS_WEAPON_XM1014: + return "weapon_xm1014"; + case CS_WEAPON_P90: + return "weapon_p90"; + case CS_WEAPON_TMP: + return "weapon_tmp"; + case CS_WEAPON_M4A1: + return "weapon_m4a1"; + case CS_WEAPON_AWP: + return "weapon_awp"; + case CS_WEAPON_FIVESEVEN: + return "weapon_fiveseven"; + case CS_WEAPON_UMP45: + return "weapon_ump45"; + case CS_WEAPON_SG550: + return "weapon_ag550"; + case CS_WEAPON_SCOUT: + return "weapon_scout"; + case CS_WEAPON_MAC10: + return "weapon_mac10"; + case CS_WEAPON_G3SG1: + return "weapon_g3sg1"; + case CS_WEAPON_ELITE: + return "weapon_elite"; + case CS_WEAPON_P228: + return "weapon_p228"; + case CS_WEAPON_DEAGLE: + return "weapon_deagle"; + case CS_WEAPON_USP: + return "weapon_usp"; + case CS_WEAPON_GLOCK18: + return "weapon_glock18"; + + // Counter-Strike 1.6 + case CS_WEAPON_FAMAS: + return "weapon_famas"; + case CS_WEAPON_GALIL: + return "weapon_galil"; + + // Unconfirmed shield + case CS_WEAPON_SHIELD: + return "weapon_shield"; + } + } + + return "weapon_knife"; // return knife, always good ;) } // Thanks Botman for this code (from forum). -void UTIL_BotSprayLogo(edict_t *pEntity, char *logo_name) { - int index; - TraceResult pTrace; - Vector v_src, v_dest; - UTIL_MakeVectors(pEntity->v.v_angle); - v_src = pEntity->v.origin + pEntity->v.view_ofs; - v_dest = v_src + gpGlobals->v_forward * 80; - UTIL_TraceLine(v_src, v_dest, ignore_monsters, - pEntity->v.pContainingEntity, &pTrace); - - index = DECAL_INDEX(logo_name); - - if (index < 0) - return; - - if ((pTrace.pHit) && (pTrace.flFraction < 1.0)) { - if (pTrace.pHit->v.solid != SOLID_BSP) - return; - - MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); - - if (index > 255) { - WRITE_BYTE(TE_WORLDDECALHIGH); - index -= 256; - } else - WRITE_BYTE(TE_WORLDDECAL); - - WRITE_COORD(pTrace.vecEndPos.x); - WRITE_COORD(pTrace.vecEndPos.y); - WRITE_COORD(pTrace.vecEndPos.z); - WRITE_BYTE(index); - - MESSAGE_END(); - - EMIT_SOUND_DYN2(pEntity, CHAN_VOICE, "player/sprayer.wav", 1.0, - ATTN_NORM, 0, 100); - } +void UTIL_BotSprayLogo(edict_t* pEntity, const char* logo_name) { + TraceResult pTrace; + UTIL_MakeVectors(pEntity->v.v_angle); + const Vector v_src = pEntity->v.origin + pEntity->v.view_ofs; + const Vector v_dest = v_src + gpGlobals->v_forward * 80; + UTIL_TraceLine(v_src, v_dest, ignore_monsters, + pEntity->v.pContainingEntity, &pTrace); + + int index = DECAL_INDEX(logo_name); + + if (index < 0) + return; + + if ((pTrace.pHit) && (pTrace.flFraction < 1.0f)) { + if (pTrace.pHit->v.solid != SOLID_BSP) + return; + + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); + + if (index > 255) { + WRITE_BYTE(TE_WORLDDECALHIGH); + index -= 256; + } + else + WRITE_BYTE(TE_WORLDDECAL); + + WRITE_COORD(pTrace.vecEndPos.x); + WRITE_COORD(pTrace.vecEndPos.y); + WRITE_COORD(pTrace.vecEndPos.z); + WRITE_BYTE(index); + + MESSAGE_END(); + + EMIT_SOUND_DYN2(pEntity, CHAN_VOICE, "player/sprayer.wav", 1.0f, + ATTN_NORM, 0, 100); + } } // Give a radio message botty boy! -void UTIL_BotRadioMessage(cBot *pBot, int radio, char *arg1, char *arg2) { - // To be sure the console will only change when we MAY change. - // The values will only be changed when console_nr is 0 - if (pBot->console_nr == 0) { - switch (radio) { - case 1: - strcpy(pBot->arg1, "radio1"); - break; - case 2: - strcpy(pBot->arg1, "radio2"); - break; - case 3: - strcpy(pBot->arg1, "radio3"); - break; - } - - strcpy(pBot->arg2, arg1); - strcpy(pBot->arg3, arg2); - pBot->console_nr = 1; // Begin message - - // 02/07/04 - pointed out, eliminate any possible 'devide by zero' - int iExtra = (100 / (pBot->ipCreateRadio + 1)); - if (iExtra > 30) - iExtra = 30; - pBot->fDoRadio = gpGlobals->time + iExtra; - } +void UTIL_BotRadioMessage(cBot* pBot, const int radio, const char* arg1, const char* arg2) { + // To be sure the console will only change when we MAY change. + // The values will only be changed when console_nr is 0 + if (pBot->console_nr == 0) { + switch (radio) { + case 1: + std::strcpy(pBot->arg1, "radio1"); + break; + case 2: + std::strcpy(pBot->arg1, "radio2"); + break; + case 3: + std::strcpy(pBot->arg1, "radio3"); + break; + } + + std::strcpy(pBot->arg2, arg1); + std::strcpy(pBot->arg3, arg2); + pBot->console_nr = 1; // Begin message + + // 02/07/04 - pointed out, eliminate any possible 'devide by zero' + int iExtra = 100 / (pBot->ipCreateRadio + 1); + iExtra = std::min(iExtra, 30); + pBot->fDoRadio = gpGlobals->time + static_cast(iExtra); + } } ////////////////////////////////// // UTIL_getGrenadeType function // - Stefan ////////////////////////////////// -int UTIL_GetGrenadeType(edict_t *pEntity) { - - const int length = 32; +int UTIL_GetGrenadeType(edict_t* pEntity) { + if (!pEntity || !pEntity->v.model) + { + return 0; + } - char model_name[length]; - memset(model_name, 0, sizeof(model_name)); + const std::string_view model_name(STRING(pEntity->v.model)); - strncpy(model_name, STRING(pEntity->v.model), length - 1); - model_name[length - 1] = 0; // Make sure it is NULL terminated + if (model_name == "models/w_hegrenade.mdl") return 1; // He grenade + if (model_name == "models/w_flashbang.mdl") return 2; // FlashBang + if (model_name == "models/w_smokegrenade.mdl") return 3; // SmokeGrenade + if (model_name == "models/w_c4.mdl") return 4; // C4 Explosive - if (strcmp(model_name, "models/w_hegrenade.mdl") == 0) return 1; // He grenade - if (strcmp(model_name, "models/w_flashbang.mdl") == 0) return 2; // FlashBang - if (strcmp(model_name, "models/w_smokegrenade.mdl") == 0) return 3; // SmokeGrenade - if (strcmp(model_name, "models/w_c4.mdl") == 0) return 4; // C4 Explosive + // when an empty string, let us know we missed something + if (model_name.empty()) { - char msg[512]; - memset(msg, 0, sizeof(msg)); + char msg[512] = {}; - // when non empty string, let us know we missed something - if (!strlen(model_name) == 0) { - sprintf(msg, "UTIL_GetGrenadeType unknown grenade model : %s\n", model_name); - } + snprintf(msg, sizeof(msg), "UTIL_GetGrenadeType unknown grenade model: %s\n", model_name.data() ? model_name.data() : "(null)"); + } - rblog(msg); - - return 0; + return 0; } // 2 functions from podbot source -unsigned short FixedUnsigned16(float value, float scale) { - int output; +unsigned short fixed_unsigned16(const float value, const float scale) { + int output = static_cast(value * scale); - output = (int) value * scale; - if (output < 0) - output = 0; - if (output > 0xFFFF) - output = 0xFFFF; + output = std::max(output, 0); + output = std::min(output, 0xFFFF); - return (unsigned short) output; + return static_cast(output); } -short FixedSigned16(float value, float scale) { - int output; - - output = (int) value * scale; - - if (output > 32767) - output = 32767; +short fixed_signed16(const float value, const float scale) { + int output = static_cast(value * scale); - if (output < -32768) - output = -32768; + output = std::min(output, 32767); + output = std::max(output, -32768); - return (short) output; + return static_cast(output); } // Using POD/SDK source to print nice messages on the client machine -void HUD_DrawString(int r, int g, int b, char *msg, edict_t *edict) { - // FROM PODBOT SOURCE - // Hacked together Version of HUD_DrawString - MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, edict); - WRITE_BYTE(TE_TEXTMESSAGE); - WRITE_BYTE(1); - WRITE_SHORT(FixedSigned16(-1, 1 << 13)); - WRITE_SHORT(FixedSigned16(0, 1 << 13)); - WRITE_BYTE(2); - WRITE_BYTE(r); //r - WRITE_BYTE(g); //g - WRITE_BYTE(b); //b - WRITE_BYTE(0); - WRITE_BYTE(255); - WRITE_BYTE(255); - WRITE_BYTE(255); - WRITE_BYTE(200); - WRITE_SHORT(FixedUnsigned16(0.0078125, 1 << 8)); - WRITE_SHORT(FixedUnsigned16(2, 1 << 8)); - WRITE_SHORT(FixedUnsigned16(6, 1 << 8)); - WRITE_SHORT(FixedUnsigned16(0.1, 1 << 8)); - WRITE_STRING((const char *) &msg[0]); - MESSAGE_END(); -} - - -void UTIL_FixAngles(Vector *Angles) { - if (Angles->x > 180.0) - Angles->x -= 360.0; - if (Angles->x < -180.0) - Angles->x += 360.0; - if (Angles->y > 180.0) - Angles->y -= 360.0; - if (Angles->y < -180.0) - Angles->y += 360.0; - - Angles->z = 0.0; -} - -void UTIL_SayTextBot(const char *pText, cBot *pBot) { - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG("SayText", -1); - - char szTemp[160]; - char szName[BOT_NAME_LEN + 1]; - int i = 0; - - // clear out - memset(szTemp, 0, sizeof(szTemp)); - memset(szName, 0, sizeof(szName)); - - // init - szTemp[0] = 2; - - int entind = ENTINDEX(pBot->pEdict); - - if (IsAlive(pBot->pEdict)) { - strcpy(szName, pBot->name); - for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - - // valid - if (pPlayer) - if (IsAlive(pPlayer)) // alive - { - MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pPlayer); - WRITE_BYTE(entind); - sprintf(&szTemp[1], "%s : %s", szName, pText); - WRITE_STRING(&szTemp[0]); - MESSAGE_END(); - } - } - } else { - strcpy(szName, pBot->name); - for (i = 1; i <= gpGlobals->maxClients; i++) { - edict_t *pPlayer = INDEXENT(i); - if (pPlayer) - if (!IsAlive(pPlayer)) { - MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pPlayer); - WRITE_BYTE(entind); - sprintf(&szTemp[1], "*DEAD*%s : %s", szName, pText); - WRITE_STRING(&szTemp[0]); - MESSAGE_END(); - } - } - } - - - // pass through on ChatEngine (not always) - if (RANDOM_LONG(0, 100) < 90) { - char chSentence[80]; - memset(chSentence, 0, sizeof(chSentence)); - - // copy pText to chSentence - strcpy(chSentence, pText); - - // pass through - ChatEngine.set_sentence(pBot->name, chSentence); - } +void HUD_DrawString(const int r, const int g, const int b, const char* msg, edict_t* edict) { + // FROM PODBOT SOURCE + // Hacked together Version of HUD_DrawString + MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, edict); + WRITE_BYTE(TE_TEXTMESSAGE); + WRITE_BYTE(1); + WRITE_SHORT(fixed_signed16(-1, 1 << 13)); + WRITE_SHORT(fixed_signed16(0, 1 << 13)); + WRITE_BYTE(2); + WRITE_BYTE(r); //r + WRITE_BYTE(g); //g + WRITE_BYTE(b); //b + WRITE_BYTE(0); + WRITE_BYTE(255); + WRITE_BYTE(255); + WRITE_BYTE(255); + WRITE_BYTE(200); + WRITE_SHORT(fixed_unsigned16(0.0078125, 1 << 8)); + WRITE_SHORT(fixed_unsigned16(2, 1 << 8)); + WRITE_SHORT(fixed_unsigned16(6, 1 << 8)); + WRITE_SHORT(fixed_unsigned16(0.1f, 1 << 8)); + WRITE_STRING(&msg[0]); + MESSAGE_END(); +} + + +void UTIL_FixAngles(Vector* Angles) { + if (Angles->x > 180.0f) + Angles->x -= 360.0f; + if (Angles->x < -180.0f) + Angles->x += 360.0f; + if (Angles->y > 180.0f) + Angles->y -= 360.0f; + if (Angles->y < -180.0f) + Angles->y += 360.0f; + + Angles->z = 0.0f; +} + +void UTIL_SayTextBot(const char* pText, cBot* pBot) { + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG("SayText", -1); + + char szTemp[160]; + char szName[BOT_NAME_LEN + 1]; + int i; + + // clear out + std::memset(szTemp, 0, sizeof(szTemp)); + std::memset(szName, 0, sizeof(szName)); + + // init + szTemp[0] = 2; + + const int entind = ENTINDEX(pBot->pEdict); + + if (IsAlive(pBot->pEdict)) { + std::strcpy(szName, pBot->name); + for (i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + + // valid + if (pPlayer) + if (IsAlive(pPlayer)) // alive + { + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pPlayer); + WRITE_BYTE(entind); + snprintf(&szTemp[1], sizeof(szTemp) - 1, "%s : %s", szName, pText); + WRITE_STRING(&szTemp[0]); + MESSAGE_END(); + } + } + } + else { + std::strcpy(szName, pBot->name); + for (i = 1; i <= gpGlobals->maxClients; i++) { + edict_t* pPlayer = INDEXENT(i); + if (pPlayer) + if (!IsAlive(pPlayer)) { + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pPlayer); + WRITE_BYTE(entind); + snprintf(&szTemp[1], sizeof(szTemp) - 1, "*DEAD*%s : %s", szName, pText); + WRITE_STRING(&szTemp[0]); + MESSAGE_END(); + } + } + } + + + // pass through on ChatEngine (not always) + if (RANDOM_LONG(0, 100) < 90) { + char chSentence[80] = {}; + + // copy pText to chSentence + std::strcpy(chSentence, pText); + + // pass through + ChatEngine.set_sentence(pBot->name, chSentence); + } } // This UTIL_SpeechSynth() is taken from the POD source. -void UTIL_SpeechSynth(edict_t *pEdict, char *szMessage) { - if (Game.bSpeechBroadcasting == false) - return; +void UTIL_SpeechSynth(edict_t* pEdict, const char* szMessage) { + if (Game.bSpeechBroadcasting == false) + return; - char szSpeak[128]; + char szSpeak[128]; - sprintf(szSpeak, "speak \"%s\"\n", szMessage); - CLIENT_COMMAND(pEdict, szSpeak); + snprintf(szSpeak, sizeof(szSpeak), "speak \"%s\"\n", szMessage); + CLIENT_COMMAND(pEdict, szSpeak); } // $Log: util.cpp,v $