Files
assaultcube-/source/src/bot/botmanager.cpp
T
KekschenandGitHub b37ced5f3f fixed space
someone missed a space
2022-08-22 05:53:41 +02:00

1220 lines
31 KiB
C++

//
// C++ Implementation: bot
//
// Description: Code for botmanager
//
// Main bot file
//
// Author: Rick <[email protected]>
//
//
//
#include "cube.h"
#include "bot.h"
extern void respawnself();
CBotManager BotManager;
// Bot manager class begin
CBotManager::~CBotManager(void)
{
EndMap();
ClearStoredBots();
}
void CBotManager::Init()
{
m_pBotToView = NULL;
m_iFrameTime = 0;
m_iPrevTime = lastmillis;
m_sBotSkill = 1; // Default all bots have the skill 'Good'
CreateSkillData();
LoadBotNamesFile();
LoadBotTeamsFile();
//WaypointClass.Init();
lsrand(time(NULL));
}
void CBotManager::Think()
{
if (m_bInit)
{
Init();
m_bInit = false;
}
if (m_pBotToView) ViewBot();
AddDebugText("m_sMaxAStarBots: %d", m_sMaxAStarBots);
AddDebugText("m_sCurrentTriggerNr: %d", m_sCurrentTriggerNr);
short x, y;
WaypointClass.GetNodeIndexes(player1->o, &x, &y);
AddDebugText("x: %d y: %d", x, y);
m_iFrameTime = lastmillis - m_iPrevTime;
if (m_iFrameTime > 250) m_iFrameTime = 250;
m_iPrevTime = lastmillis;
// Is it time to re-add bots?
if ((m_fReAddBotDelay < lastmillis) && (m_fReAddBotDelay != -1.0f))
{
while(m_StoredBots.Empty() == false)
{
CStoredBot *pStoredBot = m_StoredBots.Pop();
ReAddBot(pStoredBot);
delete pStoredBot;
}
m_fReAddBotDelay = -1.0f;
}
// Let all bots 'think'
loopv(bots)
{
if (!bots[i]) continue;
if (bots[i]->pBot)
{
bots[i]->pBot->CheckWeaponSwitch(); // 2011jan17:ft: fix non-shooting bots
bots[i]->pBot->Think();
}
else condebug("Error: pBot == NULL in bot ent\n");
}
}
void CBotManager::LoadBotNamesFile()
{
// Init bot names array first
for (int i=0;i<100;i++)
strcpy(m_szBotNames[i], "Bot");
m_sBotNameCount = 0;
// Load bot file
char szNameFileName[256];
MakeBotFileName("bot_names.txt", NULL, NULL, szNameFileName);
FILE *fp = fopen(szNameFileName, "r");
char szNameBuffer[256];
int iIndex, iStrIndex;
if (!fp)
{
conoutf("Warning: Couldn't load bot names file");
return;
}
while (fgets(szNameBuffer, 80, fp) != NULL)
{
if (m_sBotNameCount >= 150)
{
conoutf("Warning: Max bot names reached(150), ignoring the rest of the"
" names");
break;
}
short length = (short)strlen(szNameBuffer);
if (szNameBuffer[length-1] == '\n')
{
szNameBuffer[length-1] = 0; // remove '\n'
length--;
}
iStrIndex = 0;
while (iStrIndex < length)
{
if ((szNameBuffer[iStrIndex] < ' ') || (szNameBuffer[iStrIndex] > '~') ||
(szNameBuffer[iStrIndex] == '"'))
{
for (iIndex=iStrIndex; iIndex < length; iIndex++)
szNameBuffer[iIndex] = szNameBuffer[iIndex+1];
}
iStrIndex++;
}
if (szNameBuffer[0] != 0)
{
if (strlen(szNameBuffer) >= 16)
{ conoutf("Warning: bot name \"%s\" has to many characters(16 is max)",
szNameBuffer);
}
copystring(m_szBotNames[m_sBotNameCount], szNameBuffer, 16);
m_sBotNameCount++;
}
}
fclose(fp);
}
const char *CBotManager::GetBotName()
{
const char *szOutput = NULL;
TMultiChoice<const char *> BotNameChoices;
short ChoiceVal;
for(int j=0;j<m_sBotNameCount;j++)
{
ChoiceVal = 50;
loopv(bots)
{
if (bots[i] && (!strcasecmp(bots[i]->name, m_szBotNames[j])))
{
ChoiceVal = 0;
break;
}
}
if (!strcasecmp(player1->name, m_szBotNames[j]))
ChoiceVal = 0;
if(ChoiceVal) BotNameChoices.Insert(m_szBotNames[j], ChoiceVal);
}
// Couldn't find a selection?
if (!BotNameChoices.GetSelection(szOutput))
szOutput = "Bot";
return szOutput;
}
void CBotManager::LoadBotTeamsFile()
{
// Init bot teams array first
for (int i=0;i<20;i++)
strcpy(m_szBotTeams[i], "b0ts");
m_sBotTeamCount = 0;
// Load bot file
char szNameFileName[256];
MakeBotFileName("bot_teams.txt", NULL, NULL, szNameFileName);
FILE *fp = fopen(szNameFileName, "r");
char szNameBuffer[256];
int iIndex, iStrIndex;
if (!fp)
{
conoutf("Warning: Couldn't load bot teams file");
return;
}
while ((m_sBotTeamCount < 20) && (fgets(szNameBuffer, 80, fp) != NULL))
{
short length = (short)strlen(szNameBuffer);
if (szNameBuffer[length-1] == '\n')
{
szNameBuffer[length-1] = 0; // remove '\n'
length--;
}
iStrIndex = 0;
while (iStrIndex < length)
{
if ((szNameBuffer[iStrIndex] < ' ') || (szNameBuffer[iStrIndex] > '~') ||
(szNameBuffer[iStrIndex] == '"'))
{
for (iIndex=iStrIndex; iIndex < length; iIndex++)
szNameBuffer[iIndex] = szNameBuffer[iIndex+1];
}
iStrIndex++;
}
if (szNameBuffer[0] != 0)
{
copystring(m_szBotTeams[m_sBotTeamCount], szNameBuffer, 5);
m_sBotTeamCount++;
}
}
fclose(fp);
}
const char *CBotManager::GetBotTeam()
{
int teamsize[2] = {0, 0};
int biggestTeam = 0;
if(team_isactive(player1->team))
{
teamsize[player1->team]++;
}
loopv(bots) if(bots[i] && team_isactive(bots[i]->team))
{
teamsize[bots[i]->team]++;
}
biggestTeam = teamsize[1] > teamsize[0];
return teamnames[biggestTeam ^ 1];
// Following code will never be executed.
// (bot teams aren't ready, we use normal teams instead)
const char *szOutput = NULL;
TMultiChoice<const char *> BotTeamChoices;
short ChoiceVal;
for(int j=0;j<m_sBotTeamCount;j++)
{
ChoiceVal = 50;
/* UNDONE?
loopv(bots)
{
if (bots[i] && (!strcasecmp(bots[i]->name, m_szBotNames[j])))
ChoiceVal -= 10;
}
if (!strcasecmp(player1->name, m_szBotNames[j]))
ChoiceVal -= 10;
if (ChoiceVal <= 0)
ChoiceVmonsterclearal = 1;*/
BotTeamChoices.Insert(m_szBotTeams[j], ChoiceVal);
}
// Couldn't find a selection?
if (!BotTeamChoices.GetSelection(szOutput))
szOutput = "b0t";
return szOutput;
}
void CBotManager::RenderBots()
{
//static bool drawblue;
loopv(bots)
{
if (bots[i] && (bots[i] != m_pBotToView))
{
renderclientp(bots[i]);
}
}
}
void CBotManager::EndMap()
{
// Remove all bots
loopv(bots)
{
if(!bots[i] || !bots[i]->pBot)
continue;
// Store bots so they can be re-added after map change
if (bots[i]->pBot && bots[i]->name[0] && team_isactive(bots[i]->team))
{
CStoredBot *pStoredBot = new CStoredBot(bots[i]->name, (char *) team_string(bots[i]->team),
bots[i]->pBot->m_sSkillNr);
m_StoredBots.AddNode(pStoredBot);
}
delete bots[i]->pBot;
bots[i]->pBot = NULL;
freebotent(bots[i]);
}
bots.setsize(0);
condebug("Cleared all bots");
m_fReAddBotDelay = lastmillis + 7500;
//if(ishost()) WaypointClass.SaveWPExpFile(); //UNDONE
}
void CBotManager::BeginMap(const char *szMapName)
{
EndMap(); // End previous map
WaypointClass.Init();
WaypointClass.SetMapName(szMapName);
if (!WaypointClass.LoadWaypoints())
WaypointClass.StartFlood();
//WaypointClass.LoadWPExpFile(); // UNDONE
CalculateMaxAStarCount();
m_sUsingAStarBotsCount = 0;
PickNextTrigger();
}
int CBotManager::GetBotIndex(botent *m)
{
loopv(bots)
{
if (!bots[i])
continue;
if (bots[i] == m)
return i;
}
return -1;
}
void CBotManager::LetBotsUpdateStats()
{
loopv(bots) if (bots[i] && bots[i]->pBot) bots[i]->pBot->m_bSendC2SInit = false;
}
void CBotManager::LetBotsHear(int n, vec *loc)
{
if (bots.length() == 0 || !loc) return;
loopv(bots)
{
if (!bots[i] || !bots[i]->pBot || (bots[i]->state == CS_DEAD)) continue;
bots[i]->pBot->HearSound(n, loc);
}
}
// Notify all bots of a new waypoint
void CBotManager::AddWaypoint(node_s *pNode)
{
if (bots.length())
{
short x, y;
waypoint_s *pWP;
loopv(bots)
{
if (!bots[i] || !bots[i]->pBot) continue;
pWP = new waypoint_s;
pWP->pNode = pNode;
WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
bots[i]->pBot->m_WaypointList[x][y].AddNode(pWP);
#ifndef RELEASE_BUILD
if (!bots[i]->pBot->GetWPFromNode(pNode)) condebug("Error adding bot wp!");
#endif
}
}
CalculateMaxAStarCount();
}
// Notify all bots of a deleted waypoint
void CBotManager::DelWaypoint(node_s *pNode)
{
if (bots.length())
{
short x, y;
TLinkedList<waypoint_s *>::node_s *p;
loopv(bots)
{
if (!bots[i] || !bots[i]->pBot) continue;
WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
p = bots[i]->pBot->m_WaypointList[x][y].GetFirst();
while(p)
{
if (p->Entry->pNode == pNode)
{
delete p->Entry;
bots[i]->pBot->m_WaypointList[x][y].DeleteNode(p);
break;
}
p = p->next;
}
}
}
CalculateMaxAStarCount();
}
void CBotManager::MakeBotFileName(const char *szFileName, const char *szDir1, const char *szDir2, char *szOutput)
{
const char *DirSeperator;
#ifdef WIN32
DirSeperator = "\\";
strcpy(szOutput, "bot\\");
#else
DirSeperator = "/";
strcpy(szOutput, "bot/");
#endif
if (szDir1)
{
strcat(szOutput, szDir1);
strcat(szOutput, DirSeperator);
}
if (szDir2)
{
strcat(szOutput, szDir2);
strcat(szOutput, DirSeperator);
}
strcat(szOutput, szFileName);
}
void CBotManager::CreateSkillData()
{
// First give the bot skill structure some default data
InitSkillData();
// Now see if we can load the skill.cfg file
char SkillFileName[256] = "";
FILE *pSkillFile = NULL;
int SkillNr = -1;
float value = 0;
MakeBotFileName("bot_skill.cfg", NULL, NULL, SkillFileName);
pSkillFile = fopen(SkillFileName, "r");
conoutf("Reading bot_skill.cfg file... ");
if (pSkillFile == NULL) // file doesn't exist
{
conoutf("skill file not found, default settings will be used\n");
return;
}
int ch;
char cmd_line[256];
int cmd_index;
char *cmd, *arg1;
while (pSkillFile)
{
cmd_index = 0;
cmd_line[cmd_index] = 0;
ch = fgetc(pSkillFile);
// skip any leading blanks
while (ch == ' ')
ch = fgetc(pSkillFile);
while ((ch != EOF) && (ch != '\r') && (ch != '\n'))
{
if (ch == '\t') // convert tabs to spaces
ch = ' ';
cmd_line[cmd_index] = ch;
ch = fgetc(pSkillFile);
// skip multiple spaces in input file
while ((cmd_line[cmd_index] == ' ') && (ch == ' '))
ch = fgetc(pSkillFile);
cmd_index++;
}
if (ch == '\r') // is it a carriage return?
{
ch = fgetc(pSkillFile); // skip the linefeed
}
// if reached end of file, then close it
if (ch == EOF)
{
fclose(pSkillFile);
pSkillFile = NULL;
}
cmd_line[cmd_index] = 0; // terminate the command line
cmd_index = 0;
cmd = cmd_line;
arg1 = NULL;
// skip to blank or end of string...
while ((cmd_line[cmd_index] != ' ') && (cmd_line[cmd_index] != 0))
cmd_index++;
if (cmd_line[cmd_index] == ' ')
{
cmd_line[cmd_index++] = 0;
arg1 = &cmd_line[cmd_index];
}
if ((cmd_line[0] == '#') || (cmd_line[0] == 0))
continue; // skip if comment or blank line
if (strcasecmp(cmd, "[SKILL1]") == 0)
SkillNr = 0;
else if (strcasecmp(cmd, "[SKILL2]") == 0)
SkillNr = 1;
else if (strcasecmp(cmd, "[SKILL3]") == 0)
SkillNr = 2;
else if (strcasecmp(cmd, "[SKILL4]") == 0)
SkillNr = 3;
else if (strcasecmp(cmd, "[SKILL5]") == 0)
SkillNr = 4;
if ((arg1 == NULL) || (*arg1 == 0))
continue;
if (SkillNr == -1) // Not in a skill block yet?
continue;
value = atof(arg1);
if (strcasecmp(cmd, "min_x_aim_speed") == 0)
{
m_BotSkills[SkillNr].flMinAimXSpeed = value;
}
else if (strcasecmp(cmd, "max_x_aim_speed") == 0)
{
m_BotSkills[SkillNr].flMaxAimXSpeed = value;
}
else if (strcasecmp(cmd, "min_y_aim_speed") == 0)
{
m_BotSkills[SkillNr].flMinAimYSpeed = value;
}
else if (strcasecmp(cmd, "max_y_aim_speed") == 0)
{
m_BotSkills[SkillNr].flMaxAimYSpeed = value;
}
else if (strcasecmp(cmd, "min_x_aim_offset") == 0)
{
m_BotSkills[SkillNr].flMinAimXOffset = value;
}
else if (strcasecmp(cmd, "max_x_aim_offset") == 0)
{
m_BotSkills[SkillNr].flMaxAimXOffset = value;
}
else if (strcasecmp(cmd, "min_y_aim_offset") == 0)
{
m_BotSkills[SkillNr].flMinAimYOffset = value;
}
else if (strcasecmp(cmd, "max_y_aim_offset") == 0)
{
m_BotSkills[SkillNr].flMaxAimYOffset = value;
}
else if (strcasecmp(cmd, "min_attack_delay") == 0)
{
m_BotSkills[SkillNr].flMinAttackDelay = value;
}
else if (strcasecmp(cmd, "max_attack_delay") == 0)
{
m_BotSkills[SkillNr].flMaxAttackDelay = value;
}
else if (strcasecmp(cmd, "min_enemy_search_delay") == 0)
{
m_BotSkills[SkillNr].flMinEnemySearchDelay = value;
}
else if (strcasecmp(cmd, "max_enemy_search_delay") == 0)
{
m_BotSkills[SkillNr].flMaxEnemySearchDelay = value;
}
else if (strcasecmp(cmd, "max_always_detect_distance") == 0)
{
m_BotSkills[SkillNr].flAlwaysDetectDistance = value;
}
else if (strcasecmp(cmd, "can_predict_position") == 0)
{
m_BotSkills[SkillNr].bCanPredict = value!=0;
}
else if (strcasecmp(cmd, "field_of_view") == 0)
{
if (value < 80) value = 80;
else if (value > 240) value = 120;
m_BotSkills[SkillNr].iFov = (int)value;
}
else if (strcasecmp(cmd, "max_hear_volume") == 0)
{
if (value < 0) value = 0;
else if (value > 255) value = 100;
m_BotSkills[SkillNr].iMaxHearVolume = (int)value;
}
else if (strcasecmp(cmd, "can_circle_strafe") == 0)
{
m_BotSkills[SkillNr].bCircleStrafe = value!=0;
}
else if (strcasecmp(cmd, "can_search_items_in_combat") == 0)
{
m_BotSkills[SkillNr].bCanSearchItemsInCombat = value!=0;
}
}
conoutf("done");
}
void CBotManager::InitSkillData()
{
// Best skill
m_BotSkills[0].flMinReactionDelay = 0.015f;
m_BotSkills[0].flMaxReactionDelay = 0.035f;
m_BotSkills[0].flMinAimXOffset = 15.0f;
m_BotSkills[0].flMaxAimXOffset = 20.0f;
m_BotSkills[0].flMinAimYOffset = 10.0f;
m_BotSkills[0].flMaxAimYOffset = 15.0f;
m_BotSkills[0].flMinAimXSpeed = 330.0f;
m_BotSkills[0].flMaxAimXSpeed = 355.0f;
m_BotSkills[0].flMinAimYSpeed = 400.0f;
m_BotSkills[0].flMaxAimYSpeed = 450.0f;
m_BotSkills[0].flMinAttackDelay = 0.1f;
m_BotSkills[0].flMaxAttackDelay = 0.4f;
m_BotSkills[0].flMinEnemySearchDelay = 0.09f;
m_BotSkills[0].flMaxEnemySearchDelay = 0.12f;
m_BotSkills[0].flAlwaysDetectDistance = 12.0f;
m_BotSkills[0].bCanPredict = true;
m_BotSkills[0].iMaxHearVolume = 75;
m_BotSkills[0].iFov = 200;
m_BotSkills[0].bCircleStrafe = true;
m_BotSkills[0].bCanSearchItemsInCombat = true;
// Good skill
m_BotSkills[1].flMinReactionDelay = 0.035f;
m_BotSkills[1].flMaxReactionDelay = 0.045f;
m_BotSkills[1].flMinAimXOffset = 20.0f;
m_BotSkills[1].flMaxAimXOffset = 25.0f;
m_BotSkills[1].flMinAimYOffset = 15.0f;
m_BotSkills[1].flMaxAimYOffset = 20.0f;
m_BotSkills[1].flMinAimXSpeed = 250.0f;
m_BotSkills[1].flMaxAimXSpeed = 265.0f;
m_BotSkills[1].flMinAimYSpeed = 260.0f;
m_BotSkills[1].flMaxAimYSpeed = 285.0f;
m_BotSkills[1].flMinAttackDelay = 0.3f;
m_BotSkills[1].flMaxAttackDelay = 0.6f;
m_BotSkills[1].flMinEnemySearchDelay = 0.12f;
m_BotSkills[1].flMaxEnemySearchDelay = 0.17f;
m_BotSkills[1].flAlwaysDetectDistance = 10.0f;
m_BotSkills[1].bCanPredict = true;
m_BotSkills[1].iMaxHearVolume = 60;
m_BotSkills[1].iFov = 160;
m_BotSkills[1].bCircleStrafe = true;
m_BotSkills[1].bCanSearchItemsInCombat = true;
// Medium skill
m_BotSkills[2].flMinReactionDelay = 0.075f;
m_BotSkills[2].flMaxReactionDelay = 0.010f;
m_BotSkills[2].flMinAimXOffset = 25.0f;
m_BotSkills[2].flMaxAimXOffset = 30.0f;
m_BotSkills[2].flMinAimYOffset = 20.0f;
m_BotSkills[2].flMaxAimYOffset = 25.0f;
m_BotSkills[2].flMinAimXSpeed = 190.0f;
m_BotSkills[2].flMaxAimXSpeed = 125.0f;
m_BotSkills[2].flMinAimYSpeed = 210.0f;
m_BotSkills[2].flMaxAimYSpeed = 240.0f;
m_BotSkills[2].flMinAttackDelay = 0.75f;
m_BotSkills[2].flMaxAttackDelay = 1.0f;
m_BotSkills[2].flMinEnemySearchDelay = 0.18f;
m_BotSkills[2].flMaxEnemySearchDelay = 0.22f;
m_BotSkills[2].flAlwaysDetectDistance = 8.0f;
m_BotSkills[2].bCanPredict = false;
m_BotSkills[2].iMaxHearVolume = 45;
m_BotSkills[2].iFov = 130;
m_BotSkills[2].bCircleStrafe = true;
m_BotSkills[2].bCanSearchItemsInCombat = false;
// Worse skill
m_BotSkills[3].flMinReactionDelay = 0.15f;
m_BotSkills[3].flMaxReactionDelay = 0.20f;
m_BotSkills[3].flMinAimXOffset = 30.0f;
m_BotSkills[3].flMaxAimXOffset = 35.0f;
m_BotSkills[3].flMinAimYOffset = 25.0f;
m_BotSkills[3].flMaxAimYOffset = 30.0f;
m_BotSkills[3].flMinAimXSpeed = 155.0f;
m_BotSkills[3].flMaxAimXSpeed = 170.0f;
m_BotSkills[3].flMinAimYSpeed = 160.0f;
m_BotSkills[3].flMaxAimYSpeed = 210.0f;
m_BotSkills[3].flMinAttackDelay = 1.2f;
m_BotSkills[3].flMaxAttackDelay = 1.6f;
m_BotSkills[3].flMinEnemySearchDelay = 0.25f;
m_BotSkills[3].flMaxEnemySearchDelay = 0.30f;
m_BotSkills[3].flAlwaysDetectDistance = 6.0f;
m_BotSkills[3].bCanPredict = false;
m_BotSkills[3].iMaxHearVolume = 30;
m_BotSkills[3].iFov = 120;
m_BotSkills[3].bCircleStrafe = false;
m_BotSkills[3].bCanSearchItemsInCombat = false;
// Bad skill
m_BotSkills[4].flMinReactionDelay = 0.30f;
m_BotSkills[4].flMaxReactionDelay = 0.50f;
m_BotSkills[4].flMinAimXOffset = 35.0f;
m_BotSkills[4].flMaxAimXOffset = 40.0f;
m_BotSkills[4].flMinAimYOffset = 30.0f;
m_BotSkills[4].flMaxAimYOffset = 35.0f;
m_BotSkills[4].flMinAimXSpeed = 45.0f;
m_BotSkills[4].flMaxAimXSpeed = 60.0f;
m_BotSkills[4].flMinAimYSpeed = 125.0f;
m_BotSkills[4].flMaxAimYSpeed = 180.0f;
m_BotSkills[4].flMinAttackDelay = 1.5f;
m_BotSkills[4].flMaxAttackDelay = 2.0f;
m_BotSkills[4].flMinEnemySearchDelay = 0.30f;
m_BotSkills[4].flMaxEnemySearchDelay = 0.36f;
m_BotSkills[4].flAlwaysDetectDistance = 4.0f;
m_BotSkills[4].bCanPredict = false;
m_BotSkills[4].iMaxHearVolume = 15;
m_BotSkills[4].iFov = 110;
m_BotSkills[4].bCircleStrafe = false;
m_BotSkills[4].bCanSearchItemsInCombat = false;
}
void CBotManager::ChangeBotSkill(short Skill, botent *bot)
{
static const char *SkillNames[5] = { "best", "good", "medium", "worse", "bad" };
if (bot && bot->pBot)
{
// Only change skill of a single bot
bot->pBot->m_pBotSkill = &m_BotSkills[Skill];
bot->pBot->m_sSkillNr = Skill;
conoutf("Skill of %s is now %s", bot->name, SkillNames[Skill]);
return;
}
// Change skill of all bots
loopv(bots)
{
if (!bots[i] || !bots[i]->pBot) continue;
bots[i]->pBot->m_pBotSkill = &m_BotSkills[Skill];
bots[i]->pBot->m_sSkillNr = Skill;
}
// Change default bot skill
m_sBotSkill = Skill;
conoutf("Skill of all bots is now %s", SkillNames[Skill]);
}
void CBotManager::ViewBot()
{
// Check if this bot is still in game
bool bFound = false;
loopv(bots)
{
if (bots[i] == m_pBotToView)
{
bFound = true;
break;
}
}
if (!bFound)
{
DisableBotView();
return;
}
player1->state = CS_DEAD; // Fake dead
player1->o = m_pBotToView->o;
player1->o.z += 1.0f;
player1->yaw = m_pBotToView->yaw;
player1->pitch = m_pBotToView->pitch;
player1->roll = m_pBotToView->roll;
player1->radius = 0; // Don't collide
player1->resetinterp();
}
void CBotManager::DisableBotView()
{
m_pBotToView = NULL;
respawnself();
player1->radius = 1.1f;
}
void CBotManager::CalculateMaxAStarCount()
{
if (WaypointClass.m_iWaypointCount > 0) // Are there any waypoints?
{
m_sMaxAStarBots = 8 - short(ceil((float)WaypointClass.m_iWaypointCount /
1000.0f));
if (m_sMaxAStarBots < 1)
m_sMaxAStarBots = 1;
}
else
m_sMaxAStarBots = 1;
}
void CBotManager::PickNextTrigger()
{
short lowest = -1;
bool found0 = false; // True if found a trigger with nr 0
loopv(ents)
{
entity &e = ents[i];
if (OUTBORD(e.x, e.y)) continue;
vec o(e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight);
node_s *pWptNearEnt = NULL;
pWptNearEnt = WaypointClass.GetNearestTriggerWaypoint(o, 2.0f);
if (pWptNearEnt)
{
if ((pWptNearEnt->sTriggerNr > 0) &&
((pWptNearEnt->sTriggerNr < lowest) || (lowest == -1)))
lowest = pWptNearEnt->sTriggerNr;
if (pWptNearEnt->sTriggerNr == 0) found0 = true;
}
#ifdef WP_FLOOD
pWptNearEnt = WaypointClass.GetNearestTriggerFloodWP(o, 2.0f);
if (pWptNearEnt)
{
if ((pWptNearEnt->sTriggerNr > 0) &&
((pWptNearEnt->sTriggerNr < lowest) || (lowest == -1)))
lowest = pWptNearEnt->sTriggerNr;
if (pWptNearEnt->sTriggerNr == 0) found0 = true;
}
#endif
}
if ((lowest == -1) && found0) lowest = 0;
if (lowest != -1)
m_sCurrentTriggerNr = lowest;
}
botent *CBotManager::CreateBot(const char *team, const char *skill, const char *name)
{
if (m_bInit)
{
Init();
m_bInit = false;
}
botent *m = newbotent();
if (!m) return NULL;
loopi(NUMGUNS) m->ammo[i] = m->mag[i] = 0;
m->lifesequence = 0;
m->pBot = new CACBot;
m->type = ENT_BOT;
m->pBot->m_pMyEnt = m;
m->pBot->m_iLastBotUpdate = 0;
m->pBot->m_bSendC2SInit = false;
if (name && *name) copystring(m->name, name, 16);
else copystring(m->name, BotManager.GetBotName(), 16);
updateclientname((playerent *)m);
const char *tempteam = team && *team && strcmp(team, "random") ? team : BotManager.GetBotTeam();
m->team = team_base(teamatoi(tempteam));
setskin(m, m->team == TEAM_CLA ? rnd(4) : rnd(6), m->team);
if (skill && *skill && strcmp(skill, "random"))
{
if (!strcasecmp(skill, "best")) m->pBot->m_sSkillNr = 0;
else if (!strcasecmp(skill, "good")) m->pBot->m_sSkillNr = 1;
else if (!strcasecmp(skill, "medium")) m->pBot->m_sSkillNr = 2;
else if (!strcasecmp(skill, "worse")) m->pBot->m_sSkillNr = 3;
else if (!strcasecmp(skill, "bad")) m->pBot->m_sSkillNr = 4;
else
{
conoutf("Wrong skill specified. Should be best, good, medium, "
"worse or bad");
conoutf("Using default skill instead...");
m->pBot->m_sSkillNr = BotManager.m_sBotSkill;
}
}
else // No skill specified, use default
m->pBot->m_sSkillNr = BotManager.m_sBotSkill;
m->pBot->m_pBotSkill = &BotManager.m_BotSkills[m->pBot->m_sSkillNr];
// Sync waypoints
m->pBot->SyncWaypoints();
m->pBot->Spawn();
bots.add(m);
return m;
}
bool botmode()
{
if(m_botmode) return true;
conoutf("the current game mode does not support bots");
return false;
}
// Bot manager class end
void addbot(char *arg1, char *arg2, char *arg3)
{
if(!botmode()) return;
botent *b = BotManager.CreateBot(arg1, arg2, arg3);
if (b) conoutf("Bot connected: %s", b->name);
else { conoutf("Error: Couldn't create bot!"); return; }
}
COMMAND(addbot, "sss");
void addnbot(char *arg1, char *arg2, char *arg3)
{
if(!botmode()) return;
if (!arg1 || !arg1[0]) return;
int i = atoi(arg1);
while(i > 0)
{
addbot(arg2, arg3, NULL);
i--;
}
}
COMMAND(addnbot, "sss");
void botsshoot(int *Shoot)
{
switch(*Shoot)
{
case 0:
BotManager.SetBotsShoot(false);
conoutf("Bots won't shoot");
break;
case 1:
BotManager.SetBotsShoot(true);
conoutf("Bots will shoot");
break;
default:
intret(BotManager.BotsShoot()); break;
}
}
COMMAND(botsshoot, "i");
void idlebots(int *Idle)
{
switch(*Idle)
{
case 0:
BotManager.SetIdleBots(false);
conoutf("Bots aren't idle");
break;
case 1:
BotManager.SetIdleBots(true);
conoutf("Bots are idle");
break;
default:
intret(BotManager.IdleBots()); break;
}
}
COMMAND(idlebots, "i");
void kickbot(const char *szName)
{
if(!botmode()) return;
if (!szName || !(*szName))
return;
int iBotInd = -1;
loopv(bots)
{
if (!bots[i]) continue;
if (!strcmp(bots[i]->name, szName))
{
iBotInd = i;
break;
}
}
if (iBotInd != -1)
{
botent *d = bots[iBotInd];
if(d->name[0]) conoutf("bot %s disconnected", d->name);
delete d->pBot;
bots.remove(iBotInd);
freebotent(d);
}
}
COMMAND(kickbot, "s");
void kickallbots(void)
{
BotManager.ClearStoredBots();
loopv(bots)
{
if (bots[i])
{
if(bots[i]->name[0]) conoutf("bot %s disconnected", bots[i]->name);
delete bots[i]->pBot;
freebotent(bots[i]);
}
};
bots.setsize(0);
}
COMMAND(kickallbots, "");
void togglebotview(char *bot)
{
if(!botmode()) return;
/**
Disable in arena modes, this command causes the game to go in an "infinite loop"
due to player1 automatically suiciding thus causing a new round to begin.
*/
if(m_arena) { conoutf("togglebotview is not allowed in %s", modestr(gamemode, modeacronyms > 0)); return; }
if (BotManager.m_pBotToView)
BotManager.DisableBotView();
else if (bot && *bot)
{
loopv(bots)
{
if (!bots[i]) continue;
if (!strcmp(bots[i]->name, bot))
{
BotManager.EnableBotView(bots[i]);
break;
}
}
}
}
COMMAND(togglebotview, "s");
void botskill(char *bot, char *skill)
{
if (!skill || !(*skill))
return;
short SkillNr;
if (!strcasecmp(skill, "best"))
SkillNr = 0;
else if (!strcasecmp(skill, "good"))
SkillNr = 1;
else if (!strcasecmp(skill, "medium"))
SkillNr = 2;
else if (!strcasecmp(skill, "worse"))
SkillNr = 3;
else if (!strcasecmp(skill, "bad"))
SkillNr = 4;
else
{
conoutf("Wrong skill specified. Should be best, good, medium, worse or bad");
return;
}
if (bot)
{
loopv(bots)
{
if (bots[i] && !strcmp(bots[i]->name, bot))
{
BotManager.ChangeBotSkill(SkillNr, bots[i]);
break;
}
}
}
else
BotManager.ChangeBotSkill(SkillNr);
}
COMMAND(botskill, "ss");
void botskillall(char *skill)
{
botskill(NULL, skill);
}
COMMAND(botskillall, "s");
#ifndef RELEASE_BUILD
void telebot(void)
{
vec dest = player1->o, forward, right, up;
vec angles(player1->pitch, player1->yaw, player1->roll);
traceresult_s tr;
AnglesToVectors(angles, forward, right, up);
forward.mul(4.0f);
dest.add(forward);
TraceLine(player1->o, dest, player1, true, &tr);
if (!tr.collided)
{
// Get the first bot
loopv(bots)
{
if (!bots[i] || !bots[i]->pBot) continue;
bots[i]->o = tr.end;
bots[i]->resetinterp();
break;
}
}
}
COMMAND(telebot, "");
void testvisible(int iDir)
{
vec angles, end, forward, right, up;
traceresult_s tr;
int Dir;
switch(iDir)
{
case 0: default: Dir = FORWARD; break;
case 1: Dir = BACKWARD; break;
case 2: Dir = LEFT; break;
case 3: Dir = RIGHT; break;
case 4: Dir = UP; break;
case 5: Dir = DOWN; break;
}
vec from = player1->o;
from.z -= (player1->eyeheight - 1.25f);
end = from;
makevec(&angles, player1->pitch, player1->yaw, player1->roll);
angles.x=0;
if (Dir & UP)
angles.x = WrapXAngle(angles.x + 45.0f);
else if (Dir & DOWN)
angles.x = WrapXAngle(angles.x - 45.0f);
if ((Dir & FORWARD) || (Dir & BACKWARD))
{
if (Dir & BACKWARD)
angles.y = WrapYZAngle(angles.y + 180.0f);
if (Dir & LEFT)
{
if (Dir & FORWARD)
angles.y = WrapYZAngle(angles.y - 45.0f);
else
angles.y = WrapYZAngle(angles.y + 45.0f);
}
else if (Dir & RIGHT)
{
if (Dir & FORWARD)
angles.y = WrapYZAngle(angles.y + 45.0f);
else
angles.y = WrapYZAngle(angles.y - 45.0f);
}
}
else if (Dir & LEFT)
angles.y = WrapYZAngle(angles.y - 90.0f);
else if (Dir & RIGHT)
angles.y = WrapYZAngle(angles.y + 90.0f);
else if (Dir & UP)
angles.x = WrapXAngle(angles.x + 90.0f);
else if (Dir & DOWN)
angles.x = WrapXAngle(angles.x - 90.0f);
AnglesToVectors(angles, forward, right, up);
forward.mul(20.0f);
end.add(forward);
TraceLine(from, end, player1, false, &tr);
//debugbeam(from, tr.end);
char sz[250];
sprintf(sz, "dist: %f; hit: %d", GetDistance(from, tr.end), tr.collided);
condebug(sz);
}
COMMANDF(testvisible, "i", (int *dir) { testvisible(*dir); });
#endif