rpgxef/code/game/g_bot.c

1094 lines
23 KiB
C
Raw Normal View History

// Copyright (C) 1999-2000 Id Software, Inc.
//
// g_bot.c
#include "g_local.h"
2013-05-16 21:54:40 +00:00
#include "g_main.h"
#include "g_client.h"
#include "g_syscalls.h"
#include "ai_main.h"
static int32_t g_numBots;
static char* g_botInfos[MAX_BOTS];
int32_t g_numArenas;
static char* g_arenaInfos[MAX_ARENAS];
2014-11-10 19:58:55 +00:00
static const uint16_t BOT_BEGIN_DELAY_BASE = 2000;
static const uint16_t BOT_BEGIN_DELAY_INCREMENT = 2000;
2014-11-10 20:17:30 +00:00
enum g_botLimits_e {
BOT_SPAWN_QUEUE_DEPTH = 16
};
typedef struct {
int32_t clientNum;
int32_t spawnTime;
} botSpawnQueue_t;
static int32_t botBeginDelay;
static botSpawnQueue_t botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH];
vmCvar_t bot_minplayers;
extern gentity_t* podium1;
extern gentity_t* podium2;
extern gentity_t* podium3;
/*
===============
G_ParseInfos
===============
*/
2014-11-10 20:17:30 +00:00
int32_t G_ParseInfos(char* buf, int32_t max, char* infos[]) {
char* token = NULL;
int32_t count = 0;
char key[MAX_TOKEN_CHARS];
char info[MAX_INFO_STRING];
count = 0;
2014-11-10 20:17:30 +00:00
while (qtrue) {
token = COM_Parse(&buf);
if (token == NULL || token[0] == 0) {
break;
}
2014-11-10 20:17:30 +00:00
if (strcmp(token, "{") != 0) {
Com_Printf("Missing { in info file\n");
break;
}
2014-11-10 20:17:30 +00:00
if (count == max) {
Com_Printf("Max infos exceeded\n");
break;
}
info[0] = '\0';
2014-11-10 20:17:30 +00:00
while (qtrue) {
token = COM_ParseExt(&buf, qtrue);
if (token == NULL || token[0] == 0) {
Com_Printf("Unexpected end of info file\n");
break;
}
2014-11-10 20:17:30 +00:00
if (strcmp(token, "}") == 0) {
break;
}
strncpy(key, token, sizeof(key));
2014-11-10 20:17:30 +00:00
token = COM_ParseExt(&buf, qfalse);
if (token == NULL || token[0] == 0) {
strcpy(token, "<NULL>");
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(info, key, token);
}
//NOTE: extra space for arena number
infos[count] = G_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
if (infos[count] != NULL) {
strcpy(infos[count], info);
count++;
}
}
return count;
}
/*
===============
G_LoadArenasFromFile
===============
*/
2014-11-10 20:17:30 +00:00
static void G_LoadArenasFromFile(char* filename) {
int32_t len = 0;
fileHandle_t f = 0;
char buf[MAX_ARENAS_TEXT];
2014-11-10 20:17:30 +00:00
len = trap_FS_FOpenFile(filename, &f, FS_READ);
if (f == 0) {
trap_Printf(va(S_COLOR_RED "file not found: %s\n", filename));
return;
}
2014-11-10 20:17:30 +00:00
if (len >= MAX_ARENAS_TEXT) {
trap_Printf(va(S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT));
trap_FS_FCloseFile(f);
return;
}
2014-11-10 20:17:30 +00:00
trap_FS_Read(buf, len, f);
buf[len] = 0;
2014-11-10 20:17:30 +00:00
trap_FS_FCloseFile(f);
2014-11-10 20:17:30 +00:00
g_numArenas += G_ParseInfos(buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas]);
}
/*
===============
G_LoadArenas
===============
*/
2014-11-10 20:17:30 +00:00
static void G_LoadArenas(void) {
int32_t numdirs = 0;
vmCvar_t arenasFile;
char filename[128];
char dirlist[4096];
char* dirptr = NULL;
int32_t i = 0;
int32_t n = 0;
int32_t dirlen = 0;
g_numArenas = 0;
2014-11-10 20:17:30 +00:00
trap_Cvar_Register(&arenasFile, "g_arenasFile", "", CVAR_INIT | CVAR_ROM);
if (*arenasFile.string) {
G_LoadArenasFromFile(arenasFile.string);
2014-11-10 20:17:30 +00:00
}
else {
G_LoadArenasFromFile("scripts/arenas.txt");
}
// get all arenas from .arena files
2014-11-10 20:17:30 +00:00
numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, sizeof(dirlist));
dirptr = dirlist;
for (i = 0; i < numdirs; i++, dirptr += dirlen + 1)
{
dirlen = strlen(dirptr);
strcpy(filename, "scripts/");
strcat(filename, dirptr);
G_LoadArenasFromFile(filename);
}
2014-11-10 20:17:30 +00:00
trap_Printf(va("%i arenas parsed\n", g_numArenas));
for (n = 0; n < g_numArenas; n++) {
Info_SetValueForKey(g_arenaInfos[n], "num", va("%i", n));
}
}
/*
===============
G_GetArenaInfoByNumber
===============
*/
2014-11-10 20:17:30 +00:00
const char* G_GetArenaInfoByMap(const char* map) {
int32_t n = 0;
2014-11-10 20:17:30 +00:00
for (n = 0; n < g_numArenas; n++) {
if (Q_stricmp(Info_ValueForKey(g_arenaInfos[n], "map"), map) == 0) {
return g_arenaInfos[n];
}
}
return NULL;
}
/*
=================
PlayerIntroSound
=================
*/
2014-11-10 20:17:30 +00:00
static void PlayerIntroSound(const char* modelAndSkin) {
char model[MAX_QPATH];
char* skin = NULL;
strncpy(model, modelAndSkin, sizeof(model));
skin = strchr(model, '/');
2014-11-10 20:17:30 +00:00
if (skin != NULL) {
*skin++ = '\0';
2014-11-10 20:17:30 +00:00
}
else {
skin = model;
}
2014-11-10 20:17:30 +00:00
if (Q_stricmp(skin, "default") == 0) {
skin = model;
}
2014-11-10 20:17:30 +00:00
if (Q_stricmp(skin, "red") == 0) {
skin = model;
}
2014-11-10 20:17:30 +00:00
if (Q_stricmp(skin, "blue") == 0) {
skin = model;
}
//precached in cg_info.c, CG_LoadingClient
2014-11-10 20:17:30 +00:00
trap_SendConsoleCommand(EXEC_APPEND, va("play sound/voice/computer/misc/%s.wav\n", skin));
}
/*
===============
G_AddRandomBot
===============
*/
2014-11-10 20:17:30 +00:00
void G_AddRandomBot(int32_t team) {
int32_t i = 0;
int32_t n = 0;
int32_t num = 0;
int32_t skill = 0;
char* value = NULL;
char netname[36];
char* teamstr = NULL;
gclient_t* cl = NULL;
num = 0;
2014-11-10 20:17:30 +00:00
for (n = 0; n < g_numBots; n++) {
value = Info_ValueForKey(g_botInfos[n], "name");
//
2014-11-10 20:17:30 +00:00
for (i = 0; i < g_maxclients.integer; i++) {
cl = level.clients + i;
2014-11-10 20:17:30 +00:00
if (cl->pers.connected != CON_CONNECTED) {
continue;
}
2014-11-10 20:17:30 +00:00
if ((g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) == 0) {
continue;
}
2014-11-10 20:17:30 +00:00
if (team >= 0 && cl->sess.sessionTeam != team) {
continue;
}
2014-11-10 20:17:30 +00:00
if (Q_stricmp(value, cl->pers.netname) == 0) {
break;
}
}
if (i >= g_maxclients.integer) {
num++;
}
}
num = random() * num;
2014-11-10 20:17:30 +00:00
for (n = 0; n < g_numBots; n++) {
value = Info_ValueForKey(g_botInfos[n], "name");
//
2014-11-10 20:17:30 +00:00
for (i = 0; i < g_maxclients.integer; i++) {
cl = level.clients + i;
2014-11-10 20:17:30 +00:00
if (cl->pers.connected != CON_CONNECTED) {
continue;
}
2014-11-10 20:17:30 +00:00
if ((g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) == 0) {
continue;
}
2014-11-10 20:17:30 +00:00
if (team >= 0 && cl->sess.sessionTeam != team) {
continue;
}
2014-11-10 20:17:30 +00:00
if (Q_stricmp(value, cl->pers.netname) == 0) {
break;
}
}
if (i >= g_maxclients.integer) {
num--;
if (num <= 0) {
2014-11-10 20:17:30 +00:00
skill = trap_Cvar_VariableIntegerValue("g_spSkill");
if (team == TEAM_RED) {
teamstr = "red";
2014-11-10 20:17:30 +00:00
}
else if (team == TEAM_BLUE) {
teamstr = "blue";
2014-11-10 20:17:30 +00:00
}
else {
teamstr = "";
}
strncpy(netname, value, sizeof(netname)-1);
netname[sizeof(netname)-1] = '\0';
Q_CleanStr(netname);
2014-11-10 20:17:30 +00:00
trap_SendConsoleCommand(EXEC_INSERT, va("addbot %s %i %s %i\n", netname, skill, teamstr, 0));
return;
}
}
}
}
/*
===============
G_RemoveRandomBot
===============
*/
2014-11-10 20:17:30 +00:00
qboolean G_RemoveRandomBot(int32_t team) {
int32_t i = 0;
char netname[36];
gclient_t* cl = NULL;
2014-11-10 20:17:30 +00:00
for (i = 0; i < g_maxclients.integer; i++) {
cl = level.clients + i;
2014-11-10 20:17:30 +00:00
if (cl->pers.connected != CON_CONNECTED) {
continue;
}
2014-11-10 20:17:30 +00:00
if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) {
continue;
}
2014-11-10 20:17:30 +00:00
if (team >= 0 && cl->sess.sessionTeam != team) {
continue;
}
strcpy(netname, cl->pers.netname);
Q_CleanStr(netname);
2014-11-10 20:17:30 +00:00
trap_SendConsoleCommand(EXEC_INSERT, va("kick \"%s\"\n", netname));
return qtrue;
}
return qfalse;
}
/*
===============
G_CountHumanPlayers
===============
*/
2014-11-10 20:17:30 +00:00
int32_t G_CountHumanPlayers(int32_t team) {
int32_t i = 0;
int32_t num = 0;
gclient_t* cl = NULL;
num = 0;
2014-11-10 20:17:30 +00:00
for (i = 0; i < g_maxclients.integer; i++) {
cl = level.clients + i;
2014-11-10 20:17:30 +00:00
if (cl->pers.connected != CON_CONNECTED) {
continue;
}
2014-11-10 20:17:30 +00:00
if (g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) {
continue;
}
2014-11-10 20:17:30 +00:00
if (team >= 0 && cl->sess.sessionTeam != team) {
continue;
}
num++;
}
return num;
}
/*
===============
G_CountBotPlayers
===============
*/
2014-11-10 20:17:30 +00:00
int32_t G_CountBotPlayers(int32_t team) {
int32_t i = 0;
int32_t n = 0;
int32_t num = 0;
gclient_t* cl = NULL;
2014-11-10 20:17:30 +00:00
for (; i < g_maxclients.integer; i++) {
cl = level.clients + i;
2014-11-10 20:17:30 +00:00
if (cl->pers.connected != CON_CONNECTED) {
continue;
}
2014-11-10 20:17:30 +00:00
if ((g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) == 0) {
continue;
}
2014-11-10 20:17:30 +00:00
if (team >= 0 && cl->sess.sessionTeam != team) {
continue;
}
num++;
}
2014-11-10 20:17:30 +00:00
for (; n < BOT_SPAWN_QUEUE_DEPTH; n++) {
if (botSpawnQueue[n].spawnTime == 0) {
continue;
}
2014-11-10 20:17:30 +00:00
if (botSpawnQueue[n].spawnTime > level.time) {
continue;
}
num++;
}
return num;
}
/*
===============
G_CheckMinimumPlayers
===============
*/
2014-11-10 20:17:30 +00:00
void G_CheckMinimumPlayers(void) {
int32_t minplayers = 0;
int32_t humanplayers = 0;
int32_t botplayers = 0;
static int32_t checkminimumplayers_time = 0;
2014-11-10 20:17:30 +00:00
if (level.intermissiontime != 0) {
return;
}
//only check once each 10 seconds
if (checkminimumplayers_time > level.time - 10000) {
return;
}
checkminimumplayers_time = level.time;
trap_Cvar_Update(&bot_minplayers);
minplayers = bot_minplayers.integer;
if (minplayers <= 0) {
return;
}
if (g_gametype.integer != GT_TOURNAMENT) {
2014-11-10 20:17:30 +00:00
botplayers = G_CountBotPlayers(TEAM_SPECTATOR);
if (botplayers > 0) {
G_RemoveRandomBot(TEAM_SPECTATOR);
}
}
if (g_gametype.integer >= GT_TEAM) {
if (minplayers >= g_maxclients.integer / 2) {
2014-11-10 20:17:30 +00:00
minplayers = (g_maxclients.integer / 2) - 1;
}
2014-11-10 20:17:30 +00:00
humanplayers = G_CountHumanPlayers(TEAM_RED);
botplayers = G_CountBotPlayers(TEAM_RED);
//
if (humanplayers + botplayers < minplayers) {
2014-11-10 20:17:30 +00:00
G_AddRandomBot(TEAM_RED);
}
else if (humanplayers + botplayers > minplayers && botplayers) {
G_RemoveRandomBot(TEAM_RED);
}
//
2014-11-10 20:17:30 +00:00
humanplayers = G_CountHumanPlayers(TEAM_BLUE);
botplayers = G_CountBotPlayers(TEAM_BLUE);
//
if (humanplayers + botplayers < minplayers) {
2014-11-10 20:17:30 +00:00
G_AddRandomBot(TEAM_BLUE);
}
else if (humanplayers + botplayers > minplayers && botplayers) {
G_RemoveRandomBot(TEAM_BLUE);
}
2014-11-10 20:17:30 +00:00
}
else if (g_gametype.integer == GT_TOURNAMENT) {
if (minplayers >= g_maxclients.integer) {
2014-11-10 20:17:30 +00:00
minplayers = g_maxclients.integer - 1;
}
2014-11-10 20:17:30 +00:00
humanplayers = G_CountHumanPlayers(-1);
botplayers = G_CountBotPlayers(-1);
//
if (humanplayers + botplayers < minplayers) {
2014-11-10 20:17:30 +00:00
G_AddRandomBot(TEAM_FREE);
}
else if (humanplayers + botplayers > minplayers && botplayers) {
// try to remove spectators first
2014-11-10 20:17:30 +00:00
if (!G_RemoveRandomBot(TEAM_SPECTATOR)) {
// just remove the bot that is playing
2014-11-10 20:17:30 +00:00
G_RemoveRandomBot(-1);
}
}
2014-11-10 20:17:30 +00:00
}
else if (g_gametype.integer == GT_FFA) {
if (minplayers >= g_maxclients.integer) {
2014-11-10 20:17:30 +00:00
minplayers = g_maxclients.integer - 1;
}
2014-11-10 20:17:30 +00:00
humanplayers = G_CountHumanPlayers(TEAM_FREE);
botplayers = G_CountBotPlayers(TEAM_FREE);
//
if (humanplayers + botplayers < minplayers) {
2014-11-10 20:17:30 +00:00
G_AddRandomBot(TEAM_FREE);
}
else if (humanplayers + botplayers > minplayers && botplayers) {
G_RemoveRandomBot(TEAM_FREE);
}
}
}
/*
===============
G_CheckBotSpawn
===============
*/
2014-11-10 20:17:30 +00:00
void G_CheckBotSpawn(void) {
int32_t n = 0;
char userinfo[MAX_INFO_VALUE];
G_CheckMinimumPlayers();
2014-11-10 20:17:30 +00:00
for (; n < BOT_SPAWN_QUEUE_DEPTH; n++) {
if (botSpawnQueue[n].spawnTime == 0) {
continue;
}
2014-11-10 20:17:30 +00:00
if (botSpawnQueue[n].spawnTime > level.time) {
continue;
}
2014-11-10 20:17:30 +00:00
G_Client_Begin(botSpawnQueue[n].clientNum, qfalse, qtrue, qfalse);
botSpawnQueue[n].spawnTime = 0;
2014-11-10 20:17:30 +00:00
if (g_gametype.integer == GT_SINGLE_PLAYER) {
trap_GetUserinfo(botSpawnQueue[n].clientNum, userinfo, sizeof(userinfo));
PlayerIntroSound(Info_ValueForKey(userinfo, "model"));
}
}
}
/*
===============
AddBotToSpawnQueue
===============
*/
2014-11-10 20:17:30 +00:00
static void AddBotToSpawnQueue(int32_t clientNum, int32_t delay) {
int32_t n = 0;
2014-11-10 20:17:30 +00:00
for (; n < BOT_SPAWN_QUEUE_DEPTH; n++) {
if (botSpawnQueue[n].spawnTime == 0) {
botSpawnQueue[n].spawnTime = level.time + delay;
botSpawnQueue[n].clientNum = clientNum;
return;
}
}
2014-11-10 20:17:30 +00:00
G_Printf(S_COLOR_YELLOW "Unable to delay spawn\n");
G_Client_Begin(clientNum, qfalse, qtrue, qfalse);
}
/*
===============
G_QueueBotBegin
===============
*/
2014-11-10 20:17:30 +00:00
void G_QueueBotBegin(int32_t clientNum) {
AddBotToSpawnQueue(clientNum, botBeginDelay);
botBeginDelay += BOT_BEGIN_DELAY_INCREMENT;
}
/*
===============
G_BotConnect
===============
*/
2014-11-10 20:17:30 +00:00
qboolean G_BotConnect(int32_t clientNum, qboolean restart) {
bot_settings_t settings;
char userinfo[MAX_INFO_STRING];
2014-11-10 20:17:30 +00:00
trap_GetUserinfo(clientNum, userinfo, sizeof(userinfo));
strncpy(settings.characterfile, Info_ValueForKey(userinfo, "characterfile"), sizeof(settings.characterfile));
2014-11-10 20:17:30 +00:00
settings.skill = atoi(Info_ValueForKey(userinfo, "skill"));
strncpy(settings.team, Info_ValueForKey(userinfo, "team"), sizeof(settings.team));
strncpy(settings.pclass, Info_ValueForKey(userinfo, "class"), sizeof(settings.pclass));
2014-11-10 20:17:30 +00:00
if (AI_main_BotAISetupClient(clientNum, &settings) == 0) {
trap_DropClient(clientNum, "BotAISetupClient failed");
return qfalse;
}
2014-11-10 20:17:30 +00:00
if (restart && (g_gametype.integer == GT_SINGLE_PLAYER)) {
g_entities[clientNum].botDelayBegin = qtrue;
2014-11-10 20:17:30 +00:00
}
else {
g_entities[clientNum].botDelayBegin = qfalse;
}
return qtrue;
}
/*
===============
G_AddBot
===============
*/
2014-11-10 20:17:30 +00:00
static void G_AddBot(const char* name, double skill, const char* team, const char* pclass, int32_t delay, char* altname) {
int32_t clientNum = 0;
char* botinfo = NULL;
gentity_t* bot = NULL;
char* key = NULL;
char* s = NULL;
char* botname = NULL;
char* model = NULL;
char userinfo[MAX_INFO_STRING];
int32_t preTeam = 0;
// get the botinfo from bots.txt
2014-11-10 20:17:30 +00:00
botinfo = G_GetBotInfoByName(name);
if (botinfo == NULL || botinfo[0] == 0) {
G_Printf(S_COLOR_RED "Error: Bot '%s' not defined\n", name);
return;
}
// create the bot's userinfo
userinfo[0] = '\0';
2014-11-10 20:17:30 +00:00
botname = Info_ValueForKey(botinfo, "funname");
if (botname == NULL || botname[0] == 0) {
botname = Info_ValueForKey(botinfo, "name");
}
// check for an alternative name
if ((altname != NULL) && (altname[0] != 0)) {
botname = altname;
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(userinfo, "name", botname);
Info_SetValueForKey(userinfo, "rate", "25000");
Info_SetValueForKey(userinfo, "snaps", "20");
Info_SetValueForKey(userinfo, "skill", va("%1.2f", skill));
key = "model";
2014-11-10 20:17:30 +00:00
model = Info_ValueForKey(botinfo, key);
if (*model) {
model = "munro/main/default"; //RPG-X MODEL SYSTEM
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(userinfo, key, model);
key = "gender";
2014-11-10 20:17:30 +00:00
s = Info_ValueForKey(botinfo, key);
if (!*s) {
s = "male";
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(userinfo, "sex", s);
key = "color";
2014-11-10 20:17:30 +00:00
s = Info_ValueForKey(botinfo, key);
if (!*s) {
s = "4";
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(userinfo, key, s);
s = Info_ValueForKey(botinfo, "aifile");
2014-11-10 20:17:30 +00:00
if (!*s) {
trap_Printf(S_COLOR_RED "Error: bot has no aifile specified\n");
return;
}
// have the server allocate a client slot
clientNum = trap_BotAllocateClient();
2014-11-10 20:17:30 +00:00
if (clientNum == -1) {
G_Printf(S_COLOR_RED "Unable to add bot. All player slots are in use.\n");
G_Printf(S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n");
return;
}
// initialize the bot settings
2014-11-10 20:17:30 +00:00
if (team == NULL || !*team) {
if (g_gametype.integer >= GT_TEAM) {
if (G_Client_PickTeam(clientNum) == TEAM_RED) {
team = "red";
}
else {
team = "blue";
}
}
else {
team = "red";
}
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(userinfo, "characterfile", Info_ValueForKey(botinfo, "aifile"));
Info_SetValueForKey(userinfo, "skill", va("%5.2f", skill));
Info_SetValueForKey(userinfo, "team", team);
2014-11-10 20:17:30 +00:00
bot = &g_entities[clientNum];
bot->r.svFlags |= SVF_BOT;
bot->inuse = qtrue;
// register the userinfo
2014-11-10 20:17:30 +00:00
trap_SetUserinfo(clientNum, userinfo);
if (g_gametype.integer >= GT_TEAM)
{
if (team && Q_stricmp(team, "red") == 0)
{
bot->client->sess.sessionTeam = TEAM_RED;
}
else if (team && Q_stricmp(team, "blue") == 0)
{
bot->client->sess.sessionTeam = TEAM_BLUE;
}
else
{
2014-11-10 20:17:30 +00:00
bot->client->sess.sessionTeam = G_Client_PickTeam(-1);
}
}
preTeam = bot->client->sess.sessionTeam;
// have it connect to the game as a normal client
2014-11-10 20:17:30 +00:00
if (G_Client_Connect(clientNum, qtrue, qtrue)) {
return;
}
if (bot->client->sess.sessionTeam != preTeam)
{
trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING);
if (bot->client->sess.sessionTeam == TEAM_SPECTATOR)
{
bot->client->sess.sessionTeam = preTeam;
}
if (bot->client->sess.sessionTeam == TEAM_RED)
{
team = "Red";
}
else
{
team = "Blue";
}
2014-11-10 20:17:30 +00:00
Info_SetValueForKey(userinfo, "team", team);
2014-11-10 20:17:30 +00:00
trap_SetUserinfo(clientNum, userinfo);
2014-11-10 20:17:30 +00:00
bot->client->ps.persistant[PERS_TEAM] = bot->client->sess.sessionTeam;
2014-11-10 20:17:30 +00:00
G_ReadSessionData(bot->client);
G_Client_UserinfoChanged(clientNum);
}
2014-11-10 20:17:30 +00:00
if (delay == 0) {
G_Client_Begin(clientNum, qfalse, qfalse, qfalse);
return;
}
2014-11-10 20:17:30 +00:00
AddBotToSpawnQueue(clientNum, delay);
}
/*
===============
Svcmd_AddBot_f
===============
*/
2014-11-10 20:17:30 +00:00
void Svcmd_AddBot_f(void) {
int32_t skill = 0;
int32_t delay = 0;
char name[MAX_TOKEN_CHARS];
char altname[MAX_TOKEN_CHARS];
char string[MAX_TOKEN_CHARS];
char team[MAX_TOKEN_CHARS];
char pclass[MAX_TOKEN_CHARS];
// are bots enabled?
2014-11-10 20:17:30 +00:00
if (trap_Cvar_VariableIntegerValue("bot_enable") == 0) {
return;
}
memset(name, 0, sizeof(name));
memset(altname, 0, sizeof(altname));
memset(string, 0, sizeof(string));
memset(team, 0, sizeof(team));
memset(pclass, 0, sizeof(pclass));
// name
2014-11-10 20:17:30 +00:00
trap_Argv(1, name, sizeof(name));
if (name[0] == 0) {
trap_Printf("Usage: Addbot <botname> [skill 1-4] [team] [class] [msec delay] [altname]\n");
return;
}
// skill
2014-11-10 20:17:30 +00:00
trap_Argv(2, string, sizeof(string));
if (string[0] == 0) {
skill = 4;
2014-11-10 20:17:30 +00:00
}
else {
skill = atoi(string);
}
// team
2014-11-10 20:17:30 +00:00
trap_Argv(3, team, sizeof(team));
// class
2014-11-10 20:17:30 +00:00
trap_Argv(4, pclass, sizeof(pclass));
// delay
2014-11-10 20:17:30 +00:00
trap_Argv(5, string, sizeof(string));
if (string[0] == 0) {
delay = 0;
2014-11-10 20:17:30 +00:00
}
else {
delay = atoi(string);
}
// alternative name
2014-11-10 20:17:30 +00:00
trap_Argv(6, altname, sizeof(altname));
2014-11-10 20:17:30 +00:00
G_AddBot(name, skill, team, pclass, delay, altname);
// if this was issued during gameplay and we are playing locally,
// go ahead and load the bot's media immediately
2014-11-10 20:17:30 +00:00
if ((level.time - level.startTime > 1000) &&
trap_Cvar_VariableIntegerValue("cl_running")) {
trap_SendServerCommand(-1, "loaddeferred\n"); // FIXME: spelled wrong, but not changing for demo
}
}
/*
===============
Svcmd_BotList_f
===============
*/
2014-11-10 20:17:30 +00:00
void Svcmd_BotList_f(void) {
int32_t i = 0;
char name[MAX_TOKEN_CHARS];
char funname[MAX_TOKEN_CHARS];
char model[MAX_TOKEN_CHARS];
char aifile[MAX_TOKEN_CHARS];
memset(name, 0, sizeof(name));
memset(funname, 0, sizeof(funname));
memset(model, 0, sizeof(model));
memset(aifile, 0, sizeof(aifile));
trap_Printf("^1name model aifile funname\n");
for (i = 0; i < g_numBots; i++) {
2014-11-10 20:17:30 +00:00
strcpy(name, Info_ValueForKey(g_botInfos[i], "name"));
if (!*name) {
strcpy(name, "RedShirt");
}
2014-11-10 20:17:30 +00:00
strcpy(funname, Info_ValueForKey(g_botInfos[i], "funname"));
if (!*funname) {
strcpy(funname, "");
}
2014-11-10 20:17:30 +00:00
strcpy(model, Info_ValueForKey(g_botInfos[i], "model"));
if (!*model) {
strcpy(model, "munro/main/default"); //RPG-X MODEL SYSTEM
}
2014-11-10 20:17:30 +00:00
strcpy(aifile, Info_ValueForKey(g_botInfos[i], "aifile"));
if (!*aifile) {
strcpy(aifile, "bots/default_c.c");
}
trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, aifile, funname));
}
}
/*
===============
G_SpawnBots
===============
*/
2014-11-10 20:17:30 +00:00
static void G_SpawnBots(char* botList, int32_t baseDelay) {
char* bot;
char* p;
int32_t skill = 0;
int32_t delay = 0;
char bots[MAX_INFO_VALUE];
podium1 = NULL;
podium2 = NULL;
podium3 = NULL;
memset(bots, 0, sizeof(bots));
2014-11-10 20:17:30 +00:00
skill = trap_Cvar_VariableIntegerValue("g_spSkill");
if (skill < 1 || skill > 5) {
trap_Cvar_Set("g_spSkill", "2");
skill = 2;
}
strncpy(bots, botList, sizeof(bots));
p = &bots[0];
delay = baseDelay;
2014-11-10 20:17:30 +00:00
while (*p) {
//skip spaces
2014-11-10 20:17:30 +00:00
while (*p && *p == ' ') {
p++;
}
2014-11-10 20:17:30 +00:00
if (!p) {
break;
}
// mark start of bot name
bot = p;
// skip until space of null
2014-11-10 20:17:30 +00:00
while (*p && *p != ' ') {
p++;
}
2014-11-10 20:17:30 +00:00
if (*p) {
*p++ = 0;
}
// we must add the bot this way, calling G_AddBot directly at this stage
// does "Bad Things"
2014-11-10 20:17:30 +00:00
trap_SendConsoleCommand(EXEC_INSERT, va("addbot %s %i free %i\n", bot, skill, delay));
delay += BOT_BEGIN_DELAY_INCREMENT;
}
}
/*
===============
G_LoadBotsFromFile
===============
*/
2014-11-10 20:17:30 +00:00
static void G_LoadBotsFromFile(char* filename) {
int32_t len = 0;
fileHandle_t f = 0;
char buf[MAX_BOTS_TEXT];
2014-11-10 20:17:30 +00:00
len = trap_FS_FOpenFile(filename, &f, FS_READ);
if (f == 0) {
trap_Printf(va(S_COLOR_RED "file not found: %s\n", filename));
return;
}
2014-11-10 20:17:30 +00:00
if (len >= MAX_BOTS_TEXT) {
trap_Printf(va(S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT));
trap_FS_FCloseFile(f);
return;
}
memset(buf, 0, sizeof(buf));
2014-11-10 20:17:30 +00:00
trap_FS_Read(buf, len, f);
buf[len] = 0;
2014-11-10 20:17:30 +00:00
trap_FS_FCloseFile(f);
2014-11-10 20:17:30 +00:00
g_numBots += G_ParseInfos(buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots]);
}
/*
===============
G_LoadBots
===============
*/
2014-11-10 20:17:30 +00:00
static void G_LoadBots(void) {
vmCvar_t botsFile;
int32_t numdirs = 0;
char filename[128];
char dirlist[4096];
char* dirptr = NULL;
int32_t i = 0;
int32_t dirlen = 0;
2014-11-10 20:17:30 +00:00
if (trap_Cvar_VariableIntegerValue("bot_enable") == 0) {
return;
}
memset(filename, 0, sizeof(filename));
memset(dirlist, 0, sizeof(dirlist));
g_numBots = 0;
2014-11-10 20:17:30 +00:00
trap_Cvar_Register(&botsFile, "g_botsFile", "", CVAR_INIT | CVAR_ROM);
if (*botsFile.string) {
G_LoadBotsFromFile(botsFile.string);
}
else {
G_LoadBotsFromFile("scripts/bots.txt");
}
// get all bots from .bot files
2014-11-10 20:17:30 +00:00
numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, sizeof(dirlist));
dirptr = dirlist;
for (i = 0; i < numdirs; i++, dirptr += dirlen + 1)
{
dirlen = strlen(dirptr);
strcpy(filename, "scripts/");
strcat(filename, dirptr);
G_LoadBotsFromFile(filename);
}
2014-11-10 20:17:30 +00:00
trap_Printf(va("%i bots parsed\n", g_numBots));
}
/*
===============
G_GetBotInfoByNumber
===============
*/
2014-11-10 20:17:30 +00:00
char* G_GetBotInfoByNumber(int32_t num) {
if (num < 0 || num >= g_numBots) {
trap_Printf(va(S_COLOR_RED "Invalid bot number: %i\n", num));
return NULL;
}
return g_botInfos[num];
}
/*
===============
G_GetBotInfoByName
===============
*/
2014-11-10 20:17:30 +00:00
char* G_GetBotInfoByName(const char* name) {
int32_t n = 0;
char* value = NULL;
2014-11-10 20:17:30 +00:00
for (n = 0; n < g_numBots; n++) {
value = Info_ValueForKey(g_botInfos[n], "name");
if (Q_stricmp(value, name) == 0) {
return g_botInfos[n];
}
}
return NULL;
}
2014-11-10 20:17:30 +00:00
void G_InitBots(qboolean restart) {
const char* arenainfo = NULL;
char* strValue = NULL;
char map[MAX_QPATH];
char serverinfo[MAX_INFO_STRING];
memset(map, 0, sizeof(map));
memset(serverinfo, 0, sizeof(serverinfo));
G_LoadBots();
G_LoadArenas();
2014-11-10 20:17:30 +00:00
trap_Cvar_Register(&bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO);
2014-11-10 20:17:30 +00:00
if (g_gametype.integer == GT_SINGLE_PLAYER) {
int32_t fragLimit = 0;
int32_t timeLimit = 0;
int32_t basedelay = 0;
2014-11-10 20:17:30 +00:00
trap_GetServerinfo(serverinfo, sizeof(serverinfo));
Q_strncpyz(map, Info_ValueForKey(serverinfo, "mapname"), sizeof(map));
arenainfo = G_GetArenaInfoByMap(map);
if (arenainfo == NULL) {
return;
}
2014-11-10 20:17:30 +00:00
strValue = Info_ValueForKey(arenainfo, "fraglimit");
fragLimit = atoi(strValue);
if (fragLimit != 0) {
trap_Cvar_Set("fraglimit", strValue);
}
else {
trap_Cvar_Set("fraglimit", "0");
}
2014-11-10 20:17:30 +00:00
strValue = Info_ValueForKey(arenainfo, "timelimit");
timeLimit = atoi(strValue);
if (timeLimit != 0) {
trap_Cvar_Set("timelimit", strValue);
}
else {
trap_Cvar_Set("timelimit", "0");
}
2014-11-10 20:17:30 +00:00
if (fragLimit == 0 && timeLimit == 0) {
trap_Cvar_Set("fraglimit", "10");
trap_Cvar_Set("timelimit", "0");
}
if (g_holoIntro.integer != 0)
{ // The player will be looking at the holodeck doors for the first five seconds, so take that into account.
basedelay = BOT_BEGIN_DELAY_BASE + TIME_INTRO;
2014-11-10 20:17:30 +00:00
}
else {
basedelay = BOT_BEGIN_DELAY_BASE;
}
2014-11-10 20:17:30 +00:00
strValue = Info_ValueForKey(arenainfo, "special");
if (Q_stricmp(strValue, "training") == 0) {
basedelay += 10000;
}
2014-11-10 20:17:30 +00:00
if (!restart) {
G_SpawnBots(Info_ValueForKey(arenainfo, "bots"), basedelay);
}
}
}