build: reworked build and package to simplify process

build: reworked build and package to simplify process
build: windows dll is now compiled by clang, msvc build added to extras package
fix: clear all the implicit conversions in the code (also fixed some bugs)
fix: crash on  never xash3d-fwgs engine
fix: fixed bad bot behaviors on aarch64
fix: crash on some maps due to missing previous node
fix: finally removed memset(this) within bot creatin
This commit is contained in:
jeefo 2023-04-02 12:17:12 +03:00 committed by GitHub
commit 53df621dfc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1004 additions and 949 deletions

View file

@ -1,30 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: jeefo
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Video**
If it's problem with AI behaviour, add video to help explain your problem.
**Environment details:**
- OS: [e.g. windows 10 / linux debian 10.4]
- Engine: [e.g. rehlds 3.8.0.702 / vanilla 8684]
- GameDLL: [e.g. regamedll 5.18.0.479 / vanilla]
- Metamod: [e.g. metamod-r / metamod-p / metamod]
- Bot Version: [e.g. 4.1.566]
- Game mode: [e.g. csdm / public / zombie]
Additionally, please provide list of AMX Mod X plugins: ``amxx plugins`` and list of Metamod plugins: ``meta plugins``.
**Additional context**
Add any other context about the problem here.

View file

@ -12,117 +12,84 @@ on:
types: [published] types: [published]
jobs: jobs:
unix: bot-build:
runs-on: ubuntu-latest
container:
image: j4sdk/x86-buildtools:lunar
options: --hostname github-actions
strategy: strategy:
matrix: matrix:
include: arch: ['linux-x86', 'linux-x86-gcc', 'linux-aarch64', 'darwin-x86', 'windows-x86', 'windows-x86-gcc']
- name: linux-x86 fail-fast: false
bin: yapb.so
opts: CXX=gcc-11 CXX_LD=gold meson setup build
- name: macos-x86
bin: yapb.dylib
opts: meson setup build --cross-file=x86-darwin
name: ${{ matrix.name }}
runs-on: ubuntu-latest
container: j4sdk/x86-buildtools:latest
steps: steps:
- name: Checkout Repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Configure meson
- name: Configure Build shell: bash
run: | run: |
${{ matrix.opts }} if [[ ${{matrix.arch}} == linux-x86 ]]; then
CXX=clang CXX_LD=lld meson setup ${{matrix.arch}}
- name: Compile Source elif [[ ${{matrix.arch}} == linux-x86-gcc ]]; then
CXX=gcc CXX_LD=gold meson setup ${{matrix.arch}}
else
meson setup ${{matrix.arch}}/ --cross-file ${{matrix.arch}}
fi
- name: Build sources
shell: bash
run: | run: |
meson compile -v -C build meson compile -C ${{matrix.arch}}
echo "artifcat=${{matrix.arch}}/`ls ${{matrix.arch}}/ | egrep '^yapb.(so|dylib|dll)$' | head -1`" >> $GITHUB_ENV
- name: Upload Artifacts - name: Upload artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: ${{ matrix.bin }} name: ${{matrix.arch}}
path: build/${{ matrix.bin }} path: ${{env.artifcat}}
windows: bot-msvc:
name: windows-x86 env:
runs-on: windows-2022 name: windows-x86-msvc
name: bot-build (windows-x86-msvc)
runs-on: windows-latest
steps: steps:
- name: Checkout Repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Setup msbuild
- name: Setup MSBuild
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
with: with:
arch: amd64_x86 arch: amd64_x86
- name: Install meson
- name: Setup Meson
run: | run: |
python -m pip install --upgrade meson ninja python -m pip install --upgrade meson ninja
- name: Configure meson
- name: Configure Build
run: | run: |
meson setup build meson setup ${{env.name}}
- name: Build sources
- name: Compile Source
run: | run: |
meson compile -v -C build meson compile -C ${{env.name}}
- name: Upload artifacts
- name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: yapb.dll name: ${{env.name}}
path: build/yapb.dll path: ${{env.name}}/yapb.dll
linux-arm64: bot-release:
name: linux-arm64
runs-on: self-hosted
continue-on-error: true
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- name: Configure Build
run: |
CXX=clang-12 CXX_LD=lld-12 meson setup build
- name: Compile Source
run: |
meson compile -v -C build
- name: Rename Binary
run: |
mv build/yapb.so build/yapb.arm64.so
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: yapb.arm64.so
path: build/yapb.arm64.so
publish:
if: | if: |
github.event_name == 'release' && github.event_name == 'release' &&
github.event.action == 'published' github.event.action == 'published'
name: publish name: bot-release
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [unix, windows, linux-arm64] needs: [bot-build, bot-msvc]
steps: steps:
- name: Checkout Repository - name: Checkout Repository
@ -130,38 +97,32 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Install signing tools
- name: Install Signing Tools
run: | run: |
sudo apt-get update && sudo apt-get upgrade -y sudo apt-get update
sudo apt-get install -y --no-install-recommends osslsigncode sudo apt-get install -y --no-install-recommends osslsigncode
- name: Get Artifacts - name: Get Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
path: artifacts path: artifacts
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.x' python-version: '3.x'
- name: Configure meson
- name: Setup Meson
run: | run: |
python -m pip install --upgrade meson ninja urllib3 python -m pip install --upgrade meson ninja requests
- name: Create packages
- name: Create Packages
run: | run: |
meson setup dist meson setup dist
ninja -C dist package ninja -C dist package
env: env:
CS_CERTIFICATE: ${{ secrets.CS_CERTIFICATE }} CS_CERTIFICATE: ${{secrets.CS_CERTIFICATE}}
CS_CERTIFICATE_PASSWORD: ${{ secrets.CS_CERTIFICATE_PASSWORD }} CS_CERTIFICATE_PASSWORD: ${{secrets.CS_CERTIFICATE_PASSWORD}}
- name: Publish release
- name: Publish Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
files: pkg/* files: pkg/*
env: env:
GITHUB_TOKEN: ${{ secrets.API_TOKEN }} GITHUB_TOKEN: ${{secrets.API_TOKEN}}

View file

@ -74,6 +74,13 @@ yb_walking_allowed "1"
// //
yb_camping_allowed "1" yb_camping_allowed "1"
//
// Allows bots to partially avoid grenades.
// ---
// Default: "1", Min: "0", Max: "1"
//
yb_avoid_grenades "1"
// //
// Lower bound of time from which time for camping is calculated // Lower bound of time from which time for camping is calculated
// --- // ---
@ -130,6 +137,13 @@ yb_destroy_breakables_around "1"
// //
yb_object_pickup_radius "450.0" yb_object_pickup_radius "450.0"
//
// The radius on which bot destroy breakables around it, when not touching with them.
// ---
// Default: "400.0", Min: "64.0", Max: "1024.0"
//
yb_object_destroy_radius "400.0"
// //
// Specifies the paths for the bot chatter sound files. // Specifies the paths for the bot chatter sound files.
// --- // ---
@ -158,6 +172,13 @@ yb_attack_monsters "0"
// //
yb_pickup_custom_items "0" yb_pickup_custom_items "0"
//
// Allows or disallows bots to pickup best weapons.
// ---
// Default: "1", Min: "0", Max: "1"
//
yb_pickup_best "1"
// //
// Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages. // Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages.
// --- // ---
@ -268,19 +289,40 @@ yb_password_key "_ybpw"
// //
yb_csdm_mode "0" yb_csdm_mode "0"
//
// Specifies the maximum health of breakable object, that bot will consider to destroy.
// ---
// Default: "500.0", Min: "1.0", Max: "3000.0"
//
yb_breakable_health_limit "500.0"
// //
// Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format. // Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format.
// --- // ---
// Default: "1", Min: "0", Max: "1" // Default: "0", Min: "0", Max: "1"
// //
yb_graph_fixcamp "1" yb_graph_fixcamp "0"
// //
// Specifies the URL from bots will be able to download graph in case of missing local one. Set to empty, if no downloads needed. // Specifies the URL from bots will be able to download graph in case of missing local one. Set to empty, if no downloads needed.
// --- // ---
// Default: "yapb.jeefo.net" // Default: "yapb.jeefo.net"
// //
yb_graph_url "yapb.jeefo.net" yb_graph_url "yapb-gcdn.akamaized.net"
//
// Every N graph nodes placed on map, the graph will be saved automatically (without checks).
// ---
// Default: "15", Min: "0", Max: "2048"
//
yb_graph_auto_save_count "15"
//
// Maximum distance to draw graph nodes from editor viewport.
// ---
// Default: "400", Min: "64", Max: "3072"
//
yb_graph_draw_distance "400"
// //
// Kick bots to automatically make room for human players. // Kick bots to automatically make room for human players.
@ -289,6 +331,13 @@ yb_graph_url "yapb.jeefo.net"
// //
yb_autovacate "1" yb_autovacate "1"
//
// Kick the bot immediately when a human player joins the server (yb_autovacate must be enabled).
// ---
// Default: "1", Min: "0", Max: "1"
//
yb_kick_after_player_connect "1"
// //
// Specifies the number bots to be added to the game. // Specifies the number bots to be added to the game.
// --- // ---
@ -402,7 +451,7 @@ yb_show_avatars "1"
yb_show_latency "2" yb_show_latency "2"
// //
// Allows to save bot names upon changelevel, so bot names will be the same after a map change // Allows to save bot names upon changelevel, so bot names will be the same after a map change.
// --- // ---
// Default: "1", Min: "0", Max: "1" // Default: "1", Min: "0", Max: "1"
// //
@ -436,6 +485,20 @@ yb_ping_base_min "7"
// //
yb_ping_base_max "34" yb_ping_base_max "34"
//
// Interval in which bots are added to the game.
// ---
// Default: "0.1", Min: "0.1", Max: "1.0"
//
yb_quota_adding_interval "0.1"
//
// Interval on which overall bot quota are checked.
// ---
// Default: "0.4", Min: "0.4", Max: "2.0"
//
yb_quota_maintain_interval "0.4"
// //
// Specifies the language for bot messages and menus. // Specifies the language for bot messages and menus.
// --- // ---
@ -443,6 +506,27 @@ yb_ping_base_max "34"
// //
yb_language "en" yb_language "en"
//
// Randomly disconnect and connect bots, simulating players join/quit.
// ---
// Default: "0", Min: "0", Max: "1"
//
yb_rotate_bots "0"
//
// Specifies minimum amount of seconds bot keep connected, if rotation active.
// ---
// Default: "360.0", Min: "120.0", Max: "7200.0"
//
yb_rotate_stay_min "360.0"
//
// Specifies maximum amount of seconds bot keep connected, if rotation active.
// ---
// Default: "3600.0", Min: "1800.0", Max: "14400.0"
//
yb_rotate_stay_max "3600.0"
// //
// Enables or disables extra hard difficulty for bots. // Enables or disables extra hard difficulty for bots.
// --- // ---

@ -1 +1 @@
Subproject commit 19242b19194bf39dc97a5d51d27bd1f60f701597 Subproject commit dade2ade585d7fbd3ac91dec93cd70c92225e832

View file

@ -38,20 +38,20 @@ public:
}; };
private: private:
Array <StringArray> m_chat; Array <StringArray> m_chat {};
Array <Array <ChatterItem>> m_chatter; Array <Array <ChatterItem>> m_chatter {};
Array <BotName> m_botNames; Array <BotName> m_botNames {};
Array <Keywords> m_replies; Array <Keywords> m_replies {};
SmallArray <WeaponInfo> m_weapons; SmallArray <WeaponInfo> m_weapons {};
SmallArray <WeaponProp> m_weaponProps; SmallArray <WeaponProp> m_weaponProps {};
StringArray m_logos; StringArray m_logos {};
StringArray m_avatars; StringArray m_avatars {};
HashMap <uint32, String, Hash <int32>> m_language; HashMap <uint32, String, Hash <int32>> m_language {};
HashMap <int32, DifficultyData> m_difficulty; HashMap <int32, DifficultyData> m_difficulty {};
HashMap <String, String> m_custom; HashMap <String, String> m_custom {};
// default tables for personality weapon preferences, overridden by weapon.cfg // default tables for personality weapon preferences, overridden by weapon.cfg
SmallArray <int32> m_normalWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16 }; SmallArray <int32> m_normalWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16 };

View file

@ -63,21 +63,21 @@ public:
}; };
private: private:
StringArray m_args; StringArray m_args {};
Array <BotCmd> m_cmds; Array <BotCmd> m_cmds {};
Array <BotMenu> m_menus; Array <BotMenu> m_menus {};
Deque <PrintQueue> m_printQueue; Deque <PrintQueue> m_printQueue {};
IntArray m_campIterator; IntArray m_campIterator {};
edict_t *m_ent; edict_t *m_ent {};
Bot *m_djump; Bot *m_djump {};
bool m_isFromConsole; bool m_isFromConsole {};
bool m_rapidOutput; bool m_rapidOutput {};
bool m_isMenuFillCommand; bool m_isMenuFillCommand {};
bool m_ignoreTranslate; bool m_ignoreTranslate {};
int m_menuServerFillTeam; int m_menuServerFillTeam {};
int m_interMenuData[4] = { 0, }; int m_interMenuData[4] = { 0, };
float m_printQueueFlushTimestamp {}; float m_printQueueFlushTimestamp {};

View file

@ -112,27 +112,27 @@ public:
using EntitySearch = Lambda <EntitySearchResult (edict_t *)>; using EntitySearch = Lambda <EntitySearchResult (edict_t *)>;
private: private:
int m_drawModels[DrawLine::Count] { }; int m_drawModels[DrawLine::Count] {};
int m_spawnCount[Team::Unassigned] { }; int m_spawnCount[Team::Unassigned] {};
// bot client command // bot client command
StringArray m_botArgs; StringArray m_botArgs {};
edict_t *m_startEntity; edict_t *m_startEntity {};
edict_t *m_localEntity; edict_t *m_localEntity {};
Array <edict_t *> m_breakables; Array <edict_t *> m_breakables {};
SmallArray <ConVarReg> m_cvars; SmallArray <ConVarReg> m_cvars {};
SharedLibrary m_gameLib; SharedLibrary m_gameLib {};
EngineWrap m_engineWrap; EngineWrap m_engineWrap {};
bool m_precached; bool m_precached {};
int m_gameFlags {}; int m_gameFlags {};
int m_mapFlags {}; int m_mapFlags {};
float m_oneSecondFrame; // per second updated float m_oneSecondFrame {}; // per second updated
float m_halfSecondFrame; // per half second update float m_halfSecondFrame {}; // per half second update
public: public:
Game (); Game ();
@ -240,8 +240,8 @@ public:
} }
// gets custom engine argv for client command // gets custom engine argv for client command
const char *botArgv (size_t index) const { const char *botArgv (int32 index) const {
if (index >= m_botArgs.length ()) { if (static_cast <size_t> (index) >= m_botArgs.length ()) {
return ""; return "";
} }
return m_botArgs[index].chars (); return m_botArgs[index].chars ();
@ -517,7 +517,7 @@ public:
class LightMeasure final : public Singleton <LightMeasure> { class LightMeasure final : public Singleton <LightMeasure> {
private: private:
lightstyle_t m_lightstyle[MAX_LIGHTSTYLES] {}; lightstyle_t m_lightstyle[MAX_LIGHTSTYLES] {};
int m_lightstyleValue[MAX_LIGHTSTYLEVALUE] {}; uint32 m_lightstyleValue[MAX_LIGHTSTYLEVALUE] {};
bool m_doAnimation = false; bool m_doAnimation = false;
Color m_point; Color m_point;
@ -563,8 +563,8 @@ public:
// simple handler for parsing and rewriting queries (fake queries) // simple handler for parsing and rewriting queries (fake queries)
class QueryBuffer { class QueryBuffer {
SmallArray <uint8> m_buffer; SmallArray <uint8> m_buffer {};
size_t m_cursor; size_t m_cursor {};
public: public:
QueryBuffer (const uint8 *msg, size_t length, size_t shift) : m_cursor (0) { QueryBuffer (const uint8 *msg, size_t length, size_t shift) : m_cursor (0) {
@ -640,7 +640,7 @@ private:
#if defined (CR_WINDOWS) #if defined (CR_WINDOWS)
# define DLSYM_FUNCTION GetProcAddress # define DLSYM_FUNCTION GetProcAddress
# define DLCLOSE_FUNCTION FreeLibrary # define DLCLOSE_FUNCTION FreeLibrary
# define DLSYM_RETURN PVOID # define DLSYM_RETURN SharedLibrary::Handle
# define DLSYM_HANDLE HMODULE # define DLSYM_HANDLE HMODULE
#else #else
# define DLSYM_FUNCTION dlsym # define DLSYM_FUNCTION dlsym
@ -654,7 +654,7 @@ private:
Detour <decltype (DLSYM_FUNCTION)> m_dlsym; Detour <decltype (DLSYM_FUNCTION)> m_dlsym;
Detour <decltype (DLCLOSE_FUNCTION)> m_dlclose; Detour <decltype (DLCLOSE_FUNCTION)> m_dlclose;
HashMap <StringRef, DLSYM_RETURN> m_exports; HashMap <StringRef, SharedLibrary::Func> m_exports;
SharedLibrary m_self; SharedLibrary m_self;
@ -663,7 +663,7 @@ public:
public: public:
void initialize (); void initialize ();
DLSYM_RETURN lookup (SharedLibrary::Handle module, const char *function); SharedLibrary::Func lookup (SharedLibrary::Handle module, const char *function);
int close (DLSYM_HANDLE module) { int close (DLSYM_HANDLE module) {
if (m_self.handle () == module) { if (m_self.handle () == module) {
@ -701,7 +701,7 @@ public:
} }
public: public:
static DLSYM_RETURN CR_STDCALL lookupHandler (SharedLibrary::Handle module, const char *function) { static SharedLibrary::Func CR_STDCALL lookupHandler (SharedLibrary::Handle module, const char *function) {
return EntityLinkage::instance ().lookup (module, function); return EntityLinkage::instance ().lookup (module, function);
} }

View file

@ -186,10 +186,10 @@ struct PODPath {
// this structure links nodes returned from pathfinder // this structure links nodes returned from pathfinder
class PathWalk final : public DenyCopying { class PathWalk final : public DenyCopying {
private: private:
size_t m_cursor = 0; size_t m_cursor {};
size_t m_length = 0; size_t m_length {};
UniquePtr <int32[]> m_path; UniquePtr <int32[]> m_path {};
public: public:
explicit PathWalk () = default; explicit PathWalk () = default;
@ -263,60 +263,59 @@ private:
int x, y, z; int x, y, z;
}; };
int m_editFlags; int m_editFlags {};
int m_loadAttempts; int m_loadAttempts {};
int m_cacheNodeIndex; int m_cacheNodeIndex {};
int m_lastJumpNode; int m_lastJumpNode {};
int m_findWPIndex; int m_findWPIndex {};
int m_facingAtIndex; int m_facingAtIndex {};
int m_highestDamage[kGameTeamNum] {}; int m_highestDamage[kGameTeamNum] {};
int m_autoSaveCount; int m_autoSaveCount {};
float m_timeJumpStarted; float m_timeJumpStarted {};
float m_autoPathDistance; float m_autoPathDistance {};
float m_pathDisplayTime; float m_pathDisplayTime {};
float m_arrowDisplayTime; float m_arrowDisplayTime {};
bool m_isOnLadder; bool m_isOnLadder {};
bool m_endJumpPoint; bool m_endJumpPoint {};
bool m_jumpLearnNode; bool m_jumpLearnNode {};
bool m_hasChanged; bool m_hasChanged {};
bool m_needsVisRebuild; bool m_needsVisRebuild {};
bool m_narrowChecked; bool m_narrowChecked {};
Vector m_learnVelocity; Vector m_learnVelocity {};
Vector m_learnPosition; Vector m_learnPosition {};
Vector m_bombOrigin; Vector m_bombOrigin {};
Vector m_lastNode; Vector m_lastNode {};
IntArray m_terrorPoints; IntArray m_terrorPoints {};
IntArray m_ctPoints; IntArray m_ctPoints {};
IntArray m_goalPoints; IntArray m_goalPoints {};
IntArray m_campPoints; IntArray m_campPoints {};
IntArray m_sniperPoints; IntArray m_sniperPoints {};
IntArray m_rescuePoints; IntArray m_rescuePoints {};
IntArray m_visitedGoals; IntArray m_visitedGoals {};
SmallArray <int32> m_buckets[kMaxBucketsInsidePos][kMaxBucketsInsidePos][kMaxBucketsInsidePos]; SmallArray <int32> m_buckets[kMaxBucketsInsidePos][kMaxBucketsInsidePos][kMaxBucketsInsidePos];
SmallArray <Matrix> m_matrix; SmallArray <Matrix> m_matrix {};
SmallArray <Practice> m_practice; SmallArray <Practice> m_practice {};
SmallArray <Path> m_paths; SmallArray <Path> m_paths {};
SmallArray <uint8> m_vistable; SmallArray <uint8> m_vistable {};
String m_graphAuthor; String m_graphAuthor {};
String m_graphModified; String m_graphModified {};
ExtenHeader m_extenHeader {}; ExtenHeader m_extenHeader {};
StorageHeader m_graphHeader {}; StorageHeader m_graphHeader {};
edict_t *m_editor; edict_t *m_editor {};
public: public:
BotGraph (); BotGraph ();
~BotGraph () = default; ~BotGraph () = default;
public: public:
int getFacingIndex (); int getFacingIndex ();
int getFarest (const Vector &origin, float maxDistance = 32.0); int getFarest (const Vector &origin, float maxDistance = 32.0);
int getNearest (const Vector &origin, float minDistance = kInfiniteDistance, int flags = -1); int getNearest (const Vector &origin, float minDistance = kInfiniteDistance, int flags = -1);

View file

@ -54,13 +54,13 @@ public:
using UniqueBot = UniquePtr <Bot>; using UniqueBot = UniquePtr <Bot>;
private: private:
float m_timeRoundStart; float m_timeRoundStart {};
float m_timeRoundEnd; float m_timeRoundEnd {};
float m_timeRoundMid; float m_timeRoundMid {};
float m_autoKillCheckTime; // time to kill all the bots ? float m_autoKillCheckTime {}; // time to kill all the bots ?
float m_maintainTime; // time to maintain bot creation float m_maintainTime {}; // time to maintain bot creation
float m_quotaMaintainTime; // time to maintain bot quota float m_quotaMaintainTime {}; // time to maintain bot quota
float m_grenadeUpdateTime {}; // time to update active grenades float m_grenadeUpdateTime {}; // time to update active grenades
float m_entityUpdateTime {}; // time to update intresting entities float m_entityUpdateTime {}; // time to update intresting entities
float m_plantSearchUpdateTime {}; // time to update for searching planted bomb float m_plantSearchUpdateTime {}; // time to update for searching planted bomb
@ -68,26 +68,26 @@ private:
float m_timeBombPlanted {}; // time the bomb were planted float m_timeBombPlanted {}; // time the bomb were planted
float m_lastRadioTime[kGameTeamNum] {}; // global radio time float m_lastRadioTime[kGameTeamNum] {}; // global radio time
int m_lastWinner; // the team who won previous round int m_lastWinner {}; // the team who won previous round
int m_lastDifficulty; // last bots difficulty int m_lastDifficulty {}; // last bots difficulty
int m_bombSayStatus; // some bot is issued whine about bomb int m_bombSayStatus {}; // some bot is issued whine about bomb
int m_lastRadio[kGameTeamNum] {}; // last radio message for team int m_lastRadio[kGameTeamNum] {}; // last radio message for team
bool m_leaderChoosen[kGameTeamNum] {}; // is team leader choose theese round bool m_leaderChoosen[kGameTeamNum] {}; // is team leader choose theese round
bool m_economicsGood[kGameTeamNum] {}; // is team able to buy anything bool m_economicsGood[kGameTeamNum] {}; // is team able to buy anything
bool m_bombPlanted; bool m_bombPlanted {};
bool m_botsCanPause; bool m_botsCanPause {};
bool m_roundOver; bool m_roundOver {};
Array <edict_t *> m_activeGrenades; // holds currently active grenades on the map Array <edict_t *> m_activeGrenades {}; // holds currently active grenades on the map
Array <edict_t *> m_intrestingEntities; // holds currently intresting entities on the map Array <edict_t *> m_intrestingEntities {}; // holds currently intresting entities on the map
Deque <String> m_saveBotNames; // bots names that persist upon changelevel Deque <String> m_saveBotNames {}; // bots names that persist upon changelevel
Deque <BotRequest> m_addRequests; // bot creation tab Deque <BotRequest> m_addRequests {}; // bot creation tab
SmallArray <BotTask> m_filters; // task filters SmallArray <BotTask> m_filters {}; // task filters
SmallArray <UniqueBot> m_bots; // all available bots SmallArray <UniqueBot> m_bots {}; // all available bots
edict_t *m_killerEntity; // killer entity for bots edict_t *m_killerEntity {}; // killer entity for bots
FrustumData m_frustumData {}; FrustumData m_frustumData {};
protected: protected:

View file

@ -82,22 +82,22 @@ private:
}; };
private: private:
HashMap <String, int32> m_textMsgCache; // cache strings for faster access for textmsg HashMap <String, int32> m_textMsgCache {}; // cache strings for faster access for textmsg
HashMap <String, int32> m_showMenuCache; // cache for the showmenu message HashMap <String, int32> m_showMenuCache {}; // cache for the showmenu message
HashMap <String, int32> m_statusIconCache; // cache for status icon message HashMap <String, int32> m_statusIconCache {}; // cache for status icon message
HashMap <String, int32> m_teamInfoCache; // cache for teaminfo message HashMap <String, int32> m_teamInfoCache {}; // cache for teaminfo message
private: private:
Bot *m_bot {}; // owner of a message Bot *m_bot {}; // owner of a message
NetMsg m_current {}; // ongoing message id NetMsg m_current {}; // ongoing message id
SmallArray <Args> m_args; // args collected from write* functions SmallArray <Args> m_args {}; // args collected from write* functions
HashMap <String, NetMsg> m_wanted; // wanted messages HashMap <String, NetMsg> m_wanted {}; // wanted messages
HashMap <int32, NetMsg> m_reverseMap; // maps engine message id to our message id HashMap <int32, NetMsg> m_reverseMap {}; // maps engine message id to our message id
HashMap <NetMsg, int32, MsgHash> m_maps; // maps our message to id to engine message id HashMap <NetMsg, int32, MsgHash> m_maps {}; // maps our message to id to engine message id
HashMap <NetMsg, MsgFunc, MsgHash> m_handlers; // maps our message id to handler function HashMap <NetMsg, MsgFunc, MsgHash> m_handlers {}; // maps our message id to handler function
private: private:
void netMsgTextMsg (); void netMsgTextMsg ();

View file

@ -19,13 +19,13 @@
class Product final : public Singleton <Product> { class Product final : public Singleton <Product> {
public: public:
struct Build { struct Build {
StringRef hash { MODULE_BUILD_HASH }; StringRef hash { MODULE_COMMIT_COUNT };
StringRef author { MODULE_BUILD_AUTHOR }; StringRef count { MODULE_COMMIT_HASH };
StringRef count { MODULE_BUILD_COUNT }; StringRef author { MODULE_AUTHOR };
StringRef machine { MODULE_BUILD_MACHINE }; StringRef machine { MODULE_MACHINE };
StringRef compiler { MODULE_BUILD_COMPILER }; StringRef compiler { MODULE_COMPILER };
StringRef id { MODULE_BOT_BUILD_ID }; StringRef id { MODULE_BUILD_ID };
} build { }; } build {};
public: public:
StringRef name { "YaPB" }; StringRef name { "YaPB" };
@ -38,7 +38,7 @@ public:
StringRef logtag { "YB" }; StringRef logtag { "YB" };
StringRef dtime { __DATE__ " " __TIME__ }; StringRef dtime { __DATE__ " " __TIME__ };
StringRef date { __DATE__ }; StringRef date { __DATE__ };
StringRef version { MODULE_BOT_VERSION "." MODULE_BUILD_COUNT }; StringRef version { MODULE_VERSION "." MODULE_COMMIT_COUNT };
StringRef cmdPri { "yb" }; StringRef cmdPri { "yb" };
StringRef cmdSec { "yapb" }; StringRef cmdSec { "yapb" };
}; };

View file

@ -21,15 +21,15 @@ CR_DECLARE_SCOPED_ENUM (Noise,
class BotSupport final : public Singleton <BotSupport> { class BotSupport final : public Singleton <BotSupport> {
private: private:
bool m_needToSendWelcome; bool m_needToSendWelcome {};
float m_welcomeReceiveTime; float m_welcomeReceiveTime {};
StringArray m_sentences; StringArray m_sentences {};
SmallArray <Client> m_clients; SmallArray <Client> m_clients {};
SmallArray <Twin <String, String>> m_tags; SmallArray <Twin <String, String>> m_tags {};
HashMap <int32, String> m_weaponAlias; HashMap <int32, String> m_weaponAlias {};
HashMap <String, int32> m_noiseCache; HashMap <String, int32> m_noiseCache {};
Detour <decltype (sendto)> m_sendToDetour { "ws2_32.dll", "sendto", sendto }; Detour <decltype (sendto)> m_sendToDetour { "ws2_32.dll", "sendto", sendto };
public: public:

View file

@ -8,12 +8,11 @@
#pragma once #pragma once
// fallback if no git or custom build // fallback if no git or custom build
#define MODULE_BUILD_HASH "0" #define MODULE_COMMIT_COUNT "0"
#define MODULE_BUILD_AUTHOR "yapb@jeefo.net" #define MODULE_COMMIT_HASH "0"
#define MODULE_BUILD_COUNT "0" #define MODULE_AUTHOR "default@mail.net"
#define MODULE_BUILD_MACHINE "localhost" #define MODULE_MACHINE "localhost"
#define MODULE_BUILD_COMPILER "unknown" #define MODULE_COMPILER "default"
#define MODULE_VERSION "4.0"
#define MODULE_BOT_VERSION "4.3" #define MODULE_VERSION_FILE 4,0,0,000
#define MODULE_BOT_VERSION_FILE 4,3,0,000 #define MODULE_BUILD_ID "0:0"
#define MODULE_BOT_BUILD_ID "0:0"

View file

@ -9,12 +9,12 @@
// generated by meson build system // generated by meson build system
#ifndef MODULE_BUILD_HASH #ifndef MODULE_BUILD_HASH
# define MODULE_BUILD_HASH "@hash@" # define MODULE_COMMIT_COUNT "@count@"
# define MODULE_BUILD_AUTHOR @author@ # define MODULE_COMMIT_HASH "@hash@"
# define MODULE_BUILD_COUNT "@count@" # define MODULE_AUTHOR @author@
# define MODULE_BUILD_MACHINE "@machine@" # define MODULE_MACHINE "@machine@"
# define MODULE_BUILD_COMPILER "@compiler@" # define MODULE_COMPILER "@compiler@"
# define MODULE_BOT_VERSION "@version@" # define MODULE_VERSION "@version@"
# define MODULE_BOT_VERSION_FILE @version_win@,@count@ # define MODULE_VERSION_FILE @winver@,@count@
# define MODULE_BOT_BUILD_ID "@count@:@hash@" # define MODULE_BUILD_ID "@count@:@hash@"
#endif #endif

View file

@ -349,7 +349,7 @@ CR_DECLARE_SCOPED_ENUM (Reload,
) )
// collision probes // collision probes
CR_DECLARE_SCOPED_ENUM (CollisionProbe, CR_DECLARE_SCOPED_ENUM (CollisionProbe, uint32,
Jump = cr::bit (0), // probe jump when colliding Jump = cr::bit (0), // probe jump when colliding
Duck = cr::bit (1), // probe duck when colliding Duck = cr::bit (1), // probe duck when colliding
Strafe = cr::bit (2) // probe strafing when colliding Strafe = cr::bit (2) // probe strafing when colliding
@ -367,7 +367,7 @@ CR_DECLARE_SCOPED_ENUM (BotMsg,
) )
// sensing states // sensing states
CR_DECLARE_SCOPED_ENUM (Sense, CR_DECLARE_SCOPED_ENUM_TYPE (Sense, uint32,
SeeingEnemy = cr::bit (0), // seeing an enemy SeeingEnemy = cr::bit (0), // seeing an enemy
HearingEnemy = cr::bit (1), // hearing an enemy HearingEnemy = cr::bit (1), // hearing an enemy
SuspectEnemy = cr::bit (2), // suspect enemy behind obstacle SuspectEnemy = cr::bit (2), // suspect enemy behind obstacle
@ -378,7 +378,7 @@ CR_DECLARE_SCOPED_ENUM (Sense,
) )
// positions to aim at // positions to aim at
CR_DECLARE_SCOPED_ENUM (AimFlags, CR_DECLARE_SCOPED_ENUM_TYPE (AimFlags, uint32,
Nav = cr::bit (0), // aim at nav point Nav = cr::bit (0), // aim at nav point
Camp = cr::bit (1), // aim at camp vector Camp = cr::bit (1), // aim at camp vector
PredictPath = cr::bit (2), // aim at predicted path PredictPath = cr::bit (2), // aim at predicted path
@ -590,12 +590,12 @@ struct Client {
// define chatting collection structure // define chatting collection structure
struct ChatCollection { struct ChatCollection {
int chatProbability; int chatProbability {};
float chatDelay; float chatDelay {};
float timeNextChat; float timeNextChat {};
int entityIndex; int entityIndex {};
String sayText; String sayText {};
StringArray lastUsedSentences; StringArray lastUsedSentences {};
}; };
// include bot graph stuff // include bot graph stuff
@ -622,7 +622,7 @@ private:
int m_oldButtons {}; // our old buttons int m_oldButtons {}; // our old buttons
int m_reloadState {}; // current reload state int m_reloadState {}; // current reload state
int m_voicePitch; // bot voice pitch int m_voicePitch {}; // bot voice pitch
int m_loosedBombNodeIndex {}; // nearest to loosed bomb node int m_loosedBombNodeIndex {}; // nearest to loosed bomb node
int m_plantedBombNodeIndex {}; // nearest to planted bomb node int m_plantedBombNodeIndex {}; // nearest to planted bomb node
int m_currentNodeIndex {}; // current node index int m_currentNodeIndex {}; // current node index
@ -636,18 +636,17 @@ private:
int m_liftState {}; // state of lift handling int m_liftState {}; // state of lift handling
int m_radioSelect {}; // radio entry int m_radioSelect {}; // radio entry
float m_headedTime {}; float m_headedTime {};
float m_prevTime {}; // time previously checked movement speed float m_prevTime {}; // time previously checked movement speed
float m_heavyTimestamp; // is it time to execute heavy-weight functions float m_heavyTimestamp {}; // is it time to execute heavy-weight functions
float m_prevSpeed {}; // speed some frames before float m_prevSpeed {}; // speed some frames before
float m_timeDoorOpen {}; // time to next door open check float m_timeDoorOpen {}; // time to next door open check
float m_lastChatTime {}; // time bot last chatted float m_lastChatTime {}; // time bot last chatted
float m_timeLogoSpray {}; // time bot last spray logo float m_timeLogoSpray {}; // time bot last spray logo
float m_knifeAttackTime {}; // time to rush with knife (at the beginning of the round) float m_knifeAttackTime {}; // time to rush with knife (at the beginning of the round)
float m_duckDefuseCheckTime {}; // time to check for ducking for defuse float m_duckDefuseCheckTime {}; // time to check for ducking for defuse
float m_frameInterval; // bot's frame interval float m_frameInterval {}; // bot's frame interval
float m_lastCommandTime; // time bot last thinked float m_lastCommandTime {}; // time bot last thinked
float m_reloadCheckTime {}; // time to check reloading float m_reloadCheckTime {}; // time to check reloading
float m_zoomCheckTime {}; // time to check zoom again float m_zoomCheckTime {}; // time to check zoom again
float m_shieldCheckTime {}; // time to check shiled drawing again float m_shieldCheckTime {}; // time to check shiled drawing again
@ -685,15 +684,15 @@ private:
float m_minSpeed {}; // minimum speed in normal mode float m_minSpeed {}; // minimum speed in normal mode
float m_oldCombatDesire {}; // holds old desire for filtering float m_oldCombatDesire {}; // holds old desire for filtering
float m_itemCheckTime {}; // time next search for items needs to be done float m_itemCheckTime {}; // time next search for items needs to be done
float m_joinServerTime; // time when bot joined the game float m_joinServerTime {}; // time when bot joined the game
float m_playServerTime; // time bot spent in the game float m_playServerTime {}; // time bot spent in the game
float m_changeViewTime {}; // timestamp to change look at while at freezetime float m_changeViewTime {}; // timestamp to change look at while at freezetime
float m_breakableTime {}; // breakeble acquired time float m_breakableTime {}; // breakeble acquired time
bool m_moveToGoal {}; // bot currently moving to goal?? bool m_moveToGoal {}; // bot currently moving to goal??
bool m_isStuck {}; // bot is stuck bool m_isStuck {}; // bot is stuck
bool m_isReloading {}; // bot is reloading a gun bool m_isReloading {}; // bot is reloading a gun
bool m_forceRadio; // should bot use radio anyway? bool m_forceRadio {}; // should bot use radio anyway?
bool m_defendedBomb {}; // defend action issued bool m_defendedBomb {}; // defend action issued
bool m_defendHostage {}; // defend action issued bool m_defendHostage {}; // defend action issued
bool m_duckDefuse {}; // should or not bot duck to defuse bomb bool m_duckDefuse {}; // should or not bot duck to defuse bomb
@ -708,14 +707,14 @@ private:
bool m_moveToC4 {}; // ct is moving to bomb bool m_moveToC4 {}; // ct is moving to bomb
bool m_grenadeRequested {}; // bot requested change to grenade bool m_grenadeRequested {}; // bot requested change to grenade
Pickup m_pickupType; // type of entity which needs to be used/picked up Pickup m_pickupType {}; // type of entity which needs to be used/picked up
PathWalk m_pathWalk; // pointer to current node from path PathWalk m_pathWalk {}; // pointer to current node from path
Dodge m_combatStrafeDir; // direction to strafe Dodge m_combatStrafeDir {}; // direction to strafe
Fight m_fightStyle; // combat style to use Fight m_fightStyle {}; // combat style to use
CollisionState m_collisionState; // collision State CollisionState m_collisionState {}; // collision State
FindPath m_pathType; // which pathfinder to use FindPath m_pathType {}; // which pathfinder to use
uint8 m_enemyParts {}; // visibility flags uint8 m_enemyParts {}; // visibility flags
TraceResult m_lastTrace[TraceChannel::Num]; // last trace result TraceResult m_lastTrace[TraceChannel::Num] {}; // last trace result
edict_t *m_pickupItem {}; // pointer to entity of item to use/pickup edict_t *m_pickupItem {}; // pointer to entity of item to use/pickup
edict_t *m_itemIgnore {}; // pointer to entity to ignore for pickup edict_t *m_itemIgnore {}; // pointer to entity to ignore for pickup
@ -725,31 +724,31 @@ private:
edict_t *m_targetEntity {}; // the entity that the bot is trying to reach edict_t *m_targetEntity {}; // the entity that the bot is trying to reach
edict_t *m_avoidGrenade {}; // pointer to grenade entity to avoid edict_t *m_avoidGrenade {}; // pointer to grenade entity to avoid
Vector m_liftTravelPos; // lift travel position Vector m_liftTravelPos {}; // lift travel position
Vector m_moveAngles; // bot move angles Vector m_moveAngles {}; // bot move angles
Vector m_idealAngles; // angle wanted Vector m_idealAngles {}; // angle wanted
Vector m_randomizedIdealAngles; // angle wanted with noise Vector m_randomizedIdealAngles {}; // angle wanted with noise
Vector m_angularDeviation; // angular deviation from current to ideal angles Vector m_angularDeviation {}; // angular deviation from current to ideal angles
Vector m_aimSpeed; // aim speed calculated Vector m_aimSpeed {}; // aim speed calculated
Vector m_aimLastError; // last calculated aim error Vector m_aimLastError {}; // last calculated aim error
Vector m_prevOrigin; // origin some frames before Vector m_prevOrigin {}; // origin some frames before
Vector m_lookAt; // vector bot should look at Vector m_lookAt {}; // vector bot should look at
Vector m_throw; // origin of node to throw grenades Vector m_throw {}; // origin of node to throw grenades
Vector m_enemyOrigin; // target origin chosen for shooting Vector m_enemyOrigin {}; // target origin chosen for shooting
Vector m_grenade; // calculated vector for grenades Vector m_grenade {}; // calculated vector for grenades
Vector m_entity; // origin of entities like buttons etc. Vector m_entity {}; // origin of entities like buttons etc.
Vector m_camp; // aiming vector when camping. Vector m_camp {}; // aiming vector when camping.
Vector m_desiredVelocity; // desired velocity for jump nodes Vector m_desiredVelocity {}; // desired velocity for jump nodes
Vector m_breakableOrigin; // origin of breakable Vector m_breakableOrigin {}; // origin of breakable
Array <edict_t *> m_ignoredBreakable; // list of ignored breakables Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
Array <edict_t *> m_hostages; // pointer to used hostage entities Array <edict_t *> m_hostages {}; // pointer to used hostage entities
Array <Route> m_routes; // pointer Array <Route> m_routes {}; // pointer
Array <int32> m_goalHistory; // history of selected goals Array <int32> m_goalHistory {}; // history of selected goals
BinaryHeap <RouteTwin> m_routeQue; BinaryHeap <RouteTwin> m_routeQue {};
Path *m_path {}; // pointer to the current path node Path *m_path {}; // pointer to the current path node
String m_chatBuffer; // space for strings (say text...) String m_chatBuffer {}; // space for strings (say text...)
FrustumPlane m_frustum[FrustumSide::Num] {}; FrustumPlane m_frustum[FrustumSide::Num] {};
private: private:
@ -879,8 +878,8 @@ private:
void focusEnemy (); void focusEnemy ();
void selectBestWeapon (); void selectBestWeapon ();
void selectSecondary (); void selectSecondary ();
void selectWeaponByName (const char *name); void selectWeaponByName (StringRef name);
void selectWeaponById (int num); void selectWeaponByIndex (int index);
void completeTask (); void completeTask ();
void tasks (); void tasks ();
@ -938,17 +937,17 @@ private:
} }
public: public:
entvars_t *pev; entvars_t *pev {};
int m_index; // saved bot index int m_index {}; // saved bot index
int m_wantedTeam; // player team bot wants select int m_wantedTeam {}; // player team bot wants select
int m_wantedSkin; // player model bot wants to select int m_wantedSkin {}; // player model bot wants to select
int m_difficulty; // bots hard level int m_difficulty {}; // bots hard level
int m_moneyAmount; // amount of money in bot's bank int m_moneyAmount {}; // amount of money in bot's bank
float m_spawnTime {}; // time this bot spawned float m_spawnTime {}; // time this bot spawned
float m_timeTeamOrder {}; // time of last radio command float m_timeTeamOrder {}; // time of last radio command
float m_slowFrameTimestamp; // time to per-second think float m_slowFrameTimestamp {}; // time to per-second think
float m_nextBuyTime {}; // next buy time float m_nextBuyTime {}; // next buy time
float m_checkDarkTime {}; // check for darkness time float m_checkDarkTime {}; // check for darkness time
float m_preventFlashing {}; // bot turned away from flashbang float m_preventFlashing {}; // bot turned away from flashbang
@ -957,11 +956,11 @@ public:
float m_blindSidemoveSpeed {}; // mad side move speeds when bot is blind float m_blindSidemoveSpeed {}; // mad side move speeds when bot is blind
float m_fallDownTime {}; // time bot started to fall float m_fallDownTime {}; // time bot started to fall
float m_duckForJump {}; // is bot needed to duck for double jump float m_duckForJump {}; // is bot needed to duck for double jump
float m_baseAgressionLevel; // base aggression level (on initializing) float m_baseAgressionLevel {}; // base aggression level (on initializing)
float m_baseFearLevel; // base fear level (on initializing) float m_baseFearLevel {}; // base fear level (on initializing)
float m_agressionLevel; // dynamic aggression level (in game) float m_agressionLevel {}; // dynamic aggression level (in game)
float m_fearLevel; // dynamic fear level (in game) float m_fearLevel {}; // dynamic fear level (in game)
float m_nextEmotionUpdate; // next time to sanitize emotions float m_nextEmotionUpdate {}; // next time to sanitize emotions
float m_updateTime {}; // skip some frames in bot thinking float m_updateTime {}; // skip some frames in bot thinking
float m_updateInterval {}; // interval between frames float m_updateInterval {}; // interval between frames
float m_goalValue {}; // ranking value for this node float m_goalValue {}; // ranking value for this node
@ -980,21 +979,21 @@ public:
float m_shootTime {}; // time to shoot float m_shootTime {}; // time to shoot
float m_timeLastFired {}; // time to last firing float m_timeLastFired {}; // time to last firing
float m_difficultyChange {}; // time when auto-difficulty was last applied to this bot float m_difficultyChange {}; // time when auto-difficulty was last applied to this bot
float m_kpdRatio; // kill per death ratio float m_kpdRatio {}; // kill per death ratio
float m_healthValue; // clamped bot health float m_healthValue {}; // clamped bot health
float m_stayTime; // stay time before reconnect float m_stayTime {}; // stay time before reconnect
int m_blindNodeIndex {}; // node index to cover when blind int m_blindNodeIndex {}; // node index to cover when blind
int m_flashLevel {}; // flashlight level int m_flashLevel {}; // flashlight level
int m_basePing; // base ping for bot int m_basePing {}; // base ping for bot
int m_numEnemiesLeft {}; // number of enemies alive left on map int m_numEnemiesLeft {}; // number of enemies alive left on map
int m_numFriendsLeft {}; // number of friend alive left on map int m_numFriendsLeft {}; // number of friend alive left on map
int m_retryJoin; // retry count for chosing team/class int m_retryJoin {}; // retry count for chosing team/class
int m_startAction; // team/class selection state int m_startAction {}; // team/class selection state
int m_voteKickIndex {}; // index of player to vote against int m_voteKickIndex {}; // index of player to vote against
int m_lastVoteKick {}; // last index int m_lastVoteKick {}; // last index
int m_voteMap {}; // number of map to vote for int m_voteMap {}; // number of map to vote for
int m_logotypeIndex; // index for logotype int m_logotypeIndex {}; // index for logotype
int m_buyState {}; // current count in buying int m_buyState {}; // current count in buying
int m_blindButton {}; // buttons bot press, when blind int m_blindButton {}; // buttons bot press, when blind
int m_radioOrder {}; // actual command int m_radioOrder {}; // actual command
@ -1002,14 +1001,14 @@ public:
int m_chosenGoalIndex {}; // used for experience, same as above int m_chosenGoalIndex {}; // used for experience, same as above
int m_lastDamageType {}; // stores last damage int m_lastDamageType {}; // stores last damage
int m_team {}; // bot team int m_team {}; // bot team
int m_currentWeapon; // one current weapon for each bot int m_currentWeapon {}; // one current weapon for each bot
int m_weaponType; // current weapon type int m_weaponType {}; // current weapon type
int m_ammoInClip[kMaxWeapons] {}; // ammo in clip for each weapons int m_ammoInClip[kMaxWeapons] {}; // ammo in clip for each weapons
int m_ammo[MAX_AMMO_SLOTS] {}; // total ammo amounts int m_ammo[MAX_AMMO_SLOTS] {}; // total ammo amounts
bool m_isVIP {}; // bot is vip? bool m_isVIP {}; // bot is vip?
bool m_notKilled; // has the player been killed or has he just respawned bool m_notKilled {}; // has the player been killed or has he just respawned
bool m_notStarted; // team/class not chosen yet bool m_notStarted {}; // team/class not chosen yet
bool m_ignoreBuyDelay {}; // when reaching buyzone in the middle of the round don't do pauses bool m_ignoreBuyDelay {}; // when reaching buyzone in the middle of the round don't do pauses
bool m_inBombZone {}; // bot in the bomb zone or not bool m_inBombZone {}; // bot in the bomb zone or not
bool m_inBuyZone {}; // bot currently in buy zone bool m_inBuyZone {}; // bot currently in buy zone
@ -1033,17 +1032,17 @@ public:
edict_t *m_lastVictim {}; // pointer to killed entity edict_t *m_lastVictim {}; // pointer to killed entity
edict_t *m_trackingEdict {}; // pointer to last tracked player when camping/hiding edict_t *m_trackingEdict {}; // pointer to last tracked player when camping/hiding
Vector m_pathOrigin; // origin of node Vector m_pathOrigin {}; // origin of node
Vector m_destOrigin; // origin of move destination Vector m_destOrigin {}; // origin of move destination
Vector m_position; // position to move to in move to position task Vector m_position {}; // position to move to in move to position task
Vector m_doubleJumpOrigin; // origin of double jump Vector m_doubleJumpOrigin {}; // origin of double jump
Vector m_lastEnemyOrigin; // vector to last enemy origin Vector m_lastEnemyOrigin {}; // vector to last enemy origin
ChatCollection m_sayTextBuffer; // holds the index & the actual message of the last unprocessed text message of a player ChatCollection m_sayTextBuffer {}; // holds the index & the actual message of the last unprocessed text message of a player
BurstMode m_weaponBurstMode; // bot using burst mode? (famas/glock18, but also silencer mode) BurstMode m_weaponBurstMode {}; // bot using burst mode? (famas/glock18, but also silencer mode)
Personality m_personality; // bots type Personality m_personality {}; // bots type
Array <BotTask> m_tasks; Array <BotTask> m_tasks {};
Deque <int32> m_msgQueue; Deque <int32> m_msgQueue {};
public: public:
Bot (edict_t *bot, int difficulty, int personality, int team, int skin); Bot (edict_t *bot, int difficulty, int personality, int team, int skin);
@ -1101,6 +1100,7 @@ public:
bool canSkipNextTrace (TraceChannel channel); bool canSkipNextTrace (TraceChannel channel);
int getAmmo (); int getAmmo ();
int getAmmo (int id);
int getNearestToPlantedBomb (); int getNearestToPlantedBomb ();
float getFrameInterval (); float getFrameInterval ();
@ -1112,6 +1112,8 @@ public:
return m_ammoInClip[m_currentWeapon]; return m_ammoInClip[m_currentWeapon];
} }
bool isLowOnAmmo (const int index, const float factor) const;
Vector getCenter () const { Vector getCenter () const {
return (pev->absmax + pev->absmin) * 0.5; return (pev->absmax + pev->absmin) * 0.5;
}; };

View file

@ -5,13 +5,12 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
# version is now passed into the bot dll
project ( project (
'yapb', 'yapb',
'cpp', 'cpp',
version: '4.3', version: '4.3',
license: 'MIT', license: 'MIT',
default_options: [ default_options: [
'buildtype=release', 'buildtype=release',
'b_ndebug=if-release', 'b_ndebug=if-release',
@ -22,211 +21,202 @@ project (
'optimization=3', 'optimization=3',
'default_library=static', 'default_library=static',
'cpp_eh=none', 'cpp_eh=none',
'cpp_rtti=false',
'b_vscrt=static_from_buildtype', 'b_vscrt=static_from_buildtype',
'b_lto=true', 'b_lto=true',
'b_lto_mode=default',
'b_lundef=true', 'b_lundef=true',
], ],
meson_version: '>=0.56.0') meson_version: '>=0.58.0')
find_program ('ninja', required: true) find_program('ninja', required: true)
find_program ('git', required: true)
find_program ('hostname', required: true)
cpp = meson.get_compiler ('cpp') compiler= meson.get_compiler('cpp')
sys = host_machine.system () cross = meson.is_cross_build ()
target = host_machine.cpu_family () os = host_machine.system()
version = meson.project_version () cpu = host_machine.cpu_family()
count = run_command ('git', 'rev-list', '--count', 'HEAD', check: false).stdout ().strip () cxx = compiler.get_id()
build_type = get_option ('buildtype')
cpp_id = cpp.get_id ()
cpp_version = cpp.version ()
optmize = get_option ('buildtype') == 'release' or get_option ('buildtype') == 'debugoptimized'
msvc = cpp_id == 'msvc' or cpp_id == 'clang-cl'
gcc = cpp_id == 'gcc'
clang = cpp_id == 'clang'
win32 = sys == 'windows'
linux = sys == 'linux'
mac = sys == 'darwin'
aarch64 = target == 'aarch64'
# cpp and ldflags from scratch
cxxflags = []
ldflags = [] ldflags = []
ccflags = []
cdata = configuration_data() # custom flags
flags_versioned = ['-DVERSION_GENERATED']
if win32 # git is optional, but it's setups our version info
cdata.set ('version_win', ','.join (version.split ('.'))) git = find_program('git', required: false)
cdata.set ('machine', run_command ('hostname', check: false).stdout ().strip ())
else if git.found()
cdata.set ('version_win', version) run_command('git', 'config', '--global', '--add', 'safe.directory', '*', check: false)
cdata.set ('machine', run_command ('hostname', '-f', check: false).stdout ().strip ())
# get the commit data
count = run_command('git', 'rev-list', '--count', 'HEAD', check: false).stdout().strip()
hash = run_command ('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip()
author = run_command ('git', 'log', '--pretty="%ae"', '-1', check: false).stdout().strip()
hostname = find_program('hostname', required: false)
if hostname.found()
machine = run_command('hostname', check: false).stdout().strip()
else
machine = 'unknown'
endif
bot_version = meson.project_version()
cxx_version = compiler.version()
if os == 'windows'
winver = ','.join(bot_version.split('.'))
else
winver = bot_version
endif
cxxflags += flags_versioned
version_data = configuration_data()
version_data.set('count', count)
version_data.set('hash', hash)
version_data.set('author', author)
version_data.set('machine', machine)
version_data.set('version', bot_version)
version_data.set('winver', winver)
version_data.set('compiler', '@0@ @1@'.format (cxx, cxx_version))
configure_file (input: 'inc/version.h.in', output: 'version.build.h', configuration: version_data)
endif endif
cdata.set ('hash', run_command ('git', 'rev-parse', '--short', 'HEAD', check: false).stdout ().strip ()) # configure flags gcc and clang
cdata.set ('author', run_command ('git', 'log', '--pretty="%ae"', '-1', check: false).stdout ().strip ()) if cxx == 'clang' or cxx == 'gcc'
cxxflags += [
cdata.set ('count', count) '-mtune=generic', '-fno-threadsafe-statics'
cdata.set ('version', version)
cdata.set ('compiler', cpp_id + ' ' + cpp_version)
configure_file (input: 'inc/version.h.in', output: 'version.build.h', configuration: cdata)
ccflags += '-DVERSION_GENERATED'
if clang or gcc
ccflags += [
'-fno-threadsafe-statics',
'-fno-exceptions',
'-fno-rtti'
] ]
if not aarch64 if cpu == 'aarch64'
ccflags += '-m32' cxxflags += [
endif '-march=armv8-a+fp+simd',
if not mac
ccflags += [
'-pedantic',
]
endif
if optmize
if (clang or gcc) and not mac
if not aarch64 and not (clang and win32)
ccflags += [
'-fdata-sections',
'-ffunction-sections'
]
endif
if gcc
ccflags += '-fgraphite-identity'
ldflags += '-flto-partition=none'
endif
if not aarch64 and not (clang and win32)
ldflags += [
'-Wl,--version-script=../ldscript.lds',
'-Wl,--gc-sections'
]
endif
endif
endif
if linux
ldflags += [
'-lm',
'-ldl'
]
if not aarch64
ldflags += '-m32'
endif
endif
endif
if linux or mac or (win32 and (gcc or clang))
if mac
ccflags += '-mmacosx-version-min=10.9'
ldflags += [
'-lstdc++',
'-mmacosx-version-min=10.9'
] ]
else else
ldflags += '-static-libgcc' cxxflags += [
'-march=x86-64', '-mmmx', '-msse', '-msse2', '-msse3', '-mssse3', '-mfpmath=sse'
]
endif endif
if not optmize # setup optimization flags
ccflags += [ if build_type == 'release'
'-g3', cxxflags += [
'-ggdb', '-funroll-loops', '-fomit-frame-pointer', '-fno-stack-protector', '-fvisibility=hidden', '-fvisibility-inlines-hidden'
'-DCR_DEBUG'
]
else
ccflags += [
'-mtune=generic',
'-fno-builtin',
'-funroll-loops',
'-fomit-frame-pointer',
'-fno-stack-protector',
'-fvisibility=hidden',
'-fvisibility-inlines-hidden'
] ]
if not aarch64 if os != 'darwin' and os != 'windows' and cpu != 'aarch64'
ccflags += [ cxxflags += [
'-msse2', '-fdata-sections',
'-mfpmath=sse', '-ffunction-sections'
] ]
else
ccflags += '-march=armv8-a+fp+simd'
endif
if clang and not mac
ldflags += [ ldflags += [
'-nostdlib++', '-Wl,--version-script=../ext/ldscripts/version.lds',
'-Wunused-command-line-argument' '-Wl,--gc-sections'
] ]
elif gcc and not mac
ldflags += '-Wl,--no-undefined' if cxx == 'gcc'
cxxflags += [
'-fgraphite-identity', '-floop-nest-optimize'
]
ldflags += [
'-fgraphite-identity', '-floop-nest-optimize', '-flto-partition=none'
]
endif
endif
else
cxxflags += ['-g3', '-ggdb', '-DDEBUG', '-D_FORTIFY_SOURCE=2']
endif
# special script for mingw-64 builds
if os == 'windows' and cxx == 'gcc' and compiler.version().version_compare('<12.0')
ldflags += [
'-Xlinker', '--script', '-Xlinker', '../ext/ldscripts/i386pe.lds'
]
endif
# always statically link libgcc on non darwin platforms
if os != 'darwin'
if cross or (cxx != 'clang' and os == 'windows')
ldflags += '-static-libgcc'
endif
else
cxxflags += '-mmacosx-version-min=10.9'
ldflags += [
'-lstdc++', '-mmacosx-version-min=10.9'
]
endif
# by default we buid 32bit binaries
if cpu != 'aarch64' and not get_option('64bit')
cxxflags += '-m32'
ldflags += '-m32'
if cross and cxx == 'clang' and os == 'windows'
ldflags += '-Wl,/MACHINE:X86'
cxxflags += '-Wl,/MACHINE:X86'
endif endif
endif endif
endif
if win32 and msvc # link needed libraries
ldflags += [ if os == 'linux'
'/MACHINE:X86',
'user32.lib',
'ws2_32.lib'
]
ccflags += [
'/TP',
'/D _WIN32_WINNT=0x0501',
'/D _USING_V110_SDK71_',
'/Zc:threadSafeInit-'
]
if optmize
ccflags += [
'/GL',
'/GS-',
'/Ob2',
'/Oy',
'/Oi',
'/Ot',
'/fp:precise',
'/GF'
]
ldflags += [ ldflags += [
'/LTCG', '-lm','-ldl'
'delayimp.lib', ]
'/DELAYLOAD:user32.dll', elif os == 'windows'
'/SUBSYSTEM:WINDOWS,5.01', if cxx == 'gcc' or (cross and cxx == 'clang')
] ldflags += '-Wl,--kill-at'
endif
ldflags += [
'-luser32', '-lws2_32'
]
endif
elif os == 'windows' and (cxx =='msvc' or cxx == 'clang-cl')
if not get_option('64bit') and cxx == 'clang'
cxxflags += '/MACHINE:X86'
ldflags += '/MACHINE:X86'
endif endif
elif win32 and (clang or gcc) cxxflags += [
ldflags += [ '/TP', '/D _WIN32_WINNT=0x0501', '/D _USING_V110_SDK71_', '/Zc:threadSafeInit-'
'-Wl,--kill-at',
'-luser32',
'-lws2_32'
] ]
if clang # minor optimizations for release build
ldflags += '-Wl,/MACHINE:X86' if build_type == 'release'
ccflags += '-Wl,/MACHINE:X86' cxxflags += [
else '/GS-', '/Ob2', '/Oy', '/Oi', '/Ot', '/fp:precise', '/GF', '/GS-', '/GF', '/arch:SSE2'
ldflags += ['-Xlinker', '--script', '-Xlinker', '../i386pe.lds'] ]
# add wpo if msvc
if cxx == 'msvc'
cxxflags += [
'/GL'
]
endif
# add linker flags
ldflags += [
'/LTCG', 'delayimp.lib', '/DELAYLOAD:user32.dll', '/DELAYLOAD:ws2_32.dll', '/SUBSYSTEM:WINDOWS,5.01',
]
endif endif
ldflags += [
'user32.lib', 'ws2_32.lib'
]
endif endif
add_global_arguments (ccflags, language: 'cpp') # pass our hell to meson
add_global_arguments (cxxflags, language: 'cpp')
add_global_link_arguments (ldflags, language: 'cpp') add_global_link_arguments (ldflags, language: 'cpp')
# add the sources
sources = files ( sources = files (
'src/botlib.cpp', 'src/botlib.cpp',
'src/chatlib.cpp', 'src/chatlib.cpp',
@ -243,24 +233,27 @@ sources = files (
'src/support.cpp' 'src/support.cpp'
) )
# add the include directories
includes = include_directories ([ includes = include_directories ([
'.', 'inc', 'ext', 'ext/crlib' '.', 'inc', 'ext', 'ext/crlib'
], is_system: true) ], is_system: true)
if win32 # if have git and on windows add windows-specific version info
if os == 'windows' and git.found()
sources += import('windows').compile_resources ( sources += import('windows').compile_resources (
'vc/yapb.rc', 'vc/yapb.rc',
include_directories: includes, include_directories: includes,
args: ['-DVERSION_GENERATED'] args: flags_versioned
) )
endif endif
shared_library ( # instruct meson we're want our little shared lib bot
meson.project_name (), shared_library(
meson.project_name(),
sources, sources,
include_directories: includes, include_directories: includes,
gnu_symbol_visibility: 'hidden', gnu_symbol_visibility: 'hidden',
name_prefix: '') name_prefix: '')
run_target ('package', run_target('package',
command: ['python3', meson.project_source_root () + '/package.py', '@0@.@1@'.format (version, count)]) command: ['python3', meson.project_source_root() + '/package.py', '@0@.@1@'.format(bot_version, count)])

9
meson_options.txt Normal file
View file

@ -0,0 +1,9 @@
#
# YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
# Copyright © 2004-2023 YaPB Project <yapb@jeefo.net>.
#
# SPDX-License-Identifier: MIT
#
option('64bit', type : 'boolean', value : false,
description: 'Enables bot build with as 64-bit binary.')

View file

@ -7,295 +7,310 @@
# #
import os, sys, subprocess, base64 import os, sys, subprocess, base64
import urllib3 import glob, requests
import pathlib, shutil import pathlib, shutil
import zipfile, tarfile import zipfile, tarfile
import datetime, calendar import datetime, calendar
class CodeSign (object): class BotSign(object):
def __init__(self, product, url, verify_signature=False): def __init__(self, product: str, url: str):
self.signing = True self.signing = True
self.ossl_path = "/usr/bin/osslsigncode" self.ossl_path = '/usr/bin/osslsigncode'
self.local_key = os.path.join (pathlib.Path ().absolute (), "key.pfx"); self.local_key = os.path.join(pathlib.Path().absolute(), 'bot_release_key.pfx');
self.product = product self.product = product
self.url = url self.url = url
self.verify_signature = verify_signature
if not os.path.exists (self.ossl_path): if not os.path.exists(self.ossl_path):
self.signing = False self.signing = False
if not "CS_CERTIFICATE" in os.environ: if not 'CS_CERTIFICATE' in os.environ:
self.signing = False self.signing = False
if not "CS_CERTIFICATE_PASSWORD" in os.environ: if not 'CS_CERTIFICATE_PASSWORD' in os.environ:
self.signing = False self.signing = False
if self.signing: if self.signing:
self.password = os.environ.get ("CS_CERTIFICATE_PASSWORD") self.password = os.environ.get('CS_CERTIFICATE_PASSWORD')
encoded = os.environ.get ("CS_CERTIFICATE") encoded = os.environ.get('CS_CERTIFICATE')
if len (encoded) < 64: if len(encoded) < 64:
print ('Damaged certificate. Signing disabled.') print('Damaged certificate. Signing disabled.')
self.signing = False self.signing = False
return return
decoded = base64.b64decode (encoded) decoded = base64.b64decode(encoded)
with open (self.local_key, "wb") as key: with open(self.local_key, 'wb') as key:
key.write (decoded) key.write(decoded)
def has(self): def has(self):
return self.signing return self.signing
def sign_file_inplace (self, filename): def sign_file_inplace(self, filename):
signed_filename = filename + ".signed" signed_filename = filename + '.signed'
sign = [] signed_cmdline = []
sign.append (self.ossl_path) signed_cmdline.append (self.ossl_path)
sign.append ("sign") signed_cmdline.append ('sign')
sign.append ("-pkcs12") signed_cmdline.append ('-pkcs12')
sign.append (self.local_key) signed_cmdline.append (self.local_key)
sign.append ("-pass") signed_cmdline.append ('-pass')
sign.append (self.password) signed_cmdline.append (self.password)
sign.append ("-n") signed_cmdline.append ('-n')
sign.append (self.product) signed_cmdline.append (self.product)
sign.append ("-i") signed_cmdline.append ('-i')
sign.append (self.url) signed_cmdline.append (self.url)
sign.append ("-h") signed_cmdline.append ('-h')
sign.append ("sha384") signed_cmdline.append ('sha384')
sign.append ("-t") signed_cmdline.append ('-t')
sign.append ("http://timestamp.sectigo.com") signed_cmdline.append ('http://timestamp.sectigo.com')
sign.append ("-in") signed_cmdline.append ('-in')
sign.append (filename) signed_cmdline.append (filename)
sign.append ("-out") signed_cmdline.append ('-out')
sign.append (signed_filename) signed_cmdline.append (signed_filename)
result = subprocess.run (sign, capture_output=True, text=True) result = subprocess.run (signed_cmdline, capture_output=True, text=True)
if result.returncode == 0: if result.returncode == 0:
os.unlink (filename) os.unlink(filename)
shutil.move (signed_filename, filename) shutil.move(signed_filename, filename)
print ("Signing result: {}".format (result.stdout)) return False
if self.verify_signature: class BotPackage(object):
verify = [] def __init__(self, name: str, archive: str, artifact: dict, extra: bool = False):
verify.append (self.ossl_path) self.name = name
verify.append ("verify") self.archive = archive
verify.append (filename) self.artifact = artifact
self.extra = extra
verify = subprocess.run (verify, capture_output=True, text=True) class BotRelease(object):
print (verify.stdout)
else:
print (result.stdout)
class BotRelease (object):
def __init__(self): def __init__(self):
print ("Initializing Packaging") if len(sys.argv) < 2:
raise Exception('Missing required parameters.')
meson_src_root_env = "MESON_SOURCE_ROOT"
if meson_src_root_env in os.environ:
os.chdir (os.environ.get (meson_src_root_env))
self.work_dir = os.path.join (pathlib.Path ().absolute (), "cfg")
self.bot_dir = os.path.join (self.work_dir, "addons", "yapb")
self.pkg_dir = os.path.join (pathlib.Path ().absolute (), "pkg")
if len (sys.argv) < 2:
raise Exception("Missing required parameters.")
self.project = 'yapb'
self.version = sys.argv[1] self.version = sys.argv[1]
self.artifacts = 'artifacts' self.artifacts = 'artifacts'
self.graphs = 'yapb-gcdn.akamaized.net'
self.win32exe = 'https://github.com/yapb/setup/releases/latest/download/botsetup.exe'
self.cs = CodeSign ("YaPB", "https://yapb.jeefo.net/") meson_src_root_env = 'MESON_SOURCE_ROOT'
if self.cs.has (): if meson_src_root_env in os.environ:
print ("Code Signing Enabled") os.chdir(os.environ.get(meson_src_root_env))
else: else:
print ("Code Signing Disabled") raise Exception(f'No direct access, only via meson build.')
os.makedirs (self.pkg_dir, exist_ok=True) path = pathlib.Path().absolute()
self.pkg_win32 = os.path.join (self.pkg_dir, "yapb-{}-windows.zip".format (self.version)) if not os.path.isdir(os.path.join(path, self.artifacts)):
self.pkg_linux = os.path.join (self.pkg_dir, "yapb-{}-linux.tar.xz".format (self.version)) raise Exception('Artifacts directory missing.')
self.pkg_macos = os.path.join (self.pkg_dir, "yapb-{}-macos.zip".format (self.version))
self.pkg_win32_sfx = self.pkg_win32.replace ("zip", "exe") print(f'Releasing {self.project} v{self.version}')
self.pkg_win32_sfx_url = "https://github.com/yapb/setup/releases/latest/download/botsetup.exe"
def make_directories (self): self.work_dir = os.path.join(path, 'release')
dirs = [ shutil.copytree(f'{path}/cfg', self.work_dir, dirs_exist_ok=True)
"bin",
os.path.join ("data", "pwf"),
os.path.join ("data", "train"),
os.path.join ("data", "graph"),
os.path.join ("data", "logs")
]
for dir in dirs: self.bot_dir = os.path.join(self.work_dir, 'addons', self.project)
os.makedirs (os.path.join (self.bot_dir, dir), exist_ok=True) self.pkg_dir = os.path.join(path, 'pkg')
def http_pull (self, url, local_file): self.cs = BotSign('YaPB', 'https://yapb.jeefo.net/')
http = urllib3.PoolManager (10, headers = {"user-agent": "YaPB"})
data = http.urlopen ("GET", url)
with open (local_file, "wb") as file: if self.cs.has():
file.write (data.data) print('Signing enabled')
else:
print('Signing disabled')
def get_graph_file (self, name): os.makedirs(self.pkg_dir, exist_ok=True)
file = os.path.join (self.bot_dir, "data", "graph", "{}.graph".format (name)) self.http_pull(self.win32exe, 'botsetup.exe')
url = "https://yapb.jeefo.net/graph/{}.graph".format (name)
if os.path.exists (file): self.pkg_matrix = []
self.pkg_matrix.append (BotPackage('windows', 'zip', {'windows-x86': 'dll'}))
self.pkg_matrix.append (BotPackage('windows', 'exe', {'windows-x86': 'dll'}))
self.pkg_matrix.append (BotPackage('linux', 'tar.xz', {'linux-x86': 'so'}))
self.pkg_matrix.append (BotPackage('darwin', 'zip', {'darwin-x86': 'dylib'}))
self.pkg_matrix.append (BotPackage('extras', 'zip', {'linux-aarch64': 'so', 'linux-x86-gcc': 'so', 'windows-x86-gcc': 'dll', 'windows-x86-msvc': 'dll'}, extra=True))
def create_dirs(self):
for dir in ['pwf', 'train', 'graph', 'logs']:
os.makedirs(os.path.join(self.bot_dir, 'data', dir), exist_ok=True)
def http_pull(self, url: str, tp: str):
headers = {
'User-Agent': 'YaPB/4',
}
with requests.get(url, headers=headers) as r:
r.raise_for_status()
with open(tp, 'wb') as f:
f.write(r.content)
def get_graph_file(self, name: str):
file = os.path.join(self.bot_dir, 'data', 'graph', f'{name}.graph')
url = f'http://{self.graphs}/graph/{name}.graph'
if os.path.exists(file):
return return
self.http_pull (url, file) self.http_pull(url, file)
def get_default_graphs (self): def create_graphs(self):
print ("Downloading graphs: ") default_list = 'default.graph.txt'
self.http_pull(f'http://{self.graphs}/DEFAULT.txt', default_list)
default_list = "default.graph.txt" with open(default_list) as file:
self.http_pull ("https://gs.yapb.jeefo.net//DEFAULT.txt", default_list) files = [line.rstrip() for line in file.readlines()]
with open (default_list) as file:
files = [line.rstrip () for line in file.readlines ()]
for file in files: for file in files:
print (" " + file) print(f'Getting graphs: {file} ', end='\r', flush=True)
self.get_graph_file(file)
self.get_graph_file (file) print()
def unlink_binaries (self): def compress_directory(self, path: str, handle: zipfile.ZipFile):
libs = ["yapb.so", "yapb.arm64.so", "yapb.dll", "yapb.dylib"] length = len(path) + 1
for lib in libs:
path = os.path.join (self.bot_dir, "bin", lib)
if os.path.exists (path):
os.remove (path)
def sign_binary (self, binary):
if self.cs.has () and (binary.endswith ("dll") or binary.endswith ("exe")):
print ("Signing {}".format (binary))
self.cs.sign_file_inplace (binary)
def copy_binary (self, binary):
dest_path = os.path.join (self.bot_dir, "bin", os.path.basename (binary))
shutil.copy (binary, dest_path)
self.sign_binary (dest_path)
def compress_directory (self, path, handle):
length = len (path) + 1
empty_dirs = [] empty_dirs = []
for root, dirs, files in os.walk (path): for root, dirs, files in os.walk(path):
empty_dirs.extend ([dir for dir in dirs if os.listdir (os.path.join (root, dir)) == []]) empty_dirs.extend([dir for dir in dirs if os.listdir(os.path.join(root, dir)) == []])
for file in files: for file in files:
file_path = os.path.join (root, file) file_path = os.path.join(root, file)
handle.write (file_path, file_path[length:]) handle.write(file_path, file_path[length:])
for dir in empty_dirs: for dir in empty_dirs:
dir_path = os.path.join (root, dir) dir_path = os.path.join(root, dir)
zif = zipfile.ZipInfo (dir_path[length:] + "/") zif = zipfile.ZipInfo(dir_path[length:] + '/')
handle.writestr (zif, "") handle.writestr(zif, '')
empty_dirs = [] empty_dirs = []
def create_zip (self, dir): def create_zip(self, dest: str, custom_dir: str = None):
zf = zipfile.ZipFile (dir, "w", zipfile.ZIP_DEFLATED, compresslevel=9) zf = zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED, compresslevel=9)
zf.comment = bytes (self.version, encoding = "ascii") zf.comment = bytes(self.version, encoding = 'ascii')
self.compress_directory (self.work_dir, zf) self.compress_directory(custom_dir if custom_dir else self.work_dir, zf)
zf.close () zf.close()
def convert_zip_txz (self, zfn, txz): def convert_zip_txz(self, zfn: str, txz: str):
timeshift = int ((datetime.datetime.now () - datetime.datetime.utcnow ()).total_seconds ()) timeshift = int((datetime.datetime.now() - datetime.datetime.utcnow()).total_seconds())
with zipfile.ZipFile (zfn) as zipf: with zipfile.ZipFile(zfn) as zipf:
with tarfile.open (txz, "w:xz") as tarf: with tarfile.open(txz, 'w:xz') as tarf:
for zif in zipf.infolist (): for zif in zipf.infolist():
tif = tarfile.TarInfo (name = zif.filename) tif = tarfile.TarInfo(name = zif.filename)
tif.size = zif.file_size tif.size = zif.file_size
tif.mtime = calendar.timegm (zif.date_time) - timeshift tif.mtime = calendar.timegm(zif.date_time) - timeshift
tarf.addfile (tarinfo = tif, fileobj = zipf.open (zif.filename)) tarf.addfile(tarinfo = tif, fileobj = zipf.open(zif.filename))
os.remove (zfn) os.remove(zfn)
def install_binary (self, ext, unlink_existing = True): def convert_zip_sfx(self, zfn: str, exe: str):
lib = "yapb.{}".format (ext) with open('botsetup.exe', 'rb') as sfx, open(zfn, 'rb') as zfn, open(exe, 'wb') as dest:
binary = os.path.join (self.artifacts, lib) dest.write(sfx.read())
dest.write(zfn.read())
if os.path.isdir (binary): self.sign_binary(exe)
binary = os.path.join (binary, lib)
if not os.path.exists (binary): def unlink_binaries(self):
print ("Packaging failed for {}. Skipping...".format (lib)) path = os.path.join(self.bot_dir, 'bin');
return False
if unlink_existing: shutil.rmtree(path, ignore_errors=True)
self.unlink_binaries () os.makedirs(path, exist_ok=True)
self.copy_binary (binary) def sign_binary(self, binary: str):
if self.cs.has() and (binary.endswith('dll') or binary.endswith('exe')):
self.cs.sign_file_inplace(binary)
return True def copy_binary(self, binary: str, artifact: str):
if artifact:
dest_path = os.path.join(self.bot_dir, 'bin', artifact)
os.makedirs(dest_path, exist_ok=True)
def create_pkg_win32 (self): dest_path = os.path.join(dest_path, os.path.basename(binary))
print ("Generating Win32 ZIP") else:
dest_path = os.path.join(self.bot_dir, 'bin', os.path.basename(binary))
if not self.install_binary ("dll"): shutil.copy(binary, dest_path)
self.sign_binary(dest_path)
def install_binary(self, pkg: BotPackage):
num_artifacts_errors = 0
num_artifacts = len(pkg.artifact)
for artifact in pkg.artifact:
binary = os.path.join(self.artifacts, artifact, f'{self.project}.{pkg.artifact[artifact]}')
binary_base = os.path.basename(binary)
if not os.path.exists(binary):
num_artifacts_errors += 1
print(f'[{binary_base}: FAIL]', end=' ')
continue
print(f'[{binary_base}: OK]', end=' ')
if num_artifacts == 1:
self.unlink_binaries()
self.copy_binary(binary, artifact if pkg.extra else None)
return num_artifacts_errors < num_artifacts
def create_pkg(self, pkg: BotPackage):
dest = os.path.join (self.pkg_dir, f'{self.project}-{self.version}-{pkg.name}.{pkg.archive}')
dest_tmp = f'{dest}.tmp'
if os.path.exists(dest):
os.remove(dest)
self.unlink_binaries()
print(f'Generating {os.path.basename(dest)}:', end=' ')
if not self.install_binary(pkg):
print(' -> Failed...')
return return
self.create_zip (self.pkg_win32) if dest.endswith('zip') or dest.endswith('exe'):
self.http_pull (self.pkg_win32_sfx_url, "botsetup.exe") if pkg.extra:
dest_dir = os.path.join(self.bot_dir, 'bin')
self.create_zip(dest_tmp, dest_dir)
else:
self.create_zip(dest_tmp)
print ("Generating Win32 EXE") if dest.endswith('exe'):
self.convert_zip_sfx(dest_tmp, dest)
else:
shutil.move(dest_tmp, dest)
elif dest.endswith('tar.xz'):
self.create_zip(dest_tmp)
self.convert_zip_txz(dest_tmp, dest)
with open ("botsetup.exe", "rb") as sfx, open (self.pkg_win32, "rb") as zfn, open (self.pkg_win32_sfx, "wb") as exe: print('-> Success...')
exe.write (sfx.read ()) self.unlink_binaries()
exe.write (zfn.read ())
self.sign_binary (self.pkg_win32_sfx) if os.path.exists(dest_tmp):
os.remove(dest_tmp)
def create_pkg_linux (self): def create_pkgs(self):
print ("Generating Linux TXZ") for pkg in self.pkg_matrix:
self.create_pkg(pkg)
self.unlink_binaries () print('Finished release')
self.install_binary ("arm64.so")
if not self.install_binary ("so", False): @staticmethod
return def run():
r = BotRelease()
tmp_file = "tmp.zip" r.create_dirs()
r.create_graphs()
self.create_zip (tmp_file) r.create_pkgs()
self.convert_zip_txz (tmp_file, self.pkg_linux)
def create_pkg_macos (self):
print ("Generating macOS ZIP")
if not self.install_binary ("dylib"):
return
self.create_zip (self.pkg_macos)
def create_pkgs (self):
self.create_pkg_linux ()
self.create_pkg_win32 ()
self.create_pkg_macos ()
release = BotRelease ()
release.make_directories ()
release.get_default_graphs ()
release.create_pkgs ()
# entry point
if __name__ == "__main__":
BotRelease.run()

View file

@ -71,8 +71,8 @@ void Bot::pushMsgQueue (int message) {
} }
float Bot::isInFOV (const Vector &destination) { float Bot::isInFOV (const Vector &destination) {
float entityAngle = cr::modAngles (destination.yaw ()); // find yaw angle from source to destination... float entityAngle = cr::wrapAngle360 (destination.yaw ()); // find yaw angle from source to destination...
float viewAngle = cr::modAngles (pev->v_angle.y); // get bot's current view angle... float viewAngle = cr::wrapAngle360 (pev->v_angle.y); // get bot's current view angle...
// return the absolute value of angle to destination entity // return the absolute value of angle to destination entity
// zero degrees means straight ahead, 45 degrees to the left or // zero degrees means straight ahead, 45 degrees to the left or
@ -232,7 +232,8 @@ void Bot::checkGrenadesThrow () {
} }
break; break;
case Weapon::Flashbang: { case Weapon::Flashbang:
{
int nearest = graph.getNearest ((m_lastEnemy->v.velocity * 0.5f).get2d () + m_lastEnemy->v.origin); int nearest = graph.getNearest ((m_lastEnemy->v.velocity * 0.5f).get2d () + m_lastEnemy->v.origin);
if (nearest != kInvalidNodeIndex) { if (nearest != kInvalidNodeIndex) {
@ -339,7 +340,7 @@ void Bot::avoidGrenades () {
if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == Difficulty::Expert && strcmp (model, "flashbang.mdl") == 0) { if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == Difficulty::Expert && strcmp (model, "flashbang.mdl") == 0) {
// don't look at flash bang // don't look at flash bang
if (!(m_states & Sense::SeeingEnemy)) { if (!(m_states & Sense::SeeingEnemy)) {
m_lookAt.y = cr::normalizeAngles ((game.getEntityOrigin (pent) - getEyesPos ()).angles ().y + 180.0f); m_lookAt.y = cr::wrapAngle ((game.getEntityOrigin (pent) - getEyesPos ()).angles ().y + 180.0f);
m_canChooseAimDirection = false; m_canChooseAimDirection = false;
m_preventFlashing = game.time () + rg.get (1.0f, 2.0f); m_preventFlashing = game.time () + rg.get (1.0f, 2.0f);
@ -636,6 +637,7 @@ void Bot::updatePickups () {
const auto &config = conf.getWeapons (); const auto &config = conf.getWeapons ();
const auto &primary = config[primaryWeaponCarried]; const auto &primary = config[primaryWeaponCarried];
const auto &secondary = config[secondaryWeaponCarried]; const auto &secondary = config[secondaryWeaponCarried];
const auto &primaryProp = conf.getWeaponProp (primary.id); const auto &primaryProp = conf.getWeaponProp (primary.id);
const auto &secondaryProp = conf.getWeaponProp (secondary.id); const auto &secondaryProp = conf.getWeaponProp (secondary.id);
@ -950,7 +952,7 @@ void Bot::showChaterIcon (bool show) {
return; return;
} }
auto sendBotVoice = [] (bool show, edict_t *ent, int ownId) { auto sendBotVoice = [&show] (edict_t *ent, int ownId) {
MessageWriter (MSG_ONE, msgs.id (NetMsg::BotVoice), nullptr, ent) // begin message MessageWriter (MSG_ONE, msgs.id (NetMsg::BotVoice), nullptr, ent) // begin message
.writeByte (show) // switch on/off .writeByte (show) // switch on/off
.writeByte (ownId); .writeByte (ownId);
@ -964,13 +966,13 @@ void Bot::showChaterIcon (bool show) {
} }
if (!show && (client.iconFlags[ownIndex] & ClientFlags::Icon) && client.iconTimestamp[ownIndex] < game.time ()) { if (!show && (client.iconFlags[ownIndex] & ClientFlags::Icon) && client.iconTimestamp[ownIndex] < game.time ()) {
sendBotVoice (false, client.ent, entindex ()); sendBotVoice (client.ent, entindex ());
client.iconTimestamp[ownIndex] = 0.0f; client.iconTimestamp[ownIndex] = 0.0f;
client.iconFlags[ownIndex] &= ~ClientFlags::Icon; client.iconFlags[ownIndex] &= ~ClientFlags::Icon;
} }
else if (show && !(client.iconFlags[ownIndex] & ClientFlags::Icon)) { else if (show && !(client.iconFlags[ownIndex] & ClientFlags::Icon)) {
sendBotVoice (true, client.ent, entindex ()); sendBotVoice (client.ent, entindex ());
} }
} }
} }
@ -1225,43 +1227,36 @@ bool Bot::isWeaponRestrictedAMX (int weaponIndex) {
return false; return false;
} }
auto checkRestriction = [&weaponIndex] (StringRef cvar, const int *data) -> bool {
// check for weapon restrictions auto restrictedWeapons = game.findCvar (cvar);
if (cr::bit (weaponIndex) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) {
auto restrictedWeapons = game.findCvar ("amx_restrweapons");
if (restrictedWeapons.empty ()) { if (restrictedWeapons.empty ()) {
return false; return false;
} }
constexpr int indices[] = { 4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11 };
// find the weapon index // find the weapon index
int index = indices[weaponIndex - 1]; int index = data[weaponIndex - 1];
// validate index range // validate index range
if (index < 0 || index >= static_cast <int> (restrictedWeapons.length ())) { if (index < 0 || index >= static_cast <int> (restrictedWeapons.length ())) {
return false; return false;
} }
return restrictedWeapons[index] != '0'; return restrictedWeapons[static_cast <size_t> (index)] != '0';
};
// check for weapon restrictions
if (cr::bit (weaponIndex) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) {
constexpr int ids[] = { 4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11 };
// verify restrictions
return checkRestriction ("amx_restrweapons", ids);
} }
// check for equipment restrictions // check for equipment restrictions
else { else {
auto restrictedEquipment = game.findCvar ("amx_restrequipammo"); constexpr int ids[] = { -1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 0, 1, 5 };
if (restrictedEquipment.empty ()) { // verify restrictions
return false; return checkRestriction ("amx_restrequipammo", ids);
}
constexpr int indices[] = { -1, -1, -1, 3, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 0, 1, 5 };
// find the weapon index
int index = indices[weaponIndex - 1];
// validate index range
if (index < 0 || index >= static_cast <int> (restrictedEquipment.length ())) {
return false;
}
return restrictedEquipment[index] != '0';
} }
} }
@ -1294,7 +1289,7 @@ int Bot::pickBestWeapon (int *vec, int count, int moneySave) {
bool needMoreRandomWeapon = (m_personality == Personality::Careful) || (rg.chance (25) && m_personality == Personality::Normal); bool needMoreRandomWeapon = (m_personality == Personality::Careful) || (rg.chance (25) && m_personality == Personality::Normal);
if (needMoreRandomWeapon) { if (needMoreRandomWeapon) {
auto buyFactor = (m_moneyAmount - static_cast <float> (moneySave)) / (16000.0f - static_cast <float> (moneySave)) * 3.0f; auto buyFactor = (static_cast <float> (m_moneyAmount) - static_cast <float> (moneySave)) / (16000.0f - static_cast <float> (moneySave)) * 3.0f;
if (buyFactor < 1.0f) { if (buyFactor < 1.0f) {
buyFactor = 1.0f; buyFactor = 1.0f;
@ -1537,7 +1532,7 @@ void Bot::buyStuff () {
break; break;
case BuyState::ArmorVestHelm: // if armor is damaged and bot has some money, buy some armor case BuyState::ArmorVestHelm: // if armor is damaged and bot has some money, buy some armor
if (pev->armorvalue < rg.get (50, 80) && teamHasGoodEconomics && (isPistolMode || (teamHasGoodEconomics && hasPrimaryWeapon ()))) { if (pev->armorvalue < rg.get (50.0f, 80.0f) && teamHasGoodEconomics && (isPistolMode || (teamHasGoodEconomics && hasPrimaryWeapon ()))) {
// if bot is rich, buy kevlar + helmet, else buy a single kevlar // if bot is rich, buy kevlar + helmet, else buy a single kevlar
if (m_moneyAmount > 1500 && !isWeaponRestricted (Weapon::ArmorHelm)) { if (m_moneyAmount > 1500 && !isWeaponRestricted (Weapon::ArmorHelm)) {
issueCommand ("buyequip;menuselect 2"); issueCommand ("buyequip;menuselect 2");
@ -1988,7 +1983,7 @@ void Bot::filterTasks () {
timeHeard += 10.0f; timeHeard += 10.0f;
ratio = timeHeard * 0.1f; ratio = timeHeard * 0.1f;
} }
bool lowAmmo = m_ammoInClip[m_currentWeapon] < conf.findWeaponById (m_currentWeapon).maxClip * 0.18f; bool lowAmmo = isLowOnAmmo (m_currentWeapon, 0.18f);
bool sniping = m_sniperStopTime <= game.time () && lowAmmo; bool sniping = m_sniperStopTime <= game.time () && lowAmmo;
if (bots.isBombPlanted () || m_isStuck || usesKnife ()) { if (bots.isBombPlanted () || m_isStuck || usesKnife ()) {
@ -2169,6 +2164,10 @@ BotTask *Bot::getTask () {
return &m_tasks.last (); return &m_tasks.last ();
} }
bool Bot::isLowOnAmmo (const int id, const float factor) const {
return static_cast <float> (m_ammoInClip[id]) < static_cast <float> (conf.findWeaponById (id).maxClip) * factor;
}
void Bot::clearTask (Task id) { void Bot::clearTask (Task id) {
// this function removes one task from the bot task stack. // this function removes one task from the bot task stack.
@ -2868,7 +2867,7 @@ void Bot::updateAimDir () {
auto radius = graph[index].radius; auto radius = graph[index].radius;
if (radius > 0.0f) { if (radius > 0.0f) {
return Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (2.0f, 4.0f); return Vector (pev->angles.x, cr::wrapAngle (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (2.0f, 4.0f);
} }
return nullptr; return nullptr;
}; };
@ -3952,7 +3951,7 @@ void Bot::defuseBomb_ () {
graph.setBombOrigin (true); graph.setBombOrigin (true);
if (m_numFriendsLeft != 0 && rg.chance (50)) { if (m_numFriendsLeft != 0 && rg.chance (50)) {
if (timeToBlowUp <= 3.0) { if (timeToBlowUp <= 3.0f) {
if (cv_radio_mode.int_ () == 2) { if (cv_radio_mode.int_ () == 2) {
pushChatterMessage (Chatter::BarelyDefused); pushChatterMessage (Chatter::BarelyDefused);
} }
@ -4016,7 +4015,7 @@ void Bot::defuseBomb_ () {
selectWeaponByName ("weapon_knife"); selectWeaponByName ("weapon_knife");
if (weaponIndex > 0 && weaponIndex < kNumWeapons) { if (weaponIndex > 0 && weaponIndex < kNumWeapons) {
selectWeaponById (weaponIndex); selectWeaponByIndex (weaponIndex);
} }
m_isReloading = false; m_isReloading = false;
} }
@ -4588,16 +4587,16 @@ void Bot::pickupItem_ () {
if (index < 7) { if (index < 7) {
// secondary weapon. i.e., pistol // secondary weapon. i.e., pistol
int wid = 0; int weaponIndex = 0;
for (index = 0; index < 7; ++index) { for (index = 0; index < 7; ++index) {
if (pev->weapons & cr::bit (info[index].id)) { if (pev->weapons & cr::bit (info[index].id)) {
wid = index; weaponIndex = index;
} }
} }
if (wid > 0) { if (weaponIndex > 0) {
selectWeaponById (wid); selectWeaponByIndex (weaponIndex);
issueCommand ("drop"); issueCommand ("drop");
if (hasShield ()) { if (hasShield ()) {
@ -4608,15 +4607,17 @@ void Bot::pickupItem_ () {
} }
else { else {
// primary weapon // primary weapon
int wid = bestWeaponCarried (); int weaponIndex = bestWeaponCarried ();
bool niceWeapon = rateGroundWeapon (m_pickupItem); bool niceWeapon = rateGroundWeapon (m_pickupItem);
if ((wid == Weapon::Shield || wid > 6 || hasShield ()) && niceWeapon) { auto tab = conf.getRawWeapons ();
selectWeaponById (wid);
if ((tab->id == Weapon::Shield || weaponIndex > 6 || hasShield ()) && niceWeapon) {
selectWeaponByIndex (weaponIndex);
issueCommand ("drop"); issueCommand ("drop");
} }
if (!wid || !niceWeapon) { if (!weaponIndex || !niceWeapon) {
m_itemIgnore = m_pickupItem; m_itemIgnore = m_pickupItem;
m_pickupItem = nullptr; m_pickupItem = nullptr;
m_pickupType = Pickup::None; m_pickupType = Pickup::None;
@ -4640,10 +4641,10 @@ void Bot::pickupItem_ () {
// near to shield? // near to shield?
else if (itemDistance < 50.0f) { else if (itemDistance < 50.0f) {
// get current best weapon to check if it's a primary in need to be dropped // get current best weapon to check if it's a primary in need to be dropped
int wid = bestWeaponCarried (); int weaponIndex = bestWeaponCarried ();
if (wid > 6) { if (weaponIndex > 6) {
selectWeaponById (wid); selectWeaponByIndex (weaponIndex);
issueCommand ("drop"); issueCommand ("drop");
} }
} }
@ -5059,7 +5060,7 @@ void Bot::logic () {
pev->button &= ~IN_DUCK; pev->button &= ~IN_DUCK;
m_moveSpeed = -pev->maxspeed; m_moveSpeed = -pev->maxspeed;
m_strafeSpeed = pev->maxspeed * m_needAvoidGrenade; m_strafeSpeed = pev->maxspeed * static_cast <float> (m_needAvoidGrenade);
} }
// time to reach waypoint // time to reach waypoint
@ -5222,11 +5223,11 @@ void Bot::showDebugOverlay () {
} }
String aimFlags; String aimFlags;
for (int i = 0; i < 9; ++i) { for (uint32 i = 0u; i < 9u; ++i) {
bool hasFlag = m_aimFlags & cr::bit (i); auto bit = cr::bit (i);
if (hasFlag) { if (m_aimFlags & bit) {
aimFlags.appendf (" %s", flags[cr::bit (i)]); aimFlags.appendf (" %s", flags[static_cast <int32> (bit)]);
} }
} }
auto weapon = util.weaponIdToAlias (m_currentWeapon); auto weapon = util.weaponIdToAlias (m_currentWeapon);
@ -5289,7 +5290,11 @@ bool Bot::hasHostage () {
} }
int Bot::getAmmo () { int Bot::getAmmo () {
const auto &prop = conf.getWeaponProp (m_currentWeapon); return getAmmo (m_currentWeapon);
}
int Bot::getAmmo (int id) {
const auto &prop = conf.getWeaponProp (id);
if (prop.ammo1 == -1 || prop.ammo1 > kMaxWeapons - 1) { if (prop.ammo1 == -1 || prop.ammo1 > kMaxWeapons - 1) {
return 0; return 0;
@ -5408,11 +5413,12 @@ void Bot::updatePracticeValue (int damage) {
if (graph.length () < 1 || graph.hasChanged () || m_chosenGoalIndex < 0 || m_prevGoalIndex < 0) { if (graph.length () < 1 || graph.hasChanged () || m_chosenGoalIndex < 0 || m_prevGoalIndex < 0) {
return; return;
} }
auto health = static_cast <int> (m_healthValue);
// only rate goal waypoint if bot died because of the damage // only rate goal waypoint if bot died because of the damage
// FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons // FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons
if (m_healthValue - damage <= 0) { if (health - damage <= 0) {
graph.setDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex, cr::clamp (graph.getDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex) - static_cast <int> (m_healthValue / 20), -kMaxPracticeGoalValue, kMaxPracticeGoalValue)); graph.setDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex, cr::clamp (graph.getDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex) - health / 20, -kMaxPracticeGoalValue, kMaxPracticeGoalValue));
} }
} }
@ -5453,10 +5459,10 @@ void Bot::updatePracticeDamage (edict_t *attacker, int damage) {
graph.setDangerDamage (victimIndex, victimIndex, victimIndex, cr::clamp (graph.getDangerDamage (victimTeam, victimIndex, victimIndex), 0, kMaxPracticeDamageValue)); graph.setDangerDamage (victimIndex, victimIndex, victimIndex, cr::clamp (graph.getDangerDamage (victimTeam, victimIndex, victimIndex), 0, kMaxPracticeDamageValue));
} }
} }
float updateDamage = util.isFakeClient (attacker) ? 10.0f : 7.0f; auto updateDamage = util.isFakeClient (attacker) ? 10 : 7;
// store away the damage done // store away the damage done
int damageValue = cr::clamp (graph.getDangerDamage (m_team, victimIndex, attackerIndex) + static_cast <int> (damage / updateDamage), 0, kMaxPracticeDamageValue); int damageValue = cr::clamp (graph.getDangerDamage (m_team, victimIndex, attackerIndex) + damage / updateDamage, 0, kMaxPracticeDamageValue);
if (damageValue > graph.getHighestDamageForTeam (m_team)) { if (damageValue > graph.getHighestDamageForTeam (m_team)) {
graph.setHighestDamageForTeam (m_team, damageValue); graph.setHighestDamageForTeam (m_team, damageValue);
@ -6047,7 +6053,7 @@ float Bot::getShiftSpeed () {
if (getCurrentTaskId () == Task::SeekCover || (pev->flags & FL_DUCKING) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PathFlag::Jump) || (m_path != nullptr && m_path->flags & NodeFlag::Ladder) || isOnLadder () || isInWater () || m_isStuck) { if (getCurrentTaskId () == Task::SeekCover || (pev->flags & FL_DUCKING) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PathFlag::Jump) || (m_path != nullptr && m_path->flags & NodeFlag::Ladder) || isOnLadder () || isInWater () || m_isStuck) {
return pev->maxspeed; return pev->maxspeed;
} }
return static_cast <float> (pev->maxspeed * 0.4f); return pev->maxspeed * 0.4f;
} }
void Bot::calculateFrustum () { void Bot::calculateFrustum () {

View file

@ -61,12 +61,12 @@ void BotSupport::addChatErrors (String &line) {
// "length / 2" percent of time drop a character // "length / 2" percent of time drop a character
if (rg.chance (percentile)) { if (rg.chance (percentile)) {
line.erase (rg.get (length / 8, length - length / 8), 1); line.erase (static_cast <size_t> (rg.get (length / 8, length - length / 8), 1));
} }
// "length" / 4 precent of time swap character // "length" / 4 precent of time swap character
if (rg.chance (percentile / 2)) { if (rg.chance (percentile / 2)) {
size_t pos = rg.get (length / 8, 3 * length / 8); // choose random position in string size_t pos = static_cast <size_t> (rg.get (length / 8, 3 * length / 8)); // choose random position in string
cr::swap (line[pos], line[pos + 1]); cr::swap (line[pos], line[pos + 1]);
} }
} }
@ -190,7 +190,7 @@ void Bot::prepareChatMessage (StringRef message) {
// get bot's victim // get bot's victim
auto getMyVictim = [&] () -> String {; auto getMyVictim = [&] () -> String {;
return humanizedName (game.indexOfPlayer (m_lastVictim)); return humanizedName (game.indexOfPlayer (m_lastVictim));
}; };
// get the game name alias // get the game name alias

View file

@ -461,8 +461,8 @@ Vector Bot::getBodyOffsetError (float distance) {
} }
if (m_aimErrorTime < game.time ()) { if (m_aimErrorTime < game.time ()) {
const float error = distance / (cr::clamp (m_difficulty, 1, 3) * 1000.0f); const float error = distance / (cr::clamp (static_cast <float> (m_difficulty), 1.0f, 3.0f) * 1000.0f);
Vector &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins; auto &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins;
m_aimLastError = Vector (rg.get (mins.x * error, maxs.x * error), rg.get (mins.y * error, maxs.y * error), rg.get (mins.z * error, maxs.z * error)); m_aimLastError = Vector (rg.get (mins.x * error, maxs.x * error), rg.get (mins.y * error, maxs.y * error), rg.get (mins.z * error, maxs.z * error));
m_aimErrorTime = game.time () + rg.get (1.0f, 1.2f); m_aimErrorTime = game.time () + rg.get (1.0f, 1.2f);
@ -728,7 +728,7 @@ bool Bot::needToPauseFiring (float distance) {
const float yPunch = cr::deg2rad (pev->punchangle.y); const float yPunch = cr::deg2rad (pev->punchangle.y);
const float interval = getFrameInterval (); const float interval = getFrameInterval ();
const float tolerance = (100.0f - m_difficulty * 25.0f) / 99.0f; const float tolerance = (100.0f - static_cast <float> (m_difficulty) * 25.0f) / 99.0f;
// check if we need to compensate recoil // check if we need to compensate recoil
if (cr::tanf (cr::sqrtf (cr::abs (xPunch * xPunch) + cr::abs (yPunch * yPunch))) * distance > offset + 30.0f + tolerance) { if (cr::tanf (cr::sqrtf (cr::abs (xPunch * xPunch) + cr::abs (yPunch * yPunch))) * distance > offset + 30.0f + tolerance) {
@ -848,10 +848,8 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
} }
} }
else { else {
const auto &prop = conf.getWeaponProp (tab[index].id);
// if automatic weapon press attack // if automatic weapon press attack
if (tab[choosen].primaryFireHold && m_ammo[prop.ammo1] > tab[index].minPrimaryAmmo) { if (tab[choosen].primaryFireHold && getAmmo (tab[index].id) > tab[index].minPrimaryAmmo) {
pev->button |= IN_ATTACK; pev->button |= IN_ATTACK;
} }
@ -953,9 +951,7 @@ void Bot::fireWeapons () {
// is the bot carrying this weapon? // is the bot carrying this weapon?
if (weapons & cr::bit (id)) { if (weapons & cr::bit (id)) {
const auto &prop = conf.getWeaponProp (id); if (getAmmo (id) >= tab[selectIndex].minPrimaryAmmo) {
if (prop.ammo1 != -1 && prop.ammo1 < kMaxWeapons && m_ammo[prop.ammo1] >= tab[selectIndex].minPrimaryAmmo) {
// available ammo found, reload weapon // available ammo found, reload weapon
if (m_reloadState == Reload::None || m_reloadCheckTime > game.time ()) { if (m_reloadState == Reload::None || m_reloadCheckTime > game.time ()) {
@ -1431,10 +1427,9 @@ void Bot::selectBestWeapon () {
if (tab[selectIndex].id == m_currentWeapon && (getAmmoInClip () < 0 || getAmmoInClip () >= tab[selectIndex].minPrimaryAmmo)) { if (tab[selectIndex].id == m_currentWeapon && (getAmmoInClip () < 0 || getAmmoInClip () >= tab[selectIndex].minPrimaryAmmo)) {
ammoLeft = true; ammoLeft = true;
} }
const auto &prop = conf.getWeaponProp (id);
// is no ammo required for this weapon OR enough ammo available to fire // is no ammo required for this weapon OR enough ammo available to fire
if (prop.ammo1 < 0 || (prop.ammo1 < kMaxWeapons && m_ammo[prop.ammo1] >= tab[selectIndex].minPrimaryAmmo)) { if (getAmmo (id) >= tab[selectIndex].minPrimaryAmmo) {
ammoLeft = true; ammoLeft = true;
} }
@ -1485,14 +1480,13 @@ int Bot::bestWeaponCarried () {
return num; return num;
} }
void Bot::selectWeaponByName (const char *name) { void Bot::selectWeaponByName (StringRef name) {
issueCommand (name); issueCommand (name.chars ());
} }
void Bot::selectWeaponById (int num) { void Bot::selectWeaponByIndex (int index) {
auto tab = conf.getRawWeapons (); auto tab = conf.getRawWeapons ();
issueCommand (tab[index].name);
issueCommand (tab[num].name);
} }
void Bot::decideFollowUser () { void Bot::decideFollowUser () {
@ -1599,7 +1593,7 @@ void Bot::checkReload () {
m_reloadCheckTime = game.time () + 3.0f; m_reloadCheckTime = game.time () + 3.0f;
if (m_reloadState != Reload::None) { if (m_reloadState != Reload::None) {
int weaponIndex = 0; int wid = 0;
int weapons = pev->weapons; int weapons = pev->weapons;
if (m_reloadState == Reload::Primary) { if (m_reloadState == Reload::Primary) {
@ -1620,15 +1614,15 @@ void Bot::checkReload () {
for (int i = 1; i < kMaxWeapons; ++i) { for (int i = 1; i < kMaxWeapons; ++i) {
if (weapons & cr::bit (i)) { if (weapons & cr::bit (i)) {
weaponIndex = i; wid = i;
break; break;
} }
} }
const auto &prop = conf.getWeaponProp (weaponIndex); const auto &prop = conf.getWeaponProp (wid);
if (m_ammoInClip[weaponIndex] < conf.findWeaponById (weaponIndex).maxClip * 0.8f && prop.ammo1 != -1 && prop.ammo1 < kMaxWeapons && m_ammo[prop.ammo1] > 0) { if (isLowOnAmmo (prop.id, 0.75f) && getAmmo (prop.id) > 0) {
if (m_currentWeapon != weaponIndex) { if (m_currentWeapon != prop.id) {
selectWeaponByName (prop.classname.chars ()); selectWeaponByName (prop.classname);
} }
pev->button &= ~IN_ATTACK; pev->button &= ~IN_ATTACK;

View file

@ -95,6 +95,9 @@ void BotConfig::loadMainConfig (bool isFirstLoad) {
} }
file.close (); file.close ();
} }
else {
game.serverCommand (strings.format ("%s cvars save", product.cmdPri));
}
// android is abit hard to play, lower the difficulty by default // android is abit hard to play, lower the difficulty by default
if (plat.android && cv_difficulty.int_ () > 3) { if (plat.android && cv_difficulty.int_ () > 3) {

View file

@ -45,10 +45,10 @@ int BotControl::cmdKickBot () {
enum args { alias = 1, team }; enum args { alias = 1, team };
// if team is specified, kick from specified tram // if team is specified, kick from specified tram
if (strValue (alias).find ("_ct", 0) != String::InvalidIndex || intValue (team) == 2 || strValue (team) == "ct") { if (strValue (alias).endsWith ("_ct") || intValue (team) == 2 || strValue (team) == "ct") {
bots.kickFromTeam (Team::CT); bots.kickFromTeam (Team::CT);
} }
else if (strValue (alias).find ("_t", 0) != String::InvalidIndex || intValue (team) == 1 || strValue (team) == "t") { else if (strValue (alias).endsWith ("_t") || intValue (team) == 1 || strValue (team) == "t") {
bots.kickFromTeam (Team::Terrorist); bots.kickFromTeam (Team::Terrorist);
} }
else { else {
@ -73,10 +73,10 @@ int BotControl::cmdKillBots () {
enum args { alias = 1, team, max }; enum args { alias = 1, team, max };
// if team is specified, kick from specified tram // if team is specified, kick from specified tram
if (strValue (alias).find ("_ct", 0) != String::InvalidIndex || intValue (team) == 2 || strValue (team) == "ct") { if (strValue (alias).endsWith ("_ct") || intValue (team) == 2 || strValue (team) == "ct") {
bots.killAllBots (Team::CT); bots.killAllBots (Team::CT);
} }
else if (strValue (alias).find ("_t", 0) != String::InvalidIndex || intValue (team) == 1 || strValue (team) == "t") { else if (strValue (alias).endsWith ("_t") || intValue (team) == 1 || strValue (team) == "t") {
bots.killAllBots (Team::Terrorist); bots.killAllBots (Team::Terrorist);
} }
else { else {
@ -488,7 +488,7 @@ int BotControl::cmdNodeSave () {
else { else {
if (graph.checkNodes (false)) { if (graph.checkNodes (false)) {
graph.saveGraphData (); graph.saveGraphData ();
msg ("All nodes has been saved and written to disk."); msg ("All nodes has been saved and written to disk.\n*** Please don't forget to share your work by typing \"%s g upload\". Thank you! ***", product.cmdPri);
} }
else { else {
msg ("Could not save save nodes to disk. Graph check has failed."); msg ("Could not save save nodes to disk. Graph check has failed.");
@ -1943,7 +1943,7 @@ void BotControl::kickBotByMenu (int page) {
for (auto &menu : m_menus) { for (auto &menu : m_menus) {
if (menu.ident == id) { if (menu.ident == id) {
menu.slots = menuKeys & static_cast <uint32> (-1); menu.slots = static_cast <int> (static_cast <uint32> (menuKeys) & static_cast <uint32> (-1));
menu.text = menus; menu.text = menus;
break; break;

View file

@ -681,7 +681,7 @@ void Game::checkCvarsBounds () {
if (is (GameFlags::Xash3D)) { if (is (GameFlags::Xash3D)) {
static cvar_t *sv_forcesimulating = engfuncs.pfnCVarGetPointer ("sv_forcesimulating"); static cvar_t *sv_forcesimulating = engfuncs.pfnCVarGetPointer ("sv_forcesimulating");
if (sv_forcesimulating && sv_forcesimulating->value != 1.0f) { if (sv_forcesimulating && !cr::fequal (sv_forcesimulating->value, 1.0f)) {
game.print ("Force-enable Xash3D sv_forcesimulating cvar."); game.print ("Force-enable Xash3D sv_forcesimulating cvar.");
engfuncs.pfnCVarSetFloat ("sv_forcesimulating", 1.0f); engfuncs.pfnCVarSetFloat ("sv_forcesimulating", 1.0f);
} }
@ -798,7 +798,7 @@ bool Game::loadCSBinary () {
auto entity = m_gameLib.resolve <EntityFunction> ("weapon_famas"); auto entity = m_gameLib.resolve <EntityFunction> ("weapon_famas");
// detect xash engine // detect xash engine
if (engfuncs.pfnCVarGetPointer ("build") != nullptr) { if (engfuncs.pfnCVarGetPointer ("host_ver") != nullptr) {
m_gameFlags |= (GameFlags::Legacy | GameFlags::Xash3D); m_gameFlags |= (GameFlags::Legacy | GameFlags::Xash3D);
if (entity != nullptr) { if (entity != nullptr) {
@ -1035,7 +1035,7 @@ bool Game::isShootableBreakable (edict_t *ent) {
auto limit = cv_breakable_health_limit.float_ (); auto limit = cv_breakable_health_limit.float_ ();
if ((strcmp (ent->v.classname.chars (), "func_breakable") == 0 && ent->v.health < limit) || (strcmp (ent->v.classname.chars (), "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE) && ent->v.health < limit) || (strcmp (ent->v.classname.chars (), "func_wall") == 0 && ent->v.health < limit)) { if ((strcmp (ent->v.classname.chars (), "func_breakable") == 0 && ent->v.health < limit) || (strcmp (ent->v.classname.chars (), "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE) && ent->v.health < limit) || (strcmp (ent->v.classname.chars (), "func_wall") == 0 && ent->v.health < limit)) {
if (ent->v.takedamage != DAMAGE_NO && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY)) { if (ent->v.takedamage > 0.0f && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY)) {
return (ent->v.movetype == MOVETYPE_PUSH || ent->v.movetype == MOVETYPE_PUSHSTEP); return (ent->v.movetype == MOVETYPE_PUSH || ent->v.movetype == MOVETYPE_PUSHSTEP);
} }
} }
@ -1112,8 +1112,7 @@ void LightMeasure::animateLight () {
m_lightstyleValue[j] = 256; m_lightstyleValue[j] = 256;
continue; continue;
} }
int value = m_lightstyle[j].map[index % m_lightstyle[j].length] - 'a'; m_lightstyleValue[j] = static_cast <uint32> (m_lightstyle[j].map[index % m_lightstyle[j].length] - 'a') * 22u;
m_lightstyleValue[j] = value * 22;
} }
} }

View file

@ -7,7 +7,7 @@
#include <yapb.h> #include <yapb.h>
ConVar cv_graph_fixcamp ("yb_graph_fixcamp", "1", "Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format."); ConVar cv_graph_fixcamp ("yb_graph_fixcamp", "0", "Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format.");
ConVar cv_graph_url ("yb_graph_url", product.download.chars (), "Specifies the URL from bots will be able to download graph in case of missing local one. Set to empty, if no downloads needed.", false, 0.0f, 0.0f); ConVar cv_graph_url ("yb_graph_url", product.download.chars (), "Specifies the URL from bots will be able to download graph in case of missing local one. Set to empty, if no downloads needed.", false, 0.0f, 0.0f);
ConVar cv_graph_auto_save_count ("yb_graph_auto_save_count", "15", "Every N graph nodes placed on map, the graph will be saved automatically (without checks).", true, 0.0f, kMaxNodes); ConVar cv_graph_auto_save_count ("yb_graph_auto_save_count", "15", "Every N graph nodes placed on map, the graph will be saved automatically (without checks).", true, 0.0f, kMaxNodes);
ConVar cv_graph_draw_distance ("yb_graph_draw_distance", "400", "Maximum distance to draw graph nodes from editor viewport.", true, 64.0f, 3072.0f); ConVar cv_graph_draw_distance ("yb_graph_draw_distance", "400", "Maximum distance to draw graph nodes from editor viewport.", true, 64.0f, 3072.0f);
@ -33,7 +33,6 @@ void BotGraph::reset () {
for (int team = Team::Terrorist; team < kGameTeamNum; ++team) { for (int team = Team::Terrorist; team < kGameTeamNum; ++team) {
m_highestDamage[team] = 1; m_highestDamage[team] = 1;
} }
m_graphAuthor.clear (); m_graphAuthor.clear ();
m_graphModified.clear (); m_graphModified.clear ();
} }
@ -59,7 +58,7 @@ int BotGraph::clearConnections (int index) {
struct Connection { struct Connection {
int index {}; int index {};
int number {}; int number {};
int distance {}; float distance {};
float angles {}; float angles {};
public: public:
@ -71,7 +70,7 @@ int BotGraph::clearConnections (int index) {
void reset () { void reset () {
index = kInvalidNodeIndex; index = kInvalidNodeIndex;
number = kInvalidNodeIndex; number = kInvalidNodeIndex;
distance = kInfiniteDistanceLong; distance = kInfiniteDistance;
angles = 0.0f; angles = 0.0f;
} }
}; };
@ -86,14 +85,14 @@ int BotGraph::clearConnections (int index) {
cur.number = i; cur.number = i;
cur.index = link.index; cur.index = link.index;
cur.distance = link.distance; cur.distance = static_cast <float> (link.distance);
if (cur.index == kInvalidNodeIndex) { if (cur.index == kInvalidNodeIndex) {
cur.distance = kInfiniteDistanceLong; cur.distance = kInfiniteDistance;
} }
if (cur.distance < top.distance) { if (cur.distance < top.distance) {
top.distance = link.distance; top.distance = static_cast <float> (link.distance);
top.number = i; top.number = i;
top.index = cur.index; top.index = cur.index;
} }
@ -173,7 +172,7 @@ int BotGraph::clearConnections (int index) {
return false; return false;
} }
if ((cur.distance + prev2.distance) * 1.1f / 2.0f < static_cast <float> (prev.distance)) { if ((cur.distance + prev2.distance) * 1.1f / 2.0f < prev.distance) {
if (path.links[prev.number].index == prev.index) { if (path.links[prev.number].index == prev.index) {
ctrl.msg ("Removing a useless (P.0.1) connection from index = %d to %d.", index, prev.index); ctrl.msg ("Removing a useless (P.0.1) connection from index = %d to %d.", index, prev.index);
@ -215,7 +214,7 @@ int BotGraph::clearConnections (int index) {
// check pass 1 // check pass 1
if (exists (top.index) && exists (sorted[0].index) && exists (sorted[1].index)) { if (exists (top.index) && exists (sorted[0].index) && exists (sorted[1].index)) {
if ((sorted[1].angles - top.angles < 80.0f || 360.0f - (sorted[1].angles - top.angles) < 80.0f) && (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder)) && !(path.links[sorted[0].number].flags & PathFlag::Jump)) { if ((sorted[1].angles - top.angles < 80.0f || 360.0f - (sorted[1].angles - top.angles) < 80.0f) && (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder)) && !(path.links[sorted[0].number].flags & PathFlag::Jump)) {
if ((sorted[1].distance + top.distance) * 1.1f / 2.0f < static_cast <float> (sorted[0].distance)) { if ((sorted[1].distance + top.distance) * 1.1f / 2.0f < sorted[0].distance) {
if (path.links[sorted[0].number].index == sorted[0].index) { if (path.links[sorted[0].number].index == sorted[0].index) {
ctrl.msg ("Removing a useless (P.1.1) connection from index = %d to %d.", index, sorted[0].index); ctrl.msg ("Removing a useless (P.1.1) connection from index = %d to %d.", index, sorted[0].index);
@ -257,7 +256,7 @@ int BotGraph::clearConnections (int index) {
} }
if (cur.angles - prev.angles < 40.0f) { if (cur.angles - prev.angles < 40.0f) {
if (prev.distance < static_cast <float> (cur.distance * 1.1f)) { if (prev.distance < cur.distance * 1.1f) {
// leave alone ladder connections and don't remove jump connections.. // leave alone ladder connections and don't remove jump connections..
if (((path.flags & NodeFlag::Ladder) && (m_paths[cur.index].flags & NodeFlag::Ladder)) || (path.links[cur.number].flags & PathFlag::Jump)) { if (((path.flags & NodeFlag::Ladder) && (m_paths[cur.index].flags & NodeFlag::Ladder)) || (path.links[cur.number].flags & PathFlag::Jump)) {
@ -290,7 +289,7 @@ int BotGraph::clearConnections (int index) {
ctrl.msg ("Failed to remove a useless (P.2) connection from index = %d to %d.", index, cur.index); ctrl.msg ("Failed to remove a useless (P.2) connection from index = %d to %d.", index, cur.index);
} }
} }
else if (cur.distance < static_cast <float> (prev.distance * 1.1f)) { else if (cur.distance < prev.distance * 1.1f) {
// leave alone ladder connections and don't remove jump connections.. // leave alone ladder connections and don't remove jump connections..
if (((path.flags & NodeFlag::Ladder) && (m_paths[prev.index].flags & NodeFlag::Ladder)) || (path.links[prev.number].flags & PathFlag::Jump)) { if (((path.flags & NodeFlag::Ladder) && (m_paths[prev.index].flags & NodeFlag::Ladder)) || (path.links[prev.number].flags & PathFlag::Jump)) {
return false; return false;
@ -338,7 +337,7 @@ int BotGraph::clearConnections (int index) {
// check pass 3 // check pass 3
if (exists (top.index) && exists (sorted[0].index)) { if (exists (top.index) && exists (sorted[0].index)) {
if ((top.angles - sorted[0].angles < 40.0f || (360.0f - top.angles - sorted[0].angles) < 40.0f) && (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder)) && !(path.links[sorted[0].number].flags & PathFlag::Jump)) { if ((top.angles - sorted[0].angles < 40.0f || (360.0f - top.angles - sorted[0].angles) < 40.0f) && (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder)) && !(path.links[sorted[0].number].flags & PathFlag::Jump)) {
if (top.distance * 1.1f < static_cast <float> (sorted[0].distance)) { if (top.distance * 1.1f < sorted[0].distance) {
if (path.links[sorted[0].number].index == sorted[0].index) { if (path.links[sorted[0].number].index == sorted[0].index) {
ctrl.msg ("Removing a useless (P.3.1) connection from index = %d to %d.", index, sorted[0].index); ctrl.msg ("Removing a useless (P.3.1) connection from index = %d to %d.", index, sorted[0].index);
@ -364,7 +363,7 @@ int BotGraph::clearConnections (int index) {
ctrl.msg ("Failed to remove a useless (P.3) connection from index = %d to %d.", sorted[0].index, index); ctrl.msg ("Failed to remove a useless (P.3) connection from index = %d to %d.", sorted[0].index, index);
} }
} }
else if (sorted[0].distance * 1.1f < static_cast <float> (top.distance) && !(path.links[top.number].flags & PathFlag::Jump)) { else if (sorted[0].distance * 1.1f < top.distance && !(path.links[top.number].flags & PathFlag::Jump)) {
if (path.links[top.number].index == top.index) { if (path.links[top.number].index == top.index) {
ctrl.msg ("Removing a useless (P.3.3) connection from index = %d to %d.", index, sorted[0].index); ctrl.msg ("Removing a useless (P.3.3) connection from index = %d to %d.", index, sorted[0].index);
@ -903,7 +902,7 @@ void BotGraph::setRadius (int index, float radius) {
int node = exists (index) ? index : getEditorNearest (); int node = exists (index) ? index : getEditorNearest ();
if (node != kInvalidNodeIndex) { if (node != kInvalidNodeIndex) {
m_paths[node].radius = static_cast <float> (radius); m_paths[node].radius = radius;
// play "done" sound... // play "done" sound...
game.playSound (m_editor, "common/wpn_hudon.wav"); game.playSound (m_editor, "common/wpn_hudon.wav");
@ -1235,7 +1234,7 @@ void BotGraph::calculatePathRadius (int index) {
break; break;
} }
direction.y = cr::normalizeAngles (direction.y + static_cast <float> (circleRadius)); direction.y = cr::wrapAngle (direction.y + static_cast <float> (circleRadius));
} }
if (wayBlocked) { if (wayBlocked) {
@ -1362,7 +1361,7 @@ bool BotGraph::loadPathMatrix () {
if (distance < (matrix + (i * count) + j)->dist) { if (distance < (matrix + (i * count) + j)->dist) {
(matrix + (i * count) + j)->dist = static_cast <int16> (distance); (matrix + (i * count) + j)->dist = static_cast <int16> (distance);
(matrix + (i * count) + j)->index = static_cast <int16> ((matrix + (i * count) + k)->index); (matrix + (i * count) + j)->index = (matrix + (i * count) + k)->index;
} }
} }
} }
@ -1625,11 +1624,11 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
logger.error ("Unable to open %s file for writing (filename: '%s').", name, filename); logger.error ("Unable to open %s file for writing (filename: '%s').", name, filename);
return false; return false;
} }
int32 rawLength = data.template length <int32> () * sizeof (U); auto rawLength = data.length () * sizeof (U);
SmallArray <uint8> compressed (rawLength + sizeof (uint8) * ULZ::Excess); SmallArray <uint8> compressed (rawLength + sizeof (uint8) * ULZ::Excess);
// try to compress // try to compress
auto compressedLength = ulz.compress (reinterpret_cast <uint8 *> (data.data ()), rawLength, reinterpret_cast <uint8 *> (compressed.data ())); auto compressedLength = static_cast <size_t> (ulz.compress (reinterpret_cast <uint8 *> (data.data ()), static_cast <int32> (rawLength), reinterpret_cast <uint8 *> (compressed.data ())));
if (compressedLength > 0) { if (compressedLength > 0) {
StorageHeader hdr {}; StorageHeader hdr {};
@ -1638,8 +1637,8 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
hdr.version = version; hdr.version = version;
hdr.options = options; hdr.options = options;
hdr.length = length (); hdr.length = length ();
hdr.compressed = compressedLength; hdr.compressed = static_cast <int32> (compressedLength);
hdr.uncompressed = rawLength; hdr.uncompressed = static_cast <int32> (rawLength);
file.write (&hdr, sizeof (StorageHeader)); file.write (&hdr, sizeof (StorageHeader));
file.write (compressed.data (), sizeof (uint8), compressedLength); file.write (compressed.data (), sizeof (uint8), compressedLength);
@ -1788,15 +1787,18 @@ template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name,
if ((hdr.options & options) != options) { if ((hdr.options & options) != options) {
return raiseLoadingError (isGraph, file, "Incorrect storage format for %s (filename: '%s').", name, filename); return raiseLoadingError (isGraph, file, "Incorrect storage format for %s (filename: '%s').", name, filename);
} }
SmallArray <uint8> compressed (hdr.compressed + sizeof (uint8) * ULZ::Excess); auto compressedSize = static_cast <size_t> (hdr.compressed);
auto numberNodes = static_cast <size_t> (hdr.length);
SmallArray <uint8> compressed (compressedSize + sizeof (uint8) * ULZ::Excess);
// graph is not resized upon load // graph is not resized upon load
if (isGraph) { if (isGraph) {
resizeData (hdr.length); resizeData (numberNodes);
} }
// read compressed data // read compressed data
if (file.read (compressed.data (), sizeof (uint8), hdr.compressed) == static_cast <size_t> (hdr.compressed)) { if (file.read (compressed.data (), sizeof (uint8), compressedSize) == compressedSize) {
// try to uncompress // try to uncompress
if (ulz.uncompress (compressed.data (), hdr.compressed, reinterpret_cast <uint8 *> (data.data ()), hdr.uncompressed) == ULZ::UncompressFailure) { if (ulz.uncompress (compressed.data (), hdr.compressed, reinterpret_cast <uint8 *> (data.data ()), hdr.uncompressed) == ULZ::UncompressFailure) {
@ -1837,6 +1839,7 @@ template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name,
else { else {
return raiseLoadingError (isGraph, file, "Unable to read ULZ data for %s (filename: '%s').", name, filename); return raiseLoadingError (isGraph, file, "Unable to read ULZ data for %s (filename: '%s').", name, filename);
} }
return false;
} }
bool BotGraph::loadGraphData () { bool BotGraph::loadGraphData () {
@ -2031,7 +2034,7 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) {
game.testLine (sourceNew, destinationNew, TraceIgnore::Monsters, m_editor, &tr); game.testLine (sourceNew, destinationNew, TraceIgnore::Monsters, m_editor, &tr);
// check if we didn't hit anything, if not then it's in mid-air // check if we didn't hit anything, if not then it's in mid-air
if (tr.flFraction >= 1.0) { if (tr.flFraction >= 1.0f) {
return false; // can't reach this one return false; // can't reach this one
} }
} }
@ -2143,10 +2146,10 @@ void BotGraph::rebuildVisibility () {
res &= 2; res &= 2;
} }
} }
shift = (path.number % 4) << 1; shift = static_cast <uint8> ((path.number % 4) << 1);
m_vistable[vis.number * m_paths.length () + path.number] &= ~(3 << shift); m_vistable[vis.number * length () + path.number] &= static_cast <uint8> (~(3 << shift));
m_vistable[vis.number * m_paths.length () + path.number] |= res << shift; m_vistable[vis.number * length () + path.number] |= res << shift;
if (!(res & 2)) { if (!(res & 2)) {
++crouchCount; ++crouchCount;
@ -2168,7 +2171,7 @@ bool BotGraph::isVisible (int srcIndex, int destIndex) {
return false; return false;
} }
uint8 res = m_vistable[srcIndex * m_paths.length () + destIndex]; uint8 res = m_vistable[srcIndex * length () + destIndex];
res >>= (destIndex % 4) << 1; res >>= (destIndex % 4) << 1;
return !((res & 3) == 3); return !((res & 3) == 3);
@ -2179,7 +2182,7 @@ bool BotGraph::isDuckVisible (int srcIndex, int destIndex) {
return false; return false;
} }
uint8 res = m_vistable[srcIndex * m_paths.length () + destIndex]; uint8 res = m_vistable[srcIndex * length () + destIndex];
res >>= (destIndex % 4) << 1; res >>= (destIndex % 4) << 1;
return !((res & 2) == 2); return !((res & 2) == 2);
@ -2190,7 +2193,7 @@ bool BotGraph::isStandVisible (int srcIndex, int destIndex) {
return false; return false;
} }
uint8 res = m_vistable[srcIndex * m_paths.length () + destIndex]; uint8 res = m_vistable[srcIndex * length () + destIndex];
res >>= (destIndex % 4) << 1; res >>= (destIndex % 4) << 1;
return !((res & 1) == 1); return !((res & 1) == 1);
@ -2498,18 +2501,29 @@ void BotGraph::frame () {
auto getNodeData = [&] (StringRef type, int node) -> String { auto getNodeData = [&] (StringRef type, int node) -> String {
String message, flags; String message, flags;
const auto &path = m_paths[node]; const auto &p = m_paths[node];
bool jumpPoint = false; bool jumpPoint = false;
// iterate through connections and find, if it's a jump path // iterate through connections and find, if it's a jump path
for (const auto &link : path.links) { for (const auto &link : p.links) {
// check if we got a valid connection // check if we got a valid connection
if (link.index != kInvalidNodeIndex && (link.flags & PathFlag::Jump)) { if (link.index != kInvalidNodeIndex && (link.flags & PathFlag::Jump)) {
jumpPoint = true; jumpPoint = true;
} }
} }
flags.assignf ("%s%s%s%s%s%s%s%s%s%s%s%s", (path.flags & NodeFlag::Lift) ? " LIFT" : "", (path.flags & NodeFlag::Crouch) ? " CROUCH" : "", (path.flags & NodeFlag::Camp) ? " CAMP" : "", (path.flags & NodeFlag::TerroristOnly) ? " TERRORIST" : "", (path.flags & NodeFlag::CTOnly) ? " CT" : "", (path.flags & NodeFlag::Sniper) ? " SNIPER" : "", (path.flags & NodeFlag::Goal) ? " GOAL" : "", (path.flags & NodeFlag::Ladder) ? " LADDER" : "", (path.flags & NodeFlag::Rescue) ? " RESCUE" : "", (path.flags & NodeFlag::DoubleJump) ? " JUMPHELP" : "", (path.flags & NodeFlag::NoHostage) ? " NOHOSTAGE" : "", jumpPoint ? " JUMP" : ""); flags.assignf ("%s%s%s%s%s%s%s%s%s%s%s%s",
(p.flags & NodeFlag::Lift) ? " LIFT" : "",
(p.flags & NodeFlag::Crouch) ? " CROUCH" : "",
(p.flags & NodeFlag::Camp) ? " CAMP" : "",
(p.flags & NodeFlag::TerroristOnly) ? " TERRORIST" : "",
(p.flags & NodeFlag::CTOnly) ? " CT" : "",
(p.flags & NodeFlag::Sniper) ? " SNIPER" : "",
(p.flags & NodeFlag::Goal) ? " GOAL" : "",
(p.flags & NodeFlag::Ladder) ? " LADDER" : "",
(p.flags & NodeFlag::Rescue) ? " RESCUE" : "",
(p.flags & NodeFlag::DoubleJump) ? " JUMPHELP" : "",
(p.flags & NodeFlag::NoHostage) ? " NOHOSTAGE" : "", jumpPoint ? " JUMP" : "");
if (flags.empty ()) { if (flags.empty ()) {
flags.assign ("(none)"); flags.assign ("(none)");
@ -2519,7 +2533,7 @@ void BotGraph::frame () {
message.assignf (" %s node:\n" message.assignf (" %s node:\n"
" Node %d of %d, Radius: %.1f, Light: %.1f\n" " Node %d of %d, Radius: %.1f, Light: %.1f\n"
" Flags: %s\n" " Flags: %s\n"
" Origin: (%.1f, %.1f, %.1f)\n", type, node, m_paths.length () - 1, path.radius, path.light, flags, path.origin.x, path.origin.y, path.origin.z); " Origin: (%.1f, %.1f, %.1f)\n", type, node, m_paths.length () - 1, p.radius, p.light, flags, p.origin.x, p.origin.y, p.origin.z);
return message; return message;
}; };
@ -2669,11 +2683,16 @@ bool BotGraph::checkNodes (bool teleportPlayer) {
} }
// perform DFS instead of floyd-warshall, this shit speedup this process in a bit // perform DFS instead of floyd-warshall, this shit speedup this process in a bit
auto length = cr::min (static_cast <size_t> (kMaxNodes), m_paths.length ());
// ensure valid capacity
assert (length > 8 && length < static_cast <size_t> (kMaxNodes));
PathWalk walk; PathWalk walk;
walk.init (m_paths.length ()); walk.init (length);
Array <bool> visited; Array <bool> visited;
visited.resize (m_paths.length ()); visited.resize (length);
// first check incoming connectivity, initialize the "visited" table // first check incoming connectivity, initialize the "visited" table
for (auto &visit : visited) { for (auto &visit : visited) {
@ -2710,10 +2729,10 @@ bool BotGraph::checkNodes (bool teleportPlayer) {
// then check outgoing connectivity // then check outgoing connectivity
Array <IntArray> outgoingPaths; // store incoming paths for speedup Array <IntArray> outgoingPaths; // store incoming paths for speedup
outgoingPaths.resize (m_paths.length ()); outgoingPaths.resize (length);
for (const auto &path : m_paths) { for (const auto &path : m_paths) {
outgoingPaths[path.number].resize (m_paths.length () + 1); outgoingPaths[path.number].resize (length + 1);
for (const auto &link : path.links) { for (const auto &link : path.links) {
if (exists (link.index)) { if (exists (link.index)) {
@ -3123,8 +3142,8 @@ void BotGraph::convertFromPOD (Path &path, const PODPath &pod) {
path.links[i].flags = pod.conflags[i]; path.links[i].flags = pod.conflags[i];
path.links[i].velocity = pod.velocity[i]; path.links[i].velocity = pod.velocity[i];
} }
path.vis.stand = pod.vis.stand; path.vis.stand = 0;
path.vis.crouch = pod.vis.crouch; path.vis.crouch = 0;
} }
void BotGraph::convertToPOD (const Path &path, PODPath &pod) { void BotGraph::convertToPOD (const Path &path, PODPath &pod) {

View file

@ -1010,12 +1010,12 @@ CR_EXPORT int Server_GetPhysicsInterface (int version, server_physics_api_t *phy
return HLTrue; return HLTrue;
} }
DLSYM_RETURN EntityLinkage::lookup (SharedLibrary::Handle module, const char *function) { SharedLibrary::Func EntityLinkage::lookup (SharedLibrary::Handle module, const char *function) {
static const auto &gamedll = game.lib ().handle (); static const auto &gamedll = game.lib ().handle ();
static const auto &self = m_self.handle (); static const auto &self = m_self.handle ();
const auto resolve = [&] (SharedLibrary::Handle handle) { const auto resolve = [&] (SharedLibrary::Handle handle) {
return reinterpret_cast <DLSYM_RETURN> (m_dlsym (static_cast <DLSYM_HANDLE> (handle), function)); return m_dlsym (handle, function);
}; };
if (ents.needsBypass () && !strcmp (function, "CreateInterface")) { if (ents.needsBypass () && !strcmp (function, "CreateInterface")) {
@ -1028,10 +1028,16 @@ DLSYM_RETURN EntityLinkage::lookup (SharedLibrary::Handle module, const char *fu
} }
// if requested module is yapb module, put in cache the looked up symbol // if requested module is yapb module, put in cache the looked up symbol
if (self != module || (plat.win && (static_cast <uint16> (reinterpret_cast <unsigned long> (function) >> 16) & 0xffff) == 0)) { if (self != module) {
return resolve (module); return resolve (module);
} }
#if defined (CR_WINDOWS)
if (HIWORD (function) == 0) {
return resolve (module);
}
#endif
if (m_exports.has (function)) { if (m_exports.has (function)) {
return m_exports[function]; return m_exports[function];
} }
@ -1057,7 +1063,7 @@ void EntityLinkage::callPlayerFunction (edict_t *ent) {
playerFunction = game.lib ().resolve <EntityFunction> ("player"); playerFunction = game.lib ().resolve <EntityFunction> ("player");
} }
else { else {
playerFunction = reinterpret_cast <EntityFunction> (lookup (game.lib ().handle (), "player")); playerFunction = reinterpret_cast <EntityFunction> (reinterpret_cast <void *> (lookup (game.lib ().handle (), "player")));
} }
if (!playerFunction) { if (!playerFunction) {

View file

@ -132,7 +132,7 @@ void BotManager::touchKillerEntity (Bot *bot) {
KeyValueData kv {}; KeyValueData kv {};
kv.szClassName = const_cast <char *> (prop.classname.chars ()); kv.szClassName = const_cast <char *> (prop.classname.chars ());
kv.szKeyName = "damagetype"; kv.szKeyName = "damagetype";
kv.szValue = const_cast <char *> (strings.format ("%d", cr::bit (4))); kv.szValue = strings.format ("%d", cr::bit (4));
kv.fHandled = HLFalse; kv.fHandled = HLFalse;
MDLL_KeyValue (m_killerEntity, &kv); MDLL_KeyValue (m_killerEntity, &kv);
@ -782,7 +782,6 @@ void BotManager::listBots () {
ctrl.msg ("%-3.5s\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.4s\t%-3.4s\t%-3.5s\t%-3.8s", "index", "name", "personality", "team", "difficulty", "frags", "alive", "timeleft"); ctrl.msg ("%-3.5s\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.4s\t%-3.4s\t%-3.5s\t%-3.8s", "index", "name", "personality", "team", "difficulty", "frags", "alive", "timeleft");
for (const auto &bot : bots) { for (const auto &bot : bots) {
;
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%-3.0f secs", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no", bot->m_stayTime - game.time ()); ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%-3.0f secs", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no", bot->m_stayTime - game.time ());
} }
ctrl.msg ("%d bots", m_bots.length ()); ctrl.msg ("%d bots", m_bots.length ());
@ -819,7 +818,7 @@ float BotManager::getAverageTeamKPD (bool calcForBots) {
} }
if (calc.second > 0) { if (calc.second > 0) {
return calc.first / calc.second; return calc.first / static_cast <float> (calc.second);
} }
return 0.0f; return 0.0f;
} }
@ -930,10 +929,10 @@ void BotManager::balanceBotDifficulties () {
float score = bot->m_kpdRatio; float score = bot->m_kpdRatio;
// if kd ratio is going to go to low, we need to try to set higher difficulty // if kd ratio is going to go to low, we need to try to set higher difficulty
if (score < 0.8 || (score <= 1.2 && ratioBots < ratioPlayer)) { if (score < 0.8f || (score <= 1.2f && ratioBots < ratioPlayer)) {
updateDifficulty (bot.get (), +1); updateDifficulty (bot.get (), +1);
} }
else if (score > 4.0f || (score >= 2.5 && ratioBots > ratioPlayer)) { else if (score > 4.0f || (score >= 2.5f && ratioBots > ratioPlayer)) {
updateDifficulty (bot.get (), -1); updateDifficulty (bot.get (), -1);
} }
} }
@ -949,9 +948,6 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) {
// this function does core operation of creating bot, it's called by addbot (), // this function does core operation of creating bot, it's called by addbot (),
// when bot setup completed, (this is a bot class constructor) // when bot setup completed, (this is a bot class constructor)
// we're not initializing all the variables in bot class, so do an ugly thing... memset this
plat.bzero (this, sizeof (*this));
int clientIndex = game.indexOfEntity (bot); int clientIndex = game.indexOfEntity (bot);
pev = &bot->v; pev = &bot->v;
@ -1458,7 +1454,7 @@ void Bot::resetPathSearchType () {
switch (m_personality) { switch (m_personality) {
default: default:
case Personality::Normal: case Personality::Normal:
m_pathType = morale ? FindPath::Optimal : FindPath::Safe; m_pathType = morale ? FindPath::Optimal : FindPath::Fast;
break; break;
case Personality::Rusher: case Personality::Rusher:

View file

@ -335,7 +335,7 @@ void MessageDispatcher::netMsgScoreInfo () {
// if we're have bot, set the kd ratio // if we're have bot, set the kd ratio
if (bot != nullptr) { if (bot != nullptr) {
bot->m_kpdRatio = bot->pev->frags / cr::max <long> (m_args[deaths].long_, 1); bot->m_kpdRatio = bot->pev->frags / cr::max (static_cast <float> (m_args[deaths].long_), 1.0f);
} }
} }

View file

@ -23,7 +23,7 @@ private:
public: public:
// get the bot version string // get the bot version string
virtual const char *getBotVersion () override { virtual const char *getBotVersion () override {
return MODULE_BOT_VERSION "." MODULE_BUILD_COUNT; return MODULE_VERSION "." MODULE_COMMIT_COUNT;
} }
// checks if bots are currently in game // checks if bots are currently in game

View file

@ -82,6 +82,7 @@ int Bot::findBestGoal () {
tactic = 4; tactic = 4;
return findGoalPost (tactic, defensiveNodes, offensiveNodes); return findGoalPost (tactic, defensiveNodes, offensiveNodes);
} }
auto difficulty = static_cast <float> (m_difficulty);
offensive = m_agressionLevel * 100.0f; offensive = m_agressionLevel * 100.0f;
defensive = m_fearLevel * 100.0f; defensive = m_fearLevel * 100.0f;
@ -94,8 +95,8 @@ int Bot::findBestGoal () {
else if (m_team == Team::CT) { else if (m_team == Team::CT) {
// on hostage maps force more bots to save hostages // on hostage maps force more bots to save hostages
if (game.mapIs (MapFlags::HostageRescue)) { if (game.mapIs (MapFlags::HostageRescue)) {
defensive -= 25.0f - m_difficulty * 0.5f; defensive -= 25.0f - difficulty * 0.5f;
offensive += 25.0f + m_difficulty * 5.0f; offensive += 25.0f + difficulty * 5.0f;
} }
else { else {
defensive -= 25.0f; defensive -= 25.0f;
@ -112,8 +113,8 @@ int Bot::findBestGoal () {
} }
return m_chosenGoalIndex = findBombNode (); return m_chosenGoalIndex = findBombNode ();
} }
defensive += 25.0f + m_difficulty * 4.0f; defensive += 25.0f + difficulty * 4.0f;
offensive -= 25.0f - m_difficulty * 0.5f; offensive -= 25.0f - difficulty * 0.5f;
if (m_personality != Personality::Rusher) { if (m_personality != Personality::Rusher) {
defensive += 10.0f; defensive += 10.0f;
@ -121,7 +122,7 @@ int Bot::findBestGoal () {
} }
else if (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && bots.getRoundStartTime () + 10.0f < game.time ()) { else if (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && bots.getRoundStartTime () + 10.0f < game.time ()) {
// send some terrorists to guard planted bomb // send some terrorists to guard planted bomb
if (!m_defendedBomb && bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && getBombTimeleft () >= 15.0) { if (!m_defendedBomb && bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && getBombTimeleft () >= 15.0f) {
return pushToHistroy (m_chosenGoalIndex = graph.getNearest (graph.getBombOrigin ())); return pushToHistroy (m_chosenGoalIndex = graph.getNearest (graph.getBombOrigin ()));
} }
} }
@ -456,21 +457,18 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// bot is stuc, but not yet decided what to do? // bot is stuc, but not yet decided what to do?
if (m_collisionState == CollisionState::Undecided) { if (m_collisionState == CollisionState::Undecided) {
int bits = 0; uint32 bits = 0;
if (isOnLadder ()) { if (isOnLadder ()) {
bits |= CollisionProbe::Strafe; bits |= CollisionProbe::Strafe;
} }
else if (isInWater ()) {
bits |= (CollisionProbe::Jump | CollisionProbe::Strafe);
}
else { else {
bits |= (CollisionProbe::Strafe | CollisionProbe::Jump); bits |= (CollisionProbe::Strafe | CollisionProbe::Jump);
} }
// collision check allowed if not flying through the air // collision check allowed if not flying through the air
if (isOnFloor () || isOnLadder () || isInWater ()) { if (isOnFloor () || isOnLadder () || isInWater ()) {
int state[kMaxCollideMoves * 2 + 1] {}; uint32 state[kMaxCollideMoves * 2 + 1] {};
int i = 0; int i = 0;
Vector src {}, dst {}; Vector src {}, dst {};
@ -689,7 +687,7 @@ bool Bot::updateNavigation () {
// if graph node radius non zero vary origin a bit depending on the body angles // if graph node radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) { if (m_path->radius > 0.0f) {
m_pathOrigin += Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (0.0f, m_path->radius); m_pathOrigin += Vector (pev->angles.x, cr::wrapAngle (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (0.0f, m_path->radius);
} }
m_navTimeset = game.time (); m_navTimeset = game.time ();
} }
@ -726,7 +724,7 @@ bool Bot::updateNavigation () {
} }
} }
if (!(graph[m_previousNodes[0]].flags & NodeFlag::Ladder)) { if (graph.exists (m_previousNodes[0]) && (graph[m_previousNodes[0]].flags & NodeFlag::Ladder)) {
if (cr::abs (m_pathOrigin.z - pev->origin.z) > 5.0f) { if (cr::abs (m_pathOrigin.z - pev->origin.z) > 5.0f) {
m_pathOrigin.z += pev->origin.z - m_pathOrigin.z; m_pathOrigin.z += pev->origin.z - m_pathOrigin.z;
} }
@ -1325,7 +1323,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
// this function finds a path from srcIndex to destIndex // this function finds a path from srcIndex to destIndex
auto dangerFactor = [&] () -> float { auto dangerFactor = [&] () -> float {
return rg.get (cv_path_danger_factor_min.float_ (), cv_path_danger_factor_max.float_ ()) * 2.0f / cr::clamp (m_difficulty, 1, 3); return rg.get (cv_path_danger_factor_min.float_ (), cv_path_danger_factor_max.float_ ()) * 2.0f / cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f);
}; };
// least kills and number of nodes to goal for a team // least kills and number of nodes to goal for a team
@ -1403,11 +1401,13 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
for (const auto &link : parent.links) { for (const auto &link : parent.links) {
if (link.index == currentIndex) { if (link.index == currentIndex) {
const auto distance = static_cast <float> (link.distance);
// we don't like ladder or crouch point // we don't like ladder or crouch point
if (current.flags & (NodeFlag::Crouch | NodeFlag::Ladder)) { if (current.flags & (NodeFlag::Crouch | NodeFlag::Ladder)) {
return link.distance * 1.5f; return distance * 1.5f;
} }
return static_cast <float> (link.distance); return distance;
} }
} }
return 65355.0f; return 65355.0f;
@ -1608,7 +1608,7 @@ void Bot::clearSearchNodes () {
} }
void Bot::clearRoute () { void Bot::clearRoute () {
m_routes.resize (graph.length ()); m_routes.resize (static_cast <size_t> (graph.length ()));
for (int i = 0; i < graph.length (); ++i) { for (int i = 0; i < graph.length (); ++i) {
auto route = &m_routes[i]; auto route = &m_routes[i];
@ -1633,7 +1633,7 @@ int Bot::findAimingNode (const Vector &to) {
if (destIndex == kInvalidNodeIndex) { if (destIndex == kInvalidNodeIndex) {
return kInvalidNodeIndex; return kInvalidNodeIndex;
} }
const float kMaxDistance = ((m_states & Sense::HearingEnemy) || (m_states & Sense::HearingEnemy) || m_seeEnemyTime + 3.0f > game.time ()) ? 0.0f : 512.0f; const float kMaxDistance = ((m_states & Sense::HearingEnemy) || (m_states & Sense::SuspectEnemy) || m_seeEnemyTime + 3.0f > game.time ()) ? 0.0f : 512.0f;
while (destIndex != m_currentNodeIndex) { while (destIndex != m_currentNodeIndex) {
destIndex = (graph.m_matrix.data () + (destIndex * graph.length ()) + m_currentNodeIndex)->index; destIndex = (graph.m_matrix.data () + (destIndex * graph.length ()) + m_currentNodeIndex)->index;
@ -2411,7 +2411,7 @@ bool Bot::advanceMovement () {
// if wayzone radius non zero vary origin a bit depending on the body angles // if wayzone radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) { if (m_path->radius > 0.0f) {
m_pathOrigin += Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (0.0f, m_path->radius); m_pathOrigin += Vector (pev->angles.x, cr::wrapAngle (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (0.0f, m_path->radius);
} }
if (isOnLadder ()) { if (isOnLadder ()) {
@ -2432,17 +2432,17 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
// use some TraceLines to determine if anything is blocking the current path of the bot. // use some TraceLines to determine if anything is blocking the current path of the bot.
// first do a trace from the bot's eyes forward... // first do a trace from the bot's eyes forward...
Vector src = getEyesPos (); auto src = getEyesPos ();
Vector forward = src + normal * 24.0f; auto forward = src + normal * 24.0f;
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
bool traceResult = false; bool traceResult = false;
auto checkDoor = [&traceResult] (TraceResult *tr) { auto checkDoor = [&traceResult] (TraceResult *result) {
if (!game.mapIs (MapFlags::HasDoors)) { if (!game.mapIs (MapFlags::HasDoors)) {
return false; return false;
} }
return !traceResult && tr->flFraction < 1.0f && strncmp ("func_door", tr->pHit->v.classname.chars (), 9) != 0; return !traceResult && result->flFraction < 1.0f && strncmp ("func_door", result->pHit->v.classname.chars (), 9) != 0;
}; };
// trace from the bot's eyes straight forward... // trace from the bot's eyes straight forward...
@ -2596,11 +2596,11 @@ bool Bot::canJumpUp (const Vector &normal) {
if (!isOnFloor () && (isOnLadder () || !isInWater ())) { if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
return false; return false;
} }
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math... auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
// check for normal jump height first... // check for normal jump height first...
Vector src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f); auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
Vector dest = src + normal * 32.0f; auto dest = src + normal * 32.0f;
// trace a line forward at maximum jump height... // trace a line forward at maximum jump height...
game.testLine (src, dest, TraceIgnore::Monsters, ent (), &tr); game.testLine (src, dest, TraceIgnore::Monsters, ent (), &tr);
@ -2762,7 +2762,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
} }
// convert current view angle to vectors for TraceLine math... // convert current view angle to vectors for TraceLine math...
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
// now check same height to one side of the bot... // now check same height to one side of the bot...
src = baseHeight + right * 16.0f; src = baseHeight + right * 16.0f;
@ -2900,14 +2900,14 @@ bool Bot::isDeadlyMove (const Vector &to) {
void Bot::changePitch (float speed) { void Bot::changePitch (float speed) {
// this function turns a bot towards its ideal_pitch // this function turns a bot towards its ideal_pitch
float idealPitch = cr::normalizeAngles (pev->idealpitch); float idealPitch = cr::wrapAngle (pev->idealpitch);
float curent = cr::normalizeAngles (pev->v_angle.x); float curent = cr::wrapAngle (pev->v_angle.x);
// turn from the current v_angle pitch to the idealpitch by selecting // turn from the current v_angle pitch to the idealpitch by selecting
// the quickest way to turn to face that direction // the quickest way to turn to face that direction
// find the difference in the curent and ideal angle // find the difference in the curent and ideal angle
float normalizePitch = cr::normalizeAngles (idealPitch - curent); float normalizePitch = cr::wrapAngle (idealPitch - curent);
if (normalizePitch > 0.0f) { if (normalizePitch > 0.0f) {
if (normalizePitch > speed) { if (normalizePitch > speed) {
@ -2920,7 +2920,7 @@ void Bot::changePitch (float speed) {
} }
} }
pev->v_angle.x = cr::normalizeAngles (curent + normalizePitch); pev->v_angle.x = cr::wrapAngle (curent + normalizePitch);
if (pev->v_angle.x > 89.9f) { if (pev->v_angle.x > 89.9f) {
pev->v_angle.x = 89.9f; pev->v_angle.x = 89.9f;
@ -2935,14 +2935,14 @@ void Bot::changePitch (float speed) {
void Bot::changeYaw (float speed) { void Bot::changeYaw (float speed) {
// this function turns a bot towards its ideal_yaw // this function turns a bot towards its ideal_yaw
float idealPitch = cr::normalizeAngles (pev->ideal_yaw); float idealPitch = cr::wrapAngle (pev->ideal_yaw);
float curent = cr::normalizeAngles (pev->v_angle.y); float curent = cr::wrapAngle (pev->v_angle.y);
// turn from the current v_angle yaw to the ideal_yaw by selecting // turn from the current v_angle yaw to the ideal_yaw by selecting
// the quickest way to turn to face that direction // the quickest way to turn to face that direction
// find the difference in the curent and ideal angle // find the difference in the curent and ideal angle
float normalizePitch = cr::normalizeAngles (idealPitch - curent); float normalizePitch = cr::wrapAngle (idealPitch - curent);
if (normalizePitch > 0.0f) { if (normalizePitch > 0.0f) {
if (normalizePitch > speed) { if (normalizePitch > speed) {
@ -2954,7 +2954,7 @@ void Bot::changeYaw (float speed) {
normalizePitch = -speed; normalizePitch = -speed;
} }
} }
pev->v_angle.y = cr::normalizeAngles (curent + normalizePitch); pev->v_angle.y = cr::wrapAngle (curent + normalizePitch);
pev->angles.y = pev->v_angle.y; pev->angles.y = pev->v_angle.y;
} }
@ -3084,7 +3084,7 @@ void Bot::updateLookAnglesNewbie (const Vector &direction, float delta) {
Vector spring { 13.0f, 13.0f, 0.0f }; Vector spring { 13.0f, 13.0f, 0.0f };
Vector damperCoefficient { 0.22f, 0.22f, 0.0f }; Vector damperCoefficient { 0.22f, 0.22f, 0.0f };
const float offset = cr::clamp (m_difficulty, 1, 4) * 25.0f; const float offset = cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 25.0f;
Vector influence = Vector (0.25f, 0.17f, 0.0f) * (100.0f - offset) / 100.f; Vector influence = Vector (0.25f, 0.17f, 0.0f) * (100.0f - offset) / 100.f;
Vector randomization = Vector (2.0f, 0.18f, 0.0f) * (100.0f - offset) / 100.f; Vector randomization = Vector (2.0f, 0.18f, 0.0f) * (100.0f - offset) / 100.f;

View file

@ -285,7 +285,7 @@ void BotSupport::checkWelcome () {
bool needToSendMsg = (graph.length () > 0 ? m_needToSendWelcome : true); bool needToSendMsg = (graph.length () > 0 ? m_needToSendWelcome : true);
auto receiveEntity = game.getLocalEntity (); auto receiveEntity = game.getLocalEntity ();
if (isAlive (receiveEntity) && m_welcomeReceiveTime < 1.0 && needToSendMsg) { if (isAlive (receiveEntity) && m_welcomeReceiveTime < 1.0f && needToSendMsg) {
m_welcomeReceiveTime = game.time () + 4.0f; // receive welcome message in four seconds after game has commencing m_welcomeReceiveTime = game.time () + 4.0f; // receive welcome message in four seconds after game has commencing
} }
@ -509,7 +509,7 @@ void BotSupport::simulateNoise (int playerIndex) {
} }
} }
if (noise.dist <= 0.0) { if (noise.dist <= 0.0f) {
return; // didn't issue sound? return; // didn't issue sound?
} }
@ -613,7 +613,7 @@ void BotSupport::calculatePings () {
if (!bot) { if (!bot) {
continue; continue;
} }
int part = static_cast <int> (average.first * 0.2f); int part = static_cast <int> (static_cast <float> (average.first) * 0.2f);
int botPing = bot->m_basePing + rg.get (average.first - part, average.first + part) + rg.get (bot->m_difficulty / 2, bot->m_difficulty); int botPing = bot->m_basePing + rg.get (average.first - part, average.first + part) + rg.get (bot->m_difficulty / 2, bot->m_difficulty);
int botLoss = rg.get (average.second / 2, average.second); int botLoss = rg.get (average.second / 2, average.second);

View file

@ -16,21 +16,21 @@
#include VERSION_HEADER #include VERSION_HEADER
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION MODULE_BOT_VERSION_FILE FILEVERSION MODULE_VERSION_FILE
PRODUCTVERSION MODULE_BOT_VERSION_FILE PRODUCTVERSION MODULE_VERSION_FILE
FILEOS 0x40004 FILEOS 0x40004
FILETYPE 0x2 { FILETYPE 0x2 {
BLOCK "StringFileInfo" { BLOCK "StringFileInfo" {
BLOCK "040904E4" { BLOCK "040904E4" {
VALUE "CompanyName", "YaPB Project" "\0" VALUE "CompanyName", "YaPB Project" "\0"
VALUE "FileDescription", "YaPB v" MODULE_BOT_VERSION "." MODULE_BUILD_COUNT " - The Counter-Strike Bot" "\0" VALUE "FileDescription", "YaPB v" MODULE_VERSION "." MODULE_COMMIT_COUNT " - The Counter-Strike Bot" "\0"
VALUE "LegalCopyright", "Copyright \251 2023 YaPB Project" "\0" VALUE "LegalCopyright", "Copyright \251 2023 YaPB Project" "\0"
VALUE "OriginalFilename", "yapb.dll" "\0" VALUE "OriginalFilename", "yapb.dll" "\0"
VALUE "ProductName", "YaPB" "\0" VALUE "ProductName", "YaPB" "\0"
VALUE "InternalName", "YaPB DLL" "\0" VALUE "InternalName", "YaPB DLL" "\0"
VALUE "FileVersion", MODULE_BOT_VERSION "." MODULE_BUILD_COUNT "\0" VALUE "FileVersion", MODULE_VERSION "." MODULE_COMMIT_COUNT "\0"
VALUE "ProductVersion", MODULE_BOT_VERSION "." MODULE_BUILD_COUNT "\0" VALUE "ProductVersion", MODULE_VERSION "." MODULE_COMMIT_COUNT "\0"
VALUE "SpecialBuild", MODULE_BOT_BUILD_ID "\0" VALUE "SpecialBuild", MODULE_BUILD_ID "\0"
} }
} }
BLOCK "VarFileInfo" { BLOCK "VarFileInfo" {

View file

@ -151,7 +151,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\vc;..\inc;..\ext;..\ext\crlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\vc;..\inc;..\ext;..\ext\crlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;CR_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;CR_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader /> <PrecompiledHeader />
<PrecompiledHeaderFile>yapb.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>yapb.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>.\debug\inf\yapb.pch</PrecompiledHeaderOutputFile> <PrecompiledHeaderOutputFile>.\debug\inf\yapb.pch</PrecompiledHeaderOutputFile>