//
// C++ Implementation: bot
//
// Description: Code for botmanager
//
// Main bot file
//
// Author: Rick <rickhelmus@gmail.com>
//
//
//
#include "cube.h"
#include "bot.h"
extern void respawnself();
CBotManager BotManager;
// Bot manager class begin
CBotManager::~CBotManager(void)
{
EndMap();
}
void CBotManager::Init()
{
m_bBotsShoot = true;
m_bIdleBots = false;
m_iFrameTime = 0;
m_iPrevTime = lastmillis;
LoadBotNamesFile();
//WaypointClass.Init();
lsrand(time(NULL));
}
void CBotManager::Think()
{
if (m_bInit)
{
Init();
m_bInit = false;
}
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;
// Added by Victor: control multiplayer bots
const int ourcn = getclientnum();
if(ourcn >= 0 && m_ai(gamemode))
{
// handle the bots
loopv(players)
{
if(!players[i] || players[i]->ownernum != ourcn) continue;
playerent *b = players[i];
// ensure there is a bot controller
if(!b->pBot)
{
b->pBot = new CACBot;
b->pBot->m_pMyEnt = b;
// create skills
b->pBot->m_pBotSkill = NULL;
b->pBot->MakeSkill(b->level);
// Sync waypoints
b->pBot->SyncWaypoints();
// Try spawn
b->pBot->Spawn();
}
b->pBot->Think();
}
}
}
void CBotManager::LoadBotNamesFile()
{
// Init bot names array first
for (int i=0;i<MAXBOTNAMES;i++)
strcpy(m_szBotNames[i], "Bot");
m_sBotNameCount = 0;
// Load bot file
char szNameFileName[256];
MakeBotFileName("bot_names.txt", 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 >= MAXBOTNAMES)
{
conoutf("Warning: Max bot names reached (%d), ignoring the rest of the names", MAXBOTNAMES);
break;
}
// Skip entries starting with //
if(szNameBuffer[0] == '/' && szNameBuffer[0] == szNameBuffer[1])
continue;
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) > MAXNAMELEN)
conoutf("Warning: bot name \"%s\" has too many characters (%d is max)", szNameBuffer, MAXNAMELEN);
copystring(m_szBotNames[m_sBotNameCount], szNameBuffer, MAXNAMELEN);
m_sBotNameCount++;
}
}
fclose(fp);
}
void CBotManager::GetBotName(int seed, playerent *pl)
{
if(!m_sBotNameCount)
{
copystring(pl->name, "a bot");
return;
}
// Use a random name
const char *name = m_szBotNames[detrnd(seed, m_sBotNameCount)];
const char *rank = ""; // prepend rank (only if based on skill)
if(name[0] == '*') // rank is based on skill
{
name += name[1] == ' ' ? 2 : 1; // skip * and space
if(pl->level >= 90) rank = "Lt. "; // 10%
else if(pl->level >= 70) rank = "Sgt. "; // 20%
else if(pl->level >= 40) rank = "Cpl. "; // 30%
else if(pl->level >= 20) rank = "Pfc. "; // 20%
else rank = "Pvt. "; // 20%
}
defformatstring(fname)("%s%s", rank, name);
filtername(pl->name, fname);
}
void playerent::removeai()
{
if(pBot)
{
DELETEP(pBot->m_pBotSkill);
DELETEP(pBot);
}
loopv(players)
if(players[i] && this == players[i]->enemy)
players[i]->enemy = NULL;
}
void CBotManager::EndMap()
{
// Remove all bots
loopv(players)
if(players[i])
players[i]->removeai();
}
void CBotManager::BeginMap(const char *szMapName)
{
EndMap(); // End previous map
WaypointClass.Init();
WaypointClass.SetMapName(szMapName);
if (szMapName[0] && !WaypointClass.LoadWaypoints())
WaypointClass.StartFlood();
//WaypointClass.LoadWPExpFile(); // UNDONE
CalculateMaxAStarCount();
m_sUsingAStarBotsCount = 0;
PickNextTrigger();
}
void CBotManager::LetBotsHear(int n, const vec *loc)
{
if (!loc) return;
loopv(players)
{
if (!players[i] || players[i]->ownernum != player1->clientnum || !players[i]->pBot || players[i]->state == CS_DEAD) continue;
players[i]->pBot->HearSound(n, loc);
}
}
// Notify all bots of a new waypoint
void CBotManager::AddWaypoint(node_s *pNode)
{
if (players.length())
{
short x, y;
waypoint_s *pWP;
loopv(players)
{
if (!players[i] || !players[i]->pBot) continue;
pWP = new waypoint_s;
pWP->pNode = pNode;
WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
players[i]->pBot->m_WaypointList[x][y].AddNode(pWP);
#ifndef RELEASE_BUILD
if (!players[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 (players.length())
{
short x, y;
TLinkedList<waypoint_s *>::node_s *p;
loopv(players)
{
if (!players[i] || !players[i]->pBot) continue;
WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
p = players[i]->pBot->m_WaypointList[x][y].GetFirst();
while(p)
{
if (p->Entry->pNode == pNode)
{
delete p->Entry;
players[i]->pBot->m_WaypointList[x][y].DeleteNode(p);
break;
}
p = p->next;
}
}
}
CalculateMaxAStarCount();
}
void CBotManager::MakeBotFileName(const char *szFileName, const char *szDir1, 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);
}
strcat(szOutput, szFileName);
}
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 defined AC_CUBE
/* if ((e.type != TRIGGER) || !e.spawned)
continue;*/
#elif defined VANILLA_CUBE
if ((e.type != CARROT) || !e.spawned)
continue;
#endif
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;
}
// Bot manager class end
#ifndef RELEASE_BUILD
#ifdef VANILLA_CUBE
void drawbeamtocarrots()
{
loopv(ents)
{
entity &e = ents[i];
vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
if ((e.type != CARROT) || !e.spawned) continue;
particle_trail(PART_SMOKE, 500, player1->o, o);
}
}
COMMAND(drawbeamtocarrots, "");
void drawbeamtoteleporters()
{
loopv(ents)
{
entity &e = ents[i];
vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
if (e.type != TELEPORT) continue;
particle_trail(PART_SMOKE, 500, player1->o, o);
}
}
COMMAND(drawbeamtoteleporters, "");
#endif
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); });
void mapsize(void)
{
switch(ssize)
{
case 128: intret(7); break;
case 256: intret(8); break;
case 512: intret(9); break;
case 1024: intret(10); break;
case 2048: intret(11); break;
case 4096: intret(12); break;
default: intret(6); break;
}
}
COMMAND(mapsize, "");
#endif
Skip to content
153
pages