diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4e807929..8217211ff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,9 @@ jobs: buildTests: 'Tests' steps: + - name: Configure + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Checkout uses: actions/checkout@v4 with: @@ -32,7 +35,7 @@ jobs: - name: Setup MSBuild uses: microsoft/setup-msbuild@v2 with: - vs-version: '16.8' + vs-version: '16' - name: Build and Run unittests run: | @@ -58,95 +61,72 @@ jobs: move msvc\${{ env.buildRelease }}\mp.pdb publish\debug\mp.pdb - name: Deploy artifacts - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4 with: name: win32 path: publish/* testdemos: name: 'Test demos' - runs-on: ubuntu-20.04 - container: s1lentq/testdemos:latest + runs-on: ubuntu-latest + container: rehldsorg/testdemos:latest needs: [windows] - env: - WINEDEBUG: -all - WINEDLLOVERRIDES: mshtml= - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - defaults: run: shell: bash - working-directory: ../../../opt/HLDS + working-directory: /opt/HLDS + + strategy: + fail-fast: false + matrix: + test: [ + { file: 'cstrike-basic-1', desc: 'CS: Testing jumping, scenarios, shooting etc' }, + ] steps: - name: Deploying windows artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: win32 - - name: Play demos + - name: Setup dependencies run: | chown root ~ rsync -a deps/regamedll/* . mv $GITHUB_WORKSPACE/tests/mp.dll cstrike/dlls/mp.dll - descs=( - "CS: Testing jumping, scenarios, shooting etc" - ) - - demos=( - "cstrike-basic-1" - ) - - retVal=0 - for i in "${!demos[@]}"; do - params=$(cat "testdemos/${demos[i]}.params") - - echo -e "\e[1m[$((i + 1))/${#demos[@]}] \e[1;36m${descs[i]} testing...\e[0m" - echo -e " - \e[0;33mParameters $params\e[0m" - - wine hlds.exe --rehlds-enable-all-hooks --rehlds-test-play "testdemos/${demos[i]}.bin" $params &> result.log || retVal=$? - - if [ $retVal -ne 777 ] && [ $retVal -ne 9 ]; then - # Print with catchy messages - while read line; do - echo -e " \e[0;33m$line" - done <<< $(cat result.log | sed '0,/demo failed/I!d;/wine:/d;/./,$!d') - - echo " 🔸 🔸 🔸 🔸 🔸 🔸 🔸 🔸 🔸 🔸" - while read line; do - echo -e " \e[1;31m$line"; - done < rehlds_demo_error.txt - echo -e " \e[30;41mExit code: $retVal\e[0m" - echo -e "\e[1m[$((i + 1))/${#demos[@]}] \e[1;36m${descs[i]} testing...\e[1;31m Failed ❌" - exit 6 # Test demo failed - else - # Print result HLDS console - while read line; do - echo -e " \e[0;33m$line" - done <<< $(cat result.log | sed '/wine:/d;/./,$!d') - echo -e " \e[30;43mExit code: $retVal\e[0m" - echo -e "\e[1m[$((i + 1))/${#demos[@]}] \e[1;36m${descs[i]} testing...\e[1;32m Succeed ✔" - fi - done + - name: Play test + env: + demo: ${{ matrix.test.file }} + desc: ${{ matrix.test.desc }} + run: ./runTest.sh linux: name: 'Linux' - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest + container: debian:11-slim steps: + - name: Install dependencies + run: | + dpkg --add-architecture i386 + apt-get update + apt-get install -y \ + gcc-multilib g++-multilib \ + build-essential \ + libc6-dev libc6-dev-i386 \ + git cmake rsync \ + g++ gcc + + - name: Configure + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Checkout uses: actions/checkout@v4 with: + submodules: recursive fetch-depth: 0 - submodules: true - - - name: Check dependencies - run: | - sudo dpkg --add-architecture i386 - sudo apt-get update - sudo apt-get install -y gcc-multilib g++-multilib - name: Build and Run unittests run: | @@ -171,7 +151,7 @@ jobs: fi shell: bash - - name: Build + - name: Build using GCC Compiler run: | rm -rf build && CC=gcc CXX=g++ cmake -B build && cmake --build build -j8 @@ -199,18 +179,12 @@ jobs: shell: bash - name: Deploy artifacts - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4 id: upload-job with: name: linux32 path: publish/* - - name: Cleanup temporary artifacts - if: success() && steps.upload-job.outcome == 'success' - run: | - rm -rf cssdk - rm -f appversion.h - publish: name: 'Publish' runs-on: ubuntu-latest @@ -218,12 +192,12 @@ jobs: steps: - name: Deploying linux artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: linux32 - name: Deploying windows artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: win32 @@ -264,8 +238,3 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.API_TOKEN }} - - name: Cleanup temporary artifacts - if: success() && steps.publish-job.outcome == 'success' - run: | - rm -rf bin dist debug cssdk - rm -f *.zip appversion.h diff --git a/README.md b/README.md index 90f58990d..a1198f080 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab | swapteams | Swap the teams and restart the game (1 sec delay to restart by default).
Args:
`0` - swap teams without restart.
`>0.001` - time delay in seconds to restart the round after swap. | | give | Give weapon command.
Args:

Usage:
`give weapon_ak47`
`give weapon_usp`

NOTE: `sv_cheats 1` required. | | impulse 255 | Give all weapons.

NOTE: `sv_cheats 1` required. | +| impulse 200 | Noclip with air acceleration.

NOTE: `sv_cheats 1` required. | ## Configuration (cvars)
@@ -97,6 +98,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab | mp_ct_give_player_knife | 1 | 0 | 1 | Whether Counter-Terrorist player spawn with knife. | | mp_ct_default_weapons_primary | "" | "" | - | The default primary (rifle) weapon that the CTs will spawn with. | | mp_ct_default_weapons_secondary | "usp" | "" | - | The default secondary (pistol) weapon that the CTs will spawn with. | +| mp_default_weapons_random | 0 | 0 | 1 | Randomize default weapons (if there are multiple).
`0` disabled
`1` enabled | | mp_give_player_c4 | 1 | 0 | 1 | Whether this map should spawn a C4 bomb for a player or not.
`0` disabled
`1` enabled | | mp_weapons_allow_map_placed | 1 | 0 | 1 | When set, map weapons (located on the floor by map) will be shown.
`0` hide all map weapons.
`1` enabled
`NOTE`: Effect will work after round restart. | | mp_free_armor | 0 | 0 | 2 | Give free armor on player spawn.
`0` disabled
`1` Give Kevlar
`2` Give Kevlar + Helmet | @@ -122,16 +124,25 @@ This means that plugins that do binary code analysis (Orpheu for example) probab | mp_ammo_respawn_time | 20 | 0.0 | - | The respawn time for ammunition. | | mp_vote_flags | km | 0 | - | Vote systems enabled in server.
`0` voting disabled
`k` votekick enabled via `vote` command
`m` votemap enabled via `votemap` command | | mp_votemap_min_time | 180 | 0.0 | - | Minimum seconds that must elapse on map before `votemap` command can be used. | +| mp_flymove_method | 0 | 0 | 1 | Set the method used for flymove calculations.
`0` default method
`1` alternative method (more accurate) | +| mp_stamina_restore_rate | 0 | 0.0 | - | Framerate (FPS), that used as reference when restoring stamina (fuser2) after jump. | +| mp_logkills | 1 | 0 | 1 | Log kills.
`0` disabled
`1` enabled | +| mp_jump_height | 45 | 0.0 | - | Player jump height. | +| bot_excellent_morale | 0 | 0 | 1 | Bots always have great morale regardless of defeat or victory. | +| mp_randomspawn | 0 | 0 | 1 | Random player spawns
`0` disabled
`1` enabled
`NOTE`: Navigation `maps/.nav` file required | +| mp_playerid_showhealth | 1 | 0 | 2 | Player ID display mode.
`0` don't show health
`1` show health for teammates only (default CS behaviour)
`2` show health for all players | +| mp_playerid_field | 3 | 0 | 3 | Player ID field display mode.
`0` don't show additional information
`1` show team name
`2` show health percentage
`3` show both team name and health percentage | +| mp_knockback | 170 | - | - | Knockback force applied to the victim when damaged by strong weapons (e.g. `AWP`, `AK47`).
Works only if not crouching, and not hit in the legs.
Set to `0` to disable. |
## How to install zBot for CS 1.6? * Extract all the files from an [archive](regamedll/extra/zBot/bot_profiles.zip?raw=true) -* Enter `-bots` option at the command line HLDS +* Enable CVar `bot_enable 1` in `cstrike/game_init.cfg` (if this config file does not exist, create it) -## How to install CSCZ hostage AI for CS 1.6? +## How to install CS:CZ hostage AI for CS 1.6? * Extract all the files from an [archive](regamedll/extra/HostageImprov/host_improv.zip?raw=true) -* Enter `-host-improv` option at the command line HLDS +* Enable CVar `hostage_ai_enable 1` in `cstrike/game_init.cfg` (if this config file does not exist, create it) ## Build instructions ### Checking requirements diff --git a/build.sh b/build.sh index 876b458da..6dbfad242 100755 --- a/build.sh +++ b/build.sh @@ -33,7 +33,7 @@ main() case "$C" in ("intel"|"icc") CC=icc CXX=icpc ;; ("gcc"|"g++") CC=gcc CXX=g++ ;; - ("clang|llvm") CC=clang CXX=clang++ ;; + ("clang"|"llvm") CC=clang CXX=clang++ ;; *) ;; esac diff --git a/dist/game.cfg b/dist/game.cfg index c5dad01c9..f566ee9ba 100644 --- a/dist/game.cfg +++ b/dist/game.cfg @@ -466,6 +466,13 @@ mp_ct_default_weapons_primary "" // Default value: "usp" mp_ct_default_weapons_secondary "usp" +// Randomize default weapons (if there are multiple) +// 0 - disabled (default behaviour) +// 1 - enabled +// +// Default value: "0" +mp_default_weapons_random "0" + // Give the player free armor on player spawn // 0 - No armor (default behavior) // 1 - Give Kevlar @@ -521,7 +528,7 @@ mp_give_c4_frags "3" // Default value: "1.0" mp_hostages_rescued_ratio "1.0" -// Legacy func_vehicle behavior when blocked by another entity. +// Legacy func_vehicle behavior when blocked by another entity. // New one is more useful for playing multiplayer. // // 0 - New behavior @@ -620,3 +627,63 @@ mp_vote_flags "km" // // Default value: "180" mp_votemap_min_time "180" + +// Set the method used for flymove calculations. +// 0 - default method +// 1 - alternative method (more accurate) +// +// Default value: "0" +mp_flymove_method "0" + +// Framerate (FPS), that is used as a reference when restoring stamina (fuser2) after a jump. +// 0 - disabled +// +// Default value: "0" +mp_stamina_restore_rate "0" + +// Player jump height +// +// Default value: "45" +mp_jump_height "45" + +// Bots always have great morale regardless of defeat or victory. +// 0 - disabled (default behaviour) +// 1 - enabled +// +// Default value: "0" +bot_excellent_morale "0" + +// Random player spawns +// 0 - disabled (default behaviour) +// 1 - enabled +// +// NOTE: Navigation "maps/.nav" file required +// +// Default value: "0" +mp_randomspawn "0" + +// Player ID display mode +// +// 0 - don't show health (default behaviour) +// 1 - show health for teammates only (default CS behaviour) +// 2 - show health for all players +// +// Default value: "1" +mp_playerid_showhealth "1" + +// Player ID field display mode +// +// 0 - don't show additional information +// 1 - show team name +// 2 - show health percentage +// 3 - show both team name and health percentage +// +// Default value: "3" +mp_playerid_field "3" + +// Knockback force applied to the victim when damaged by strong weapons (e.g. AWP, AK47). +// Works only if not crouching, and not hit in the legs. +// Set to "0" to disable. +// +// Default: "170" +mp_knockback "170" diff --git a/dist/game_init.cfg b/dist/game_init.cfg index 90e31b2d5..de6cb5326 100644 --- a/dist/game_init.cfg +++ b/dist/game_init.cfg @@ -1,3 +1,18 @@ +// Enables ZBots for the server +// NOTE: ZBots are always enabled on a listen server, regardless of this cvar +// 0 - disabled +// 1 - enabled +// +// Default value: "0" +bot_enable "0" + +// Enables the improve AI for hostages from CS:CZ +// 0 - disabled (classic hostage) +// 1 - enabled (improved hostage) +// +// Default value: "0" +hostage_ai_enable "0" + // Sets mins/maxs hull bounds for the player. // 0 - disabled (default behaviour, sets engine) // 1 - enabled (sets gamedll) diff --git a/regamedll/CMakeLists.txt b/regamedll/CMakeLists.txt index bc0a990e9..c1a13f9bd 100644 --- a/regamedll/CMakeLists.txt +++ b/regamedll/CMakeLists.txt @@ -20,20 +20,47 @@ cmake_minimum_required(VERSION 3.1) project(regamedll CXX) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + option(DEBUG "Build with debug information." OFF) option(USE_STATIC_LIBSTDC "Enables static linking libstdc++." OFF) option(USE_LEGACY_LIBC "Enables linking against legacy libc (<= 2.15) for compat with older distros (Debian 8/Ubuntu 16.04/Centos 7)." OFF) +option(XASH_COMPAT "Enable Xash3D FWGS compatiblity (support for it's 64-bit ABI support and crossplatform library suffix)") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Avoid -rdynamic -fPIC options -set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "") -set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +if (NOT XASH_COMPAT) + set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "") + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +endif() -set(COMPILE_FLAGS "-m32 -U_FORTIFY_SOURCE") -set(LINK_FLAGS "-m32 -s") -set(LINK_LIBS dl aelf32) +set(COMPILE_FLAGS "-U_FORTIFY_SOURCE") +set(LINK_LIBS dl) + +# do not strip debuginfo during link +if (NOT DEBUG) + set(LINK_FLAGS "-s") +endif() + +# add -m32 flag only on 64-bit systems, if building for 64-bit wasn't enabled with XASH_COMPAT +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + if (XASH_COMPAT) + set(COMPILE_FLAGS "${COMPILE_FLAGS} -DXASH_64BIT") + else() + set(COMPILE_FLAGS "${COMPILE_FLAGS} -m32") + set(LINK_FLAGS "${LINK_FLAGS} -m32") + list(APPEND LINK_LIBS aelf32) + set(CMAKE_SIZEOF_VOID_P 4) + endif() +endif() + +if(XASH_COMPAT) + # Xash3D FWGS Library Naming Scheme compliance + # see documentation: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/extensions/library-naming.md + include(LibraryNaming) +endif() set(COMPILE_FLAGS "${COMPILE_FLAGS} -Wall -fno-exceptions -fno-builtin -Wno-unknown-pragmas") @@ -47,7 +74,7 @@ else() endif() # Check Intel C++ compiler -if ("$ENV{CXX}" MATCHES "icpc") +if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") # # -fp-model=precise # ICC uses -fp-model fast=1 by default for more aggressive optimizations on floating-point calculations @@ -75,11 +102,15 @@ if ("$ENV{CXX}" MATCHES "icpc") set(COMPILE_FLAGS "${COMPILE_FLAGS} -ipo") set(LINK_FLAGS "${LINK_FLAGS} -ipo") endif() -else() - # Produce code optimized for the most common IA32/AMD64/EM64T processors. - # As new processors are deployed in the marketplace, the behavior of this option will change. +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (NOT XASH_COMPAT OR XASH_AMD64 OR XASH_X86) + # Produce code optimized for the most common IA32/AMD64/EM64T processors. + # As new processors are deployed in the marketplace, the behavior of this option will change. + set(COMPILE_FLAGS "${COMPILE_FLAGS} \ + -mtune=generic -msse3") + endif() + set(COMPILE_FLAGS "${COMPILE_FLAGS} \ - -mtune=generic -msse3\ -fpermissive -fno-sized-deallocation\ -Wno-delete-non-virtual-dtor -Wno-invalid-offsetof\ -Wno-unused-variable -Wno-unused-value -Wno-unused-result -Wno-unused-function\ @@ -87,7 +118,7 @@ else() -Wno-sign-compare -Wno-format -Wno-ignored-attributes -Wno-strict-aliasing") # Check Clang compiler - if ("$ENV{CXX}" MATCHES "clang") + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(COMPILE_FLAGS "${COMPILE_FLAGS} \ -Wno-unused-local-typedef\ -Wno-unused-private-field\ @@ -102,16 +133,11 @@ else() # GCC >= 8.3 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) - set(COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-stringop-truncation -Wno-format-truncation -Wno-class-memaccess") + set(COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-stringop-truncation -Wno-format-truncation -Wno-class-memaccess -fcf-protection=none") endif() endif() endif() -# GCC >= 8.3 -if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) - set(COMPILE_FLAGS "${COMPILE_FLAGS} -fcf-protection=none") -endif() - if (NOT DEBUG) set(LINK_FLAGS "${LINK_FLAGS} \ -Wl,-gc-sections -Wl,--version-script=\"${PROJECT_SOURCE_DIR}/../version_script.lds\"") @@ -378,14 +404,36 @@ if (USE_STATIC_LIBSTDC) set(LINK_FLAGS "${LINK_FLAGS} -static-libgcc -static-libstdc++") endif() -set(LINK_FLAGS "${LINK_FLAGS} \ - -Wl,-rpath,'$ORIGIN/.' \ - -L${PROJECT_SOURCE_DIR}/lib/linux32") +set(LINK_FLAGS "${LINK_FLAGS} -Wl,-rpath,'$ORIGIN/.'") + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(LINK_FLAGS "${LINK_FLAGS} -L${PROJECT_SOURCE_DIR}/lib/linux32") +endif() set_target_properties(regamedll PROPERTIES - OUTPUT_NAME cs - PREFIX "" COMPILE_FLAGS ${COMPILE_FLAGS} LINK_FLAGS ${LINK_FLAGS} - POSITION_INDEPENDENT_CODE OFF ) + +if (XASH_COMPAT) + if (CMAKE_SYSTEM_NAME STREQUAL "Android") + set_target_properties(regamedll PROPERTIES + OUTPUT_NAME server) + else() + set_target_properties(regamedll PROPERTIES + PREFIX "" + OUTPUT_NAME cs${POSTFIX}) + endif() +else() + set_target_properties(regamedll PROPERTIES + OUTPUT_NAME cs + PREFIX "" + POSITION_INDEPENDENT_CODE OFF) +endif() + +install(TARGETS regamedll + RUNTIME DESTINATION "cstrike/dlls/" + LIBRARY DESTINATION "cstrike/dlls/" + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) diff --git a/regamedll/cmake/LibraryNaming.cmake b/regamedll/cmake/LibraryNaming.cmake new file mode 100644 index 000000000..a20529aed --- /dev/null +++ b/regamedll/cmake/LibraryNaming.cmake @@ -0,0 +1,198 @@ +include(CheckSymbolExists) + +macro(check_build_target symbol) + check_symbol_exists(${symbol} "build.h" ${symbol}) +endmacro() + +macro(check_group_build_target symbol group) + if(NOT ${group}) + check_build_target(${symbol}) + if(${symbol}) + set(${group} TRUE) + endif() + else() + set(${symbol} FALSE) + endif() +endmacro() + +# So there is a problem: +# 1. Number of these symbols only grows, as we support more and more ports +# 2. CMake was written by morons and can't check these symbols in parallel +# 3. MSVC is very slow at everything (startup, parsing, generating error) + +# Solution: group these symbols and set variable if one of them was found +# this way we can reorder to reorder them by most common configurations +# but we can't generate this list anymore! ... OR IS IT ??? + +# Well, after reordering positions in engine's buildenums.h, we can partially autogenerate this list! +# echo "check_build_target(XASH_64BIT)" +# grep "#define PLATFORM" buildenums.h | cut -d' ' -f 2 | cut -d_ -f 2- | awk '{ print "check_group_build_target(XASH_" $1 " XASH_PLATFORM)" }' +# grep "#define ARCHITECTURE" buildenums.h | cut -d' ' -f 2 | cut -d_ -f 2- | awk '{ print "check_group_build_target(XASH_" $1 " XASH_ARCHITECTURE)" +# grep "#define ENDIAN" buildenums.h | cut -d' ' -f 2 | cut -d_ -f 2- | awk '{ print "check_group_build_target(XASH_" $1 "_ENDIAN XASH_ENDIANNESS)"}' +# echo "if(XASH_ARM)" +# grep '^#undef XASH' build.h | grep "XASH_ARM[v_]" | awk '{ print "check_build_target(" $2 ")"}' +# echo "endif()" +# echo "if(XASH_RISCV)" +# grep '^#undef XASH' build.h | grep "XASH_RISCV_" | awk '{ print "check_build_target(" $2 ")"}' +# echo "endif()" + +# NOTE: Android must have priority over Linux to work correctly! + +set(CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/public/") +check_build_target(XASH_64BIT) +check_group_build_target(XASH_WIN32 XASH_PLATFORM) +check_group_build_target(XASH_ANDROID XASH_PLATFORM) +check_group_build_target(XASH_LINUX XASH_PLATFORM) +check_group_build_target(XASH_FREEBSD XASH_PLATFORM) +check_group_build_target(XASH_APPLE XASH_PLATFORM) +check_group_build_target(XASH_NETBSD XASH_PLATFORM) +check_group_build_target(XASH_OPENBSD XASH_PLATFORM) +check_group_build_target(XASH_EMSCRIPTEN XASH_PLATFORM) +check_group_build_target(XASH_DOS4GW XASH_PLATFORM) +check_group_build_target(XASH_HAIKU XASH_PLATFORM) +check_group_build_target(XASH_SERENITY XASH_PLATFORM) +check_group_build_target(XASH_IRIX XASH_PLATFORM) +check_group_build_target(XASH_NSWITCH XASH_PLATFORM) +check_group_build_target(XASH_PSVITA XASH_PLATFORM) +check_group_build_target(XASH_WASI XASH_PLATFORM) +check_group_build_target(XASH_SUNOS XASH_PLATFORM) +check_group_build_target(XASH_X86 XASH_ARCHITECTURE) +check_group_build_target(XASH_AMD64 XASH_ARCHITECTURE) +check_group_build_target(XASH_ARM XASH_ARCHITECTURE) +check_group_build_target(XASH_MIPS XASH_ARCHITECTURE) +check_group_build_target(XASH_PPC XASH_ARCHITECTURE) +check_group_build_target(XASH_JS XASH_ARCHITECTURE) +check_group_build_target(XASH_E2K XASH_ARCHITECTURE) +check_group_build_target(XASH_RISCV XASH_ARCHITECTURE) +check_group_build_target(XASH_LITTLE_ENDIAN XASH_ENDIANNESS) +check_group_build_target(XASH_BIG_ENDIAN XASH_ENDIANNESS) +if(XASH_ARM) +check_build_target(XASH_ARM_HARDFP) +check_build_target(XASH_ARM_SOFTFP) +check_build_target(XASH_ARMv4) +check_build_target(XASH_ARMv5) +check_build_target(XASH_ARMv6) +check_build_target(XASH_ARMv7) +check_build_target(XASH_ARMv8) +endif() +if(XASH_RISCV) +check_build_target(XASH_RISCV_DOUBLEFP) +check_build_target(XASH_RISCV_SINGLEFP) +check_build_target(XASH_RISCV_SOFTFP) +endif() +unset(CMAKE_REQUIRED_INCLUDES) + +# engine/common/build.c +if(XASH_ANDROID) + set(BUILDOS "android") +elseif(XASH_WIN32 OR XASH_LINUX OR XASH_APPLE) + set(BUILDOS "") # no prefix for default OS +elseif(XASH_FREEBSD) + set(BUILDOS "freebsd") +elseif(XASH_NETBSD) + set(BUILDOS "netbsd") +elseif(XASH_OPENBSD) + set(BUILDOS "openbsd") +elseif(XASH_EMSCRIPTEN) + set(BUILDOS "emscripten") +elseif(XASH_DOS4GW) + set(BUILDOS "DOS4GW") +elseif(XASH_HAIKU) + set(BUILDOS "haiku") +elseif(XASH_SERENITY) + set(BUILDOS "serenityos") +elseif(XASH_NSWITCH) + set(BUILDOS "nswitch") +elseif(XASH_PSVITA) + set(BUILDOS "psvita") +elseif(XASH_IRIX) + set(BUILDOS "irix") +elseif(XASH_WASI) + set(BUILDOS "wasi") +elseif(XASH_SUNOS) + set(BUILDOS "sunos") +else() + message(SEND_ERROR "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug") +endif() + +if(XASH_AMD64) + set(BUILDARCH "amd64") +elseif(XASH_X86) + if(XASH_WIN32 OR XASH_LINUX OR XASH_APPLE) + set(BUILDARCH "") # no prefix for default OS + else() + set(BUILDARCH "i386") + endif() +elseif(XASH_ARM AND XASH_64BIT) + set(BUILDARCH "arm64") +elseif(XASH_ARM) + set(BUILDARCH "armv") + if(XASH_ARMv8) + set(BUILDARCH "${BUILDARCH}8_32") + elseif(XASH_ARMv7) + set(BUILDARCH "${BUILDARCH}7") + elseif(XASH_ARMv6) + set(BUILDARCH "${BUILDARCH}6") + elseif(XASH_ARMv5) + set(BUILDARCH "${BUILDARCH}5") + elseif(XASH_ARMv4) + set(BUILDARCH "${BUILDARCH}4") + else() + message(SEND_ERROR "Unknown ARM") + endif() + + if(XASH_ARM_HARDFP) + set(BUILDARCH "${BUILDARCH}hf") + else() + set(BUILDARCH "${BUILDARCH}l") + endif() +elseif(XASH_MIPS) + set(BUILDARCH "mips") + if(XASH_64BIT) + set(BUILDARCH "${BUILDARCH}64") + endif() + + if(XASH_LITTLE_ENDIAN) + set(BUILDARCH "${BUILDARCH}el") + endif() +elseif(XASH_RISCV) + set(BUILDARCH "riscv") + if(XASH_64BIT) + set(BUILDARCH "${BUILDARCH}64") + else() + set(BUILDARCH "${BUILDARCH}32") + endif() + + if(XASH_RISCV_DOUBLEFP) + set(BUILDARCH "${BUILDARCH}d") + elseif(XASH_RISCV_SINGLEFP) + set(BUILDARCH "${BUILDARCH}f") + endif() +elseif(XASH_JS) + set(BUILDARCH "javascript") +elseif(XASH_E2K) + set(BUILDARCH "e2k") +elseif(XASH_PPC) + set(BUILDARCH "ppc") + if(XASH_64BIT) + set(BUILDARCH "${BUILDARCH}64") + endif() + + if(XASH_LITTLE_ENDIAN) + set(BUILDARCH "${BUILDARCH}el") + endif() +else() + message(SEND_ERROR "Place your architecture name here! If this is a mistake, try to fix conditions above and report a bug") +endif() + +if(BUILDOS STREQUAL "android") + set(POSTFIX "") # force disable for Android, as Android ports aren't distributed in normal way and doesn't follow library naming +elseif(BUILDOS AND BUILDARCH) + set(POSTFIX "_${BUILDOS}_${BUILDARCH}") +elseif(BUILDARCH) + set(POSTFIX "_${BUILDARCH}") +else() + set(POSTFIX "") +endif() + +message(STATUS "Library postfix: " ${POSTFIX}) diff --git a/regamedll/dlls/API/CAPI_Impl.cpp b/regamedll/dlls/API/CAPI_Impl.cpp index 41175c838..ba40f1908 100644 --- a/regamedll/dlls/API/CAPI_Impl.cpp +++ b/regamedll/dlls/API/CAPI_Impl.cpp @@ -143,6 +143,66 @@ CGrenade *EXT_FUNC SpawnGrenade_api(WeaponIdType weaponId, entvars_t *pevOwner, return nullptr; } +int EXT_FUNC UTIL_SharedRandomLong_api(unsigned int seed, int low, int high) +{ + return UTIL_SharedRandomLong(seed, low, high); +} + +float EXT_FUNC UTIL_SharedRandomFloat_api(unsigned int seed, float low, float high) +{ + return UTIL_SharedRandomFloat(seed, low, high); +} + +void EXT_FUNC UTIL_SetGroupTrace_api(int groupmask, int op) +{ + UTIL_SetGroupTrace(groupmask, op); +} + +void EXT_FUNC UTIL_UnsetGroupTrace_api() +{ + UTIL_UnsetGroupTrace(); +} + +int EXT_FUNC UTIL_EntitiesInBox_api(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask) +{ + return UTIL_EntitiesInBox(pList, listMax, mins, maxs, flagMask); +} + +void EXT_FUNC UTIL_ScreenShake_api(const Vector ¢er, float amplitude, float frequency, float duration, float radius) +{ + UTIL_ScreenShake(center, amplitude, frequency, duration, radius); +} + +void EXT_FUNC UTIL_ScreenFadeAll_api(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) +{ + UTIL_ScreenFadeAll(color, fadeTime, fadeHold, alpha, flags); +} + +void EXT_FUNC UTIL_ScreenFade_api(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) +{ + UTIL_ScreenFade(pEntity, color, fadeTime, fadeHold, alpha, flags); +} + +float EXT_FUNC UTIL_WaterLevel_api(const Vector &position, float minz, float maxz) +{ + return UTIL_WaterLevel(position, minz, maxz); +} + +void EXT_FUNC UTIL_Bubbles_api(Vector mins, Vector maxs, int count) +{ + UTIL_Bubbles(mins, maxs, count); +} + +void EXT_FUNC UTIL_BubbleTrail_api(Vector from, Vector to, int count) +{ + UTIL_BubbleTrail(from, to, count); +} + +char EXT_FUNC UTIL_TextureHit_api(TraceResult *ptr, Vector vecSrc, Vector vecEnd) +{ + return UTIL_TextureHit(ptr, vecSrc, vecEnd); +} + ReGameFuncs_t g_ReGameApiFuncs = { CREATE_NAMED_ENTITY, @@ -176,6 +236,19 @@ ReGameFuncs_t g_ReGameApiFuncs = { TextureTypePlaySound_api, CreateWeaponBox_api, SpawnGrenade_api, + + UTIL_SharedRandomLong_api, + UTIL_SharedRandomFloat_api, + UTIL_SetGroupTrace_api, + UTIL_UnsetGroupTrace_api, + UTIL_EntitiesInBox_api, + UTIL_ScreenShake_api, + UTIL_ScreenFadeAll_api, + UTIL_ScreenFade_api, + UTIL_WaterLevel_api, + UTIL_Bubbles_api, + UTIL_BubbleTrail_api, + UTIL_TextureHit_api, }; GAMEHOOK_REGISTRY(CBasePlayer_Spawn); @@ -336,6 +409,8 @@ GAMEHOOK_REGISTRY(CSGameRules_SendDeathMessage); GAMEHOOK_REGISTRY(CBasePlayer_PlayerDeathThink); GAMEHOOK_REGISTRY(CBasePlayer_Observer_Think); GAMEHOOK_REGISTRY(CBasePlayer_RemoveAllItems); +GAMEHOOK_REGISTRY(CBasePlayer_UpdateStatusBar); +GAMEHOOK_REGISTRY(CBasePlayer_TakeDamageImpulse); int CReGameApi::GetMajorVersion() { return REGAMEDLL_API_VERSION_MAJOR; diff --git a/regamedll/dlls/API/CAPI_Impl.h b/regamedll/dlls/API/CAPI_Impl.h index 2635be5b9..727fdf9e4 100644 --- a/regamedll/dlls/API/CAPI_Impl.h +++ b/regamedll/dlls/API/CAPI_Impl.h @@ -749,6 +749,14 @@ typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBase typedef IHookChainClassImpl CReGameHook_CBasePlayer_RemoveAllItems; typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBasePlayer_RemoveAllItems; +// CBasePlayer::UpdateStatusBar hook +typedef IHookChainClassImpl CReGameHook_CBasePlayer_UpdateStatusBar; +typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBasePlayer_UpdateStatusBar; + +// CBasePlayer::TakeDamageImpulse hook +typedef IHookChainClassImpl CReGameHook_CBasePlayer_TakeDamageImpulse; +typedef IHookChainRegistryClassImpl CReGameHookRegistry_CBasePlayer_TakeDamageImpulse; + class CReGameHookchains: public IReGameHookchains { public: // CBasePlayer virtual @@ -910,6 +918,8 @@ class CReGameHookchains: public IReGameHookchains { CReGameHookRegistry_CBasePlayer_PlayerDeathThink m_CBasePlayer_PlayerDeathThink; CReGameHookRegistry_CBasePlayer_Observer_Think m_CBasePlayer_Observer_Think; CReGameHookRegistry_CBasePlayer_RemoveAllItems m_CBasePlayer_RemoveAllItems; + CReGameHookRegistry_CBasePlayer_UpdateStatusBar m_CBasePlayer_UpdateStatusBar; + CReGameHookRegistry_CBasePlayer_TakeDamageImpulse m_CBasePlayer_TakeDamageImpulse; public: virtual IReGameHookRegistry_CBasePlayer_Spawn *CBasePlayer_Spawn(); @@ -1070,6 +1080,8 @@ class CReGameHookchains: public IReGameHookchains { virtual IReGameHookRegistry_CBasePlayer_PlayerDeathThink *CBasePlayer_PlayerDeathThink(); virtual IReGameHookRegistry_CBasePlayer_Observer_Think *CBasePlayer_Observer_Think(); virtual IReGameHookRegistry_CBasePlayer_RemoveAllItems *CBasePlayer_RemoveAllItems(); + virtual IReGameHookRegistry_CBasePlayer_UpdateStatusBar *CBasePlayer_UpdateStatusBar(); + virtual IReGameHookRegistry_CBasePlayer_TakeDamageImpulse *CBasePlayer_TakeDamageImpulse(); }; extern CReGameHookchains g_ReGameHookchains; diff --git a/regamedll/dlls/API/CSPlayer.cpp b/regamedll/dlls/API/CSPlayer.cpp index 8ebf79306..cab27a473 100644 --- a/regamedll/dlls/API/CSPlayer.cpp +++ b/regamedll/dlls/API/CSPlayer.cpp @@ -412,6 +412,11 @@ EXT_FUNC void CCSPlayer::Observer_SetMode(int iMode) BasePlayer()->Observer_SetMode(iMode); } +EXT_FUNC void CCSPlayer::Observer_FindNextPlayer(bool bReverse, const char *name) +{ + BasePlayer()->Observer_FindNextPlayer(bReverse, name); +} + EXT_FUNC bool CCSPlayer::SelectSpawnSpot(const char *pEntClassName, CBaseEntity *&pSpot) { return BasePlayer()->SelectSpawnSpot(pEntClassName, pSpot); @@ -534,6 +539,11 @@ EXT_FUNC bool CCSPlayer::CheckActivityInGame() return (fabs(deltaYaw) >= 0.1f && fabs(deltaPitch) >= 0.1f); } +EXT_FUNC void CCSPlayer::TakeDamageImpulse(CBasePlayer *pAttacker, float flKnockbackForce, float flVelModifier) +{ + BasePlayer()->TakeDamageImpulse(pAttacker, flKnockbackForce, flVelModifier); +} + void CCSPlayer::ResetVars() { m_szModel[0] = '\0'; diff --git a/regamedll/dlls/ammo.cpp b/regamedll/dlls/ammo.cpp index 1df3622cc..8c264ac78 100644 --- a/regamedll/dlls/ammo.cpp +++ b/regamedll/dlls/ammo.cpp @@ -24,7 +24,7 @@ void CBasePlayerAmmo::Spawn() BOOL CBasePlayerAmmo::AddAmmo(CBaseEntity *pOther) { auto ammoInfo = GetAmmoInfo(pev->classname); - if (pOther->GiveAmmo(ammoInfo->buyClipSize, ammoInfo->ammoName2) == -1) + if (!ammoInfo || pOther->GiveAmmo(ammoInfo->buyClipSize, ammoInfo->ammoName2) == -1) { return FALSE; } diff --git a/regamedll/dlls/animation.cpp b/regamedll/dlls/animation.cpp index 7d5df6f07..fbf6a1bb6 100644 --- a/regamedll/dlls/animation.cpp +++ b/regamedll/dlls/animation.cpp @@ -547,7 +547,9 @@ void AngleQuaternion(vec_t *angles, vec_t *quaternion) { static const ALIGN16_BEG size_t ps_signmask[4] ALIGN16_END = { 0x80000000, 0, 0x80000000, 0 }; - __m128 a = _mm_loadu_ps(angles); + vec4_t _ps_angles = { angles[0], angles[1], angles[2], 0.0f }; + + __m128 a = _mm_loadu_ps(_ps_angles); a = _mm_mul_ps(a, _mm_load_ps(_ps_0p5)); //a *= 0.5 __m128 s, c; sincos_ps(a, &s, &c); diff --git a/regamedll/dlls/bot/cs_bot.cpp b/regamedll/dlls/bot/cs_bot.cpp index dd85448c6..2302e4611 100644 --- a/regamedll/dlls/bot/cs_bot.cpp +++ b/regamedll/dlls/bot/cs_bot.cpp @@ -900,3 +900,129 @@ float CCSBot::GetRangeToFarthestEscortedHostage() const return away.m_farRange; } + +// Remove all occurrences of a given area from the path list +void CCSBot::RemovePath(CNavArea *area) +{ + int i = 0; + while (i < m_pathLength) + { + if (m_path[i].area == area) + { + // If this area is linked to a ladder, clear the reference + if (m_path[i].ladder == m_pathLadder) + m_pathLadder = nullptr; + + m_pathLength--; + + // adjust the current path index if the removed element is being used + if (i == m_pathIndex) + { + if (i > 0) + m_pathIndex = i - 1; + else + m_pathIndex = 0; + } + + if (m_pathLength != i) + Q_memmove(&m_path[i], &m_path[i + 1], (m_pathLength - i) * sizeof(m_path[0])); + + // clear the slot + Q_memset(&m_path[m_pathLength], 0, sizeof(m_path[m_pathLength])); + } + else + { + i++; + } + } +} + +// Remove a hiding spot from the checked spots list +void CCSBot::RemoveHidingSpot(HidingSpot *spot) +{ + int i = 0; + while (i < m_checkedHidingSpotCount) + { + if (m_checkedHidingSpot[i].spot == spot) + { + m_checkedHidingSpotCount--; + + if (m_checkedHidingSpotCount != i) + Q_memmove(&m_checkedHidingSpot[i], &m_checkedHidingSpot[i + 1], (m_checkedHidingSpotCount - i) * sizeof(m_checkedHidingSpot[0])); + + // clear the slot + Q_memset(&m_checkedHidingSpot[m_checkedHidingSpotCount], 0, sizeof(m_checkedHidingSpot[m_checkedHidingSpotCount])); + } + else + { + i++; + } + } +} + +// Handle navigation-related cleanup when a nav area, spot, or encounter is destroyed +void CCSBot::OnDestroyNavDataNotify(NavNotifyDestroyType navNotifyType, void *dead) +{ + switch (navNotifyType) + { + case NAV_NOTIFY_DESTROY_AREA: + { + CNavArea *area = static_cast(dead); + + // If the destroyed area was linked to a spot encounter, clear it + if (m_spotEncounter) + { + if (m_spotEncounter->from.area == area || m_spotEncounter->to.area == area) + m_spotEncounter = nullptr; + } + + RemovePath(area); + + // Invalidate any references to the destroyed area + + if (m_noiseArea == area) + m_noiseArea = nullptr; + + if (m_currentArea == area) + m_currentArea = nullptr; + + if (m_lastKnownArea == area) + m_lastKnownArea = nullptr; + + if (m_hideState.GetSearchArea() == area) + m_hideState.SetSearchArea(nullptr); + + if (m_huntState.GetHuntArea() == area) + m_huntState.ClearHuntArea(); + + break; + } + case NAV_NOTIFY_DESTROY_SPOT_ENCOUNTER: + { + CNavArea *area = static_cast(dead); + + // Remove the encounter if it references the destroyed area + if (m_spotEncounter && area->HasSpotEncounter(m_spotEncounter)) + m_spotEncounter = nullptr; + + break; + } + case NAV_NOTIFY_DESTROY_SPOT: + { + HidingSpot *spot = static_cast(dead); + + // Remove the destroyed hiding spot from the spot order list + if (m_spotEncounter) + { + SpotOrderList &spotOrderList = m_spotEncounter->spotList; + spotOrderList.erase(std::remove_if(spotOrderList.begin(), spotOrderList.end(), [&](SpotOrder &spotOrder) { + return spotOrder.spot == spot; + } + ), spotOrderList.end()); + } + + RemoveHidingSpot(spot); + break; + } + } +} diff --git a/regamedll/dlls/bot/cs_bot.h b/regamedll/dlls/bot/cs_bot.h index 9670744f9..0ba299857 100644 --- a/regamedll/dlls/bot/cs_bot.h +++ b/regamedll/dlls/bot/cs_bot.h @@ -71,6 +71,7 @@ class HuntState: public BotState virtual const char *GetName() const { return "Hunt"; } void ClearHuntArea() { m_huntArea = nullptr; } + CNavArea *GetHuntArea() { return m_huntArea; } private: CNavArea *m_huntArea; @@ -204,6 +205,7 @@ class HideState: public BotState const Vector &GetHidingSpot() const { return m_hidingSpot; } void SetSearchArea(CNavArea *area) { m_searchFromArea = area; } + CNavArea *GetSearchArea() { return m_searchFromArea; } void SetSearchRange(float range) { m_range = range; } void SetDuration(float time) { m_duration = time; } @@ -533,12 +535,21 @@ class CCSBot: public CBot bool IsAwareOfEnemyDeath() const; // return true if we *noticed* that our enemy died int GetLastVictimID() const; // return the ID (entindex) of the last victim we killed, or zero +#ifdef REGAMEDLL_ADD + bool CanSeeSniper(void) const; ///< return true if we can see an enemy sniper + bool HasSeenSniperRecently(void) const; ///< return true if we have seen a sniper recently +#endif + // navigation bool HasPath() const; void DestroyPath(); float GetFeetZ() const; // return Z of bottom of feet + void OnDestroyNavDataNotify(NavNotifyDestroyType navNotifyType, void *dead); + void RemovePath(CNavArea *area); + void RemoveHidingSpot(HidingSpot *spot); + enum PathResult { PROGRESSING, // we are moving along the path @@ -921,6 +932,11 @@ class CCSBot: public CBot float m_fireWeaponTimestamp; +#ifdef REGAMEDLL_ADD + bool m_isEnemySniperVisible; ///< do we see an enemy sniper right now + CountdownTimer m_sawEnemySniperTimer; ///< tracking time since saw enemy sniper +#endif + // reaction time system enum { MAX_ENEMY_QUEUE = 20 }; struct ReactionState @@ -1109,6 +1125,11 @@ inline bool CCSBot::IsAtBombsite() inline CCSBot::MoraleType CCSBot::GetMorale() const { +#ifdef REGAMEDLL_ADD + if (cv_bot_excellent_morale.value != 0.0f) + return EXCELLENT; +#endif + return m_morale; } @@ -1250,6 +1271,18 @@ inline int CCSBot::GetLastVictimID() const return m_lastVictimID; } +#ifdef REGAMEDLL_ADD +inline bool CCSBot::CanSeeSniper(void) const +{ + return m_isEnemySniperVisible; +} + +inline bool CCSBot::HasSeenSniperRecently(void) const +{ + return !m_sawEnemySniperTimer.IsElapsed(); +} +#endif + inline bool CCSBot::HasPath() const { return m_pathLength != 0; diff --git a/regamedll/dlls/bot/cs_bot_chatter.cpp b/regamedll/dlls/bot/cs_bot_chatter.cpp index 1476d8d58..d48898ae7 100644 --- a/regamedll/dlls/bot/cs_bot_chatter.cpp +++ b/regamedll/dlls/bot/cs_bot_chatter.cpp @@ -246,6 +246,17 @@ void BotHostageBeingTakenMeme::Interpret(CCSBot *pSender, CCSBot *pReceiver) con pReceiver->GetChatter()->Say("Affirmative"); } +#ifdef REGAMEDLL_ADD +//--------------------------------------------------------------------------------------------------------------- +/** + * A teammate warned about snipers, so we shouldn't warn again for awhile + */ +void BotWarnSniperMeme::Interpret(CCSBot* sender, CCSBot* receiver) const +{ + receiver->GetChatter()->FriendSpottedSniper(); +} +#endif + BotSpeakable::BotSpeakable() { m_phrase = nullptr; @@ -464,6 +475,7 @@ bool BotPhraseManager::Initialize(const char *filename, int bankIndex) phraseData = SharedParse(phraseData); if (!phraseData) { + if (phrase) delete phrase; CONSOLE_ECHO("Error parsing '%s' - expected identifier\n", filename); FREE_FILE(phraseDataFile); return false; @@ -1269,6 +1281,9 @@ void BotChatterInterface::Reset() m_planInterval.Invalidate(); m_encourageTimer.Invalidate(); m_escortingHostageTimer.Invalidate(); +#ifdef REGAMEDLL_ADD + m_warnSniperTimer.Invalidate(); +#endif } // Register a statement for speaking @@ -1625,6 +1640,42 @@ void BotChatterInterface::EnemySpotted() AddStatement(say); } +#ifdef REGAMEDLL_ADD +//--------------------------------------------------------------------------------------------------------------- +/** + * If a friend warned of snipers, don't warn again for awhile + */ +void BotChatterInterface::FriendSpottedSniper(void) +{ + m_warnSniperTimer.Start(60.0f); +} + +//--------------------------------------------------------------------------------------------------------------- +/** + * Warn of an enemy sniper + */ +void BotChatterInterface::SpottedSniper(void) +{ + if (!m_warnSniperTimer.IsElapsed()) + { + return; + } + + if (m_me->GetFriendsRemaining() == 0) + { + // no-one to warn + return; + } + + BotStatement* say = new BotStatement(this, REPORT_INFORMATION, 10.0f); + + say->AppendPhrase(TheBotPhrases->GetPhrase("SniperWarning")); + say->AttachMeme(new BotWarnSniperMeme()); + + AddStatement(say); +} +#endif + NOXREF void BotChatterInterface::Clear(Place place) { BotStatement *say = new BotStatement(this, REPORT_INFORMATION, 10.0f); diff --git a/regamedll/dlls/bot/cs_bot_chatter.h b/regamedll/dlls/bot/cs_bot_chatter.h index 4a4591cf6..8d3a5baa0 100644 --- a/regamedll/dlls/bot/cs_bot_chatter.h +++ b/regamedll/dlls/bot/cs_bot_chatter.h @@ -140,6 +140,14 @@ class BotRequestReportMeme: public BotMeme virtual void Interpret(CCSBot *pSender, CCSBot *pReceiver) const; // cause the given bot to act on this meme }; +#ifdef REGAMEDLL_ADD +class BotWarnSniperMeme : public BotMeme +{ +public: + virtual void Interpret(CCSBot* sender, CCSBot* receiver) const; ///< cause the given bot to act on this meme +}; +#endif + enum BotStatementType { REPORT_VISIBLE_ENEMIES, @@ -531,6 +539,10 @@ class BotChatterInterface void KilledFriend(); void FriendlyFire(); +#ifdef REGAMEDLL_ADD + void SpottedSniper(void); + void FriendSpottedSniper(void); +#endif bool SeesAtLeastOneEnemy() const { return m_seeAtLeastOneEnemy; } private: @@ -557,6 +569,9 @@ class BotChatterInterface CountdownTimer m_spottedLooseBombTimer; CountdownTimer m_heardNoiseTimer; CountdownTimer m_escortingHostageTimer; +#ifdef REGAMEDLL_ADD + CountdownTimer m_warnSniperTimer; +#endif }; inline BotChatterInterface::VerbosityType BotChatterInterface::GetVerbosity() const diff --git a/regamedll/dlls/bot/cs_bot_event.cpp b/regamedll/dlls/bot/cs_bot_event.cpp index 199616659..70411c8e5 100644 --- a/regamedll/dlls/bot/cs_bot_event.cpp +++ b/regamedll/dlls/bot/cs_bot_event.cpp @@ -56,6 +56,11 @@ void CCSBot::OnEvent(GameEventType event, CBaseEntity *pEntity, CBaseEntity *pOt DecreaseMorale(); } break; +#ifdef REGAMEDLL_FIXES + case EVENT_NEW_MATCH: + m_morale = POSITIVE; // starting a new round makes everyone a little happy + break; +#endif } if (!IsAlive()) diff --git a/regamedll/dlls/bot/cs_bot_init.cpp b/regamedll/dlls/bot/cs_bot_init.cpp index a6503e901..2db6b02d5 100644 --- a/regamedll/dlls/bot/cs_bot_init.cpp +++ b/regamedll/dlls/bot/cs_bot_init.cpp @@ -28,6 +28,7 @@ #include "precompiled.h" +cvar_t cv_bot_enable = { "bot_enable", "0", 0, 0.0f, nullptr }; cvar_t cv_bot_traceview = { "bot_traceview", "0", FCVAR_SERVER, 0.0f, nullptr }; cvar_t cv_bot_stop = { "bot_stop", "0", FCVAR_SERVER, 0.0f, nullptr }; cvar_t cv_bot_show_nav = { "bot_show_nav", "0", FCVAR_SERVER, 0.0f, nullptr }; @@ -64,6 +65,7 @@ cvar_t cv_bot_join_delay = { "bot_join_delay", "0", FCVAR_SERVER, 0. cvar_t cv_bot_freeze = { "bot_freeze", "0", 0, 0.0f, nullptr }; cvar_t cv_bot_mimic = { "bot_mimic", "0", 0, 0.0f, nullptr }; cvar_t cv_bot_mimic_yaw_offset = { "bot_mimic_yaw_offset", "0", 0, 0.0f, nullptr }; +cvar_t cv_bot_excellent_morale = { "bot_excellent_morale", "0", 0, 0.0f, nullptr }; #else // Migrated to bot_quota_mode, use "match" cvar_t cv_bot_quota_match = { "bot_quota_match", "0", FCVAR_SERVER, 0.0f, nullptr }; @@ -135,6 +137,7 @@ void Bot_RegisterCVars() CVAR_REGISTER(&cv_bot_freeze); CVAR_REGISTER(&cv_bot_mimic); CVAR_REGISTER(&cv_bot_mimic_yaw_offset); + CVAR_REGISTER(&cv_bot_excellent_morale); #endif } @@ -193,6 +196,11 @@ void CCSBot::ResetValues() m_currentArea = nullptr; m_lastKnownArea = nullptr; +#ifdef REGAMEDLL_ADD + m_isEnemySniperVisible = false; + m_sawEnemySniperTimer.Invalidate(); +#endif + m_avoidFriendTimer.Invalidate(); m_isFriendInTheWay = false; m_isWaitingBehindFriend = false; @@ -329,10 +337,10 @@ void CCSBot::ResetValues() // NOTE: For some reason, this can be called twice when a bot is added. void CCSBot::SpawnBot() { - TheCSBots()->ValidateMapData(); + TheCSBots()->LoadNavigationMap(); ResetValues(); - Q_strcpy(m_name, STRING(pev->netname)); + Q_strlcpy(m_name, STRING(pev->netname)); SetState(&m_buyState); SetTouch(&CCSBot::BotTouch); diff --git a/regamedll/dlls/bot/cs_bot_init.h b/regamedll/dlls/bot/cs_bot_init.h index 8687e3f7f..691c518f2 100644 --- a/regamedll/dlls/bot/cs_bot_init.h +++ b/regamedll/dlls/bot/cs_bot_init.h @@ -28,6 +28,7 @@ #pragma once +extern cvar_t cv_bot_enable; extern cvar_t cv_bot_traceview; extern cvar_t cv_bot_stop; extern cvar_t cv_bot_show_nav; @@ -64,6 +65,7 @@ extern cvar_t cv_bot_join_delay; extern cvar_t cv_bot_freeze; extern cvar_t cv_bot_mimic; extern cvar_t cv_bot_mimic_yaw_offset; +extern cvar_t cv_bot_excellent_morale; #else extern cvar_t cv_bot_quota_match; #endif diff --git a/regamedll/dlls/bot/cs_bot_learn.cpp b/regamedll/dlls/bot/cs_bot_learn.cpp index 76edded7a..e68a77a5e 100644 --- a/regamedll/dlls/bot/cs_bot_learn.cpp +++ b/regamedll/dlls/bot/cs_bot_learn.cpp @@ -477,31 +477,24 @@ void CCSBot::StartSaveProcess() void CCSBot::UpdateSaveProcess() { - char filename[256]; - char msg[256]; - char cmd[128]; + char gd[64]{}; + GET_GAME_DIR(gd); - GET_GAME_DIR(filename); - - Q_strcat(filename, "\\"); - Q_strcat(filename, TheBots->GetNavMapFilename()); - - HintMessageToAllPlayers("Saving..."); + char filename[MAX_OSPATH]; + Q_snprintf(filename, sizeof(filename), "%s\\%s", gd, TheBots->GetNavMapFilename()); SaveNavigationMap(filename); + char msg[256]{}; Q_snprintf(msg, sizeof(msg), "Navigation file '%s' saved.", filename); HintMessageToAllPlayers(msg); + CONSOLE_ECHO("%s\n", msg); hideProgressMeter(); StartNormalProcess(); -#ifndef REGAMEDLL_FIXES - Q_snprintf(cmd, sizeof(cmd), "map %s\n", STRING(gpGlobals->mapname)); -#else - Q_snprintf(cmd, sizeof(cmd), "changelevel %s\n", STRING(gpGlobals->mapname)); -#endif - - SERVER_COMMAND(cmd); + // tell bot manager that the analysis is completed + if (TheCSBots()) + TheCSBots()->AnalysisCompleted(); } void CCSBot::StartNormalProcess() diff --git a/regamedll/dlls/bot/cs_bot_manager.cpp b/regamedll/dlls/bot/cs_bot_manager.cpp index b4391be7f..8c7c7a1fc 100644 --- a/regamedll/dlls/bot/cs_bot_manager.cpp +++ b/regamedll/dlls/bot/cs_bot_manager.cpp @@ -231,13 +231,12 @@ bool CCSBotManager::IsOnOffense(CBasePlayer *pPlayer) const // Invoked when a map has just been loaded void CCSBotManager::ServerActivate() { - DestroyNavigationMap(); m_isMapDataLoaded = false; m_zoneCount = 0; m_gameScenario = SCENARIO_DEATHMATCH; - ValidateMapData(); + LoadNavigationMap(); RestartRound(); m_isLearningMap = false; @@ -578,18 +577,21 @@ void CCSBotManager::ServerCommand(const char *pcmd) } else if (FStrEq(pcmd, "bot_nav_save")) { - GET_GAME_DIR(buffer); - Q_strcat(buffer, "\\"); - Q_strcat(buffer, CBotManager::GetNavMapFilename()); + char gd[64]{}; + GET_GAME_DIR(gd); + + char filename[MAX_OSPATH]; + Q_snprintf(filename, sizeof(filename), "%s\\%s", gd, CBotManager::GetNavMapFilename()); - if (SaveNavigationMap(buffer)) - CONSOLE_ECHO("Navigation map '%s' saved.\n", buffer); + if (SaveNavigationMap(filename)) + CONSOLE_ECHO("Navigation map '%s' saved.\n", filename); else - CONSOLE_ECHO("ERROR: Cannot save navigation map '%s'.\n", buffer); + CONSOLE_ECHO("ERROR: Cannot save navigation map '%s'.\n", filename); } else if (FStrEq(pcmd, "bot_nav_load")) { - ValidateMapData(); + m_isMapDataLoaded = false; // force nav reload + LoadNavigationMap(); } else if (FStrEq(pcmd, "bot_nav_use_place")) { @@ -887,12 +889,10 @@ void CCSBotManager::MaintainBotQuota() if (m_isLearningMap) return; - int totalHumansInGame = UTIL_HumansInGame(); - int humanPlayersInGame = UTIL_HumansInGame(IGNORE_SPECTATORS); - int spectatorPlayersInGame = UTIL_SpectatorsInGame(); + int humanPlayersInGame = UTIL_HumansInGame(); // don't add bots until local player has been registered, to make sure he's player ID #1 - if (!IS_DEDICATED_SERVER() && totalHumansInGame == 0) + if (!IS_DEDICATED_SERVER() && humanPlayersInGame == 0) return; int desiredBotCount = int(cv_bot_quota.value); @@ -947,7 +947,7 @@ void CCSBotManager::MaintainBotQuota() // wait for a player to join, if necessary if (cv_bot_join_after_player.value > 0.0) { - if (humanPlayersInGame == 0 && spectatorPlayersInGame == 0) + if (humanPlayersInGame == 0) desiredBotCount = 0; } @@ -1144,22 +1144,232 @@ class CollectOverlappingAreas CCSBotManager::Zone *m_zone; }; -// Search the map entities to determine the game scenario and define important zones. -void CCSBotManager::ValidateMapData() +LINK_ENTITY_TO_CLASS(info_spawn_point, CPointEntity, CCSPointEntity) + +inline bool IsFreeSpace(Vector vecOrigin, int iHullNumber, edict_t *pSkipEnt = nullptr) +{ + if (UTIL_PointContents(vecOrigin) != CONTENTS_EMPTY) + return false; + + TraceResult trace; + UTIL_TraceHull(vecOrigin, vecOrigin, dont_ignore_monsters, iHullNumber, pSkipEnt, &trace); + + return (!trace.fStartSolid && !trace.fAllSolid && trace.fInOpen); +} + +inline bool pointInRadius(Vector vecOrigin, float radius) +{ + CBaseEntity *pEntity = nullptr; + while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecOrigin, radius))) + { + if (!UTIL_IsValidEntity(pEntity->edict())) + continue; // ignore the entity marked for deletion + + if (FClassnameIs(pEntity->edict(), "info_spawn_point")) + return true; + } + + return false; +} + +// a simple algorithm that searches for the farthest point (so that the player does not look at the wall) +inline bool GetIdealLookYawForSpawnPoint(const Vector &vecStart, float &flIdealLookYaw) +{ + const float ANGLE_STEP = 30.0f; + float bestDistance = 0.0f; + + for (float angleYaw = 0.0f; angleYaw <= 360.0f; angleYaw += ANGLE_STEP) + { + TraceResult tr; + UTIL_MakeVectors(Vector(0, angleYaw, 0)); + + Vector vecEnd(vecStart + gpGlobals->v_forward * 8192); + UTIL_TraceLine(vecStart, vecEnd, ignore_monsters, nullptr, &tr); + + float distance = (vecStart - tr.vecEndPos).Length(); + if (distance > bestDistance) + { + bestDistance = distance; + flIdealLookYaw = angleYaw; + } + } + + return bestDistance > 0.0f; +} + +// this function from leaked csgo sources 2020y +inline bool IsValidArea(CNavArea *area) +{ + ShortestPathCost cost; + bool bNotOrphaned; + + // check that we can path from the nav area to a ct spawner to confirm it isn't orphaned. + CBaseEntity *CTSpawn = UTIL_FindEntityByClassname(nullptr, "info_player_start"); + + if (CTSpawn) + { + CNavArea *CTSpawnArea = TheNavAreaGrid.GetNearestNavArea(&CTSpawn->pev->origin); + bNotOrphaned = NavAreaBuildPath(area, CTSpawnArea, nullptr, cost); + + if (bNotOrphaned) + return true; + } + + // double check that we can path from the nav area to a t spawner to confirm it isn't orphaned. + CBaseEntity *TSpawn = UTIL_FindEntityByClassname(nullptr, "info_player_deathmatch"); + + if (TSpawn) + { + CNavArea *TSpawnArea = TheNavAreaGrid.GetNearestNavArea(&TSpawn->pev->origin); + bNotOrphaned = NavAreaBuildPath(area, TSpawnArea, nullptr, cost); + + if (bNotOrphaned) + return true; + } + + return false; +} + +#ifdef REGAMEDLL_ADD + +// Generates spawn points (info_spawn_point entities) for players and bots based on the navigation map data +// It uses the navigation areas to find valid spots for spawn points considering factors like area size, slope, +// available free space, and distance from other spawn points. +void GenerateSpawnPointsFromNavData() { + // Remove any existing spawn points + UTIL_RemoveOther("info_spawn_point"); + + const int MAX_SPAWNS_POINTS = 128; // Max allowed spawn points + + const float MAX_SLOPE = 0.85f; // Maximum slope allowed for a spawn point + const float MIN_AREA_SIZE = 32.0f; // Minimum area size for a valid spawn point + const float MIN_NEARBY_SPAWNPOINT = 128.0f; // Minimum distance between spawn point + + // Total number of spawn points generated + int totalSpawns = 0; + + for (CNavArea *area : TheNavAreaList) + { + if (totalSpawns >= MAX_SPAWNS_POINTS) + break; + + if (!area) + continue; + + // Skip areas that are too small + if (area->GetSizeX() < MIN_AREA_SIZE || area->GetSizeY() < MIN_AREA_SIZE) + continue; + + // Skip areas with unwanted attributes (jump, crouch, etc.) + if (area->GetAttributes() != 0) + continue; + + // Skip areas with steep slopes + if (area->GetAreaSlope() < MAX_SLOPE) + { + //CONSOLE_ECHO("Skip area slope: %0.3f\n", area->GetAreaSlope()); + continue; + } + + // Calculate the spawn point position above the area center + Vector vecOrigin = *area->GetCenter() + Vector(0, 0, HalfHumanHeight + 5); + + // Ensure there is free space at the calculated position + if (!IsFreeSpace(vecOrigin, human_hull)) + { + //CONSOLE_ECHO("No free space!\n"); + continue; + } + + if (pointInRadius(vecOrigin, MIN_NEARBY_SPAWNPOINT)) + continue; // spawn point is too close to others + + if (!IsValidArea(area)) + continue; + + // Calculate ideal spawn point yaw angle + float flIdealSpawnPointYaw = 0.0f; + if (GetIdealLookYawForSpawnPoint(vecOrigin, flIdealSpawnPointYaw)) + { + CBaseEntity *pPoint = CBaseEntity::Create("info_spawn_point", vecOrigin, Vector(0, flIdealSpawnPointYaw, 0), nullptr); + if (pPoint) + { + totalSpawns++; + //CONSOLE_ECHO("Add spawn at x:%f y:%f z:%f with angle %0.1f slope %0.3f \n", vecOrigin.x, vecOrigin.y, vecOrigin.z, bestAngle.y, area->GetAreaSlope()); + + // use only for debugging + if (randomspawn.value > 1.0f) + { + SET_MODEL(ENT(pPoint->pev), "models/player.mdl"); + pPoint->pev->sequence = ACT_IDLE; + pPoint->pev->rendermode = kRenderTransAdd; + pPoint->pev->renderamt = 150.0f; + } + } + } + } + + CONSOLE_ECHO("Total spawns points: %i\n", totalSpawns); +} + +#endif + +// Load the map's navigation data +bool CCSBotManager::LoadNavigationMap() +{ + // check if the map data is already loaded or if bots are not allowed if (m_isMapDataLoaded || !AreBotsAllowed()) - return; + return false; m_isMapDataLoaded = true; - if (LoadNavigationMap()) + // Clear navigation map data from previous map + DestroyNavigationMap(); + + // Try to load the map's navigation file + NavErrorType navStatus = ::LoadNavigationMap(); + if (navStatus != NAV_OK) { - CONSOLE_ECHO("Failed to load navigation map.\n"); - return; + CONSOLE_ECHO("ERROR: Failed to load 'maps/%s.nav' file navigation map!\n", STRING(gpGlobals->mapname)); + + switch (navStatus) + { + case NAV_CANT_ACCESS_FILE: + CONSOLE_ECHO("\tNavigation file not found or access denied.\n"); + break; + case NAV_INVALID_FILE: + CONSOLE_ECHO("\tInvalid navigation file format.\n"); + break; + case NAV_BAD_FILE_VERSION: + CONSOLE_ECHO("\tBad navigation file version.\n"); + break; + case NAV_CORRUPT_DATA: + CONSOLE_ECHO("\tCorrupted navigation data detected.\n"); + break; + default: + break; + } + + if (navStatus != NAV_CANT_ACCESS_FILE) + CONSOLE_ECHO("\tTry regenerating it using the command: bot_nav_analyze\n"); + + return false; } - CONSOLE_ECHO("Navigation map loaded.\n"); + // Determine the scenario for the current map (e.g., bomb defuse, hostage rescue etc) + DetermineMapScenario(); + +#ifdef REGAMEDLL_ADD + GenerateSpawnPointsFromNavData(); +#endif + + return true; +} +// Search the map entities to determine the game scenario and define important zones. +void CCSBotManager::DetermineMapScenario() +{ m_zoneCount = 0; m_gameScenario = SCENARIO_DEATHMATCH; @@ -1211,6 +1421,13 @@ void CCSBotManager::ValidateMapData() found = true; isLegacy = false; } + else if (FClassnameIs(pEntity->pev, "func_escapezone")) + { + m_gameScenario = SCENARIO_ESCAPE; + found = true; + isLegacy = false; + } + if (found) { @@ -1292,6 +1509,59 @@ void CCSBotManager::ValidateMapData() } } +// Tell all bots that the given nav data no longer exists +// This function is called when a part of the map or the nav data is destroyed +void CCSBotManager::OnDestroyNavDataNotify(NavNotifyDestroyType navNotifyType, void *dead) +{ + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsBot()) + continue; + + // Notify the bot about the destroyed nav data + CCSBot *pBot = static_cast(pPlayer); + pBot->OnDestroyNavDataNotify(navNotifyType, dead); + } +} + +// Called when the map analysis process has completed +// This function makes sure all bots are removed from the map analysis process +// and are reset to normal bot behavior. It also reloads the navigation map +// and triggers a game restart after the analysis is completed +void CCSBotManager::AnalysisCompleted() +{ + // Ensure that all bots are no longer involved in map analysis and start their normal process + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + + if (!UTIL_IsValidPlayer(pPlayer)) + continue; + + if (!pPlayer->IsBot()) + continue; + + CCSBot *pBot = static_cast(pPlayer); + pBot->StartNormalProcess(); + } + + m_isLearningMap = false; + m_isMapDataLoaded = false; + m_isAnalysisRequested = false; + + // Try to reload the navigation map from the file + if (LoadNavigationMap()) + { + // Initiate a game restart in 3 seconds + CVAR_SET_FLOAT("sv_restart", 3); + } +} + bool CCSBotManager::AddBot(const BotProfile *profile, BotProfileTeamType team) { if (!AreBotsAllowed()) diff --git a/regamedll/dlls/bot/cs_bot_manager.h b/regamedll/dlls/bot/cs_bot_manager.h index 1faea5461..6f3b0e689 100644 --- a/regamedll/dlls/bot/cs_bot_manager.h +++ b/regamedll/dlls/bot/cs_bot_manager.h @@ -56,13 +56,16 @@ class CCSBotManager: public CBotManager virtual bool IsImportantPlayer(CBasePlayer *pPlayer) const; // return true if pPlayer is important to scenario (VIP, bomb carrier, etc) public: - void ValidateMapData(); + bool LoadNavigationMap(); + void DetermineMapScenario(); void OnFreeEntPrivateData(CBaseEntity *pEntity); + void OnDestroyNavDataNotify(NavNotifyDestroyType navNotifyType, void *dead); bool IsLearningMap() const { return m_isLearningMap; } void SetLearningMapFlag() { m_isLearningMap = true; } bool IsAnalysisRequested() const { return m_isAnalysisRequested; } void RequestAnalysis() { m_isAnalysisRequested = true; } void AckAnalysisRequest() { m_isAnalysisRequested = false; } + void AnalysisCompleted(); // difficulty levels static BotDifficultyType GetDifficultyLevel() @@ -85,7 +88,8 @@ class CCSBotManager: public CBotManager SCENARIO_DEATHMATCH, SCENARIO_DEFUSE_BOMB, SCENARIO_RESCUE_HOSTAGES, - SCENARIO_ESCORT_VIP + SCENARIO_ESCORT_VIP, + SCENARIO_ESCAPE }; GameScenarioType GetScenario() const @@ -268,3 +272,4 @@ inline bool AreBotsAllowed() } void PrintAllEntities(); +void GenerateSpawnPointsFromNavData(); diff --git a/regamedll/dlls/bot/cs_bot_update.cpp b/regamedll/dlls/bot/cs_bot_update.cpp index 37d496659..de75bf6aa 100644 --- a/regamedll/dlls/bot/cs_bot_update.cpp +++ b/regamedll/dlls/bot/cs_bot_update.cpp @@ -255,7 +255,7 @@ void CCSBot::Update() Vector dir = m_spotEncounter->path.to - m_spotEncounter->path.from; float length = dir.NormalizeInPlace(); - for (auto &order : m_spotEncounter->spotList) { + for (auto& order : m_spotEncounter->spotList) { UTIL_DrawBeamPoints(m_spotEncounter->path.from + order.t * length * dir, *order.spot->GetPosition(), 3, 0, 255, 255); } } @@ -339,7 +339,7 @@ void CCSBot::Update() UpdateReactionQueue(); // "threat" may be the same as our current enemy - CBasePlayer *threat = GetRecognizedEnemy(); + CBasePlayer* threat = GetRecognizedEnemy(); if (threat) { // adjust our personal "safe" time @@ -592,6 +592,10 @@ void CCSBot::Update() SecondaryAttack(); } +#ifdef REGAMEDLL_ADD + if (!IsBlind()) + { +#endif // check encounter spots UpdatePeripheralVision(); @@ -601,11 +605,24 @@ void CCSBot::Update() GetChatter()->SpottedBomber(GetBomber()); } +#ifdef REGAMEDLL_ADD + // watch for snipers + if (CanSeeSniper() && !HasSeenSniperRecently()) + { + GetChatter()->SpottedSniper(); + + const float sniperRecentInterval = 20.0f; + m_sawEnemySniperTimer.Start(sniperRecentInterval); + } +#endif + if (CanSeeLooseBomb()) { GetChatter()->SpottedLooseBomb(TheCSBots()->GetLooseBomb()); } - +#ifdef REGAMEDLL_ADD +} +#endif // Scenario interrupts switch (TheCSBots()->GetScenario()) { diff --git a/regamedll/dlls/bot/cs_bot_vision.cpp b/regamedll/dlls/bot/cs_bot_vision.cpp index f7b16acc5..5e4e30264 100644 --- a/regamedll/dlls/bot/cs_bot_vision.cpp +++ b/regamedll/dlls/bot/cs_bot_vision.cpp @@ -697,6 +697,13 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() m_closestVisibleFriend = nullptr; m_closestVisibleHumanFriend = nullptr; +#ifdef REGAMEDLL_ADD + m_isEnemySniperVisible = false; + CBasePlayer* sniperThreat = NULL; + float sniperThreatRange = 99999999999.9f; + bool sniperThreatIsFacingMe = false; +#endif + float closeFriendRange = 99999999999.9f; float closeHumanFriendRange = 99999999999.9f; @@ -789,6 +796,53 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() Vector d = pev->origin - pPlayer->pev->origin; float distSq = d.LengthSquared(); +#ifdef REGAMEDLL_ADD + CBasePlayerWeapon *pCurrentWeapon = static_cast(pPlayer->m_pActiveItem); + if (pCurrentWeapon && isSniperRifle(pCurrentWeapon)) + { + m_isEnemySniperVisible = true; + if (sniperThreat) + { + if (IsPlayerLookingAtMe(pPlayer)) + { + if (sniperThreatIsFacingMe) + { + // several snipers are facing us - keep closest + if (distSq < sniperThreatRange) + { + sniperThreat = pPlayer; + sniperThreatRange = distSq; + sniperThreatIsFacingMe = true; + } + } + else + { + // even if this sniper is farther away, keep it because he's aiming at us + sniperThreat = pPlayer; + sniperThreatRange = distSq; + sniperThreatIsFacingMe = true; + } + } + else + { + // this sniper is not looking at us, only consider it if we dont have a sniper facing us + if (!sniperThreatIsFacingMe && distSq < sniperThreatRange) + { + sniperThreat = pPlayer; + sniperThreatRange = distSq; + } + } + } + else + { + // first sniper we see + sniperThreat = pPlayer; + sniperThreatRange = distSq; + sniperThreatIsFacingMe = IsPlayerLookingAtMe(pPlayer); + } + } +#endif + // maintain set of visible threats, sorted by increasing distance if (threatCount == 0) { @@ -950,6 +1004,23 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() { return currentThreat; } + + // if we are a sniper and we see a sniper threat, attack it unless + // there are other close enemies facing me + if (IsSniper() && sniperThreat) + { + const float closeCombatRange = 500.0f; + + for (t = 0; t < threatCount; ++t) + { + if (threat[t].range < closeCombatRange && IsPlayerLookingAtMe(threat[t].enemy)) + { + return threat[t].enemy; + } + } + + return sniperThreat; + } #endif // otherwise, find the closest threat that without using shield diff --git a/regamedll/dlls/bot/states/cs_bot_buy.cpp b/regamedll/dlls/bot/states/cs_bot_buy.cpp index f210423e9..c34351f07 100644 --- a/regamedll/dlls/bot/states/cs_bot_buy.cpp +++ b/regamedll/dlls/bot/states/cs_bot_buy.cpp @@ -462,17 +462,26 @@ void BuyState::OnUpdate(CCSBot *me) me->ClientCommand("vesthelm"); me->ClientCommand("vest"); - // pistols - if we have no preferred pistol, buy at random - if (TheCSBots()->AllowPistols() && !me->GetProfile()->HasPistolPreference()) + if (TheCSBots()->AllowPistols() +#ifndef REGAMEDLL_FIXES + && !me->GetProfile()->HasPistolPreference() +#endif + ) { if (m_buyPistol) { - int which = RANDOM_LONG(0, MAX_BUY_WEAPON_SECONDARY - 1); +#ifdef REGAMEDLL_FIXES + // pistols - if we have no preferred pistol, buy at random + if (!me->GetProfile()->HasPistolPreference()) +#endif + { + int which = RANDOM_LONG(0, MAX_BUY_WEAPON_SECONDARY - 1); - if (me->m_iTeam == TERRORIST) - me->ClientCommand(secondaryWeaponBuyInfoT[which].buyAlias); - else - me->ClientCommand(secondaryWeaponBuyInfoCT[which].buyAlias); + if (me->m_iTeam == TERRORIST) + me->ClientCommand(secondaryWeaponBuyInfoT[which].buyAlias); + else + me->ClientCommand(secondaryWeaponBuyInfoCT[which].buyAlias); + } // only buy one pistol m_buyPistol = false; diff --git a/regamedll/dlls/bot/states/cs_bot_idle.cpp b/regamedll/dlls/bot/states/cs_bot_idle.cpp index e38bde885..4bfcd7cc0 100644 --- a/regamedll/dlls/bot/states/cs_bot_idle.cpp +++ b/regamedll/dlls/bot/states/cs_bot_idle.cpp @@ -773,6 +773,93 @@ void IdleState::OnUpdate(CCSBot *me) } break; } + case CCSBotManager::SCENARIO_ESCAPE: + { + if (me->m_iTeam == TERRORIST) + { + // if early in round, pick a random zone, otherwise pick closest zone + const float earlyTime = 20.0f; + const CCSBotManager::Zone *zone = nullptr; + + if (TheCSBots()->GetElapsedRoundTime() < earlyTime) + { + // pick random zone + zone = TheCSBots()->GetRandomZone(); + } + else + { + // pick closest zone + zone = TheCSBots()->GetClosestZone(me->GetLastKnownArea(), PathCost(me)); + } + + if (zone) + { + // pick a random spot within the escape zone + const Vector *pos = TheCSBots()->GetRandomPositionInZone(zone); + if (pos) + { + // move to escape zone + // me->SetTask(CCSBot::VIP_ESCAPE); + me->Run(); + me->MoveTo(pos); + return; + } + } + } + // CT + else + { + if (me->IsSniper()) + { + if (RANDOM_FLOAT(0, 100) <= defenseSniperCampChance) + { + // snipe escape zone(s) + const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone(); + if (zone) + { + CNavArea *area = TheCSBots()->GetRandomAreaInZone(zone); + if (area) + { + me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT); + me->Hide(area, -1.0, sniperHideRange); + me->SetDisposition(CCSBot::OPPORTUNITY_FIRE); + me->PrintIfWatched("Sniping near escape zone\n"); + return; + } + } + } + } + + // rogues just hunt, unless they want to snipe + // if the whole team has decided to rush, hunt + if (me->IsRogue() || TheCSBots()->IsDefenseRushing()) + break; + + // the lower our morale gets, the more we want to camp the escape zone(s) + float guardEscapeZoneChance = -34.0f * me->GetMorale(); + + if (RANDOM_FLOAT(0.0f, 100.0f) < guardEscapeZoneChance) + { + // guard escape zone(s) + const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone(); + if (zone) + { + CNavArea *area = TheCSBots()->GetRandomAreaInZone(zone); + if (area) + { + // guard the escape zone - stay closer if our morale is low + //me->SetTask(CCSBot::GUARD_VIP_ESCAPE_ZONE); + me->PrintIfWatched("I'm guarding an escape zone\n"); + + float escapeGuardRange = 750.0f + 250.0f * (me->GetMorale() + 3); + me->Hide(area, -1.0, escapeGuardRange); + me->SetDisposition(CCSBot::OPPORTUNITY_FIRE); + return; + } + } + } + } + } // deathmatch default: { diff --git a/regamedll/dlls/career_tasks.cpp b/regamedll/dlls/career_tasks.cpp index 972ccca9b..050a9aab0 100644 --- a/regamedll/dlls/career_tasks.cpp +++ b/regamedll/dlls/career_tasks.cpp @@ -260,7 +260,7 @@ void CCareerTask::OnEvent(GameEventType event, CBasePlayer *pVictim, CBasePlayer while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity"))) { - if (pHostage && pHostage->IsDead()) + if (pHostage->IsDead()) hostagesCount++; } @@ -389,7 +389,6 @@ void CCareerTaskManager::Reset(bool deleteTasks) delete task; m_tasks.clear(); - m_nextId = 0; } else { @@ -397,6 +396,7 @@ void CCareerTaskManager::Reset(bool deleteTasks) task->Reset(); } + m_nextId = 0; m_finishedTaskTime = 0; m_finishedTaskRound = 0; m_shouldLatchRoundEndMessage = false; diff --git a/regamedll/dlls/client.cpp b/regamedll/dlls/client.cpp index e1b4d3221..b802a2e31 100644 --- a/regamedll/dlls/client.cpp +++ b/regamedll/dlls/client.cpp @@ -133,6 +133,7 @@ static entity_field_alias_t custom_entity_field_alias[] = { "animtime", 0 }, }; +edict_t *g_pEdicts = nullptr; bool g_bServerActive = false; bool g_bItemCreatedByBuying = false; PLAYERPVSSTATUS g_PVSStatus[MAX_CLIENTS]; @@ -464,7 +465,7 @@ NOXREF int CountTeams() void ListPlayers(CBasePlayer *current) { - char message[120] = "", cNumber[12]; + char message[120]{}; CBaseEntity *pEntity = nullptr; while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) @@ -478,12 +479,7 @@ void ListPlayers(CBasePlayer *current) CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); int iUserID = GETPLAYERUSERID(ENT(pPlayer->pev)); - Q_sprintf(cNumber, "%d", iUserID); - Q_strcpy(message, "\n"); - Q_strcat(message, cNumber); - Q_strcat(message, " : "); - Q_strcat(message, STRING(pPlayer->pev->netname)); - + Q_snprintf(message, sizeof(message), "\n%d : %s", iUserID, STRING(pPlayer->pev->netname)); ClientPrint(current->pev, HUD_PRINTCONSOLE, message); } @@ -737,8 +733,8 @@ void EXT_FUNC ClientPutInServer(edict_t *pEntity) pPlayer->m_iJoiningState = SHOWLTEXT; - static char sName[128]; - Q_strcpy(sName, STRING(pPlayer->pev->netname)); + char sName[128]; + Q_strlcpy(sName, STRING(pPlayer->pev->netname)); for (char *pApersand = sName; pApersand && *pApersand != '\0'; pApersand++) { @@ -796,12 +792,12 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) { if (CMD_ARGC_() >= 2) { - Q_sprintf(szTemp, "%s %s", pcmd, CMD_ARGS()); + Q_snprintf(szTemp, sizeof(szTemp), "%s %s", pcmd, CMD_ARGS()); } else { // Just a one word command, use the first word...sigh - Q_sprintf(szTemp, "%s", pcmd); + Q_snprintf(szTemp, sizeof(szTemp), "%s", pcmd); } p = szTemp; @@ -815,7 +811,9 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) if (*p == '"') { p++; - p[Q_strlen(p) - 1] = '\0'; + size_t len = Q_strlen(p); + if (len > 0) + p[len - 1] = '\0'; } // Check if buffer contains an invalid unicode sequence @@ -843,6 +841,12 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) char *pszConsoleFormat = nullptr; bool consoleUsesPlaceName = false; +#ifdef REGAMEDLL_ADD + // there's no team on FFA mode + if (teamonly && CSGameRules()->IsFreeForAll() && (pPlayer->m_iTeam == CT || pPlayer->m_iTeam == TERRORIST)) + teamonly = FALSE; +#endif + // team only if (teamonly) { @@ -966,8 +970,8 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) } } - Q_strcat(text, p); - Q_strcat(text, "\n"); + Q_strlcat(text, p); + Q_strlcat(text, "\n"); // loop through all players // Start with the first player. @@ -997,7 +1001,13 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) if (gpGlobals->deathmatch != 0.0f && CSGameRules()->m_VoiceGameMgr.PlayerHasBlockedPlayer(pReceiver, pPlayer)) continue; - if (teamonly && pReceiver->m_iTeam != pPlayer->m_iTeam) + if (teamonly +#ifdef REGAMEDLL_FIXES + && CSGameRules()->PlayerRelationship(pPlayer, pReceiver) != GR_TEAMMATE +#else + && pReceiver->m_iTeam != pPlayer->m_iTeam +#endif + ) continue; if ( @@ -1010,7 +1020,13 @@ void Host_Say(edict_t *pEntity, BOOL teamonly) continue; } - if ((pReceiver->m_iIgnoreGlobalChat == IGNOREMSG_ENEMY && pReceiver->m_iTeam == pPlayer->m_iTeam) + if ((pReceiver->m_iIgnoreGlobalChat == IGNOREMSG_ENEMY +#ifdef REGAMEDLL_FIXES + && CSGameRules()->PlayerRelationship(pPlayer, pReceiver) == GR_TEAMMATE +#else + && pReceiver->m_iTeam == pPlayer->m_iTeam +#endif + ) || pReceiver->m_iIgnoreGlobalChat == IGNOREMSG_NONE) { MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pReceiver->pev); @@ -3747,6 +3763,8 @@ void EXT_FUNC ServerDeactivate() void EXT_FUNC ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) { + g_pEdicts = pEdictList; + #ifdef REGAMEDLL_ADD // // Tells clients which version of player movement (pmove) the server is using @@ -3815,9 +3833,6 @@ void EXT_FUNC ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) #ifdef REGAMEDLL_ADD CSGameRules()->ServerActivate(); - - if (location_area_info.value) - LoadNavigationMap(); #endif } @@ -4298,7 +4313,7 @@ void ClientPrecache() PRECACHE_GENERIC("sprites/scope_arc_ne.tga"); PRECACHE_GENERIC("sprites/scope_arc_sw.tga"); - m_usResetDecals = g_engfuncs.pfnPrecacheEvent(1, "events/decal_reset.sc"); + m_usResetDecals = PRECACHE_EVENT(1, "events/decal_reset.sc"); } const char *EXT_FUNC GetGameDescription() @@ -4990,7 +5005,7 @@ void EXT_FUNC UpdateClientData(const edict_t *ent, int sendweapons, struct clien cd->flSwimTime = pev->flSwimTime; cd->waterjumptime = int(pev->teleport_time); - Q_strcpy(cd->physinfo, ENGINE_GETPHYSINFO(ent)); + Q_strlcpy(cd->physinfo, ENGINE_GETPHYSINFO(ent)); cd->maxspeed = pev->maxspeed; cd->fov = pev->fov; @@ -5201,8 +5216,10 @@ int EXT_FUNC InconsistentFile(const edict_t *pEdict, const char *filename, char if (!CVAR_GET_FLOAT("mp_consistency")) return 0; + const int BufferLen = 256; + // Default behavior is to kick the player - Q_sprintf(disconnect_message, "Server is enforcing file consistency for %s\n", filename); + Q_snprintf(disconnect_message, BufferLen, "Server is enforcing file consistency for %s\n", filename); // Kick now with specified disconnect message. return 1; diff --git a/regamedll/dlls/combat.cpp b/regamedll/dlls/combat.cpp index 17fe84e27..8c66a4754 100644 --- a/regamedll/dlls/combat.cpp +++ b/regamedll/dlls/combat.cpp @@ -285,8 +285,6 @@ void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker damageRatio = GetAmountOfPlayerVisible(vecSrc, pEntity); } - damageRatio = GetAmountOfPlayerVisible(vecSrc, pEntity); - float length; #ifdef REGAMEDLL_ADD // allow to damage breakable objects diff --git a/regamedll/dlls/debug.cpp b/regamedll/dlls/debug.cpp index 82d50d71d..81605143d 100644 --- a/regamedll/dlls/debug.cpp +++ b/regamedll/dlls/debug.cpp @@ -27,7 +27,7 @@ NOXREF void UTIL_DPrintf(DebugOutputType outputType, char *pszMsg, ...) { va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); @@ -41,7 +41,7 @@ void UTIL_DPrintf(char *pszMsg, ...) va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); @@ -130,7 +130,7 @@ NOXREF void UTIL_BotDPrintf(char *pszMsg, ...) { va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); @@ -146,7 +146,7 @@ void UTIL_CareerDPrintf(char *pszMsg, ...) { va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); @@ -162,7 +162,7 @@ NOXREF void UTIL_TutorDPrintf(char *pszMsg, ...) { va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); @@ -178,7 +178,7 @@ NOXREF void UTIL_StatsDPrintf(char *pszMsg, ...) { va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); @@ -194,7 +194,7 @@ NOXREF void UTIL_HostageDPrintf(char *pszMsg, ...) { va_list argptr; va_start(argptr, pszMsg); - vsprintf(theDebugBuffer, pszMsg, argptr); + Q_vsnprintf(theDebugBuffer, sizeof(theDebugBuffer), pszMsg, argptr); va_end(argptr); SERVER_PRINT(theDebugBuffer); diff --git a/regamedll/dlls/ehandle.h b/regamedll/dlls/ehandle.h index 9a8717826..61d22a5cc 100644 --- a/regamedll/dlls/ehandle.h +++ b/regamedll/dlls/ehandle.h @@ -130,6 +130,10 @@ inline edict_t *EntityHandle::Set(edict_t *pEdict) { m_serialnumber = pEdict->serialnumber; } + else + { + m_serialnumber = 0; + } return pEdict; } diff --git a/regamedll/dlls/explode.cpp b/regamedll/dlls/explode.cpp index 104d84720..6d1c7ccd4 100644 --- a/regamedll/dlls/explode.cpp +++ b/regamedll/dlls/explode.cpp @@ -201,7 +201,7 @@ void ExplosionCreate(const Vector ¢er, Vector &angles, edict_t *pOwner, int CBaseEntity *pExplosion = CBaseEntity::Create("env_explosion", center, angles, pOwner); - Q_sprintf(buf, "%3d", magnitude); + Q_snprintf(buf, sizeof(buf), "%3d", magnitude); kvd.szKeyName = "iMagnitude"; kvd.szValue = buf; diff --git a/regamedll/dlls/func_tank.cpp b/regamedll/dlls/func_tank.cpp index e12c90768..14c2ef6fc 100644 --- a/regamedll/dlls/func_tank.cpp +++ b/regamedll/dlls/func_tank.cpp @@ -302,7 +302,7 @@ void CFuncTank::ControllerPostFrame() Assert(m_pController != nullptr); - if (m_pController->pev->button & IN_ATTACK) + if (m_pController && m_pController->pev->button & IN_ATTACK) { Vector vecForward; UTIL_MakeVectorsPrivate(pev->angles, vecForward, nullptr, nullptr); diff --git a/regamedll/dlls/game.cpp b/regamedll/dlls/game.cpp index 789435938..2c6d9fbff 100644 --- a/regamedll/dlls/game.cpp +++ b/regamedll/dlls/game.cpp @@ -160,6 +160,7 @@ cvar_t t_default_grenades = { "mp_t_default_grenades", "", 0, 0.0 cvar_t t_give_player_knife = { "mp_t_give_player_knife", "1", 0, 1.0f, nullptr }; cvar_t t_default_weapons_secondary = { "mp_t_default_weapons_secondary", "glock18", 0, 0.0f, nullptr }; cvar_t t_default_weapons_primary = { "mp_t_default_weapons_primary", "", 0, 0.0f, nullptr }; +cvar_t default_weapons_random = { "mp_default_weapons_random", "", 0, 0.0f, nullptr }; cvar_t free_armor = { "mp_free_armor", "0", 0, 0.0f, nullptr }; cvar_t teamflash = { "mp_team_flash", "1", 0, 1.0f, nullptr }; cvar_t allchat = { "sv_allchat", "0", 0, 0.0f, nullptr }; @@ -171,15 +172,16 @@ cvar_t deathmsg_flags = { "mp_deathmsg_flags", "abc", 0, 0.0f cvar_t assist_damage_threshold = { "mp_assist_damage_threshold", "40", 0, 40.0f, nullptr }; cvar_t freezetime_duck = { "mp_freezetime_duck", "1", 0, 1.0f, nullptr }; cvar_t freezetime_jump = { "mp_freezetime_jump", "1", 0, 1.0f, nullptr }; +cvar_t jump_height = { "mp_jump_height", "45", FCVAR_SERVER, 45.0f, nullptr }; -cvar_t hostages_rescued_ratio = { "mp_hostages_rescued_ratio", "1.0", 0, 1.0f, nullptr }; +cvar_t hostages_rescued_ratio = { "mp_hostages_rescued_ratio", "1.0", 0, 1.0f, nullptr }; -cvar_t legacy_vehicle_block = { "mp_legacy_vehicle_block", "1", 0, 0.0f, nullptr }; +cvar_t legacy_vehicle_block = { "mp_legacy_vehicle_block", "1", 0, 0.0f, nullptr }; cvar_t dying_time = { "mp_dying_time", "3.0", 0, 3.0f, nullptr }; cvar_t defuser_allocation = { "mp_defuser_allocation", "0", 0, 0.0f, nullptr }; cvar_t location_area_info = { "mp_location_area_info", "0", 0, 0.0f, nullptr }; -cvar_t chat_loc_fallback = { "mp_chat_loc_fallback", "1", 1, 0.0f, nullptr }; +cvar_t chat_loc_fallback = { "mp_chat_loc_fallback", "1", 0, 1.0f, nullptr }; cvar_t item_respawn_time = { "mp_item_respawn_time", "30", FCVAR_SERVER, 30.0f, nullptr }; cvar_t weapon_respawn_time = { "mp_weapon_respawn_time", "20", FCVAR_SERVER, 20.0f, nullptr }; @@ -188,6 +190,17 @@ cvar_t ammo_respawn_time = { "mp_ammo_respawn_time", "20", FCVAR_SERVER, 2 cvar_t vote_flags = { "mp_vote_flags", "km", 0, 0.0f, nullptr }; cvar_t votemap_min_time = { "mp_votemap_min_time", "180", 0, 180.0f, nullptr }; +cvar_t flymove_method = { "mp_flymove_method", "0", 0, 0.0f, nullptr }; +cvar_t stamina_restore_rate = { "mp_stamina_restore_rate", "0", 0, 0.f, nullptr }; + +cvar_t logkills = { "mp_logkills", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t randomspawn = { "mp_randomspawn", "0", FCVAR_SERVER, 0.0f, nullptr }; + +cvar_t playerid_showhealth = { "mp_playerid_showhealth", "1", 0, 1.0f, nullptr }; +cvar_t playerid_field = { "mp_playerid_field", "3", 0, 3.0f, nullptr }; + +cvar_t knockback = { "mp_knockback", "170", 0, 170.0f, nullptr }; + void GameDLL_Version_f() { if (Q_stricmp(CMD_ARGV(1), "version") != 0) @@ -431,6 +444,7 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&t_give_player_knife); CVAR_REGISTER(&t_default_weapons_secondary); CVAR_REGISTER(&t_default_weapons_primary); + CVAR_REGISTER(&default_weapons_random); CVAR_REGISTER(&free_armor); CVAR_REGISTER(&teamflash); CVAR_REGISTER(&allchat); @@ -449,6 +463,7 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&freezetime_duck); CVAR_REGISTER(&freezetime_jump); + CVAR_REGISTER(&jump_height); CVAR_REGISTER(&defuser_allocation); CVAR_REGISTER(&location_area_info); CVAR_REGISTER(&chat_loc_fallback); @@ -459,12 +474,32 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&vote_flags); CVAR_REGISTER(&votemap_min_time); + CVAR_REGISTER(&randomspawn); + + CVAR_REGISTER(&cv_bot_enable); + CVAR_REGISTER(&cv_hostage_ai_enable); + CVAR_REGISTER(&logkills); + + CVAR_REGISTER(&playerid_showhealth); + CVAR_REGISTER(&playerid_field); + + CVAR_REGISTER(&stamina_restore_rate); + + CVAR_REGISTER(&flymove_method); + + CVAR_REGISTER(&knockback); // print version CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); + // execute initial pre-configurations + SERVER_COMMAND("exec game_init.cfg\n"); + SERVER_EXECUTE(); + #endif // REGAMEDLL_ADD + Regamedll_Game_Init(); + Bot_RegisterCVars(); Tutor_RegisterCVars(); Hostage_RegisterCVars(); @@ -473,12 +508,6 @@ void EXT_FUNC GameDLLInit() VoiceGameMgr_RegisterCVars(); #endif -#ifdef REGAMEDLL_ADD - // execute initial pre-configurations - SERVER_COMMAND("exec game_init.cfg\n"); - SERVER_EXECUTE(); -#endif - } SpewRetval_t GameDLL_SpewHandler(SpewType_t spewType, int level, const char *pMsg) diff --git a/regamedll/dlls/game.h b/regamedll/dlls/game.h index 09da5305a..ecf1001c8 100644 --- a/regamedll/dlls/game.h +++ b/regamedll/dlls/game.h @@ -186,6 +186,7 @@ extern cvar_t t_default_grenades; extern cvar_t t_give_player_knife; extern cvar_t t_default_weapons_secondary; extern cvar_t t_default_weapons_primary; +extern cvar_t default_weapons_random; extern cvar_t free_armor; extern cvar_t teamflash; extern cvar_t allchat; @@ -200,6 +201,7 @@ extern cvar_t deathmsg_flags; extern cvar_t assist_damage_threshold; extern cvar_t freezetime_duck; extern cvar_t freezetime_jump; +extern cvar_t jump_height; extern cvar_t defuser_allocation; extern cvar_t location_area_info; extern cvar_t chat_loc_fallback; @@ -208,6 +210,13 @@ extern cvar_t weapon_respawn_time; extern cvar_t ammo_respawn_time; extern cvar_t vote_flags; extern cvar_t votemap_min_time; +extern cvar_t flymove_method; +extern cvar_t stamina_restore_rate; +extern cvar_t logkills; +extern cvar_t randomspawn; +extern cvar_t playerid_showhealth; +extern cvar_t playerid_field; +extern cvar_t knockback; #endif @@ -215,4 +224,6 @@ extern cvar_t scoreboard_showmoney; extern cvar_t scoreboard_showhealth; extern cvar_t scoreboard_showdefkit; + + void GameDLLInit(); diff --git a/regamedll/dlls/gamerules.cpp b/regamedll/dlls/gamerules.cpp index 45210e212..3d9f14182 100644 --- a/regamedll/dlls/gamerules.cpp +++ b/regamedll/dlls/gamerules.cpp @@ -8,8 +8,8 @@ CGameRules::CGameRules() m_bBombDropped = FALSE; m_bGameOver = false; - m_GameDesc = new char[sizeof("Counter-Strike")]; - Q_strcpy(m_GameDesc, AreRunningCZero() ? "Condition Zero" : "Counter-Strike"); + const char *pszGameDesc = AreRunningCZero() ? "Condition Zero" : "Counter-Strike"; + m_GameDesc = CloneString(pszGameDesc); } CGameRules::~CGameRules() diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index a4f556ab9..220e09556 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -1009,5 +1009,4 @@ char *GetTeam(int team); void DestroyMapCycle(mapcycle_t *cycle); int ReloadMapCycleFile(char *filename, mapcycle_t *cycle); int CountPlayers(); -void ExtractCommandString(char *s, char *szCommand); int GetMapCount(); diff --git a/regamedll/dlls/ggrenade.cpp b/regamedll/dlls/ggrenade.cpp index 41e92f27d..878355658 100644 --- a/regamedll/dlls/ggrenade.cpp +++ b/regamedll/dlls/ggrenade.cpp @@ -1170,6 +1170,12 @@ void CGrenade::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useTy if (!m_bIsC4) return; +#ifdef REGAMEDLL_FIXES + // block the start of defuse if the bomb timer has expired + if (m_flC4Blow > 0 && gpGlobals->time >= m_flC4Blow) + return; +#endif + // TODO: We must be sure that the activator is a player. CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pActivator->pev); @@ -1503,6 +1509,13 @@ void CGrenade::C4Think() { DefuseBombEnd(pPlayer, false); } +#ifdef REGAMEDLL_FIXES + // if the bomb timer has expired and defuse is still ongoing, stop the defuse + else if (gpGlobals->time >= m_flC4Blow) + { + DefuseBombEnd(pPlayer, false); + } +#endif } else { diff --git a/regamedll/dlls/globals.cpp b/regamedll/dlls/globals.cpp index 3f698d59f..d02c8fee9 100644 --- a/regamedll/dlls/globals.cpp +++ b/regamedll/dlls/globals.cpp @@ -11,4 +11,3 @@ BOOL gDisplayTitle; bool g_bIsBeta = false; bool g_bIsCzeroGame = false; bool g_bAllowedCSBot = false; -bool g_bHostageImprov = false; diff --git a/regamedll/dlls/globals.h b/regamedll/dlls/globals.h index 020242918..805fd12ad 100644 --- a/regamedll/dlls/globals.h +++ b/regamedll/dlls/globals.h @@ -38,4 +38,3 @@ extern BOOL gDisplayTitle; extern bool g_bIsBeta; extern bool g_bIsCzeroGame; extern bool g_bAllowedCSBot; -extern bool g_bHostageImprov; diff --git a/regamedll/dlls/h_export.cpp b/regamedll/dlls/h_export.cpp index 486a3eedf..544a92ffb 100644 --- a/regamedll/dlls/h_export.cpp +++ b/regamedll/dlls/h_export.cpp @@ -13,7 +13,6 @@ C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pEnginefuncsTable, global gpGlobals = pGlobals; FileSystem_Init(); - Regamedll_Game_Init(); } #if defined(_LINUX) diff --git a/regamedll/dlls/hostage/hostage.cpp b/regamedll/dlls/hostage/hostage.cpp index 7ef34b222..c6f57091c 100644 --- a/regamedll/dlls/hostage/hostage.cpp +++ b/regamedll/dlls/hostage/hostage.cpp @@ -28,6 +28,7 @@ #include "precompiled.h" +cvar_t cv_hostage_ai_enable = { "hostage_ai_enable", "0", 0, 0.0f, nullptr }; cvar_t cv_hostage_debug = { "hostage_debug", "0", FCVAR_SERVER, 0.0f, nullptr }; cvar_t cv_hostage_stop = { "hostage_stop", "0", FCVAR_SERVER, 0.0f, nullptr }; @@ -268,39 +269,48 @@ void CHostage::Spawn() void CHostage::Precache() { - if (AreImprovAllowed()) + if (cv_hostage_ai_enable.value) { + string_t model = iStringNull; + static int which = 0; switch (which) { + default: case REGULAR_GUY: - pev->model = MAKE_STRING("models/hostageA.mdl"); + model = MAKE_STRING("models/hostageA.mdl"); break; case OLD_GUY: - pev->model = MAKE_STRING("models/hostageB.mdl"); + model = MAKE_STRING("models/hostageB.mdl"); break; case BLACK_GUY: - pev->model = MAKE_STRING("models/hostageC.mdl"); + model = MAKE_STRING("models/hostageC.mdl"); break; case GOOFY_GUY: - pev->model = MAKE_STRING("models/hostageD.mdl"); - break; - default: + model = MAKE_STRING("models/hostageD.mdl"); break; } m_whichModel = static_cast(which); - if (++which > 3) - which = 0; + if (++which > GOOFY_GUY) + which = REGULAR_GUY; + + if (g_pFileSystem->FileExists(model)) + { + pev->model = model; + } + else + { + // It seems that the model is missing, so use classic hostages + CVAR_SET_FLOAT("hostage_ai_enable", 0); + } } - else + + if (pev->model.IsNull()) { m_whichModel = REGULAR_GUY; - if (pev->model.IsNull()) - { - pev->model = MAKE_STRING("models/scientist.mdl"); - } + pev->model = MAKE_STRING("models/scientist.mdl"); } PRECACHE_MODEL(pev->model); @@ -342,7 +352,7 @@ void CHostage::IdleThink() const float giveUpTime = (1 / 30.0f); float const updateRate = 0.1f; - if (AreImprovAllowed() && !TheNavAreaList.empty()) + if (cv_hostage_ai_enable.value && !TheNavAreaList.empty()) { if (!m_improv) { @@ -619,7 +629,7 @@ void CHostage::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir BOOL CHostage::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) { #ifdef REGAMEDLL_ADD - if (!CanTakeDamage(pevAttacker)) + if (pevAttacker && !CanTakeDamage(pevAttacker)) return FALSE; #endif @@ -771,7 +781,7 @@ void CHostage::SetDeathActivity() return; } - if (AreImprovAllowed()) + if (cv_hostage_ai_enable.value) { switch (m_LastHitGroup) { @@ -1397,7 +1407,7 @@ void Hostage_RegisterCVars() { // These cvars are only used in czero #ifdef REGAMEDLL_FIXES - if (!AreImprovAllowed()) + if (!cv_hostage_ai_enable.value) return; #endif @@ -1432,7 +1442,7 @@ void CHostageManager::ServerActivate() AddHostage(pHostage); } - if (AreImprovAllowed()) + if (cv_hostage_ai_enable.value) { for (auto& snd : hostageSoundStruct) { m_chatter.AddSound(snd.type, snd.fileName); @@ -1568,6 +1578,9 @@ void SimpleChatter::AddSound(HostageChatterType type, char *filename) Q_snprintf(actualFilename, sizeof(actualFilename), "sound\\%s", filename); + if (!g_pFileSystem->FileExists(actualFilename)) + return; + chatter->file[chatter->count].filename = CloneString(filename); chatter->file[chatter->count].duration = (double)GET_APPROX_WAVE_PLAY_LEN(actualFilename) / 1000.0; @@ -1605,6 +1618,9 @@ void SimpleChatter::Shuffle(ChatterSet *chatter) char *SimpleChatter::GetSound(HostageChatterType type, float *duration) { ChatterSet *chatter = &m_chatter[type]; + if (chatter->count == 0) + return nullptr; + char *sound; Shuffle(chatter); diff --git a/regamedll/dlls/hostage/hostage.h b/regamedll/dlls/hostage/hostage.h index 44a501235..f869cacfb 100644 --- a/regamedll/dlls/hostage/hostage.h +++ b/regamedll/dlls/hostage/hostage.h @@ -77,6 +77,7 @@ enum HostageChatterType extern CHostageManager *g_pHostages; extern int g_iHostageNumber; +extern cvar_t cv_hostage_ai_enable; extern cvar_t cv_hostage_debug; extern cvar_t cv_hostage_stop; @@ -284,11 +285,5 @@ class CHostageManager SimpleChatter m_chatter; }; -// Determine whether hostage improv can be used or not -inline bool AreImprovAllowed() -{ - return g_bHostageImprov; -} - void Hostage_RegisterCVars(); void InstallHostageManager(); diff --git a/regamedll/dlls/hostage/hostage_improv.cpp b/regamedll/dlls/hostage/hostage_improv.cpp index 9cdba7777..6932d2185 100644 --- a/regamedll/dlls/hostage/hostage_improv.cpp +++ b/regamedll/dlls/hostage/hostage_improv.cpp @@ -1605,9 +1605,9 @@ void CHostageImprov::Afraid() int which = RANDOM_LONG(0, 100) % 3 + 1; - Q_sprintf(animInto, "cower_into_%d", which); - Q_sprintf(animLoop, "cower_loop_%d", which); - Q_sprintf(animExit, "cower_exit_%d", which); + Q_snprintf(animInto, sizeof(animInto), "cower_into_%d", which); + Q_snprintf(animLoop, sizeof(animLoop), "cower_loop_%d", which); + Q_snprintf(animExit, sizeof(animExit), "cower_exit_%d", which); m_animateState.AddSequence(this, animInto); m_animateState.AddSequence(this, animLoop, RANDOM_FLOAT(3, 10)); diff --git a/regamedll/dlls/hostage/states/hostage_idle.cpp b/regamedll/dlls/hostage/states/hostage_idle.cpp index c2c311a15..618848fc8 100644 --- a/regamedll/dlls/hostage/states/hostage_idle.cpp +++ b/regamedll/dlls/hostage/states/hostage_idle.cpp @@ -78,7 +78,7 @@ void HostageIdleState::OnUpdate(CHostageImprov *improv) } } - if (m_moveState && improv->IsAtMoveGoal()) + if (m_moveState != NotMoving && improv->IsAtMoveGoal()) { m_moveState = NotMoving; diff --git a/regamedll/dlls/items.cpp b/regamedll/dlls/items.cpp index 03620e362..ba138e044 100644 --- a/regamedll/dlls/items.cpp +++ b/regamedll/dlls/items.cpp @@ -237,7 +237,7 @@ BOOL CItemBattery::MyTouch(CBasePlayer *pPlayer) pct--; char szcharge[64]; - Q_sprintf(szcharge, "!HEV_%1dP", pct); + Q_snprintf(szcharge, sizeof(szcharge), "!HEV_%1dP", pct); pPlayer->SetSuitUpdate(szcharge, SUIT_SENTENCE, SUIT_NEXT_IN_30SEC); return TRUE; diff --git a/regamedll/dlls/lights.cpp b/regamedll/dlls/lights.cpp index d44cddcc1..aa8629ea0 100644 --- a/regamedll/dlls/lights.cpp +++ b/regamedll/dlls/lights.cpp @@ -129,11 +129,11 @@ void CEnvLight::KeyValue(KeyValueData *pkvd) pkvd->fHandled = TRUE; char szColor[64]; - Q_sprintf(szColor, "%d", r); + Q_snprintf(szColor, sizeof(szColor), "%d", r); CVAR_SET_STRING("sv_skycolor_r", szColor); - Q_sprintf(szColor, "%d", g); + Q_snprintf(szColor, sizeof(szColor), "%d", g); CVAR_SET_STRING("sv_skycolor_g", szColor); - Q_sprintf(szColor, "%d", b); + Q_snprintf(szColor, sizeof(szColor), "%d", b); CVAR_SET_STRING("sv_skycolor_b", szColor); } else @@ -147,13 +147,13 @@ void CEnvLight::Spawn() char szVector[64]; UTIL_MakeAimVectors(pev->angles); - Q_sprintf(szVector, "%f", gpGlobals->v_forward.x); + Q_snprintf(szVector, sizeof(szVector), "%f", gpGlobals->v_forward.x); CVAR_SET_STRING("sv_skyvec_x", szVector); - Q_sprintf(szVector, "%f", gpGlobals->v_forward.y); + Q_snprintf(szVector, sizeof(szVector), "%f", gpGlobals->v_forward.y); CVAR_SET_STRING("sv_skyvec_y", szVector); - Q_sprintf(szVector, "%f", gpGlobals->v_forward.z); + Q_snprintf(szVector, sizeof(szVector), "%f", gpGlobals->v_forward.z); CVAR_SET_STRING("sv_skyvec_z", szVector); CLight::Spawn(); diff --git a/regamedll/dlls/multiplay_gamerules.cpp b/regamedll/dlls/multiplay_gamerules.cpp index 8ba46556d..2b91a50f5 100644 --- a/regamedll/dlls/multiplay_gamerules.cpp +++ b/regamedll/dlls/multiplay_gamerules.cpp @@ -178,15 +178,12 @@ bool CCStrikeGameMgrHelper::GetCanHearPlayer(CBasePlayer* pListener, CBasePlayer void Broadcast(const char *sentence) { - char text[32]; + char text[128]; if (!sentence) - { return; - } - Q_strcpy(text, "%!MRAD_"); - Q_strcat(text, UTIL_VarArgs("%s", sentence)); + Q_snprintf(text, sizeof(text), "%%!MRAD_%s", sentence); MESSAGE_BEGIN(MSG_BROADCAST, gmsgSendAudio); WRITE_BYTE(0); @@ -507,7 +504,7 @@ CHalfLifeMultiplay::CHalfLifeMultiplay() char szCommand[256]; ALERT(at_console, "Executing listen server config file\n"); - Q_sprintf(szCommand, "exec %s\n", lservercfgfile); + Q_snprintf(szCommand, sizeof(szCommand), "exec %s\n", lservercfgfile); SERVER_COMMAND(szCommand); } } @@ -659,8 +656,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(CleanUpMap)() #endif // Remove grenades and C4 - const int grenadesRemoveCount = 20; - UTIL_RemoveOther("grenade", grenadesRemoveCount); + UTIL_RemoveOther("grenade"); #ifndef REGAMEDLL_FIXES // Remove defuse kit @@ -1820,6 +1816,11 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(RestartRound)() if (!UTIL_IsValidPlayer(pPlayer)) continue; +#ifdef REGAMEDLL_FIXES + if (!pPlayer->IsBot()) + pPlayer->ForceClientDllUpdate(); +#endif + pPlayer->Reset(); } @@ -3249,19 +3250,6 @@ void CHalfLifeMultiplay::CareerRestart() } m_bSkipSpawn = false; - - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - - if (!UTIL_IsValidPlayer(pPlayer)) - continue; - - if (!pPlayer->IsBot()) - { - pPlayer->ForceClientDllUpdate(); - } - } } BOOL CHalfLifeMultiplay::IsMultiplayer() @@ -4163,29 +4151,33 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, pVictim->CSPlayer()->m_iNumKilledByUnanswered[iPlayerIndexKiller - 1]++; } } - - // Did he kill himself? - if (pVictim->pev == pevKiller) - { - // killed self - char *team = GetTeam(pVictim->m_iTeam); - UTIL_LogPrintf("\"%s<%i><%s><%s>\" committed suicide with \"%s\"\n", STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), - GETPLAYERAUTHID(pVictim->edict()), team, killer_weapon_name); - } - else if (pevKiller->flags & FL_CLIENT) +#ifdef REGAMEDLL_ADD + if (static_cast(logkills.value)) +#endif { - const char *VictimTeam = GetTeam(pVictim->m_iTeam); - const char *KillerTeam = (pKiller && pKiller->IsPlayer()) ? GetTeam(pKiller->m_iTeam) : ""; + // Did he kill himself? + if (pVictim->pev == pevKiller) + { + // killed self + char *team = GetTeam(pVictim->m_iTeam); + UTIL_LogPrintf("\"%s<%i><%s><%s>\" committed suicide with \"%s\"\n", STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), + GETPLAYERAUTHID(pVictim->edict()), team, killer_weapon_name); + } + else if (pevKiller->flags & FL_CLIENT) + { + const char *VictimTeam = GetTeam(pVictim->m_iTeam); + const char *KillerTeam = (pKiller && pKiller->IsPlayer()) ? GetTeam(pKiller->m_iTeam) : ""; - UTIL_LogPrintf("\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n", STRING(pevKiller->netname), GETPLAYERUSERID(ENT(pevKiller)), GETPLAYERAUTHID(ENT(pevKiller)), - KillerTeam, STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), GETPLAYERAUTHID(pVictim->edict()), VictimTeam, killer_weapon_name); - } - else - { - // killed by the world - char *team = GetTeam(pVictim->m_iTeam); - UTIL_LogPrintf("\"%s<%i><%s><%s>\" committed suicide with \"%s\" (world)\n", STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), - GETPLAYERAUTHID(pVictim->edict()), team, killer_weapon_name); + UTIL_LogPrintf("\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n", STRING(pevKiller->netname), GETPLAYERUSERID(ENT(pevKiller)), GETPLAYERAUTHID(ENT(pevKiller)), + KillerTeam, STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), GETPLAYERAUTHID(pVictim->edict()), VictimTeam, killer_weapon_name); + } + else + { + // killed by the world + char *team = GetTeam(pVictim->m_iTeam); + UTIL_LogPrintf("\"%s<%i><%s><%s>\" committed suicide with \"%s\" (world)\n", STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), + GETPLAYERAUTHID(pVictim->edict()), team, killer_weapon_name); + } } // TODO: It is called in CBasePlayer::Killed too, most likely, @@ -4554,12 +4546,7 @@ int ReloadMapCycleFile(char *filename, mapcycle_t *cycle) if (Q_strlen(pToken) <= 0) break; -#ifdef REGAMEDLL_FIXES - Q_strncpy(szMap, pToken, sizeof(szMap) - 1); - szMap[sizeof(szMap) - 1] = '\0'; -#else - Q_strcpy(szMap, pToken); -#endif + Q_strlcpy(szMap, pToken); // Any more tokens on this line? if (SharedTokenWaiting(pFileList)) @@ -4568,7 +4555,7 @@ int ReloadMapCycleFile(char *filename, mapcycle_t *cycle) if (Q_strlen(pToken) > 0) { hasBuffer = true; - Q_strcpy(szBuffer, pToken); + Q_strlcpy(szBuffer, pToken); } } @@ -4580,7 +4567,7 @@ int ReloadMapCycleFile(char *filename, mapcycle_t *cycle) item = new mapcycle_item_s; - Q_strcpy(item->mapname, szMap); + Q_strlcpy(item->mapname, szMap); item->minplayers = 0; item->maxplayers = 0; @@ -4610,7 +4597,7 @@ int ReloadMapCycleFile(char *filename, mapcycle_t *cycle) REMOVE_KEY_VALUE(szBuffer, "minplayers"); REMOVE_KEY_VALUE(szBuffer, "maxplayers"); - Q_strcpy(item->rulebuffer, szBuffer); + Q_strlcpy(item->rulebuffer, szBuffer); } item->next = cycle->items; @@ -4675,7 +4662,7 @@ int CountPlayers() } // Parse commands/key value pairs to issue right after map xxx command is issued on server level transition -void ExtractCommandString(char *s, char *szCommand) +void ExtractCommandString(char *s, char *szCommand, size_t len) { // Now make rules happen char pkey[512]; @@ -4744,13 +4731,13 @@ void ExtractCommandString(char *s, char *szCommand) *c = '\0'; - Q_strcat(szCommand, pkey); + Q_strlcat(szCommand, pkey, len); if (Q_strlen(value) > 0) { - Q_strcat(szCommand, " "); - Q_strcat(szCommand, value); + Q_strlcat(szCommand, " ", len); + Q_strlcat(szCommand, value, len); } - Q_strcat(szCommand, "\n"); + Q_strlcat(szCommand, "\n", len); /*if (!*s) { @@ -4937,10 +4924,10 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(ChangeLevel)() #ifdef REGAMEDLL_FIXES // the absolute default level is de_dust - Q_strcpy(szFirstMapInList, "de_dust"); + Q_strlcpy(szFirstMapInList, "de_dust"); #else // the absolute default level is hldm1 - Q_strcpy(szFirstMapInList, "hldm1"); + Q_strlcpy(szFirstMapInList, "hldm1"); #endif int curplayers; @@ -4958,7 +4945,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(ChangeLevel)() // Has the map cycle filename changed? if (Q_stricmp(mapcfile, szPreviousMapCycleFile) != 0) { - Q_strcpy(szPreviousMapCycleFile, mapcfile); + Q_strlcpy(szPreviousMapCycleFile, mapcfile); DestroyMapCycle(&mapcycle); @@ -4976,8 +4963,8 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(ChangeLevel)() mapcycle_item_s *item; // Assume current map - Q_strcpy(szNextMap, STRING(gpGlobals->mapname)); - Q_strcpy(szFirstMapInList, STRING(gpGlobals->mapname)); + Q_strlcpy(szNextMap, STRING(gpGlobals->mapname)); + Q_strlcpy(szFirstMapInList, STRING(gpGlobals->mapname)); // Traverse list for (item = mapcycle.next_item; item->next != mapcycle.next_item; item = item->next) @@ -5030,14 +5017,14 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(ChangeLevel)() mapcycle.next_item = item->next; // Perform logic on current item - Q_strcpy(szNextMap, item->mapname); - ExtractCommandString(item->rulebuffer, szCommands); - Q_strcpy(szRules, item->rulebuffer); + Q_strlcpy(szNextMap, item->mapname); + ExtractCommandString(item->rulebuffer, szCommands, sizeof(szCommands)); + Q_strlcpy(szRules, item->rulebuffer); } if (!IS_MAP_VALID(szNextMap)) { - Q_strcpy(szNextMap, szFirstMapInList); + Q_strlcpy(szNextMap, szFirstMapInList); } m_bGameOver = true; @@ -5077,17 +5064,7 @@ void CHalfLifeMultiplay::SendMOTDToClient(edict_t *client) while (pFileList && *pFileList && char_count < MAX_MOTD_LENGTH) { char chunk[MAX_MOTD_CHUNK + 1]; - - if (Q_strlen(pFileList) < sizeof(chunk)) - { - Q_strcpy(chunk, pFileList); - } - else - { - Q_strncpy(chunk, pFileList, sizeof(chunk) - 1); - // Q_strncpy doesn't always append the null terminator - chunk[sizeof(chunk) - 1] = '\0'; - } + Q_strlcpy(chunk, pFileList); char_count += Q_strlen(chunk); diff --git a/regamedll/dlls/observer.cpp b/regamedll/dlls/observer.cpp index 4b3521a6c..3ccefc433 100644 --- a/regamedll/dlls/observer.cpp +++ b/regamedll/dlls/observer.cpp @@ -480,14 +480,14 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Observer_SetMode)(int iMode) { #ifdef REGAMEDLL_FIXES m_hObserverTarget = Observer_IsValidTarget( ENTINDEX(m_hObserverTarget->edict()), forcecamera != CAMERA_MODE_SPEC_ANYONE ); -#else +#else CBasePlayer *pTarget = m_hObserverTarget; - if (pTarget == this - || !pTarget - || pTarget->has_disconnected - || pTarget->GetObserverMode() != OBS_NONE - || (pTarget->pev->effects & EF_NODRAW) + if (pTarget == this + || !pTarget + || pTarget->has_disconnected + || pTarget->GetObserverMode() != OBS_NONE + || (pTarget->pev->effects & EF_NODRAW) || (forcecamera != CAMERA_MODE_SPEC_ANYONE && pTarget->m_iTeam != m_iTeam)) m_hObserverTarget = nullptr; #endif @@ -534,7 +534,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Observer_SetMode)(int iMode) // print spepctaor mode on client screen char modemsg[16]; - Q_sprintf(modemsg, "#Spec_Mode%i", pev->iuser1); + Q_snprintf(modemsg, sizeof(modemsg), "#Spec_Mode%i", pev->iuser1); ClientPrint(pev, HUD_PRINTCENTER, modemsg); m_iObserverLastMode = iMode; @@ -548,4 +548,4 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Observer_Think)() Observer_HandleButtons(); Observer_CheckTarget(); Observer_CheckProperties(); -} \ No newline at end of file +} diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index 1a895df62..67aa43e47 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -115,6 +115,57 @@ void CBasePlayer::SendItemStatus() MESSAGE_END(); } +#ifdef REGAMEDLL_ADD + +enum PlayerIdShowHealth +{ + PLAYERID_HIDE = 0, // Don't show health + PLAYERID_TEAMMATES = 1, // Show health for teammates only (default CS) + PLAYERID_ALL = 2 // Show health for all players +}; + +enum PlayerIdField +{ + PLAYERID_FIELD_NONE = 0, // No extra info + PLAYERID_FIELD_TEAM = 1, // Show team name + PLAYERID_FIELD_HEALTH = 2, // Show health percentage + PLAYERID_FIELD_BOTH = 3 // Show both team name and health +}; + +inline const char *GetPlayerIdString(bool sameTeam) +{ + int showHealth = static_cast(playerid_showhealth.value); + int fieldType = static_cast(playerid_field.value); + + // Don't show health + if (showHealth == PLAYERID_HIDE) + { + return (fieldType == PLAYERID_FIELD_NONE) ? "1 %p2" : "1 %c1: %p2"; + } + + // Health only for teammates + if (showHealth == PLAYERID_TEAMMATES && !sameTeam) + { + switch (fieldType) + { + case PLAYERID_FIELD_TEAM: return "1 %c1: %p2"; + case PLAYERID_FIELD_HEALTH: return "1 %p2"; + case PLAYERID_FIELD_BOTH: return "1 %c1: %p2"; + default: return "1 %p2"; + } + } + + // Show health to everyone + switch (fieldType) + { + case PLAYERID_FIELD_TEAM: return "1 %c1: %p2\n2 : %i3%%"; + case PLAYERID_FIELD_HEALTH: return "1 %p2\n2 %h: %i3%%"; + case PLAYERID_FIELD_BOTH: return "1 %c1: %p2\n2 %h: %i3%%"; + default: return "1 %p2\n2 : %i3%%"; + } +} +#endif + const char *GetCSModelName(int item_id) { const char *modelName = nullptr; @@ -569,13 +620,20 @@ LINK_HOOK_CLASS_VOID_CHAIN2(CBasePlayer, DeathSound) void EXT_FUNC CBasePlayer::__API_HOOK(DeathSound)() { +#if REGAMEDLL_FIXES + // FIXED: Don't interrupt pain sounds with death sound, use any available channel instead + const int channel = CHAN_AUTO; +#else + const int channel = CHAN_VOICE; +#endif + // temporarily using pain sounds for death sounds switch (RANDOM_LONG(1, 4)) { - case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/die1.wav", VOL_NORM, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/die2.wav", VOL_NORM, ATTN_NORM); break; - case 3: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/die3.wav", VOL_NORM, ATTN_NORM); break; - case 4: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/death6.wav", VOL_NORM, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), channel, "player/die1.wav", VOL_NORM, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), channel, "player/die2.wav", VOL_NORM, ATTN_NORM); break; + case 3: EMIT_SOUND(ENT(pev), channel, "player/die3.wav", VOL_NORM, ATTN_NORM); break; + case 4: EMIT_SOUND(ENT(pev), channel, "player/death6.wav", VOL_NORM, ATTN_NORM); break; } } @@ -1175,7 +1233,7 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva if (!ShouldDoLargeFlinch(m_LastHitGroup, iGunType)) { - m_flVelocityModifier = 0.5f; + TakeDamageImpulse(pAttack, 0.0f, 0.5f); if (m_LastHitGroup == HITGROUP_HEAD) m_bHighDamage = (flDamage > 60); @@ -1188,10 +1246,13 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva { if (pev->velocity.Length() < 300) { - Vector attack_velocity = (pev->origin - pAttack->pev->origin).Normalize() * 170; - pev->velocity = pev->velocity + attack_velocity; +#ifdef REGAMEDLL_ADD + float knockbackValue = knockback.value; +#else + float knockbackValue = 170; +#endif - m_flVelocityModifier = 0.65f; + TakeDamageImpulse(pAttack, knockbackValue, 0.65f); } SetAnimation(PLAYER_LARGE_FLINCH); @@ -1480,7 +1541,7 @@ void CBasePlayer::PackDeadPlayerItems() { DropShield(); #ifdef REGAMEDLL_ADD - if(iPackGun != GR_PLR_DROP_GUN_ALL) + if (iPackGun != GR_PLR_DROP_GUN_ALL) #endif { bSkipPrimSec = true; @@ -1637,6 +1698,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(GiveDefaultItems)() // Give default secondary equipment { char *secondaryString = NULL; + int secondaryCount = 0; + const int MAX_SECONDARY = 13; // x2 + 1 + WeaponInfoStruct *secondaryWeaponInfoArray[MAX_SECONDARY]; + if (m_iTeam == CT) secondaryString = ct_default_weapons_secondary.string; else if (m_iTeam == TERRORIST) @@ -1658,18 +1723,34 @@ void EXT_FUNC CBasePlayer::__API_HOOK(GiveDefaultItems)() if (weaponInfo) { const auto iItemID = GetItemIdByWeaponId(weaponInfo->id); if (iItemID != ITEM_NONE && !HasRestrictItem(iItemID, ITEM_TYPE_EQUIPPED) && IsSecondaryWeapon(iItemID)) { - GiveWeapon(weaponInfo->gunClipSize * iAmountOfBPAmmo, weaponInfo->entityName); + if (default_weapons_random.value != 0.0f) { + if (secondaryCount < MAX_SECONDARY) { + secondaryWeaponInfoArray[secondaryCount++] = weaponInfo; + } + } + else { + GiveWeapon(weaponInfo->gunClipSize * iAmountOfBPAmmo, weaponInfo->entityName); + } } } secondaryString = SharedParse(secondaryString); } + + if (default_weapons_random.value != 0.0f) { + WeaponInfoStruct *weaponInfo = secondaryWeaponInfoArray[RANDOM_LONG(0, secondaryCount - 1)]; + if (weaponInfo) + GiveWeapon(weaponInfo->gunClipSize * iAmountOfBPAmmo, weaponInfo->entityName); + } } } // Give default primary equipment { char *primaryString = NULL; + int primaryCount = 0; + const int MAX_PRIMARY = 39; // x2 + 1 + WeaponInfoStruct *primaryWeaponInfoArray[MAX_PRIMARY]; if (m_iTeam == CT) primaryString = ct_default_weapons_primary.string; @@ -1692,12 +1773,25 @@ void EXT_FUNC CBasePlayer::__API_HOOK(GiveDefaultItems)() if (weaponInfo) { const auto iItemID = GetItemIdByWeaponId(weaponInfo->id); if (iItemID != ITEM_NONE && !HasRestrictItem(iItemID, ITEM_TYPE_EQUIPPED) && IsPrimaryWeapon(iItemID)) { - GiveWeapon(weaponInfo->gunClipSize * iAmountOfBPAmmo, weaponInfo->entityName); + if (default_weapons_random.value != 0.0f) { + if (primaryCount < MAX_PRIMARY) { + primaryWeaponInfoArray[primaryCount++] = weaponInfo; + } + } + else { + GiveWeapon(weaponInfo->gunClipSize * iAmountOfBPAmmo, weaponInfo->entityName); + } } } primaryString = SharedParse(primaryString); } + + if (default_weapons_random.value != 0.0f) { + WeaponInfoStruct *weaponInfo = primaryWeaponInfoArray[RANDOM_LONG(0, primaryCount - 1)]; + if (weaponInfo) + GiveWeapon(weaponInfo->gunClipSize * iAmountOfBPAmmo, weaponInfo->entityName); + } } } @@ -2179,7 +2273,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) { CBasePlayer *pAttacker = CBasePlayer::Instance(pevAttacker); - if(pAttacker /*safety*/ && !pAttacker->IsBot() && pAttacker->m_iTeam != m_iTeam) + if (pAttacker /*safety*/ && !pAttacker->IsBot() && pAttacker->m_iTeam != m_iTeam) { if (pAttacker->HasShield()) killerHasShield = true; @@ -2455,8 +2549,16 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) break; } + // UNDO: This code was intended to set the victim's angle with the throw direction + // For bots, it works correctly because they do not send angle updates to the server, + // However, for players, the client overrides the angle with its own view direction in the next frame, + // causing a visual glitch where the model rotates excessively + // The issue is more noticeable with high cmdrate values (e.g., cl_cmdrate >100) + // Disabled to avoid this artifact +#ifndef REGAMEDLL_FIXES pev->angles.y = UTIL_VecToAngles(-pev->velocity).y; pev->v_angle.y = pev->angles.y; +#endif m_iThrowDirection = THROW_NONE; } @@ -2556,11 +2658,19 @@ BOOL CBasePlayer::IsBombGuy() LINK_HOOK_CLASS_VOID_CHAIN(CBasePlayer, SetAnimation, (PLAYER_ANIM playerAnim), playerAnim) +int CBasePlayer::GetAnimDesired(const char *szAnim, AnimationType type) +{ + const char *refAnim = (type == ANIM_CROUCH && (pev->flags & FL_DUCKING)) ? "crouch_" : "ref_"; + + char szAnimConstruct[128]; + Q_snprintf(szAnimConstruct, sizeof(szAnimConstruct), "%s%s_%s", refAnim, szAnim, m_szAnimExtention); + return LookupSequence(szAnimConstruct); +} + void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) { int animDesired; float speed; - char szAnim[64]; int hopSeq; int leapSeq; @@ -2696,16 +2806,17 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) if (m_Activity == m_IdealActivity) return; + const char *refAnim; + switch (m_Activity) { - case ACT_RANGE_ATTACK1: Q_strcpy(szAnim, "ref_shoot_"); break; - case ACT_RANGE_ATTACK2: Q_strcpy(szAnim, "ref_shoot2_"); break; - case ACT_RELOAD: Q_strcpy(szAnim, "ref_reload_"); break; - default: Q_strcpy(szAnim, "ref_aim_"); break; + case ACT_RANGE_ATTACK1: refAnim = "shoot"; break; + case ACT_RANGE_ATTACK2: refAnim = "shoot2"; break; + case ACT_RELOAD: refAnim = "reload"; break; + default: refAnim = "aim"; break; } - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired(refAnim, ANIM_NORMAL); if (animDesired == -1) animDesired = 0; @@ -2727,13 +2838,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) { m_flLastFired = gpGlobals->time; - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_shoot_"); - else - Q_strcpy(szAnim, "ref_shoot_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("shoot", ANIM_CROUCH); if (animDesired == -1) animDesired = 0; @@ -2748,13 +2853,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) { m_flLastFired = gpGlobals->time; - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_shoot2_"); - else - Q_strcpy(szAnim, "ref_shoot2_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("shoot2", ANIM_CROUCH); if (animDesired == -1) animDesired = 0; @@ -2767,13 +2866,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) } case ACT_RELOAD: { - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_reload_"); - else - Q_strcpy(szAnim, "ref_reload_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("reload", ANIM_CROUCH); if (animDesired == -1) animDesired = 0; @@ -2788,13 +2881,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) } case ACT_HOLDBOMB: { - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_aim_"); - else - Q_strcpy(szAnim, "ref_aim_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("aim", ANIM_CROUCH); if (animDesired == -1) animDesired = 0; @@ -2811,13 +2898,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) { if (speed <= 135.0f || m_flLastFired + 4.0 >= gpGlobals->time) { - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_aim_"); - else - Q_strcpy(szAnim, "ref_aim_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("aim", ANIM_CROUCH); if (animDesired == -1) animDesired = 0; @@ -2825,18 +2906,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) } else { - Q_strcpy(szAnim, "run_"); - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("run", ANIM_NORMAL); if (animDesired == -1) { - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_aim_"); - else - Q_strcpy(szAnim, "ref_aim_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("aim", ANIM_CROUCH); if (animDesired == -1) animDesired = 0; @@ -2974,10 +3047,8 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) break; case 3: case 4: -#ifndef REGAMEDLL_FIXES m_iThrowDirection = THROW_FORWARD; break; -#endif case 5: case 6: m_iThrowDirection = THROW_HITVEL; @@ -3086,20 +3157,9 @@ void EXT_FUNC CBasePlayer::__API_HOOK(SetAnimation)(PLAYER_ANIM playerAnim) { if (m_Activity != ACT_FLINCH && m_Activity != ACT_LARGE_FLINCH) { - Q_strcpy(szAnim, "run_"); - Q_strcat(szAnim, m_szAnimExtention); - - animDesired = LookupSequence(szAnim); + animDesired = GetAnimDesired("run", ANIM_NORMAL); if (animDesired == -1) - { - if (pev->flags & FL_DUCKING) - Q_strcpy(szAnim, "crouch_aim_"); - else - Q_strcpy(szAnim, "ref_aim_"); - - Q_strcat(szAnim, m_szAnimExtention); - animDesired = LookupSequence(szAnim); - } + animDesired = GetAnimDesired("aim", ANIM_CROUCH); else pev->gaitsequence = animDesired; @@ -4200,7 +4260,7 @@ void CBasePlayer::PlayerUse() } } - bool useNewHostages = !TheNavAreaList.empty() && AreImprovAllowed(); + bool useNewHostages = !TheNavAreaList.empty() && cv_hostage_ai_enable.value; CBaseEntity *pObject = nullptr; CBaseEntity *pClosest = nullptr; Vector vecLOS; @@ -4310,10 +4370,10 @@ void CBasePlayer::PlayerUse() } // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away // BUGBUG This is an "off" use - else if ((m_afButtonReleased & IN_USE) + else if ((m_afButtonReleased & IN_USE) #ifdef REGAMEDLL_FIXES && (caps & FCAP_ONOFF_USE)) -#else +#else && (pObject->ObjectCaps() & FCAP_ONOFF_USE)) #endif { @@ -5047,8 +5107,7 @@ void CBasePlayer::CheckSuitUpdate() { // play sentence number char sentence[MAX_SENTENCE_NAME + 1]; - Q_strcpy(sentence, "!"); - Q_strcat(sentence, gszallsentencenames[isentence]); + Q_snprintf(sentence, sizeof(sentence), "!%s", gszallsentencenames[isentence]); EMIT_SOUND_SUIT(ENT(pev), sentence); } else @@ -5186,12 +5245,9 @@ void CBasePlayer::UpdatePlayerSound() m_iExtraSoundTypes = 0; } - if (pSound) - { - pSound->m_vecOrigin = pev->origin; - pSound->m_iVolume = iVolume; - pSound->m_iType |= (bits_SOUND_PLAYER | m_iExtraSoundTypes); - } + pSound->m_vecOrigin = pev->origin; + pSound->m_iVolume = iVolume; + pSound->m_iType |= (bits_SOUND_PLAYER | m_iExtraSoundTypes); // keep track of virtual muzzle flash m_iWeaponFlash -= 256 * gpGlobals->frametime; @@ -5369,22 +5425,24 @@ void EXT_FUNC CBasePlayer::__API_HOOK(PostThink)() } // checks if the spot is clear of players -BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot) +BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot, float fRadius) { if (!pSpot->IsTriggered(pPlayer)) return FALSE; -#ifdef REGAMEDLL_ADD - if (!kill_filled_spawn.value) - return TRUE; -#endif - CBaseEntity *pEntity = nullptr; - while ((pEntity = UTIL_FindEntityInSphere(pEntity, pSpot->pev->origin, MAX_PLAYER_USE_RADIUS))) + + while ((pEntity = UTIL_FindEntityInSphere(pEntity, pSpot->pev->origin, fRadius))) { // if ent is a client, don't spawn on 'em - if (pEntity->IsPlayer() && pEntity != pPlayer) + if (pEntity->IsPlayer() && pEntity != pPlayer +#ifdef REGAMEDLL_FIXES + && pEntity->IsAlive() +#endif + ) + { return FALSE; + } } return TRUE; @@ -5409,17 +5467,34 @@ bool CBasePlayer::SelectSpawnSpot(const char *pEntClassName, CBaseEntity *&pSpot { if (pSpot) { - // check if pSpot is valid - if (IsSpawnPointValid(this, pSpot)) +#ifdef REGAMEDLL_ADD + if (FClassnameIs(pSpot->edict(), "info_spawn_point")) { - if (pSpot->pev->origin == Vector(0, 0, 0)) + if (!IsSpawnPointValid(this, pSpot, 512.0f) || pSpot->pev->origin == Vector(0, 0, 0)) { pSpot = UTIL_FindEntityByClassname(pSpot, pEntClassName); continue; } + else + { + return true; + } + } + else +#endif + { + // check if pSpot is valid + if (IsSpawnPointValid(this, pSpot, MAX_PLAYER_USE_RADIUS)) + { + if (pSpot->pev->origin == Vector(0, 0, 0)) + { + pSpot = UTIL_FindEntityByClassname(pSpot, pEntClassName); + continue; + } - // if so, go to pSpot - return true; + // if so, go to pSpot + return true; + } } } @@ -5477,6 +5552,24 @@ edict_t *EXT_FUNC CBasePlayer::__API_HOOK(EntSelectSpawnPoint)() if (!FNullEnt(pSpot)) goto ReturnSpot; } +#ifdef REGAMEDLL_ADD + else if (randomspawn.value > 0) + { + pSpot = g_pLastSpawn; + + if (SelectSpawnSpot("info_spawn_point", pSpot)) + { + g_pLastSpawn = pSpot; + + return pSpot->edict(); + } + + if (m_iTeam == CT) + goto CTSpawn; + else if (m_iTeam == TERRORIST) + goto TSpawn; + } +#endif // VIP spawn point else if (g_pGameRules->IsDeathmatch() && m_bIsVIP) { @@ -5504,6 +5597,9 @@ edict_t *EXT_FUNC CBasePlayer::__API_HOOK(EntSelectSpawnPoint)() // The terrorist spawn points else if (g_pGameRules->IsDeathmatch() && m_iTeam == TERRORIST) { +#ifdef REGAMEDLL_ADD +TSpawn: +#endif pSpot = g_pLastTerroristSpawn; if (SelectSpawnSpot("info_player_deathmatch", pSpot)) @@ -5946,7 +6042,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Spawn)() TheBots->OnEvent(EVENT_PLAYER_SPAWNED, this); } - m_allowAutoFollowTime = false; + m_allowAutoFollowTime = 0.0f; for (i = 0; i < COMMANDS_TO_TRACK; i++) m_flLastCommandTime[i] = -1; @@ -8073,13 +8169,15 @@ void CBasePlayer::InitStatusBar() m_SbarString0[0] = '\0'; } -void CBasePlayer::UpdateStatusBar() +LINK_HOOK_CLASS_VOID_CHAIN2(CBasePlayer, UpdateStatusBar) + +void EXT_FUNC CBasePlayer::__API_HOOK(UpdateStatusBar)() { int newSBarState[SBAR_END]; char sbuf0[MAX_SBAR_STRING]; Q_memset(newSBarState, 0, sizeof(newSBarState)); - Q_strcpy(sbuf0, m_SbarString0); + Q_strlcpy(sbuf0, m_SbarString0); // Find an ID Target TraceResult tr; @@ -8109,11 +8207,19 @@ void CBasePlayer::UpdateStatusBar() if (sameTeam || GetObserverMode() != OBS_NONE) { if (playerid.value != PLAYERID_MODE_OFF || GetObserverMode() != OBS_NONE) - Q_strcpy(sbuf0, "1 %c1: %p2\n2 %h: %i3%%"); +#ifndef REGAMEDLL_ADD + Q_strlcpy(sbuf0, "1 %c1: %p2\n2 %h: %i3%%"); +#else + Q_strlcpy(sbuf0, GetPlayerIdString(sameTeam)); +#endif else - Q_strcpy(sbuf0, " "); - - newSBarState[SBAR_ID_TARGETHEALTH] = int((pEntity->pev->health / pEntity->pev->max_health) * 100); + Q_strlcpy(sbuf0, " "); +#ifdef REGAMEDLL_ADD + if (static_cast(playerid_showhealth.value) != PLAYERID_FIELD_NONE) +#endif + { + newSBarState[SBAR_ID_TARGETHEALTH] = int((pEntity->pev->health / pEntity->pev->max_health) * 100); + } if (!(m_flDisplayHistory & DHF_FRIEND_SEEN) && !(pev->flags & FL_SPECTATOR)) { @@ -8124,10 +8230,18 @@ void CBasePlayer::UpdateStatusBar() else if (GetObserverMode() == OBS_NONE) { if (playerid.value != PLAYERID_MODE_TEAMONLY && playerid.value != PLAYERID_MODE_OFF) - Q_strcpy(sbuf0, "1 %c1: %p2"); +#ifndef REGAMEDLL_ADD + Q_strlcpy(sbuf0, "1 %c1: %p2"); +#else + Q_strlcpy(sbuf0, GetPlayerIdString(sameTeam)); +#endif else - Q_strcpy(sbuf0, " "); + Q_strlcpy(sbuf0, " "); +#ifdef REGAMEDLL_ADD + if (static_cast(playerid_showhealth.value) == PLAYERID_ALL) + newSBarState[SBAR_ID_TARGETHEALTH] = int((pEntity->pev->health / pEntity->pev->max_health) * 100); +#endif if (!(m_flDisplayHistory & DHF_ENEMY_SEEN)) { m_flDisplayHistory |= DHF_ENEMY_SEEN; @@ -8140,9 +8254,9 @@ void CBasePlayer::UpdateStatusBar() else if (pEntity->Classify() == CLASS_HUMAN_PASSIVE) { if (playerid.value != PLAYERID_MODE_OFF || GetObserverMode() != OBS_NONE) - Q_strcpy(sbuf0, "1 %c1 %h: %i3%%"); + Q_strlcpy(sbuf0, "1 %c1 %h: %i3%%"); else - Q_strcpy(sbuf0, " "); + Q_strlcpy(sbuf0, " "); newSBarState[SBAR_ID_TARGETTYPE] = SBAR_TARGETTYPE_HOSTAGE; newSBarState[SBAR_ID_TARGETHEALTH] = int((pEntity->pev->health / pEntity->pev->max_health) * 100); @@ -8184,7 +8298,7 @@ void CBasePlayer::UpdateStatusBar() WRITE_STRING(sbuf0); MESSAGE_END(); - Q_strcpy(m_SbarString0, sbuf0); + Q_strlcpy(m_SbarString0, sbuf0); // make sure everything's resent bForceResend = true; @@ -8503,7 +8617,7 @@ void CBasePlayer::__API_HOOK(SwitchTeam)() UpdateLocation(true); - if (m_iTeam) + if (m_iTeam != UNASSIGNED) { SetScoreboardAttributes(); } @@ -9375,14 +9489,10 @@ void CBasePlayer::AddAutoBuyData(const char *str) { if (len > 0) { - Q_strncat(m_autoBuyString, " ", len); + Q_strlcat(m_autoBuyString, " "); } -#ifndef REGAMEDLL_FIXES - Q_strncat(m_autoBuyString, str, sizeof(m_autoBuyString) - Q_strlen(m_autoBuyString)); -#else - Q_strncat(m_autoBuyString, str, sizeof(m_autoBuyString) - Q_strlen(m_autoBuyString) - 1); -#endif + Q_strlcat(m_autoBuyString, str); } } @@ -9399,9 +9509,7 @@ void CBasePlayer::InitRebuyData(const char *str) m_rebuyString = nullptr; } - m_rebuyString = new char[Q_strlen(str) + 1]; - Q_strcpy(m_rebuyString, str); - m_rebuyString[Q_strlen(str)] = '\0'; + m_rebuyString = CloneString(str); } void CBasePlayer::AutoBuy() @@ -9429,7 +9537,7 @@ void CBasePlayer::AutoBuy() if (c) { - Q_strcpy(prioritizedString, c); + Q_strlcpy(prioritizedString, c); PrioritizeAutoBuyString(prioritizedString, m_autoBuyString); ParseAutoBuyString(prioritizedString, boughtPrimary, boughtSecondary); @@ -9439,7 +9547,7 @@ void CBasePlayer::AutoBuy() if (c) { - Q_strcpy(prioritizedString, c); + Q_strlcpy(prioritizedString, c); PrioritizeAutoBuyString(prioritizedString, m_autoBuyString); ParseAutoBuyString(prioritizedString, boughtPrimary, boughtSecondary); @@ -9587,11 +9695,11 @@ const char *CBasePlayer::PickPrimaryCareerTaskWeapon() CCareerTask *pTask = taskVector[i]; if (IsPrimaryWeaponId(pTask->GetWeaponId())) - Q_strncat(buf, WeaponIDToAlias(pTask->GetWeaponId()), sizeof(buf) - Q_strlen(buf) - 1); + Q_strlcat(buf, WeaponIDToAlias(pTask->GetWeaponId())); else - Q_strncat(buf, GetBuyStringForWeaponClass(pTask->GetWeaponClassId()), sizeof(buf) - Q_strlen(buf) - 1); + Q_strlcat(buf, GetBuyStringForWeaponClass(pTask->GetWeaponClassId())); - Q_strncat(buf, " ", sizeof(buf) - Q_strlen(buf) - 1); + Q_strlcat(buf, " "); } return buf; @@ -9662,11 +9770,11 @@ const char *CBasePlayer::PickSecondaryCareerTaskWeapon() CCareerTask *pTask = taskVector[i]; if (IsSecondaryWeaponId(pTask->GetWeaponId())) - Q_strncat(buf, WeaponIDToAlias(pTask->GetWeaponId()), sizeof(buf) - Q_strlen(buf) - 1); + Q_strlcat(buf, WeaponIDToAlias(pTask->GetWeaponId())); else - Q_strncat(buf, GetBuyStringForWeaponClass(pTask->GetWeaponClassId()), sizeof(buf) - Q_strlen(buf) - 1); + Q_strlcat(buf, GetBuyStringForWeaponClass(pTask->GetWeaponClassId())); - Q_strncat(buf, " ", sizeof(buf) - Q_strlen(buf) - 1); + Q_strlcat(buf, " "); } return buf; @@ -9715,13 +9823,13 @@ const char *CBasePlayer::PickGrenadeKillWeaponString() } // PostAutoBuyCommandProcessing - reorders the tokens in autobuyString based on the order of tokens in the priorityString. -void CBasePlayer::PrioritizeAutoBuyString(char *autobuyString, const char *priorityString) +void CBasePlayer::PrioritizeAutoBuyString(char (&autobuyString)[MAX_AUTOBUY_LENGTH], const char *priorityString) { char newString[MAX_AUTOBUY_LENGTH]; int newStringPos = 0; char priorityToken[32]; - if (!priorityString || !autobuyString) + if (!priorityString) return; const char *priorityChar = priorityString; @@ -9788,7 +9896,7 @@ void CBasePlayer::PrioritizeAutoBuyString(char *autobuyString, const char *prior // terminate the string. Trailing spaces shouldn't matter. newString[newStringPos] = '\0'; - Q_sprintf(autobuyString, "%s", newString); + Q_snprintf(autobuyString, sizeof(autobuyString), "%s", newString); } void CBasePlayer::ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary) @@ -10158,7 +10266,11 @@ bool CBasePlayer::IsObservingPlayer(CBasePlayer *pPlayer) void CBasePlayer::UpdateLocation(bool forceUpdate) { +#ifdef REGAMEDLL_FIXES + if (!forceUpdate && m_flLastUpdateTime > gpGlobals->time - 2.0f) +#else if (!forceUpdate && m_flLastUpdateTime >= gpGlobals->time + 2.0f) +#endif return; const char *placeName = nullptr; @@ -10758,6 +10870,17 @@ bool CBasePlayer::Kill() return true; } +LINK_HOOK_CLASS_VOID_CHAIN(CBasePlayer, TakeDamageImpulse, (CBasePlayer *pAttacker, float flKnockbackForce, float flVelModifier), pAttacker, flKnockbackForce, flVelModifier) + +void EXT_FUNC CBasePlayer::__API_HOOK(TakeDamageImpulse)(CBasePlayer *pAttacker, float flKnockbackForce, float flVelModifier) +{ + if (flKnockbackForce != 0.0f) + pev->velocity += (pev->origin - pAttacker->pev->origin).Normalize() * flKnockbackForce; + + if (flVelModifier != 0.0f) + m_flVelocityModifier = flVelModifier; +} + const usercmd_t *CBasePlayer::GetLastUserCommand() const { #ifdef REGAMEDLL_API diff --git a/regamedll/dlls/player.h b/regamedll/dlls/player.h index 182c4a379..f3ec649c3 100644 --- a/regamedll/dlls/player.h +++ b/regamedll/dlls/player.h @@ -450,6 +450,8 @@ class CBasePlayer: public CBaseMonster { void PlayerDeathThink_OrigFunc(); void Observer_Think_OrigFunc(); void RemoveAllItems_OrigFunc(BOOL removeSuit); + void UpdateStatusBar_OrigFunc(); + void TakeDamageImpulse_OrigFunc(CBasePlayer *pAttacker, float flKnockbackForce, float flVelModifier); CCSPlayer *CSPlayer() const; #endif // REGAMEDLL_API @@ -522,7 +524,9 @@ class CBasePlayer: public CBaseMonster { void UpdatePlayerSound(); void DeathSound(); void SetAnimation(PLAYER_ANIM playerAnim); - void SetWeaponAnimType(const char *szExtention) { Q_strcpy(m_szAnimExtention, szExtention); } + enum AnimationType { ANIM_NORMAL, ANIM_CROUCH }; + int GetAnimDesired(const char *szAnim, AnimationType type); + void SetWeaponAnimType(const char *szExtention) { Q_strlcpy(m_szAnimExtention, szExtention); } void CheatImpulseCommands(int iImpulse); void StartDeathCam(); void StartObserver(Vector &vecPosition, Vector &vecViewAngle); @@ -602,7 +606,7 @@ class CBasePlayer: public CBaseMonster { void AddAutoBuyData(const char *str); void AutoBuy(); void ClientCommand(const char *cmd, const char *arg1 = nullptr, const char *arg2 = nullptr, const char *arg3 = nullptr); - void PrioritizeAutoBuyString(char *autobuyString, const char *priorityString); + void PrioritizeAutoBuyString(char (&autobuyString)[MAX_AUTOBUY_LENGTH], const char *priorityString); const char *PickPrimaryCareerTaskWeapon(); const char *PickSecondaryCareerTaskWeapon(); const char *PickFlashKillWeaponString(); @@ -656,6 +660,7 @@ class CBasePlayer: public CBaseMonster { void UseEmpty(); void DropIdlePlayer(const char *reason); bool Kill(); + void TakeDamageImpulse(CBasePlayer *pAttacker, float flKnockbackForce, float flVelModifier); // templates template @@ -1042,7 +1047,7 @@ int TrainSpeed(int iSpeed, int iMax); void LogAttack(CBasePlayer *pAttacker, CBasePlayer *pVictim, int teamAttack, int healthHit, int armorHit, int newHealth, int newArmor, const char *killer_weapon_name); bool CanSeeUseable(CBasePlayer *me, CBaseEntity *pEntity); void FixPlayerCrouchStuck(edict_t *pPlayer); -BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot); +BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot, float fRadius); CBaseEntity *FindEntityForward(CBaseEntity *pMe); real_t GetPlayerPitch(const edict_t *pEdict); real_t GetPlayerYaw(const edict_t *pEdict); diff --git a/regamedll/dlls/qstring.h b/regamedll/dlls/qstring.h index 0b158292b..1f5d679dd 100644 --- a/regamedll/dlls/qstring.h +++ b/regamedll/dlls/qstring.h @@ -30,16 +30,18 @@ #define QSTRING_DEFINE -constexpr unsigned int iStringNull = {0}; - // Quake string (helper class) class QString final { public: +#if XASH_64BIT + using qstring_t = int; +#else using qstring_t = unsigned int; +#endif - QString(): m_string(iStringNull) {}; - QString(qstring_t string): m_string(string) {}; + QString(); + QString(qstring_t string); bool IsNull() const; bool IsNullOrEmpty() const; @@ -52,13 +54,15 @@ class QString final bool operator==(const char *pszString) const; operator const char *() const; - operator unsigned int() const; + operator qstring_t() const; const char *str() const; private: qstring_t m_string; }; +constexpr QString::qstring_t iStringNull = {0}; + #ifdef USE_QSTRING #define string_t QString #endif @@ -70,10 +74,25 @@ class QString final extern globalvars_t *gpGlobals; -#define STRING(offset) ((const char *)(gpGlobals->pStringBase + (unsigned int)(offset))) -#define MAKE_STRING(str) ((unsigned int)(str) - (unsigned int)(STRING(0))) +#define STRING(offset) ((const char *)(gpGlobals->pStringBase + (QString::qstring_t)(offset))) +#if XASH_64BIT +// Xash3D FWGS in 64-bit mode has internal string pool which allows mods to continue use 32-bit string_t +inline int MAKE_STRING(const char *str) +{ + ptrdiff_t diff = str - STRING(0); + if (diff >= INT_MIN && diff <= INT_MAX) + return (int)diff; + + return ALLOC_STRING(str); +} +#else +#define MAKE_STRING(str) ((QString::qstring_t)(str) - (QString::qstring_t)(STRING(0))) +#endif // Inlines +inline QString::QString(): m_string(iStringNull) {} +inline QString::QString(qstring_t string): m_string(string) {} + inline bool QString::IsNull() const { return m_string == iStringNull; @@ -115,7 +134,7 @@ inline QString::operator const char *() const return str(); } -inline QString::operator unsigned int() const +inline QString::operator qstring_t() const { return m_string; } diff --git a/regamedll/dlls/saverestore.cpp b/regamedll/dlls/saverestore.cpp index a3a55c7f6..514ca64b8 100644 --- a/regamedll/dlls/saverestore.cpp +++ b/regamedll/dlls/saverestore.cpp @@ -250,10 +250,10 @@ edict_t *CSaveRestoreBuffer::EntityFromIndex(int entityIndex) int CSaveRestoreBuffer::EntityFlagsSet(int entityIndex, int flags) { - if (!m_pData || entityIndex < 0) + if (!m_pData) return 0; - if (!m_pData || entityIndex < 0 || entityIndex > m_pData->tableCount) + if (entityIndex < 0 || entityIndex > m_pData->tableCount) return 0; m_pData->pTable[entityIndex].flags |= flags; @@ -962,8 +962,8 @@ void CGlobalState::EntityAdd(string_t globalname, string_t mapName, GLOBALESTATE pNewEntity->pNext = m_pList; m_pList = pNewEntity; - Q_strcpy(pNewEntity->name, STRING(globalname)); - Q_strcpy(pNewEntity->levelName, STRING(mapName)); + Q_strlcpy(pNewEntity->name, STRING(globalname)); + Q_strlcpy(pNewEntity->levelName, STRING(mapName)); pNewEntity->state = state; m_listCount++; @@ -1068,7 +1068,7 @@ void CGlobalState::EntityUpdate(string_t globalname, string_t mapname) globalentity_t *pEnt = Find(globalname); if (pEnt) { - Q_strcpy(pEnt->levelName, STRING(mapname)); + Q_strlcpy(pEnt->levelName, STRING(mapname)); } } diff --git a/regamedll/dlls/skill.cpp b/regamedll/dlls/skill.cpp index 87fd4468f..3c0bfe0d3 100644 --- a/regamedll/dlls/skill.cpp +++ b/regamedll/dlls/skill.cpp @@ -10,7 +10,7 @@ NOXREF float GetSkillCvar(char *pName) float flValue; char szBuffer[64]; - iCount = Q_sprintf(szBuffer, "%s%d", pName, gSkillData.iSkillLevel); + iCount = Q_snprintf(szBuffer, sizeof(szBuffer), "%s%d", pName, gSkillData.iSkillLevel); flValue = CVAR_GET_FLOAT(szBuffer); if (flValue <= 0.0f) diff --git a/regamedll/dlls/sound.cpp b/regamedll/dlls/sound.cpp index baf5353cc..25e930e3d 100644 --- a/regamedll/dlls/sound.cpp +++ b/regamedll/dlls/sound.cpp @@ -1040,11 +1040,10 @@ void USENTENCEG_InitLRU(unsigned char *plru, int count) // ipick is passed in as the requested sentence ordinal. // ipick 'next' is returned. // return of -1 indicates an error. -int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset) +int USENTENCEG_PickSequential(int isentenceg, char (&szfound)[64], int ipick, int freset) { char *szgroupname; unsigned char count; - char sznum[12]; if (!fSentencesInit) return -1; @@ -1061,10 +1060,7 @@ int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int fres if (ipick >= count) ipick = count - 1; - Q_strcpy(szfound, "!"); - Q_strcat(szfound, szgroupname); - Q_snprintf(sznum, sizeof(sznum), "%d", ipick); - Q_strcat(szfound, sznum); + Q_snprintf(szfound, sizeof(szfound), "!%s%d", szgroupname, ipick); if (ipick >= count) { @@ -1084,13 +1080,12 @@ int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int fres // rest of the lru filled with -1. The first integer in the lru is // actually the size of the list. Returns ipick, the ordinal // of the picked sentence within the group. -int USENTENCEG_Pick(int isentenceg, char *szfound) +int USENTENCEG_Pick(int isentenceg, char (&szfound)[64]) { char *szgroupname; unsigned char *plru; unsigned char i; unsigned char count; - char sznum[12]; unsigned char ipick = 0xFF; BOOL ffound = FALSE; @@ -1119,11 +1114,7 @@ int USENTENCEG_Pick(int isentenceg, char *szfound) if (ffound) { - Q_strcpy(szfound, "!"); - Q_strcat(szfound, szgroupname); - Q_snprintf(sznum, sizeof(sznum), "%d", ipick); - Q_strcat(szfound, sznum); - + Q_snprintf(szfound, sizeof(szfound), "!%s%d", szgroupname, ipick); return ipick; } else @@ -1168,8 +1159,6 @@ int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, float atte if (!fSentencesInit) return -1; - name[0] = '\0'; - ipick = USENTENCEG_Pick(isentenceg, name); #ifndef REGAMEDLL_FIXES @@ -1194,8 +1183,6 @@ int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname, float volume, if (!fSentencesInit) return -1; - name[0] = '\0'; - isentenceg = SENTENCEG_GetIndex(szgroupname); if (isentenceg < 0) { @@ -1223,8 +1210,6 @@ int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szgroupname, float v if (!fSentencesInit) return -1; - name[0] = '\0'; - isentenceg = SENTENCEG_GetIndex(szgroupname); if (isentenceg < 0) return -1; @@ -1323,7 +1308,7 @@ void SENTENCEG_Init() ALERT(at_warning, "Sentence %s longer than %d letters\n", pString, MAX_SENTENCE_NAME - 1); } - Q_strcpy(gszallsentencenames[gcallsentences++], pString); + Q_strlcpy(gszallsentencenames[gcallsentences++], pString); if (--j <= i) continue; @@ -1354,10 +1339,10 @@ void SENTENCEG_Init() break; } - Q_strcpy(rgsentenceg[isentencegs].szgroupname, &(buffer[i])); + Q_strlcpy(rgsentenceg[isentencegs].szgroupname, &(buffer[i])); rgsentenceg[isentencegs].count = 1; - Q_strcpy(szgroup, &(buffer[i])); + Q_strlcpy(szgroup, &(buffer[i])); continue; } @@ -1377,7 +1362,7 @@ void SENTENCEG_Init() i = 0; - while (rgsentenceg[i].count && i < MAX_SENTENCE_GROUPS) + while (i < MAX_SENTENCE_GROUPS && rgsentenceg[i].count) { USENTENCEG_InitLRU(&(rgsentenceg[i].rgblru[0]), rgsentenceg[i].count); i++; @@ -1385,9 +1370,8 @@ void SENTENCEG_Init() } // convert sentence (sample) name to !sentencenum, return !sentencenum -int SENTENCEG_Lookup(const char *sample, char *sentencenum) +int SENTENCEG_Lookup(const char *sample, char (&sentencenum)[32]) { - char sznum[12]; int i; // this is a sentence name; lookup sentence number @@ -1396,13 +1380,7 @@ int SENTENCEG_Lookup(const char *sample, char *sentencenum) { if (!Q_stricmp(gszallsentencenames[i], sample + 1)) { - if (sentencenum) - { - Q_strcpy(sentencenum, "!"); - Q_snprintf(sznum, sizeof(sznum), "%d", i); - Q_strcat(sentencenum, sznum); - } - + Q_snprintf(sentencenum, sizeof(sentencenum), "!%d", i); return i; } } @@ -1580,7 +1558,7 @@ void TEXTURETYPE_Init() j = Q_min(j, MAX_TEXTURENAME_LENGHT - 1 + i); buffer[j] = '\0'; - Q_strcpy(&(grgszTextureName[gcTextures++][0]), &(buffer[i])); + Q_strlcpy(grgszTextureName[gcTextures++], &(buffer[i])); } FREE_FILE(pMemFile); @@ -1616,7 +1594,7 @@ float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int char chTextureType; float fvol; float fvolbar; - char szBuffer[64]; + char szBuffer[MAX_TEXTURENAME_LENGHT]; const char *pTextureName; float rgfl1[3]; float rgfl2[3]; @@ -1666,8 +1644,7 @@ float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int pTextureName++; // '}}' - Q_strcpy(szBuffer, pTextureName); - szBuffer[MAX_TEXTURENAME_LENGHT - 1] = '\0'; + Q_strlcpy(szBuffer, pTextureName); // get texture type chTextureType = TEXTURETYPE_Find(szBuffer); diff --git a/regamedll/dlls/sound.h b/regamedll/dlls/sound.h index d2f0b41a7..b4e716fbc 100644 --- a/regamedll/dlls/sound.h +++ b/regamedll/dlls/sound.h @@ -170,15 +170,13 @@ class CSpeaker: public CBaseEntity BOOL FEnvSoundInRange(entvars_t *pev, entvars_t *pevTarget, float *pflRange); void USENTENCEG_InitLRU(unsigned char *plru, int count); -int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset); -int USENTENCEG_Pick(int isentenceg, char *szfound); int SENTENCEG_GetIndex(const char *szgroupname); int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, float attenuation, int flags, int pitch); int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname, float volume, float attenuation, int flags, int pitch); int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szgroupname, float volume, float attenuation, int flags, int pitch, int ipick, int freset); void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); void SENTENCEG_Init(); -int SENTENCEG_Lookup(const char *sample, char *sentencenum); +int SENTENCEG_Lookup(const char *sample, char (&sentencenum)[32]); void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch); void EMIT_SOUND_SUIT(edict_t *entity, const char *sample); void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg); diff --git a/regamedll/dlls/triggers.cpp b/regamedll/dlls/triggers.cpp index 82bf6907a..63282c06e 100644 --- a/regamedll/dlls/triggers.cpp +++ b/regamedll/dlls/triggers.cpp @@ -585,7 +585,6 @@ void CTriggerCDAudio::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP PlayTrack(pCaller->edict()); } -#ifdef REGAMEDLL_FIXES const char *g_szMP3trackFileMap[] = { "", "", @@ -617,7 +616,6 @@ const char *g_szMP3trackFileMap[] = "media/Suspense05.mp3", "media/Suspense07.mp3" }; -#endif void PlayCDTrack(edict_t *pClient, int iTrack) { @@ -625,7 +623,7 @@ void PlayCDTrack(edict_t *pClient, int iTrack) if (!pClient) return; - if (iTrack < -1 || iTrack > 30) + if (iTrack < -1 || iTrack >= (int)ARRAYSIZE(g_szMP3trackFileMap)) { ALERT(at_console, "TriggerCDAudio - Track %d out of range\n", iTrack); return; @@ -645,7 +643,7 @@ void PlayCDTrack(edict_t *pClient, int iTrack) CLIENT_COMMAND(pClient, UTIL_VarArgs("mp3 play %s\n", g_szMP3trackFileMap[iTrack])); #else char string[64]; - Q_sprintf(string, "cd play %3d\n", iTrack); + Q_snprintf(string, sizeof(string), "cd play %3d\n", iTrack); CLIENT_COMMAND(pClient, string); #endif } @@ -1214,7 +1212,7 @@ void CChangeLevel::KeyValue(KeyValueData *pkvd) ALERT(at_error, "Map name '%s' too long (32 chars)\n", pkvd->szValue); } - Q_strcpy(m_szMapName, pkvd->szValue); + Q_strlcpy(m_szMapName, pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "landmark")) @@ -1224,7 +1222,7 @@ void CChangeLevel::KeyValue(KeyValueData *pkvd) ALERT(at_error, "Landmark name '%s' too long (32 chars)\n", pkvd->szValue); } - Q_strcpy(m_szLandmarkName, pkvd->szValue); + Q_strlcpy(m_szLandmarkName, pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "changetarget")) @@ -1356,7 +1354,7 @@ void CChangeLevel::ChangeLevelNow(CBaseEntity *pActivator) } // This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory - Q_strcpy(st_szNextMap, m_szMapName); + Q_strlcpy(st_szNextMap, m_szMapName); m_hActivator = pActivator; SUB_UseTargets(pActivator, USE_TOGGLE, 0); @@ -1369,7 +1367,7 @@ void CChangeLevel::ChangeLevelNow(CBaseEntity *pActivator) if (!FNullEnt(pentLandmark)) { - Q_strcpy(st_szNextSpot, m_szLandmarkName); + Q_strlcpy(st_szNextSpot, m_szLandmarkName); gpGlobals->vecLandmarkOffset = VARS(pentLandmark)->origin; } @@ -1415,8 +1413,8 @@ int CChangeLevel::AddTransitionToList(LEVELLIST *pLevelList, int listCount, cons } } - Q_strcpy(pLevelList[listCount].mapName, pMapName); - Q_strcpy(pLevelList[listCount].landmarkName, pLandmarkName); + Q_strlcpy(pLevelList[listCount].mapName, pMapName); + Q_strlcpy(pLevelList[listCount].landmarkName, pLandmarkName); pLevelList[listCount].pentLandmark = pentLandmark; pLevelList[listCount].vecLandmarkOrigin = VARS(pentLandmark)->origin; @@ -1591,12 +1589,12 @@ NOXREF void NextLevel() { gpGlobals->mapname = ALLOC_STRING("start"); pChange = GetClassPtr((CChangeLevel *)nullptr); - Q_strcpy(pChange->m_szMapName, "start"); + Q_strlcpy(pChange->m_szMapName, "start"); } else pChange = GetClassPtr((CChangeLevel *)VARS(pent)); - Q_strcpy(st_szNextMap, pChange->m_szMapName); + Q_strlcpy(st_szNextMap, pChange->m_szMapName); g_pGameRules->SetGameOver(); if (pChange->pev->nextthink < gpGlobals->time) diff --git a/regamedll/dlls/tutor_base_tutor.cpp b/regamedll/dlls/tutor_base_tutor.cpp index 2268d18b7..a44ef4728 100644 --- a/regamedll/dlls/tutor_base_tutor.cpp +++ b/regamedll/dlls/tutor_base_tutor.cpp @@ -68,12 +68,10 @@ void TutorMessageEvent::AddParameter(char *str) TutorMessageEventParam *param = new TutorMessageEventParam; param->m_next = nullptr; - param->m_data = new char[Q_strlen(str) + 1]; + param->m_data = CloneString(str); if (param->m_data) { - Q_strcpy(param->m_data, str); - param->m_data[Q_strlen(str)] = '\0'; m_numParameters++; if (m_paramList) @@ -101,11 +99,7 @@ char *TutorMessageEvent::GetNextParameter(char *buf, int buflen) m_numParameters--; m_paramList = param->m_next; - Q_strncpy(buf, param->m_data, buflen); - -#ifdef REGAMEDLL_FIXES - buf[buflen - 1] = '\0'; -#endif + Q_strlcpy(buf, param->m_data, buflen); delete param; return buf; diff --git a/regamedll/dlls/tutor_cs_tutor.cpp b/regamedll/dlls/tutor_cs_tutor.cpp index 18e5d2b67..aa6bdd4bc 100644 --- a/regamedll/dlls/tutor_cs_tutor.cpp +++ b/regamedll/dlls/tutor_cs_tutor.cpp @@ -213,7 +213,7 @@ void ParseMessageParameters(char *&messageData, TutorMessage *ret) if (!Q_stricmp(token, "String")) { messageData = SharedParse((char *)messageData); - ret->m_text = Q_strdup(SharedGetToken()); + ret->m_text = CloneString(SharedGetToken()); } else if (!Q_stricmp(token, "Duration")) { @@ -832,7 +832,7 @@ TutorMessageEvent *CCSTutor::CreateTutorMessageEvent(TutorMessageID mid, CBaseEn { numtasks = TheCareerTasks->GetNumRemainingTasks(); } - Q_sprintf(numLeftStr, "%d", numtasks); + Q_snprintf(numLeftStr, sizeof(numLeftStr), "%d", numtasks); event->AddParameter(numLeftStr); break; } @@ -2820,8 +2820,7 @@ void CCSTutor::ConstructRecentDeathsList(TeamName team, char *buf, int buflen, T if (!buf || !buflen) return; - char scratch[32]; - buf[0] = '\0'; + int len = 0; for (int i = 1; i <= gpGlobals->maxClients; i++) { @@ -2837,10 +2836,7 @@ void CCSTutor::ConstructRecentDeathsList(TeamName team, char *buf, int buflen, T if (pPlayer->m_iTeam != team) continue; - Q_strcat(buf, " %n"); - Q_sprintf(scratch, "%d\n", i); - Q_strcat(buf, scratch); - + len += Q_snprintf(&buf[len], buflen - len, " %%n%d\n", i); m_playerDeathInfo[i].m_event = event; } } diff --git a/regamedll/dlls/util.cpp b/regamedll/dlls/util.cpp index 7841b006b..e0ffed532 100644 --- a/regamedll/dlls/util.cpp +++ b/regamedll/dlls/util.cpp @@ -690,10 +690,7 @@ void UTIL_Log(const char *fmt, ...) Q_vsnprintf(string, sizeof(string), fmt, ap); va_end(ap); - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; + Q_strlcat(string, "\n"); FILE *fp = fopen("regamedll.log", "at"); if (fp) @@ -717,10 +714,7 @@ void UTIL_ServerPrint(const char *fmt, ...) Q_vsnprintf(string, sizeof(string), fmt, ap); va_end(ap); - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; + Q_strlcat(string, "\n"); SERVER_PRINT(string); } @@ -738,10 +732,7 @@ void UTIL_PrintConsole(edict_t *pEdict, const char *fmt, ...) Q_vsnprintf(string, sizeof(string), fmt, ap); va_end(ap); - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; + Q_strlcat(string, "\n"); ClientPrint(pEntity->pev, HUD_PRINTCONSOLE, string); } @@ -759,10 +750,7 @@ void UTIL_SayText(edict_t *pEdict, const char *fmt, ...) Q_vsnprintf(string, sizeof(string), fmt, ap); va_end(ap); - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; + Q_strlcat(string, "\n"); MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pEntity->edict()); WRITE_BYTE(pEntity->entindex()); @@ -781,28 +769,28 @@ void UTIL_SayTextAll(const char *pText, CBaseEntity *pEntity) char *UTIL_dtos1(int d) { static char buf[12]; - Q_sprintf(buf, "%d", d); + Q_snprintf(buf, sizeof(buf), "%d", d); return buf; } char *UTIL_dtos2(int d) { static char buf[12]; - Q_sprintf(buf, "%d", d); + Q_snprintf(buf, sizeof(buf), "%d", d); return buf; } NOXREF char *UTIL_dtos3(int d) { static char buf[12]; - Q_sprintf(buf, "%d", d); + Q_snprintf(buf, sizeof(buf), "%d", d); return buf; } NOXREF char *UTIL_dtos4(int d) { static char buf[12]; - Q_sprintf(buf, "%d", d); + Q_snprintf(buf, sizeof(buf), "%d", d); return buf; } @@ -991,7 +979,7 @@ char *UTIL_VarArgs(char *format, ...) static char string[1024]; va_start(argptr, format); - vsprintf(string, format, argptr); + Q_vsnprintf(string, sizeof(string), format, argptr); va_end(argptr); return string; @@ -1476,7 +1464,7 @@ void UTIL_Remove(CBaseEntity *pEntity) pEntity->pev->targetname = 0; } -NOXREF BOOL UTIL_IsValidEntity(edict_t *pent) +BOOL UTIL_IsValidEntity(edict_t *pent) { if (!pent || pent->free || (pent->v.flags & FL_KILLME)) return FALSE; @@ -1561,7 +1549,7 @@ void UTIL_LogPrintf(const char *fmt, ...) static char string[1024]; va_start(argptr, fmt); - vsprintf(string, fmt, argptr); + Q_vsnprintf(string, sizeof(string), fmt, argptr); va_end(argptr); ALERT(at_logged, "%s", string); @@ -1580,7 +1568,7 @@ char UTIL_TextureHit(TraceResult *ptr, Vector vecSrc, Vector vecEnd) float rgfl1[3]; float rgfl2[3]; const char *pTextureName; - char szbuffer[64]; + char szbuffer[MAX_TEXTURENAME_LENGHT]; CBaseEntity *pEntity = CBaseEntity::Instance(ptr->pHit); #ifdef REGAMEDLL_FIXES @@ -1606,8 +1594,8 @@ char UTIL_TextureHit(TraceResult *ptr, Vector vecSrc, Vector vecEnd) if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') pTextureName++; - Q_strcpy(szbuffer, pTextureName); - szbuffer[16] = '\0'; + Q_strlcpy(szbuffer, pTextureName); + chTextureType = TEXTURETYPE_Find(szbuffer); } else @@ -1672,14 +1660,12 @@ int UTIL_ReadFlags(const char *c) // Determine whether bots can be used or not bool UTIL_AreBotsAllowed() { -#ifdef REGAMEDLL_ADD - if (g_engfuncs.pfnEngCheckParm == nullptr) - return false; -#endif - if (AreRunningCZero()) { #ifdef REGAMEDLL_ADD + if (g_engfuncs.pfnEngCheckParm == nullptr) + return false; + // If they pass in -nobots, don't allow bots. This is for people who host servers, to // allow them to disallow bots to enforce CPU limits. int nobots = ENG_CHECK_PARM("-nobots", nullptr); @@ -1699,15 +1685,10 @@ bool UTIL_AreBotsAllowed() return true; } - // allow the using of bots for CS 1.6 - int bots = ENG_CHECK_PARM("-bots", nullptr); - if (bots) - { - return true; - } -#endif - + return cv_bot_enable.value > 0; +#else return false; +#endif } bool UTIL_IsBeta() @@ -1740,18 +1721,10 @@ bool UTIL_AreHostagesImprov() } #ifdef REGAMEDLL_ADD - if (g_engfuncs.pfnEngCheckParm == nullptr) - return false; - - // someday in CS 1.6 - int improv = ENG_CHECK_PARM("-host-improv", nullptr); - if (improv) - { - return true; - } -#endif - + return cv_hostage_ai_enable.value > 0; +#else return false; +#endif } int UTIL_GetNumPlayers() diff --git a/regamedll/dlls/vehicle.cpp b/regamedll/dlls/vehicle.cpp index 3ff84b941..7bf2d4184 100644 --- a/regamedll/dlls/vehicle.cpp +++ b/regamedll/dlls/vehicle.cpp @@ -126,7 +126,7 @@ void CFuncVehicle::Blocked(CBaseEntity *pOther) float minz = pev->origin.z; #ifdef REGAMEDLL_FIXES - float maxz = pev->origin.z + (2 * Q_abs(pev->mins.z - pev->maxs.z)); + float maxz = pev->origin.z + (2 * Q_abs(pev->mins.z - pev->maxs.z)); #else float maxz = pev->origin.z + (2 * Q_abs(int(pev->mins.z - pev->maxs.z))); #endif @@ -343,15 +343,20 @@ void CFuncVehicle::CheckTurning() if (pev->speed > 0) { UTIL_TraceLine(m_vFrontRight, m_vFrontRight - (gpGlobals->v_right * 16.0), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + + if (tr.flFraction != 1.0f) + { + m_iTurnAngle = 1; + } } else if (pev->speed < 0) { UTIL_TraceLine(m_vBackLeft, m_vBackLeft + (gpGlobals->v_right * 16.0), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); - } - if (tr.flFraction != 1.0f) - { - m_iTurnAngle = 1; + if (tr.flFraction != 1.0f) + { + m_iTurnAngle = 1; + } } } else if (m_iTurnAngle > 0) @@ -359,15 +364,20 @@ void CFuncVehicle::CheckTurning() if (pev->speed > 0) { UTIL_TraceLine(m_vFrontLeft, m_vFrontLeft + (gpGlobals->v_right * 16.0), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + + if (tr.flFraction != 1.0f) + { + m_iTurnAngle = -1; + } } else if (pev->speed < 0) { UTIL_TraceLine(m_vBackRight, m_vBackRight - (gpGlobals->v_right * 16.0), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); - } - if (tr.flFraction != 1.0f) - { - m_iTurnAngle = -1; + if (tr.flFraction != 1.0f) + { + m_iTurnAngle = -1; + } } } diff --git a/regamedll/dlls/weapons.cpp b/regamedll/dlls/weapons.cpp index 087f45eee..b98749cb6 100644 --- a/regamedll/dlls/weapons.cpp +++ b/regamedll/dlls/weapons.cpp @@ -618,11 +618,17 @@ void CBasePlayerItem::DefaultTouch(CBaseEntity *pOther) CBasePlayer *pPlayer = static_cast(pOther); if (pPlayer->m_bIsVIP - && m_iId != WEAPON_USP + && +#ifndef REGAMEDLL_FIXES + m_iId != WEAPON_USP && m_iId != WEAPON_GLOCK18 && m_iId != WEAPON_P228 && m_iId != WEAPON_DEAGLE - && m_iId != WEAPON_KNIFE) + && m_iId != WEAPON_KNIFE +#else + !IsSecondaryWeapon(m_iId) +#endif + ) { return; } @@ -652,11 +658,11 @@ void CBasePlayerWeapon::SetPlayerShieldAnim() if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shield"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shield"); } else { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); } } @@ -666,7 +672,7 @@ void CBasePlayerWeapon::ResetPlayerShieldAnim() { if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); } } } @@ -697,7 +703,7 @@ bool CBasePlayerWeapon::ShieldSecondaryFire(int iUpAnim, int iDownAnim) { m_iWeaponState &= ~WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); m_fMaxSpeed = 250.0f; m_pPlayer->m_bShieldDrawn = false; } @@ -705,7 +711,7 @@ bool CBasePlayerWeapon::ShieldSecondaryFire(int iUpAnim, int iDownAnim) { m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shielded"); m_fMaxSpeed = 180.0f; m_pPlayer->m_bShieldDrawn = true; } @@ -728,7 +734,7 @@ void EXT_FUNC CBasePlayerWeapon::__API_HOOK(KickBack)(float up_base, float later real_t flKickUp = up_base; float flKickLateral = lateral_base; - if (m_iShotsFired > 1) // consider == 0 case + if (m_iShotsFired > 1) // consider == 0 case { flKickUp += m_iShotsFired * up_modifier; flKickLateral += m_iShotsFired * lateral_modifier; @@ -1491,7 +1497,7 @@ BOOL EXT_FUNC CBasePlayerWeapon::__API_HOOK(DefaultDeploy)(char *szViewModel, ch m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); #endif model_name = m_pPlayer->pev->viewmodel; - Q_strcpy(m_pPlayer->m_szAnimExtention, szAnimExt); + Q_strlcpy(m_pPlayer->m_szAnimExtention, szAnimExt); SendWeaponAnim(iAnim, skiplocal); m_pPlayer->m_flNextAttack = 0.75f; @@ -1671,7 +1677,7 @@ void CBasePlayerWeapon::Holster(int skiplocal) m_fInReload = FALSE; m_pPlayer->pev->viewmodel = 0; m_pPlayer->pev->weaponmodel = 0; - + #ifdef REGAMEDLL_FIXES m_fInSpecialReload = 0; #endif diff --git a/regamedll/dlls/weapons.h b/regamedll/dlls/weapons.h index 9266d8434..252c4c5f1 100644 --- a/regamedll/dlls/weapons.h +++ b/regamedll/dlls/weapons.h @@ -1333,7 +1333,6 @@ class CM249: public CBasePlayerWeapon const float M3_MAX_SPEED = 230.0f; const float M3_DAMAGE = 20.0f; -const Vector M3_CONE_VECTOR = Vector(0.0675, 0.0675, 0.0); // special shotgun spreads enum m3_e { @@ -1764,7 +1763,6 @@ class CTMP: public CBasePlayerWeapon const float XM1014_MAX_SPEED = 240.0f; const float XM1014_DAMAGE = 20.0f; -const Vector XM1014_CONE_VECTOR = Vector(0.0725, 0.0725, 0.0); // special shotgun spreads enum xm1014_e { diff --git a/regamedll/dlls/weapontype.cpp b/regamedll/dlls/weapontype.cpp index dd7dc0f82..cc341d7c9 100644 --- a/regamedll/dlls/weapontype.cpp +++ b/regamedll/dlls/weapontype.cpp @@ -545,7 +545,7 @@ WeaponInfoStruct *GetDefaultWeaponInfo(int weaponID) AmmoInfoStruct *GetAmmoInfo(const char *ammoName) { for (auto& info : g_ammoInfo) { - if (!Q_stricmp(info.ammoName1, ammoName)) { + if (info.ammoName1 && !Q_stricmp(info.ammoName1, ammoName)) { return &info; } } diff --git a/regamedll/dlls/world.cpp b/regamedll/dlls/world.cpp index 55147af09..cc4a3de55 100644 --- a/regamedll/dlls/world.cpp +++ b/regamedll/dlls/world.cpp @@ -216,7 +216,7 @@ void CWorld::Spawn() Precache(); g_szMapBriefingText[0] = '\0'; - Q_sprintf(szMapBriefingFile, "maps/%s.txt", STRING(gpGlobals->mapname)); + Q_snprintf(szMapBriefingFile, sizeof(szMapBriefingFile), "maps/%s.txt", STRING(gpGlobals->mapname)); int flength = 0; char *pFile = (char *)LOAD_FILE_FOR_ME(szMapBriefingFile, &flength); diff --git a/regamedll/dlls/wpn_shared/wpn_aug.cpp b/regamedll/dlls/wpn_shared/wpn_aug.cpp index 988c74808..dfba3fdf9 100644 --- a/regamedll/dlls/wpn_shared/wpn_aug.cpp +++ b/regamedll/dlls/wpn_shared/wpn_aug.cpp @@ -73,6 +73,15 @@ void CAUG::SecondaryAttack() else m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 90; +#ifdef REGAMEDLL_FIXES + if (TheBots) + { + TheBots->OnEvent(EVENT_WEAPON_ZOOMED, m_pPlayer); + } + + m_pPlayer->ResetMaxSpeed(); +#endif + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.3f; } diff --git a/regamedll/dlls/wpn_shared/wpn_flashbang.cpp b/regamedll/dlls/wpn_shared/wpn_flashbang.cpp index 0443353e8..bbebb8ebf 100644 --- a/regamedll/dlls/wpn_shared/wpn_flashbang.cpp +++ b/regamedll/dlls/wpn_shared/wpn_flashbang.cpp @@ -114,7 +114,7 @@ bool CFlashbang::ShieldSecondaryFire(int iUpAnim, int iDownAnim) m_iWeaponState &= ~WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); m_fMaxSpeed = FLASHBANG_MAX_SPEED; m_pPlayer->m_bShieldDrawn = false; @@ -124,7 +124,7 @@ bool CFlashbang::ShieldSecondaryFire(int iUpAnim, int iDownAnim) m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shielded"); m_fMaxSpeed = FLASHBANG_MAX_SPEED_SHIELD; m_pPlayer->m_bShieldDrawn = true; @@ -151,9 +151,9 @@ void CFlashbang::SetPlayerShieldAnim() return; if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) - Q_strcpy(m_pPlayer->m_szAnimExtention, "shield"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shield"); else - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); } void CFlashbang::ResetPlayerShieldAnim() @@ -163,7 +163,7 @@ void CFlashbang::ResetPlayerShieldAnim() if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); } } diff --git a/regamedll/dlls/wpn_shared/wpn_hegrenade.cpp b/regamedll/dlls/wpn_shared/wpn_hegrenade.cpp index 49d8eb7a8..ca2c63adb 100644 --- a/regamedll/dlls/wpn_shared/wpn_hegrenade.cpp +++ b/regamedll/dlls/wpn_shared/wpn_hegrenade.cpp @@ -117,7 +117,7 @@ bool CHEGrenade::ShieldSecondaryFire(int iUpAnim, int iDownAnim) { m_iWeaponState &= ~WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); m_fMaxSpeed = HEGRENADE_MAX_SPEED; m_pPlayer->m_bShieldDrawn = false; @@ -126,7 +126,7 @@ bool CHEGrenade::ShieldSecondaryFire(int iUpAnim, int iDownAnim) { m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shielded"); m_fMaxSpeed = HEGRENADE_MAX_SPEED_SHIELD; m_pPlayer->m_bShieldDrawn = true; @@ -153,9 +153,9 @@ void CHEGrenade::SetPlayerShieldAnim() return; if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) - Q_strcpy(m_pPlayer->m_szAnimExtention, "shield"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shield"); else - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); } void CHEGrenade::ResetPlayerShieldAnim() @@ -165,7 +165,7 @@ void CHEGrenade::ResetPlayerShieldAnim() if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); } } diff --git a/regamedll/dlls/wpn_shared/wpn_knife.cpp b/regamedll/dlls/wpn_shared/wpn_knife.cpp index fcbf2d949..43a2f28f2 100644 --- a/regamedll/dlls/wpn_shared/wpn_knife.cpp +++ b/regamedll/dlls/wpn_shared/wpn_knife.cpp @@ -180,7 +180,7 @@ void CKnife::SetPlayerShieldAnim() if (!m_pPlayer->HasShield()) return; - Q_strcpy(m_pPlayer->m_szAnimExtention, (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) != 0 ? "shield" : "shieldknife"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) != 0 ? "shield" : "shieldknife"); } void CKnife::ResetPlayerShieldAnim() @@ -190,7 +190,7 @@ void CKnife::ResetPlayerShieldAnim() if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldknife"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldknife"); } } @@ -207,7 +207,7 @@ bool CKnife::ShieldSecondaryFire(int iUpAnim, int iDownAnim) SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldknife"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldknife"); m_fMaxSpeed = KNIFE_MAX_SPEED; m_pPlayer->m_bShieldDrawn = false; @@ -217,7 +217,7 @@ bool CKnife::ShieldSecondaryFire(int iUpAnim, int iDownAnim) m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shielded"); m_fMaxSpeed = KNIFE_MAX_SPEED_SHIELD; m_pPlayer->m_bShieldDrawn = true; @@ -339,14 +339,14 @@ BOOL CKnife::Swing(BOOL fFirst) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.0f; // play wiff or swish sound - EMIT_SOUND_DYN(m_pPlayer->edict(), - CHAN_WEAPON, - RANDOM_LONG(0, 1) ? + EMIT_SOUND_DYN(m_pPlayer->edict(), + CHAN_WEAPON, + RANDOM_LONG(0, 1) ? "weapons/knife_slash1.wav" : - "weapons/knife_slash2.wav", - VOL_NORM, - ATTN_NORM, - 0, + "weapons/knife_slash2.wav", + VOL_NORM, + ATTN_NORM, + 0, 94); // player "shoot" animation @@ -390,10 +390,10 @@ BOOL CKnife::Swing(BOOL fFirst) m_pPlayer->SetAnimation(PLAYER_ATTACK1); ClearMultiDamage(); - pEntity->TraceAttack(m_pPlayer->pev, + pEntity->TraceAttack(m_pPlayer->pev, KnifeSwingDamage(m_flNextPrimaryAttack + 0.4f < UTIL_WeaponTimeBase()), - gpGlobals->v_forward, - &tr, + gpGlobals->v_forward, + &tr, (DMG_NEVERGIB | DMG_BULLET)); ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev); @@ -402,7 +402,7 @@ BOOL CKnife::Swing(BOOL fFirst) if (pEntity) // -V595 #endif { - if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE + if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE #ifdef REGAMEDLL_FIXES && pEntity->Classify() != CLASS_VEHICLE #endif @@ -518,14 +518,14 @@ BOOL CKnife::Stab(BOOL fFirst) m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0f; // play wiff or swish sound - EMIT_SOUND_DYN(m_pPlayer->edict(), - CHAN_WEAPON, - RANDOM_LONG(0, 1) ? - "weapons/knife_slash1.wav" : - "weapons/knife_slash2.wav", - VOL_NORM, - ATTN_NORM, - 0, + EMIT_SOUND_DYN(m_pPlayer->edict(), + CHAN_WEAPON, + RANDOM_LONG(0, 1) ? + "weapons/knife_slash1.wav" : + "weapons/knife_slash2.wav", + VOL_NORM, + ATTN_NORM, + 0, 94); // player "shoot" animation @@ -586,7 +586,7 @@ BOOL CKnife::Stab(BOOL fFirst) if (pEntity) // -V595 #endif { - if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE + if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE #ifdef REGAMEDLL_FIXES && pEntity->Classify() != CLASS_VEHICLE #endif diff --git a/regamedll/dlls/wpn_shared/wpn_m3.cpp b/regamedll/dlls/wpn_shared/wpn_m3.cpp index 64ad7c5f0..2f341c951 100644 --- a/regamedll/dlls/wpn_shared/wpn_m3.cpp +++ b/regamedll/dlls/wpn_shared/wpn_m3.cpp @@ -1,5 +1,7 @@ #include "precompiled.h" +const Vector M3_CONE_VECTOR = Vector(0.0675, 0.0675, 0.0); // special shotgun spreads + LINK_ENTITY_TO_CLASS(weapon_m3, CM3, CCSM3) void CM3::Spawn() diff --git a/regamedll/dlls/wpn_shared/wpn_m4a1.cpp b/regamedll/dlls/wpn_shared/wpn_m4a1.cpp index 89ea81b5b..e9bb062e2 100644 --- a/regamedll/dlls/wpn_shared/wpn_m4a1.cpp +++ b/regamedll/dlls/wpn_shared/wpn_m4a1.cpp @@ -82,13 +82,13 @@ void CM4A1::SecondaryAttack() { m_iWeaponState &= ~WPNSTATE_M4A1_SILENCED; SendWeaponAnim(M4A1_DETACH_SILENCER, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "rifle"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "rifle"); } else { m_iWeaponState |= WPNSTATE_M4A1_SILENCED; SendWeaponAnim(M4A1_ATTACH_SILENCER, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "rifle"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "rifle"); } m_flTimeWeaponIdle = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 2.0f; diff --git a/regamedll/dlls/wpn_shared/wpn_sg552.cpp b/regamedll/dlls/wpn_shared/wpn_sg552.cpp index e86eec412..f9eb61f5f 100644 --- a/regamedll/dlls/wpn_shared/wpn_sg552.cpp +++ b/regamedll/dlls/wpn_shared/wpn_sg552.cpp @@ -72,6 +72,15 @@ void CSG552::SecondaryAttack() else m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 90; +#ifdef REGAMEDLL_FIXES + if (TheBots) + { + TheBots->OnEvent(EVENT_WEAPON_ZOOMED, m_pPlayer); + } + + m_pPlayer->ResetMaxSpeed(); +#endif + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.3f; } diff --git a/regamedll/dlls/wpn_shared/wpn_smokegrenade.cpp b/regamedll/dlls/wpn_shared/wpn_smokegrenade.cpp index ae13158b4..56c5e27bc 100644 --- a/regamedll/dlls/wpn_shared/wpn_smokegrenade.cpp +++ b/regamedll/dlls/wpn_shared/wpn_smokegrenade.cpp @@ -117,7 +117,7 @@ bool CSmokeGrenade::ShieldSecondaryFire(int iUpAnim, int iDownAnim) m_iWeaponState &= ~WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); m_fMaxSpeed = SMOKEGRENADE_MAX_SPEED; m_pPlayer->m_bShieldDrawn = false; @@ -127,7 +127,7 @@ bool CSmokeGrenade::ShieldSecondaryFire(int iUpAnim, int iDownAnim) m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shielded"); m_fMaxSpeed = SMOKEGRENADE_MAX_SPEED_SHIELD; m_pPlayer->m_bShieldDrawn = true; @@ -154,9 +154,9 @@ void CSmokeGrenade::SetPlayerShieldAnim() return; if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) - Q_strcpy(m_pPlayer->m_szAnimExtention, "shield"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shield"); else - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); } void CSmokeGrenade::ResetPlayerShieldAnim() @@ -166,7 +166,7 @@ void CSmokeGrenade::ResetPlayerShieldAnim() if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "shieldgren"); } } diff --git a/regamedll/dlls/wpn_shared/wpn_usp.cpp b/regamedll/dlls/wpn_shared/wpn_usp.cpp index 5a474f652..de823f30e 100644 --- a/regamedll/dlls/wpn_shared/wpn_usp.cpp +++ b/regamedll/dlls/wpn_shared/wpn_usp.cpp @@ -98,14 +98,14 @@ void CUSP::SecondaryAttack() m_iWeaponState &= ~WPNSTATE_USP_SILENCED; SendWeaponAnim(USP_DETACH_SILENCER, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "onehanded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "onehanded"); } else { m_iWeaponState |= WPNSTATE_USP_SILENCED; SendWeaponAnim(USP_ATTACH_SILENCER, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "onehanded"); + Q_strlcpy(m_pPlayer->m_szAnimExtention, "onehanded"); } m_flNextSecondaryAttack = m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + USP_ADJUST_SIL_TIME; diff --git a/regamedll/dlls/wpn_shared/wpn_xm1014.cpp b/regamedll/dlls/wpn_shared/wpn_xm1014.cpp index a4e4304eb..c664fe597 100644 --- a/regamedll/dlls/wpn_shared/wpn_xm1014.cpp +++ b/regamedll/dlls/wpn_shared/wpn_xm1014.cpp @@ -1,5 +1,7 @@ #include "precompiled.h" +const Vector XM1014_CONE_VECTOR = Vector(0.0725, 0.0725, 0.0); // special shotgun spreads + LINK_ENTITY_TO_CLASS(weapon_xm1014, CXM1014, CCSXM1014) void CXM1014::Spawn() diff --git a/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd b/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd index 79fe8333a..cab21059a 100644 --- a/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd +++ b/regamedll/extra/Toolkit/GameDefinitionFile/regamedll-cs.fgd @@ -1,12 +1,17 @@ // ----------------------------------------------------------------------- // Counter-Strike game definition file (.fgd) -// Version 0.9.0.0 +// Version 0.9.5.0 // Valve Hammer Editor >= 3.5 // Last update: September 25th, 2016 // ----------------------------------------------------------------------- // Based on FGD 0.8.0.0 by Dmitrich! // -// Full compatibility with https://github.com/s1lentq/ReGameDLL_CS +// Full compatibility with https://github.com/rehlds/ReGameDLL_CS +// ----------------------------------------------------------------------- +// December 12th, 2024 (0.9.5.0) +// - Added target_cdaudio +// ----------------------------------------------------------------------- +// // ----------------------------------------------------------------------- // September 25th, 2016 - s1lent (0.9.0.0) // - Removed xen entities. @@ -2323,6 +2328,46 @@ ] ] +// This entity allows to play a specific CD track when the player is within the specified radius around the entity or when triggered +// A brush entity equivalent is available as trigger_cdaudio +@PointClass base(Targetname) color(255 240 128) = target_cdaudio : "CD Audio Target" +[ + health(choices) : "Track #" : -1 = + [ + -1 : "Stop" +// 1 : "None" + 2 : "Track 1" + 3 : "Track 2" + 4 : "Track 3" + 5 : "Track 4" + 6 : "Track 5" + 7 : "Track 6" + 8 : "Track 7" + 9 : "Track 8" + 10 : "Track 9" + 11 : "Track 10" + 12 : "Track 11" + 13 : "Track 12" + 14 : "Track 13" + 15 : "Track 14" + 16 : "Track 15" + 17 : "Track 16" + 18 : "Track 17" + 19 : "Track 18" + 20 : "Track 19" + 21 : "Track 20" + 22 : "Track 21" + 23 : "Track 22" + 24 : "Track 23" + 25 : "Track 24" + 26 : "Track 25" + 27 : "Track 26" + 28 : "Track 27" + 29 : "Track 28" + ] + scale(string) : "Player Radius" : "128.0" // The player must come within this radius for the track to start playing +] + @SolidClass base(Targetname) = trigger_changelevel : "Trigger Change level" [ map(string) : "New map name" @@ -3098,3 +3143,7 @@ @PointClass base(BaseCommand) size(-8 -8 -8, 8 8 8) = point_clientcommand : "It issues commands to the client console" [ ] + +@PointClass iconsprite("sprites/CS/info_player_start.spr") base(PlayerClass) = info_spawn_point : "Random spawn start" +[ +] \ No newline at end of file diff --git a/regamedll/game_shared/bot/bot.cpp b/regamedll/game_shared/bot/bot.cpp index 0f19e81f4..52d1f4db7 100644 --- a/regamedll/game_shared/bot/bot.cpp +++ b/regamedll/game_shared/bot/bot.cpp @@ -486,11 +486,11 @@ NOXREF void CBot::Print(char *format, ...) const char buffer[1024]; // prefix the message with the bot's name - Q_sprintf(buffer, "%s: ", STRING(pev->netname)); + Q_snprintf(buffer, sizeof(buffer), "%s: ", STRING(pev->netname)); SERVER_PRINT(buffer); va_start(varg, format); - vsprintf(buffer, format, varg); + Q_vsnprintf(buffer, sizeof(buffer), format, varg); va_end(varg); SERVER_PRINT(buffer); @@ -509,12 +509,12 @@ void CBot::PrintIfWatched(char *format, ...) const // prefix the message with the bot's name (this can be NULL if bot was just added) const char *name = pev ? STRING(pev->netname) : "(NULL pev)"; - Q_sprintf(buffer, "%s: ", name ? name : "(NULL netname)"); + Q_snprintf(buffer, sizeof(buffer), "%s: ", name ? name : "(NULL netname)"); SERVER_PRINT(buffer); va_start(varg, format); - vsprintf(buffer, format, varg); + Q_vsnprintf(buffer, sizeof(buffer), format, varg); va_end(varg); SERVER_PRINT(buffer); diff --git a/regamedll/game_shared/bot/bot_manager.cpp b/regamedll/game_shared/bot/bot_manager.cpp index 7ccd32814..3fa121ba7 100644 --- a/regamedll/game_shared/bot/bot_manager.cpp +++ b/regamedll/game_shared/bot/bot_manager.cpp @@ -213,7 +213,7 @@ void CBotManager::StartFrame() const char *CBotManager::GetNavMapFilename() const { static char filename[256]; - Q_sprintf(filename, "maps\\%s.nav", STRING(gpGlobals->mapname)); + Q_snprintf(filename, sizeof(filename), "maps\\%s.nav", STRING(gpGlobals->mapname)); return filename; } diff --git a/regamedll/game_shared/bot/bot_profile.cpp b/regamedll/game_shared/bot/bot_profile.cpp index c025f903b..76cb37a10 100644 --- a/regamedll/game_shared/bot/bot_profile.cpp +++ b/regamedll/game_shared/bot/bot_profile.cpp @@ -178,9 +178,10 @@ void BotProfileManager::Init(const char *filename, unsigned int *checksum) m_skins[m_nextSkin] = CloneString(decoratedName); // construct the model filename + int SkinLen = Q_strlen(token) * 2 + Q_strlen("models/player//.mdl"); m_skinModelnames[m_nextSkin] = CloneString(token); - m_skinFilenames[m_nextSkin] = new char[Q_strlen(token) * 2 + Q_strlen("models/player//.mdl") + 1]; - Q_sprintf(m_skinFilenames[m_nextSkin], "models/player/%s/%s.mdl", token, token); + m_skinFilenames[m_nextSkin] = new char[SkinLen + 1]; + Q_snprintf(m_skinFilenames[m_nextSkin], SkinLen + 1, "models/player/%s/%s.mdl", token, token); m_nextSkin++; } @@ -304,7 +305,7 @@ void BotProfileManager::Init(const char *filename, unsigned int *checksum) // found attribute name - keep it char attributeName[64]; - Q_strcpy(attributeName, token); + Q_strlcpy(attributeName, token); // eat '=' dataFile = SharedParse(dataFile); diff --git a/regamedll/game_shared/bot/bot_util.cpp b/regamedll/game_shared/bot/bot_util.cpp index 449b7e373..4d694b318 100644 --- a/regamedll/game_shared/bot/bot_util.cpp +++ b/regamedll/game_shared/bot/bot_util.cpp @@ -560,7 +560,7 @@ void CONSOLE_ECHO(const char *pszMsg, ...) static char szStr[1024]; va_start(argptr, pszMsg); - vsprintf(szStr, pszMsg, argptr); + Q_vsnprintf(szStr, sizeof(szStr), pszMsg, argptr); va_end(argptr); SERVER_PRINT(szStr); @@ -572,7 +572,7 @@ void CONSOLE_ECHO_LOGGED(const char *pszMsg, ...) static char szStr[1024]; va_start(argptr, pszMsg); - vsprintf(szStr, pszMsg, argptr); + Q_vsnprintf(szStr, sizeof(szStr), pszMsg, argptr); va_end(argptr); SERVER_PRINT(szStr); diff --git a/regamedll/game_shared/bot/nav.h b/regamedll/game_shared/bot/nav.h index a917aa993..d23a734e6 100644 --- a/regamedll/game_shared/bot/nav.h +++ b/regamedll/game_shared/bot/nav.h @@ -71,6 +71,13 @@ enum NavAttributeType NAV_NO_JUMP = 0x08, // inhibit discontinuity jumping }; +enum NavNotifyDestroyType +{ + NAV_NOTIFY_DESTROY_AREA, + NAV_NOTIFY_DESTROY_SPOT, + NAV_NOTIFY_DESTROY_SPOT_ENCOUNTER +}; + enum NavDirType { NORTH = 0, diff --git a/regamedll/game_shared/bot/nav_area.cpp b/regamedll/game_shared/bot/nav_area.cpp index 8e4baf0da..845b2a3fd 100644 --- a/regamedll/game_shared/bot/nav_area.cpp +++ b/regamedll/game_shared/bot/nav_area.cpp @@ -72,17 +72,29 @@ NOXREF void buildGoodSizedList() void DestroyHidingSpots() { + // free all the HidingSpots + for (HidingSpot *spot : TheHidingSpotList) + { + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_SPOT, spot); + delete spot; + } + + TheHidingSpotList.clear(); + // remove all hiding spot references from the nav areas - for (auto area : TheNavAreaList) + for (CNavArea *area : TheNavAreaList) + { area->m_hidingSpotList.clear(); - HidingSpot::m_nextID = 0; + // free all the HidingSpots in area + for (SpotEncounter &e : area->m_spotEncounterList) + e.spotList.clear(); - // free all the HidingSpots - for (auto spot : TheHidingSpotList) - delete spot; + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_SPOT_ENCOUNTER, area); + area->m_spotEncounterList.clear(); + } - TheHidingSpotList.clear(); + HidingSpot::m_nextID = 0; } // For use when loading from a file @@ -249,6 +261,9 @@ CNavArea::CNavArea(CNavNode *nwNode, class CNavNode *neNode, class CNavNode *seN // Destructor CNavArea::~CNavArea() { + // tell all bots that this area no longer exists + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_AREA, this); + // if we are resetting the system, don't bother cleaning up - all areas are being destroyed if (m_isReset) return; @@ -344,6 +359,7 @@ void CNavArea::FinishMerge(CNavArea *adjArea) // remove subsumed adjacent area TheNavAreaList.remove(adjArea); + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_AREA, this); delete adjArea; } @@ -536,6 +552,7 @@ bool CNavArea::SplitEdit(bool splitAlongX, float splitEdge, CNavArea **outAlpha, // remove original area TheNavAreaList.remove(this); + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_AREA, this); delete this; return true; @@ -868,6 +885,7 @@ bool CNavArea::MergeEdit(CNavArea *adj) // remove subsumed adjacent area TheNavAreaList.remove(adj); + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_AREA, adj); delete adj; return true; @@ -900,6 +918,9 @@ void DestroyNavigationMap() { CNavArea::m_isReset = true; + // destroy all hiding spots + DestroyHidingSpots(); + // remove each element of the list and delete them while (!TheNavAreaList.empty()) { @@ -913,8 +934,8 @@ void DestroyNavigationMap() // destroy ladder representations DestroyLadders(); - // destroy all hiding spots - DestroyHidingSpots(); + // cleanup from previous analysis + CleanupApproachAreaAnalysisPrep(); // destroy navigation nodes created during map learning CNavNode *node, *next; @@ -1772,6 +1793,13 @@ void GenerateNavigationAreaMesh() break; } + if (!TheNavAreaList.size()) + { + // If we somehow have no areas, don't try to create an impossibly-large grid + TheNavAreaGrid.Initialize(0, 0, 0, 0); + return; + } + Extent extent; extent.lo.x = 9999999999.9f; extent.lo.y = 9999999999.9f; @@ -2828,6 +2856,22 @@ SpotEncounter *CNavArea::GetSpotEncounter(const CNavArea *from, const CNavArea * return nullptr; } +// Checks if a SpotEncounter is present in the list of encounters for the given CNavArea +bool CNavArea::HasSpotEncounter(const SpotEncounter *encounter) +{ + SpotEncounter *e; + + for (SpotEncounterList::iterator iter = m_spotEncounterList.begin(); iter != m_spotEncounterList.end(); iter++) + { + e = &(*iter); + + if (e == encounter) + return true; + } + + return false; +} + // Add spot encounter data when moving from area to area void CNavArea::AddSpotEncounters(const class CNavArea *from, NavDirType fromDir, const CNavArea *to, NavDirType toDir) { @@ -3819,9 +3863,9 @@ void EditNavAreas(NavEditCmdType cmd) name = TheNavAreaGrid.IDToName(area->GetPlace()); if (name) - Q_strcpy(locName, name); + Q_strlcpy(locName, name); else - Q_strcpy(locName, "ERROR"); + Q_strlcpy(locName, "ERROR"); } else { @@ -3968,6 +4012,7 @@ void EditNavAreas(NavEditCmdType cmd) case EDIT_DELETE: EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/blip1.wav", 1, ATTN_NORM, 0, 100); TheNavAreaList.remove(area); + TheCSBots()->OnDestroyNavDataNotify(NAV_NOTIFY_DESTROY_AREA, area); delete area; return; case EDIT_ATTRIB_CROUCH: @@ -4636,6 +4681,12 @@ void CNavAreaGrid::Initialize(float minX, float maxX, float minY, float maxY) // Add an area to the grid void CNavAreaGrid::AddNavArea(CNavArea *area) { + if (!m_grid) + { + // If we somehow have no grid (manually creating a nav area without loading or generating a mesh), don't crash + TheNavAreaGrid.Initialize(0, 0, 0, 0); + } + // add to grid const Extent *extent = area->GetExtent(); diff --git a/regamedll/game_shared/bot/nav_area.h b/regamedll/game_shared/bot/nav_area.h index c4cf8a1ff..2792cd3b6 100644 --- a/regamedll/game_shared/bot/nav_area.h +++ b/regamedll/game_shared/bot/nav_area.h @@ -273,6 +273,7 @@ class CNavArea void ComputeHidingSpots(); // analyze local area neighborhood to find "hiding spots" in this area - for map learning void ComputeSniperSpots(); // analyze local area neighborhood to find "sniper spots" in this area - for map learning + bool HasSpotEncounter(const SpotEncounter *encounter); SpotEncounter *GetSpotEncounter(const CNavArea *from, const CNavArea *to); // given the areas we are moving between, return the spots we will encounter void ComputeSpotEncounters(); // compute spot encounter data - for map learning @@ -345,6 +346,25 @@ class CNavArea void AddLadderUp(CNavLadder *ladder) { m_ladder[LADDER_UP].push_back(ladder); } void AddLadderDown(CNavLadder *ladder) { m_ladder[LADDER_DOWN].push_back(ladder); } + inline float GetAreaSlope() + { + Vector u, v; + + // compute our unit surface normal + u.x = m_extent.hi.x - m_extent.lo.x; + u.y = 0.0f; + u.z = m_neZ - m_extent.lo.z; + + v.x = 0.0f; + v.y = m_extent.hi.y - m_extent.lo.y; + v.z = m_swZ - m_extent.lo.z; + + Vector normal = CrossProduct(u, v); + normal.NormalizeInPlace(); + + return normal.z; + } + private: friend void ConnectGeneratedAreas(); friend void MergeGeneratedAreas(); diff --git a/regamedll/game_shared/bot/nav_file.cpp b/regamedll/game_shared/bot/nav_file.cpp index c9f206fd9..e43a0200e 100644 --- a/regamedll/game_shared/bot/nav_file.cpp +++ b/regamedll/game_shared/bot/nav_file.cpp @@ -67,7 +67,7 @@ Place PlaceDirectory::EntryToPlace(EntryType entry) const return UNDEFINED_PLACE; unsigned int i = entry - 1; - if (i > m_directory.size()) + if (i >= m_directory.size()) { DbgAssert(false && "PlaceDirectory::EntryToPlace: Invalid entry"); return UNDEFINED_PLACE; @@ -623,6 +623,13 @@ bool SaveNavigationMap(const char *filename) area->Save(fd, version); } + // Ensure that all data is flushed to disk +#ifdef WIN32 + _commit(fd); +#else + fsync(fd); +#endif + _close(fd); return true; } @@ -632,12 +639,15 @@ bool SaveNavigationMap(const char *filename) void LoadLocationFile(const char *filename) { char locFilename[256]; - Q_strcpy(locFilename, filename); + Q_strlcpy(locFilename, filename); - char *dot = Q_strchr(locFilename, '.'); + char *dot = Q_strrchr(locFilename, '.'); if (dot) { - Q_strcpy(dot, ".loc"); + int dotlen = dot - locFilename; + size_t remaining_size = sizeof(locFilename) - dotlen; + if (remaining_size > 0) + Q_snprintf(dot, remaining_size, ".loc"); int locDataLength; char *locDataFile = (char *)LOAD_FILE_FOR_ME(const_cast(locFilename), &locDataLength); @@ -771,7 +781,7 @@ NavErrorType LoadNavigationMap() // nav filename is derived from map filename char filename[256]; - Q_sprintf(filename, "maps\\%s.nav", STRING(gpGlobals->mapname)); + Q_snprintf(filename, sizeof(filename), "maps\\%s.nav", STRING(gpGlobals->mapname)); // free previous navigation map data DestroyNavigationMap(); @@ -836,6 +846,11 @@ NavErrorType LoadNavigationMap() unsigned int count; result = navFile.Read(&count, sizeof(unsigned int)); + if (count == 0) + { + return NAV_INVALID_FILE; + } + Extent extent; extent.lo.x = 9999999999.9f; extent.lo.y = 9999999999.9f; diff --git a/regamedll/msvc/ReGameDLL.vcxproj b/regamedll/msvc/ReGameDLL.vcxproj index 57e09e917..80e93c922 100644 --- a/regamedll/msvc/ReGameDLL.vcxproj +++ b/regamedll/msvc/ReGameDLL.vcxproj @@ -809,6 +809,9 @@ + + + {70A2B904-B7DB-4C48-8DE0-AF567360D572} ReGameDLL @@ -929,7 +932,7 @@ Disabled true REGAMEDLL_ADD;REGAMEDLL_API;REGAMEDLL_FIXES;REGAMEDLL_SSE;REGAMEDLL_SELF;REGAMEDLL_CHECKS;UNICODE_FIXES;BUILD_LATEST;BUILD_LATEST_FIXES;CLIENT_WEAPONS;USE_QSTRING;_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions) - Fast + Precise /arch:IA32 %(AdditionalOptions) /Zc:threadSafeInit- %(AdditionalOptions) MultiThreadedDebug @@ -966,7 +969,7 @@ Full true REGAMEDLL_ADD;REGAMEDLL_API;REGAMEDLL_FIXES;REGAMEDLL_SSE;REGAMEDLL_SELF;REGAMEDLL_CHECKS;UNICODE_FIXES;BUILD_LATEST;BUILD_LATEST_FIXES;CLIENT_WEAPONS;USE_QSTRING;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) - Fast + Precise /arch:IA32 %(AdditionalOptions) /Zc:threadSafeInit- %(AdditionalOptions) MultiThreaded @@ -1099,6 +1102,7 @@ Use precompiled.h NoExtensions + Precise true diff --git a/regamedll/msvc/ReGameDLL.vcxproj.filters b/regamedll/msvc/ReGameDLL.vcxproj.filters index ef2017ae4..bfc6d6256 100644 --- a/regamedll/msvc/ReGameDLL.vcxproj.filters +++ b/regamedll/msvc/ReGameDLL.vcxproj.filters @@ -1070,4 +1070,9 @@ public\tier0 + + + regamedll + + \ No newline at end of file diff --git a/regamedll/pm_shared/pm_shared.cpp b/regamedll/pm_shared/pm_shared.cpp index fa106c09b..846b410d0 100644 --- a/regamedll/pm_shared/pm_shared.cpp +++ b/regamedll/pm_shared/pm_shared.cpp @@ -123,7 +123,7 @@ void PM_InitTextureTypes() j = Q_min(j, MAX_TEXTURENAME_LENGHT - 1 + i); buffer[j] = '\0'; - Q_strcpy(&(pm_grgszTextureName[pm_gcTextures++][0]), &(buffer[i])); + Q_strlcpy(pm_grgszTextureName[pm_gcTextures++], &(buffer[i])); } // Must use engine to free since we are in a .dll @@ -364,8 +364,7 @@ void PM_CatagorizeTextureType() if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') pTextureName++; - Q_strcpy(pmove->sztexturename, pTextureName); - pmove->sztexturename[MAX_TEXTURENAME_LENGHT - 1] = '\0'; + Q_strlcpy(pmove->sztexturename, pTextureName, MAX_TEXTURENAME_LENGHT); // get texture type pmove->chtexturetype = PM_FindTextureType(pmove->sztexturename); @@ -642,9 +641,184 @@ void PM_FixupGravityVelocity() pmove->velocity[2] -= (pmove->movevars->gravity * pmove->frametime * ent_gravity * 0.5); PM_CheckVelocity(); } +#ifdef REGAMEDLL_ADD +int PM_FlyMove_New() +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity; + vec3_t clipVelocity; + int i, j, k; + pmtrace_t trace; + vec3_t end; + float time_left; + float into; + vec3_t endVelocity; + vec3_t endClipVelocity; + int blocked; + + numbumps = 4; + + VectorCopy(pmove->velocity, primal_velocity); + + + time_left = pmove->frametime; + + numplanes = 0; + blocked = 0x00; // Assume not blocked + + // never turn against original velocity + VectorCopy(pmove->velocity, planes[numplanes]); + VectorNormalize(planes[numplanes]); + numplanes++; + + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { + + // calculate position we are trying to move to + VectorMA(pmove->origin, time_left, pmove->velocity, end); + // see if we can make it there + trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); + + if (trace.allsolid) { + // entity is completely trapped in another solid + pmove->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration + return 4; + } + + if (trace.fraction > 0) { + // actually covered some distance + VectorCopy(trace.endpos, pmove->origin); + } + + if (trace.fraction == 1) { + break; // moved the entire distance + } + + // save entity for contact + PM_AddToTouched(trace, pmove->velocity); + + // If the plane we hit has a high z component in the normal, then + // it's probably a floor + if (trace.plane.normal[2] > 0.7f) + { + // floor + blocked |= 0x01; + } + + // If the plane has a zero z component in the normal, then it's a + // step or wall + if (!trace.plane.normal[2]) + { + // step / wall + blocked |= 0x02; + } + + time_left -= time_left * trace.fraction; + + if (numplanes >= MAX_CLIP_PLANES) { + // this shouldn't really happen + VectorClear(pmove->velocity); + break; + } + + // + // if this is the same plane we hit before, nudge velocity + // out along it, which fixes some epsilon issues with + // non-axial planes + // + for (i = 0; i < numplanes; i++) { + if (DotProduct(trace.plane.normal, planes[i]) > 0.99) { + VectorAdd(trace.plane.normal, pmove->velocity, pmove->velocity); + break; + } + } + if (i < numplanes) { + continue; + } + VectorCopy(trace.plane.normal, planes[numplanes]); + numplanes++; + + // + // modify velocity so it parallels all of the clip planes + // + + // find a plane that it enters + for (i = 0; i < numplanes; i++) { + into = DotProduct(pmove->velocity, planes[i]); + if (into >= 0.1) { + continue; // move doesn't interact with the plane + } + + // slide along the plane + PM_ClipVelocity(pmove->velocity, planes[i], clipVelocity, 1); + + // slide along the plane + PM_ClipVelocity(endVelocity, planes[i], endClipVelocity, 1); + + // see if there is a second plane that the new move enters + for (j = 0; j < numplanes; j++) { + if (j == i) { + continue; + } + if (DotProduct(clipVelocity, planes[j]) >= 0.1) { + continue; // move doesn't interact with the plane + } + + // try clipping the move to the plane + PM_ClipVelocity(clipVelocity, planes[j], clipVelocity, 1); + PM_ClipVelocity(endClipVelocity, planes[j], endClipVelocity, 1); + + // see if it goes back into the first clip plane + if (DotProduct(clipVelocity, planes[i]) >= 0) { + continue; + } + + // slide the original velocity along the crease + CrossProduct(planes[i], planes[j], dir); + VectorNormalize(dir); + d = DotProduct(dir, pmove->velocity); + VectorScale(dir, d, clipVelocity); + + CrossProduct(planes[i], planes[j], dir); + VectorNormalize(dir); + d = DotProduct(dir, endVelocity); + VectorScale(dir, d, endClipVelocity); + + // see if there is a third plane the the new move enters + for (k = 0; k < numplanes; k++) { + if (k == i || k == j) { + continue; + } + if (DotProduct(clipVelocity, planes[k]) >= 0.1) { + continue; // move doesn't interact with the plane + } + + // stop dead at a tripple plane interaction + VectorClear(pmove->velocity); + return 4; + } + } + + // if we have fixed all interactions, try another move + VectorCopy(clipVelocity, pmove->velocity); + VectorCopy(endClipVelocity, endVelocity); + break; + } + } + + return blocked; +} +#endif int PM_FlyMove() { +#ifdef REGAMEDLL_ADD + if (flymove_method.value) + return PM_FlyMove_New(); +#endif int bumpcount, numbumps; vec3_t dir; float d; @@ -887,6 +1061,18 @@ void PM_WalkMove() { real_t flRatio = (100 - pmove->fuser2 * 0.001 * 19) * 0.01; +#ifdef REGAMEDLL_ADD + // change stamina restoration speed by fps reference + if (stamina_restore_rate.value > 0.0f) + { + real_t flReferenceFrametime = 1.0f / stamina_restore_rate.value; + + float flFrametimeRatio = pmove->frametime / flReferenceFrametime; + + flRatio = pow(flRatio, flFrametimeRatio); + } +#endif + pmove->velocity[0] *= flRatio; pmove->velocity[1] *= flRatio; } @@ -2430,13 +2616,18 @@ inline real_t PM_JumpHeight(bool longjump) #ifdef REGAMEDLL_API if (longjump) { - if(pmoveplayer->m_flLongJumpHeight > 0.0) + if (pmoveplayer->m_flLongJumpHeight > 0.0) return pmoveplayer->m_flLongJumpHeight; } else if (pmoveplayer->m_flJumpHeight > 0.0) return pmoveplayer->m_flJumpHeight; #endif + +#ifdef REGAMEDLL_ADD + return Q_sqrt(2.0 * 800.0f * (longjump ? 56.0f : Q_max(jump_height.value, 0.0f))); +#else return Q_sqrt(2.0 * 800.0f * (longjump ? 56.0f : 45.0f)); +#endif } LINK_HOOK_VOID_CHAIN2(PM_Jump) @@ -2607,6 +2798,7 @@ void EXT_FUNC __API_HOOK(PM_Jump)() { // NOTE: don't do it in .f (float) real_t flRatio = (100.0 - pmove->fuser2 * 0.001 * 19.0) * 0.01; + pmove->velocity[2] *= flRatio; } diff --git a/regamedll/public/build.h b/regamedll/public/build.h new file mode 100644 index 000000000..24fd9d5bb --- /dev/null +++ b/regamedll/public/build.h @@ -0,0 +1,269 @@ +/* +build.h - compile-time build information + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ +#pragma once +#ifndef BUILD_H +#define BUILD_H + +/* +All XASH_* macros set by this header are guaranteed to have positive value +otherwise not defined. + +Every macro is intended to be the unified interface for buildsystems that lack +platform & CPU detection, and a neat quick way for checks in platform code +For Q_build* macros, refer to buildenums.h + +Any new define must be undefined at first +You can generate #undef list below with this oneliner: + $ sed 's/\t//g' build.h | grep '^#define XASH' | awk '{ print $2 }' | \ + sort | uniq | awk '{ print "#undef " $1 }' + +Then you can use another oneliner to query all variables: + $ grep '^#undef XASH' build.h | awk '{ print $2 }' +*/ + +#undef XASH_64BIT +#undef XASH_AMD64 +#undef XASH_ANDROID +#undef XASH_APPLE +#undef XASH_ARM +#undef XASH_ARM_HARDFP +#undef XASH_ARM_SOFTFP +#undef XASH_ARMv4 +#undef XASH_ARMv5 +#undef XASH_ARMv6 +#undef XASH_ARMv7 +#undef XASH_ARMv8 +#undef XASH_BIG_ENDIAN +#undef XASH_DOS4GW +#undef XASH_E2K +#undef XASH_EMSCRIPTEN +#undef XASH_FREEBSD +#undef XASH_HAIKU +#undef XASH_IOS +#undef XASH_IRIX +#undef XASH_JS +#undef XASH_LINUX +#undef XASH_LITTLE_ENDIAN +#undef XASH_MIPS +#undef XASH_MOBILE_PLATFORM +#undef XASH_NETBSD +#undef XASH_OPENBSD +#undef XASH_POSIX +#undef XASH_PPC +#undef XASH_RISCV +#undef XASH_RISCV_DOUBLEFP +#undef XASH_RISCV_SINGLEFP +#undef XASH_RISCV_SOFTFP +#undef XASH_SERENITY +#undef XASH_SUNOS +#undef XASH_WIN32 +#undef XASH_X86 +#undef XASH_NSWITCH +#undef XASH_PSVITA +#undef XASH_WASI +#undef XASH_WASM + +//================================================================ +// +// PLATFORM DETECTION CODE +// +//================================================================ +#if defined _WIN32 + #define XASH_WIN32 1 +#elif defined __EMSCRIPTEN__ + #define XASH_EMSCRIPTEN 1 +#elif defined __WATCOMC__ && defined __DOS__ + #define XASH_DOS4GW 1 +#else // POSIX compatible + #define XASH_POSIX 1 + #if defined __linux__ + #if defined __ANDROID__ + #define XASH_ANDROID 1 + #endif + #define XASH_LINUX 1 + #elif defined __FreeBSD__ + #define XASH_FREEBSD 1 + #elif defined __NetBSD__ + #define XASH_NETBSD 1 + #elif defined __OpenBSD__ + #define XASH_OPENBSD 1 + #elif defined __HAIKU__ + #define XASH_HAIKU 1 + #elif defined __serenity__ + #define XASH_SERENITY 1 + #elif defined __sgi + #define XASH_IRIX 1 + #elif defined __APPLE__ + #include + #define XASH_APPLE 1 + #if TARGET_OS_IOS + #define XASH_IOS 1 + #endif // TARGET_OS_IOS + #elif defined __SWITCH__ + #define XASH_NSWITCH 1 + #elif defined __vita__ + #define XASH_PSVITA 1 + #elif defined __wasi__ + #define XASH_WASI 1 + #elif defined __sun__ + #define XASH_SUNOS 1 + #else + #error + #endif +#endif + +// XASH_SAILFISH is special: SailfishOS by itself is a normal GNU/Linux platform +// It doesn't make sense to split it to separate platform +// but we still need XASH_MOBILE_PLATFORM for the engine. +// So this macro is defined entirely in build-system: see main wscript +// HLSDK/PrimeXT/other SDKs users note: you may ignore this macro +#if XASH_ANDROID || XASH_IOS || XASH_NSWITCH || XASH_PSVITA || XASH_SAILFISH + #define XASH_MOBILE_PLATFORM 1 +#endif + +//================================================================ +// +// ENDIANNESS DEFINES +// +//================================================================ + +#if !defined XASH_ENDIANNESS + #if defined XASH_WIN32 || __LITTLE_ENDIAN__ + //!!! Probably all WinNT installations runs in little endian + #define XASH_LITTLE_ENDIAN 1 + #elif __BIG_ENDIAN__ + #define XASH_BIG_ENDIAN 1 + #elif defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ && defined __ORDER_LITTLE_ENDIAN__ // some compilers define this + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define XASH_BIG_ENDIAN 1 + #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define XASH_LITTLE_ENDIAN 1 + #endif + #else + #include + #if __BYTE_ORDER == __BIG_ENDIAN + #define XASH_BIG_ENDIAN 1 + #elif __BYTE_ORDER == __LITTLE_ENDIAN + #define XASH_LITTLE_ENDIAN 1 + #endif + #endif // !XASH_WIN32 +#endif + +//================================================================ +// +// CPU ARCHITECTURE DEFINES +// +//================================================================ +#if defined __x86_64__ || defined _M_X64 + #define XASH_64BIT 1 + #define XASH_AMD64 1 +#elif defined __i386__ || defined _X86_ || defined _M_IX86 + #define XASH_X86 1 +#elif defined __aarch64__ || defined _M_ARM64 + #define XASH_64BIT 1 + #define XASH_ARM 8 +#elif defined __mips__ + #define XASH_MIPS 1 +#elif defined __EMSCRIPTEN__ + #define XASH_JS 1 +#elif defined __e2k__ + #define XASH_64BIT 1 + #define XASH_E2K 1 +#elif defined __PPC__ || defined __powerpc__ + #define XASH_PPC 1 + #if defined __PPC64__ || defined __powerpc64__ + #define XASH_64BIT 1 + #endif +#elif defined _M_ARM // msvc + #define XASH_ARM 7 + #define XASH_ARM_HARDFP 1 +#elif defined __arm__ + #if __ARM_ARCH == 8 || __ARM_ARCH_8__ + #define XASH_ARM 8 + #elif __ARM_ARCH == 7 || __ARM_ARCH_7__ + #define XASH_ARM 7 + #elif __ARM_ARCH == 6 || __ARM_ARCH_6__ || __ARM_ARCH_6J__ + #define XASH_ARM 6 + #elif __ARM_ARCH == 5 || __ARM_ARCH_5__ + #define XASH_ARM 5 + #elif __ARM_ARCH == 4 || __ARM_ARCH_4__ + #define XASH_ARM 4 + #else + #error "Unknown ARM" + #endif + + #if defined __SOFTFP__ || __ARM_PCS_VFP == 0 + #define XASH_ARM_SOFTFP 1 + #else // __SOFTFP__ + #define XASH_ARM_HARDFP 1 + #endif // __SOFTFP__ +#elif defined __riscv + #define XASH_RISCV 1 + + #if __riscv_xlen == 64 + #define XASH_64BIT 1 + #elif __riscv_xlen != 32 + #error "Unknown RISC-V ABI" + #endif + + #if defined __riscv_float_abi_soft + #define XASH_RISCV_SOFTFP 1 + #elif defined __riscv_float_abi_single + #define XASH_RISCV_SINGLEFP 1 + #elif defined __riscv_float_abi_double + #define XASH_RISCV_DOUBLEFP 1 + #else + #error "Unknown RISC-V float ABI" + #endif +#elif defined __wasm__ + #if defined __wasm64__ + #define XASH_64BIT 1 + #endif + #define XASH_WASM 1 +#else + #error "Place your architecture name here! If this is a mistake, try to fix conditions above and report a bug" +#endif + +#if !XASH_64BIT && ( defined( __LP64__ ) || defined( _LP64 )) +#define XASH_64BIT 1 +#endif + +#if XASH_ARM == 8 + #define XASH_ARMv8 1 +#elif XASH_ARM == 7 + #define XASH_ARMv7 1 +#elif XASH_ARM == 6 + #define XASH_ARMv6 1 +#elif XASH_ARM == 5 + #define XASH_ARMv5 1 +#elif XASH_ARM == 4 + #define XASH_ARMv4 1 +#endif + +#endif // BUILD_H diff --git a/regamedll/public/regamedll/API/CSPlayer.h b/regamedll/public/regamedll/API/CSPlayer.h index 05b81b704..3ead37a22 100644 --- a/regamedll/public/regamedll/API/CSPlayer.h +++ b/regamedll/public/regamedll/API/CSPlayer.h @@ -117,6 +117,8 @@ class CCSPlayer: public CCSMonster { virtual void Reset(); virtual void OnSpawnEquip(bool addDefault = true, bool equipGame = true); virtual void SetScoreboardAttributes(CBasePlayer *destination = nullptr); + virtual void Observer_FindNextPlayer(bool bReverse, const char *name = nullptr); + virtual void TakeDamageImpulse(CBasePlayer *pAttacker, float flKnockbackForce, float flVelModifier); bool IsPlayerDominated(int iPlayerIndex) const; void SetPlayerDominated(CBasePlayer *pPlayer, bool bDominated); diff --git a/regamedll/public/regamedll/regamedll_api.h b/regamedll/public/regamedll/regamedll_api.h index 74879aa42..2e52c0c7c 100644 --- a/regamedll/public/regamedll/regamedll_api.h +++ b/regamedll/public/regamedll/regamedll_api.h @@ -38,7 +38,7 @@ #include #define REGAMEDLL_API_VERSION_MAJOR 5 -#define REGAMEDLL_API_VERSION_MINOR 28 +#define REGAMEDLL_API_VERSION_MINOR 31 // CBasePlayer::Spawn hook typedef IHookChainClass IReGameHook_CBasePlayer_Spawn; @@ -628,6 +628,14 @@ typedef IHookChainRegistryClass IReGameHookRegistry_CBa typedef IHookChainClass IReGameHook_CBasePlayer_RemoveAllItems; typedef IHookChainRegistryClass IReGameHookRegistry_CBasePlayer_RemoveAllItems; +// CBasePlayer::UpdateStatusBar hook +typedef IHookChainClass IReGameHook_CBasePlayer_UpdateStatusBar; +typedef IHookChainRegistryClass IReGameHookRegistry_CBasePlayer_UpdateStatusBar; + +// CBasePlayer::TakeDamageImpulse hook +typedef IHookChainClass IReGameHook_CBasePlayer_TakeDamageImpulse; +typedef IHookChainRegistryClass IReGameHookRegistry_CBasePlayer_TakeDamageImpulse; + class IReGameHookchains { public: virtual ~IReGameHookchains() {} @@ -790,6 +798,8 @@ class IReGameHookchains { virtual IReGameHookRegistry_CBasePlayer_PlayerDeathThink *CBasePlayer_PlayerDeathThink() = 0; virtual IReGameHookRegistry_CBasePlayer_Observer_Think *CBasePlayer_Observer_Think() = 0; virtual IReGameHookRegistry_CBasePlayer_RemoveAllItems *CBasePlayer_RemoveAllItems() = 0; + virtual IReGameHookRegistry_CBasePlayer_UpdateStatusBar *CBasePlayer_UpdateStatusBar() = 0; + virtual IReGameHookRegistry_CBasePlayer_TakeDamageImpulse *CBasePlayer_TakeDamageImpulse() = 0; }; struct ReGameFuncs_t { @@ -816,6 +826,18 @@ struct ReGameFuncs_t { void (*TextureTypePlaySound)(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType); class CWeaponBox *(*CreateWeaponBox)(CBasePlayerItem *pItem, CBasePlayer *pPlayerOwner, const char *modelName, Vector &origin, Vector &angles, Vector &velocity, float lifeTime, bool packAmmo); class CGrenade *(*SpawnGrenade)(WeaponIdType weaponId, entvars_t *pevOwner, Vector &vecSrc, Vector &vecThrow, float time, int iTeam, unsigned short usEvent); + int (*UTIL_SharedRandomLong)(unsigned int seed, int low, int high); + float (*UTIL_SharedRandomFloat)(unsigned int seed, float low, float high); + void (*UTIL_SetGroupTrace)(int groupmask, int op); + void (*UTIL_UnsetGroupTrace)(); + int (*UTIL_EntitiesInBox)(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask); + void (*UTIL_ScreenShake)(const Vector ¢er, float amplitude, float frequency, float duration, float radius); + void (*UTIL_ScreenFadeAll)(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); + void (*UTIL_ScreenFade)(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); + float (*UTIL_WaterLevel)(const Vector &position, float minz, float maxz); + void (*UTIL_Bubbles)(Vector mins, Vector maxs, int count); + void (*UTIL_BubbleTrail)(Vector from, Vector to, int count); + char (*UTIL_TextureHit)(TraceResult *ptr, Vector vecSrc, Vector vecEnd); }; class IReGameApi { diff --git a/regamedll/public/strtools.h b/regamedll/public/strtools.h index 5cdf8c41d..11279c76e 100644 --- a/regamedll/public/strtools.h +++ b/regamedll/public/strtools.h @@ -156,40 +156,48 @@ inline char *Q_strlcpy(char *dest, const char *src, size_t size) { // a safe variant of strcpy that truncates the result to fit in the destination buffer template char *Q_strlcpy(char (&dest)[size], const char *src) { - return Q_strlcpy(dest, src, size); + return Q_strlcpy(static_cast(dest), src, size); } // safely concatenate two strings. // a variant of strcat that truncates the result to fit in the destination buffer -template -size_t Q_strlcat(char (&dest)[size], const char *src) +inline size_t Q_strlcat(char *dest, const char *src, size_t maxDestSize) { size_t srclen; // Length of source string size_t dstlen; // Length of destination string // Figure out how much room is left - dstlen = Q_strlen(dest); - size_t length = size - dstlen + 1; + dstlen = strlen(dest); + size_t unRemainingSize = maxDestSize - dstlen - 1; + + // Sanity check in case dest doesn't contain a null termination + if (dstlen > (maxDestSize - 1)) + dstlen = maxDestSize - 1; - if (!length) { - // No room, return immediately - return dstlen; + if (unRemainingSize <= 0 || unRemainingSize > maxDestSize) + { + dest[dstlen] = '\0'; + return dstlen; // No room, return immediately } // Figure out how much room is needed - srclen = Q_strlen(src); + srclen = strlen(src); // Copy the appropriate amount - if (srclen > length) { - srclen = length; - } + if (srclen > unRemainingSize) + srclen = unRemainingSize; Q_memcpy(dest + dstlen, src, srclen); dest[dstlen + srclen] = '\0'; - return dstlen + srclen; } +template +inline size_t Q_strlcat(char (&dest)[size], const char *src) +{ + return Q_strlcat(static_cast(dest), src, size); +} + // Force slashes of either type to be = separator character inline void Q_FixSlashes(char *pname, char separator = CORRECT_PATH_SEPARATOR) { diff --git a/regamedll/regamedll/regamedll.cpp b/regamedll/regamedll/regamedll.cpp index 5e6190d15..9602faa17 100644 --- a/regamedll/regamedll/regamedll.cpp +++ b/regamedll/regamedll/regamedll.cpp @@ -33,7 +33,6 @@ void Regamedll_Game_Init() g_bIsBeta = UTIL_IsBeta(); g_bIsCzeroGame = UTIL_IsGame("czero"); g_bAllowedCSBot = UTIL_AreBotsAllowed(); // determine whether bots can be used or not - g_bHostageImprov = UTIL_AreHostagesImprov(); // determine whether hostage improv can be used or not WeaponInfoReset(); } diff --git a/regamedll/regamedll/types.natvis b/regamedll/regamedll/types.natvis new file mode 100644 index 000000000..4dc313c65 --- /dev/null +++ b/regamedll/regamedll/types.natvis @@ -0,0 +1,107 @@ + + + + + + {{ { pev->pContainingEntity - g_pEdicts }, { pev->classname }, { pev->health }, { m_iTeam }, { m_iModelName } }} + + + + + + { *m_pContainingEntity } + + + + + {{ { pev->pContainingEntity - g_pEdicts }, { pev->classname } }} + + + m_pNext + m_pNext + (*this) + + + + + + + {{ { pev->pContainingEntity - g_pEdicts }, { pev->classname }, { pev->model } }} + + + + + {{ {this - g_pEdicts}, { v.classname }, { v.model } }} + + + + + {{ { pContainingEntity - g_pEdicts }, { classname }, { model } }} + + + + + { &gpGlobals->pStringBase[m_string],s } + + + + + allocator + + ($T1 *)m_pMemory + m_nAllocationCount + m_nGrowSize + + + + + + {{ size = { m_Size } }} + + + m_Size + (ElemType_t *)m_Memory.m_pMemory + + + + + + + {{ size = { m_Tree.m_NumElements } }} + + m_Tree.m_NumElements + m_Tree.m_Elements + + + + ((CTree::Node_t *)m_Tree.m_Elements.m_pMemory)[iMap].m_Data.elem + iMap++ + + + + + + + + {{ size = { $T2 } }} + + + $T2 + ($T1 *)&m_Memory[0] + + + + + + + + m_NumElements + m_Elements + + m_NumElements + ((Node_t *)m_Elements.m_pMemory)[$i].m_Data + + + + + diff --git a/regamedll/unittests/mathfun_tests.cpp b/regamedll/unittests/mathfun_tests.cpp index 6cb36eca5..7d39cb3b7 100644 --- a/regamedll/unittests/mathfun_tests.cpp +++ b/regamedll/unittests/mathfun_tests.cpp @@ -32,10 +32,10 @@ TEST(SinCosPrecision, SseMathFun, 10000) double sse_sin = _mm_cvtss_f32(s); double sse_cos = _mm_cvtss_f32(c); - sprintf(localbuf, "sin precision failure for angle=%f", i); + Q_snprintf(localbuf, sizeof(localbuf), "sin precision failure for angle=%f", i); DOUBLES_EQUAL(localbuf, x87_sin, sse_sin, 0.000001); - sprintf(localbuf, "cos precision failure for angle=%f", i); + Q_snprintf(localbuf, sizeof(localbuf), "cos precision failure for angle=%f", i); DOUBLES_EQUAL(localbuf, x87_cos, sse_cos, 0.000001); } } diff --git a/regamedll/version/version.h b/regamedll/version/version.h index 62c33001b..77bdbb482 100644 --- a/regamedll/version/version.h +++ b/regamedll/version/version.h @@ -6,5 +6,5 @@ #pragma once #define VERSION_MAJOR 5 -#define VERSION_MINOR 28 +#define VERSION_MINOR 31 #define VERSION_MAINTENANCE 0