1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 // sv_bot.c
23
24 #include "server.h"
25 #include "../botlib/botlib.h"
26
27 typedef struct bot_debugpoly_s
28 {
29 int inuse;
30 int color;
31 int numPoints;
32 vec3_t points[128];
33 } bot_debugpoly_t;
34
35 static bot_debugpoly_t *debugpolygons;
36 int bot_maxdebugpolys;
37
38 extern botlib_export_t *botlib_export;
39 int bot_enable;
40
41
42 /*
43 ==================
44 SV_BotAllocateClient
45 ==================
46 */
SV_BotAllocateClient(void)47 int SV_BotAllocateClient(void) {
48 int i;
49 client_t *cl;
50
51 // find a client slot
52 for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) {
53 if ( cl->state == CS_FREE ) {
54 break;
55 }
56 }
57
58 if ( i == sv_maxclients->integer ) {
59 return -1;
60 }
61
62 cl->gentity = SV_GentityNum( i );
63 cl->gentity->s.number = i;
64 cl->state = CS_ACTIVE;
65 cl->lastPacketTime = svs.time;
66 cl->netchan.remoteAddress.type = NA_BOT;
67 cl->rate = 16384;
68
69 return i;
70 }
71
72 /*
73 ==================
74 SV_BotFreeClient
75 ==================
76 */
SV_BotFreeClient(int clientNum)77 void SV_BotFreeClient( int clientNum ) {
78 client_t *cl;
79
80 if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
81 Com_Error( ERR_DROP, "SV_BotFreeClient: bad clientNum: %i", clientNum );
82 }
83 cl = &svs.clients[clientNum];
84 cl->state = CS_FREE;
85 cl->name[0] = 0;
86 if ( cl->gentity ) {
87 cl->gentity->r.svFlags &= ~SVF_BOT;
88 }
89 }
90
91 /*
92 ==================
93 BotDrawDebugPolygons
94 ==================
95 */
BotDrawDebugPolygons(void (* drawPoly)(int color,int numPoints,float * points),int value)96 void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value) {
97 static cvar_t *bot_debug, *bot_groundonly, *bot_reachability, *bot_highlightarea;
98 bot_debugpoly_t *poly;
99 int i, parm0;
100
101 if (!debugpolygons)
102 return;
103 //bot debugging
104 if (!bot_debug) bot_debug = Cvar_Get("bot_debug", "0", 0);
105 //
106 if (bot_enable && bot_debug->integer) {
107 //show reachabilities
108 if (!bot_reachability) bot_reachability = Cvar_Get("bot_reachability", "0", 0);
109 //show ground faces only
110 if (!bot_groundonly) bot_groundonly = Cvar_Get("bot_groundonly", "1", 0);
111 //get the hightlight area
112 if (!bot_highlightarea) bot_highlightarea = Cvar_Get("bot_highlightarea", "0", 0);
113 //
114 parm0 = 0;
115 if (svs.clients[0].lastUsercmd.buttons & BUTTON_ATTACK) parm0 |= 1;
116 if (bot_reachability->integer) parm0 |= 2;
117 if (bot_groundonly->integer) parm0 |= 4;
118 botlib_export->BotLibVarSet("bot_highlightarea", bot_highlightarea->string);
119 botlib_export->Test(parm0, NULL, svs.clients[0].gentity->r.currentOrigin,
120 svs.clients[0].gentity->r.currentAngles);
121 } //end if
122 //draw all debug polys
123 for (i = 0; i < bot_maxdebugpolys; i++) {
124 poly = &debugpolygons[i];
125 if (!poly->inuse) continue;
126 drawPoly(poly->color, poly->numPoints, (float *) poly->points);
127 //Com_Printf("poly %i, numpoints = %d\n", i, poly->numPoints);
128 }
129 }
130
131 /*
132 ==================
133 BotImport_Print
134 ==================
135 */
BotImport_Print(int type,char * fmt,...)136 void QDECL BotImport_Print(int type, char *fmt, ...)
137 {
138 char str[2048];
139 va_list ap;
140
141 va_start(ap, fmt);
142 Q_vsnprintf(str, sizeof(str), fmt, ap);
143 va_end(ap);
144
145 switch(type) {
146 case PRT_MESSAGE: {
147 Com_Printf("%s", str);
148 break;
149 }
150 case PRT_WARNING: {
151 Com_Printf(S_COLOR_YELLOW "Warning: %s", str);
152 break;
153 }
154 case PRT_ERROR: {
155 Com_Printf(S_COLOR_RED "Error: %s", str);
156 break;
157 }
158 case PRT_FATAL: {
159 Com_Printf(S_COLOR_RED "Fatal: %s", str);
160 break;
161 }
162 case PRT_EXIT: {
163 Com_Error(ERR_DROP, S_COLOR_RED "Exit: %s", str);
164 break;
165 }
166 default: {
167 Com_Printf("unknown print type\n");
168 break;
169 }
170 }
171 }
172
173 /*
174 ==================
175 BotImport_Trace
176 ==================
177 */
BotImport_Trace(bsp_trace_t * bsptrace,vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,int passent,int contentmask)178 void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) {
179 trace_t trace;
180
181 SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse);
182 //copy the trace information
183 bsptrace->allsolid = trace.allsolid;
184 bsptrace->startsolid = trace.startsolid;
185 bsptrace->fraction = trace.fraction;
186 VectorCopy(trace.endpos, bsptrace->endpos);
187 bsptrace->plane.dist = trace.plane.dist;
188 VectorCopy(trace.plane.normal, bsptrace->plane.normal);
189 bsptrace->plane.signbits = trace.plane.signbits;
190 bsptrace->plane.type = trace.plane.type;
191 bsptrace->surface.value = trace.surfaceFlags;
192 bsptrace->ent = trace.entityNum;
193 bsptrace->exp_dist = 0;
194 bsptrace->sidenum = 0;
195 bsptrace->contents = 0;
196 }
197
198 /*
199 ==================
200 BotImport_EntityTrace
201 ==================
202 */
BotImport_EntityTrace(bsp_trace_t * bsptrace,vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,int entnum,int contentmask)203 void BotImport_EntityTrace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask) {
204 trace_t trace;
205
206 SV_ClipToEntity(&trace, start, mins, maxs, end, entnum, contentmask, qfalse);
207 //copy the trace information
208 bsptrace->allsolid = trace.allsolid;
209 bsptrace->startsolid = trace.startsolid;
210 bsptrace->fraction = trace.fraction;
211 VectorCopy(trace.endpos, bsptrace->endpos);
212 bsptrace->plane.dist = trace.plane.dist;
213 VectorCopy(trace.plane.normal, bsptrace->plane.normal);
214 bsptrace->plane.signbits = trace.plane.signbits;
215 bsptrace->plane.type = trace.plane.type;
216 bsptrace->surface.value = trace.surfaceFlags;
217 bsptrace->ent = trace.entityNum;
218 bsptrace->exp_dist = 0;
219 bsptrace->sidenum = 0;
220 bsptrace->contents = 0;
221 }
222
223
224 /*
225 ==================
226 BotImport_PointContents
227 ==================
228 */
BotImport_PointContents(vec3_t point)229 int BotImport_PointContents(vec3_t point) {
230 return SV_PointContents(point, -1);
231 }
232
233 /*
234 ==================
235 BotImport_inPVS
236 ==================
237 */
BotImport_inPVS(vec3_t p1,vec3_t p2)238 int BotImport_inPVS(vec3_t p1, vec3_t p2) {
239 return SV_inPVS (p1, p2);
240 }
241
242 /*
243 ==================
244 BotImport_BSPEntityData
245 ==================
246 */
BotImport_BSPEntityData(void)247 char *BotImport_BSPEntityData(void) {
248 return CM_EntityString();
249 }
250
251 /*
252 ==================
253 BotImport_BSPModelMinsMaxsOrigin
254 ==================
255 */
BotImport_BSPModelMinsMaxsOrigin(int modelnum,vec3_t angles,vec3_t outmins,vec3_t outmaxs,vec3_t origin)256 void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin) {
257 clipHandle_t h;
258 vec3_t mins, maxs;
259 float max;
260 int i;
261
262 h = CM_InlineModel(modelnum);
263 CM_ModelBounds(h, mins, maxs);
264 //if the model is rotated
265 if ((angles[0] || angles[1] || angles[2])) {
266 // expand for rotation
267
268 max = RadiusFromBounds(mins, maxs);
269 for (i = 0; i < 3; i++) {
270 mins[i] = -max;
271 maxs[i] = max;
272 }
273 }
274 if (outmins) VectorCopy(mins, outmins);
275 if (outmaxs) VectorCopy(maxs, outmaxs);
276 if (origin) VectorClear(origin);
277 }
278
279 /*
280 ==================
281 BotImport_GetMemory
282 ==================
283 */
BotImport_GetMemory(int size)284 void *BotImport_GetMemory(int size) {
285 void *ptr;
286
287 ptr = Z_TagMalloc( size, TAG_BOTLIB );
288 return ptr;
289 }
290
291 /*
292 ==================
293 BotImport_FreeMemory
294 ==================
295 */
BotImport_FreeMemory(void * ptr)296 void BotImport_FreeMemory(void *ptr) {
297 Z_Free(ptr);
298 }
299
300 /*
301 =================
302 BotImport_HunkAlloc
303 =================
304 */
BotImport_HunkAlloc(int size)305 void *BotImport_HunkAlloc( int size ) {
306 if( Hunk_CheckMark() ) {
307 Com_Error( ERR_DROP, "SV_Bot_HunkAlloc: Alloc with marks already set\n" );
308 }
309 return Hunk_Alloc( size, h_high );
310 }
311
312 /*
313 ==================
314 BotImport_DebugPolygonCreate
315 ==================
316 */
BotImport_DebugPolygonCreate(int color,int numPoints,vec3_t * points)317 int BotImport_DebugPolygonCreate(int color, int numPoints, vec3_t *points) {
318 bot_debugpoly_t *poly;
319 int i;
320
321 if (!debugpolygons)
322 return 0;
323
324 for (i = 1; i < bot_maxdebugpolys; i++) {
325 if (!debugpolygons[i].inuse)
326 break;
327 }
328 if (i >= bot_maxdebugpolys)
329 return 0;
330 poly = &debugpolygons[i];
331 poly->inuse = qtrue;
332 poly->color = color;
333 poly->numPoints = numPoints;
334 Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
335 //
336 return i;
337 }
338
339 /*
340 ==================
341 BotImport_DebugPolygonShow
342 ==================
343 */
BotImport_DebugPolygonShow(int id,int color,int numPoints,vec3_t * points)344 void BotImport_DebugPolygonShow(int id, int color, int numPoints, vec3_t *points) {
345 bot_debugpoly_t *poly;
346
347 if (!debugpolygons) return;
348 poly = &debugpolygons[id];
349 poly->inuse = qtrue;
350 poly->color = color;
351 poly->numPoints = numPoints;
352 Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
353 }
354
355 /*
356 ==================
357 BotImport_DebugPolygonDelete
358 ==================
359 */
BotImport_DebugPolygonDelete(int id)360 void BotImport_DebugPolygonDelete(int id)
361 {
362 if (!debugpolygons) return;
363 debugpolygons[id].inuse = qfalse;
364 }
365
366 /*
367 ==================
368 BotImport_DebugLineCreate
369 ==================
370 */
BotImport_DebugLineCreate(void)371 int BotImport_DebugLineCreate(void) {
372 vec3_t points[1];
373 return BotImport_DebugPolygonCreate(0, 0, points);
374 }
375
376 /*
377 ==================
378 BotImport_DebugLineDelete
379 ==================
380 */
BotImport_DebugLineDelete(int line)381 void BotImport_DebugLineDelete(int line) {
382 BotImport_DebugPolygonDelete(line);
383 }
384
385 /*
386 ==================
387 BotImport_DebugLineShow
388 ==================
389 */
BotImport_DebugLineShow(int line,vec3_t start,vec3_t end,int color)390 void BotImport_DebugLineShow(int line, vec3_t start, vec3_t end, int color) {
391 vec3_t points[4], dir, cross, up = {0, 0, 1};
392 float dot;
393
394 VectorCopy(start, points[0]);
395 VectorCopy(start, points[1]);
396 //points[1][2] -= 2;
397 VectorCopy(end, points[2]);
398 //points[2][2] -= 2;
399 VectorCopy(end, points[3]);
400
401
402 VectorSubtract(end, start, dir);
403 VectorNormalize(dir);
404 dot = DotProduct(dir, up);
405 if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
406 else CrossProduct(dir, up, cross);
407
408 VectorNormalize(cross);
409
410 VectorMA(points[0], 2, cross, points[0]);
411 VectorMA(points[1], -2, cross, points[1]);
412 VectorMA(points[2], -2, cross, points[2]);
413 VectorMA(points[3], 2, cross, points[3]);
414
415 BotImport_DebugPolygonShow(line, color, 4, points);
416 }
417
418 /*
419 ==================
420 SV_BotClientCommand
421 ==================
422 */
BotClientCommand(int client,char * command)423 void BotClientCommand( int client, char *command ) {
424 SV_ExecuteClientCommand( &svs.clients[client], command, qtrue );
425 }
426
427 /*
428 ==================
429 SV_BotFrame
430 ==================
431 */
SV_BotFrame(int time)432 void SV_BotFrame( int time ) {
433 if (!bot_enable) return;
434 //NOTE: maybe the game is already shutdown
435 if (!gvm) return;
436 VM_Call( gvm, BOTAI_START_FRAME, time );
437 }
438
439 /*
440 ===============
441 SV_BotLibSetup
442 ===============
443 */
SV_BotLibSetup(void)444 int SV_BotLibSetup( void ) {
445 if (!bot_enable) {
446 return 0;
447 }
448
449 if ( !botlib_export ) {
450 Com_Printf( S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n" );
451 return -1;
452 }
453
454 return botlib_export->BotLibSetup();
455 }
456
457 /*
458 ===============
459 SV_ShutdownBotLib
460
461 Called when either the entire server is being killed, or
462 it is changing to a different game directory.
463 ===============
464 */
SV_BotLibShutdown(void)465 int SV_BotLibShutdown( void ) {
466
467 if ( !botlib_export ) {
468 return -1;
469 }
470
471 return botlib_export->BotLibShutdown();
472 }
473
474 /*
475 ==================
476 SV_BotInitCvars
477 ==================
478 */
SV_BotInitCvars(void)479 void SV_BotInitCvars(void) {
480
481 Cvar_Get("bot_enable", "1", 0); //enable the bot
482 Cvar_Get("bot_developer", "0", CVAR_CHEAT); //bot developer mode
483 Cvar_Get("bot_debug", "0", CVAR_CHEAT); //enable bot debugging
484 Cvar_Get("bot_maxdebugpolys", "2", 0); //maximum number of debug polys
485 Cvar_Get("bot_groundonly", "1", 0); //only show ground faces of areas
486 Cvar_Get("bot_reachability", "0", 0); //show all reachabilities to other areas
487 Cvar_Get("bot_visualizejumppads", "0", CVAR_CHEAT); //show jumppads
488 Cvar_Get("bot_forceclustering", "0", 0); //force cluster calculations
489 Cvar_Get("bot_forcereachability", "0", 0); //force reachability calculations
490 Cvar_Get("bot_forcewrite", "0", 0); //force writing aas file
491 Cvar_Get("bot_aasoptimize", "0", 0); //no aas file optimisation
492 Cvar_Get("bot_saveroutingcache", "0", 0); //save routing cache
493 Cvar_Get("bot_thinktime", "100", CVAR_CHEAT); //msec the bots thinks
494 Cvar_Get("bot_reloadcharacters", "0", 0); //reload the bot characters each time
495 Cvar_Get("bot_testichat", "0", 0); //test ichats
496 Cvar_Get("bot_testrchat", "0", 0); //test rchats
497 Cvar_Get("bot_testsolid", "0", CVAR_CHEAT); //test for solid areas
498 Cvar_Get("bot_testclusters", "0", CVAR_CHEAT); //test the AAS clusters
499 Cvar_Get("bot_fastchat", "0", 0); //fast chatting bots
500 Cvar_Get("bot_nochat", "0", 0); //disable chats
501 Cvar_Get("bot_pause", "0", CVAR_CHEAT); //pause the bots thinking
502 Cvar_Get("bot_report", "0", CVAR_CHEAT); //get a full report in ctf
503 Cvar_Get("bot_grapple", "0", 0); //enable grapple
504 Cvar_Get("bot_rocketjump", "1", 0); //enable rocket jumping
505 Cvar_Get("bot_challenge", "0", 0); //challenging bot
506 Cvar_Get("bot_minplayers", "0", 0); //minimum players in a team or the game
507 Cvar_Get("bot_interbreedchar", "", CVAR_CHEAT); //bot character used for interbreeding
508 Cvar_Get("bot_interbreedbots", "10", CVAR_CHEAT); //number of bots used for interbreeding
509 Cvar_Get("bot_interbreedcycle", "20", CVAR_CHEAT); //bot interbreeding cycle
510 Cvar_Get("bot_interbreedwrite", "", CVAR_CHEAT); //write interbreeded bots to this file
511 }
512
513 /*
514 ==================
515 SV_BotInitBotLib
516 ==================
517 */
SV_BotInitBotLib(void)518 void SV_BotInitBotLib(void) {
519 botlib_import_t botlib_import;
520
521 if (debugpolygons) Z_Free(debugpolygons);
522 bot_maxdebugpolys = Cvar_VariableIntegerValue("bot_maxdebugpolys");
523 debugpolygons = Z_Malloc(sizeof(bot_debugpoly_t) * bot_maxdebugpolys);
524
525 botlib_import.Print = BotImport_Print;
526 botlib_import.Trace = BotImport_Trace;
527 botlib_import.EntityTrace = BotImport_EntityTrace;
528 botlib_import.PointContents = BotImport_PointContents;
529 botlib_import.inPVS = BotImport_inPVS;
530 botlib_import.BSPEntityData = BotImport_BSPEntityData;
531 botlib_import.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin;
532 botlib_import.BotClientCommand = BotClientCommand;
533
534 //memory management
535 botlib_import.GetMemory = BotImport_GetMemory;
536 botlib_import.FreeMemory = BotImport_FreeMemory;
537 botlib_import.AvailableMemory = Z_AvailableMemory;
538 botlib_import.HunkAlloc = BotImport_HunkAlloc;
539
540 // file system access
541 botlib_import.FS_FOpenFile = FS_FOpenFileByMode;
542 botlib_import.FS_Read = FS_Read2;
543 botlib_import.FS_Write = FS_Write;
544 botlib_import.FS_FCloseFile = FS_FCloseFile;
545 botlib_import.FS_Seek = FS_Seek;
546
547 //debug lines
548 botlib_import.DebugLineCreate = BotImport_DebugLineCreate;
549 botlib_import.DebugLineDelete = BotImport_DebugLineDelete;
550 botlib_import.DebugLineShow = BotImport_DebugLineShow;
551
552 //debug polygons
553 botlib_import.DebugPolygonCreate = BotImport_DebugPolygonCreate;
554 botlib_import.DebugPolygonDelete = BotImport_DebugPolygonDelete;
555
556 botlib_export = (botlib_export_t *)GetBotLibAPI( BOTLIB_API_VERSION, &botlib_import );
557 assert(botlib_export); // somehow we end up with a zero import.
558 }
559
560
561 //
562 // * * * BOT AI CODE IS BELOW THIS POINT * * *
563 //
564
565 /*
566 ==================
567 SV_BotGetConsoleMessage
568 ==================
569 */
SV_BotGetConsoleMessage(int client,char * buf,int size)570 int SV_BotGetConsoleMessage( int client, char *buf, int size )
571 {
572 client_t *cl;
573 int index;
574
575 cl = &svs.clients[client];
576 cl->lastPacketTime = svs.time;
577
578 if ( cl->reliableAcknowledge == cl->reliableSequence ) {
579 return qfalse;
580 }
581
582 cl->reliableAcknowledge++;
583 index = cl->reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 );
584
585 if ( !cl->reliableCommands[index][0] ) {
586 return qfalse;
587 }
588
589 Q_strncpyz( buf, cl->reliableCommands[index], size );
590 return qtrue;
591 }
592
593 #if 0
594 /*
595 ==================
596 EntityInPVS
597 ==================
598 */
599 int EntityInPVS( int client, int entityNum ) {
600 client_t *cl;
601 clientSnapshot_t *frame;
602 int i;
603
604 cl = &svs.clients[client];
605 frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
606 for ( i = 0; i < frame->num_entities; i++ ) {
607 if ( svs.snapshotEntities[(frame->first_entity + i) % svs.numSnapshotEntities].number == entityNum ) {
608 return qtrue;
609 }
610 }
611 return qfalse;
612 }
613 #endif
614
615 /*
616 ==================
617 SV_BotGetSnapshotEntity
618 ==================
619 */
SV_BotGetSnapshotEntity(int client,int sequence)620 int SV_BotGetSnapshotEntity( int client, int sequence ) {
621 client_t *cl;
622 clientSnapshot_t *frame;
623
624 cl = &svs.clients[client];
625 frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
626 if (sequence < 0 || sequence >= frame->num_entities) {
627 return -1;
628 }
629 return svs.snapshotEntities[(frame->first_entity + sequence) % svs.numSnapshotEntities].number;
630 }
631
632