ai_main.c (45513B)
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 // 23 24 /***************************************************************************** 25 * name: ai_main.c 26 * 27 * desc: Quake3 bot AI 28 * 29 * $Archive: /MissionPack/code/game/ai_main.c $ 30 * 31 *****************************************************************************/ 32 33 34 #include "g_local.h" 35 #include "q_shared.h" 36 #include "botlib.h" //bot lib interface 37 #include "be_aas.h" 38 #include "be_ea.h" 39 #include "be_ai_char.h" 40 #include "be_ai_chat.h" 41 #include "be_ai_gen.h" 42 #include "be_ai_goal.h" 43 #include "be_ai_move.h" 44 #include "be_ai_weap.h" 45 // 46 #include "ai_main.h" 47 #include "ai_dmq3.h" 48 #include "ai_chat.h" 49 #include "ai_cmd.h" 50 #include "ai_dmnet.h" 51 #include "ai_vcmd.h" 52 53 // 54 #include "chars.h" 55 #include "inv.h" 56 #include "syn.h" 57 58 #define MAX_PATH 144 59 60 61 //bot states 62 bot_state_t *botstates[MAX_CLIENTS]; 63 //number of bots 64 int numbots; 65 //floating point time 66 float floattime; 67 //time to do a regular update 68 float regularupdate_time; 69 // 70 int bot_interbreed; 71 int bot_interbreedmatchcount; 72 // 73 vmCvar_t bot_thinktime; 74 vmCvar_t bot_memorydump; 75 vmCvar_t bot_saveroutingcache; 76 vmCvar_t bot_pause; 77 vmCvar_t bot_report; 78 vmCvar_t bot_testsolid; 79 vmCvar_t bot_testclusters; 80 vmCvar_t bot_developer; 81 vmCvar_t bot_interbreedchar; 82 vmCvar_t bot_interbreedbots; 83 vmCvar_t bot_interbreedcycle; 84 vmCvar_t bot_interbreedwrite; 85 86 87 void ExitLevel( void ); 88 89 90 /* 91 ================== 92 BotAI_Print 93 ================== 94 */ 95 void QDECL BotAI_Print(int type, char *fmt, ...) { 96 char str[2048]; 97 va_list ap; 98 99 va_start(ap, fmt); 100 vsprintf(str, fmt, ap); 101 va_end(ap); 102 103 switch(type) { 104 case PRT_MESSAGE: { 105 G_Printf("%s", str); 106 break; 107 } 108 case PRT_WARNING: { 109 G_Printf( S_COLOR_YELLOW "Warning: %s", str ); 110 break; 111 } 112 case PRT_ERROR: { 113 G_Printf( S_COLOR_RED "Error: %s", str ); 114 break; 115 } 116 case PRT_FATAL: { 117 G_Printf( S_COLOR_RED "Fatal: %s", str ); 118 break; 119 } 120 case PRT_EXIT: { 121 G_Error( S_COLOR_RED "Exit: %s", str ); 122 break; 123 } 124 default: { 125 G_Printf( "unknown print type\n" ); 126 break; 127 } 128 } 129 } 130 131 132 /* 133 ================== 134 BotAI_Trace 135 ================== 136 */ 137 void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { 138 trace_t trace; 139 140 trap_Trace(&trace, start, mins, maxs, end, passent, contentmask); 141 //copy the trace information 142 bsptrace->allsolid = trace.allsolid; 143 bsptrace->startsolid = trace.startsolid; 144 bsptrace->fraction = trace.fraction; 145 VectorCopy(trace.endpos, bsptrace->endpos); 146 bsptrace->plane.dist = trace.plane.dist; 147 VectorCopy(trace.plane.normal, bsptrace->plane.normal); 148 bsptrace->plane.signbits = trace.plane.signbits; 149 bsptrace->plane.type = trace.plane.type; 150 bsptrace->surface.value = trace.surfaceFlags; 151 bsptrace->ent = trace.entityNum; 152 bsptrace->exp_dist = 0; 153 bsptrace->sidenum = 0; 154 bsptrace->contents = 0; 155 } 156 157 /* 158 ================== 159 BotAI_GetClientState 160 ================== 161 */ 162 int BotAI_GetClientState( int clientNum, playerState_t *state ) { 163 gentity_t *ent; 164 165 ent = &g_entities[clientNum]; 166 if ( !ent->inuse ) { 167 return qfalse; 168 } 169 if ( !ent->client ) { 170 return qfalse; 171 } 172 173 memcpy( state, &ent->client->ps, sizeof(playerState_t) ); 174 return qtrue; 175 } 176 177 /* 178 ================== 179 BotAI_GetEntityState 180 ================== 181 */ 182 int BotAI_GetEntityState( int entityNum, entityState_t *state ) { 183 gentity_t *ent; 184 185 ent = &g_entities[entityNum]; 186 memset( state, 0, sizeof(entityState_t) ); 187 if (!ent->inuse) return qfalse; 188 if (!ent->r.linked) return qfalse; 189 if (ent->r.svFlags & SVF_NOCLIENT) return qfalse; 190 memcpy( state, &ent->s, sizeof(entityState_t) ); 191 return qtrue; 192 } 193 194 /* 195 ================== 196 BotAI_GetSnapshotEntity 197 ================== 198 */ 199 int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) { 200 int entNum; 201 202 entNum = trap_BotGetSnapshotEntity( clientNum, sequence ); 203 if ( entNum == -1 ) { 204 memset(state, 0, sizeof(entityState_t)); 205 return -1; 206 } 207 208 BotAI_GetEntityState( entNum, state ); 209 210 return sequence + 1; 211 } 212 213 /* 214 ================== 215 BotAI_BotInitialChat 216 ================== 217 */ 218 void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) { 219 int i, mcontext; 220 va_list ap; 221 char *p; 222 char *vars[MAX_MATCHVARIABLES]; 223 224 memset(vars, 0, sizeof(vars)); 225 va_start(ap, type); 226 p = va_arg(ap, char *); 227 for (i = 0; i < MAX_MATCHVARIABLES; i++) { 228 if( !p ) { 229 break; 230 } 231 vars[i] = p; 232 p = va_arg(ap, char *); 233 } 234 va_end(ap); 235 236 mcontext = BotSynonymContext(bs); 237 238 trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] ); 239 } 240 241 242 /* 243 ================== 244 BotTestAAS 245 ================== 246 */ 247 void BotTestAAS(vec3_t origin) { 248 int areanum; 249 aas_areainfo_t info; 250 251 trap_Cvar_Update(&bot_testsolid); 252 trap_Cvar_Update(&bot_testclusters); 253 if (bot_testsolid.integer) { 254 if (!trap_AAS_Initialized()) return; 255 areanum = BotPointAreaNum(origin); 256 if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area"); 257 else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area"); 258 } 259 else if (bot_testclusters.integer) { 260 if (!trap_AAS_Initialized()) return; 261 areanum = BotPointAreaNum(origin); 262 if (!areanum) 263 BotAI_Print(PRT_MESSAGE, "\r^1Solid! "); 264 else { 265 trap_AAS_AreaInfo(areanum, &info); 266 BotAI_Print(PRT_MESSAGE, "\rarea %d, cluster %d ", areanum, info.cluster); 267 } 268 } 269 } 270 271 /* 272 ================== 273 BotReportStatus 274 ================== 275 */ 276 void BotReportStatus(bot_state_t *bs) { 277 char goalname[MAX_MESSAGE_SIZE]; 278 char netname[MAX_MESSAGE_SIZE]; 279 char *leader, flagstatus[32]; 280 // 281 ClientName(bs->client, netname, sizeof(netname)); 282 if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; 283 else leader = " "; 284 285 strcpy(flagstatus, " "); 286 if (gametype == GT_CTF) { 287 if (BotCTFCarryingFlag(bs)) { 288 if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); 289 else strcpy(flagstatus, S_COLOR_BLUE"F "); 290 } 291 } 292 #ifdef MISSIONPACK 293 else if (gametype == GT_1FCTF) { 294 if (Bot1FCTFCarryingFlag(bs)) { 295 if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); 296 else strcpy(flagstatus, S_COLOR_BLUE"F "); 297 } 298 } 299 else if (gametype == GT_HARVESTER) { 300 if (BotHarvesterCarryingCubes(bs)) { 301 if (BotTeam(bs) == TEAM_RED) Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_RED"%2d", bs->inventory[INVENTORY_REDCUBE]); 302 else Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_BLUE"%2d", bs->inventory[INVENTORY_BLUECUBE]); 303 } 304 } 305 #endif 306 307 switch(bs->ltgtype) { 308 case LTG_TEAMHELP: 309 { 310 EasyClientName(bs->teammate, goalname, sizeof(goalname)); 311 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname); 312 break; 313 } 314 case LTG_TEAMACCOMPANY: 315 { 316 EasyClientName(bs->teammate, goalname, sizeof(goalname)); 317 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname); 318 break; 319 } 320 case LTG_DEFENDKEYAREA: 321 { 322 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); 323 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname); 324 break; 325 } 326 case LTG_GETITEM: 327 { 328 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); 329 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname); 330 break; 331 } 332 case LTG_KILL: 333 { 334 ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); 335 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname); 336 break; 337 } 338 case LTG_CAMP: 339 case LTG_CAMPORDER: 340 { 341 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus); 342 break; 343 } 344 case LTG_PATROL: 345 { 346 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus); 347 break; 348 } 349 case LTG_GETFLAG: 350 { 351 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus); 352 break; 353 } 354 case LTG_RUSHBASE: 355 { 356 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus); 357 break; 358 } 359 case LTG_RETURNFLAG: 360 { 361 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus); 362 break; 363 } 364 case LTG_ATTACKENEMYBASE: 365 { 366 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: attacking the enemy base\n", netname, leader, flagstatus); 367 break; 368 } 369 case LTG_HARVEST: 370 { 371 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: harvesting\n", netname, leader, flagstatus); 372 break; 373 } 374 default: 375 { 376 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus); 377 break; 378 } 379 } 380 } 381 382 /* 383 ================== 384 BotTeamplayReport 385 ================== 386 */ 387 void BotTeamplayReport(void) { 388 int i; 389 char buf[MAX_INFO_STRING]; 390 391 BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n"); 392 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { 393 // 394 if ( !botstates[i] || !botstates[i]->inuse ) continue; 395 // 396 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); 397 //if no config string or no name 398 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; 399 //skip spectators 400 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) { 401 BotReportStatus(botstates[i]); 402 } 403 } 404 BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n"); 405 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { 406 // 407 if ( !botstates[i] || !botstates[i]->inuse ) continue; 408 // 409 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); 410 //if no config string or no name 411 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; 412 //skip spectators 413 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) { 414 BotReportStatus(botstates[i]); 415 } 416 } 417 } 418 419 /* 420 ================== 421 BotSetInfoConfigString 422 ================== 423 */ 424 void BotSetInfoConfigString(bot_state_t *bs) { 425 char goalname[MAX_MESSAGE_SIZE]; 426 char netname[MAX_MESSAGE_SIZE]; 427 char action[MAX_MESSAGE_SIZE]; 428 char *leader, carrying[32], *cs; 429 bot_goal_t goal; 430 // 431 ClientName(bs->client, netname, sizeof(netname)); 432 if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; 433 else leader = " "; 434 435 strcpy(carrying, " "); 436 if (gametype == GT_CTF) { 437 if (BotCTFCarryingFlag(bs)) { 438 strcpy(carrying, "F "); 439 } 440 } 441 #ifdef MISSIONPACK 442 else if (gametype == GT_1FCTF) { 443 if (Bot1FCTFCarryingFlag(bs)) { 444 strcpy(carrying, "F "); 445 } 446 } 447 else if (gametype == GT_HARVESTER) { 448 if (BotHarvesterCarryingCubes(bs)) { 449 if (BotTeam(bs) == TEAM_RED) Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_REDCUBE]); 450 else Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_BLUECUBE]); 451 } 452 } 453 #endif 454 455 switch(bs->ltgtype) { 456 case LTG_TEAMHELP: 457 { 458 EasyClientName(bs->teammate, goalname, sizeof(goalname)); 459 Com_sprintf(action, sizeof(action), "helping %s", goalname); 460 break; 461 } 462 case LTG_TEAMACCOMPANY: 463 { 464 EasyClientName(bs->teammate, goalname, sizeof(goalname)); 465 Com_sprintf(action, sizeof(action), "accompanying %s", goalname); 466 break; 467 } 468 case LTG_DEFENDKEYAREA: 469 { 470 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); 471 Com_sprintf(action, sizeof(action), "defending %s", goalname); 472 break; 473 } 474 case LTG_GETITEM: 475 { 476 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); 477 Com_sprintf(action, sizeof(action), "getting item %s", goalname); 478 break; 479 } 480 case LTG_KILL: 481 { 482 ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); 483 Com_sprintf(action, sizeof(action), "killing %s", goalname); 484 break; 485 } 486 case LTG_CAMP: 487 case LTG_CAMPORDER: 488 { 489 Com_sprintf(action, sizeof(action), "camping"); 490 break; 491 } 492 case LTG_PATROL: 493 { 494 Com_sprintf(action, sizeof(action), "patrolling"); 495 break; 496 } 497 case LTG_GETFLAG: 498 { 499 Com_sprintf(action, sizeof(action), "capturing flag"); 500 break; 501 } 502 case LTG_RUSHBASE: 503 { 504 Com_sprintf(action, sizeof(action), "rushing base"); 505 break; 506 } 507 case LTG_RETURNFLAG: 508 { 509 Com_sprintf(action, sizeof(action), "returning flag"); 510 break; 511 } 512 case LTG_ATTACKENEMYBASE: 513 { 514 Com_sprintf(action, sizeof(action), "attacking the enemy base"); 515 break; 516 } 517 case LTG_HARVEST: 518 { 519 Com_sprintf(action, sizeof(action), "harvesting"); 520 break; 521 } 522 default: 523 { 524 trap_BotGetTopGoal(bs->gs, &goal); 525 trap_BotGoalName(goal.number, goalname, sizeof(goalname)); 526 Com_sprintf(action, sizeof(action), "roaming %s", goalname); 527 break; 528 } 529 } 530 cs = va("l\\%s\\c\\%s\\a\\%s", 531 leader, 532 carrying, 533 action); 534 trap_SetConfigstring (CS_BOTINFO + bs->client, cs); 535 } 536 537 /* 538 ============== 539 BotUpdateInfoConfigStrings 540 ============== 541 */ 542 void BotUpdateInfoConfigStrings(void) { 543 int i; 544 char buf[MAX_INFO_STRING]; 545 546 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { 547 // 548 if ( !botstates[i] || !botstates[i]->inuse ) 549 continue; 550 // 551 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); 552 //if no config string or no name 553 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) 554 continue; 555 BotSetInfoConfigString(botstates[i]); 556 } 557 } 558 559 /* 560 ============== 561 BotInterbreedBots 562 ============== 563 */ 564 void BotInterbreedBots(void) { 565 float ranks[MAX_CLIENTS]; 566 int parent1, parent2, child; 567 int i; 568 569 // get rankings for all the bots 570 for (i = 0; i < MAX_CLIENTS; i++) { 571 if ( botstates[i] && botstates[i]->inuse ) { 572 ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; 573 } 574 else { 575 ranks[i] = -1; 576 } 577 } 578 579 if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, ranks, &parent1, &parent2, &child)) { 580 trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs); 581 trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1); 582 } 583 // reset the kills and deaths 584 for (i = 0; i < MAX_CLIENTS; i++) { 585 if (botstates[i] && botstates[i]->inuse) { 586 botstates[i]->num_kills = 0; 587 botstates[i]->num_deaths = 0; 588 } 589 } 590 } 591 592 /* 593 ============== 594 BotWriteInterbreeded 595 ============== 596 */ 597 void BotWriteInterbreeded(char *filename) { 598 float rank, bestrank; 599 int i, bestbot; 600 601 bestrank = 0; 602 bestbot = -1; 603 // get the best bot 604 for (i = 0; i < MAX_CLIENTS; i++) { 605 if ( botstates[i] && botstates[i]->inuse ) { 606 rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; 607 } 608 else { 609 rank = -1; 610 } 611 if (rank > bestrank) { 612 bestrank = rank; 613 bestbot = i; 614 } 615 } 616 if (bestbot >= 0) { 617 //write out the new goal fuzzy logic 618 trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename); 619 } 620 } 621 622 /* 623 ============== 624 BotInterbreedEndMatch 625 626 add link back into ExitLevel? 627 ============== 628 */ 629 void BotInterbreedEndMatch(void) { 630 631 if (!bot_interbreed) return; 632 bot_interbreedmatchcount++; 633 if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) { 634 bot_interbreedmatchcount = 0; 635 // 636 trap_Cvar_Update(&bot_interbreedwrite); 637 if (strlen(bot_interbreedwrite.string)) { 638 BotWriteInterbreeded(bot_interbreedwrite.string); 639 trap_Cvar_Set("bot_interbreedwrite", ""); 640 } 641 BotInterbreedBots(); 642 } 643 } 644 645 /* 646 ============== 647 BotInterbreeding 648 ============== 649 */ 650 void BotInterbreeding(void) { 651 int i; 652 653 trap_Cvar_Update(&bot_interbreedchar); 654 if (!strlen(bot_interbreedchar.string)) return; 655 //make sure we are in tournament mode 656 if (gametype != GT_TOURNAMENT) { 657 trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT)); 658 ExitLevel(); 659 return; 660 } 661 //shutdown all the bots 662 for (i = 0; i < MAX_CLIENTS; i++) { 663 if (botstates[i] && botstates[i]->inuse) { 664 BotAIShutdownClient(botstates[i]->client, qfalse); 665 } 666 } 667 //make sure all item weight configs are reloaded and Not shared 668 trap_BotLibVarSet("bot_reloadcharacters", "1"); 669 //add a number of bots using the desired bot character 670 for (i = 0; i < bot_interbreedbots.integer; i++) { 671 trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s 4 free %i %s%d\n", 672 bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i) ); 673 } 674 // 675 trap_Cvar_Set("bot_interbreedchar", ""); 676 bot_interbreed = qtrue; 677 } 678 679 /* 680 ============== 681 BotEntityInfo 682 ============== 683 */ 684 void BotEntityInfo(int entnum, aas_entityinfo_t *info) { 685 trap_AAS_EntityInfo(entnum, info); 686 } 687 688 /* 689 ============== 690 NumBots 691 ============== 692 */ 693 int NumBots(void) { 694 return numbots; 695 } 696 697 /* 698 ============== 699 BotTeamLeader 700 ============== 701 */ 702 int BotTeamLeader(bot_state_t *bs) { 703 int leader; 704 705 leader = ClientFromName(bs->teamleader); 706 if (leader < 0) return qfalse; 707 if (!botstates[leader] || !botstates[leader]->inuse) return qfalse; 708 return qtrue; 709 } 710 711 /* 712 ============== 713 AngleDifference 714 ============== 715 */ 716 float AngleDifference(float ang1, float ang2) { 717 float diff; 718 719 diff = ang1 - ang2; 720 if (ang1 > ang2) { 721 if (diff > 180.0) diff -= 360.0; 722 } 723 else { 724 if (diff < -180.0) diff += 360.0; 725 } 726 return diff; 727 } 728 729 /* 730 ============== 731 BotChangeViewAngle 732 ============== 733 */ 734 float BotChangeViewAngle(float angle, float ideal_angle, float speed) { 735 float move; 736 737 angle = AngleMod(angle); 738 ideal_angle = AngleMod(ideal_angle); 739 if (angle == ideal_angle) return angle; 740 move = ideal_angle - angle; 741 if (ideal_angle > angle) { 742 if (move > 180.0) move -= 360.0; 743 } 744 else { 745 if (move < -180.0) move += 360.0; 746 } 747 if (move > 0) { 748 if (move > speed) move = speed; 749 } 750 else { 751 if (move < -speed) move = -speed; 752 } 753 return AngleMod(angle + move); 754 } 755 756 /* 757 ============== 758 BotChangeViewAngles 759 ============== 760 */ 761 void BotChangeViewAngles(bot_state_t *bs, float thinktime) { 762 float diff, factor, maxchange, anglespeed, disired_speed; 763 int i; 764 765 if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; 766 // 767 if (bs->enemy >= 0) { 768 factor = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1); 769 maxchange = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800); 770 } 771 else { 772 factor = 0.05f; 773 maxchange = 360; 774 } 775 if (maxchange < 240) maxchange = 240; 776 maxchange *= thinktime; 777 for (i = 0; i < 2; i++) { 778 // 779 if (bot_challenge.integer) { 780 //smooth slowdown view model 781 diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); 782 anglespeed = diff * factor; 783 if (anglespeed > maxchange) anglespeed = maxchange; 784 bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i], 785 bs->ideal_viewangles[i], anglespeed); 786 } 787 else { 788 //over reaction view model 789 bs->viewangles[i] = AngleMod(bs->viewangles[i]); 790 bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); 791 diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); 792 disired_speed = diff * factor; 793 bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); 794 if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; 795 if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; 796 anglespeed = bs->viewanglespeed[i]; 797 if (anglespeed > maxchange) anglespeed = maxchange; 798 if (anglespeed < -maxchange) anglespeed = -maxchange; 799 bs->viewangles[i] += anglespeed; 800 bs->viewangles[i] = AngleMod(bs->viewangles[i]); 801 //demping 802 bs->viewanglespeed[i] *= 0.45 * (1 - factor); 803 } 804 //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` 805 //bs->viewangles[i] = bs->ideal_viewangles[i]; 806 } 807 //bs->viewangles[PITCH] = 0; 808 if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; 809 //elementary action: view 810 trap_EA_View(bs->client, bs->viewangles); 811 } 812 813 /* 814 ============== 815 BotInputToUserCommand 816 ============== 817 */ 818 void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time) { 819 vec3_t angles, forward, right; 820 short temp; 821 int j; 822 823 //clear the whole structure 824 memset(ucmd, 0, sizeof(usercmd_t)); 825 // 826 //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed); 827 //the duration for the user command in milli seconds 828 ucmd->serverTime = time; 829 // 830 if (bi->actionflags & ACTION_DELAYEDJUMP) { 831 bi->actionflags |= ACTION_JUMP; 832 bi->actionflags &= ~ACTION_DELAYEDJUMP; 833 } 834 //set the buttons 835 if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK; 836 if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK; 837 if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK; 838 if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE; 839 if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE; 840 if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING; 841 if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE; 842 if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE; 843 if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG; 844 if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE; 845 if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL; 846 if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME; 847 // 848 ucmd->weapon = bi->weapon; 849 //set the view angles 850 //NOTE: the ucmd->angles are the angles WITHOUT the delta angles 851 ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]); 852 ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]); 853 ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]); 854 //subtract the delta angles 855 for (j = 0; j < 3; j++) { 856 temp = ucmd->angles[j] - delta_angles[j]; 857 /*NOTE: disabled because temp should be mod first 858 if ( j == PITCH ) { 859 // don't let the player look up or down more than 90 degrees 860 if ( temp > 16000 ) temp = 16000; 861 else if ( temp < -16000 ) temp = -16000; 862 } 863 */ 864 ucmd->angles[j] = temp; 865 } 866 //NOTE: movement is relative to the REAL view angles 867 //get the horizontal forward and right vector 868 //get the pitch in the range [-180, 180] 869 if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH]; 870 else angles[PITCH] = 0; 871 angles[YAW] = bi->viewangles[YAW]; 872 angles[ROLL] = 0; 873 AngleVectors(angles, forward, right, NULL); 874 //bot input speed is in the range [0, 400] 875 bi->speed = bi->speed * 127 / 400; 876 //set the view independent movement 877 ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed; 878 ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed; 879 ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed; 880 //normal keyboard movement 881 if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127; 882 if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127; 883 if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127; 884 if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127; 885 //jump/moveup 886 if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127; 887 //crouch/movedown 888 if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127; 889 // 890 //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove); 891 //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime); 892 } 893 894 /* 895 ============== 896 BotUpdateInput 897 ============== 898 */ 899 void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { 900 bot_input_t bi; 901 int j; 902 903 //add the delta angles to the bot's current view angles 904 for (j = 0; j < 3; j++) { 905 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); 906 } 907 //change the bot view angles 908 BotChangeViewAngles(bs, (float) elapsed_time / 1000); 909 //retrieve the bot input 910 trap_EA_GetInput(bs->client, (float) time / 1000, &bi); 911 //respawn hack 912 if (bi.actionflags & ACTION_RESPAWN) { 913 if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); 914 } 915 //convert the bot input to a usercmd 916 BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); 917 //subtract the delta angles 918 for (j = 0; j < 3; j++) { 919 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); 920 } 921 } 922 923 /* 924 ============== 925 BotAIRegularUpdate 926 ============== 927 */ 928 void BotAIRegularUpdate(void) { 929 if (regularupdate_time < FloatTime()) { 930 trap_BotUpdateEntityItems(); 931 regularupdate_time = FloatTime() + 0.3; 932 } 933 } 934 935 /* 936 ============== 937 RemoveColorEscapeSequences 938 ============== 939 */ 940 void RemoveColorEscapeSequences( char *text ) { 941 int i, l; 942 943 l = 0; 944 for ( i = 0; text[i]; i++ ) { 945 if (Q_IsColorString(&text[i])) { 946 i++; 947 continue; 948 } 949 if (text[i] > 0x7E) 950 continue; 951 text[l++] = text[i]; 952 } 953 text[l] = '\0'; 954 } 955 956 /* 957 ============== 958 BotAI 959 ============== 960 */ 961 int BotAI(int client, float thinktime) { 962 bot_state_t *bs; 963 char buf[1024], *args; 964 int j; 965 966 trap_EA_ResetInput(client); 967 // 968 bs = botstates[client]; 969 if (!bs || !bs->inuse) { 970 BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client); 971 return qfalse; 972 } 973 974 //retrieve the current client state 975 BotAI_GetClientState( client, &bs->cur_ps ); 976 977 //retrieve any waiting server commands 978 while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { 979 //have buf point to the command and args to the command arguments 980 args = strchr( buf, ' '); 981 if (!args) continue; 982 *args++ = '\0'; 983 984 //remove color espace sequences from the arguments 985 RemoveColorEscapeSequences( args ); 986 987 if (!Q_stricmp(buf, "cp ")) 988 { /*CenterPrintf*/ } 989 else if (!Q_stricmp(buf, "cs")) 990 { /*ConfigStringModified*/ } 991 else if (!Q_stricmp(buf, "print")) { 992 //remove first and last quote from the chat message 993 memmove(args, args+1, strlen(args)); 994 args[strlen(args)-1] = '\0'; 995 trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); 996 } 997 else if (!Q_stricmp(buf, "chat")) { 998 //remove first and last quote from the chat message 999 memmove(args, args+1, strlen(args)); 1000 args[strlen(args)-1] = '\0'; 1001 trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); 1002 } 1003 else if (!Q_stricmp(buf, "tchat")) { 1004 //remove first and last quote from the chat message 1005 memmove(args, args+1, strlen(args)); 1006 args[strlen(args)-1] = '\0'; 1007 trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); 1008 } 1009 #ifdef MISSIONPACK 1010 else if (!Q_stricmp(buf, "vchat")) { 1011 BotVoiceChatCommand(bs, SAY_ALL, args); 1012 } 1013 else if (!Q_stricmp(buf, "vtchat")) { 1014 BotVoiceChatCommand(bs, SAY_TEAM, args); 1015 } 1016 else if (!Q_stricmp(buf, "vtell")) { 1017 BotVoiceChatCommand(bs, SAY_TELL, args); 1018 } 1019 #endif 1020 else if (!Q_stricmp(buf, "scores")) 1021 { /*FIXME: parse scores?*/ } 1022 else if (!Q_stricmp(buf, "clientLevelShot")) 1023 { /*ignore*/ } 1024 } 1025 //add the delta angles to the bot's current view angles 1026 for (j = 0; j < 3; j++) { 1027 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); 1028 } 1029 //increase the local time of the bot 1030 bs->ltime += thinktime; 1031 // 1032 bs->thinktime = thinktime; 1033 //origin of the bot 1034 VectorCopy(bs->cur_ps.origin, bs->origin); 1035 //eye coordinates of the bot 1036 VectorCopy(bs->cur_ps.origin, bs->eye); 1037 bs->eye[2] += bs->cur_ps.viewheight; 1038 //get the area the bot is in 1039 bs->areanum = BotPointAreaNum(bs->origin); 1040 //the real AI 1041 BotDeathmatchAI(bs, thinktime); 1042 //set the weapon selection every AI frame 1043 trap_EA_SelectWeapon(bs->client, bs->weaponnum); 1044 //subtract the delta angles 1045 for (j = 0; j < 3; j++) { 1046 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); 1047 } 1048 //everything was ok 1049 return qtrue; 1050 } 1051 1052 /* 1053 ================== 1054 BotScheduleBotThink 1055 ================== 1056 */ 1057 void BotScheduleBotThink(void) { 1058 int i, botnum; 1059 1060 botnum = 0; 1061 1062 for( i = 0; i < MAX_CLIENTS; i++ ) { 1063 if( !botstates[i] || !botstates[i]->inuse ) { 1064 continue; 1065 } 1066 //initialize the bot think residual time 1067 botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots; 1068 botnum++; 1069 } 1070 } 1071 1072 /* 1073 ============== 1074 BotWriteSessionData 1075 ============== 1076 */ 1077 void BotWriteSessionData(bot_state_t *bs) { 1078 const char *s; 1079 const char *var; 1080 1081 s = va( 1082 "%i %i %i %i %i %i %i %i" 1083 " %f %f %f" 1084 " %f %f %f" 1085 " %f %f %f", 1086 bs->lastgoal_decisionmaker, 1087 bs->lastgoal_ltgtype, 1088 bs->lastgoal_teammate, 1089 bs->lastgoal_teamgoal.areanum, 1090 bs->lastgoal_teamgoal.entitynum, 1091 bs->lastgoal_teamgoal.flags, 1092 bs->lastgoal_teamgoal.iteminfo, 1093 bs->lastgoal_teamgoal.number, 1094 bs->lastgoal_teamgoal.origin[0], 1095 bs->lastgoal_teamgoal.origin[1], 1096 bs->lastgoal_teamgoal.origin[2], 1097 bs->lastgoal_teamgoal.mins[0], 1098 bs->lastgoal_teamgoal.mins[1], 1099 bs->lastgoal_teamgoal.mins[2], 1100 bs->lastgoal_teamgoal.maxs[0], 1101 bs->lastgoal_teamgoal.maxs[1], 1102 bs->lastgoal_teamgoal.maxs[2] 1103 ); 1104 1105 var = va( "botsession%i", bs->client ); 1106 1107 trap_Cvar_Set( var, s ); 1108 } 1109 1110 /* 1111 ============== 1112 BotReadSessionData 1113 ============== 1114 */ 1115 void BotReadSessionData(bot_state_t *bs) { 1116 char s[MAX_STRING_CHARS]; 1117 const char *var; 1118 1119 var = va( "botsession%i", bs->client ); 1120 trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); 1121 1122 sscanf(s, 1123 "%i %i %i %i %i %i %i %i" 1124 " %f %f %f" 1125 " %f %f %f" 1126 " %f %f %f", 1127 &bs->lastgoal_decisionmaker, 1128 &bs->lastgoal_ltgtype, 1129 &bs->lastgoal_teammate, 1130 &bs->lastgoal_teamgoal.areanum, 1131 &bs->lastgoal_teamgoal.entitynum, 1132 &bs->lastgoal_teamgoal.flags, 1133 &bs->lastgoal_teamgoal.iteminfo, 1134 &bs->lastgoal_teamgoal.number, 1135 &bs->lastgoal_teamgoal.origin[0], 1136 &bs->lastgoal_teamgoal.origin[1], 1137 &bs->lastgoal_teamgoal.origin[2], 1138 &bs->lastgoal_teamgoal.mins[0], 1139 &bs->lastgoal_teamgoal.mins[1], 1140 &bs->lastgoal_teamgoal.mins[2], 1141 &bs->lastgoal_teamgoal.maxs[0], 1142 &bs->lastgoal_teamgoal.maxs[1], 1143 &bs->lastgoal_teamgoal.maxs[2] 1144 ); 1145 } 1146 1147 /* 1148 ============== 1149 BotAISetupClient 1150 ============== 1151 */ 1152 int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) { 1153 char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; 1154 bot_state_t *bs; 1155 int errnum; 1156 1157 if (!botstates[client]) botstates[client] = G_Alloc(sizeof(bot_state_t)); 1158 bs = botstates[client]; 1159 1160 if (bs && bs->inuse) { 1161 BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client); 1162 return qfalse; 1163 } 1164 1165 if (!trap_AAS_Initialized()) { 1166 BotAI_Print(PRT_FATAL, "AAS not initialized\n"); 1167 return qfalse; 1168 } 1169 1170 //load the bot character 1171 bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill); 1172 if (!bs->character) { 1173 BotAI_Print(PRT_FATAL, "couldn't load skill %f from %s\n", settings->skill, settings->characterfile); 1174 return qfalse; 1175 } 1176 //copy the settings 1177 memcpy(&bs->settings, settings, sizeof(bot_settings_t)); 1178 //allocate a goal state 1179 bs->gs = trap_BotAllocGoalState(client); 1180 //load the item weights 1181 trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH); 1182 errnum = trap_BotLoadItemWeights(bs->gs, filename); 1183 if (errnum != BLERR_NOERROR) { 1184 trap_BotFreeGoalState(bs->gs); 1185 return qfalse; 1186 } 1187 //allocate a weapon state 1188 bs->ws = trap_BotAllocWeaponState(); 1189 //load the weapon weights 1190 trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH); 1191 errnum = trap_BotLoadWeaponWeights(bs->ws, filename); 1192 if (errnum != BLERR_NOERROR) { 1193 trap_BotFreeGoalState(bs->gs); 1194 trap_BotFreeWeaponState(bs->ws); 1195 return qfalse; 1196 } 1197 //allocate a chat state 1198 bs->cs = trap_BotAllocChatState(); 1199 //load the chat file 1200 trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH); 1201 trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH); 1202 errnum = trap_BotLoadChatFile(bs->cs, filename, name); 1203 if (errnum != BLERR_NOERROR) { 1204 trap_BotFreeChatState(bs->cs); 1205 trap_BotFreeGoalState(bs->gs); 1206 trap_BotFreeWeaponState(bs->ws); 1207 return qfalse; 1208 } 1209 //get the gender characteristic 1210 trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH); 1211 //set the chat gender 1212 if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); 1213 else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); 1214 else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); 1215 1216 bs->inuse = qtrue; 1217 bs->client = client; 1218 bs->entitynum = client; 1219 bs->setupcount = 4; 1220 bs->entergame_time = FloatTime(); 1221 bs->ms = trap_BotAllocMoveState(); 1222 bs->walker = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WALKER, 0, 1); 1223 numbots++; 1224 1225 if (trap_Cvar_VariableIntegerValue("bot_testichat")) { 1226 trap_BotLibVarSet("bot_testichat", "1"); 1227 BotChatTest(bs); 1228 } 1229 //NOTE: reschedule the bot thinking 1230 BotScheduleBotThink(); 1231 //if interbreeding start with a mutation 1232 if (bot_interbreed) { 1233 trap_BotMutateGoalFuzzyLogic(bs->gs, 1); 1234 } 1235 // if we kept the bot client 1236 if (restart) { 1237 BotReadSessionData(bs); 1238 } 1239 //bot has been setup succesfully 1240 return qtrue; 1241 } 1242 1243 /* 1244 ============== 1245 BotAIShutdownClient 1246 ============== 1247 */ 1248 int BotAIShutdownClient(int client, qboolean restart) { 1249 bot_state_t *bs; 1250 1251 bs = botstates[client]; 1252 if (!bs || !bs->inuse) { 1253 //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client); 1254 return qfalse; 1255 } 1256 1257 if (restart) { 1258 BotWriteSessionData(bs); 1259 } 1260 1261 if (BotChat_ExitGame(bs)) { 1262 trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL); 1263 } 1264 1265 trap_BotFreeMoveState(bs->ms); 1266 //free the goal state` 1267 trap_BotFreeGoalState(bs->gs); 1268 //free the chat file 1269 trap_BotFreeChatState(bs->cs); 1270 //free the weapon weights 1271 trap_BotFreeWeaponState(bs->ws); 1272 //free the bot character 1273 trap_BotFreeCharacter(bs->character); 1274 // 1275 BotFreeWaypoints(bs->checkpoints); 1276 BotFreeWaypoints(bs->patrolpoints); 1277 //clear activate goal stack 1278 BotClearActivateGoalStack(bs); 1279 //clear the bot state 1280 memset(bs, 0, sizeof(bot_state_t)); 1281 //set the inuse flag to qfalse 1282 bs->inuse = qfalse; 1283 //there's one bot less 1284 numbots--; 1285 //everything went ok 1286 return qtrue; 1287 } 1288 1289 /* 1290 ============== 1291 BotResetState 1292 1293 called when a bot enters the intermission or observer mode and 1294 when the level is changed 1295 ============== 1296 */ 1297 void BotResetState(bot_state_t *bs) { 1298 int client, entitynum, inuse; 1299 int movestate, goalstate, chatstate, weaponstate; 1300 bot_settings_t settings; 1301 int character; 1302 playerState_t ps; //current player state 1303 float entergame_time; 1304 1305 //save some things that should not be reset here 1306 memcpy(&settings, &bs->settings, sizeof(bot_settings_t)); 1307 memcpy(&ps, &bs->cur_ps, sizeof(playerState_t)); 1308 inuse = bs->inuse; 1309 client = bs->client; 1310 entitynum = bs->entitynum; 1311 character = bs->character; 1312 movestate = bs->ms; 1313 goalstate = bs->gs; 1314 chatstate = bs->cs; 1315 weaponstate = bs->ws; 1316 entergame_time = bs->entergame_time; 1317 //free checkpoints and patrol points 1318 BotFreeWaypoints(bs->checkpoints); 1319 BotFreeWaypoints(bs->patrolpoints); 1320 //reset the whole state 1321 memset(bs, 0, sizeof(bot_state_t)); 1322 //copy back some state stuff that should not be reset 1323 bs->ms = movestate; 1324 bs->gs = goalstate; 1325 bs->cs = chatstate; 1326 bs->ws = weaponstate; 1327 memcpy(&bs->cur_ps, &ps, sizeof(playerState_t)); 1328 memcpy(&bs->settings, &settings, sizeof(bot_settings_t)); 1329 bs->inuse = inuse; 1330 bs->client = client; 1331 bs->entitynum = entitynum; 1332 bs->character = character; 1333 bs->entergame_time = entergame_time; 1334 //reset several states 1335 if (bs->ms) trap_BotResetMoveState(bs->ms); 1336 if (bs->gs) trap_BotResetGoalState(bs->gs); 1337 if (bs->ws) trap_BotResetWeaponState(bs->ws); 1338 if (bs->gs) trap_BotResetAvoidGoals(bs->gs); 1339 if (bs->ms) trap_BotResetAvoidReach(bs->ms); 1340 } 1341 1342 /* 1343 ============== 1344 BotAILoadMap 1345 ============== 1346 */ 1347 int BotAILoadMap( int restart ) { 1348 int i; 1349 vmCvar_t mapname; 1350 1351 if (!restart) { 1352 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); 1353 trap_BotLibLoadMap( mapname.string ); 1354 } 1355 1356 for (i = 0; i < MAX_CLIENTS; i++) { 1357 if (botstates[i] && botstates[i]->inuse) { 1358 BotResetState( botstates[i] ); 1359 botstates[i]->setupcount = 4; 1360 } 1361 } 1362 1363 BotSetupDeathmatchAI(); 1364 1365 return qtrue; 1366 } 1367 1368 #ifdef MISSIONPACK 1369 void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ); 1370 #endif 1371 1372 /* 1373 ================== 1374 BotAIStartFrame 1375 ================== 1376 */ 1377 int BotAIStartFrame(int time) { 1378 int i; 1379 gentity_t *ent; 1380 bot_entitystate_t state; 1381 int elapsed_time, thinktime; 1382 static int local_time; 1383 static int botlib_residual; 1384 static int lastbotthink_time; 1385 1386 G_CheckBotSpawn(); 1387 1388 trap_Cvar_Update(&bot_rocketjump); 1389 trap_Cvar_Update(&bot_grapple); 1390 trap_Cvar_Update(&bot_fastchat); 1391 trap_Cvar_Update(&bot_nochat); 1392 trap_Cvar_Update(&bot_testrchat); 1393 trap_Cvar_Update(&bot_thinktime); 1394 trap_Cvar_Update(&bot_memorydump); 1395 trap_Cvar_Update(&bot_saveroutingcache); 1396 trap_Cvar_Update(&bot_pause); 1397 trap_Cvar_Update(&bot_report); 1398 1399 if (bot_report.integer) { 1400 // BotTeamplayReport(); 1401 // trap_Cvar_Set("bot_report", "0"); 1402 BotUpdateInfoConfigStrings(); 1403 } 1404 1405 if (bot_pause.integer) { 1406 // execute bot user commands every frame 1407 for( i = 0; i < MAX_CLIENTS; i++ ) { 1408 if( !botstates[i] || !botstates[i]->inuse ) { 1409 continue; 1410 } 1411 if( g_entities[i].client->pers.connected != CON_CONNECTED ) { 1412 continue; 1413 } 1414 botstates[i]->lastucmd.forwardmove = 0; 1415 botstates[i]->lastucmd.rightmove = 0; 1416 botstates[i]->lastucmd.upmove = 0; 1417 botstates[i]->lastucmd.buttons = 0; 1418 botstates[i]->lastucmd.serverTime = time; 1419 trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); 1420 } 1421 return qtrue; 1422 } 1423 1424 if (bot_memorydump.integer) { 1425 trap_BotLibVarSet("memorydump", "1"); 1426 trap_Cvar_Set("bot_memorydump", "0"); 1427 } 1428 if (bot_saveroutingcache.integer) { 1429 trap_BotLibVarSet("saveroutingcache", "1"); 1430 trap_Cvar_Set("bot_saveroutingcache", "0"); 1431 } 1432 //check if bot interbreeding is activated 1433 BotInterbreeding(); 1434 //cap the bot think time 1435 if (bot_thinktime.integer > 200) { 1436 trap_Cvar_Set("bot_thinktime", "200"); 1437 } 1438 //if the bot think time changed we should reschedule the bots 1439 if (bot_thinktime.integer != lastbotthink_time) { 1440 lastbotthink_time = bot_thinktime.integer; 1441 BotScheduleBotThink(); 1442 } 1443 1444 elapsed_time = time - local_time; 1445 local_time = time; 1446 1447 botlib_residual += elapsed_time; 1448 1449 if (elapsed_time > bot_thinktime.integer) thinktime = elapsed_time; 1450 else thinktime = bot_thinktime.integer; 1451 1452 // update the bot library 1453 if ( botlib_residual >= thinktime ) { 1454 botlib_residual -= thinktime; 1455 1456 trap_BotLibStartFrame((float) time / 1000); 1457 1458 if (!trap_AAS_Initialized()) return qfalse; 1459 1460 //update entities in the botlib 1461 for (i = 0; i < MAX_GENTITIES; i++) { 1462 ent = &g_entities[i]; 1463 if (!ent->inuse) { 1464 trap_BotLibUpdateEntity(i, NULL); 1465 continue; 1466 } 1467 if (!ent->r.linked) { 1468 trap_BotLibUpdateEntity(i, NULL); 1469 continue; 1470 } 1471 if (ent->r.svFlags & SVF_NOCLIENT) { 1472 trap_BotLibUpdateEntity(i, NULL); 1473 continue; 1474 } 1475 // do not update missiles 1476 if (ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK) { 1477 trap_BotLibUpdateEntity(i, NULL); 1478 continue; 1479 } 1480 // do not update event only entities 1481 if (ent->s.eType > ET_EVENTS) { 1482 trap_BotLibUpdateEntity(i, NULL); 1483 continue; 1484 } 1485 #ifdef MISSIONPACK 1486 // never link prox mine triggers 1487 if (ent->r.contents == CONTENTS_TRIGGER) { 1488 if (ent->touch == ProximityMine_Trigger) { 1489 trap_BotLibUpdateEntity(i, NULL); 1490 continue; 1491 } 1492 } 1493 #endif 1494 // 1495 memset(&state, 0, sizeof(bot_entitystate_t)); 1496 // 1497 VectorCopy(ent->r.currentOrigin, state.origin); 1498 if (i < MAX_CLIENTS) { 1499 VectorCopy(ent->s.apos.trBase, state.angles); 1500 } else { 1501 VectorCopy(ent->r.currentAngles, state.angles); 1502 } 1503 VectorCopy(ent->s.origin2, state.old_origin); 1504 VectorCopy(ent->r.mins, state.mins); 1505 VectorCopy(ent->r.maxs, state.maxs); 1506 state.type = ent->s.eType; 1507 state.flags = ent->s.eFlags; 1508 if (ent->r.bmodel) state.solid = SOLID_BSP; 1509 else state.solid = SOLID_BBOX; 1510 state.groundent = ent->s.groundEntityNum; 1511 state.modelindex = ent->s.modelindex; 1512 state.modelindex2 = ent->s.modelindex2; 1513 state.frame = ent->s.frame; 1514 state.event = ent->s.event; 1515 state.eventParm = ent->s.eventParm; 1516 state.powerups = ent->s.powerups; 1517 state.legsAnim = ent->s.legsAnim; 1518 state.torsoAnim = ent->s.torsoAnim; 1519 state.weapon = ent->s.weapon; 1520 // 1521 trap_BotLibUpdateEntity(i, &state); 1522 } 1523 1524 BotAIRegularUpdate(); 1525 } 1526 1527 floattime = trap_AAS_Time(); 1528 1529 // execute scheduled bot AI 1530 for( i = 0; i < MAX_CLIENTS; i++ ) { 1531 if( !botstates[i] || !botstates[i]->inuse ) { 1532 continue; 1533 } 1534 // 1535 botstates[i]->botthink_residual += elapsed_time; 1536 // 1537 if ( botstates[i]->botthink_residual >= thinktime ) { 1538 botstates[i]->botthink_residual -= thinktime; 1539 1540 if (!trap_AAS_Initialized()) return qfalse; 1541 1542 if (g_entities[i].client->pers.connected == CON_CONNECTED) { 1543 BotAI(i, (float) thinktime / 1000); 1544 } 1545 } 1546 } 1547 1548 1549 // execute bot user commands every frame 1550 for( i = 0; i < MAX_CLIENTS; i++ ) { 1551 if( !botstates[i] || !botstates[i]->inuse ) { 1552 continue; 1553 } 1554 if( g_entities[i].client->pers.connected != CON_CONNECTED ) { 1555 continue; 1556 } 1557 1558 BotUpdateInput(botstates[i], time, elapsed_time); 1559 trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); 1560 } 1561 1562 return qtrue; 1563 } 1564 1565 /* 1566 ============== 1567 BotInitLibrary 1568 ============== 1569 */ 1570 int BotInitLibrary(void) { 1571 char buf[144]; 1572 1573 //set the maxclients and maxentities library variables before calling BotSetupLibrary 1574 trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf)); 1575 if (!strlen(buf)) strcpy(buf, "8"); 1576 trap_BotLibVarSet("maxclients", buf); 1577 Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES); 1578 trap_BotLibVarSet("maxentities", buf); 1579 //bsp checksum 1580 trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf)); 1581 if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf); 1582 //maximum number of aas links 1583 trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf)); 1584 if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf); 1585 //maximum number of items in a level 1586 trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf)); 1587 if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf); 1588 //game type 1589 trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf)); 1590 if (!strlen(buf)) strcpy(buf, "0"); 1591 trap_BotLibVarSet("g_gametype", buf); 1592 //bot developer mode and log file 1593 trap_BotLibVarSet("bot_developer", bot_developer.string); 1594 trap_BotLibVarSet("log", buf); 1595 //no chatting 1596 trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf)); 1597 if (strlen(buf)) trap_BotLibVarSet("nochat", "0"); 1598 //visualize jump pads 1599 trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf)); 1600 if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf); 1601 //forced clustering calculations 1602 trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf)); 1603 if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf); 1604 //forced reachability calculations 1605 trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf)); 1606 if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf); 1607 //force writing of AAS to file 1608 trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf)); 1609 if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf); 1610 //no AAS optimization 1611 trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf)); 1612 if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf); 1613 // 1614 trap_Cvar_VariableStringBuffer("bot_saveroutingcache", buf, sizeof(buf)); 1615 if (strlen(buf)) trap_BotLibVarSet("saveroutingcache", buf); 1616 //reload instead of cache bot character files 1617 trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf)); 1618 if (!strlen(buf)) strcpy(buf, "0"); 1619 trap_BotLibVarSet("bot_reloadcharacters", buf); 1620 //base directory 1621 trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf)); 1622 if (strlen(buf)) trap_BotLibVarSet("basedir", buf); 1623 //game directory 1624 trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf)); 1625 if (strlen(buf)) trap_BotLibVarSet("gamedir", buf); 1626 //cd directory 1627 trap_Cvar_VariableStringBuffer("fs_cdpath", buf, sizeof(buf)); 1628 if (strlen(buf)) trap_BotLibVarSet("cddir", buf); 1629 // 1630 #ifdef MISSIONPACK 1631 trap_BotLibDefine("MISSIONPACK"); 1632 #endif 1633 //setup the bot library 1634 return trap_BotLibSetup(); 1635 } 1636 1637 /* 1638 ============== 1639 BotAISetup 1640 ============== 1641 */ 1642 int BotAISetup( int restart ) { 1643 int errnum; 1644 1645 trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT); 1646 trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT); 1647 trap_Cvar_Register(&bot_saveroutingcache, "bot_saveroutingcache", "0", CVAR_CHEAT); 1648 trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT); 1649 trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT); 1650 trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT); 1651 trap_Cvar_Register(&bot_testclusters, "bot_testclusters", "0", CVAR_CHEAT); 1652 trap_Cvar_Register(&bot_developer, "bot_developer", "0", CVAR_CHEAT); 1653 trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0); 1654 trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0); 1655 trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0); 1656 trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0); 1657 1658 //if the game is restarted for a tournament 1659 if (restart) { 1660 return qtrue; 1661 } 1662 1663 //initialize the bot states 1664 memset( botstates, 0, sizeof(botstates) ); 1665 1666 errnum = BotInitLibrary(); 1667 if (errnum != BLERR_NOERROR) return qfalse; 1668 return qtrue; 1669 } 1670 1671 /* 1672 ============== 1673 BotAIShutdown 1674 ============== 1675 */ 1676 int BotAIShutdown( int restart ) { 1677 1678 int i; 1679 1680 //if the game is restarted for a tournament 1681 if ( restart ) { 1682 //shutdown all the bots in the botlib 1683 for (i = 0; i < MAX_CLIENTS; i++) { 1684 if (botstates[i] && botstates[i]->inuse) { 1685 BotAIShutdownClient(botstates[i]->client, restart); 1686 } 1687 } 1688 //don't shutdown the bot library 1689 } 1690 else { 1691 trap_BotLibShutdown(); 1692 } 1693 return qtrue; 1694 } 1695