mirror of
https://github.com/ENSL/NS.git
synced 2025-04-20 16:30:56 +00:00
Initial bot commit
* Added server commands and cvars for adding AI players to the game. * Added auto modes for automating the adding and removal of bots * Bots connect to the server and join teams correctly
This commit is contained in:
parent
4b1ec43d81
commit
777025e794
11 changed files with 739 additions and 51 deletions
|
@ -353,7 +353,12 @@ void ClientPutInServer( edict_t *pEntity )
|
|||
|
||||
// Allocate a CBasePlayer for pev, and call spawn
|
||||
pPlayer->SetPlayMode(PLAYMODE_READYROOM);
|
||||
//pPlayer->Spawn();
|
||||
|
||||
//if (pev->flags & FL_FAKECLIENT)
|
||||
//{
|
||||
// pPlayer->Spawn();
|
||||
//}
|
||||
|
||||
|
||||
// Reset interpolation during first frame
|
||||
pPlayer->pev->effects |= EF_NOINTERP;
|
||||
|
|
|
@ -118,13 +118,15 @@ void WRITE_STRING(const char* inData);
|
|||
|
||||
#ifdef AVH_SERVER
|
||||
|
||||
#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity)
|
||||
#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister)
|
||||
#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat)
|
||||
#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString)
|
||||
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat)
|
||||
#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString)
|
||||
#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer)
|
||||
#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity)
|
||||
#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister)
|
||||
#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat)
|
||||
#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString)
|
||||
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat)
|
||||
#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString)
|
||||
#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer)
|
||||
#define REGISTER_SERVER_FUNCTION (*g_engfuncs.pfnAddServerCommand)
|
||||
#define RUN_AI_MOVE (*g_engfuncs.pfnRunPlayerMove)
|
||||
|
||||
#endif // AVH_SERVER
|
||||
|
||||
|
|
|
@ -123,6 +123,15 @@ cvar_t avh_mapvoteratio = {kvMapVoteRatio, ".6", FCVAR_SERVER};
|
|||
cvar_t avh_blockscripts = {kvBlockScripts, "1", FCVAR_SERVER};
|
||||
cvar_t avh_jumpmode = {kvJumpMode, "1", FCVAR_SERVER};
|
||||
cvar_t avh_version = {kvVersion, "330", FCVAR_SERVER};
|
||||
|
||||
// AI Player Settings
|
||||
cvar_t avh_botsenabled = { kvBotsEnabled,"0", FCVAR_SERVER }; // Bots can be added to the server Y/N
|
||||
cvar_t avh_botautomode = { kvBotAutoMode,"0", FCVAR_SERVER }; // Defines automated behaviour for adding/removing bots
|
||||
cvar_t avh_botminplayers = { kvBotMinPlayers,"0", FCVAR_SERVER }; // If bots are enabled and auto mode == 2 then it will maintain this player count by adding/removing as needed
|
||||
cvar_t avh_botskill = { kvBotSkill,"0", FCVAR_SERVER }; // Sets the skill for the bots (0 = easiest, 3 = hardest)
|
||||
cvar_t avh_botusemapdefaults = { kvBotUseMapDefaults,"0", FCVAR_SERVER }; // Defines automated behaviour for adding/removing bots
|
||||
|
||||
|
||||
//playtest cvars
|
||||
cvar_t avh_fastjp = {kvfastjp, "0", FCVAR_SERVER};
|
||||
cvar_t avh_randomrfk = {kvRandomRfk, "1", FCVAR_SERVER};
|
||||
|
@ -204,6 +213,13 @@ void GameDLLInit( void )
|
|||
CVAR_REGISTER (&defaultteam);
|
||||
CVAR_REGISTER (&allowmonsters);
|
||||
CVAR_REGISTER (&mp_chattime);
|
||||
|
||||
// Register AI player settings
|
||||
CVAR_REGISTER(&avh_botsenabled);
|
||||
CVAR_REGISTER(&avh_botautomode);
|
||||
CVAR_REGISTER(&avh_botminplayers);
|
||||
CVAR_REGISTER(&avh_botusemapdefaults);
|
||||
CVAR_REGISTER(&avh_botskill);
|
||||
|
||||
// Register AvH variables
|
||||
CVAR_REGISTER (&avh_drawdamage);
|
||||
|
|
|
@ -3139,8 +3139,8 @@ void CBasePlayer::Spawn( void )
|
|||
pev->takedamage = DAMAGE_AIM;
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_WALK;
|
||||
pev->flags &= FL_PROXY; // keep proxy flag sey by engine
|
||||
pev->flags |= FL_CLIENT;
|
||||
pev->flags &= FL_PROXY | FL_FAKECLIENT; // keep proxy and fakeclient flags set by engine
|
||||
pev->flags |= FL_CLIENT;
|
||||
pev->air_finished = gpGlobals->time + 12;
|
||||
pev->dmg = 2; // initial water damage
|
||||
pev->effects = 0;
|
||||
|
|
56
main/source/mod/AIPlayers/AvHAIPlayer.h
Normal file
56
main/source/mod/AIPlayers/AvHAIPlayer.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef AVH_AI_PLAYER_H
|
||||
#define AVH_AI_PLAYER_H
|
||||
|
||||
#include "../AvHPlayer.h"
|
||||
|
||||
typedef struct AVH_AI_PLAYER
|
||||
{
|
||||
AvHPlayer* Player = nullptr;
|
||||
edict_t* Edict = nullptr;
|
||||
AvHTeamNumber Team = TEAM_IND;
|
||||
float ForwardMove = 0.0f;
|
||||
float SideMove = 0.0f;
|
||||
float UpMove = 0.0f;
|
||||
int Button = 0.0f;
|
||||
int Impulse = 0.0f;
|
||||
byte AdjustedMsec = 0;
|
||||
|
||||
float f_previous_command_time = 0.0f;
|
||||
|
||||
} AvHAIPlayer;
|
||||
|
||||
string BotNames[MAX_PLAYERS] = { "MrRobot",
|
||||
"Wall-E",
|
||||
"BeepBoop",
|
||||
"Robotnik",
|
||||
"JonnyAutomaton",
|
||||
"Burninator",
|
||||
"SteelDeath",
|
||||
"Meatbag",
|
||||
"Undertaker",
|
||||
"Botini",
|
||||
"Robottle",
|
||||
"Rusty",
|
||||
"HeavyMetal",
|
||||
"Combot",
|
||||
"BagelLover",
|
||||
"Screwdriver",
|
||||
"LoveBug",
|
||||
"iSmash",
|
||||
"Chippy",
|
||||
"Baymax",
|
||||
"BoomerBot",
|
||||
"Jarvis",
|
||||
"Marvin",
|
||||
"Data",
|
||||
"Scrappy",
|
||||
"Mortis",
|
||||
"TerrorHertz",
|
||||
"Omicron",
|
||||
"Herbie",
|
||||
"Robogeddon",
|
||||
"Velociripper",
|
||||
"TerminalFerocity"
|
||||
};
|
||||
|
||||
#endif
|
477
main/source/mod/AIPlayers/AvHAIPlayerManager.cpp
Normal file
477
main/source/mod/AIPlayers/AvHAIPlayerManager.cpp
Normal file
|
@ -0,0 +1,477 @@
|
|||
#include "AvHAIPlayerManager.h"
|
||||
#include "AvHAIPlayer.h"
|
||||
#include "../AvHGamerules.h"
|
||||
#include "../dlls/client.h"
|
||||
#include <time.h>
|
||||
|
||||
float last_think_time = 0.0f;
|
||||
float BotDeltaTime = 0.01666667f;
|
||||
|
||||
AvHAIPlayer ActiveAIPlayers[MAX_PLAYERS];
|
||||
|
||||
extern cvar_t avh_botautomode;
|
||||
extern cvar_t avh_botsenabled;
|
||||
extern cvar_t avh_botminplayers;
|
||||
extern cvar_t avh_botusemapdefaults;
|
||||
|
||||
float LastAIPlayerCountUpdate = 0.0f;
|
||||
|
||||
int BotNameIndex = 0;
|
||||
|
||||
void AIMGR_UpdateAIPlayerCounts()
|
||||
{
|
||||
// Don't add or remove bots too quickly, otherwise it can cause lag or even overflows
|
||||
if (gpGlobals->time - LastAIPlayerCountUpdate < 0.2f) { return; }
|
||||
|
||||
// If game has ended, kick bots that have dropped back to the ready room
|
||||
if (GetGameRules()->GetVictoryTeam() != TEAM_IND)
|
||||
{
|
||||
AIMGR_RemoveBotsInReadyRoom();
|
||||
return;
|
||||
}
|
||||
|
||||
LastAIPlayerCountUpdate = gpGlobals->time;
|
||||
|
||||
if (avh_botsenabled.value == 0)
|
||||
{
|
||||
if (AIMGR_GetNumAIPlayers() > 0)
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(0);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (avh_botautomode.value == 0) // Manual mode: do nothing, server can manually add/remove as they want
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (avh_botautomode.value == 1) // Balance only: bots will only be added and removed to ensure teams remain balanced
|
||||
{
|
||||
AIMGR_UpdateTeamBalance();
|
||||
return;
|
||||
}
|
||||
|
||||
if (avh_botautomode.value == 2) // Fill teams: bots will be added and removed to maintain a minimum player count
|
||||
{
|
||||
AIMGR_UpdateFillTeams();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AIMGR_UpdateTeamBalance()
|
||||
{
|
||||
AvHTeamNumber teamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber teamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
// If team A has more players, then either remove a bot from team A, or add one to B to bring them in line
|
||||
if (GetGameRules()->GetTeamAPlayerCount() > GetGameRules()->GetTeamBPlayerCount())
|
||||
{
|
||||
// Favour removing bots to balance teams over adding more
|
||||
if (AIMGR_AIPlayerExistsOnTeam(teamA))
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(1);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AIMGR_AddAIPlayerToTeam(2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same if team B outmatches team A
|
||||
if (GetGameRules()->GetTeamBPlayerCount() > GetGameRules()->GetTeamAPlayerCount())
|
||||
{
|
||||
// Again, favour removing bots over adding more
|
||||
if (AIMGR_AIPlayerExistsOnTeam(teamB))
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(2);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AIMGR_AddAIPlayerToTeam(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If both teams are evenly matched, check to ensure we don't have bots on both sides. The purpose of balance mode
|
||||
// is to maintain the minimum bots required to keep teams even, so get rid of extras if needed
|
||||
if (AIMGR_AIPlayerExistsOnTeam(teamA) && AIMGR_AIPlayerExistsOnTeam(teamB))
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(teamB);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIMGR_UpdateFillTeams()
|
||||
{
|
||||
AvHTeamNumber teamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber teamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
int TeamSizeA = GetGameRules()->GetTeamAPlayerCount();
|
||||
int TeamSizeB = GetGameRules()->GetTeamBPlayerCount();
|
||||
int TotalPlayers = TeamSizeA + TeamSizeB;
|
||||
int NumDesiredPlayers = min(gpGlobals->maxClients, (int)floorf(avh_botminplayers.value));
|
||||
|
||||
// We have exceeded our minimum desired player count, start removing bots to make more room for humans
|
||||
if (TotalPlayers > NumDesiredPlayers)
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add bots to ensure we reach the number of desired players
|
||||
if (TotalPlayers < NumDesiredPlayers)
|
||||
{
|
||||
AIMGR_AddAIPlayerToTeam(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TeamSizeA > (TeamSizeB + 1))
|
||||
{
|
||||
if (AIMGR_AIPlayerExistsOnTeam(teamA))
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(1);
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (TeamSizeB > (TeamSizeA + 1))
|
||||
{
|
||||
if (AIMGR_AIPlayerExistsOnTeam(teamB))
|
||||
{
|
||||
AIMGR_RemoveAIPlayerFromTeam(2);
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIMGR_RemoveAIPlayerFromTeam(int Team)
|
||||
{
|
||||
if (AIMGR_GetNumAIPlayers() == 0) { return; }
|
||||
|
||||
AvHTeamNumber DesiredTeam = TEAM_IND;
|
||||
|
||||
AvHTeamNumber teamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber teamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
if (Team > 0)
|
||||
{
|
||||
DesiredTeam = (Team == 1) ? teamA : teamB;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetGameRules()->GetTeamAPlayerCount() > GetGameRules()->GetTeamBPlayerCount())
|
||||
{
|
||||
DesiredTeam = teamA;
|
||||
}
|
||||
else
|
||||
{
|
||||
DesiredTeam = teamB;
|
||||
}
|
||||
}
|
||||
|
||||
// We will go through the potential bots we could kick. We want to avoid kicking bots which have a lot of
|
||||
// resources tied up in them or are commanding, which could cause big disruption to the team they're leaving
|
||||
|
||||
int MinValue = 0; // Track the least valuable bot on the desired team.
|
||||
int IndexToKick = -1; // Current bot to be kicked
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
// Don't kick if the slot is empty, or the bot in that slot isn't on the right team
|
||||
if (!ActiveAIPlayers[i].Player || ActiveAIPlayers[i].Player->GetTeam() != DesiredTeam) { continue; }
|
||||
|
||||
AvHPlayer* theAIPlayer = ActiveAIPlayers[i].Player;
|
||||
|
||||
float BotValue = theAIPlayer->GetResources();
|
||||
|
||||
AvHPlayerClass theAIPlayerClass = (AvHPlayerClass)theAIPlayer->GetEffectivePlayerClass();
|
||||
|
||||
switch (theAIPlayerClass)
|
||||
{
|
||||
case PLAYERCLASS_COMMANDER:
|
||||
BotValue += 1000.0f; // Ensure this guy isn't kicked unless he's the only bot on the team!
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_HEAVY_MARINE:
|
||||
BotValue += kHeavyArmorCost;
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_JETPACK_MARINE:
|
||||
BotValue += kJetpackCost;
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_LEVEL2:
|
||||
BotValue += kGorgeCost;
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_LEVEL3:
|
||||
BotValue += kLerkCost;
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_LEVEL4:
|
||||
BotValue += kFadeCost;
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_LEVEL5:
|
||||
BotValue += kOnosCost;
|
||||
break;
|
||||
case PLAYERCLASS_ALIVE_GESTATING:
|
||||
BotValue += 10.0f;
|
||||
break;
|
||||
case PLAYERCLASS_DEAD_ALIEN:
|
||||
case PLAYERCLASS_DEAD_MARINE:
|
||||
BotValue -= 10.0f; // Favour kicking bots who are dead rather than alive
|
||||
break;
|
||||
case PLAYERCLASS_REINFORCING:
|
||||
BotValue -= 5.0f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (IndexToKick < 0 || BotValue < MinValue)
|
||||
{
|
||||
IndexToKick = i;
|
||||
MinValue = BotValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (IndexToKick > -1)
|
||||
{
|
||||
ActiveAIPlayers[IndexToKick].Player->Kick();
|
||||
|
||||
memset(&ActiveAIPlayers[IndexToKick], 0, sizeof(AvHAIPlayer));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIMGR_AddAIPlayerToTeam(int Team)
|
||||
{
|
||||
int NewBotIndex = -1;
|
||||
edict_t* BotEnt = nullptr;
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (!ActiveAIPlayers[i].Player)
|
||||
{
|
||||
NewBotIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NewBotIndex < 0)
|
||||
{
|
||||
ALERT(at_console, "Bot limit reached, cannot add more\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (AIMGR_GetNumAIPlayers() == 0)
|
||||
{
|
||||
// Initialise the name index to a random number so we don't always get the same bot names
|
||||
BotNameIndex = RANDOM_LONG(0, 31);
|
||||
}
|
||||
|
||||
AvHTeamNumber DesiredTeam = TEAM_IND;
|
||||
|
||||
AvHTeamNumber teamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber teamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
if (Team > 0)
|
||||
{
|
||||
DesiredTeam = (Team == 1) ? teamA : teamB;
|
||||
}
|
||||
|
||||
// Retrieve the current bot name and then cycle the index so the names are always unique
|
||||
string NewName = "[BOT]" + BotNames[BotNameIndex];
|
||||
|
||||
BotEnt = (*g_engfuncs.pfnCreateFakeClient)(NewName.c_str());
|
||||
|
||||
BotNameIndex++;
|
||||
|
||||
if (BotNameIndex > 31)
|
||||
{
|
||||
BotNameIndex = 0;
|
||||
}
|
||||
|
||||
if (!BotEnt)
|
||||
{
|
||||
ALERT(at_console, "Failed to create AI player: server is full\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char ptr[128]; // allocate space for message from ClientConnect
|
||||
int clientIndex;
|
||||
|
||||
char* infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(BotEnt);
|
||||
clientIndex = ENTINDEX(BotEnt);
|
||||
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "model", "");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "rate", "3500.000000");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "cl_updaterate", "20");
|
||||
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "cl_lw", "0");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "cl_lc", "0");
|
||||
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "tracker", "0");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "cl_dlmax", "128");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "lefthand", "1");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "friends", "0");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "dm", "0");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "ah", "1");
|
||||
(*g_engfuncs.pfnSetClientKeyValue)(clientIndex, infobuffer, "_vgui_menus", "0");
|
||||
|
||||
ClientConnect(BotEnt, STRING(BotEnt->v.netname), "127.0.0.1", ptr);
|
||||
ClientPutInServer(BotEnt);
|
||||
|
||||
BotEnt->v.flags |= FL_FAKECLIENT; // Shouldn't be needed but just to be sure
|
||||
|
||||
BotEnt->v.idealpitch = BotEnt->v.v_angle.x;
|
||||
BotEnt->v.ideal_yaw = BotEnt->v.v_angle.y;
|
||||
|
||||
BotEnt->v.pitch_speed = 270; // slightly faster than HLDM of 225
|
||||
BotEnt->v.yaw_speed = 250; // slightly faster than HLDM of 210
|
||||
|
||||
AvHPlayer* theNewAIPlayer = GetClassPtr((AvHPlayer*)&BotEnt->v);
|
||||
|
||||
if (theNewAIPlayer)
|
||||
{
|
||||
if (DesiredTeam != TEAM_IND)
|
||||
{
|
||||
ALERT(at_console, "Adding AI Player to team: %d\n", (int)Team);
|
||||
GetGameRules()->AttemptToJoinTeam(theNewAIPlayer, DesiredTeam, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT(at_console, "Auto-assigning AI Player to team\n");
|
||||
GetGameRules()->AutoAssignPlayer(theNewAIPlayer);
|
||||
}
|
||||
|
||||
ActiveAIPlayers[NewBotIndex].Player = theNewAIPlayer;
|
||||
ActiveAIPlayers[NewBotIndex].Edict = BotEnt;
|
||||
ActiveAIPlayers[NewBotIndex].Team = theNewAIPlayer->GetTeam();
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT(at_console, "Failed to create AI player: invalid AvHPlayer instance\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
byte BotThrottledMsec(AvHAIPlayer* inAIPlayer)
|
||||
{
|
||||
// Thanks to The Storm (ePODBot) for this one, finally fixed the bot running speed!
|
||||
int newmsec = (int)((gpGlobals->time - inAIPlayer->f_previous_command_time) * 1000);
|
||||
|
||||
if (newmsec > 255)
|
||||
{
|
||||
newmsec = 255;
|
||||
}
|
||||
|
||||
return (byte)newmsec;
|
||||
}
|
||||
|
||||
void AIMGR_UpdateAIPlayers()
|
||||
{
|
||||
// If bots are not enabled then do nothing
|
||||
if (avh_botsenabled.value == 0) { return; }
|
||||
|
||||
static clock_t prevtime = 0.0f;
|
||||
static clock_t currTime = 0.0f;
|
||||
|
||||
currTime = clock();
|
||||
|
||||
double timeSinceLastThink = (double)((currTime - last_think_time) / CLOCKS_PER_SEC);
|
||||
|
||||
if (IS_DEDICATED_SERVER() || timeSinceLastThink >= BOT_MIN_FRAME_TIME)
|
||||
{
|
||||
BotDeltaTime = timeSinceLastThink;
|
||||
|
||||
|
||||
for (int bot_index = 0; bot_index < MAX_PLAYERS; bot_index++)
|
||||
{
|
||||
if (!ActiveAIPlayers[bot_index].Player) { continue; } // Slot isn't filled
|
||||
|
||||
AvHAIPlayer* bot = &ActiveAIPlayers[bot_index];
|
||||
|
||||
// Needed to correctly handle client prediction and physics calculations
|
||||
byte adjustedmsec = BotThrottledMsec(bot);
|
||||
|
||||
// save the command time
|
||||
bot->f_previous_command_time = gpGlobals->time;
|
||||
|
||||
// Simulate PM_PlayerMove so client prediction and stuff can be executed correctly.
|
||||
RUN_AI_MOVE(bot->Edict, bot->Edict->v.v_angle, bot->ForwardMove,
|
||||
bot->SideMove, bot->UpMove, bot->Button, bot->Impulse, adjustedmsec);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
prevtime = currTime;
|
||||
|
||||
}
|
||||
|
||||
float AIMGR_GetBotDeltaTime()
|
||||
{
|
||||
return BotDeltaTime;
|
||||
}
|
||||
|
||||
int AIMGR_GetNumAIPlayers()
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (ActiveAIPlayers[i].Player != nullptr)
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
int Result = 0;
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Team == Team)
|
||||
{
|
||||
Result++;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team)
|
||||
{
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Team == Team)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AIMGR_RemoveBotsInReadyRoom()
|
||||
{
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (ActiveAIPlayers[i].Player != nullptr && ActiveAIPlayers[i].Player->GetInReadyRoom())
|
||||
{
|
||||
ActiveAIPlayers[i].Player->Kick();
|
||||
|
||||
memset(&ActiveAIPlayers[i], 0, sizeof(AvHAIPlayer));
|
||||
}
|
||||
}
|
||||
}
|
22
main/source/mod/AIPlayers/AvHAIPlayerManager.h
Normal file
22
main/source/mod/AIPlayers/AvHAIPlayerManager.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef AVH_AI_PLAYER_MANAGER_H
|
||||
#define AVH_AI_PLAYER_MANAGER_H
|
||||
|
||||
#include "../AvHConstants.h"
|
||||
|
||||
// Max rate bot can run its logic, default is 1/60th second. WARNING: Increasing the rate past 100hz causes bots to move and turn slowly due to GoldSrc limits!
|
||||
static const double BOT_MIN_FRAME_TIME = (1.0 / 60.0);
|
||||
|
||||
void AIMGR_AddAIPlayerToTeam(int Team);
|
||||
void AIMGR_RemoveAIPlayerFromTeam(int Team);
|
||||
void AIMGR_UpdateAIPlayers();
|
||||
void AIMGR_RemoveBotsInReadyRoom();
|
||||
float AIMGR_GetBotDeltaTime();
|
||||
|
||||
void AIMGR_UpdateAIPlayerCounts();
|
||||
void AIMGR_UpdateTeamBalance();
|
||||
void AIMGR_UpdateFillTeams();
|
||||
|
||||
int AIMGR_GetNumAIPlayers();
|
||||
int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team);
|
||||
|
||||
#endif
|
|
@ -333,14 +333,11 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
|
|||
{
|
||||
if(theAvHPlayer)
|
||||
{
|
||||
if(!(theAvHPlayer->pev->flags & FL_FAKECLIENT))
|
||||
if (!theAvHPlayer->GetIsBeingDigested())
|
||||
{
|
||||
if(!theAvHPlayer->GetIsBeingDigested())
|
||||
{
|
||||
theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true);
|
||||
}
|
||||
theSuccess = true;
|
||||
theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true);
|
||||
}
|
||||
theSuccess = true;
|
||||
}
|
||||
theSuccess = true;
|
||||
}
|
||||
|
@ -348,26 +345,23 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
|
|||
{
|
||||
if(theAvHPlayer)
|
||||
{
|
||||
if(!(theAvHPlayer->pev->flags & FL_FAKECLIENT))
|
||||
if (!theAvHPlayer->GetIsBeingDigested())
|
||||
{
|
||||
if(!theAvHPlayer->GetIsBeingDigested())
|
||||
// : 984
|
||||
// Add a throttle on the readyroom key
|
||||
const static int kReadyRoomThrottleTimeout = 2.0f;
|
||||
if ((theAvHPlayer->GetTimeLastF4() == -1.0f) ||
|
||||
(gpGlobals->time > theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout))
|
||||
{
|
||||
// : 984
|
||||
// Add a throttle on the readyroom key
|
||||
const static int kReadyRoomThrottleTimeout=2.0f;
|
||||
if ( (theAvHPlayer->GetTimeLastF4() == -1.0f) ||
|
||||
(gpGlobals->time > theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout) )
|
||||
{
|
||||
theAvHPlayer->SendMessage(kReadyRoomThrottleMessage);
|
||||
theAvHPlayer->SetTimeLastF4(gpGlobals->time);
|
||||
}
|
||||
else if ( gpGlobals->time < theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout )
|
||||
{
|
||||
theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true);
|
||||
}
|
||||
theAvHPlayer->SendMessage(kReadyRoomThrottleMessage);
|
||||
theAvHPlayer->SetTimeLastF4(gpGlobals->time);
|
||||
}
|
||||
else if (gpGlobals->time < theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout)
|
||||
{
|
||||
theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true);
|
||||
}
|
||||
theSuccess = true;
|
||||
}
|
||||
theSuccess = true;
|
||||
}
|
||||
theSuccess = true;
|
||||
}
|
||||
|
@ -1369,24 +1363,12 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
|
|||
#ifdef WIN32
|
||||
else if(FStrEq(pcmd, "createfake"))
|
||||
{
|
||||
if(this->GetCheatsEnabled())
|
||||
if(!theAvHPlayer || theIsServerOp || theIsPlaytest || theIsDedicatedServer || this->GetCheatsEnabled())
|
||||
{
|
||||
char theFakeClientName[256];
|
||||
sprintf(theFakeClientName, "Bot%d", RANDOM_LONG(0, 2000));
|
||||
edict_t* BotEnt = (*g_engfuncs.pfnCreateFakeClient)(theFakeClientName);
|
||||
|
||||
// create the player entity by calling MOD's player function
|
||||
// (from LINK_ENTITY_TO_CLASS for player object)
|
||||
player( VARS(BotEnt) );
|
||||
|
||||
char ptr[128]; // allocate space for message from ClientConnect
|
||||
ClientConnect( BotEnt, theFakeClientName, "127.0.0.1", ptr );
|
||||
|
||||
// Pieter van Dijk - use instead of DispatchSpawn() - Hip Hip Hurray!
|
||||
ClientPutInServer( BotEnt );
|
||||
|
||||
BotEnt->v.flags |= FL_FAKECLIENT;
|
||||
this->CreateAIPlayer(TEAM_IND);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -201,6 +201,7 @@
|
|||
#include "AvHNetworkMessages.h"
|
||||
#include "AvHNexusServer.h"
|
||||
#include "AvHParticleTemplateClient.h"
|
||||
#include "AIPlayers/AvHAIPlayerManager.h"
|
||||
|
||||
// : 0001073
|
||||
#ifdef USE_OLDAUTH
|
||||
|
@ -231,6 +232,11 @@ extern cvar_t avh_mapvoteratio;
|
|||
extern cvar_t avh_structurelimit;
|
||||
extern cvar_t avh_version;
|
||||
|
||||
extern cvar_t avh_botsenabled;
|
||||
extern cvar_t avh_botautomode;
|
||||
extern cvar_t avh_botminplayers;
|
||||
extern cvar_t avh_botskill;
|
||||
|
||||
BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot );
|
||||
inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); }
|
||||
//extern void ResetCachedEntities();
|
||||
|
@ -245,7 +251,6 @@ extern cvar_t avh_countdowntime;
|
|||
extern int gCommanderPointsAwardedEventID;
|
||||
extern cvar_t avh_networkmeterrate;
|
||||
|
||||
std::string gPlayerNames[128];
|
||||
cvar_t* cocName;
|
||||
cvar_t* cocExp;
|
||||
int gStartPlayerID = 0;
|
||||
|
@ -267,6 +272,7 @@ int kProfileRunConfig = 0xFFFFFFFF;
|
|||
|
||||
std::string GetLogStringForPlayer( edict_t *pEntity );
|
||||
|
||||
|
||||
const AvHMapExtents& GetMapExtents()
|
||||
{
|
||||
return GetGameRules()->GetMapExtents();
|
||||
|
@ -342,6 +348,49 @@ AvHGamerules::AvHGamerules() : mTeamA(TEAM_ONE), mTeamB(TEAM_TWO)
|
|||
RegisterServerVariable(&avh_fastjp);
|
||||
RegisterServerVariable(&avh_randomrfk);
|
||||
RegisterServerVariable(&avh_parasiteonmap);
|
||||
RegisterServerVariable(&avh_botsenabled);
|
||||
RegisterServerVariable(&avh_botautomode);
|
||||
RegisterServerVariable(&avh_botminplayers);
|
||||
RegisterServerVariable(&avh_botskill);
|
||||
|
||||
REGISTER_SERVER_FUNCTION("sv_addaiplayer", []()
|
||||
{
|
||||
if (avh_botsenabled.value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int DesiredTeam = 0;
|
||||
|
||||
if (CMD_ARGC() >= 2)
|
||||
{
|
||||
const char* TeamInput = CMD_ARGV(1);
|
||||
|
||||
if (TeamInput != NULL)
|
||||
{
|
||||
DesiredTeam = atoi(TeamInput);
|
||||
}
|
||||
}
|
||||
|
||||
AIMGR_AddAIPlayerToTeam(DesiredTeam);
|
||||
});
|
||||
|
||||
REGISTER_SERVER_FUNCTION("sv_removeaiplayer", []()
|
||||
{
|
||||
int DesiredTeam = 0;
|
||||
|
||||
if (CMD_ARGC() >= 2)
|
||||
{
|
||||
const char* TeamInput = CMD_ARGV(1);
|
||||
|
||||
if (TeamInput != NULL)
|
||||
{
|
||||
DesiredTeam = atoi(TeamInput);
|
||||
}
|
||||
}
|
||||
|
||||
AIMGR_RemoveAIPlayerFromTeam(DesiredTeam);
|
||||
});
|
||||
|
||||
g_VoiceGameMgr.Init(&gVoiceHelper, gpGlobals->maxClients);
|
||||
|
||||
|
@ -1205,6 +1254,30 @@ bool AvHGamerules::GetArePlayersAllowedToJoinImmediately(void) const
|
|||
return thePlayerIsAllowedToJoinImmediately;
|
||||
}
|
||||
|
||||
bool AvHGamerules::GetTeamHasRoomToJoin(AvHTeamNumber inTeamNumber) const
|
||||
{
|
||||
AvHTeamNumber teamA = this->mTeamA.GetTeamNumber();
|
||||
AvHTeamNumber teamB = this->mTeamB.GetTeamNumber();
|
||||
AvHTeamNumber theOtherTeamNumber = (inTeamNumber == teamA) ? teamB : teamA;
|
||||
|
||||
const AvHTeam* theTeam = this->GetTeam(inTeamNumber);
|
||||
const AvHTeam* theOtherTeam = this->GetTeam(theOtherTeamNumber);
|
||||
|
||||
if (theTeam && theOtherTeam)
|
||||
{
|
||||
int theWouldBeNumPlayersOnTeam = theTeam->GetPlayerCount();
|
||||
int theWouldBeNumPlayersOnOtherTeam = theOtherTeam->GetPlayerCount();
|
||||
|
||||
int theDiscrepancyAllowed = max(1.0f, avh_limitteams.value);
|
||||
if (((theWouldBeNumPlayersOnTeam - theWouldBeNumPlayersOnOtherTeam) <= theDiscrepancyAllowed) || this->GetIsTournamentMode() || this->GetCheatsEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AvHGamerules::GetCanJoinTeamInFuture(AvHPlayer* inPlayer, AvHTeamNumber inTeamNumber, string& outString) const
|
||||
{
|
||||
// You can switch teams, unless
|
||||
|
@ -1438,6 +1511,21 @@ bool AvHGamerules::GetIsTrainingMode(void) const
|
|||
#endif
|
||||
}
|
||||
|
||||
bool AvHGamerules::GetBotsEnabled(void) const
|
||||
{
|
||||
return (ns_cvar_float(&avh_botsenabled) > 0);
|
||||
}
|
||||
|
||||
int AvHGamerules::GetBotMinPlayerCount(void) const
|
||||
{
|
||||
return (int)ns_cvar_float(&avh_botminplayers);
|
||||
}
|
||||
|
||||
int AvHGamerules::GetBotSkill(void) const
|
||||
{
|
||||
return (int)ns_cvar_float(&avh_botskill);
|
||||
}
|
||||
|
||||
AvHMapMode AvHGamerules::GetMapMode(void) const
|
||||
{
|
||||
return this->mMapMode;
|
||||
|
@ -1600,6 +1688,26 @@ const AvHTeam* AvHGamerules::GetTeamB(void) const
|
|||
return &this->mTeamB;
|
||||
}
|
||||
|
||||
AvHTeamNumber AvHGamerules::GetTeamANumber()
|
||||
{
|
||||
return this->mTeamA.GetTeamNumber();
|
||||
}
|
||||
|
||||
AvHTeamNumber AvHGamerules::GetTeamBNumber()
|
||||
{
|
||||
return this->mTeamB.GetTeamNumber();
|
||||
}
|
||||
|
||||
int AvHGamerules::GetTeamAPlayerCount()
|
||||
{
|
||||
return this->mTeamA.GetPlayerCount();
|
||||
}
|
||||
|
||||
int AvHGamerules::GetTeamBPlayerCount()
|
||||
{
|
||||
return this->mTeamB.GetPlayerCount();
|
||||
}
|
||||
|
||||
AvHTeam* AvHGamerules::GetTeam(AvHTeamNumber inTeamNumber)
|
||||
{
|
||||
AvHTeam* theTeam = NULL;
|
||||
|
@ -3492,6 +3600,9 @@ void AvHGamerules::Think(void)
|
|||
|
||||
if(GET_RUN_CODE(4))
|
||||
{
|
||||
AIMGR_UpdateAIPlayerCounts();
|
||||
AIMGR_UpdateAIPlayers();
|
||||
|
||||
if(!this->GetGameStarted())
|
||||
{
|
||||
if(!this->GetIsTournamentMode())
|
||||
|
@ -4544,4 +4655,3 @@ void AvHGamerules::BalanceChanged()
|
|||
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -228,6 +228,7 @@ public:
|
|||
bool GetIsTesting(void) const;
|
||||
bool GetIsValidFutureTeam(AvHPlayer inPlayer, int inTeamNumber) const;
|
||||
bool GetCanJoinTeamInFuture(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, string& outString) const;
|
||||
bool GetTeamHasRoomToJoin(AvHTeamNumber theTeamNumber) const;
|
||||
const AvHBaseInfoLocationListType& GetInfoLocations() const;
|
||||
int GetMaxWeight(void) const;
|
||||
const char* GetSpawnEntityName(AvHPlayer* inPlayer) const;
|
||||
|
@ -236,9 +237,14 @@ public:
|
|||
int GetTimeLimit() const;
|
||||
int GetWeightForItemAndAmmo(AvHWeaponID inWeapon, int inNumRounds) const;
|
||||
bool AttemptToJoinTeam(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, bool inDisplayErrorMessage = true);
|
||||
void AutoAssignPlayer(AvHPlayer* inPlayer);
|
||||
const AvHTeam* GetTeam(AvHTeamNumber inTeamNumber) const;
|
||||
const AvHTeam* GetTeamA() const;
|
||||
const AvHTeam* GetTeamB() const;
|
||||
AvHTeamNumber GetTeamANumber();
|
||||
AvHTeamNumber GetTeamBNumber();
|
||||
int GetTeamAPlayerCount();
|
||||
int GetTeamBPlayerCount();
|
||||
AvHTeam* GetTeam(AvHTeamNumber inTeamNumber);
|
||||
AvHTeam* GetTeamA();
|
||||
AvHTeam* GetTeamB();
|
||||
|
@ -266,6 +272,12 @@ public:
|
|||
virtual bool GetIsNSMode(void) const;
|
||||
virtual bool GetIsTrainingMode(void) const;
|
||||
|
||||
virtual bool GetBotsEnabled(void) const;
|
||||
virtual int GetBotMinPlayerCount(void) const;
|
||||
virtual int GetBotSkill(void) const;
|
||||
|
||||
|
||||
|
||||
int GetBaseHealthForMessageID(AvHMessageID inMessageID) const;
|
||||
int GetBuildTimeForMessageID(AvHMessageID inMessageID) const;
|
||||
int GetCostForMessageID(AvHMessageID inMessageID) const;
|
||||
|
@ -292,7 +304,7 @@ public:
|
|||
int GetStructureLimit();
|
||||
void RemoveEntityUnderAttack(int entIndex);
|
||||
protected:
|
||||
void AutoAssignPlayer(AvHPlayer* inPlayer);
|
||||
|
||||
void PerformMapValidityCheck();
|
||||
virtual void RecalculateMapMode( void );
|
||||
bool GetDeathMatchMode(void) const;
|
||||
|
|
|
@ -142,6 +142,12 @@ float ns_cvar_float(const cvar_t *cvar);
|
|||
#define kvIronMan "mp_ironman"
|
||||
#define kvIronManTime "mp_ironmantime"
|
||||
|
||||
#define kvBotsEnabled "mp_botsenabled"
|
||||
#define kvBotMinPlayers "mp_botminplayers"
|
||||
#define kvBotUseMapDefaults "mp_botusemapdefaults"
|
||||
#define kvBotSkill "mp_botskill"
|
||||
#define kvBotAutoMode "mp_botautomode"
|
||||
|
||||
#define kvEasterEggChance "mp_eastereggchance"
|
||||
#define kvUplink "mp_uplink"
|
||||
#define kvMapVoteRatio "mp_mapvoteratio"
|
||||
|
|
Loading…
Reference in a new issue