rallyunlimited-engine/code/server/sv_bot.c
2024-02-02 19:46:17 +03:00

660 lines
16 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// sv_bot.c
#include "server.h"
#include "../botlib/botlib.h"
typedef struct bot_debugpoly_s
{
int inuse;
int color;
int numPoints;
vec3_t points[128];
} bot_debugpoly_t;
static bot_debugpoly_t *debugpolygons;
static int bot_maxdebugpolys;
extern botlib_export_t *botlib_export;
int bot_enable;
/*
==================
SV_BotAllocateClient
==================
*/
int SV_BotAllocateClient( void ) {
int i;
client_t *cl;
// find a client slot
for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) {
if ( cl->state == CS_FREE ) {
break;
}
}
if ( i == sv_maxclients->integer ) {
return -1;
}
cl->gentity = SV_GentityNum( i );
cl->gentity->s.number = i;
cl->state = CS_ACTIVE;
cl->lastPacketTime = svs.time;
cl->snapshotMsec = 1000 / sv_fps->integer;
cl->netchan.remoteAddress.type = NA_BOT;
cl->rate = 0;
cl->tld[0] = '\0';
cl->country = "BOT";
return i;
}
/*
==================
SV_BotFreeClient
==================
*/
void SV_BotFreeClient( int clientNum ) {
client_t *cl;
if ( (unsigned) clientNum >= sv_maxclients->integer ) {
Com_Error( ERR_DROP, "SV_BotFreeClient: bad clientNum: %i", clientNum );
}
cl = &svs.clients[clientNum];
cl->state = CS_FREE;
cl->name[0] = '\0';
if ( cl->gentity ) {
cl->gentity->r.svFlags &= ~SVF_BOT;
}
}
/*
==================
BotDrawDebugPolygons
==================
*/
void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value) {
static cvar_t *bot_debug, *bot_groundonly, *bot_reachability, *bot_highlightarea;
bot_debugpoly_t *poly;
int i, parm0;
if (!debugpolygons)
return;
//bot debugging
if (!bot_debug) bot_debug = Cvar_Get("bot_debug", "0", 0);
//
if (bot_enable && bot_debug->integer) {
//show reachabilities
if (!bot_reachability) bot_reachability = Cvar_Get("bot_reachability", "0", 0);
//show ground faces only
if (!bot_groundonly) bot_groundonly = Cvar_Get("bot_groundonly", "1", 0);
//get the hightlight area
if (!bot_highlightarea) bot_highlightarea = Cvar_Get("bot_highlightarea", "0", 0);
//
parm0 = 0;
if (svs.clients[0].lastUsercmd.buttons & BUTTON_ATTACK) parm0 |= 1;
if (bot_reachability->integer) parm0 |= 2;
if (bot_groundonly->integer) parm0 |= 4;
botlib_export->BotLibVarSet("bot_highlightarea", bot_highlightarea->string);
botlib_export->Test(parm0, NULL, svs.clients[0].gentity->r.currentOrigin,
svs.clients[0].gentity->r.currentAngles);
} //end if
//draw all debug polys
for (i = 0; i < bot_maxdebugpolys; i++) {
poly = &debugpolygons[i];
if (!poly->inuse) continue;
drawPoly(poly->color, poly->numPoints, (float *) poly->points);
//Com_Printf("poly %i, numpoints = %d\n", i, poly->numPoints);
}
}
/*
==================
BotImport_Print
==================
*/
static __attribute__ ((format (printf, 2, 3))) void QDECL BotImport_Print(int type, const char *fmt, ...)
{
char str[2048];
va_list ap;
va_start(ap, fmt);
Q_vsnprintf(str, sizeof(str), fmt, ap);
va_end(ap);
switch(type) {
case PRT_MESSAGE: {
Com_Printf("%s", str);
break;
}
case PRT_WARNING: {
Com_Printf(S_COLOR_YELLOW "Warning: %s", str);
break;
}
case PRT_ERROR: {
Com_Printf(S_COLOR_RED "Error: %s", str);
break;
}
case PRT_FATAL: {
Com_Printf(S_COLOR_RED "Fatal: %s", str);
break;
}
case PRT_EXIT: {
Com_Error(ERR_DROP, S_COLOR_RED "Exit: %s", str);
break;
}
default: {
Com_Printf("unknown print type\n");
break;
}
}
}
/*
==================
BotImport_Trace
==================
*/
static void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) {
trace_t trace;
SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse);
//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 = 0;
bsptrace->surface.flags = trace.surfaceFlags;
bsptrace->ent = trace.entityNum;
bsptrace->exp_dist = 0;
bsptrace->sidenum = 0;
bsptrace->contents = 0;
}
/*
==================
BotImport_EntityTrace
==================
*/
static void BotImport_EntityTrace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask) {
trace_t trace;
SV_ClipToEntity(&trace, start, mins, maxs, end, entnum, contentmask, qfalse);
//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 = 0;
bsptrace->surface.flags = trace.surfaceFlags;
bsptrace->ent = trace.entityNum;
bsptrace->exp_dist = 0;
bsptrace->sidenum = 0;
bsptrace->contents = 0;
}
/*
==================
BotImport_PointContents
==================
*/
static int BotImport_PointContents(vec3_t point) {
return SV_PointContents(point, -1);
}
/*
==================
BotImport_inPVS
==================
*/
static int BotImport_inPVS(vec3_t p1, vec3_t p2) {
return SV_inPVS (p1, p2);
}
/*
==================
BotImport_BSPEntityData
==================
*/
static char *BotImport_BSPEntityData(void) {
return CM_EntityString();
}
/*
==================
BotImport_BSPModelMinsMaxsOrigin
==================
*/
static void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin) {
clipHandle_t h;
vec3_t mins, maxs;
float max;
int i;
h = CM_InlineModel(modelnum);
CM_ModelBounds(h, mins, maxs);
//if the model is rotated
if ((angles[0] || angles[1] || angles[2])) {
// expand for rotation
max = RadiusFromBounds(mins, maxs);
for (i = 0; i < 3; i++) {
mins[i] = -max;
maxs[i] = max;
}
}
if (outmins) VectorCopy(mins, outmins);
if (outmaxs) VectorCopy(maxs, outmaxs);
if (origin) VectorClear(origin);
}
/*
==================
BotImport_GetMemory
==================
*/
static void *BotImport_GetMemory(int size) {
void *ptr;
ptr = Z_TagMalloc( size, TAG_BOTLIB );
return ptr;
}
/*
==================
BotImport_FreeMemory
==================
*/
static void BotImport_FreeMemory(void *ptr) {
Z_Free(ptr);
}
/*
=================
BotImport_HunkAlloc
=================
*/
static void *BotImport_HunkAlloc( int size ) {
if( Hunk_CheckMark() ) {
Com_Error( ERR_DROP, "%s(): Alloc with marks already set", __func__ );
}
return Hunk_Alloc( size, h_high );
}
/*
==================
BotImport_DebugPolygonCreate
==================
*/
int BotImport_DebugPolygonCreate(int color, int numPoints, vec3_t *points) {
bot_debugpoly_t *poly;
int i;
if (!debugpolygons)
return 0;
for (i = 1; i < bot_maxdebugpolys; i++) {
if (!debugpolygons[i].inuse)
break;
}
if (i >= bot_maxdebugpolys)
return 0;
poly = &debugpolygons[i];
poly->inuse = qtrue;
poly->color = color;
poly->numPoints = numPoints;
Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
//
return i;
}
/*
==================
BotImport_DebugPolygonShow
==================
*/
static void BotImport_DebugPolygonShow(int id, int color, int numPoints, vec3_t *points) {
bot_debugpoly_t *poly;
if ( !debugpolygons )
return;
if ( (unsigned) id >= bot_maxdebugpolys )
return;
poly = &debugpolygons[id];
poly->inuse = qtrue;
poly->color = color;
poly->numPoints = numPoints;
Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
}
/*
==================
BotImport_DebugPolygonDelete
==================
*/
void BotImport_DebugPolygonDelete(int id)
{
if ( !debugpolygons )
return;
if ( (unsigned) id >= bot_maxdebugpolys )
return;
debugpolygons[id].inuse = qfalse;
}
/*
==================
BotImport_DebugLineCreate
==================
*/
static int BotImport_DebugLineCreate(void) {
vec3_t points[1];
return BotImport_DebugPolygonCreate(0, 0, points);
}
/*
==================
BotImport_DebugLineDelete
==================
*/
static void BotImport_DebugLineDelete(int line) {
BotImport_DebugPolygonDelete(line);
}
/*
==================
BotImport_DebugLineShow
==================
*/
static void BotImport_DebugLineShow(int line, vec3_t start, vec3_t end, int color) {
vec3_t points[4], dir, cross, up = {0, 0, 1};
float dot;
VectorCopy(start, points[0]);
VectorCopy(start, points[1]);
//points[1][2] -= 2;
VectorCopy(end, points[2]);
//points[2][2] -= 2;
VectorCopy(end, points[3]);
VectorSubtract(end, start, dir);
VectorNormalize(dir);
dot = DotProduct(dir, up);
if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
else CrossProduct(dir, up, cross);
VectorNormalize(cross);
VectorMA(points[0], 2, cross, points[0]);
VectorMA(points[1], -2, cross, points[1]);
VectorMA(points[2], -2, cross, points[2]);
VectorMA(points[3], 2, cross, points[3]);
BotImport_DebugPolygonShow(line, color, 4, points);
}
/*
==================
SV_BotClientCommand
==================
*/
static void BotClientCommand( int client, const char *command ) {
if ( (unsigned) client < sv_maxclients->integer ) {
SV_ExecuteClientCommand( &svs.clients[client], command );
}
}
/*
==================
SV_BotFrame
==================
*/
void SV_BotFrame( int time ) {
if (!bot_enable) return;
//NOTE: maybe the game is already shutdown
if (!gvm) return;
VM_Call( gvm, 1, BOTAI_START_FRAME, time );
}
/*
===============
SV_BotLibSetup
===============
*/
int SV_BotLibSetup( void ) {
if (!bot_enable) {
return 0;
}
if ( !botlib_export ) {
Com_Printf( S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n" );
return -1;
}
return botlib_export->BotLibSetup();
}
/*
===============
SV_ShutdownBotLib
Called when either the entire server is being killed, or
it is changing to a different game directory.
===============
*/
int SV_BotLibShutdown( void ) {
if ( !botlib_export ) {
return -1;
}
return botlib_export->BotLibShutdown();
}
/*
==================
SV_BotInitCvars
==================
*/
void SV_BotInitCvars(void) {
Cvar_Get("bot_enable", "1", 0); //enable the bot
Cvar_Get("bot_developer", "0", CVAR_CHEAT); //bot developer mode
Cvar_Get("bot_debug", "0", CVAR_CHEAT); //enable bot debugging
Cvar_Get("bot_maxdebugpolys", "2", 0); //maximum number of debug polys
Cvar_Get("bot_groundonly", "1", 0); //only show ground faces of areas
Cvar_Get("bot_reachability", "0", 0); //show all reachabilities to other areas
Cvar_Get("bot_visualizejumppads", "0", CVAR_CHEAT); //show jumppads
Cvar_Get("bot_forceclustering", "0", 0); //force cluster calculations
Cvar_Get("bot_forcereachability", "0", 0); //force reachability calculations
Cvar_Get("bot_forcewrite", "0", 0); //force writing aas file
Cvar_Get("bot_aasoptimize", "0", 0); //no aas file optimisation
Cvar_Get("bot_saveroutingcache", "0", 0); //save routing cache
Cvar_Get("bot_thinktime", "100", 0); //msec the bots thinks
Cvar_Get("bot_reloadcharacters", "0", 0); //reload the bot characters each time
Cvar_Get("bot_testichat", "0", 0); //test ichats
Cvar_Get("bot_testrchat", "0", 0); //test rchats
Cvar_Get("bot_testsolid", "0", CVAR_CHEAT); //test for solid areas
Cvar_Get("bot_testclusters", "0", CVAR_CHEAT); //test the AAS clusters
Cvar_Get("bot_fastchat", "0", 0); //fast chatting bots
Cvar_Get("bot_nochat", "0", 0); //disable chats
Cvar_Get("bot_pause", "0", CVAR_CHEAT); //pause the bots thinking
Cvar_Get("bot_report", "0", CVAR_CHEAT); //get a full report in ctf
Cvar_Get("bot_grapple", "0", 0); //enable grapple
Cvar_Get("bot_rocketjump", "1", 0); //enable rocket jumping
Cvar_Get("bot_challenge", "0", 0); //challenging bot
Cvar_Get("bot_minplayers", "0", 0); //minimum players in a team or the game
Cvar_Get("bot_interbreedchar", "", CVAR_CHEAT); //bot character used for interbreeding
Cvar_Get("bot_interbreedbots", "10", CVAR_CHEAT); //number of bots used for interbreeding
Cvar_Get("bot_interbreedcycle", "20", CVAR_CHEAT); //bot interbreeding cycle
Cvar_Get("bot_interbreedwrite", "", CVAR_CHEAT); //write interbreeded bots to this file
}
/*
==================
SV_BotInitBotLib
==================
*/
void SV_BotInitBotLib(void) {
botlib_import_t botlib_import;
if (debugpolygons) Z_Free(debugpolygons);
bot_maxdebugpolys = Cvar_VariableIntegerValue("bot_maxdebugpolys");
debugpolygons = Z_Malloc(sizeof(bot_debugpoly_t) * bot_maxdebugpolys);
botlib_import.Print = BotImport_Print;
botlib_import.Trace = BotImport_Trace;
botlib_import.EntityTrace = BotImport_EntityTrace;
botlib_import.PointContents = BotImport_PointContents;
botlib_import.inPVS = BotImport_inPVS;
botlib_import.BSPEntityData = BotImport_BSPEntityData;
botlib_import.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin;
botlib_import.BotClientCommand = BotClientCommand;
//memory management
botlib_import.GetMemory = BotImport_GetMemory;
botlib_import.FreeMemory = BotImport_FreeMemory;
botlib_import.AvailableMemory = Z_AvailableMemory;
botlib_import.HunkAlloc = BotImport_HunkAlloc;
// file system access
botlib_import.FS_FOpenFile = FS_FOpenFileByMode;
botlib_import.FS_Read = FS_Read;
botlib_import.FS_Write = FS_Write;
botlib_import.FS_FCloseFile = FS_FCloseFile;
botlib_import.FS_Seek = FS_Seek;
//debug lines
botlib_import.DebugLineCreate = BotImport_DebugLineCreate;
botlib_import.DebugLineDelete = BotImport_DebugLineDelete;
botlib_import.DebugLineShow = BotImport_DebugLineShow;
//debug polygons
botlib_import.DebugPolygonCreate = BotImport_DebugPolygonCreate;
botlib_import.DebugPolygonDelete = BotImport_DebugPolygonDelete;
botlib_import.Sys_Milliseconds = Sys_Milliseconds;
botlib_export = (botlib_export_t *)GetBotLibAPI( BOTLIB_API_VERSION, &botlib_import );
assert(botlib_export); // somehow we end up with a zero import.
}
//
// * * * BOT AI CODE IS BELOW THIS POINT * * *
//
/*
==================
SV_BotGetConsoleMessage
==================
*/
int SV_BotGetConsoleMessage( int client, char *buf, int size )
{
if ( (unsigned) client < sv_maxclients->integer ) {
client_t* cl;
int index;
cl = &svs.clients[client];
cl->lastPacketTime = svs.time;
if ( cl->reliableAcknowledge == cl->reliableSequence ) {
return qfalse;
}
cl->reliableAcknowledge++;
index = cl->reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 );
if ( !cl->reliableCommands[index][0] ) {
return qfalse;
}
Q_strncpyz( buf, cl->reliableCommands[index], size );
return qtrue;
} else {
return qfalse;
}
}
#if 0
/*
==================
EntityInPVS
==================
*/
int EntityInPVS( int client, int entityNum ) {
client_t *cl;
clientSnapshot_t *frame;
int i;
cl = &svs.clients[client];
frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
for ( i = 0; i < frame->num_entities; i++ ) {
if ( svs.snapshotEntities[(frame->first_entity + i) % svs.numSnapshotEntities].number == entityNum ) {
return qtrue;
}
}
return qfalse;
}
#endif
/*
==================
SV_BotGetSnapshotEntity
==================
*/
int SV_BotGetSnapshotEntity( int client, int sequence ) {
if ( (unsigned) client < sv_maxclients->integer ) {
const client_t* cl = &svs.clients[client];
const clientSnapshot_t* frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
if ( (unsigned) sequence >= frame->num_entities ) {
return -1;
}
return frame->ents[sequence]->number;
} else {
return -1;
}
}