2012-08-04 10:54:37 +00:00
|
|
|
// Copyright (C) 1999-2000 Id Software, Inc.
|
2012-01-22 21:34:33 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* name: ai_main.c
|
|
|
|
*
|
|
|
|
* desc: Quake3 bot AI
|
|
|
|
*
|
2012-08-04 10:54:37 +00:00
|
|
|
* $Archive: /StarTrek/Code-DM/game/ai_main.c $
|
2014-10-11 16:41:15 +00:00
|
|
|
* $Author: Jmonroe $
|
2012-08-04 10:54:37 +00:00
|
|
|
* $Revision: 4 $
|
|
|
|
* $Modtime: 11/30/00 5:17p $
|
|
|
|
* $Date: 11/30/00 5:19p $
|
2012-01-22 21:34:33 +00:00
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
#include "g_local.h"
|
2012-08-04 10:54:37 +00:00
|
|
|
#include "q_shared.h"
|
|
|
|
#include "botlib.h" //bot lib interface
|
|
|
|
#include "be_aas.h"
|
|
|
|
#include "be_ea.h"
|
|
|
|
#include "be_ai_char.h"
|
|
|
|
#include "be_ai_chat.h"
|
|
|
|
#include "be_ai_gen.h"
|
|
|
|
#include "be_ai_goal.h"
|
|
|
|
#include "be_ai_move.h"
|
|
|
|
#include "be_ai_weap.h"
|
2012-01-22 21:34:33 +00:00
|
|
|
//
|
|
|
|
#include "ai_main.h"
|
|
|
|
#include "ai_dmq3.h"
|
|
|
|
#include "ai_chat.h"
|
|
|
|
#include "ai_cmd.h"
|
|
|
|
#include "ai_dmnet.h"
|
|
|
|
//
|
|
|
|
#include "chars.h"
|
|
|
|
#include "inv.h"
|
|
|
|
#include "syn.h"
|
|
|
|
|
2014-10-09 18:15:26 +00:00
|
|
|
#include "g_syscalls.h"
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
#define AI_MAX_PATH 144
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
//bot states
|
|
|
|
bot_state_t *botstates[MAX_CLIENTS];
|
|
|
|
//number of bots
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t numbots;
|
2012-01-22 21:34:33 +00:00
|
|
|
//time to do a regular update
|
2014-10-11 16:41:15 +00:00
|
|
|
double regularupdate_time;
|
2012-01-22 21:34:33 +00:00
|
|
|
//
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t bot_interbreed;
|
|
|
|
int32_t bot_interbreedmatchcount;
|
2012-01-22 21:34:33 +00:00
|
|
|
//
|
|
|
|
vmCvar_t bot_thinktime;
|
|
|
|
vmCvar_t bot_memorydump;
|
|
|
|
vmCvar_t bot_pause;
|
|
|
|
vmCvar_t bot_report;
|
|
|
|
vmCvar_t bot_testsolid;
|
|
|
|
vmCvar_t bot_interbreedchar;
|
|
|
|
vmCvar_t bot_interbreedbots;
|
|
|
|
vmCvar_t bot_interbreedcycle;
|
|
|
|
vmCvar_t bot_interbreedwrite;
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
qboolean bot_setupComplete = qfalse;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
void ExitLevel(void);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAI_Print
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void QDECL AI_main_BotAIPrint(int32_t type, char* fmt, ...) {
|
2012-01-22 21:34:33 +00:00
|
|
|
char str[2048];
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2012-08-04 10:54:37 +00:00
|
|
|
vsprintf(str, fmt, ap);
|
2012-01-22 21:34:33 +00:00
|
|
|
va_end(ap);
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
switch (type) {
|
|
|
|
case PRT_MESSAGE:
|
|
|
|
{
|
|
|
|
G_Printf("%s", str);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
case PRT_WARNING:
|
|
|
|
{
|
|
|
|
G_Printf(S_COLOR_YELLOW "Warning: %s", str);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
case PRT_ERROR:
|
|
|
|
{
|
|
|
|
G_Printf(S_COLOR_RED "Error: %s", str);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
case PRT_FATAL:
|
|
|
|
{
|
|
|
|
G_Printf(S_COLOR_RED "Fatal: %s", str);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
case PRT_EXIT:
|
|
|
|
{
|
|
|
|
G_Error(S_COLOR_RED "Exit: %s", str);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
G_Printf("unknown print type\n");
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAI_Trace
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void AI_main_BotAITrace(bsp_trace_t* bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int32_t passent, int32_t contentmask) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trace_t trace;
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_Assert(bsptrace, (void)0);
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_Trace(&trace, start, mins, maxs, end, passent, contentmask);
|
|
|
|
//copy the trace information
|
|
|
|
bsptrace->allsolid = trace.allsolid;
|
|
|
|
bsptrace->startsolid = trace.startsolid;
|
|
|
|
bsptrace->fraction = trace.fraction;
|
|
|
|
VectorCopy(trace.endpos, bsptrace->endpos);
|
|
|
|
bsptrace->plane.dist = trace.plane.dist;
|
|
|
|
VectorCopy(trace.plane.normal, bsptrace->plane.normal);
|
|
|
|
bsptrace->plane.signbits = trace.plane.signbits;
|
|
|
|
bsptrace->plane.type = trace.plane.type;
|
|
|
|
bsptrace->surface.value = trace.surfaceFlags;
|
|
|
|
bsptrace->ent = trace.entityNum;
|
|
|
|
bsptrace->exp_dist = 0;
|
|
|
|
bsptrace->sidenum = 0;
|
|
|
|
bsptrace->contents = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAI_GetClientState
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAIGetClientState(int32_t clientNum, playerState_t* state) {
|
|
|
|
gentity_t* ent = NULL;
|
|
|
|
|
|
|
|
AI_Assert(state, qfalse);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
ent = &g_entities[clientNum];
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_Assert(ent, qfalse);
|
|
|
|
|
|
|
|
if (!ent->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if (ent->client == NULL) {
|
2012-01-22 21:34:33 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
memcpy(state, &ent->client->ps, sizeof(playerState_t));
|
2012-01-22 21:34:33 +00:00
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAI_GetEntityState
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAIGetEntityState(int32_t entityNum, entityState_t* state) {
|
|
|
|
gentity_t* ent = NULL;
|
|
|
|
|
|
|
|
AI_Assert(state, qfalse);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
ent = &g_entities[entityNum];
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_Assert(ent, qfalse);
|
|
|
|
|
|
|
|
memset(state, 0, sizeof(entityState_t));
|
|
|
|
|
|
|
|
if (!ent->inuse) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ent->r.linked) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ent->r.svFlags & SVF_NOCLIENT) != 0) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(state, &ent->s, sizeof(entityState_t));
|
2012-01-22 21:34:33 +00:00
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAI_GetSnapshotEntity
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAIGetSnapshotEntity(int32_t clientNum, int32_t sequence, entityState_t* state) {
|
|
|
|
int32_t entNum = -1;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_Assert(state, -1);
|
|
|
|
|
|
|
|
entNum = trap_BotGetSnapshotEntity(clientNum, sequence);
|
|
|
|
if (entNum == -1) {
|
2012-01-22 21:34:33 +00:00
|
|
|
memset(state, 0, sizeof(entityState_t));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIGetEntityState(entNum, state);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
return sequence + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAI_BotInitialChat
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void QDECL AI_main_BotAIInitialChat(bot_state_t* bs, char* type, ...) {
|
|
|
|
int32_t i = 0;
|
|
|
|
int32_t mcontext = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
va_list ap;
|
2014-10-11 16:41:15 +00:00
|
|
|
char* p = NULL;
|
|
|
|
char* vars[MAX_MATCHVARIABLES];
|
|
|
|
|
|
|
|
AI_Assert(bs, (void)0);
|
|
|
|
AI_Assert(type, (void)0);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
memset(vars, 0, sizeof(vars));
|
|
|
|
va_start(ap, type);
|
2014-10-11 16:41:15 +00:00
|
|
|
p = va_arg(ap, char*);
|
2012-01-22 21:34:33 +00:00
|
|
|
for (i = 0; i < MAX_MATCHVARIABLES; i++) {
|
2014-10-11 16:41:15 +00:00
|
|
|
if (p == NULL) {
|
2012-01-22 21:34:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
vars[i] = p;
|
|
|
|
p = va_arg(ap, char *);
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
mcontext = CONTEXT_NORMAL | CONTEXT_NEARBYITEM | CONTEXT_NAMES;
|
|
|
|
if (BotCTFTeam(bs) == CTF_TEAM_RED) {
|
|
|
|
mcontext |= CONTEXT_CTFREDTEAM;
|
|
|
|
} else {
|
|
|
|
mcontext |= CONTEXT_CTFBLUETEAM;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
trap_BotInitialChat(bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7]);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
2012-08-04 10:54:37 +00:00
|
|
|
BotTestSolid
|
2012-01-22 21:34:33 +00:00
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void AI_main_BotTestSolid(vec3_t origin) {
|
2012-08-04 10:54:37 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (!bot_setupComplete) {
|
2012-08-04 10:54:37 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
trap_Cvar_Update(&bot_testsolid);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bot_testsolid.integer != 0) {
|
2013-08-12 18:26:28 +00:00
|
|
|
int areanum;
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (trap_AAS_Initialized() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
areanum = BotPointAreaNum(origin);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (areanum != 0) {
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "\remtpy area");
|
|
|
|
} else {
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "\r^1SOLID area");
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotReportStatus
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotReportStatus(bot_state_t* bs) {
|
2012-01-22 21:34:33 +00:00
|
|
|
char goalname[MAX_MESSAGE_SIZE];
|
|
|
|
char netname[MAX_MESSAGE_SIZE];
|
2014-10-11 16:41:15 +00:00
|
|
|
char* leader = NULL;
|
|
|
|
char* flagstatus = NULL;
|
|
|
|
|
|
|
|
if (bs == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
ClientName(bs->client, netname, sizeof(netname));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (Q_stricmp(netname, bs->teamleader) == 0) {
|
|
|
|
leader = "L";
|
|
|
|
} else {
|
|
|
|
leader = " ";
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if (BotCTFCarryingFlag(bs) != 0) {
|
|
|
|
if (BotCTFTeam(bs) == TEAM_RED) {
|
|
|
|
flagstatus = S_COLOR_RED"F";
|
|
|
|
} else {
|
|
|
|
flagstatus = S_COLOR_BLUE"F";
|
|
|
|
}
|
|
|
|
} else {
|
2012-08-04 10:54:37 +00:00
|
|
|
flagstatus = " ";
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
switch (bs->ltgtype) {
|
2012-01-22 21:34:33 +00:00
|
|
|
case LTG_TEAMHELP:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
EasyClientName(bs->teammate, goalname, sizeof(goalname));
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_TEAMACCOMPANY:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
EasyClientName(bs->teammate, goalname, sizeof(goalname));
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_DEFENDKEYAREA:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_GETITEM:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_KILL:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname));
|
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_CAMP:
|
|
|
|
case LTG_CAMPORDER:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_PATROL:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_GETFLAG:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_RUSHBASE:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
case LTG_RETURNFLAG:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus);
|
|
|
|
break;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotTeamplayReport
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotTeamplayReport(void) {
|
|
|
|
int32_t i = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
char buf[MAX_INFO_STRING];
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, S_COLOR_RED"RED\n");
|
2012-01-22 21:34:33 +00:00
|
|
|
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
|
2014-10-11 16:41:15 +00:00
|
|
|
if (botstates[i] == NULL || !botstates[i]->inuse) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf));
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//if no config string or no name
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) == 0 || strlen(Info_ValueForKey(buf, "n")) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//skip spectators
|
|
|
|
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) {
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotReportStatus(botstates[i]);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIPrint(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n");
|
2012-01-22 21:34:33 +00:00
|
|
|
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
|
2014-10-11 16:41:15 +00:00
|
|
|
if (botstates[i] == NULL || !botstates[i]->inuse) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
trap_GetConfigstring(CS_PLAYERS + i, buf, sizeof(buf));
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//if no config string or no name
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) == 0 || strlen(Info_ValueForKey(buf, "n")) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//skip spectators
|
|
|
|
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) {
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotReportStatus(botstates[i]);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotInterbreedBots
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotInterbreedBots(void) {
|
|
|
|
double ranks[MAX_CLIENTS];
|
|
|
|
int32_t parent1 = 0;
|
|
|
|
int32_t parent2 = 0;
|
|
|
|
int32_t child = 0;;
|
|
|
|
int32_t i = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
// get rankings for all the bots
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] != NULL && botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
|
2014-10-11 16:41:15 +00:00
|
|
|
} else {
|
2012-01-22 21:34:33 +00:00
|
|
|
ranks[i] = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, (float*)ranks, &parent1, &parent2, &child) != 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs);
|
|
|
|
trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1);
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
// reset the kills and deaths
|
|
|
|
for (i = 0; i < MAX_CLIENTS; i++) {
|
2014-10-11 16:41:15 +00:00
|
|
|
if (botstates[i] != NULL && botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
botstates[i]->num_kills = 0;
|
|
|
|
botstates[i]->num_deaths = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotWriteInterbreeded
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotWriteInterbreeded(char* filename) {
|
|
|
|
double rank = 0.0;
|
|
|
|
double bestrank = 0.0;
|
|
|
|
int32_t i = 0;
|
|
|
|
int32_t bestbot = -1;
|
|
|
|
|
|
|
|
AI_Assert(filename, (void)0);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
// get the best bot
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] != NULL && botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
|
2014-10-11 16:41:15 +00:00
|
|
|
} else {
|
2012-01-22 21:34:33 +00:00
|
|
|
rank = -1;
|
|
|
|
}
|
|
|
|
if (rank > bestrank) {
|
|
|
|
bestrank = rank;
|
|
|
|
bestbot = i;
|
|
|
|
}
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
if (bestbot >= 0) {
|
|
|
|
//write out the new goal fuzzy logic
|
|
|
|
trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotInterbreedEndMatch
|
|
|
|
|
|
|
|
add link back into ExitLevel?
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void AI_main_BotInterbreedEndMatch(void) {
|
|
|
|
|
|
|
|
if (bot_interbreed == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
bot_interbreedmatchcount++;
|
|
|
|
if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) {
|
|
|
|
bot_interbreedmatchcount = 0;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_Cvar_Update(&bot_interbreedwrite);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(bot_interbreedwrite.string) != 0) {
|
|
|
|
AI_main_BotWriteInterbreeded(bot_interbreedwrite.string);
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_Cvar_Set("bot_interbreedwrite", "");
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotInterbreedBots();
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotInterbreeding
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotInterbreeding(void) {
|
|
|
|
int32_t i = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
trap_Cvar_Update(&bot_interbreedchar);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(bot_interbreedchar.string) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//make sure we are in tournament mode
|
|
|
|
if (gametype != GT_TOURNAMENT) {
|
|
|
|
trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT));
|
|
|
|
ExitLevel();
|
|
|
|
return;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//shutdown all the bots
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] != NULL && botstates[i]->inuse) {
|
|
|
|
AI_main_BotAIShutdownClient(botstates[i]->client);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//make sure all item weight configs are reloaded and Not shared
|
|
|
|
trap_BotLibVarSet("bot_reloadcharacters", "1");
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//add a number of bots using the desired bot character
|
|
|
|
for (i = 0; i < bot_interbreedbots.integer; i++) {
|
2014-10-11 16:41:15 +00:00
|
|
|
trap_SendConsoleCommand(EXEC_INSERT, va("addbot %s 4 free %i %s%d\n", bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i));
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_Cvar_Set("bot_interbreedchar", "");
|
|
|
|
bot_interbreed = qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotEntityInfo
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void AI_main_BotEntityInfo(int entnum, aas_entityinfo_t* info) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_AAS_EntityInfo(entnum, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
NumBots
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_NumBots(void) {
|
2012-01-22 21:34:33 +00:00
|
|
|
return numbots;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotTeamLeader
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotTeamLeader(bot_state_t* bs) {
|
|
|
|
int32_t leader = 0;
|
|
|
|
|
|
|
|
AI_Assert(bs, qfalse);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
leader = ClientFromName(bs->teamleader);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (leader < 0) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (botstates[leader] == NULL || !botstates[leader]->inuse) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
AngleDifference
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static double AngleDifference(double ang1, double ang2) {
|
|
|
|
double diff = ang1 - ang2;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
if (ang1 > ang2) {
|
|
|
|
if (diff > 180.0) diff -= 360.0;
|
2014-10-11 16:41:15 +00:00
|
|
|
} else {
|
2012-01-22 21:34:33 +00:00
|
|
|
if (diff < -180.0) diff += 360.0;
|
|
|
|
}
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotChangeViewAngle
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static double AI_main_BotChangeViewAngle(double angle, double ideal_angle, double speed) {
|
|
|
|
double move = 0.0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
angle = AngleMod(angle);
|
|
|
|
ideal_angle = AngleMod(ideal_angle);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (angle == ideal_angle) {
|
|
|
|
return angle;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
move = ideal_angle - angle;
|
|
|
|
if (ideal_angle > angle) {
|
2014-10-11 16:41:15 +00:00
|
|
|
if (move > 180.0) {
|
|
|
|
move -= 360.0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (move < -180.0) {
|
|
|
|
move += 360.0;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
if (move > 0) {
|
2014-10-11 16:41:15 +00:00
|
|
|
if (move > speed) {
|
|
|
|
move = speed;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (move < -speed) {
|
|
|
|
move = -speed;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
return AngleMod(angle + move);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotChangeViewAngles
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotChangeViewAngles(bot_state_t* bs, double thinktime) {
|
|
|
|
double diff = 0.0;
|
|
|
|
double factor = 1.0;
|
|
|
|
double maxchange = 1800.0;
|
|
|
|
double anglespeed = 0.0;
|
|
|
|
int32_t i = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_Assert(bs, (void)0);
|
2012-08-04 10:54:37 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bs->ideal_viewangles[PITCH] > 180) {
|
|
|
|
bs->ideal_viewangles[PITCH] -= 360;
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
maxchange *= thinktime;
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < 2; i++) {
|
2012-08-04 10:54:37 +00:00
|
|
|
//smooth slowdown view model
|
|
|
|
diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]));
|
|
|
|
anglespeed = diff * factor;
|
2014-10-11 16:41:15 +00:00
|
|
|
if (anglespeed > maxchange) {
|
2012-08-04 10:54:37 +00:00
|
|
|
anglespeed = maxchange;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
bs->viewangles[i] = AI_main_BotChangeViewAngle(bs->viewangles[i], bs->ideal_viewangles[i], anglespeed);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bs->viewangles[PITCH] > 180) {
|
|
|
|
bs->viewangles[PITCH] -= 360;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//elementary action: view
|
|
|
|
trap_EA_View(bs->client, bs->viewangles);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotInputToUserCommand
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotInputToUserCommand(bot_input_t* bi, usercmd_t* ucmd, int32_t delta_angles[3], int32_t time) {
|
|
|
|
vec3_t angles;
|
|
|
|
vec3_t forward;
|
|
|
|
vec3_t right;
|
|
|
|
int16_t temp = 0;
|
|
|
|
int32_t j = 0;
|
|
|
|
|
|
|
|
AI_Assert(ucmd, (void)0)
|
|
|
|
AI_Assert(bi, (void)0)
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
//clear the whole structure
|
|
|
|
memset(ucmd, 0, sizeof(usercmd_t));
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
//Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed);
|
2012-01-22 21:34:33 +00:00
|
|
|
//the duration for the user command in milli seconds
|
|
|
|
ucmd->serverTime = time;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_DELAYEDJUMP) != 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
bi->actionflags |= ACTION_JUMP;
|
|
|
|
bi->actionflags &= ~ACTION_DELAYEDJUMP;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//set the buttons
|
2014-10-11 16:41:15 +00:00
|
|
|
if ((bi->actionflags & ACTION_RESPAWN) != 0) {
|
|
|
|
ucmd->buttons = BUTTON_ATTACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_ATTACK) != 0) {
|
|
|
|
ucmd->buttons |= BUTTON_ATTACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_TALK) != 0) {
|
|
|
|
ucmd->buttons |= BUTTON_TALK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_GESTURE) != 0) {
|
|
|
|
ucmd->buttons |= BUTTON_GESTURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_USE) != 0) {
|
|
|
|
ucmd->buttons |= BUTTON_USE_HOLDABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_WALK) != 0) {
|
|
|
|
ucmd->buttons |= BUTTON_WALKING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_ALT_ATTACK) != 0) {
|
2012-08-04 10:54:37 +00:00
|
|
|
ucmd->buttons |= BUTTON_ALT_ATTACK;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
ucmd->weapon = bi->weapon;
|
|
|
|
//set the view angles
|
|
|
|
//NOTE: the ucmd->angles are the angles WITHOUT the delta angles
|
|
|
|
ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]);
|
|
|
|
ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]);
|
|
|
|
ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//subtract the delta angles
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; j < 3; j++) {
|
2012-01-22 21:34:33 +00:00
|
|
|
temp = ucmd->angles[j] - delta_angles[j];
|
|
|
|
/*NOTE: disabled because temp should be mod first
|
|
|
|
if ( j == PITCH ) {
|
2014-10-11 16:41:15 +00:00
|
|
|
// don't let the player look up or down more than 90 degrees
|
|
|
|
if ( temp > 16000 ) temp = 16000;
|
|
|
|
else if ( temp < -16000 ) temp = -16000;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
*/
|
|
|
|
ucmd->angles[j] = temp;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//NOTE: movement is relative to the REAL view angles
|
|
|
|
//get the horizontal forward and right vector
|
|
|
|
//get the pitch in the range [-180, 180]
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bi->dir[2]) {
|
|
|
|
angles[PITCH] = bi->viewangles[PITCH];
|
|
|
|
} else {
|
|
|
|
angles[PITCH] = 0;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
angles[YAW] = bi->viewangles[YAW];
|
|
|
|
angles[ROLL] = 0;
|
|
|
|
AngleVectors(angles, forward, right, NULL);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//bot input speed is in the range [0, 400]
|
|
|
|
bi->speed = bi->speed * 127 / 400;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//set the view independent movement
|
2012-08-04 10:54:37 +00:00
|
|
|
ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed;
|
|
|
|
ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed;
|
|
|
|
ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
//normal keyboard movement
|
2014-10-11 16:41:15 +00:00
|
|
|
if ((bi->actionflags & ACTION_MOVEFORWARD) != 0) {
|
|
|
|
ucmd->forwardmove += 127;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_MOVEBACK) != 0) {
|
|
|
|
ucmd->forwardmove -= 127;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_MOVELEFT) != 0) {
|
|
|
|
ucmd->rightmove -= 127;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bi->actionflags & ACTION_MOVERIGHT) != 0) {
|
|
|
|
ucmd->rightmove += 127;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//jump/moveup
|
2014-10-11 16:41:15 +00:00
|
|
|
if ((bi->actionflags & ACTION_JUMP) != 0) {
|
|
|
|
ucmd->upmove += 127;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//crouch/movedown
|
2014-10-11 16:41:15 +00:00
|
|
|
if ((bi->actionflags & ACTION_CROUCH) != 0) {
|
|
|
|
ucmd->upmove -= 127;
|
|
|
|
}
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
//Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
|
|
|
|
//Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotUpdateInput
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
bot_input_t bi;
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotUpdateInput(bot_state_t* bs, int32_t time, int32_t elapsed_time) {
|
|
|
|
int32_t j = 0;
|
|
|
|
|
|
|
|
AI_Assert(bs, (void)0)
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
//add the delta angles to the bot's current view angles
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; j < 3; j++) {
|
2012-01-22 21:34:33 +00:00
|
|
|
bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//change the bot view angles
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotChangeViewAngles(bs, (float)elapsed_time / 1000);
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//retrieve the bot input
|
2014-10-11 16:41:15 +00:00
|
|
|
trap_EA_GetInput(bs->client, (float)time / 1000, &bi);
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//respawn hack
|
2014-10-11 16:41:15 +00:00
|
|
|
if ((bi.actionflags & ACTION_RESPAWN) != 0) {
|
|
|
|
if ((bs->lastucmd.buttons & BUTTON_ATTACK) != 0) {
|
|
|
|
bi.actionflags &= ~(ACTION_RESPAWN | ACTION_ATTACK);
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//convert the bot input to a usercmd
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time);
|
2012-01-22 21:34:33 +00:00
|
|
|
//subtract the delta angles
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
for (j = 0; j < 3; j++) {
|
|
|
|
bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAIRegularUpdate
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotAIRegularUpdate(void) {
|
2012-08-04 10:54:37 +00:00
|
|
|
if (regularupdate_time < trap_AAS_Time()) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotUpdateEntityItems();
|
2012-08-04 10:54:37 +00:00
|
|
|
regularupdate_time = trap_AAS_Time() + 0.3;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAI
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static int32_t AI_main_BotAI(int32_t client, double thinktime) {
|
|
|
|
bot_state_t* bs = NULL;
|
|
|
|
char buf[1024];
|
|
|
|
char* args = NULL;
|
|
|
|
int32_t j = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
trap_EA_ResetInput(client);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
bs = botstates[client];
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bs == NULL || !bs->inuse) {
|
|
|
|
AI_main_BotAIPrint(PRT_FATAL, "BotAI: client %d is not setup\n", client);
|
2012-01-22 21:34:33 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
//retrieve the current client state
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIGetClientState(client, &bs->cur_ps);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
//retrieve any waiting console messages
|
2014-10-11 16:41:15 +00:00
|
|
|
while (trap_BotGetConsoleMessage(client, buf, sizeof(buf))) {
|
2012-01-22 21:34:33 +00:00
|
|
|
//have buf point to the command and args to the command arguments
|
2014-10-11 16:41:15 +00:00
|
|
|
args = strchr(buf, ' ');
|
|
|
|
if (args == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
*args++ = '\0';
|
|
|
|
|
|
|
|
//remove color espace sequences from the arguments
|
2014-10-11 16:41:15 +00:00
|
|
|
Q_CleanStr(args);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (Q_stricmp(buf, "cp ") == 0) {
|
|
|
|
/*CenterPrintf*/
|
|
|
|
} else if (Q_stricmp(buf, "cs") == 0) {
|
|
|
|
/*ConfigStringModified*/
|
|
|
|
} else if (Q_stricmp(buf, "print") == 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
//remove first and last quote from the chat message
|
2014-10-11 16:41:15 +00:00
|
|
|
memmove(args, args + 1, strlen(args));
|
|
|
|
args[strlen(args) - 1] = '\0';
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args);
|
2014-10-11 16:41:15 +00:00
|
|
|
} else if (Q_stricmp(buf, "chat") == 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
//remove first and last quote from the chat message
|
2014-10-11 16:41:15 +00:00
|
|
|
memmove(args, args + 1, strlen(args));
|
|
|
|
args[strlen(args) - 1] = '\0';
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
|
2014-10-11 16:41:15 +00:00
|
|
|
} else if (Q_stricmp(buf, "tchat") == 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
//remove first and last quote from the chat message
|
2014-10-11 16:41:15 +00:00
|
|
|
memmove(args, args + 1, strlen(args));
|
|
|
|
args[strlen(args) - 1] = '\0';
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
|
2014-10-11 16:41:15 +00:00
|
|
|
} else if (Q_stricmp(buf, "scores") == 0) {
|
|
|
|
/*FIXME: parse scores?*/
|
|
|
|
} else if (Q_stricmp(buf, "clientLevelShot") == 0) {
|
|
|
|
/*ignore*/
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
//add the delta angles to the bot's current view angles
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
|
|
bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
|
|
|
|
}
|
|
|
|
//increase the local time of the bot
|
|
|
|
bs->ltime += thinktime;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
bs->thinktime = thinktime;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//origin of the bot
|
|
|
|
VectorCopy(bs->cur_ps.origin, bs->origin);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//eye coordinates of the bot
|
|
|
|
VectorCopy(bs->cur_ps.origin, bs->eye);
|
|
|
|
bs->eye[2] += bs->cur_ps.viewheight;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//get the area the bot is in
|
|
|
|
bs->areanum = BotPointAreaNum(bs->origin);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//the real AI
|
|
|
|
BotDeathmatchAI(bs, thinktime);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//set the weapon selection every AI frame
|
|
|
|
trap_EA_SelectWeapon(bs->client, bs->weaponnum);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//subtract the delta angles
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
|
|
bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//everything was ok
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotScheduleBotThink
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static void AI_main_BotScheduleBotThink(void) {
|
|
|
|
int32_t i = 0;
|
|
|
|
int32_t botnum = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] == NULL || !botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//initialize the bot think residual time
|
|
|
|
botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots;
|
|
|
|
botnum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAISetupClient
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAISetupClient(int32_t client, struct bot_settings_s* settings) {
|
|
|
|
char filename[AI_MAX_PATH];
|
|
|
|
char name[AI_MAX_PATH];
|
|
|
|
char gender[AI_MAX_PATH];
|
|
|
|
bot_state_t* bs = NULL;
|
|
|
|
int32_t errnum = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (botstates[client] == NULL) {
|
|
|
|
botstates[client] = G_Alloc(sizeof(bot_state_t));
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
bs = botstates[client];
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_Assert(bs, qfalse)
|
2012-08-04 10:54:37 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bs->inuse) {
|
|
|
|
AI_main_BotAIPrint(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client);
|
2012-01-22 21:34:33 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (trap_AAS_Initialized() == 0) {
|
|
|
|
AI_main_BotAIPrint(PRT_FATAL, "AAS not initialized\n");
|
2012-01-22 21:34:33 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
//load the bot character
|
|
|
|
bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill);
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bs->character == 0) {
|
|
|
|
AI_main_BotAIPrint(PRT_FATAL, "couldn't load skill %d from %s\n", settings->skill, settings->characterfile);
|
2012-01-22 21:34:33 +00:00
|
|
|
return qfalse;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//copy the settings
|
|
|
|
memcpy(&bs->settings, settings, sizeof(bot_settings_t));
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//allocate a goal state
|
|
|
|
bs->gs = trap_BotAllocGoalState(client);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//load the item weights
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, AI_MAX_PATH);
|
2012-01-22 21:34:33 +00:00
|
|
|
errnum = trap_BotLoadItemWeights(bs->gs, filename);
|
|
|
|
if (errnum != BLERR_NOERROR) {
|
|
|
|
trap_BotFreeGoalState(bs->gs);
|
|
|
|
return qfalse;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//allocate a weapon state
|
|
|
|
bs->ws = trap_BotAllocWeaponState();
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//load the weapon weights
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, AI_MAX_PATH);
|
2012-01-22 21:34:33 +00:00
|
|
|
errnum = trap_BotLoadWeaponWeights(bs->ws, filename);
|
|
|
|
if (errnum != BLERR_NOERROR) {
|
|
|
|
trap_BotFreeGoalState(bs->gs);
|
|
|
|
trap_BotFreeWeaponState(bs->ws);
|
|
|
|
return qfalse;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//allocate a chat state
|
|
|
|
bs->cs = trap_BotAllocChatState();
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//load the chat file
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, AI_MAX_PATH);
|
|
|
|
trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, AI_MAX_PATH);
|
2012-01-22 21:34:33 +00:00
|
|
|
errnum = trap_BotLoadChatFile(bs->cs, filename, name);
|
|
|
|
if (errnum != BLERR_NOERROR) {
|
|
|
|
trap_BotFreeChatState(bs->cs);
|
|
|
|
trap_BotFreeGoalState(bs->gs);
|
|
|
|
trap_BotFreeWeaponState(bs->ws);
|
|
|
|
return qfalse;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//get the gender characteristic
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, AI_MAX_PATH);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//set the chat gender
|
2014-10-11 16:41:15 +00:00
|
|
|
if (*gender == 'f' || *gender == 'F') {
|
|
|
|
trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE);
|
|
|
|
} else if (*gender == 'm' || *gender == 'M') {
|
|
|
|
trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE);
|
|
|
|
} else {
|
|
|
|
trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS);
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
bs->inuse = qtrue;
|
|
|
|
bs->client = client;
|
|
|
|
bs->entitynum = client;
|
|
|
|
bs->setupcount = 4;
|
2012-08-04 10:54:37 +00:00
|
|
|
bs->entergame_time = trap_AAS_Time();
|
2012-01-22 21:34:33 +00:00
|
|
|
bs->ms = trap_BotAllocMoveState();
|
2012-08-04 10:54:37 +00:00
|
|
|
bs->walker = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
numbots++;
|
|
|
|
|
|
|
|
if (trap_Cvar_VariableIntegerValue("bot_testichat")) {
|
|
|
|
trap_BotLibVarSet("bot_testichat", "1");
|
|
|
|
BotChatTest(bs);
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//NOTE: reschedule the bot thinking
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotScheduleBotThink();
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//if interbreeding start with a mutation
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bot_interbreed != 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotMutateGoalFuzzyLogic(bs->gs, 1);
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//bot has been setup succesfully
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAIShutdownClient
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAIShutdownClient(int32_t client) {
|
|
|
|
bot_state_t* bs = NULL;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
bs = botstates[client];
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bs == NULL || !bs->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
//BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client);
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BotChat_ExitGame(bs)) {
|
|
|
|
trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
trap_BotFreeMoveState(bs->ms);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
//free the goal state
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotFreeGoalState(bs->gs);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//free the chat file
|
|
|
|
trap_BotFreeChatState(bs->cs);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//free the weapon weights
|
|
|
|
trap_BotFreeWeaponState(bs->ws);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//free the bot character
|
|
|
|
trap_BotFreeCharacter(bs->character);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
BotFreeWaypoints(bs->checkpoints);
|
|
|
|
BotFreeWaypoints(bs->patrolpoints);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//clear the bot state
|
|
|
|
memset(bs, 0, sizeof(bot_state_t));
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//set the inuse flag to qfalse
|
|
|
|
bs->inuse = qfalse;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//there's one bot less
|
|
|
|
numbots--;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//everything went ok
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotResetState
|
|
|
|
|
|
|
|
called when a bot enters the intermission or observer mode and
|
|
|
|
when the level is changed
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
void AI_main_BotResetState(bot_state_t* bs) {
|
|
|
|
int32_t client = 0;
|
|
|
|
int32_t entitynum = 0;
|
|
|
|
int32_t inuse = 0;
|
|
|
|
int32_t movestate = 0;
|
|
|
|
int32_t goalstate = 0;
|
|
|
|
int32_t chatstate = 0;
|
|
|
|
int32_t weaponstate = 0;
|
|
|
|
int32_t character;
|
2012-01-22 21:34:33 +00:00
|
|
|
bot_settings_t settings;
|
2014-10-11 16:41:15 +00:00
|
|
|
playerState_t ps; //current player state
|
|
|
|
double entergame_time = 0.0;
|
|
|
|
|
|
|
|
AI_Assert(bs, (void)0)
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
//save some things that should not be reset here
|
|
|
|
memcpy(&settings, &bs->settings, sizeof(bot_settings_t));
|
|
|
|
memcpy(&ps, &bs->cur_ps, sizeof(playerState_t));
|
|
|
|
inuse = bs->inuse;
|
|
|
|
client = bs->client;
|
|
|
|
entitynum = bs->entitynum;
|
|
|
|
character = bs->character;
|
|
|
|
movestate = bs->ms;
|
|
|
|
goalstate = bs->gs;
|
|
|
|
chatstate = bs->cs;
|
|
|
|
weaponstate = bs->ws;
|
|
|
|
entergame_time = bs->entergame_time;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//free checkpoints and patrol points
|
|
|
|
BotFreeWaypoints(bs->checkpoints);
|
|
|
|
BotFreeWaypoints(bs->patrolpoints);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//reset the whole state
|
|
|
|
memset(bs, 0, sizeof(bot_state_t));
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//copy back some state stuff that should not be reset
|
|
|
|
bs->ms = movestate;
|
|
|
|
bs->gs = goalstate;
|
|
|
|
bs->cs = chatstate;
|
|
|
|
bs->ws = weaponstate;
|
|
|
|
memcpy(&bs->cur_ps, &ps, sizeof(playerState_t));
|
|
|
|
memcpy(&bs->settings, &settings, sizeof(bot_settings_t));
|
|
|
|
bs->inuse = inuse;
|
|
|
|
bs->client = client;
|
|
|
|
bs->entitynum = entitynum;
|
|
|
|
bs->character = character;
|
|
|
|
bs->entergame_time = entergame_time;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//reset several states
|
|
|
|
if (bs->ms) trap_BotResetMoveState(bs->ms);
|
|
|
|
if (bs->gs) trap_BotResetGoalState(bs->gs);
|
|
|
|
if (bs->ws) trap_BotResetWeaponState(bs->ws);
|
|
|
|
if (bs->gs) trap_BotResetAvoidGoals(bs->gs);
|
|
|
|
if (bs->ms) trap_BotResetAvoidReach(bs->ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAILoadMap
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAILoadMap(int32_t restart) {
|
|
|
|
int32_t i = 0;
|
|
|
|
vmCvar_t mapname;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (restart == 0) {
|
|
|
|
trap_Cvar_Register(&mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM);
|
|
|
|
trap_BotLibLoadMap(mapname.string);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
2012-01-22 21:34:33 +00:00
|
|
|
if (botstates[i] && botstates[i]->inuse) {
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotResetState(botstates[i]);
|
2012-01-22 21:34:33 +00:00
|
|
|
botstates[i]->setupcount = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BotSetupDeathmatchAI();
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
BotAIStartFrame
|
|
|
|
==================
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAIStartFrame(int32_t time) {
|
|
|
|
int32_t i = 0;
|
|
|
|
gentity_t* ent = NULL;
|
2012-01-22 21:34:33 +00:00
|
|
|
bot_entitystate_t state;
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t elapsed_time = 0;
|
|
|
|
int32_t thinktime = 0;
|
|
|
|
static int32_t local_time;
|
|
|
|
static int32_t botlib_residual;
|
|
|
|
static int32_t lastbotthink_time;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
G_CheckBotSpawn();
|
|
|
|
|
|
|
|
trap_Cvar_Update(&bot_rocketjump);
|
|
|
|
trap_Cvar_Update(&bot_grapple);
|
|
|
|
trap_Cvar_Update(&bot_fastchat);
|
|
|
|
trap_Cvar_Update(&bot_nochat);
|
|
|
|
trap_Cvar_Update(&bot_testrchat);
|
|
|
|
trap_Cvar_Update(&bot_thinktime);
|
|
|
|
trap_Cvar_Update(&bot_memorydump);
|
|
|
|
trap_Cvar_Update(&bot_pause);
|
|
|
|
trap_Cvar_Update(&bot_report);
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bot_report.integer != 0) {
|
|
|
|
AI_main_BotTeamplayReport();
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_Cvar_Set("bot_report", "0");
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bot_pause.integer != 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
// execute bot user commands every frame
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] == NULL || !botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if (g_entities[i].client->pers.connected != CON_CONNECTED) {
|
2012-01-22 21:34:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
botstates[i]->lastucmd.forwardmove = 0;
|
|
|
|
botstates[i]->lastucmd.rightmove = 0;
|
|
|
|
botstates[i]->lastucmd.upmove = 0;
|
|
|
|
botstates[i]->lastucmd.buttons = 0;
|
|
|
|
botstates[i]->lastucmd.serverTime = time;
|
|
|
|
trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
|
|
|
|
}
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (bot_memorydump.integer != 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibVarSet("memorydump", "1");
|
|
|
|
trap_Cvar_Set("bot_memorydump", "0");
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//check if bot interbreeding is activated
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotInterbreeding();
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//cap the bot think time
|
|
|
|
if (bot_thinktime.integer > 200) {
|
|
|
|
trap_Cvar_Set("bot_thinktime", "200");
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//if the bot think time changed we should reschedule the bots
|
|
|
|
if (bot_thinktime.integer != lastbotthink_time) {
|
|
|
|
lastbotthink_time = bot_thinktime.integer;
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotScheduleBotThink();
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
elapsed_time = time - local_time;
|
|
|
|
local_time = time;
|
|
|
|
|
|
|
|
botlib_residual += elapsed_time;
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (elapsed_time > bot_thinktime.integer) {
|
|
|
|
thinktime = elapsed_time;
|
|
|
|
} else {
|
|
|
|
thinktime = bot_thinktime.integer;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
// update the bot library
|
2014-10-11 16:41:15 +00:00
|
|
|
if (botlib_residual >= thinktime) {
|
2012-01-22 21:34:33 +00:00
|
|
|
botlib_residual -= thinktime;
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
trap_BotLibStartFrame((float)time / 1000);
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (trap_AAS_Initialized() == 0) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
//update entities in the botlib
|
|
|
|
for (i = 0; i < MAX_GENTITIES; i++) {
|
|
|
|
ent = &g_entities[i];
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if (ent == NULL) {
|
|
|
|
trap_BotLibUpdateEntity(i, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
if (!ent->inuse) {
|
|
|
|
trap_BotLibUpdateEntity(i, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
if (!ent->r.linked) {
|
|
|
|
trap_BotLibUpdateEntity(i, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
if (ent->r.svFlags & SVF_NOCLIENT) {
|
|
|
|
trap_BotLibUpdateEntity(i, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
// do not update missiles
|
2012-08-04 10:54:37 +00:00
|
|
|
if (ent->s.eType == ET_MISSILE) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibUpdateEntity(i, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
// do not update event only entities
|
|
|
|
if (ent->s.eType > ET_EVENTS) {
|
|
|
|
trap_BotLibUpdateEntity(i, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
memset(&state, 0, sizeof(bot_entitystate_t));
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
VectorCopy(ent->r.currentOrigin, state.origin);
|
|
|
|
if (i < MAX_CLIENTS) {
|
|
|
|
VectorCopy(ent->s.apos.trBase, state.angles);
|
|
|
|
} else {
|
|
|
|
VectorCopy(ent->r.currentAngles, state.angles);
|
|
|
|
}
|
|
|
|
VectorCopy(ent->s.origin2, state.old_origin);
|
|
|
|
VectorCopy(ent->r.mins, state.mins);
|
|
|
|
VectorCopy(ent->r.maxs, state.maxs);
|
|
|
|
state.type = ent->s.eType;
|
|
|
|
state.flags = ent->s.eFlags;
|
2014-10-11 16:41:15 +00:00
|
|
|
state.solid = ent->r.bmodel ? SOLID_BSP : SOLID_BBOX;
|
2012-01-22 21:34:33 +00:00
|
|
|
state.groundent = ent->s.groundEntityNum;
|
|
|
|
state.modelindex = ent->s.modelindex;
|
|
|
|
state.modelindex2 = ent->s.modelindex2;
|
|
|
|
state.frame = ent->s.frame;
|
|
|
|
state.event = ent->s.event;
|
|
|
|
state.eventParm = ent->s.eventParm;
|
|
|
|
state.powerups = ent->s.powerups;
|
|
|
|
state.legsAnim = ent->s.legsAnim;
|
|
|
|
state.torsoAnim = ent->s.torsoAnim;
|
|
|
|
state.weapon = ent->s.weapon;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibUpdateEntity(i, &state);
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAIRegularUpdate();
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// execute scheduled bot AI
|
2014-10-11 16:41:15 +00:00
|
|
|
for (i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] == NULL || !botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
botstates[i]->botthink_residual += elapsed_time;
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if (botstates[i]->botthink_residual >= thinktime) {
|
2012-01-22 21:34:33 +00:00
|
|
|
botstates[i]->botthink_residual -= thinktime;
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
if (trap_AAS_Initialized() == 0) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
if (g_entities[i].client->pers.connected == CON_CONNECTED) {
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotAI(i, (float)thinktime / 1000);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// execute bot user commands every frame
|
2014-10-11 16:41:15 +00:00
|
|
|
for (i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] == NULL || !botstates[i]->inuse) {
|
2012-01-22 21:34:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 16:41:15 +00:00
|
|
|
|
|
|
|
if (g_entities[i].client->pers.connected != CON_CONNECTED) {
|
2012-01-22 21:34:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-10-11 16:41:15 +00:00
|
|
|
AI_main_BotUpdateInput(botstates[i], time, elapsed_time);
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotInitLibrary
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
static int32_t AI_main_BotInitLibrary(void) {
|
|
|
|
int32_t gt = 0;
|
|
|
|
char buf[144];
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
//set the maxclients and maxentities library variables before calling BotSetupLibrary
|
|
|
|
trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) == 0) {
|
|
|
|
strcpy(buf, "8");
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibVarSet("maxclients", buf);
|
|
|
|
Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES);
|
|
|
|
trap_BotLibVarSet("maxentities", buf);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//bsp checksum
|
|
|
|
trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("sv_mapChecksum", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//maximum number of aas links
|
|
|
|
trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("max_aaslinks", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//maximum number of items in a level
|
|
|
|
trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("max_levelitems", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//game type
|
2012-08-04 10:54:37 +00:00
|
|
|
gt = trap_Cvar_VariableIntegerValue("g_gametype");
|
2014-10-11 16:41:15 +00:00
|
|
|
if (gt == GT_SINGLE_PLAYER) {
|
2012-08-04 10:54:37 +00:00
|
|
|
gt = AIGT_SINGLE_PLAYER;
|
2014-10-11 16:41:15 +00:00
|
|
|
} else if (gt >= GT_TEAM) {
|
2012-08-04 10:54:37 +00:00
|
|
|
gt = AIGT_TEAM;
|
2014-10-11 16:41:15 +00:00
|
|
|
} else {
|
2012-08-04 10:54:37 +00:00
|
|
|
gt = AIGT_OTHER;
|
|
|
|
}
|
|
|
|
trap_BotLibVarSet("ai_gametype", va("%i", gt));
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//bot developer mode and log file
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_Cvar_VariableStringBuffer("bot_developer", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) == 0) {
|
|
|
|
strcpy(buf, "0");
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
trap_BotLibVarSet("bot_developer", buf);
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibVarSet("log", buf);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//no chatting
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("nochat", "0");
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//visualize jump pads
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("bot_visualizejumppads", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//forced clustering calculations
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("forceclustering", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//forced reachability calculations
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("forcereachability", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//force writing of AAS to file
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("forcewrite", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//no AAS optimization
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("aasoptimize", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//reload instead of cache bot character files
|
|
|
|
trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) == 0) {
|
|
|
|
strcpy(buf, "0");
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibVarSet("bot_reloadcharacters", buf);
|
2014-10-11 16:41:15 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//base directory
|
|
|
|
trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("basedir", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//game directory
|
|
|
|
trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("gamedir", buf);
|
|
|
|
}
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
//cd directory
|
|
|
|
trap_Cvar_VariableStringBuffer("fs_cdpath", buf, sizeof(buf));
|
2014-10-11 16:41:15 +00:00
|
|
|
if (strlen(buf) != 0) {
|
|
|
|
trap_BotLibVarSet("cddir", buf);
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//setup the bot library
|
|
|
|
return trap_BotLibSetup();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAISetup
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAISetup(int32_t restart) {
|
|
|
|
int32_t errnum = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
#ifdef RANDOMIZE
|
|
|
|
srand((unsigned)time(NULL));
|
|
|
|
#endif //RANDOMIZE
|
|
|
|
|
|
|
|
bot_setupComplete = qtrue;
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT);
|
|
|
|
trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT);
|
|
|
|
trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT);
|
|
|
|
trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT);
|
|
|
|
trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT);
|
|
|
|
trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0);
|
|
|
|
trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0);
|
|
|
|
trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0);
|
|
|
|
trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0);
|
|
|
|
|
|
|
|
//if the game is restarted for a tournament
|
2014-10-11 16:41:15 +00:00
|
|
|
if (restart != 0) {
|
2012-01-22 21:34:33 +00:00
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//initialize the bot states
|
2014-10-11 16:41:15 +00:00
|
|
|
memset(botstates, 0, sizeof(botstates));
|
|
|
|
|
|
|
|
errnum = AI_main_BotInitLibrary();
|
|
|
|
if (errnum != BLERR_NOERROR) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
BotAIShutdown
|
|
|
|
==============
|
|
|
|
*/
|
2014-10-11 16:41:15 +00:00
|
|
|
int32_t AI_main_BotAIShutdown(int32_t restart) {
|
2012-01-22 21:34:33 +00:00
|
|
|
//if the game is restarted for a tournament
|
2014-10-11 16:41:15 +00:00
|
|
|
if (restart != 0) {
|
|
|
|
int32_t i = 0;
|
2013-08-12 18:26:28 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
//shutdown all the bots in the botlib
|
2014-10-11 16:41:15 +00:00
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
|
|
if (botstates[i] != NULL && botstates[i]->inuse) {
|
|
|
|
AI_main_BotAIShutdownClient(botstates[i]->client);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
//don't shutdown the bot library
|
2014-10-11 16:41:15 +00:00
|
|
|
} else {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_BotLibShutdown();
|
|
|
|
}
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|