Lerk improvements and bot names

* Improved lerk movement when it is not flying (i.e. being cautious)
* Users can now optionally add botnames.txt to the NS folder to define custom bot names
This commit is contained in:
RGreenlees 2024-04-06 16:56:27 +01:00 committed by pierow
parent e322e52619
commit 27ed51035c
6 changed files with 130 additions and 51 deletions

View file

@ -5,6 +5,8 @@
#include "AvHServerUtil.h" #include "AvHServerUtil.h"
#include <unordered_map> #include <unordered_map>
#include <algorithm>
#include <random>
BotFillTiming CurrentBotFillTiming = FILLTIMING_ALLHUMANS; BotFillTiming CurrentBotFillTiming = FILLTIMING_ALLHUMANS;
@ -16,6 +18,43 @@ bot_skill BotSkillLevels[4];
AvHMessageID ChamberSequence[3] = { ALIEN_BUILD_DEFENSE_CHAMBER, ALIEN_BUILD_MOVEMENT_CHAMBER, ALIEN_BUILD_SENSORY_CHAMBER }; AvHMessageID ChamberSequence[3] = { ALIEN_BUILD_DEFENSE_CHAMBER, ALIEN_BUILD_MOVEMENT_CHAMBER, ALIEN_BUILD_SENSORY_CHAMBER };
string DefaultBotNames[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"
};
vector<string> BotNames;
int CurrentNameIndex = 0;
char BotPrefix[32] = ""; char BotPrefix[32] = "";
extern cvar_t avh_botskill; extern cvar_t avh_botskill;
@ -110,6 +149,69 @@ bot_skill CONFIG_GetBotSkillLevel()
return BotSkillLevels[index]; return BotSkillLevels[index];
} }
void CONFIG_PopulateBotNames()
{
BotNames.clear();
string BotConfigFile = string(getModDirectory()) + "/botnames.txt";
const char* filename = BotConfigFile.c_str();
std::ifstream cFile(filename);
if (cFile.is_open())
{
std::string line;
while (getline(cFile, line))
{
if (line[0] == '/' || line.empty())
continue;
BotNames.push_back(line);
}
}
if (BotNames.size() > 2)
{
auto rng = std::default_random_engine{};
std::shuffle(begin(BotNames), end(BotNames), rng);
}
vector<string> DefaultNames;
// Ensure we have 32 names for all bots
for (int i = BotNames.size(); i < MAX_PLAYERS; i++)
{
DefaultNames.push_back(DefaultBotNames[i]);
}
if (DefaultNames.size() > 2)
{
auto rng = std::default_random_engine{};
std::shuffle(begin(DefaultNames), end(DefaultNames), rng);
}
BotNames.insert(BotNames.end(), DefaultNames.begin(), DefaultNames.end());
CurrentNameIndex = 0;
}
string CONFIG_GetNextBotName()
{
if (BotNames.size() == 0) { return "Bot"; }
string Result = BotNames[CurrentNameIndex];
CurrentNameIndex++;
if (CurrentNameIndex >= BotNames.size())
{
CurrentNameIndex = 0;
}
return Result;
}
void CONFIG_ParseConfigFile() void CONFIG_ParseConfigFile()
{ {

View file

@ -62,4 +62,8 @@ BotFillTiming CONFIG_GetBotFillTiming();
void CONFIG_RegenerateIniFile(); void CONFIG_RegenerateIniFile();
void CONFIG_PopulateBotNames();
string CONFIG_GetNextBotName();
#endif #endif

View file

@ -6213,7 +6213,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
{ {
if (bIsFlyingProfile) if (bIsFlyingProfile)
{ {
BotFollowFlightPath(pBot); BotFollowFlightPath(pBot, true);
} }
else else
{ {
@ -6351,7 +6351,7 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot)
} }
} }
void BotFollowFlightPath(AvHAIPlayer* pBot) void BotFollowFlightPath(AvHAIPlayer* pBot, bool bAllowSkip)
{ {
if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size()) if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.CurrentPath.size())
{ {
@ -6392,7 +6392,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
return; return;
} }
if (CurrentPathPoint->area != SAMPLE_POLYAREA_CROUCH && next(CurrentPathPoint) != BotNavInfo->CurrentPath.end() && next(CurrentPathPoint)->area != SAMPLE_POLYAREA_CROUCH) if (bAllowSkip && CurrentPathPoint->area != SAMPLE_POLYAREA_CROUCH && next(CurrentPathPoint) != BotNavInfo->CurrentPath.end() && next(CurrentPathPoint)->area != SAMPLE_POLYAREA_CROUCH)
{ {
SkipAheadInFlightPath(pBot); SkipAheadInFlightPath(pBot);
CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint); CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint);
@ -6400,7 +6400,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
ClosestPointToPath = vClosestPointOnLine(CurrentPathPoint->FromLocation, CurrentPathPoint->Location, pEdict->v.origin); ClosestPointToPath = vClosestPointOnLine(CurrentPathPoint->FromLocation, CurrentPathPoint->Location, pEdict->v.origin);
if (vDist3DSq(pBot->Edict->v.origin, ClosestPointToPath) > sqrf(GetPlayerRadius(pBot->Edict) * 3.0f)) if (bAllowSkip && vDist3DSq(pBot->Edict->v.origin, ClosestPointToPath) > sqrf(GetPlayerRadius(pBot->Edict) * 3.0f))
{ {
ClearBotPath(pBot); ClearBotPath(pBot);
return; return;
@ -6561,7 +6561,7 @@ void BotFollowSwimPath(AvHAIPlayer* pBot)
// We're at the surface, now tackle the path the usual way // We're at the surface, now tackle the path the usual way
if (pBot->BotNavInfo.NavProfile.bFlyingProfile) if (pBot->BotNavInfo.NavProfile.bFlyingProfile)
{ {
BotFollowFlightPath(pBot); BotFollowFlightPath(pBot, true);
} }
else else
{ {
@ -6633,6 +6633,15 @@ void BotFollowPath(AvHAIPlayer* pBot)
} }
} }
if (IsPlayerLerk(pBot->Edict))
{
if (CurrentNode.flag != SAMPLE_POLYFLAGS_WALK && CurrentNode.flag != SAMPLE_POLYFLAGS_LIFT)
{
BotFollowFlightPath(pBot, false);
return;
}
}
if (IsBotOffPath(pBot)) if (IsBotOffPath(pBot))
{ {
MoveToWithoutNav(pBot, CurrentNode.Location); MoveToWithoutNav(pBot, CurrentNode.Location);

View file

@ -332,7 +332,7 @@ void UpdateBotStuck(AvHAIPlayer* pBot);
// Used by the MoveTo command, handles the bot's movement and inputs to follow a path it has calculated for itself // Used by the MoveTo command, handles the bot's movement and inputs to follow a path it has calculated for itself
void BotFollowPath(AvHAIPlayer* pBot); void BotFollowPath(AvHAIPlayer* pBot);
void BotFollowFlightPath(AvHAIPlayer* pBot); void BotFollowFlightPath(AvHAIPlayer* pBot, bool bAllowSkip);
void BotFollowSwimPath(AvHAIPlayer* pBot); void BotFollowSwimPath(AvHAIPlayer* pBot);
void SkipAheadInFlightPath(AvHAIPlayer* pBot); void SkipAheadInFlightPath(AvHAIPlayer* pBot);

View file

@ -4598,7 +4598,7 @@ AvHMessageID GetNextAIPlayerCOAlienUpgrade(AvHAIPlayer* pBot)
// As a bombardier, we can still go fade if we can't afford Onos yet, so calculate our points savings accordingly // As a bombardier, we can still go fade if we can't afford Onos yet, so calculate our points savings accordingly
if (pBot->BotRole == BOT_ROLE_BOMBARDIER) if (pBot->BotRole == BOT_ROLE_BOMBARDIER)
{ {
if (NumPointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE)) if (CONFIG_IsOnosAllowed() && NumPointsAvailable <= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE))
{ {
return MESSAGE_NULL; return MESSAGE_NULL;
} }
@ -5039,7 +5039,7 @@ void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (pBot->BotRole == BOT_ROLE_ASSAULT) if (pBot->BotRole == BOT_ROLE_ASSAULT)
{ {
if (!IsPlayerFade(pBot->Edict) && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR)) if (CONFIG_IsFadeAllowed() && !IsPlayerFade(pBot->Edict) && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR))
{ {
if (Task->TaskType != TASK_EVOLVE) if (Task->TaskType != TASK_EVOLVE)
{ {
@ -5060,11 +5060,11 @@ void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{ {
AvHMessageID DesiredEvolution = MESSAGE_NULL; AvHMessageID DesiredEvolution = MESSAGE_NULL;
if (pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE)) if (CONFIG_IsOnosAllowed() && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FIVE))
{ {
DesiredEvolution = ALIEN_LIFEFORM_FIVE; DesiredEvolution = ALIEN_LIFEFORM_FIVE;
} }
else if (pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR)) else if (CONFIG_IsFadeAllowed() && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR))
{ {
DesiredEvolution = ALIEN_LIFEFORM_FOUR; DesiredEvolution = ALIEN_LIFEFORM_FOUR;
} }
@ -5587,8 +5587,6 @@ void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination)
void BotStopCommanderMode(AvHAIPlayer* pBot) void BotStopCommanderMode(AvHAIPlayer* pBot)
{ {
// Thanks EterniumDev (Alien) for logic to allow commander AI to leave the chair and build structures when needed
if (IsPlayerCommander(pBot->Edict)) if (IsPlayerCommander(pBot->Edict))
{ {
pBot->Player->SetUser3(AVH_USER3_MARINE_PLAYER); pBot->Player->SetUser3(AVH_USER3_MARINE_PLAYER);

View file

@ -51,40 +51,6 @@ float CountdownStartedTime = 0.0f;
bool bBotsEnabled = false; bool bBotsEnabled = false;
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"
};
AvHAICommanderMode AIMGR_GetCommanderMode() AvHAICommanderMode AIMGR_GetCommanderMode()
{ {
if (avh_botcommandermode.value == 1) if (avh_botcommandermode.value == 1)
@ -442,9 +408,8 @@ void AIMGR_AddAIPlayerToTeam(int Team)
BotNameIndex = RANDOM_LONG(0, 31); BotNameIndex = RANDOM_LONG(0, 31);
} }
// Retrieve the current bot name and then cycle the index so the names are always unique // Retrieve the next configured bot name from the list
// Slap a [BOT] tag too so players know they're not human string NewName = CONFIG_GetBotPrefix() + CONFIG_GetNextBotName();
string NewName = CONFIG_GetBotPrefix() + BotNames[BotNameIndex];
BotEnt = (*g_engfuncs.pfnCreateFakeClient)(NewName.c_str()); BotEnt = (*g_engfuncs.pfnCreateFakeClient)(NewName.c_str());
@ -1059,6 +1024,9 @@ void AIMGR_NewMap()
bHasRoundStarted = false; bHasRoundStarted = false;
bPlayerSpawned = false; bPlayerSpawned = false;
CONFIG_ParseConfigFile();
CONFIG_PopulateBotNames();
} }
bool AIMGR_IsNavmeshLoaded() bool AIMGR_IsNavmeshLoaded()
@ -1081,8 +1049,6 @@ void AIMGR_LoadNavigationData()
// Don't reload the nav mesh if it's already loaded // Don't reload the nav mesh if it's already loaded
if (NavmeshLoaded()) { return; } if (NavmeshLoaded()) { return; }
CONFIG_ParseConfigFile();
const char* theCStrLevelName = STRING(gpGlobals->mapname); const char* theCStrLevelName = STRING(gpGlobals->mapname);
if (!loadNavigationData(theCStrLevelName)) if (!loadNavigationData(theCStrLevelName))