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 <unordered_map>
#include <algorithm>
#include <random>
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 };
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] = "";
extern cvar_t avh_botskill;
@ -110,6 +149,69 @@ bot_skill CONFIG_GetBotSkillLevel()
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()
{

View file

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

View file

@ -6213,7 +6213,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
{
if (bIsFlyingProfile)
{
BotFollowFlightPath(pBot);
BotFollowFlightPath(pBot, true);
}
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())
{
@ -6392,7 +6392,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
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);
CurrentPathPoint = (BotNavInfo->CurrentPath.begin() + BotNavInfo->CurrentPathPoint);
@ -6400,7 +6400,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
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);
return;
@ -6561,7 +6561,7 @@ void BotFollowSwimPath(AvHAIPlayer* pBot)
// We're at the surface, now tackle the path the usual way
if (pBot->BotNavInfo.NavProfile.bFlyingProfile)
{
BotFollowFlightPath(pBot);
BotFollowFlightPath(pBot, true);
}
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))
{
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
void BotFollowPath(AvHAIPlayer* pBot);
void BotFollowFlightPath(AvHAIPlayer* pBot);
void BotFollowFlightPath(AvHAIPlayer* pBot, bool bAllowSkip);
void BotFollowSwimPath(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
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;
}
@ -5039,7 +5039,7 @@ void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
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)
{
@ -5060,11 +5060,11 @@ void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
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;
}
else if (pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR))
else if (CONFIG_IsFadeAllowed() && pBot->ExperiencePointsAvailable >= GetGameRules()->GetCostForMessageID(ALIEN_LIFEFORM_FOUR))
{
DesiredEvolution = ALIEN_LIFEFORM_FOUR;
}
@ -5587,8 +5587,6 @@ void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination)
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))
{
pBot->Player->SetUser3(AVH_USER3_MARINE_PLAYER);

View file

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