sv_bot.c (16211B)
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 Foobar; 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 "../game/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 */ 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 */ 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 */ 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 */ 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 vsprintf(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 */ 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 */ 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 */ 229 int BotImport_PointContents(vec3_t point) { 230 return SV_PointContents(point, -1); 231 } 232 233 /* 234 ================== 235 BotImport_inPVS 236 ================== 237 */ 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 */ 247 char *BotImport_BSPEntityData(void) { 248 return CM_EntityString(); 249 } 250 251 /* 252 ================== 253 BotImport_BSPModelMinsMaxsOrigin 254 ================== 255 */ 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 */ 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 */ 296 void BotImport_FreeMemory(void *ptr) { 297 Z_Free(ptr); 298 } 299 300 /* 301 ================= 302 BotImport_HunkAlloc 303 ================= 304 */ 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 */ 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 */ 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 */ 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 */ 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 */ 381 void BotImport_DebugLineDelete(int line) { 382 BotImport_DebugPolygonDelete(line); 383 } 384 385 /* 386 ================== 387 BotImport_DebugLineShow 388 ================== 389 */ 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 */ 423 void BotClientCommand( int client, char *command ) { 424 SV_ExecuteClientCommand( &svs.clients[client], command, qtrue ); 425 } 426 427 /* 428 ================== 429 SV_BotFrame 430 ================== 431 */ 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 */ 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 */ 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 */ 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 */ 518 void SV_BotInitBotLib(void) { 519 botlib_import_t botlib_import; 520 521 if ( !Cvar_VariableValue("fs_restrict") && !Sys_CheckCD() ) { 522 Com_Error( ERR_NEED_CD, "Game CD not in drive" ); 523 } 524 525 if (debugpolygons) Z_Free(debugpolygons); 526 bot_maxdebugpolys = Cvar_VariableIntegerValue("bot_maxdebugpolys"); 527 debugpolygons = Z_Malloc(sizeof(bot_debugpoly_t) * bot_maxdebugpolys); 528 529 botlib_import.Print = BotImport_Print; 530 botlib_import.Trace = BotImport_Trace; 531 botlib_import.EntityTrace = BotImport_EntityTrace; 532 botlib_import.PointContents = BotImport_PointContents; 533 botlib_import.inPVS = BotImport_inPVS; 534 botlib_import.BSPEntityData = BotImport_BSPEntityData; 535 botlib_import.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin; 536 botlib_import.BotClientCommand = BotClientCommand; 537 538 //memory management 539 botlib_import.GetMemory = BotImport_GetMemory; 540 botlib_import.FreeMemory = BotImport_FreeMemory; 541 botlib_import.AvailableMemory = Z_AvailableMemory; 542 botlib_import.HunkAlloc = BotImport_HunkAlloc; 543 544 // file system access 545 botlib_import.FS_FOpenFile = FS_FOpenFileByMode; 546 botlib_import.FS_Read = FS_Read2; 547 botlib_import.FS_Write = FS_Write; 548 botlib_import.FS_FCloseFile = FS_FCloseFile; 549 botlib_import.FS_Seek = FS_Seek; 550 551 //debug lines 552 botlib_import.DebugLineCreate = BotImport_DebugLineCreate; 553 botlib_import.DebugLineDelete = BotImport_DebugLineDelete; 554 botlib_import.DebugLineShow = BotImport_DebugLineShow; 555 556 //debug polygons 557 botlib_import.DebugPolygonCreate = BotImport_DebugPolygonCreate; 558 botlib_import.DebugPolygonDelete = BotImport_DebugPolygonDelete; 559 560 botlib_export = (botlib_export_t *)GetBotLibAPI( BOTLIB_API_VERSION, &botlib_import ); 561 assert(botlib_export); // bk001129 - somehow we end up with a zero import. 562 } 563 564 565 // 566 // * * * BOT AI CODE IS BELOW THIS POINT * * * 567 // 568 569 /* 570 ================== 571 SV_BotGetConsoleMessage 572 ================== 573 */ 574 int SV_BotGetConsoleMessage( int client, char *buf, int size ) 575 { 576 client_t *cl; 577 int index; 578 579 cl = &svs.clients[client]; 580 cl->lastPacketTime = svs.time; 581 582 if ( cl->reliableAcknowledge == cl->reliableSequence ) { 583 return qfalse; 584 } 585 586 cl->reliableAcknowledge++; 587 index = cl->reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 ); 588 589 if ( !cl->reliableCommands[index][0] ) { 590 return qfalse; 591 } 592 593 Q_strncpyz( buf, cl->reliableCommands[index], size ); 594 return qtrue; 595 } 596 597 #if 0 598 /* 599 ================== 600 EntityInPVS 601 ================== 602 */ 603 int EntityInPVS( int client, int entityNum ) { 604 client_t *cl; 605 clientSnapshot_t *frame; 606 int i; 607 608 cl = &svs.clients[client]; 609 frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK]; 610 for ( i = 0; i < frame->num_entities; i++ ) { 611 if ( svs.snapshotEntities[(frame->first_entity + i) % svs.numSnapshotEntities].number == entityNum ) { 612 return qtrue; 613 } 614 } 615 return qfalse; 616 } 617 #endif 618 619 /* 620 ================== 621 SV_BotGetSnapshotEntity 622 ================== 623 */ 624 int SV_BotGetSnapshotEntity( int client, int sequence ) { 625 client_t *cl; 626 clientSnapshot_t *frame; 627 628 cl = &svs.clients[client]; 629 frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK]; 630 if (sequence < 0 || sequence >= frame->num_entities) { 631 return -1; 632 } 633 return svs.snapshotEntities[(frame->first_entity + sequence) % svs.numSnapshotEntities].number; 634 } 635