diff --git a/.gitignore b/.gitignore index f8157c6..b12ad51 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ sandbot_windows_exports.txt linux_exports.txt sandbot_linux_exports.txt +*.so diff --git a/dlls/Makefile b/dlls/Makefile index 2049d7c..d1d11bb 100644 --- a/dlls/Makefile +++ b/dlls/Makefile @@ -4,20 +4,13 @@ CPP = g++ -# gcc preprocessor flags -BASEFLAGS = - -# gcc compiler flags - thanks RoboCop +# gcc flags - thanks RoboCop OPTFLAGS = \ -m32 -Wall -Wextra \ -O2 -march=core2 -mfpmath=sse \ - -fomit-frame-pointer -finline-functions -falign-loops=2 -falign-jumps=2 -falign-functions=2 \ - -Wno-write-strings \ - -Wno-missing-field-initializers \ - -Wno-unknown-pragmas \ - -Wno-attributes + -fomit-frame-pointer -finline-functions -falign-loops=2 -falign-jumps=2 -falign-functions=2 -CPPFLAGS = ${BASEFLAGS} ${OPTFLAGS} -lstdc++ -lm -I../dlls -I../engine -I../common -I../pm_shared -I../metamod +CPPFLAGS = ${OPTFLAGS} -lstdc++ -lm -I../dlls -I../engine -I../common -I../pm_shared -I../metamod OBJ = \ bot.o \ @@ -35,11 +28,11 @@ OBJ = \ bot_hunger.o \ dll.o \ engine.o \ - foolsgoldsource.o \ game.o \ h_export.o \ linkfunc.o \ meta_api.o \ + pseudomod.o \ util.o \ waypoint.o @@ -47,7 +40,7 @@ OBJ = \ .DEFAULT_GOAL := sandbot.so # if debug is requested add debug flags and build sandbot.so -# could have debug: BASEFLAGS += -DDEBUG as well but that would +# could have debug: OPTFLAGS += -DDEBUG as well but that would # mean the symbols are from a build that is different to release debug: OPTFLAGS += -g debug: sandbot.so diff --git a/dlls/bot.cpp b/dlls/bot.cpp index 788cf1c..6545a71 100644 --- a/dlls/bot.cpp +++ b/dlls/bot.cpp @@ -20,11 +20,11 @@ #include "h_export.h" bot_player_t *pBotData = nullptr; -bot_t **pBots = nullptr; // [MAX_PLAYERS]; +bot_t **pBots = nullptr; // [Game::MAX_PLAYERS]; bool b_observer_mode = false; -bot_player_t g_valveBots[MAX_PLAYERS] = +bot_player_t g_valveBots[Game::MAX_PLAYERS] = { {"Kelly", "barney", false}, {"Ted", "gina", false}, @@ -60,7 +60,7 @@ bot_player_t g_valveBots[MAX_PLAYERS] = {"Harry", "gina", false} }; -bot_player_t g_gearboxBots[MAX_PLAYERS] = +bot_player_t g_gearboxBots[Game::MAX_PLAYERS] = { {"Randy", "barney", false}, {"Brian", "beret", false}, @@ -96,13 +96,13 @@ bot_player_t g_gearboxBots[MAX_PLAYERS] = {"RichardC", "otis", false} }; -bot_player_t g_cstrikeBots[MAX_PLAYERS] = +bot_player_t g_cstrikeBots[Game::MAX_PLAYERS] = { {"Minh", NULL, false}, {"Jesse", NULL, false} }; -bot_player_t g_dodBots[MAX_PLAYERS] = +bot_player_t g_dodBots[Game::MAX_PLAYERS] = { {"Matt", NULL, false}, // mugsy {"John", NULL, false}, // pickitup @@ -138,7 +138,7 @@ bot_player_t g_dodBots[MAX_PLAYERS] = {"Unknown8", NULL, false}, // Kamikazi }; -bot_player_t g_gunmanBots[MAX_PLAYERS] = +bot_player_t g_gunmanBots[Game::MAX_PLAYERS] = { {"Herb", "bandit", false}, // BoneWolf {"Steven", "general", false}, // Wipeoot @@ -174,7 +174,7 @@ bot_player_t g_gunmanBots[MAX_PLAYERS] = {"Robin", "general", false} }; -bot_player_t g_nsBots[MAX_PLAYERS] = +bot_player_t g_nsBots[Game::MAX_PLAYERS] = { {"Charlie", NULL, false}, {"Jon", NULL, false}, @@ -210,7 +210,7 @@ bot_player_t g_nsBots[MAX_PLAYERS] = {"Nick", NULL, false} }; -bot_player_t g_hungerBots[MAX_PLAYERS] = +bot_player_t g_hungerBots[Game::MAX_PLAYERS] = { {"Bill", "civie", false}, {"Dave", "dave", false}, @@ -246,7 +246,7 @@ bot_player_t g_hungerBots[MAX_PLAYERS] = {"Ben", "zork", false} }; -bot_player_t g_shipBots[MAX_PLAYERS] = +bot_player_t g_shipBots[Game::MAX_PLAYERS] = { {"Chris", NULL, false}, {"Ailsa", NULL, false}, @@ -288,8 +288,6 @@ float pause_frequency[5] = {4, 7, 10, 15, 20}; float pause_time[5][2] = {{0.2f, 0.5f}, {0.5f, 1.0f}, {0.7f, 1.3f}, {1.0f, 1.7f}, {1.2f, 2.0f}}; // TheFatal's method for calculating the msecval -extern int msecnum; -extern float msecdel; extern float msecval; int GetBotCount() @@ -335,7 +333,7 @@ void KickBot( const int iIndex ) void KickAllBots() { - for( unsigned int index = 0; index < MAX_PLAYERS; index++ ) + for( unsigned int index = 0; index < Game::MAX_PLAYERS; index++ ) { KickBot( index ); } @@ -345,14 +343,14 @@ void CleanupGameAndBots() { if( pBotData ) { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBotData[i].bIsUsed = false; } } if( pBots ) { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { delete pBots[i]; pBots[i] = nullptr; @@ -493,7 +491,7 @@ void BotCreate( edict_t *pPlayer, const char *arg1, const char *arg2, const char iIndex++; // loop around - if( iIndex == MAX_PLAYERS ) + if( iIndex == Game::MAX_PLAYERS ) { iIndex = 0; } @@ -545,12 +543,12 @@ void BotCreate( edict_t *pPlayer, const char *arg1, const char *arg2, const char pBotEdict->v.frags = 0; index = 0; - while ((index < MAX_PLAYERS) && (pBots[index]->is_used)) + while ((index < Game::MAX_PLAYERS) && (pBots[index]->is_used)) { index++; } - if (index == MAX_PLAYERS) + if (index == Game::MAX_PLAYERS) { ClientPrint( pPlayer, HUD_PRINTNOTIFY, "Can't create bot!\n"); return; @@ -1283,20 +1281,21 @@ void BotThink( bot_t *pBot ) else pitch_degrees = 0.0; - // turn towards ideal_yaw by yaw_speed degrees - yaw_degrees = BotChangeYaw( pBot, pEdict->v.yaw_speed ); + // turn towards ideal_yaw by yaw_speed degrees + yaw_degrees = BotChangeYaw( pBot, pEdict->v.yaw_speed ); - if ((pitch_degrees >= pEdict->v.pitch_speed) || (yaw_degrees >= pEdict->v.yaw_speed)) - { + if ((pitch_degrees >= pEdict->v.pitch_speed) || (yaw_degrees >= pEdict->v.yaw_speed)) + { // don't move while turning a lot pBot->SetSpeed( 0.0 ); - } - else if ((pitch_degrees >= 10) || (yaw_degrees >= 10)) // turning more than 10 degrees? - { - pBot->SetSpeed( pBot->GetSpeed() / 4.0 ); // slow down while turning - } - else // else handle movement related actions... - { + } + else if ((pitch_degrees >= 10) || (yaw_degrees >= 10)) // turning more than 10 degrees? + { + // slow down while turning + pBot->SetSpeed( pBot->GetSpeed() / 4.0 ); + } + else // else handle movement related actions... + { if( pBot->CanShoot() && pBot->ShouldSeekEnemy() ) { // if it's CTF, prioritise looking for the flag @@ -1812,6 +1811,9 @@ bot_t::bot_t() // TODO: needed? already set in BotThink - not BotCreate? this->name[0] = '\0'; this->pEdict = nullptr; + + this->iGoalIndex = 0; + this->bCapturing = false; } bot_t::~bot_t() @@ -1820,6 +1822,8 @@ bot_t::~bot_t() void bot_t::OnSpawn() { + this->iGoalIndex = 0; + this->bCapturing = false; } void bot_t::Join() @@ -1859,6 +1863,33 @@ void bot_t::Think() { // TODO: move all of BotThink into here eventually this->PreThink(); + + // TODO: possibly this will trigger before the bot is touching the capture point? shouldn't + // though because bCapturing is only true when the bot is close enough to the waypoint + // if the bot is capturing, and is at a capture point, and it's a point that should be captured + if( this->IsCapturing() ) + { + this->SetSpeed( 0.0 ); + + // TODO: this is very rough - probably something is set in pev if the bot is + // on or near a dod_control_point/trigger_ctfgeneric - should check if it's a brush entity... + if( DistanceToNearest( this->pEdict->v.origin, "dod_control_point" ) > 200.0 || DistanceToNearest( this->pEdict->v.origin, "trigger_ctfgeneric" ) > 200.0 ) + { + UTIL_LogDPrintf( "too far from capture point while capturing; resetting\n" ); + this->SetIsCapturing( false ); + this->SetSpeed( this->GetMaxSpeed() ); + } + } + + // TODO: waypoint goal changes once it's capturing? + // if the current waypoint is a capture point and it is now captured + if( this->IsCapturing() && ShouldSkip( this->pEdict, this->iGoalIndex ) ) + { + UTIL_LogDPrintf( "leaving waypoint\n" ); + this->SetIsCapturing( false ); + this->SetSpeed( this->GetMaxSpeed() ); + } + this->PostThink(); } @@ -2268,7 +2299,7 @@ bool bot_t::IsDead() return this->pEdict->v.health < 1 || this->pEdict->v.deadflag != DEAD_NO; } -bool bot_t::IsUnderWater() +bool bot_t::IsUnderWater() const { return this->pEdict->v.waterlevel == 3; } @@ -2278,6 +2309,11 @@ bool bot_t::IsSniper() return false; } +bool bot_t::IsCapturing() const +{ + return this->bCapturing; +} + void bot_t::UpdateSounds() { edict_t *pPlayer; @@ -2332,7 +2368,7 @@ float bot_t::GetWaypointRadius() return fRadius; } -bool bot_t::BaseCanUseWeapon() +bool bot_t::BaseCanUseWeapon() const { return this->HasEnemy(); } @@ -2396,3 +2432,8 @@ bool bot_t::ShouldCapturePoint( edict_t * pControlPoint ) { return false; } + +void bot_t::SetIsCapturing( const bool bIsCapturing ) +{ + this->bCapturing = bIsCapturing; +} diff --git a/dlls/bot.h b/dlls/bot.h index 06db7a8..1e6a529 100644 --- a/dlls/bot.h +++ b/dlls/bot.h @@ -143,14 +143,9 @@ const int kGameStatusEnded = 2; const int kGameStatusGameTime = 3; const int kGameStatusUnspentLevels = 4; -#define MAX_PLAYERS 32 - - #define BOT_SKIN_LEN 32 #define BOT_NAME_LEN 32 -#define MAX_BACKPACKS 100 - // Only one of these allowed per entity, stored in pev->iuser3. typedef enum { @@ -287,13 +282,21 @@ struct bot_player_t bool bIsUsed; }; +extern bot_player_t g_valveBots[]; +extern bot_player_t g_gearboxBots[]; +extern bot_player_t g_dodBots[]; +extern bot_player_t g_gunmanBots[]; +extern bot_player_t g_nsBots[]; +extern bot_player_t g_hungerBots[]; +extern bot_player_t g_shipBots[]; + void KickBot( const int iIndex ); void KickAllBots(); void CleanupGameAndBots(); class bot_t; -typedef bool (bot_t::*CanUseWeapon)(bool); +typedef bool (bot_t::*CanUseWeapon)(bool) const; struct weapon_t { @@ -305,6 +308,8 @@ struct weapon_t class bot_t { public: + static constexpr int BOT_NAME_MAX_LENGTH = BOT_NAME_LEN + 1; + bot_t(); virtual ~bot_t(); @@ -351,8 +356,9 @@ class bot_t virtual int GetLightLevel() final; virtual bool IsDead() final; - virtual bool IsUnderWater() final; + virtual bool IsUnderWater() const final; virtual bool IsSniper(); + virtual bool IsCapturing() const; virtual void UpdateSounds(); @@ -360,13 +366,14 @@ class bot_t virtual int GetGoalType(); virtual float GetWaypointRadius(); - virtual bool BaseCanUseWeapon(); + virtual bool BaseCanUseWeapon() const; virtual std::vector GetUsableWeapons( bool strict ); virtual bool ShouldJumpAfterDeath(); virtual bool HasFlag() const; virtual bool ShouldCapturePoint( edict_t * pControlPoint ); + virtual void SetIsCapturing( const bool bIsCapturing ); bool is_used; int iBotDataIndex; @@ -374,7 +381,7 @@ class bot_t int index; edict_t *pEdict; bool bShouldInit; - char name[BOT_NAME_LEN+1]; + char name[BOT_NAME_MAX_LENGTH]; char skin[BOT_SKIN_LEN+1]; int not_started; int start_action; @@ -489,6 +496,7 @@ class bot_t float fUseDoorTime; int iGoalIndex; + bool bCapturing; bot_current_weapon_t current_weapon; // one current weapon for each bot int m_rgAmmo[MAX_AMMO_SLOTS]; // total ammo amounts (1 array for each bot) @@ -515,13 +523,13 @@ class HalfLifeBot : public bot_t public: HalfLifeBot(); - virtual int GetPistol(); + virtual int GetPistol() const; - virtual bool CanUseCrowbar( bool really ); - virtual bool CanUseGlock( bool really ); - virtual bool CanUseMP5Primary( bool really ); + virtual bool CanUseCrowbar( bool really ) const; + virtual bool CanUseGlock( bool really ) const; + virtual bool CanUseMP5Primary( bool really ) const; - virtual bool CanUseEgon( bool really ); + virtual bool CanUseEgon( bool really ) const; }; class OpposingForceBot : public HalfLifeBot @@ -565,9 +573,7 @@ class DODBot : public bot_t public: DODBot(); - virtual void OnSpawn(); virtual void Join(); - virtual void Think(); virtual float GetSpeedToEnemy() const; virtual void Reload(); @@ -584,8 +590,6 @@ class DODBot : public bot_t virtual bool ShouldCapturePoint( edict_t * pControlPoint ); - bool bCapturing; - const static int TEAM_ALLIES = 1; const static int TEAM_AXIS = 2; @@ -657,22 +661,22 @@ class GunmanBot : public bot_t virtual int GetPistolMode(); - virtual void UseGaussPistolPulse(); - virtual void UseGaussPistolCharge(); - virtual void UseGaussPistolRapid(); - virtual void UseGaussPistolSniper(); - - virtual bool CanUseFists( bool really ); - virtual bool CanUseGaussPistolPulse( bool really ); - virtual bool CanUseGaussPistolCharge( bool really ); - virtual bool CanUseGaussPistolRapid( bool really ); - virtual bool CanUseShotgun( bool really ); - virtual bool CanUseMinigun( bool really ); - virtual bool CanUseBeamgun( bool really ); - virtual bool CanUseChemgun( bool really ); - virtual bool CanUseDML( bool really ); - virtual bool CanUseDMLGrenade( bool really ); - virtual bool CanUseAICore( bool really ); + virtual void UseGaussPistolPulse() const; + virtual void UseGaussPistolCharge() const; + virtual void UseGaussPistolRapid() const; + virtual void UseGaussPistolSniper() const; + + virtual bool CanUseFists( bool really ) const; + virtual bool CanUseGaussPistolPulse( bool really ) const; + virtual bool CanUseGaussPistolCharge( bool really ) const; + virtual bool CanUseGaussPistolRapid( bool really ) const; + virtual bool CanUseShotgun( bool really ) const; + virtual bool CanUseMinigun( bool really ) const; + virtual bool CanUseBeamgun( bool really ) const; + virtual bool CanUseChemgun( bool really ) const; + virtual bool CanUseDML( bool really ) const; + virtual bool CanUseDMLGrenade( bool really ) const; + virtual bool CanUseAICore( bool really ) const; const static int PISTOL_PULSE = 1; const static int PISTOL_CHARGE = 2; @@ -851,10 +855,11 @@ typedef struct struct CapturePoint { - int iTeam; + // int iTeam; const char* szName; const char* szTarget; edict_t* pEdict; + bool bHasTriggerState; }; #define MAX_TEAMS 32 diff --git a/dlls/bot_client.cpp b/dlls/bot_client.cpp index 92785ec..8d61b1d 100644 --- a/dlls/bot_client.cpp +++ b/dlls/bot_client.cpp @@ -14,6 +14,7 @@ #include "bot_func.h" #include "bot_client.h" #include "bot_weapons.h" +#include "game.h" // https://wiki.alliedmods.net/Half-life_1_game_events @@ -904,7 +905,7 @@ void BotClient_CS_HLTV(void *p, int bot_index) // new round in CS 1.6 if ((players == 0) && (*(int *) p == 0)) { - for (index = 0; index < MAX_PLAYERS; index++) + for (index = 0; index < Game::MAX_PLAYERS; index++) { if (pBots[index]->is_used) BotSpawnInit (pBots[index]); // reset bots for new round diff --git a/dlls/bot_combat.cpp b/dlls/bot_combat.cpp index 261f2a5..2daf554 100644 --- a/dlls/bot_combat.cpp +++ b/dlls/bot_combat.cpp @@ -84,7 +84,7 @@ const bot_weapon_select_t valve_weapon_select[] = { 100, TRUE, 70, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_PISTOL}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} }; const bot_weapon_select_t gearbox_weapon_select[] = { @@ -152,7 +152,7 @@ const bot_weapon_select_t gearbox_weapon_select[] = { 0.0f, 1200.0f, 0.0f, 1200.0f, 100, TRUE, 70, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_PISTOL}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} }; const bot_weapon_select_t cs_weapon_select[] = { @@ -169,7 +169,7 @@ const bot_weapon_select_t cs_weapon_select[] = { 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_PISTOL}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} }; const bot_weapon_select_t dod_weapon_select[] = { @@ -239,7 +239,7 @@ const bot_weapon_select_t dod_weapon_select[] = { 0.0f, 50.0f, 0.0f, 0.0f, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_MELEE},*/ /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} }; const bot_weapon_select_t tfc_weapon_select[] = { @@ -295,7 +295,7 @@ const bot_weapon_select_t tfc_weapon_select[] = { 40.0f, 600.0f, 0.0f, 0.0f, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} }; const bot_weapon_select_t gunman_weapon_select[] = { @@ -336,7 +336,7 @@ const bot_weapon_select_t gunman_weapon_select[] = { 0, true, 100, 0, 0, false, false, false, false, 0.0f, 0.0f, WEAPON_NONE}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f, WEAPON_NONE} }; // see AvHBasePlayerWeapon::mRange @@ -425,7 +425,7 @@ const bot_weapon_select_t ns_weapon_select[] = { FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} }; const bot_weapon_select_t hunger_weapon_select[] = { @@ -497,21 +497,19 @@ const bot_weapon_select_t hunger_weapon_select[] = { 100, TRUE, 70, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} }; const bot_weapon_select_t ship_weapon_select[] = { /* terminator */ - {0, "", 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} + {0, "", 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f} }; edict_t *BotFindEnemy( bot_t *pBot ) { Vector vecEnd; - static bool flag=TRUE; edict_t *pent = nullptr; edict_t *pNewEnemy; - int i; edict_t *pEdict = pBot->pEdict; @@ -576,7 +574,7 @@ edict_t *BotFindEnemy( bot_t *pBot ) nearestdistance = 2500; // search the world for players... - for (i = 1; i <= gpGlobals->maxClients; i++) + for( int i = 1; i <= gpGlobals->maxClients; i++ ) { edict_t *pPlayer = INDEXENT(i); diff --git a/dlls/bot_cstrike.cpp b/dlls/bot_cstrike.cpp index 1b28f50..f1ff548 100644 --- a/dlls/bot_cstrike.cpp +++ b/dlls/bot_cstrike.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Counter-Strike specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" diff --git a/dlls/bot_dod.cpp b/dlls/bot_dod.cpp index 15d34ef..39dba08 100644 --- a/dlls/bot_dod.cpp +++ b/dlls/bot_dod.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Day of Defeat specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" @@ -13,14 +25,6 @@ DODBot::DODBot() { } -void DODBot::OnSpawn() -{ - bot_t::OnSpawn(); - - this->bCapturing = false; - this->iGoalIndex = 0; -} - void DODBot::Join() { if( this->start_action == MSG_DOD_TEAM_SELECT ) @@ -127,39 +131,6 @@ void DODBot::Join() } } -void DODBot::Think() -{ - bot_t::PreThink(); - - // TODO: possibly this will trigger before the bot is touching the capture point? shouldn't - // though because bCapturing is only true when the bot is close enough to the waypoint - // if the bot is capturing, and is at a capture point, and it's a point that should be captured - if( this->bCapturing ) - { - this->SetSpeed( 0.0 ); - - // TODO: this is very rough - probably something is set in pev if the bot is - // on or near a dod_control_point - should check if it's a brush entity... - if( DistanceToNearest(this->pEdict->v.origin, "dod_control_point") > 200 ) - { - UTIL_LogDPrintf( "too far from capture point while capturing; resetting\n" ); - this->bCapturing = false; - this->SetSpeed( this->GetMaxSpeed() ); - } - } - - // TODO: waypoint goal changes once it's capturing? - // if the current waypoint is a capture point and it is now captured - if( this->bCapturing && ShouldSkip(this->pEdict, this->iGoalIndex) ) - { - UTIL_LogDPrintf( "leaving waypoint\n" ); - this->bCapturing = false; - this->SetSpeed( this->GetMaxSpeed() ); - } - - bot_t::PostThink(); -} - float DODBot::GetSpeedToEnemy() const { if( !this->pBotEnemy ) @@ -214,7 +185,7 @@ bool DODBot::IsSniper() bool DODBot::ShouldLookForNewGoal() { - return !this->bCapturing; + return !this->IsCapturing(); } int DODBot::GetGoalType() diff --git a/dlls/bot_gunman.cpp b/dlls/bot_gunman.cpp index 1487dc4..80ec3d8 100644 --- a/dlls/bot_gunman.cpp +++ b/dlls/bot_gunman.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Gunman Chronicles specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" @@ -88,29 +100,29 @@ int GunmanBot::GetPistolMode() return this->iPistolMode; } -void GunmanBot::UseGaussPistolPulse() +void GunmanBot::UseGaussPistolPulse() const { FakeClientCommand( this->pEdict, "cust_11" ); } -void GunmanBot::UseGaussPistolCharge() +void GunmanBot::UseGaussPistolCharge() const { // gauss_bolt in logs FakeClientCommand( this->pEdict, "cust_12" ); } -void GunmanBot::UseGaussPistolRapid() +void GunmanBot::UseGaussPistolRapid() const { // gauss_charged in logs FakeClientCommand( this->pEdict, "cust_13" ); } -void GunmanBot::UseGaussPistolSniper() +void GunmanBot::UseGaussPistolSniper() const { FakeClientCommand( this->pEdict, "cust_14" ); } -bool GunmanBot::CanUseFists( bool really ) +bool GunmanBot::CanUseFists( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -120,7 +132,7 @@ bool GunmanBot::CanUseFists( bool really ) return this->GetDistanceToEnemy() < 32.0 || really; } -bool GunmanBot::CanUseGaussPistolPulse( bool really ) +bool GunmanBot::CanUseGaussPistolPulse( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -135,7 +147,7 @@ bool GunmanBot::CanUseGaussPistolPulse( bool really ) return this->GetDistanceToEnemy() < 1024.0 || really; } -bool GunmanBot::CanUseGaussPistolCharge( bool really ) +bool GunmanBot::CanUseGaussPistolCharge( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -150,7 +162,7 @@ bool GunmanBot::CanUseGaussPistolCharge( bool really ) return this->GetDistanceToEnemy() < 256.0 || really; } -bool GunmanBot::CanUseGaussPistolRapid( bool really ) +bool GunmanBot::CanUseGaussPistolRapid( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -165,7 +177,7 @@ bool GunmanBot::CanUseGaussPistolRapid( bool really ) return this->GetDistanceToEnemy() < 512.0 || really; } -bool GunmanBot::CanUseShotgun( bool really ) +bool GunmanBot::CanUseShotgun( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -180,7 +192,7 @@ bool GunmanBot::CanUseShotgun( bool really ) return this->GetDistanceToEnemy() < 150.0 || really; } -bool GunmanBot::CanUseMinigun( bool really ) +bool GunmanBot::CanUseMinigun( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -195,7 +207,7 @@ bool GunmanBot::CanUseMinigun( bool really ) return this->GetDistanceToEnemy() < 1024.0 || really; } -bool GunmanBot::CanUseBeamgun( bool really ) +bool GunmanBot::CanUseBeamgun( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -210,7 +222,7 @@ bool GunmanBot::CanUseBeamgun( bool really ) return this->GetDistanceToEnemy() < 512.0 || really; } -bool GunmanBot::CanUseChemgun( bool really ) +bool GunmanBot::CanUseChemgun( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -225,7 +237,7 @@ bool GunmanBot::CanUseChemgun( bool really ) return( this->GetDistanceToEnemy() > 128.0 && this->GetDistanceToEnemy() < 512.0 ) || really; } -bool GunmanBot::CanUseDML( bool really ) +bool GunmanBot::CanUseDML( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -240,7 +252,7 @@ bool GunmanBot::CanUseDML( bool really ) return ( this->GetDistanceToEnemy() > 256.0 && this->GetDistanceToEnemy() < 9999.0 ) || really; } -bool GunmanBot::CanUseDMLGrenade( bool really ) +bool GunmanBot::CanUseDMLGrenade( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -255,7 +267,7 @@ bool GunmanBot::CanUseDMLGrenade( bool really ) return ( this->GetDistanceToEnemy() > 128.0 && this->GetDistanceToEnemy() < 512.0 ) || really; } -bool GunmanBot::CanUseAICore( bool really ) +bool GunmanBot::CanUseAICore( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { diff --git a/dlls/bot_halflife.cpp b/dlls/bot_halflife.cpp index a1e29ba..d9ff7f4 100644 --- a/dlls/bot_halflife.cpp +++ b/dlls/bot_halflife.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Half-Life specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" @@ -21,12 +33,12 @@ HalfLifeBot::HalfLifeBot() this->weapons.push_back(mp5); } -int HalfLifeBot::GetPistol() +int HalfLifeBot::GetPistol() const { return VALVE_WEAPON_GLOCK; } -bool HalfLifeBot::CanUseCrowbar( bool really ) +bool HalfLifeBot::CanUseCrowbar( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -36,7 +48,7 @@ bool HalfLifeBot::CanUseCrowbar( bool really ) return this->GetDistanceToEnemy() < 32.0 || really; } -bool HalfLifeBot::CanUseGlock( bool really ) +bool HalfLifeBot::CanUseGlock( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -51,7 +63,7 @@ bool HalfLifeBot::CanUseGlock( bool really ) return this->GetDistanceToEnemy() < 8192.0 || really; } -bool HalfLifeBot::CanUseMP5Primary( bool really ) +bool HalfLifeBot::CanUseMP5Primary( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -71,7 +83,7 @@ bool HalfLifeBot::CanUseMP5Primary( bool really ) return this->GetDistanceToEnemy() < 8192.0 || really; } -bool HalfLifeBot::CanUseEgon( bool really ) +bool HalfLifeBot::CanUseEgon( bool really ) const { if( !bot_t::BaseCanUseWeapon() ) { @@ -88,5 +100,5 @@ bool HalfLifeBot::CanUseEgon( bool really ) return false; } - return ( this->GetDistanceToEnemy() > 128 && this->GetDistanceToEnemy() < 8192.0 ) || really; + return ( this->GetDistanceToEnemy() > 128.0 && this->GetDistanceToEnemy() < 8192.0 ) || really; } \ No newline at end of file diff --git a/dlls/bot_hunger.cpp b/dlls/bot_hunger.cpp index 2a125f8..4c960c0 100644 --- a/dlls/bot_hunger.cpp +++ b/dlls/bot_hunger.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: They Hunger specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" diff --git a/dlls/bot_navigate.cpp b/dlls/bot_navigate.cpp index 4be9b2a..87abc38 100644 --- a/dlls/bot_navigate.cpp +++ b/dlls/bot_navigate.cpp @@ -553,20 +553,37 @@ bool BotHeadTowardWaypoint( bot_t *pBot ) pBot->prev_waypoint_distance = 0.0; // if the current waypoint is the goal and it's a Day of Defeat capture point - if (mod_id == DOD_DLL && (waypoints[pBot->curr_waypoint_index].flags & W_FL_DOD_CAP) && - pBot->waypoint_goal == pBot->curr_waypoint_index && !ShouldSkip(pBot->pEdict, pBot->waypoint_goal)) + if (mod_id == DOD_DLL && (waypoints[pBot->curr_waypoint_index].flags & W_FL_DOD_CAP) && pBot->waypoint_goal == pBot->curr_waypoint_index && !ShouldSkip(pBot->pEdict, pBot->waypoint_goal)) { UTIL_LogDPrintf( "stopping near waypoint\n" ); pBot->SetSpeed( 0.0 ); - ((DODBot *)pBot)->bCapturing = true; + pBot->SetIsCapturing( true ); pBot->iGoalIndex = pBot->waypoint_goal; } - else if (mod_id == DOD_DLL && (waypoints[pBot->curr_waypoint_index].flags & W_FL_DOD_CAP) && - pBot->waypoint_goal == pBot->curr_waypoint_index && ShouldSkip(pBot->pEdict, pBot->waypoint_goal)) + else if (mod_id == DOD_DLL && (waypoints[pBot->curr_waypoint_index].flags & W_FL_DOD_CAP) && pBot->waypoint_goal == pBot->curr_waypoint_index && ShouldSkip(pBot->pEdict, pBot->waypoint_goal)) { UTIL_LogDPrintf( "moving away from waypoint\n" ); pBot->SetSpeed( pEdict->v.maxspeed ); - ((DODBot *)pBot)->bCapturing = false; + pBot->SetIsCapturing( false ); + } + else if( mod_id == GEARBOX_DLL && pGame->IsCapturePoint() ) + { + if( waypoints[pBot->curr_waypoint_index].flags & W_FL_OP4_CAPTURE_POINT ) + { + if( pBot->waypoint_goal == pBot->curr_waypoint_index && !ShouldSkip( pBot->pEdict, pBot->waypoint_goal ) ) + { + UTIL_LogDPrintf( "stopping near waypoint\n" ); + pBot->SetSpeed( 0.0 ); + pBot->SetIsCapturing( true ); + pBot->iGoalIndex = pBot->waypoint_goal; + } + else if( pBot->waypoint_goal == pBot->curr_waypoint_index &&!ShouldSkip( pBot->pEdict, pBot->waypoint_goal ) ) + { + UTIL_LogDPrintf( "moving away from waypoint\n" ); + pBot->SetSpeed( pEdict->v.maxspeed ); + pBot->SetIsCapturing( false ); + } + } } // check if the waypoint is a door waypoint diff --git a/dlls/bot_ns.cpp b/dlls/bot_ns.cpp index 0472e91..7cb9aa0 100644 --- a/dlls/bot_ns.cpp +++ b/dlls/bot_ns.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Natural Selection specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" diff --git a/dlls/bot_opposingforce.cpp b/dlls/bot_opposingforce.cpp index 03ac60d..5eeb8bd 100644 --- a/dlls/bot_opposingforce.cpp +++ b/dlls/bot_opposingforce.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Half-Life Opposing Force specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" @@ -96,20 +108,7 @@ int OpposingForceBot::GetGoalType() } else if( pGame->IsCapturePoint() ) { - if( UTIL_GetTeam(this->pEdict) == OpposingForceBot::TEAM_BLACK_MESA ) - { - return W_FL_OP4_CAPTURE_POINT_BM; - } - else if( UTIL_GetTeam(this->pEdict) == OpposingForceBot::TEAM_OPPOSING_FORCE ) - { - return W_FL_OP4_CAPTURE_POINT_OF; - } - else - { - ALERT( at_error, "Bot with unknown team %d\n", UTIL_GetTeam(this->pEdict) ); - - return W_FL_DELETED; - } + return W_FL_OP4_CAPTURE_POINT; } else { @@ -237,11 +236,49 @@ bool OpposingForceBot::FindFlag() bool OpposingForceBot::ShouldCapturePoint( edict_t * pControlPoint ) { - // TODO: work this out - there are 16 trigger_ctfgeneric entities in op4cp_park - // there are 8 pairs - each pair is one per team - // 8 to do with scoring (?), 8 to with displaying who owns the capture point (?) which have a triggerstate attribute too + // there are 16 trigger_ctfgeneric entities in op4cp_park (8 pairs of entities, 4 pairs per team) + // 8 are to do with scoring (?), 8 are to with displaying who owns the capture point (?) - these have a triggerstate attribute // those with a triggerstate attribute have a target that is an env_render which displays if that team has the point? - // pev->skin or pev->body should have the team name but doesn't + // pev->skin or pev->body should have the team name but doesn't - only one type might have this? + + // what happens - the trigger_ctfgeneric with a triggerstate attribute controls the rendering of who owns the capture point + // it will target (among other things) two env_render entities - their target names will contain _bm_ or _op_ and a renderamt of 255 to show or renderamt of 0 to hide + // if the _bm_ env_render has renderamt of 255 then it probably means that Black Mesa owns that entity + + edict_t* pEntity = nullptr; + + // look at each entity that is targeted by this trigger_ctfgeneric + while( (pEntity = UTIL_FindEntityByString( pEntity, "targetname", STRING(pControlPoint->v.target) ) ) != nullptr ) + { + if( !strcmp( STRING(pEntity->v.classname), "env_render" ) ) + { + ALERT( at_console, "Found an env_render with name %s for %s\n", STRING(pEntity->v.globalname), STRING(pControlPoint->v.globalname) ); + + // if the entity is visible and it's Black Mesa + if( pEntity->v.renderamt == 255 && strstr( STRING(pEntity->v.target), "_bm_" ) ) + { + // capture it if this is an Opposing Force player + return pGame->GetTeam( this->pEdict ) == OpposingForceBot::TEAM_OPPOSING_FORCE; + } + // if the entity is visible and it's Opposing Force + else if( pEntity->v.renderamt == 255 && strstr( STRING(pEntity->v.target), "_op_" ) ) + { + // capture it if this is a Black Mesa player + return pGame->GetTeam( this->pEdict ) == OpposingForceBot::TEAM_BLACK_MESA; + } + // if the entity is invisible + else if( pEntity->v.renderamt == 0 ) + { + // TODO: does this mean no one has captured it yet? + return true; + } + else + { + ALERT( at_error, "Cannot work out whether to capture a trigger_ctfgeneric or not: renderamt is %d and target is %s\n", pEntity->v.renderamt, STRING(pEntity->v.target) ); + } + } + } + return false; } diff --git a/dlls/bot_ship.cpp b/dlls/bot_ship.cpp index 0368d00..62318af 100644 --- a/dlls/bot_ship.cpp +++ b/dlls/bot_ship.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: The Ship specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" diff --git a/dlls/bot_test.cpp b/dlls/bot_test.cpp index 5c14d24..f6ea1a0 100644 --- a/dlls/bot_test.cpp +++ b/dlls/bot_test.cpp @@ -2,9 +2,11 @@ // // Sandbot - GoldSource engine multiplayer bot // +// Based on HPB_Bot by Jeffrey "botman" Broome +// // http://www.teamsandpit.com/ // -// Notes: +// Notes: bot test // //============================================================================= @@ -12,29 +14,73 @@ #include "h_export.h" #include "bot.h" +#include "dll.h" +#include "game.h" #include "foolsgoldsource.h" #include "test.h" -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - namespace tests { TEST_CLASS(bot_test) { public: - TEST_METHOD(TestGetBotCountNoBots) + TEST_METHOD_INITIALIZE(SetUp) + { + CleanupGameAndBots(); + + mod_id = VALVE_DLL; + other_gFunctionTable = foolsgoldsource::gEngine.GetDLLFunctions(); + ServerActivate( nullptr, 2048, 32 ); + // TODO: should be set by calling GiveFnptrsToDll + pBotData = g_valveBots; + + Assert::IsNotNull( pGame.get() ); + Assert::IsNotNull( pBots ); + } + + TEST_METHOD_CLEANUP(TearDown) + { + CleanupGameAndBots(); + } + + TEST_METHOD(TestGetBotCount_NoBots) { - Assert::AreEqual( GetBotCount(), 0 ); + Assert::AreEqual( 0, GetBotCount() ); } - TEST_METHOD(TestGetBotCountOneBot) + TEST_METHOD(TestGetBotCount_OneBot) { edict_t* pPlayer = foolsgoldsource::gEngine.edicts[1].get(); pPlayer->v.netname = ALLOC_STRING("test"); - Assert::AreEqual( GetBotCount(), 1 ); + Assert::AreEqual( 1, GetBotCount() ); + } + + TEST_METHOD(TestKickBot_NoBots) + { + CleanupGameAndBots(); + + KickBot( 1 ); + } + + TEST_METHOD(TestKickBot) + { + // TODO: should be set by calling BotCreate + pBots[0]->iBotDataIndex = 0; + pBots[0]->is_used = true; + strncpy( pBots[0]->name, "BotName", bot_t::BOT_NAME_MAX_LENGTH ); + + Assert::AreEqual( true, pBots[0]->is_used ); + Assert::AreEqual( false, pBotData[pBots[0]->iBotDataIndex].bIsUsed ); + + KickBot( 0 ); + + Assert::AreEqual( false, pBots[0]->is_used ); + Assert::AreEqual( false, pBotData[pBots[0]->iBotDataIndex].bIsUsed ); + Assert::AreEqual( (size_t)1, foolsgoldsource::gEngine.serverCommands.size() ); + Assert::AreEqual( "kick \"BotName\"\n", foolsgoldsource::gEngine.serverCommands[0].c_str() ); } }; } diff --git a/dlls/bot_tfc.cpp b/dlls/bot_tfc.cpp index 0fb90aa..11e741a 100644 --- a/dlls/bot_tfc.cpp +++ b/dlls/bot_tfc.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: Team Fortress Classic specific bot code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" diff --git a/dlls/dll.cpp b/dlls/dll.cpp index 3ffe10e..a5425d8 100644 --- a/dlls/dll.cpp +++ b/dlls/dll.cpp @@ -27,7 +27,6 @@ extern int debug_engine; extern globalvars_t *gpGlobals; extern bool g_waypoint_on; extern bool g_auto_waypoint; -extern bool g_path_waypoint; extern bool b_observer_mode; char g_argv[1024]; @@ -66,7 +65,7 @@ vector capturePoints; int num_flags = 0; // for TFC int num_backpacks = 0; int iCapturePointCount = 0; -BACKPACK_S backpacks[MAX_BACKPACKS]; +BACKPACK_S backpacks[TFCGame::MAX_BACKPACKS]; char arg[256]; float respawn_time = 0.0; @@ -229,7 +228,7 @@ int DispatchSpawn( edict_t *pent ) num_backpacks = 0; - for (int index=0; index < MAX_BACKPACKS; index++) + for (int index=0; index < TFCGame::MAX_BACKPACKS; index++) { backpacks[index].edict = nullptr; backpacks[index].armor = 0; @@ -419,16 +418,16 @@ void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ) // keep track of it pent_trigger_ctfgeneric = pentKeyvalue; } - // if it's a trigger_ctfgeneric and it's the team_no key - if( pentKeyvalue == pent_trigger_ctfgeneric && !strcmp( pkvd->szKeyName, "team_no" ) ) + // if it's a trigger_ctfgeneric and it's the triggerstate key + if( pentKeyvalue == pent_trigger_ctfgeneric && !strcmp( pkvd->szKeyName, "triggerstate" ) ) { + // TODO: this isn't working - key/values are processed sequentially most likely so we're probably getting the triggerstate before the name and targetname CapturePoint capturePoint; - ALERT( at_console, "Getting a trigger_ctfgeneric's details (team_no %d, targetname %s, target %s)\n", atoi( pkvd->szValue ), STRING(pentKeyvalue->v.globalname), STRING(pentKeyvalue->v.target) ); - // get the team_no value - capturePoint.iTeam = atoi( pkvd->szValue ); + ALERT( at_console, "Getting a trigger_ctfgeneric's (targetname %s, target %s)\n", STRING(pentKeyvalue->v.globalname), STRING(pentKeyvalue->v.target) ); + capturePoint.bHasTriggerState = true; // get the name of the capture point capturePoint.szName = STRING(pentKeyvalue->v.globalname); - // get the target (just points to a trigger_relay?) + // get the target capturePoint.szTarget = STRING(pentKeyvalue->v.target); capturePoint.pEdict = pentKeyvalue; capturePoints.push_back( capturePoint ); @@ -536,7 +535,7 @@ BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddres // don't try to add bots for 60 seconds, give client time to get added bot_check_time = gpGlobals->time + 60.0; - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { if( pBots[i]->is_used ) // count the number of bots in use count++; @@ -546,7 +545,7 @@ BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddres // then kick one of the bots off the server... if( (count > min_bots) && (min_bots != -1) ) { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { if( pBots[i]->is_used ) // is this slot used? { @@ -572,7 +571,7 @@ void ClientDisconnect( edict_t *pEntity ) { UTIL_LogDPrintf( "ClientDisconnect: pEntity=%x\n", pEntity ); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { if( pBots && pBots[i] && pBots[i]->pEdict == pEntity ) { @@ -800,20 +799,7 @@ void ClientCommand( edict_t *pEntity ) } else if (FStrEq(pcmd, "pathwaypoint")) { - if (FStrEq(arg1, "on")) - { - g_path_waypoint = TRUE; - g_waypoint_on = TRUE; // turn this on just in case - - ClientPrint(pEntity, HUD_PRINTNOTIFY, "pathwaypoint is ON\n"); - } - else if (FStrEq(arg1, "off")) - { - g_path_waypoint = FALSE; - - ClientPrint(pEntity, HUD_PRINTNOTIFY, "pathwaypoint is OFF\n"); - } - else if (FStrEq(arg1, "create1")) + if (FStrEq(arg1, "create1")) { WaypointCreatePath(pEntity, 1); } @@ -1022,7 +1008,7 @@ void ClientCommand( edict_t *pEntity ) if( pBot ) { - ALERT( at_console, "Capturing: %d\n", ((DODBot *)pBot)->bCapturing ); + ALERT( at_console, "Capturing: %d\n", pBot->IsCapturing() ); } #if 0 @@ -1109,14 +1095,14 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { if( pBotData ) { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBotData[i].bIsUsed = false; } } if( pBots ) { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { delete pBots[i]; pBots[i] = nullptr; @@ -1124,13 +1110,13 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) pBots = nullptr; } - pBots = new bot_t*[MAX_PLAYERS]; + pBots = new bot_t*[Game::MAX_PLAYERS]; if( mod_id == VALVE_DLL ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new HalfLifeBot(); } @@ -1139,7 +1125,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new OpposingForceBot(); } @@ -1148,7 +1134,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new HalfLifeBot(); } @@ -1157,7 +1143,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new CStrikeBot(); } @@ -1166,7 +1152,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new DODBot(); } @@ -1175,7 +1161,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new TFCBot(); } @@ -1184,7 +1170,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new GunmanBot(); } @@ -1193,7 +1179,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new NSBot(); } @@ -1202,7 +1188,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new ShipBot(); } @@ -1211,7 +1197,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { pGame = std::make_unique(); - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i] = new bot_t(); } @@ -1293,7 +1279,7 @@ void StartFrame( void ) count = 0; // mark the bots as needing to be respawned... - for (index = 0; index < MAX_PLAYERS; index++) + for (index = 0; index < Game::MAX_PLAYERS; index++) { if (count >= prev_num_bots) { @@ -1422,7 +1408,7 @@ void StartFrame( void ) } else if( GetBotCount() > CvarGetValue( &bot_count ) ) { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { if( pBots[i] && pBots[i]->is_used ) { diff --git a/dlls/dll.h b/dlls/dll.h index b8f6463..fd88fdf 100644 --- a/dlls/dll.h +++ b/dlls/dll.h @@ -9,6 +9,8 @@ #ifndef DLL_H #define DLL_H +extern DLL_FUNCTIONS other_gFunctionTable; + // DLL prototypes (from engine\eiface.h)... void GameDLLInit( void ); int DispatchSpawn( edict_t *pent ); diff --git a/dlls/engine.cpp b/dlls/engine.cpp index 38f75ff..d4c9021 100644 --- a/dlls/engine.cpp +++ b/dlls/engine.cpp @@ -211,7 +211,7 @@ edict_t* pfnFindEntityByString(edict_t *pEdictStartSearchAfter, const char *pszF // new round in CS 1.5 if (strcmp ("info_map_parameters", pszValue) == 0) { - for (int bot_index = 0; bot_index < MAX_PLAYERS; bot_index++) + for (int bot_index = 0; bot_index < Game::MAX_PLAYERS; bot_index++) { bot_t *pBot = pBots[bot_index]; diff --git a/dlls/foolsgoldsource.cpp b/dlls/foolsgoldsource.cpp index 88e160a..d9b6b6e 100644 --- a/dlls/foolsgoldsource.cpp +++ b/dlls/foolsgoldsource.cpp @@ -1,5 +1,17 @@ +//============================================================================= +// +// Fool's GoldSource - GoldSource engine mock +// +// http://www.teamsandpit.com/ +// +// Notes: engine mock code +// +//============================================================================= + #include "foolsgoldsource.h" +extern globalvars_t* gpGlobals; + namespace foolsgoldsource { Engine gEngine; @@ -11,35 +23,64 @@ namespace foolsgoldsource this->engineFunctions.pfnPrecacheSound = pfnPrecacheSound; this->engineFunctions.pfnSetModel = pfnSetModel; this->engineFunctions.pfnModelIndex = pfnModelIndex; + this->engineFunctions.pfnModelFrames = pfnModelFrames; this->engineFunctions.pfnSetSize = pfnSetSize; + this->engineFunctions.pfnChangeLevel = pfnChangeLevel; + this->engineFunctions.pfnGetSpawnParms = pfnGetSpawnParms; + this->engineFunctions.pfnSaveSpawnParms = pfnSaveSpawnParms; + this->engineFunctions.pfnVecToYaw = pfnVecToYaw; + this->engineFunctions.pfnVecToAngles = pfnVecToAngles; + this->engineFunctions.pfnMoveToOrigin = pfnMoveToOrigin; + this->engineFunctions.pfnChangeYaw = pfnChangeYaw; + this->engineFunctions.pfnChangePitch = pfnChangePitch; + this->engineFunctions.pfnFindEntityByString = pfnFindEntityByString; + this->engineFunctions.pfnGetEntityIllum = pfnGetEntityIllum; + this->engineFunctions.pfnFindEntityInSphere = pfnFindEntityInSphere; + this->engineFunctions.pfnFindClientInPVS = pfnFindClientInPVS; + this->engineFunctions.pfnEntitiesInPVS = pfnEntitiesInPVS; + this->engineFunctions.pfnSetOrigin = pfnSetOrigin; + this->engineFunctions.pfnEmitSound = pfnEmitSound; + this->engineFunctions.pfnEmitAmbientSound = pfnEmitAmbientSound; + this->engineFunctions.pfnTraceSphere = pfnTraceSphere; + this->engineFunctions.pfnServerCommand = pfnServerCommand; + this->engineFunctions.pfnParticleEffect = pfnParticleEffect; + this->engineFunctions.pfnLightStyle = pfnLightStyle; + this->engineFunctions.pfnRandomLong = pfnRandomLong; this->engineFunctions.pfnAlertMessage = pfnAlertMessage; this->engineFunctions.pfnAllocString = pfnAllocString; this->engineFunctions.pfnPEntityOfEntOffset = pfnPEntityOfEntOffset; this->engineFunctions.pfnPEntityOfEntIndex = pfnPEntityOfEntIndex; + this->engineFunctions.pfnServerPrint = pfnServerPrint; this->engineFunctions.pfnGetGameDir = pfnGetGameDir; this->engineFunctions.pfnIsDedicatedServer = pfnIsDedicatedServer; + this->engineFunctions.pfnPrecacheEvent = pfnPrecacheEvent; + this->engineFunctions.pfnPlaybackEvent = pfnPlaybackEvent; this->engineFunctions.pfnIsCareerMatch = pfnIsCareerMatch; + this->engineFunctions.pfnQueryClientCvarValue = pfnQueryClientCvarValue; + this->engineFunctions.pfnQueryClientCvarValue2 = pfnQueryClientCvarValue2; + this->engineFunctions.pfnCheckParm = pfnCheckParm; this->engineFunctions.pfnPEntityOfEntIndexAllEntities = pfnPEntityOfEntIndexAllEntities; + this->dllFunctions.pfnServerActivate = ServerActivate; + // install the engine functions and global variables - g_engfuncs = this->engineFunctions; - extern globalvars_t* gpGlobals; - gpGlobals = &this->globalVariables; + ::g_engfuncs = this->engineFunctions; + ::gpGlobals = &this->globalVariables; this->globalVariables.maxClients = 32; this->globalVariables.pStringBase = new char[Engine::iStringTableSize]; - memset( (char *)this->globalVariables.pStringBase, 0, Engine::iStringTableSize); + memset( const_cast(this->globalVariables.pStringBase), 0, Engine::iStringTableSize ); // start allocating at offset 1 so that checks against string_t with value 0 work // TODO: is this how the engine works? this->iStringTableOffset = 1; - // TODO: edict_t * 0 is worldspawn? + // TODO: is edict_t* 0 is worldspawn? for( int i = 0; i <= this->globalVariables.maxClients; i++ ) { // TODO: player spawning should happen later - and call one of the server-side callbacks? shared_ptr edict = std::make_shared(); edict->free = 0; - edict->pvPrivateData = new char[1]; // TODO: should be CBasePlayer's data + edict->pvPrivateData = new (std::nothrow) char[1]; // TODO: should be CBasePlayer's data edict->v.classname = ALLOC_STRING("player"); edict->v.netname = 0; edict->v.flags = FL_CLIENT; @@ -52,7 +93,7 @@ namespace foolsgoldsource this->iMaxEdicts = 1024; } - Engine::~Engine() + Engine::~Engine() noexcept { for( unsigned int i = 0; i < this->edicts.size(); i++ ) { @@ -71,17 +112,27 @@ namespace foolsgoldsource } } - const enginefuncs_t Engine::GetServerEngineFunctions() + const enginefuncs_t Engine::GetServerEngineFunctions() const { return this->engineFunctions; } - const globalvars_t Engine::GetServerGlobalVariables() + const globalvars_t Engine::GetServerGlobalVariables() const { return this->globalVariables; } - const string Engine::GetGameDirectory() + const DLL_FUNCTIONS Engine::GetDLLFunctions() const + { + return this->dllFunctions; + } + + const NEW_DLL_FUNCTIONS Engine::GetNewDLLFunctions() const + { + return this->newDllFunctions; + } + + const string Engine::GetGameDirectory() const { return this->strGameDir; } @@ -91,7 +142,7 @@ namespace foolsgoldsource this->strGameDir = strGameDir; } - bool Engine::GetIsDedicatedServer() + bool Engine::GetIsDedicatedServer() const { return this->bIsDedicatedServer; } @@ -101,7 +152,7 @@ namespace foolsgoldsource this->bIsDedicatedServer = bIsDedicatedServer; } - bool Engine::GetIsCareerMatch() + bool Engine::GetIsCareerMatch() const { return this->bIsCareerMatch; } @@ -111,9 +162,9 @@ namespace foolsgoldsource this->bIsCareerMatch = bIsCareerMatch; } - void Engine::SetMaxClients( const int iMaxClients ) + void Engine::SetMaxClients( const unsigned int iMaxClients ) { - this->globalVariables.maxClients = iMaxClients; + this->globalVariables.maxClients = (signed int)iMaxClients; } string Util::tolowercase( const string& str ) @@ -123,7 +174,7 @@ namespace foolsgoldsource for( unsigned int i = 0; i < str.length(); i++ ) { // not ideal but this is how the engine would be doing it - lowerCased[i] = tolower( str[i] ); + lowerCased[i] = tolower(str[i]); } return lowerCased; @@ -133,35 +184,53 @@ namespace foolsgoldsource // Stubbed enginefuncs_t below // ///////////////////////////////// - int pfnPrecacheModel( char* s ) + int pfnPrecacheModel(char* s) { - printf( "Precache %s\n", s ); + printf("Precaching %s\n", s); - gEngine.models.push_back( string( s ) ); + // TODO: store more than just a string + gEngine.models.push_back(string(s)); return gEngine.models.size() - 1; } - int pfnPrecacheSound( char* s ) + int pfnPrecacheSound(char* s) { - printf( "Precache %s\n", s ); + printf("Precaching %s\n", s); - gEngine.sounds.push_back( string( s ) ); + gEngine.sounds.push_back(string(s)); return gEngine.sounds.size() - 1; } - void pfnSetModel( edict_t* e, const char* m ) + void pfnSetModel(edict_t* e, const char* m) { // TODO: excessive calls will overflow the string table - is this what the engine does? e->v.model = ALLOC_STRING(m); } - int pfnModelIndex( const char* m ) + void pfnSetOrigin(edict_t* e, const float* rgflOrigin) { - for( unsigned int i = 0; i < gEngine.models.size(); i++ ) + e->v.origin[0] = rgflOrigin[0]; + e->v.origin[1] = rgflOrigin[1]; + e->v.origin[2] = rgflOrigin[2]; + } + + void pfnEmitSound(edict_t* entity, int channel, const char* sample, float volume, float attenuation, int fFlags, int pitch) + { + // TODO: check if sound exists + } + + void pfnEmitAmbientSound(edict_t* entity, float* pos, const char* samp, float vol, float attenuation, int fFlags, int pitch) + { + // TODO: check if sound exists + } + + int pfnModelIndex(const char* m) + { + for (unsigned int i = 0; i < gEngine.models.size(); i++) { - if( gEngine.models[i] == Util::tolowercase( string(m) ) ) + if (gEngine.models[i] == Util::tolowercase(string(m))) { return i; } @@ -171,7 +240,15 @@ namespace foolsgoldsource return -1; } - void pfnSetSize( edict_t* e, const float* rgflMin, const float* rgflMax ) + int pfnModelFrames(int modelIndex) + { + string model = gEngine.models[modelIndex]; + + // TODO: return the correct value + return 0; + } + + void pfnSetSize(edict_t* e, const float* rgflMin, const float* rgflMax) { // TODO: check this e->v.mins.x = rgflMin[0]; @@ -182,39 +259,141 @@ namespace foolsgoldsource e->v.maxs.z = rgflMax[2]; } - void pfnAlertMessage( ALERT_TYPE atype, char *szFmt, ... ) + void pfnChangeLevel(char* s1, char* s2) { - printf( "%s", szFmt ); + // TODO: set this->globalVariables.mapname } - edict_t* pfnPEntityOfEntOffset( int iEntOffset ) + void pfnGetSpawnParms(edict_t* ent) { - if( (unsigned int)iEntOffset >= gEngine.edicts.size() ) - { - return nullptr; - } - else - { - return gEngine.edicts[iEntOffset].get(); - } + // TODO: does nothing? + return; + } + + void pfnSaveSpawnParms(edict_t* ent) + { + // TODO: does nothing? + return; + } + + float pfnVecToYaw(const float* rgflVector) + { + // TODO: + return 0.0f; + } + + void pfnVecToAngles(const float* rgflVectorIn, float* rgflVectorOut) + { + // TODO: + } + + void pfnMoveToOrigin( edict_t* ent, const float* pflGoal, float dist, int iMoveType ) + { + ent->v.origin.x = pflGoal[0]; + ent->v.origin.y = pflGoal[1]; + ent->v.origin.z = pflGoal[2]; + } + + void pfnChangeYaw( edict_t* ent ) + { + // TODO: + } + + void pfnChangePitch( edict_t* ent ) + { + // TODO: + } + + edict_t* pfnFindEntityByString( edict_t* pEdictStartSearchAfter, const char* pszField, const char* pszValue ) + { + return nullptr; + } + + int pfnGetEntityIllum( edict_t* pEnt ) + { + return 0; + } + + edict_t* pfnFindEntityInSphere( edict_t* pEdictStartSearchAfter, const float* org, float rad ) + { + return nullptr; + } + + edict_t* pfnFindClientInPVS( edict_t* pEdict ) + { + return nullptr; + } + + edict_t* pfnEntitiesInPVS( edict_t* pplayer ) + { + return nullptr; } - int pfnAllocString( const char* szValue ) + void pfnTraceSphere( const float* v1, const float* v2, int fNoMonsters, float radius, edict_t* pentToSkip, TraceResult* ptr ) + { + fprintf( stderr, "Not implemented\n" ); + } + + void pfnServerCommand( char* str ) + { + gEngine.serverCommands.push_back( string(str) ); + } + + void pfnParticleEffect( const float* org, const float* dir, float color, float count ) + { + // TODO: do nothing? + } + + void pfnLightStyle( int style, char* val ) + { + // TODO: do nothing? + } + + int32 pfnRandomLong( int32 lLow, int32 lHigh ) + { + return rand() % lHigh + lLow; + } + + void pfnAlertMessage( ALERT_TYPE atype, char *szFmt, ... ) + { + va_list argptr; + char buffer[1024]; + + va_start( argptr, szFmt ); + vsprintf( buffer, szFmt, argptr ); + va_end( argptr ); + + printf( "%s", buffer); + } + + int pfnAllocString(const char* szValue) { globalvars_t globalVars = gEngine.GetServerGlobalVariables(); // get the next unassigned part of the string table const char* pCurrentOffset = globalVars.pStringBase + gEngine.iStringTableOffset; // copy the new string to the next unassigned part of the string table - strcpy( (char *)pCurrentOffset, szValue ); + strcpy((char*)pCurrentOffset, szValue); // get the newly assigned string's location int iCurrentOffset = gEngine.iStringTableOffset; // update the location of the next unassigned part of the string table - gEngine.iStringTableOffset += strlen( szValue ); + gEngine.iStringTableOffset += strlen(szValue); // return the newly assigned string's location return iCurrentOffset; } + edict_t* pfnPEntityOfEntOffset( int iEntOffset ) + { + if( (unsigned int)iEntOffset >= gEngine.edicts.size() ) + { + return nullptr; + } + else + { + return gEngine.edicts[iEntOffset].get(); + } + } + edict_t* pfnPEntityOfEntIndex( int iEntIndex ) { edict_t* result; @@ -231,6 +410,11 @@ namespace foolsgoldsource return result; } + void pfnServerPrint( const char* szMsg ) + { + printf( "%s", szMsg ); + } + void pfnGetGameDir( char *szGetGameDir ) { strcpy( szGetGameDir, gEngine.GetGameDirectory().c_str() ); @@ -241,11 +425,57 @@ namespace foolsgoldsource return gEngine.GetIsDedicatedServer(); } + unsigned short pfnPrecacheEvent( int type, const char* psz ) + { + printf("Precaching %s\n", psz); + + event_t event; + // down-cast so that it's a valid index - should never have too many events anyway + event.iIndex = (unsigned short)gEngine.events.size(); + event.strEventFileName = string(psz); + event.iType = type; + + gEngine.events.push_back(event); + + return event.iIndex; + } + + void pfnPlaybackEvent( int flags, const edict_t* pInvoker, unsigned short eventindex, float delay, float* origin, float* angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ) + { + bool bEventFound = false; + + for (unsigned int i = 0; i < gEngine.events.size(); i++) + { + if (gEngine.events[i].iIndex == eventindex) + { + bEventFound = true; + } + } + + if (!bEventFound) + { + // TODO: error out? + } + } + int pfnIsCareerMatch( void ) { return gEngine.GetIsCareerMatch(); } + void pfnQueryClientCvarValue( const edict_t* player, const char* cvarName ) + { + } + + void pfnQueryClientCvarValue2( const edict_t* player, const char* cvarName, int requestID ) + { + } + + int pfnCheckParm( const char* pchCmdLineToken, char** ppnext ) + { + return 0; + } + edict_t* pfnPEntityOfEntIndexAllEntities( int iEntIndex ) { edict_t* result; @@ -261,4 +491,8 @@ namespace foolsgoldsource return result; } + + void ServerActivate( edict_t* pEdictList, int edictCount, int clientMax ) + { + } } \ No newline at end of file diff --git a/dlls/foolsgoldsource.h b/dlls/foolsgoldsource.h index ecee854..43e3a90 100644 --- a/dlls/foolsgoldsource.h +++ b/dlls/foolsgoldsource.h @@ -1,3 +1,13 @@ +//============================================================================= +// +// Fool's GoldSource - GoldSource engine mock +// +// http://www.teamsandpit.com/ +// +// Notes: engine mock code +// +//============================================================================= + #ifndef _STUB_ENGINE_H_ #define _STUB_ENGINE_H_ @@ -19,23 +29,32 @@ using std::vector; namespace foolsgoldsource { + struct event_t + { + unsigned short iIndex; + string strEventFileName; + int iType; + }; + class Engine { public: Engine(); - ~Engine(); + ~Engine() noexcept; - const enginefuncs_t GetServerEngineFunctions(); - const globalvars_t GetServerGlobalVariables(); + const enginefuncs_t GetServerEngineFunctions() const; + const globalvars_t GetServerGlobalVariables() const; + const DLL_FUNCTIONS GetDLLFunctions() const; + const NEW_DLL_FUNCTIONS GetNewDLLFunctions() const; - const string GetGameDirectory(); + const string GetGameDirectory() const; void SetGameDirectory( const string& strGameDir ); - bool GetIsDedicatedServer(); + bool GetIsDedicatedServer() const; void SetIsDedicatedServer( const bool bIsDedicatedServer ); - bool GetIsCareerMatch(); + bool GetIsCareerMatch() const; void SetIsCareerMatch( const bool bIsCareerMatch); - void SetMaxClients( const int iMaxClients ); + void SetMaxClients( const unsigned int iMaxClients ); // below shouldn't be public because the game doesn't have access to them @@ -43,7 +62,9 @@ namespace foolsgoldsource vector> edicts; vector models; vector sounds; + vector events; int iMaxEdicts; + vector serverCommands; // TODO: how does the engine track this? unsigned int iStringTableOffset; @@ -52,6 +73,8 @@ namespace foolsgoldsource private: enginefuncs_t engineFunctions; globalvars_t globalVariables; + DLL_FUNCTIONS dllFunctions; + NEW_DLL_FUNCTIONS newDllFunctions; string strGameDir; @@ -67,12 +90,39 @@ namespace foolsgoldsource extern Engine gEngine; + // enginefuncs_t int pfnPrecacheModel( char* s ); int pfnPrecacheSound( char* s ); void pfnSetModel( edict_t* e, const char* m ); int pfnModelIndex( const char* m ); - + int pfnModelFrames( int modelIndex ); void pfnSetSize( edict_t* e, const float* rgflMin, const float* rgflMax ); + void pfnChangeLevel( char* s1, char* s2 ); + void pfnGetSpawnParms( edict_t* ent ); + void pfnSaveSpawnParms( edict_t* ent ); + float pfnVecToYaw( const float* rgflVector ); + void pfnVecToAngles( const float* rgflVectorIn, float* rgflVectorOut ); + void pfnMoveToOrigin( edict_t* ent, const float* pflGoal, float dist, int iMoveType ); + void pfnChangeYaw( edict_t* ent ); + void pfnChangePitch( edict_t* ent ); + edict_t* pfnFindEntityByString( edict_t* pEdictStartSearchAfter, const char* pszField, const char* pszValue ); + int pfnGetEntityIllum( edict_t* pEnt ); + edict_t* pfnFindEntityInSphere( edict_t* pEdictStartSearchAfter, const float* org, float rad ); + edict_t* pfnFindClientInPVS( edict_t* pEdict ); + edict_t* pfnEntitiesInPVS( edict_t* pplayer ); + + void pfnSetOrigin( edict_t* e, const float* rgflOrigin ); + void pfnEmitSound( edict_t* entity, int channel, const char* sample, float volume, float attenuation, int fFlags, int pitch ); + void pfnEmitAmbientSound( edict_t* entity, float* pos, const char* samp, float vol, float attenuation, int fFlags, int pitch ); + + void pfnTraceSphere( const float* v1, const float* v2, int fNoMonsters, float radius, edict_t* pentToSkip, TraceResult* ptr ); + + void pfnServerCommand( char* str ); + + void pfnParticleEffect( const float* org, const float* dir, float color, float count ); + void pfnLightStyle( int style, char* val ); + + int32 pfnRandomLong( int32 lLow, int32 lHigh ); void pfnAlertMessage( ALERT_TYPE atype, char *szFmt, ... ); @@ -82,13 +132,24 @@ namespace foolsgoldsource edict_t* pfnPEntityOfEntIndex( int iEntIndex ); + void pfnServerPrint( const char* szMsg ); + void pfnGetGameDir( char *szGetGameDir ); int pfnIsDedicatedServer( void ); + unsigned short pfnPrecacheEvent( int type, const char* psz ); + void pfnPlaybackEvent( int flags, const edict_t* pInvoker, unsigned short eventindex, float delay, float* origin, float* angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); + int pfnIsCareerMatch( void ); + void pfnQueryClientCvarValue( const edict_t* player, const char* cvarName ); + void pfnQueryClientCvarValue2( const edict_t* player, const char* cvarName, int requestID ); + int pfnCheckParm( const char* pchCmdLineToken, char** ppnext ); edict_t* pfnPEntityOfEntIndexAllEntities( int iEntIndex ); + + // DLL_FUNCTIONS + void ServerActivate( edict_t* pEdictList, int edictCount, int clientMax ); } #endif // _STUB_ENGINE_H_ \ No newline at end of file diff --git a/dlls/game.cpp b/dlls/game.cpp index db7b53b..2820249 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: game rules code +// +//============================================================================= + #include "extdll.h" #include "util.h" #include "cbase.h" @@ -49,7 +61,7 @@ unsigned int Game::BotsOnTeam( const int team ) const { int iOnTeam = 0; - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { if( pGame->GetTeam( pBots[i]->pEdict ) == team ) { @@ -111,7 +123,8 @@ int Game::GetTeam( const edict_t *pEdict ) const } } - infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)( const_cast(pEdict) ); // does pfnGetInfoKeyBuffer modify the edict_t*? + // TODO: does pfnGetInfoKeyBuffer modify the edict_t*? + infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)( const_cast(pEdict) ); strcpy(model_name, (g_engfuncs.pfnInfoKeyValue(infobuffer, "model"))); for (int index=0; index < num_teams; index++) @@ -130,7 +143,9 @@ bool Game::IsGunmanChronicles() const void Game::GetSaveGameComment( char *pBuffer, int iMaxLength ) const { + ALERT( at_console, "Setting save game comment to %s\n", STRING(gpGlobals->mapname) ); strncpy( pBuffer, STRING(gpGlobals->mapname), iMaxLength ); + ALERT( at_console, "Save game comment is %s\n", pBuffer ); } bool Game::UseToOpenDoor() const @@ -141,233 +156,234 @@ bool Game::UseToOpenDoor() const void ValveGame::GetSaveGameComment( char *pBuffer, int iMaxLength ) const { // TODO: does GameUI.dll localise these names? - if( !strncmp( STRING( gpGlobals->mapname ), "t0a0", strlen( "t0a0" ) ) ) // a, b, b1, b2, c, d + if( !strncmp( STRING(gpGlobals->mapname), "t0a0", strlen( "t0a0" ) ) ) // a, b, b1, b2, c, d { strncpy( pBuffer, "HAZARD COURSE", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c0a0", strlen( "c0a0" ) ) ) // a, b, c, d, e + else if( !strncmp( STRING(gpGlobals->mapname), "c0a0", strlen( "c0a0" ) ) ) // a, b, c, d, e { strncpy( pBuffer, "BLACK MESA INBOUND", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a0" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a0" ) ) { strncpy( pBuffer, "ANOMOLOUS MATERIALS", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a0a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a0a" ) ) { strncpy( pBuffer, "ANOMOLOUS MATERIALS", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a0b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a0b" ) ) { strncpy( pBuffer, "ANOMOLOUS MATERIALS", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a0c" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a0c" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a0d" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a0d" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a0e" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a0e" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a1" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a1" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a1a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a1a" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a1b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a1b" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a1c" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a1c" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a1d" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a1d" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c1a1f" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c1a1f" ) ) { strncpy( pBuffer, "UNFORSEEN CONSEQUENCES", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c1a2", strlen( "c1a2" ) ) ) // a, b, c, d + else if( !strncmp( STRING(gpGlobals->mapname), "c1a2", strlen( "c1a2" ) ) ) // a, b, c, d { strncpy( pBuffer, "OFFICE COMPLEX", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c1a3", strlen( "c1a3" ) ) ) // a, b, c, d + else if( !strncmp( STRING(gpGlobals->mapname), "c1a3", strlen( "c1a3" ) ) ) // a, b, c, d { strncpy( pBuffer, "\"WE'VE GOT HOSTILES\"", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c1a4", strlen( "c1a4" ) ) ) // b, d, e, f, g, i, j, k + else if( !strncmp( STRING(gpGlobals->mapname), "c1a4", strlen( "c1a4" ) ) ) // b, d, e, f, g, i, j, k { strncpy( pBuffer, "BLAST PIT", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c2a1", strlen( "c2a1" ) ) ) // a, b + else if( !strncmp( STRING(gpGlobals->mapname), "c2a1", strlen( "c2a1" ) ) ) // a, b { strncpy( pBuffer, "POWER UP", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c2a2" , strlen( "c2a2" ) ) ) // a, b1, b2, c, d, e, f, g, h + else if( !strncmp( STRING(gpGlobals->mapname), "c2a2" , strlen( "c2a2" ) ) ) // a, b1, b2, c, d, e, f, g, h { strncpy( pBuffer, "ON A RAIL", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a3" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a3" ) ) { strncpy( pBuffer, "APPREHENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a3a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a3a" ) ) { strncpy( pBuffer, "APPREHENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a3b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a3b" ) ) { strncpy( pBuffer, "APPREHENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a3c" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a3c" ) ) { strncpy( pBuffer, "APPREHENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a3d" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a3d" ) ) { strncpy( pBuffer, "APPREHENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a3e" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a3e" ) ) { strncpy( pBuffer, "APPREHENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4" ) ) { strncpy( pBuffer, "RESIDUE PROCESSING", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4a" ) ) { strncpy( pBuffer, "RESIDUE PROCESSING", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4b" ) ) { strncpy( pBuffer, "RESIDUE PROCESSING", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4c" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4c" ) ) { strncpy( pBuffer, "RESIDUE PROCESSING", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4d" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4d" ) ) { strncpy( pBuffer, "QUESTIONABLE ETHICS", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4e" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4e" ) ) { strncpy( pBuffer, "QUESTIONABLE ETHICS", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4f" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4f" ) ) { strncpy( pBuffer, "QUESTIONABLE ETHICS", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c2a4g" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c2a4g" ) ) { strncpy( pBuffer, "QUESTIONABLE ETHICS", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c2a5", strlen( "c2a5" ) ) ) // a, b, c, d, e, f, g, w, x + else if( !strncmp( STRING(gpGlobals->mapname), "c2a5", strlen( "c2a5" ) ) ) // a, b, c, d, e, f, g, w, x { strncpy( pBuffer, "SURFACE TENSION", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a1" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a1" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a1a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a1a" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a1b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a1b" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2a" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2b" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2c" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2c" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2d" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2d" ) ) { strncpy( pBuffer, "\"FORGET ABOUT FREEMAN\"", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2e" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2e" ) ) { strncpy( pBuffer, "LAMBDA CORE", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c3a2f" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c3a2f" ) ) { strncpy( pBuffer, "LAMBDA CORE", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1" ) ) { strncpy( pBuffer, "XEN", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1a" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1a" ) ) { strncpy( pBuffer, "INTERLOPER", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1b" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1b" ) ) { strncpy( pBuffer, "INTERLOPER", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1c" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1c" ) ) { strncpy( pBuffer, "INTERLOPER", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1d" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1d" ) ) { strncpy( pBuffer, "INTERLOPER", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1e" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1e" ) ) { strncpy( pBuffer, "INTERLOPER", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a1f" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a1f" ) ) { strncpy( pBuffer, "INTERLOPER", iMaxLength ); } - else if( !strncmp( STRING( gpGlobals->mapname ), "c4a2", strlen( "c4a2" ) ) ) // a, b + else if( !strncmp( STRING(gpGlobals->mapname), "c4a2", strlen( "c4a2" ) ) ) // a, b { strncpy( pBuffer, "GONARCH'S LAIR", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c4a3" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c4a3" ) ) { strncpy( pBuffer, "NIHILANTH", iMaxLength ); } - else if( !strcmp( STRING( gpGlobals->mapname ), "c5a1" ) ) + else if( !strcmp( STRING(gpGlobals->mapname), "c5a1" ) ) { strncpy( pBuffer, "NIHILANTH", iMaxLength ); } else { - strncpy( pBuffer, STRING( gpGlobals->mapname ), iMaxLength ); + strncpy( pBuffer, STRING(gpGlobals->mapname), iMaxLength ); } } int GearboxGame::GetTeam( const edict_t *pEdict ) const { - if( this->IsCTF() || !this->IsCapturePoint() ) + if( this->IsCTF() || this->IsCapturePoint() ) { - char *infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)( const_cast(pEdict) ); // does pfnGetInfoKeyBuffer modify the edict_t*? + // TODO: does pfnGetInfoKeyBuffer modify the edict_t*? + char *infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)( const_cast(pEdict) ); char szModelName[32]; strcpy( szModelName, g_engfuncs.pfnInfoKeyValue(infobuffer, "model") ); diff --git a/dlls/game.h b/dlls/game.h index 9d96490..c3c1cde 100644 --- a/dlls/game.h +++ b/dlls/game.h @@ -2,9 +2,11 @@ // // Sandbot - GoldSource engine multiplayer bot // +// Based on HPB_Bot by Jeffrey "botman" Broome +// // http://www.teamsandpit.com/ // -// Notes: +// Notes: game rules code // //============================================================================= @@ -31,6 +33,8 @@ class Game virtual void GetSaveGameComment( char *pBuffer, int iMaxLength ) const; virtual bool IsGunmanChronicles() const; + + const static int MAX_PLAYERS = 32; }; class ValveGame : public Game @@ -173,6 +177,8 @@ class TFCGame : public Game } virtual int GetTeam( const edict_t *pEdict ) const; + + const static int MAX_BACKPACKS = 100; }; class NSGame : public Game @@ -214,7 +220,7 @@ class NSGame : public Game void StartRound() const { - for( int i = 0; i < MAX_PLAYERS; i++ ) + for( int i = 0; i < Game::MAX_PLAYERS; i++ ) { pBots[i]->not_started = true; diff --git a/dlls/game_test.cpp b/dlls/game_test.cpp new file mode 100644 index 0000000..67c7476 --- /dev/null +++ b/dlls/game_test.cpp @@ -0,0 +1,33 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: game rules test code +// +//============================================================================= + +#include "CppUnitTest.h" + +#include "extdll.h" +#include "bot.h" +#include "game.h" +#include "foolsgoldsource.h" +#include "test.h" + +namespace tests +{ + TEST_CLASS(game_test) + { + public: + + TEST_METHOD(TestIsValidEdict) + { + std::unique_ptr pGame = std::make_unique(); + Assert::AreEqual( false, pGame->IsValidEdict( nullptr ) ); + } + }; +} diff --git a/dlls/h_export.cpp b/dlls/h_export.cpp index d2c7762..00ca79f 100644 --- a/dlls/h_export.cpp +++ b/dlls/h_export.cpp @@ -39,8 +39,6 @@ globalvars_t *gpGlobals; char g_szLibraryPath[64]; -extern DLL_FUNCTIONS other_gFunctionTable; - #ifndef __linux__ // required DLL entry point BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) @@ -507,14 +505,6 @@ extern "C" void GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_ return; } - extern bot_player_t g_valveBots[]; - extern bot_player_t g_gearboxBots[]; - extern bot_player_t g_dodBots[]; - extern bot_player_t g_gunmanBots[]; - extern bot_player_t g_nsBots[]; - extern bot_player_t g_hungerBots[]; - extern bot_player_t g_shipBots[]; - if( mod_id == VALVE_DLL || mod_id == TFC_DLL ) { pBotData = g_valveBots; diff --git a/dlls/h_export.h b/dlls/h_export.h index 024aaa5..70a405c 100644 --- a/dlls/h_export.h +++ b/dlls/h_export.h @@ -1,3 +1,15 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: engine hooking code +// +//============================================================================= + #ifndef _H_EXPORT_H_ #define _H_EXPORT_H_ diff --git a/dlls/h_export_test.cpp b/dlls/h_export_test.cpp index 79dc1f0..4fc314c 100644 --- a/dlls/h_export_test.cpp +++ b/dlls/h_export_test.cpp @@ -2,9 +2,11 @@ // // Sandbot - GoldSource engine multiplayer bot // +// Based on HPB_Bot by Jeffrey "botman" Broome +// // http://www.teamsandpit.com/ // -// Notes: +// Notes: engine hooking test code // //============================================================================= @@ -15,8 +17,6 @@ #include "foolsgoldsource.h" #include "test.h" -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - namespace tests { TEST_CLASS(h_export_test) diff --git a/dlls/metamod.h b/dlls/metamod.h index 7fd490c..ba60e0b 100644 --- a/dlls/metamod.h +++ b/dlls/metamod.h @@ -1,3 +1,14 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: MetaMod code +// +//============================================================================= void METAMOD_RETURN( META_RES result ); diff --git a/dlls/pseudomod.cpp b/dlls/pseudomod.cpp new file mode 100644 index 0000000..9410d69 --- /dev/null +++ b/dlls/pseudomod.cpp @@ -0,0 +1,15 @@ +//============================================================================= +// +// Pseudo Mod - replacement for MetaMod +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: +// +//============================================================================= + +#include "pseudomod.h" + +pseudo_state* gGlobalState; \ No newline at end of file diff --git a/dlls/pseudomod.h b/dlls/pseudomod.h new file mode 100644 index 0000000..1486ddd --- /dev/null +++ b/dlls/pseudomod.h @@ -0,0 +1,30 @@ +//============================================================================= +// +// Pseudo Mod - replacement for MetaMod +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: +// +//============================================================================= + +enum FUNCTION_USE +{ + IGNORE = 1, + CALL_AND_OVERRIDE = 3, + SKIP_AND_OVERRIDE = 4 +}; + +struct pseudo_state +{ + FUNCTION_USE result; +}; + +extern pseudo_state *gGlobalState; + +inline void PSEUDO_RETURN(FUNCTION_USE result) +{ + gGlobalState->result = result; +} \ No newline at end of file diff --git a/dlls/sandbot-2019.vcxproj b/dlls/sandbot-2019.vcxproj index a375768..75ea50a 100644 --- a/dlls/sandbot-2019.vcxproj +++ b/dlls/sandbot-2019.vcxproj @@ -76,6 +76,7 @@ Level3 true ProgramDatabase + StreamingSIMDExtensions2 NDEBUG;%(PreprocessorDefinitions) @@ -123,6 +124,7 @@ Level3 true ProgramDatabase + StreamingSIMDExtensions2 _DEBUG;%(PreprocessorDefinitions) @@ -164,14 +166,17 @@ + + + @@ -186,6 +191,7 @@ + diff --git a/dlls/sandbot-2019.vcxproj.filters b/dlls/sandbot-2019.vcxproj.filters index 458c41d..adde995 100644 --- a/dlls/sandbot-2019.vcxproj.filters +++ b/dlls/sandbot-2019.vcxproj.filters @@ -90,6 +90,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -143,5 +152,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/dlls/test.h b/dlls/test.h index 3b8eec1..97b3c91 100644 --- a/dlls/test.h +++ b/dlls/test.h @@ -2,23 +2,26 @@ // // Sandbot - GoldSource engine multiplayer bot // +// Based on HPB_Bot by Jeffrey "botman" Broome +// // http://www.teamsandpit.com/ // -// Notes: +// Notes: test code // //============================================================================= -#ifndef _TEST_H_ -#define _TEST_H_ +#ifndef __TEST_H__ +#define __TEST_H__ -// CppUnitTestFramework does something strange and breaks the engine's ability to load the .dll so stub it out when building for real -// #define TEST +using namespace Microsoft::VisualStudio::CppUnitTestFramework; -#ifndef TEST +// CppUnitTestFramework does something strange and breaks the engine's ability to load the .dll so stub it out when building normally +// test.ps1 creates a file called run_tests so that during the test script this block is skipped and vstest can run the tests +#if !__has_include("run_tests") #undef TEST_METHOD #define TEST_METHOD(methodName) static const void* test##methodName() { return nullptr; } void methodName() -#endif // TEST +#endif // !__has_include("run_tests") -#endif // _TEST_H_ \ No newline at end of file +#endif // __TEST_H__ \ No newline at end of file diff --git a/dlls/util.cpp b/dlls/util.cpp index fed8e6e..98e836b 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -34,9 +34,6 @@ #include #endif // __linux__ -extern char team_names[MAX_TEAMS][MAX_TEAMNAME_LENGTH]; -extern int num_teams; - int gmsgTextMsg = 0; int gmsgSayText = 0; int gmsgShowMenu = 0; @@ -268,7 +265,7 @@ int UTIL_GetClass(edict_t *pEntity) int UTIL_GetBotIndex(edict_t *pEdict) { - for (int index=0; index < MAX_PLAYERS; index++) + for (int index=0; index < Game::MAX_PLAYERS; index++) { if (pBots && pBots[index] && pBots[index]->pEdict == pEdict) { @@ -284,7 +281,7 @@ bot_t *UTIL_GetBotPointer( const edict_t *pEdict ) { int index; - for (index=0; index < MAX_PLAYERS; index++) + for (index=0; index < Game::MAX_PLAYERS; index++) { if (pBots && pBots[index] && pBots[index]->pEdict == pEdict) { @@ -292,8 +289,10 @@ bot_t *UTIL_GetBotPointer( const edict_t *pEdict ) } } - if (index < MAX_PLAYERS) - return (pBots[index]); + if (index < Game::MAX_PLAYERS) + { + return (pBots[index]); + } return nullptr; // return NULL if edict is not a bot } @@ -609,7 +608,7 @@ Vector UTIL_GetOrigin( edict_t *pEdict ) { if( !strncmp( STRING(pEdict->v.classname), "func_", 5 ) ) { - return VecBModelOrigin(pEdict); + return VecBModelOrigin( pEdict ); } return pEdict->v.origin; diff --git a/dlls/util_test.cpp b/dlls/util_test.cpp index e6f5a44..93b23d4 100644 --- a/dlls/util_test.cpp +++ b/dlls/util_test.cpp @@ -2,9 +2,11 @@ // // Sandbot - GoldSource engine multiplayer bot // +// Based on HPB_Bot by Jeffrey "botman" Broome +// // http://www.teamsandpit.com/ // -// Notes: +// Notes: utility test code // //============================================================================= @@ -15,14 +17,31 @@ #include "foolsgoldsource.h" #include "test.h" -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - namespace tests { TEST_CLASS(util_test) { public: + TEST_METHOD_INITIALIZE(SetUp) + { + CleanupGameAndBots(); + } + + TEST_METHOD(TestUTIL_GetBotIndex_NullBot) + { + int iIndex = UTIL_GetBotIndex(nullptr); + + Assert::AreEqual( -1, iIndex ); + } + + TEST_METHOD(TestUTIL_UTIL_GetBotPointer_NullBot) + { + bot_t *pBot = UTIL_GetBotPointer(nullptr); + + Assert::IsNull( pBot ); + } + TEST_METHOD(TestUTIL_ToLower) { char szTest[16] = "TEST"; @@ -30,13 +49,43 @@ namespace tests pTest = UTIL_ToLower(pTest); - Assert::AreEqual( pTest, "test" ); + Assert::AreEqual( "test", pTest ); + } + + TEST_METHOD(TestIsAlive_Dead) + { + edict_t* pPlayer = foolsgoldsource::gEngine.edicts[1].get(); + pPlayer->v.deadflag = DEAD_DYING; + pPlayer->v.health = 0; + pPlayer->v.flags = 0; + + Assert::AreEqual( false, IsAlive( pPlayer ) ); + } + + TEST_METHOD(TestIsAlive_Alive) + { + edict_t* pPlayer = foolsgoldsource::gEngine.edicts[1].get(); + pPlayer->v.deadflag = DEAD_NO; + pPlayer->v.health = 100; + pPlayer->v.flags = 0; + + Assert::AreEqual( true, IsAlive( pPlayer ) ); + } + + TEST_METHOD(TestIsAlive_NoTarget) + { + edict_t* pPlayer = foolsgoldsource::gEngine.edicts[1].get(); + pPlayer->v.deadflag = DEAD_NO; + pPlayer->v.health = 100; + pPlayer->v.flags = FL_NOTARGET; + + Assert::AreEqual( false, IsAlive( pPlayer ) ); } TEST_METHOD(TestIsValidEntity_NULL) { - Assert::IsFalse( IsValidEntity( nullptr ) ); - Assert::IsFalse( IsValidEntity( NULL ) ); + Assert::AreEqual( false, IsValidEntity( nullptr ) ); + Assert::AreEqual( false, IsValidEntity( NULL ) ); } TEST_METHOD(TestIsValidEntity_Free) @@ -44,7 +93,7 @@ namespace tests edict_t* pPlayer = foolsgoldsource::gEngine.edicts[1].get(); pPlayer->free = true; - Assert::IsFalse( IsValidEntity( pPlayer )); + Assert::AreEqual( false, IsValidEntity( pPlayer )); } TEST_METHOD(TestIsValidEntity_Kill) @@ -52,7 +101,19 @@ namespace tests edict_t* pPlayer = foolsgoldsource::gEngine.edicts[1].get(); pPlayer->v.flags &= FL_KILLME; - Assert::IsFalse( IsValidEntity( pPlayer ) ); + Assert::AreEqual( false, IsValidEntity( pPlayer ) ); + } + + TEST_METHOD(Test_IsBuilt) + { + edict_t* pEdict = foolsgoldsource::gEngine.edicts[1].get(); + pEdict->v.iuser4 |= MASK_BUILDABLE; + + Assert::AreEqual( false, UTIL_IsBuilt( pEdict ) ); + + pEdict->v.iuser4 &= ~MASK_BUILDABLE; + + Assert::AreEqual( true, UTIL_IsBuilt( pEdict ) ); } }; } diff --git a/dlls/waypoint.cpp b/dlls/waypoint.cpp index f830a3d..a6ba0f5 100644 --- a/dlls/waypoint.cpp +++ b/dlls/waypoint.cpp @@ -26,9 +26,7 @@ extern int m_spriteTexture; extern int num_backpacks; -extern BACKPACK_S backpacks[MAX_BACKPACKS]; - -extern vector capturePoints; +extern BACKPACK_S backpacks[TFCGame::MAX_BACKPACKS]; // waypoints with information bits (flags) waypoint_t waypoints[MAX_WAYPOINTS]; @@ -42,17 +40,18 @@ path_t *paths[MAX_WAYPOINTS]; // time that this waypoint was displayed (while editing) float wp_display_time[MAX_WAYPOINTS]; -bool g_waypoint_paths = FALSE; // have any paths been allocated? -bool g_waypoint_on = FALSE; -bool g_auto_waypoint = FALSE; -bool g_path_waypoint = FALSE; -bool g_path_waypoint_enable = TRUE; +bool g_waypoint_paths = false; // have any paths been allocated? +bool g_waypoint_on = false; +bool g_auto_waypoint = false; +bool g_path_waypoint_enable = true; Vector last_waypoint; float f_path_time = 0.0; +const unsigned int SHORTEST_PATH_ARRAY_LENGTH = 4; + unsigned int route_num_waypoints; -unsigned short *shortest_path[4] = {NULL, NULL, NULL, NULL}; -unsigned short *from_to[4] = {NULL, NULL, NULL, NULL}; +unsigned short *shortest_path[SHORTEST_PATH_ARRAY_LENGTH] = {NULL, NULL, NULL, NULL}; +unsigned short *from_to[SHORTEST_PATH_ARRAY_LENGTH] = {NULL, NULL, NULL, NULL}; void WaypointDebug(void) { @@ -104,13 +103,11 @@ void WaypointFree(void) // initialize the waypoint structures... void WaypointInit() { - int i; - - // have any waypoint path nodes been allocated yet? + // have any waypoint path nodes been allocated yet? if (g_waypoint_paths) WaypointFree(); // must free previously allocated path memory - for (i=0; i < 4; i++) + for (unsigned int i=0; i < SHORTEST_PATH_ARRAY_LENGTH; i++) { if (shortest_path[i] != NULL) free(shortest_path[i]); @@ -119,7 +116,7 @@ void WaypointInit() free(from_to[i]); } - for (i=0; i < MAX_WAYPOINTS; i++) + for (unsigned int i=0; i < MAX_WAYPOINTS; i++) { waypoints[i].flags = 0; waypoints[i].origin = Vector(0,0,0); @@ -135,7 +132,7 @@ void WaypointInit() last_waypoint = Vector(0,0,0); - for (i=0; i < 4; i++) + for (unsigned int i=0; i < SHORTEST_PATH_ARRAY_LENGTH; i++) { shortest_path[i] = NULL; from_to[i] = NULL; @@ -159,7 +156,7 @@ void WaypointAddPath(short int add_index, short int path_index) while (i < MAX_PATH_INDEX) { - if (p->index[i] == -1) + if (p->index[i] == WAYPOINT_NOT_FOUND) { p->index[i] = path_index; @@ -186,9 +183,9 @@ void WaypointAddPath(short int add_index, short int path_index) } p->index[0] = path_index; - p->index[1] = -1; - p->index[2] = -1; - p->index[3] = -1; + p->index[1] = WAYPOINT_NOT_FOUND; + p->index[2] = WAYPOINT_NOT_FOUND; + p->index[3] = WAYPOINT_NOT_FOUND; p->next = NULL; if (prev != NULL) @@ -220,7 +217,7 @@ void WaypointDeletePath(short int del_index) { if (p->index[i] == del_index) { - p->index[i] = -1; // unassign this path + p->index[i] = WAYPOINT_NOT_FOUND; // unassign this path } i++; @@ -254,7 +251,7 @@ void WaypointDeletePath(short int path_index, short int del_index) { if (p->index[i] == del_index) { - p->index[i] = -1; // unassign this path + p->index[i] = WAYPOINT_NOT_FOUND; // unassign this path } i++; @@ -294,7 +291,7 @@ int WaypointFindPath(path_t **pPath, int *path_index, int waypoint_index, int te { while (*path_index < MAX_PATH_INDEX) { - if ((*pPath)->index[*path_index] != -1) // found a path? + if ((*pPath)->index[*path_index] != WAYPOINT_NOT_FOUND) // found a path? { // save the return value index = (*pPath)->index[*path_index]; @@ -326,7 +323,7 @@ int WaypointFindPath(path_t **pPath, int *path_index, int waypoint_index, int te #endif } - return -1; + return WAYPOINT_NOT_FOUND; } // find the nearest waypoint to the player and return the index (-1 if not found) @@ -338,11 +335,11 @@ int WaypointFindNearest(const edict_t *pEntity, float range, int team ) TraceResult tr; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find the nearest waypoint... - min_index = -1; + min_index = WAYPOINT_NOT_FOUND; min_distance = 9999.0; for (i=0; i < num_waypoints; i++) @@ -354,8 +351,7 @@ int WaypointFindNearest(const edict_t *pEntity, float range, int team ) continue; // skip any aiming waypoints // skip this waypoint if it's team specific and teams don't match... - if ((team != -1) && (waypoints[i].flags & W_FL_TEAM_SPECIFIC) && - ((waypoints[i].flags & W_FL_TEAM) != team)) + if ((team != -1) && (waypoints[i].flags & W_FL_TEAM_SPECIFIC) && ((waypoints[i].flags & W_FL_TEAM) != team)) continue; distance = (waypoints[i].origin - pEntity->v.origin).Length(); @@ -387,11 +383,11 @@ int WaypointFindNearest(const edict_t *pEntity, float range, int team, const Vec TraceResult tr; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find the nearest waypoint... - min_index = -1; + min_index = WAYPOINT_NOT_FOUND; min_distance = 9999.0; for (index=0; index < num_waypoints; index++) @@ -434,11 +430,11 @@ int WaypointFindNearest( const edict_t *pEntity, float range, int team, const Ve TraceResult tr; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find the nearest waypoint... - min_index = -1; + min_index = WAYPOINT_NOT_FOUND; min_distance = 9999.0; for (index = 0; index < num_waypoints; index++) @@ -497,6 +493,33 @@ edict_t *FindNearest( const Vector point, const char *szClassname, float fMinimu return pNearest; } +edict_t *FindNearestTriggerCtfGeneric( const Vector point, const char *szClassname, float fMinimumDistance = 9999.99 ) +{ + edict_t *pent = nullptr; + edict_t *pNearest = nullptr; + + while( (pent = UTIL_FindEntityByClassname( pent, szClassname )) != nullptr) + { + // skip trigger_ctfgeneric with a targetname/without a triggerstate + if( !strcmp( szClassname, "trigger_ctfgeneric" ) && strlen( STRING(pent->v.targetname) ) > 0 ) + { + ALERT( at_console, "Skipping %s as it probably doesn't have a triggerstate attribute\n", STRING(pent->v.targetname) ); + continue; + } + + float fDistance = (pent->v.origin - point).Length(); + + // is this the closest? + if (fDistance < fMinimumDistance) + { + fMinimumDistance = fDistance; + pNearest = pent; + } + } + + return pNearest; +} + float DistanceToNearest(const Vector& point, const char *szClassname) { edict_t *pent = NULL; @@ -580,9 +603,11 @@ bool ShouldSkip( const edict_t *pPlayer, int index ) } } } - else if( mod_id == GEARBOX_DLL && pGame->IsCapturePoint() && (waypoints[index].flags == W_FL_OP4_CAPTURE_POINT_BM || waypoints[index].flags == W_FL_OP4_CAPTURE_POINT_OF) ) + else if( mod_id == GEARBOX_DLL && pGame->IsCapturePoint() && (waypoints[index].flags == W_FL_OP4_CAPTURE_POINT) ) { - edict_t *pNearestCapturePoint = FindNearest(waypoints[index].origin, "trigger_ctfgeneric"); + edict_t *pNearestCapturePoint = FindNearestTriggerCtfGeneric(waypoints[index].origin, "trigger_ctfgeneric"); + + ALERT(at_console, "near a trigger_ctfgeneric\n"); // if there's not a nearby trigger_ctfgeneric if( !pNearestCapturePoint ) @@ -590,6 +615,8 @@ bool ShouldSkip( const edict_t *pPlayer, int index ) return false; } + ALERT(at_console, "really near a trigger_ctfgeneric\n"); + // if the trigger_ctfgeneric nearest this waypoint already belongs to the same team as the player if( !pBot->ShouldCapturePoint( pNearestCapturePoint ) ) { @@ -667,7 +694,7 @@ int WaypointFindNearestGoal(const edict_t *pEntity, int src, int team, uint64_t // find the nearest waypoint with the matching flags... - min_index = -1; + min_index = WAYPOINT_NOT_FOUND; min_distance = 99999; for (index=0; index < num_waypoints; index++) @@ -713,11 +740,11 @@ int WaypointFindNearestGoal(const edict_t *pEntity, int src, int team, uint64_t int exclude_index; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find the nearest waypoint with the matching flags... - min_index = -1; + min_index = WAYPOINT_NOT_FOUND; min_distance = 99999; for (index=0; index < num_waypoints; index++) @@ -773,11 +800,11 @@ int WaypointFindNearestGoal(const Vector& v_src, const edict_t *pEntity, float r int distance, min_distance; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find the nearest waypoint with the matching flags... - min_index = -1; + min_index = WAYPOINT_NOT_FOUND; min_distance = 99999; for (index=0; index < num_waypoints; index++) @@ -820,7 +847,7 @@ int WaypointFindRandomGoal(const edict_t *pEntity, int team, uint64_t flags) int count = 0; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find all the waypoints with the matching flags... @@ -849,7 +876,7 @@ int WaypointFindRandomGoal(const edict_t *pEntity, int team, uint64_t flags) } if (count == 0) // no matching waypoints found - return -1; + return WAYPOINT_NOT_FOUND; index = RANDOM_LONG(1, count) - 1; @@ -865,7 +892,7 @@ int WaypointFindRandomGoal(const edict_t *pEntity, int team, uint64_t flags, int int exclude_index; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find all the waypoints with the matching flags... @@ -906,7 +933,7 @@ int WaypointFindRandomGoal(const edict_t *pEntity, int team, uint64_t flags, int } if (count == 0) // no matching waypoints found - return -1; + return WAYPOINT_NOT_FOUND; index = RANDOM_LONG(1, count) - 1; @@ -922,7 +949,7 @@ int WaypointFindRandomGoal(const Vector& v_src, const edict_t *pEntity, float ra float distance; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // find all the waypoints with the matching flags... @@ -953,7 +980,7 @@ int WaypointFindRandomGoal(const Vector& v_src, const edict_t *pEntity, float ra } if (count == 0) // no matching waypoints found - return -1; + return WAYPOINT_NOT_FOUND; index = RANDOM_LONG(1, count) - 1; @@ -964,12 +991,12 @@ int WaypointFindRandomGoal(const Vector& v_src, const edict_t *pEntity, float ra int WaypointFindNearestAiming(const Vector& v_origin) { int index; - int min_index = -1; + int min_index = WAYPOINT_NOT_FOUND; int min_distance = 9999.0; float distance; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // search for nearby aiming waypoint... for (index=0; index < num_waypoints; index++) @@ -995,12 +1022,12 @@ int WaypointFindNearestAiming(const Vector& v_origin) int WaypointFindNearestWaypoint(const edict_t *pEntity, uint64_t type) { int index; - int min_index = -1; + int min_index = WAYPOINT_NOT_FOUND; int min_distance = 9999.0; float distance; if (num_waypoints < 1) - return -1; + return WAYPOINT_NOT_FOUND; // search for nearby waypoint... for (index=0; index < num_waypoints; index++) @@ -1189,35 +1216,11 @@ void WaypointSearchItems( edict_t *pEntity, const Vector& origin, int wpt_index if( !strcmp("trigger_ctfgeneric", nearest_name) ) { - for( unsigned int i = 0; i < capturePoints.size(); i++ ) - { - ALERT(at_console, "trigger_ctfgeneric target: %s targetname: %s globalname: %s\n", capturePoints[i].pEdict->v.target, capturePoints[i].pEdict->v.targetname, capturePoints[i].pEdict->v.globalname ); - // find the matching trigger_ctfgeneric that was saved on map load - they all have a target but not a globalname - if( FStrEq(STRING(nearest_pent->v.target), capturePoints[i].szTarget) && FStrEq(STRING(nearest_pent->v.globalname), "") ) - { - ALERT( at_console, "Found a matching trigger_ctfgeneric with target %s!\n", STRING(nearest_pent->v.target) ); - } - else - { - ALERT( at_console, "Mismatched trigger_ctfgeneric with target %s\n", STRING(nearest_pent->v.target) ); - continue; - } - - if( FStrEq(STRING(nearest_pent->v.target), capturePoints[i].szTarget) && capturePoints[i].iTeam == 1 ) - { - ALERT( at_console, "Making trigger_ctfgeneric with target %s a Black Mesa capture point\n", STRING(nearest_pent->v.target) ); - waypoints[wpt_index].flags |= W_FL_OP4_CAPTURE_POINT_BM; - } - else if( FStrEq(STRING(nearest_pent->v.target), capturePoints[i].szTarget) && capturePoints[i].iTeam == 2 ) - { - ALERT( at_console, "Making trigger_ctfgeneric with target %s an Opposing Force capture point\n", STRING(nearest_pent->v.target) ); - waypoints[wpt_index].flags |= W_FL_OP4_CAPTURE_POINT_OF; - } - } if( pEntity ) { ClientPrint( pEntity, HUD_PRINTCONSOLE, "Found a trigger_ctfgeneric\n" ); } + waypoints[wpt_index].flags |= W_FL_OP4_CAPTURE_POINT; } if ((strcmp("dod_object", nearest_name) == 0)) @@ -1378,14 +1381,11 @@ void WaypointAdd(edict_t *pEntity, int flags = 0) void WaypointAddAiming(edict_t *pEntity) { - int index; - edict_t *pent = NULL; + int index = 0; if (num_waypoints >= MAX_WAYPOINTS) return; - index = 0; - // find the next available slot for the new waypoint... while (index < num_waypoints) { @@ -1437,14 +1437,14 @@ void WaypointDelete(edict_t *pEntity) index = WaypointFindNearest(pEntity, 50.0, -1); - if (index == -1) + if (index == WAYPOINT_NOT_FOUND) return; if ((waypoints[index].flags & W_FL_SNIPER) || (waypoints[index].flags & W_FL_TFC_SENTRYGUN) || (waypoints[index].flags & W_FL_TFC_DISPENSER) || (waypoints[index].flags & W_FL_JUMP)) { int i; - int min_index = -1; + int min_index = WAYPOINT_NOT_FOUND; int min_distance = 9999.0; float distance; @@ -1466,7 +1466,7 @@ void WaypointDelete(edict_t *pEntity) } } - if (min_index != -1) + if (min_index != WAYPOINT_NOT_FOUND) { waypoints[min_index].flags = W_FL_DELETED; // not being used waypoints[min_index].origin = Vector(0,0,0); @@ -1525,14 +1525,14 @@ void WaypointUpdate( const edict_t *pEntity ) // allow player to manually create a path from one waypoint to another void WaypointCreatePath(edict_t *pEntity, int cmd) { - static int waypoint1 = -1; // initialized to unassigned - static int waypoint2 = -1; // initialized to unassigned + static int waypoint1 = WAYPOINT_NOT_FOUND; // initialized to unassigned + static int waypoint2 = WAYPOINT_NOT_FOUND; // initialized to unassigned if (cmd == 1) // assign source of path { waypoint1 = WaypointFindNearest(pEntity, 50.0, -1); - if (waypoint1 == -1) + if (waypoint1 == WAYPOINT_NOT_FOUND) { // play "cancelled" sound... EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0, ATTN_NORM, 0, 100); @@ -1550,7 +1550,7 @@ void WaypointCreatePath(edict_t *pEntity, int cmd) { waypoint2 = WaypointFindNearest(pEntity, 50.0, -1); - if ((waypoint1 == -1) || (waypoint2 == -1)) + if ((waypoint1 == WAYPOINT_NOT_FOUND) || (waypoint2 == WAYPOINT_NOT_FOUND)) { // play "error" sound... EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0, ATTN_NORM, 0, 100); @@ -1569,14 +1569,14 @@ void WaypointCreatePath(edict_t *pEntity, int cmd) // allow player to manually remove a path from one waypoint to another void WaypointRemovePath(edict_t *pEntity, int cmd) { - static int waypoint1 = -1; // initialized to unassigned - static int waypoint2 = -1; // initialized to unassigned + static int waypoint1 = WAYPOINT_NOT_FOUND; // initialized to unassigned + static int waypoint2 = WAYPOINT_NOT_FOUND; // initialized to unassigned if (cmd == 1) // assign source of path { waypoint1 = WaypointFindNearest(pEntity, 50.0, -1); - if (waypoint1 == -1) + if (waypoint1 == WAYPOINT_NOT_FOUND) { // play "cancelled" sound... EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0, ATTN_NORM, 0, 100); @@ -1594,7 +1594,7 @@ void WaypointRemovePath(edict_t *pEntity, int cmd) { waypoint2 = WaypointFindNearest(pEntity, 50.0, -1); - if ((waypoint1 == -1) || (waypoint2 == -1)) + if ((waypoint1 == WAYPOINT_NOT_FOUND) || (waypoint2 == WAYPOINT_NOT_FOUND)) { // play "error" sound... EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0, ATTN_NORM, 0, 100); @@ -1822,7 +1822,7 @@ void WaypointSave() while (i < MAX_PATH_INDEX) { - if (p->index[i] != -1) + if (p->index[i] != WAYPOINT_NOT_FOUND) num++; // count path node if it's used i++; @@ -1843,7 +1843,7 @@ void WaypointSave() while (i < MAX_PATH_INDEX) { - if (p->index[i] != -1) // save path node if it's used + if (p->index[i] != WAYPOINT_NOT_FOUND) // save path node if it's used fwrite(&p->index[i], sizeof(p->index[0]), 1, pFile); i++; @@ -1992,7 +1992,7 @@ int WaypointFindReachable(edict_t *pEntity, float range, int team) // if not close enough to a waypoint then just return if (min_distance > range) - return -1; + return WAYPOINT_NOT_FOUND; return min_index; } @@ -2007,7 +2007,7 @@ void WaypointPrintInfo(edict_t *pEntity) // find the nearest waypoint... index = WaypointFindNearest(pEntity, 300.0, -1); - if (index == -1) + if (index == WAYPOINT_NOT_FOUND) return; sprintf(msg,"Waypoint %d of %d total\n", index, num_waypoints); @@ -2072,13 +2072,9 @@ void WaypointPrintInfo(edict_t *pEntity) ALERT( at_console, "item_ctfbase with model %s\n", STRING(pBase->v.model) ); } - if( flags & W_FL_OP4_CAPTURE_POINT_BM ) + if( flags & W_FL_OP4_CAPTURE_POINT ) { - ClientPrint( pEntity, HUD_PRINTNOTIFY, "There is a Black Mesa capture point near this waypoint\n"); - } - if( flags & W_FL_OP4_CAPTURE_POINT_OF ) - { - ClientPrint( pEntity, HUD_PRINTNOTIFY, "There is an Opposing Force capture point near this waypoint\n"); + ClientPrint( pEntity, HUD_PRINTNOTIFY, "There is a capture point near this waypoint\n"); } if (flags & W_FL_PRONE) @@ -2199,8 +2195,8 @@ void WaypointThink(edict_t *pEntity) } } - // check if path waypointing is on... - if (g_path_waypoint) + // check if waypointing is on... + if (g_waypoint_on) { // check if player is close enough to a waypoint and time to draw path... if ((min_distance <= 50) && (f_path_time <= gpGlobals->time)) @@ -2211,13 +2207,13 @@ void WaypointThink(edict_t *pEntity) p = paths[index]; - while (p != NULL) + while (p != nullptr) { i = 0; while (i < MAX_PATH_INDEX) { - if (p->index[i] != -1) + if (p->index[i] != WAYPOINT_NOT_FOUND) { Vector v_src = waypoints[index].origin; Vector v_dest = waypoints[p->index[i]].origin; @@ -2471,7 +2467,7 @@ void WaypointRouteInit() while (i < MAX_PATH_INDEX) { - if (p->index[i] != -1) + if (p->index[i] != WAYPOINT_NOT_FOUND) { index = p->index[i]; @@ -2598,4 +2594,3 @@ int WaypointDistanceFromTo(int src, int dest, int team) return (int)(pShortestPath[src * route_num_waypoints + dest]); } - diff --git a/dlls/waypoint.h b/dlls/waypoint.h index 3c2c21d..3965c44 100644 --- a/dlls/waypoint.h +++ b/dlls/waypoint.h @@ -17,6 +17,7 @@ const float REACHABLE_RANGE = 400.0; extern int num_waypoints; // number of waypoints currently in use extern waypoint_t waypoints[MAX_WAYPOINTS]; +extern path_t* paths[MAX_WAYPOINTS]; #define WAYPOINT_UNREACHABLE USHRT_MAX #define WAYPOINT_MAX_DISTANCE (USHRT_MAX-1) diff --git a/dlls/waypoint_test.cpp b/dlls/waypoint_test.cpp new file mode 100644 index 0000000..68ab5a8 --- /dev/null +++ b/dlls/waypoint_test.cpp @@ -0,0 +1,46 @@ +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// http://www.teamsandpit.com/ +// +// Notes: +// +//============================================================================= + +#include "CppUnitTest.h" + +#include "h_export.h" +#include "foolsgoldsource.h" +#include "test.h" +//============================================================================= +// +// Sandbot - GoldSource engine multiplayer bot +// +// Based on HPB_Bot by Jeffrey "botman" Broome +// +// http://www.teamsandpit.com/ +// +// Notes: waypointing test code +// +//============================================================================= + +#include "wpt.h" +#include "waypoint.h" + +namespace tests +{ + TEST_CLASS(waypoint_test) + { + public: + + TEST_METHOD(TestWaypointInit) + { + WaypointInit(); + + for( int i = 0; i < MAX_WAYPOINTS; i++ ) { + Assert::IsNull( paths[i] ); + } + } + }; +} diff --git a/dlls/wpt.h b/dlls/wpt.h index e3ddd7e..b057575 100644 --- a/dlls/wpt.h +++ b/dlls/wpt.h @@ -2,9 +2,11 @@ // // Sandbot - GoldSource engine multiplayer bot // +// Based on HPB_Bot by Jeffrey "botman" Broome +// // http://www.teamsandpit.com/ // -// Notes: +// Notes: .wpt file format - this file must be included before waypoint.h // //============================================================================= @@ -54,8 +56,7 @@ const int W_FL_NS_RESNODE = (1<<22); const int W_FL_TFC_BUILD_ZONE = (1<<25); // Team Fortress Classic engineer build zone -const int W_FL_OP4_CAPTURE_POINT_BM = (1<<27); // Opposing Force CTF Black Mesa capture point -const int W_FL_OP4_CAPTURE_POINT_OF = (1<<28); // Opposing Force CTF Opposing Force capture point +const int W_FL_OP4_CAPTURE_POINT = (1<<27); // Opposing Force CTF capture point const int W_FL_DOD_OBJ = (1<<29); // Day of Defeat objective const int W_FL_DOD_CAP = (1<<30); // Day of Defeat capture point diff --git a/metamod/meta_api.h b/metamod/meta_api.h index 4480b06..f47b5c0 100644 --- a/metamod/meta_api.h +++ b/metamod/meta_api.h @@ -221,12 +221,4 @@ C_DLLEXPORT int GetEngineFunctions_Post(enginefuncs_t *pengfuncsFromEngine, #define MDLL_InconsistentFile MDLL_FUNC->pfnInconsistentFile #define MDLL_AllowLagCompensation MDLL_FUNC->pfnAllowLagCompensation -// NEW API functions: -#define MNEW_FUNC gpGamedllFuncs->newapi_table - -#define MNEW_OnFreeEntPrivateData MNEW_FUNC->pfnOnFreeEntPrivateData -#define MNEW_GameShutdown MNEW_FUNC->pfnGameShutdown -#define MNEW_ShouldCollide MNEW_FUNC->pfnShouldCollide -#define MNEW_CvarValue MNEW_FUNC->pfnCvarValue - #endif /* META_API_H */ diff --git a/notes.txt b/notes.txt index 68b5ae0..bf2a4ba 100644 --- a/notes.txt +++ b/notes.txt @@ -41,3 +41,7 @@ Load it all up in gdb: gdb -s ~/Documents/sandbot/symbols/sandbot.so.debug -e ~/.steam/steam/steamapps/common/Half-Life/hl_linux -c out.core TODO: figure out loading sandbot symbols into gdb + +Bugs +---- +GetSaveGameComment doesn't work in Opposing Force? \ No newline at end of file diff --git a/readme.txt b/readme.txt index 01c2510..b2452a7 100644 --- a/readme.txt +++ b/readme.txt @@ -21,6 +21,16 @@ waypoint delete Delete a waypoint waypoint save Write waypoints to file waypoint info Print what type of waypoint a waypoint is +== Build instructions == + +Windows + dlls/sandbot-2019.sln + +Linux + sudo apt-get install gcc gcc-multilib g++-multilib + cd dlls + make + == Frequently Asked Questions == Q. Can you add support for mod x? @@ -35,11 +45,6 @@ A. It should work but I don't test it. Q. Can I make some waypoints? A. Yes, please do! -Q. How do I build Sandbot? -A. If you want to build it yourself, use a tagged release as the head of master can be unstable. - On Windows open dlls/sandbot-2019.sln with Visual Studio and compile it. - On Linux apt-get install gcc gcc-multilib g++-multilib (or equivalent) and run make inside of dlls/ - Q. How do I run the tests? A. In Visual Studio go to Tests -> Windows -> Test Explorer and in the run icon menu choose Run All. @@ -56,10 +61,12 @@ v0.4.3 (??/??/??) - [All] Fixed cvars not being read correctly in Metamod. - [All] bot_skill and bot_count now get written to config.cfg - [All] Added GetEntityAPI2 support. +- [All] Got rid of pathwaypoint on/off and replaced it with waypoint on/off. - [Windows] Fixed possible memory leaks. - [Windows] Updated compiler to Visual Studio 2019 (from Visual Studio 2017). - [Linux] Updated gcc's minimum architecture to Intel Core 2. - [Linux] Updated compiler to gcc 8.3.0 (from gcc 6.3.0). +- [Linux] Updated to compile with gcc 11.1.1 (thanks dannycolin@GitHub and mathieuf20@GitHub). - [Opposing Force] Added waypoints for op4cp_park, op4ctf_blackmesa, op4ctf_omen and op4ctf_orange (thanks RoboCop). - [Opposing Force] Bots can now use weapon_penguin. - [Natural Selection] Increased range at which marines will use weapon_machinegun and weapon_heavymachinegun. diff --git a/sandbot/half-life/dod/maps/dod_flash.wpt b/sandbot/half-life/dod/maps/dod_flash.wpt new file mode 100644 index 0000000..1fc84f8 Binary files /dev/null and b/sandbot/half-life/dod/maps/dod_flash.wpt differ diff --git a/sandbot/half-life/gearbox/maps/op4_xendm.wpt b/sandbot/half-life/gearbox/maps/op4_xendm.wpt new file mode 100644 index 0000000..1b164d0 Binary files /dev/null and b/sandbot/half-life/gearbox/maps/op4_xendm.wpt differ diff --git a/sandbot/half-life/gearbox/maps/op4ctf_zeroceromon.wpt b/sandbot/half-life/gearbox/maps/op4ctf_zeroceromon.wpt new file mode 100644 index 0000000..5be261a Binary files /dev/null and b/sandbot/half-life/gearbox/maps/op4ctf_zeroceromon.wpt differ diff --git a/symbols/sandbot.so b/symbols/sandbot.so old mode 100755 new mode 100644