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