00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "g_local.h"
00035 #include "q_shared.h"
00036 #include "botlib.h"
00037 #include "be_aas.h"
00038 #include "be_ea.h"
00039 #include "be_ai_char.h"
00040 #include "be_ai_chat.h"
00041 #include "be_ai_gen.h"
00042 #include "be_ai_goal.h"
00043 #include "be_ai_move.h"
00044 #include "be_ai_weap.h"
00045
00046 #include "ai_main.h"
00047 #include "ai_dmq3.h"
00048 #include "ai_chat.h"
00049 #include "ai_cmd.h"
00050 #include "ai_dmnet.h"
00051 #include "ai_vcmd.h"
00052
00053
00054 #include "chars.h"
00055 #include "inv.h"
00056 #include "syn.h"
00057
00058 #define MAX_PATH 144
00059
00060
00061
00062 bot_state_t *botstates[MAX_CLIENTS];
00063
00064 int numbots;
00065
00066 float floattime;
00067
00068 float regularupdate_time;
00069
00070 int bot_interbreed;
00071 int bot_interbreedmatchcount;
00072
00073 vmCvar_t bot_thinktime;
00074 vmCvar_t bot_memorydump;
00075 vmCvar_t bot_saveroutingcache;
00076 vmCvar_t bot_pause;
00077 vmCvar_t bot_report;
00078 vmCvar_t bot_testsolid;
00079 vmCvar_t bot_testclusters;
00080 vmCvar_t bot_developer;
00081 vmCvar_t bot_interbreedchar;
00082 vmCvar_t bot_interbreedbots;
00083 vmCvar_t bot_interbreedcycle;
00084 vmCvar_t bot_interbreedwrite;
00085
00086
00087 void ExitLevel( void );
00088
00089
00090
00091
00092
00093
00094
00095 void QDECL BotAI_Print(int type, char *fmt, ...) {
00096 char str[2048];
00097 va_list ap;
00098
00099 va_start(ap, fmt);
00100 vsprintf(str, fmt, ap);
00101 va_end(ap);
00102
00103 switch(type) {
00104 case PRT_MESSAGE: {
00105 G_Printf("%s", str);
00106 break;
00107 }
00108 case PRT_WARNING: {
00109 G_Printf( S_COLOR_YELLOW "Warning: %s", str );
00110 break;
00111 }
00112 case PRT_ERROR: {
00113 G_Printf( S_COLOR_RED "Error: %s", str );
00114 break;
00115 }
00116 case PRT_FATAL: {
00117 G_Printf( S_COLOR_RED "Fatal: %s", str );
00118 break;
00119 }
00120 case PRT_EXIT: {
00121 G_Error( S_COLOR_RED "Exit: %s", str );
00122 break;
00123 }
00124 default: {
00125 G_Printf( "unknown print type\n" );
00126 break;
00127 }
00128 }
00129 }
00130
00131
00132
00133
00134
00135
00136
00137 void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) {
00138 trace_t trace;
00139
00140 trap_Trace(&trace, start, mins, maxs, end, passent, contentmask);
00141
00142 bsptrace->allsolid = trace.allsolid;
00143 bsptrace->startsolid = trace.startsolid;
00144 bsptrace->fraction = trace.fraction;
00145 VectorCopy(trace.endpos, bsptrace->endpos);
00146 bsptrace->plane.dist = trace.plane.dist;
00147 VectorCopy(trace.plane.normal, bsptrace->plane.normal);
00148 bsptrace->plane.signbits = trace.plane.signbits;
00149 bsptrace->plane.type = trace.plane.type;
00150 bsptrace->surface.value = trace.surfaceFlags;
00151 bsptrace->ent = trace.entityNum;
00152 bsptrace->exp_dist = 0;
00153 bsptrace->sidenum = 0;
00154 bsptrace->contents = 0;
00155 }
00156
00157
00158
00159
00160
00161
00162 int BotAI_GetClientState( int clientNum, playerState_t *state ) {
00163 gentity_t *ent;
00164
00165 ent = &g_entities[clientNum];
00166 if ( !ent->inuse ) {
00167 return qfalse;
00168 }
00169 if ( !ent->client ) {
00170 return qfalse;
00171 }
00172
00173 memcpy( state, &ent->client->ps, sizeof(playerState_t) );
00174 return qtrue;
00175 }
00176
00177
00178
00179
00180
00181
00182 int BotAI_GetEntityState( int entityNum, entityState_t *state ) {
00183 gentity_t *ent;
00184
00185 ent = &g_entities[entityNum];
00186 memset( state, 0, sizeof(entityState_t) );
00187 if (!ent->inuse) return qfalse;
00188 if (!ent->r.linked) return qfalse;
00189 if (ent->r.svFlags & SVF_NOCLIENT) return qfalse;
00190 memcpy( state, &ent->s, sizeof(entityState_t) );
00191 return qtrue;
00192 }
00193
00194
00195
00196
00197
00198
00199 int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) {
00200 int entNum;
00201
00202 entNum = trap_BotGetSnapshotEntity( clientNum, sequence );
00203 if ( entNum == -1 ) {
00204 memset(state, 0, sizeof(entityState_t));
00205 return -1;
00206 }
00207
00208 BotAI_GetEntityState( entNum, state );
00209
00210 return sequence + 1;
00211 }
00212
00213
00214
00215
00216
00217
00218 void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) {
00219 int i, mcontext;
00220 va_list ap;
00221 char *p;
00222 char *vars[MAX_MATCHVARIABLES];
00223
00224 memset(vars, 0, sizeof(vars));
00225 va_start(ap, type);
00226 p = va_arg(ap, char *);
00227 for (i = 0; i < MAX_MATCHVARIABLES; i++) {
00228 if( !p ) {
00229 break;
00230 }
00231 vars[i] = p;
00232 p = va_arg(ap, char *);
00233 }
00234 va_end(ap);
00235
00236 mcontext = BotSynonymContext(bs);
00237
00238 trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] );
00239 }
00240
00241
00242
00243
00244
00245
00246
00247 void BotTestAAS(vec3_t origin) {
00248 int areanum;
00249 aas_areainfo_t info;
00250
00251 trap_Cvar_Update(&bot_testsolid);
00252 trap_Cvar_Update(&bot_testclusters);
00253 if (bot_testsolid.integer) {
00254 if (!trap_AAS_Initialized()) return;
00255 areanum = BotPointAreaNum(origin);
00256 if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area");
00257 else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area");
00258 }
00259 else if (bot_testclusters.integer) {
00260 if (!trap_AAS_Initialized()) return;
00261 areanum = BotPointAreaNum(origin);
00262 if (!areanum)
00263 BotAI_Print(PRT_MESSAGE, "\r^1Solid! ");
00264 else {
00265 trap_AAS_AreaInfo(areanum, &info);
00266 BotAI_Print(PRT_MESSAGE, "\rarea %d, cluster %d ", areanum, info.cluster);
00267 }
00268 }
00269 }
00270
00271
00272
00273
00274
00275
00276 void BotReportStatus(bot_state_t *bs) {
00277 char goalname[MAX_MESSAGE_SIZE];
00278 char netname[MAX_MESSAGE_SIZE];
00279 char *leader, flagstatus[32];
00280
00281 ClientName(bs->client, netname, sizeof(netname));
00282 if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L";
00283 else leader = " ";
00284
00285 strcpy(flagstatus, " ");
00286 if (gametype == GT_CTF) {
00287 if (BotCTFCarryingFlag(bs)) {
00288 if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F ");
00289 else strcpy(flagstatus, S_COLOR_BLUE"F ");
00290 }
00291 }
00292 #ifdef MISSIONPACK
00293 else if (gametype == GT_1FCTF) {
00294 if (Bot1FCTFCarryingFlag(bs)) {
00295 if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F ");
00296 else strcpy(flagstatus, S_COLOR_BLUE"F ");
00297 }
00298 }
00299 else if (gametype == GT_HARVESTER) {
00300 if (BotHarvesterCarryingCubes(bs)) {
00301 if (BotTeam(bs) == TEAM_RED) Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_RED"%2d", bs->inventory[INVENTORY_REDCUBE]);
00302 else Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_BLUE"%2d", bs->inventory[INVENTORY_BLUECUBE]);
00303 }
00304 }
00305 #endif
00306
00307 switch(bs->ltgtype) {
00308 case LTG_TEAMHELP:
00309 {
00310 EasyClientName(bs->teammate, goalname, sizeof(goalname));
00311 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname);
00312 break;
00313 }
00314 case LTG_TEAMACCOMPANY:
00315 {
00316 EasyClientName(bs->teammate, goalname, sizeof(goalname));
00317 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname);
00318 break;
00319 }
00320 case LTG_DEFENDKEYAREA:
00321 {
00322 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
00323 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname);
00324 break;
00325 }
00326 case LTG_GETITEM:
00327 {
00328 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
00329 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname);
00330 break;
00331 }
00332 case LTG_KILL:
00333 {
00334 ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname));
00335 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname);
00336 break;
00337 }
00338 case LTG_CAMP:
00339 case LTG_CAMPORDER:
00340 {
00341 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus);
00342 break;
00343 }
00344 case LTG_PATROL:
00345 {
00346 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus);
00347 break;
00348 }
00349 case LTG_GETFLAG:
00350 {
00351 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus);
00352 break;
00353 }
00354 case LTG_RUSHBASE:
00355 {
00356 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus);
00357 break;
00358 }
00359 case LTG_RETURNFLAG:
00360 {
00361 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus);
00362 break;
00363 }
00364 case LTG_ATTACKENEMYBASE:
00365 {
00366 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: attacking the enemy base\n", netname, leader, flagstatus);
00367 break;
00368 }
00369 case LTG_HARVEST:
00370 {
00371 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: harvesting\n", netname, leader, flagstatus);
00372 break;
00373 }
00374 default:
00375 {
00376 BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus);
00377 break;
00378 }
00379 }
00380 }
00381
00382
00383
00384
00385
00386
00387 void BotTeamplayReport(void) {
00388 int i;
00389 char buf[MAX_INFO_STRING];
00390
00391 BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n");
00392 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
00393
00394 if ( !botstates[i] || !botstates[i]->inuse ) continue;
00395
00396 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
00397
00398 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
00399
00400 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) {
00401 BotReportStatus(botstates[i]);
00402 }
00403 }
00404 BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n");
00405 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
00406
00407 if ( !botstates[i] || !botstates[i]->inuse ) continue;
00408
00409 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
00410
00411 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
00412
00413 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) {
00414 BotReportStatus(botstates[i]);
00415 }
00416 }
00417 }
00418
00419
00420
00421
00422
00423
00424 void BotSetInfoConfigString(bot_state_t *bs) {
00425 char goalname[MAX_MESSAGE_SIZE];
00426 char netname[MAX_MESSAGE_SIZE];
00427 char action[MAX_MESSAGE_SIZE];
00428 char *leader, carrying[32], *cs;
00429 bot_goal_t goal;
00430
00431 ClientName(bs->client, netname, sizeof(netname));
00432 if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L";
00433 else leader = " ";
00434
00435 strcpy(carrying, " ");
00436 if (gametype == GT_CTF) {
00437 if (BotCTFCarryingFlag(bs)) {
00438 strcpy(carrying, "F ");
00439 }
00440 }
00441 #ifdef MISSIONPACK
00442 else if (gametype == GT_1FCTF) {
00443 if (Bot1FCTFCarryingFlag(bs)) {
00444 strcpy(carrying, "F ");
00445 }
00446 }
00447 else if (gametype == GT_HARVESTER) {
00448 if (BotHarvesterCarryingCubes(bs)) {
00449 if (BotTeam(bs) == TEAM_RED) Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_REDCUBE]);
00450 else Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_BLUECUBE]);
00451 }
00452 }
00453 #endif
00454
00455 switch(bs->ltgtype) {
00456 case LTG_TEAMHELP:
00457 {
00458 EasyClientName(bs->teammate, goalname, sizeof(goalname));
00459 Com_sprintf(action, sizeof(action), "helping %s", goalname);
00460 break;
00461 }
00462 case LTG_TEAMACCOMPANY:
00463 {
00464 EasyClientName(bs->teammate, goalname, sizeof(goalname));
00465 Com_sprintf(action, sizeof(action), "accompanying %s", goalname);
00466 break;
00467 }
00468 case LTG_DEFENDKEYAREA:
00469 {
00470 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
00471 Com_sprintf(action, sizeof(action), "defending %s", goalname);
00472 break;
00473 }
00474 case LTG_GETITEM:
00475 {
00476 trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
00477 Com_sprintf(action, sizeof(action), "getting item %s", goalname);
00478 break;
00479 }
00480 case LTG_KILL:
00481 {
00482 ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname));
00483 Com_sprintf(action, sizeof(action), "killing %s", goalname);
00484 break;
00485 }
00486 case LTG_CAMP:
00487 case LTG_CAMPORDER:
00488 {
00489 Com_sprintf(action, sizeof(action), "camping");
00490 break;
00491 }
00492 case LTG_PATROL:
00493 {
00494 Com_sprintf(action, sizeof(action), "patrolling");
00495 break;
00496 }
00497 case LTG_GETFLAG:
00498 {
00499 Com_sprintf(action, sizeof(action), "capturing flag");
00500 break;
00501 }
00502 case LTG_RUSHBASE:
00503 {
00504 Com_sprintf(action, sizeof(action), "rushing base");
00505 break;
00506 }
00507 case LTG_RETURNFLAG:
00508 {
00509 Com_sprintf(action, sizeof(action), "returning flag");
00510 break;
00511 }
00512 case LTG_ATTACKENEMYBASE:
00513 {
00514 Com_sprintf(action, sizeof(action), "attacking the enemy base");
00515 break;
00516 }
00517 case LTG_HARVEST:
00518 {
00519 Com_sprintf(action, sizeof(action), "harvesting");
00520 break;
00521 }
00522 default:
00523 {
00524 trap_BotGetTopGoal(bs->gs, &goal);
00525 trap_BotGoalName(goal.number, goalname, sizeof(goalname));
00526 Com_sprintf(action, sizeof(action), "roaming %s", goalname);
00527 break;
00528 }
00529 }
00530 cs = va("l\\%s\\c\\%s\\a\\%s",
00531 leader,
00532 carrying,
00533 action);
00534 trap_SetConfigstring (CS_BOTINFO + bs->client, cs);
00535 }
00536
00537
00538
00539
00540
00541
00542 void BotUpdateInfoConfigStrings(void) {
00543 int i;
00544 char buf[MAX_INFO_STRING];
00545
00546 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
00547
00548 if ( !botstates[i] || !botstates[i]->inuse )
00549 continue;
00550
00551 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
00552
00553 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n")))
00554 continue;
00555 BotSetInfoConfigString(botstates[i]);
00556 }
00557 }
00558
00559
00560
00561
00562
00563
00564 void BotInterbreedBots(void) {
00565 float ranks[MAX_CLIENTS];
00566 int parent1, parent2, child;
00567 int i;
00568
00569
00570 for (i = 0; i < MAX_CLIENTS; i++) {
00571 if ( botstates[i] && botstates[i]->inuse ) {
00572 ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
00573 }
00574 else {
00575 ranks[i] = -1;
00576 }
00577 }
00578
00579 if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, ranks, &parent1, &parent2, &child)) {
00580 trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs);
00581 trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1);
00582 }
00583
00584 for (i = 0; i < MAX_CLIENTS; i++) {
00585 if (botstates[i] && botstates[i]->inuse) {
00586 botstates[i]->num_kills = 0;
00587 botstates[i]->num_deaths = 0;
00588 }
00589 }
00590 }
00591
00592
00593
00594
00595
00596
00597 void BotWriteInterbreeded(char *filename) {
00598 float rank, bestrank;
00599 int i, bestbot;
00600
00601 bestrank = 0;
00602 bestbot = -1;
00603
00604 for (i = 0; i < MAX_CLIENTS; i++) {
00605 if ( botstates[i] && botstates[i]->inuse ) {
00606 rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
00607 }
00608 else {
00609 rank = -1;
00610 }
00611 if (rank > bestrank) {
00612 bestrank = rank;
00613 bestbot = i;
00614 }
00615 }
00616 if (bestbot >= 0) {
00617
00618 trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename);
00619 }
00620 }
00621
00622
00623
00624
00625
00626
00627
00628
00629 void BotInterbreedEndMatch(void) {
00630
00631 if (!bot_interbreed) return;
00632 bot_interbreedmatchcount++;
00633 if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) {
00634 bot_interbreedmatchcount = 0;
00635
00636 trap_Cvar_Update(&bot_interbreedwrite);
00637 if (strlen(bot_interbreedwrite.string)) {
00638 BotWriteInterbreeded(bot_interbreedwrite.string);
00639 trap_Cvar_Set("bot_interbreedwrite", "");
00640 }
00641 BotInterbreedBots();
00642 }
00643 }
00644
00645
00646
00647
00648
00649
00650 void BotInterbreeding(void) {
00651 int i;
00652
00653 trap_Cvar_Update(&bot_interbreedchar);
00654 if (!strlen(bot_interbreedchar.string)) return;
00655
00656 if (gametype != GT_TOURNAMENT) {
00657 trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT));
00658 ExitLevel();
00659 return;
00660 }
00661
00662 for (i = 0; i < MAX_CLIENTS; i++) {
00663 if (botstates[i] && botstates[i]->inuse) {
00664 BotAIShutdownClient(botstates[i]->client, qfalse);
00665 }
00666 }
00667
00668 trap_BotLibVarSet("bot_reloadcharacters", "1");
00669
00670 for (i = 0; i < bot_interbreedbots.integer; i++) {
00671 trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s 4 free %i %s%d\n",
00672 bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i) );
00673 }
00674
00675 trap_Cvar_Set("bot_interbreedchar", "");
00676 bot_interbreed = qtrue;
00677 }
00678
00679
00680
00681
00682
00683
00684 void BotEntityInfo(int entnum, aas_entityinfo_t *info) {
00685 trap_AAS_EntityInfo(entnum, info);
00686 }
00687
00688
00689
00690
00691
00692
00693 int NumBots(void) {
00694 return numbots;
00695 }
00696
00697
00698
00699
00700
00701
00702 int BotTeamLeader(bot_state_t *bs) {
00703 int leader;
00704
00705 leader = ClientFromName(bs->teamleader);
00706 if (leader < 0) return qfalse;
00707 if (!botstates[leader] || !botstates[leader]->inuse) return qfalse;
00708 return qtrue;
00709 }
00710
00711
00712
00713
00714
00715
00716 float AngleDifference(float ang1, float ang2) {
00717 float diff;
00718
00719 diff = ang1 - ang2;
00720 if (ang1 > ang2) {
00721 if (diff > 180.0) diff -= 360.0;
00722 }
00723 else {
00724 if (diff < -180.0) diff += 360.0;
00725 }
00726 return diff;
00727 }
00728
00729
00730
00731
00732
00733
00734 float BotChangeViewAngle(float angle, float ideal_angle, float speed) {
00735 float move;
00736
00737 angle = AngleMod(angle);
00738 ideal_angle = AngleMod(ideal_angle);
00739 if (angle == ideal_angle) return angle;
00740 move = ideal_angle - angle;
00741 if (ideal_angle > angle) {
00742 if (move > 180.0) move -= 360.0;
00743 }
00744 else {
00745 if (move < -180.0) move += 360.0;
00746 }
00747 if (move > 0) {
00748 if (move > speed) move = speed;
00749 }
00750 else {
00751 if (move < -speed) move = -speed;
00752 }
00753 return AngleMod(angle + move);
00754 }
00755
00756
00757
00758
00759
00760
00761 void BotChangeViewAngles(bot_state_t *bs, float thinktime) {
00762 float diff, factor, maxchange, anglespeed, disired_speed;
00763 int i;
00764
00765 if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360;
00766
00767 if (bs->enemy >= 0) {
00768 factor = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1);
00769 maxchange = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800);
00770 }
00771 else {
00772 factor = 0.05f;
00773 maxchange = 360;
00774 }
00775 if (maxchange < 240) maxchange = 240;
00776 maxchange *= thinktime;
00777 for (i = 0; i < 2; i++) {
00778
00779 if (bot_challenge.integer) {
00780
00781 diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]));
00782 anglespeed = diff * factor;
00783 if (anglespeed > maxchange) anglespeed = maxchange;
00784 bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i],
00785 bs->ideal_viewangles[i], anglespeed);
00786 }
00787 else {
00788
00789 bs->viewangles[i] = AngleMod(bs->viewangles[i]);
00790 bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]);
00791 diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]);
00792 disired_speed = diff * factor;
00793 bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed);
00794 if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange;
00795 if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange;
00796 anglespeed = bs->viewanglespeed[i];
00797 if (anglespeed > maxchange) anglespeed = maxchange;
00798 if (anglespeed < -maxchange) anglespeed = -maxchange;
00799 bs->viewangles[i] += anglespeed;
00800 bs->viewangles[i] = AngleMod(bs->viewangles[i]);
00801
00802 bs->viewanglespeed[i] *= 0.45 * (1 - factor);
00803 }
00804
00805
00806 }
00807
00808 if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360;
00809
00810 trap_EA_View(bs->client, bs->viewangles);
00811 }
00812
00813
00814
00815
00816
00817
00818 void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time) {
00819 vec3_t angles, forward, right;
00820 short temp;
00821 int j;
00822
00823
00824 memset(ucmd, 0, sizeof(usercmd_t));
00825
00826
00827
00828 ucmd->serverTime = time;
00829
00830 if (bi->actionflags & ACTION_DELAYEDJUMP) {
00831 bi->actionflags |= ACTION_JUMP;
00832 bi->actionflags &= ~ACTION_DELAYEDJUMP;
00833 }
00834
00835 if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK;
00836 if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK;
00837 if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK;
00838 if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE;
00839 if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE;
00840 if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING;
00841 if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE;
00842 if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE;
00843 if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG;
00844 if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE;
00845 if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL;
00846 if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME;
00847
00848 ucmd->weapon = bi->weapon;
00849
00850
00851 ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]);
00852 ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]);
00853 ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]);
00854
00855 for (j = 0; j < 3; j++) {
00856 temp = ucmd->angles[j] - delta_angles[j];
00857
00858
00859
00860
00861
00862
00863
00864 ucmd->angles[j] = temp;
00865 }
00866
00867
00868
00869 if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH];
00870 else angles[PITCH] = 0;
00871 angles[YAW] = bi->viewangles[YAW];
00872 angles[ROLL] = 0;
00873 AngleVectors(angles, forward, right, NULL);
00874
00875 bi->speed = bi->speed * 127 / 400;
00876
00877 ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed;
00878 ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed;
00879 ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed;
00880
00881 if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127;
00882 if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127;
00883 if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127;
00884 if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127;
00885
00886 if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127;
00887
00888 if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127;
00889
00890
00891
00892 }
00893
00894
00895
00896
00897
00898
00899 void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) {
00900 bot_input_t bi;
00901 int j;
00902
00903
00904 for (j = 0; j < 3; j++) {
00905 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00906 }
00907
00908 BotChangeViewAngles(bs, (float) elapsed_time / 1000);
00909
00910 trap_EA_GetInput(bs->client, (float) time / 1000, &bi);
00911
00912 if (bi.actionflags & ACTION_RESPAWN) {
00913 if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK);
00914 }
00915
00916 BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time);
00917
00918 for (j = 0; j < 3; j++) {
00919 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00920 }
00921 }
00922
00923
00924
00925
00926
00927
00928 void BotAIRegularUpdate(void) {
00929 if (regularupdate_time < FloatTime()) {
00930 trap_BotUpdateEntityItems();
00931 regularupdate_time = FloatTime() + 0.3;
00932 }
00933 }
00934
00935
00936
00937
00938
00939
00940 void RemoveColorEscapeSequences( char *text ) {
00941 int i, l;
00942
00943 l = 0;
00944 for ( i = 0; text[i]; i++ ) {
00945 if (Q_IsColorString(&text[i])) {
00946 i++;
00947 continue;
00948 }
00949 if (text[i] > 0x7E)
00950 continue;
00951 text[l++] = text[i];
00952 }
00953 text[l] = '\0';
00954 }
00955
00956
00957
00958
00959
00960
00961 int BotAI(int client, float thinktime) {
00962 bot_state_t *bs;
00963 char buf[1024], *args;
00964 int j;
00965
00966 trap_EA_ResetInput(client);
00967
00968 bs = botstates[client];
00969 if (!bs || !bs->inuse) {
00970 BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client);
00971 return qfalse;
00972 }
00973
00974
00975 BotAI_GetClientState( client, &bs->cur_ps );
00976
00977
00978 while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) {
00979
00980 args = strchr( buf, ' ');
00981 if (!args) continue;
00982 *args++ = '\0';
00983
00984
00985 RemoveColorEscapeSequences( args );
00986
00987 if (!Q_stricmp(buf, "cp "))
00988 { }
00989 else if (!Q_stricmp(buf, "cs"))
00990 { }
00991 else if (!Q_stricmp(buf, "print")) {
00992
00993 memmove(args, args+1, strlen(args));
00994 args[strlen(args)-1] = '\0';
00995 trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args);
00996 }
00997 else if (!Q_stricmp(buf, "chat")) {
00998
00999 memmove(args, args+1, strlen(args));
01000 args[strlen(args)-1] = '\0';
01001 trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
01002 }
01003 else if (!Q_stricmp(buf, "tchat")) {
01004
01005 memmove(args, args+1, strlen(args));
01006 args[strlen(args)-1] = '\0';
01007 trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
01008 }
01009 #ifdef MISSIONPACK
01010 else if (!Q_stricmp(buf, "vchat")) {
01011 BotVoiceChatCommand(bs, SAY_ALL, args);
01012 }
01013 else if (!Q_stricmp(buf, "vtchat")) {
01014 BotVoiceChatCommand(bs, SAY_TEAM, args);
01015 }
01016 else if (!Q_stricmp(buf, "vtell")) {
01017 BotVoiceChatCommand(bs, SAY_TELL, args);
01018 }
01019 #endif
01020 else if (!Q_stricmp(buf, "scores"))
01021 { }
01022 else if (!Q_stricmp(buf, "clientLevelShot"))
01023 { }
01024 }
01025
01026 for (j = 0; j < 3; j++) {
01027 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
01028 }
01029
01030 bs->ltime += thinktime;
01031
01032 bs->thinktime = thinktime;
01033
01034 VectorCopy(bs->cur_ps.origin, bs->origin);
01035
01036 VectorCopy(bs->cur_ps.origin, bs->eye);
01037 bs->eye[2] += bs->cur_ps.viewheight;
01038
01039 bs->areanum = BotPointAreaNum(bs->origin);
01040
01041 BotDeathmatchAI(bs, thinktime);
01042
01043 trap_EA_SelectWeapon(bs->client, bs->weaponnum);
01044
01045 for (j = 0; j < 3; j++) {
01046 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
01047 }
01048
01049 return qtrue;
01050 }
01051
01052
01053
01054
01055
01056
01057 void BotScheduleBotThink(void) {
01058 int i, botnum;
01059
01060 botnum = 0;
01061
01062 for( i = 0; i < MAX_CLIENTS; i++ ) {
01063 if( !botstates[i] || !botstates[i]->inuse ) {
01064 continue;
01065 }
01066
01067 botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots;
01068 botnum++;
01069 }
01070 }
01071
01072
01073
01074
01075
01076
01077 void BotWriteSessionData(bot_state_t *bs) {
01078 const char *s;
01079 const char *var;
01080
01081 s = va(
01082 "%i %i %i %i %i %i %i %i"
01083 " %f %f %f"
01084 " %f %f %f"
01085 " %f %f %f",
01086 bs->lastgoal_decisionmaker,
01087 bs->lastgoal_ltgtype,
01088 bs->lastgoal_teammate,
01089 bs->lastgoal_teamgoal.areanum,
01090 bs->lastgoal_teamgoal.entitynum,
01091 bs->lastgoal_teamgoal.flags,
01092 bs->lastgoal_teamgoal.iteminfo,
01093 bs->lastgoal_teamgoal.number,
01094 bs->lastgoal_teamgoal.origin[0],
01095 bs->lastgoal_teamgoal.origin[1],
01096 bs->lastgoal_teamgoal.origin[2],
01097 bs->lastgoal_teamgoal.mins[0],
01098 bs->lastgoal_teamgoal.mins[1],
01099 bs->lastgoal_teamgoal.mins[2],
01100 bs->lastgoal_teamgoal.maxs[0],
01101 bs->lastgoal_teamgoal.maxs[1],
01102 bs->lastgoal_teamgoal.maxs[2]
01103 );
01104
01105 var = va( "botsession%i", bs->client );
01106
01107 trap_Cvar_Set( var, s );
01108 }
01109
01110
01111
01112
01113
01114
01115 void BotReadSessionData(bot_state_t *bs) {
01116 char s[MAX_STRING_CHARS];
01117 const char *var;
01118
01119 var = va( "botsession%i", bs->client );
01120 trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
01121
01122 sscanf(s,
01123 "%i %i %i %i %i %i %i %i"
01124 " %f %f %f"
01125 " %f %f %f"
01126 " %f %f %f",
01127 &bs->lastgoal_decisionmaker,
01128 &bs->lastgoal_ltgtype,
01129 &bs->lastgoal_teammate,
01130 &bs->lastgoal_teamgoal.areanum,
01131 &bs->lastgoal_teamgoal.entitynum,
01132 &bs->lastgoal_teamgoal.flags,
01133 &bs->lastgoal_teamgoal.iteminfo,
01134 &bs->lastgoal_teamgoal.number,
01135 &bs->lastgoal_teamgoal.origin[0],
01136 &bs->lastgoal_teamgoal.origin[1],
01137 &bs->lastgoal_teamgoal.origin[2],
01138 &bs->lastgoal_teamgoal.mins[0],
01139 &bs->lastgoal_teamgoal.mins[1],
01140 &bs->lastgoal_teamgoal.mins[2],
01141 &bs->lastgoal_teamgoal.maxs[0],
01142 &bs->lastgoal_teamgoal.maxs[1],
01143 &bs->lastgoal_teamgoal.maxs[2]
01144 );
01145 }
01146
01147
01148
01149
01150
01151
01152 int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) {
01153 char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH];
01154 bot_state_t *bs;
01155 int errnum;
01156
01157 if (!botstates[client]) botstates[client] = G_Alloc(sizeof(bot_state_t));
01158 bs = botstates[client];
01159
01160 if (bs && bs->inuse) {
01161 BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client);
01162 return qfalse;
01163 }
01164
01165 if (!trap_AAS_Initialized()) {
01166 BotAI_Print(PRT_FATAL, "AAS not initialized\n");
01167 return qfalse;
01168 }
01169
01170
01171 bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill);
01172 if (!bs->character) {
01173 BotAI_Print(PRT_FATAL, "couldn't load skill %f from %s\n", settings->skill, settings->characterfile);
01174 return qfalse;
01175 }
01176
01177 memcpy(&bs->settings, settings, sizeof(bot_settings_t));
01178
01179 bs->gs = trap_BotAllocGoalState(client);
01180
01181 trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH);
01182 errnum = trap_BotLoadItemWeights(bs->gs, filename);
01183 if (errnum != BLERR_NOERROR) {
01184 trap_BotFreeGoalState(bs->gs);
01185 return qfalse;
01186 }
01187
01188 bs->ws = trap_BotAllocWeaponState();
01189
01190 trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH);
01191 errnum = trap_BotLoadWeaponWeights(bs->ws, filename);
01192 if (errnum != BLERR_NOERROR) {
01193 trap_BotFreeGoalState(bs->gs);
01194 trap_BotFreeWeaponState(bs->ws);
01195 return qfalse;
01196 }
01197
01198 bs->cs = trap_BotAllocChatState();
01199
01200 trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH);
01201 trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH);
01202 errnum = trap_BotLoadChatFile(bs->cs, filename, name);
01203 if (errnum != BLERR_NOERROR) {
01204 trap_BotFreeChatState(bs->cs);
01205 trap_BotFreeGoalState(bs->gs);
01206 trap_BotFreeWeaponState(bs->ws);
01207 return qfalse;
01208 }
01209
01210 trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH);
01211
01212 if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE);
01213 else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE);
01214 else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS);
01215
01216 bs->inuse = qtrue;
01217 bs->client = client;
01218 bs->entitynum = client;
01219 bs->setupcount = 4;
01220 bs->entergame_time = FloatTime();
01221 bs->ms = trap_BotAllocMoveState();
01222 bs->walker = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WALKER, 0, 1);
01223 numbots++;
01224
01225 if (trap_Cvar_VariableIntegerValue("bot_testichat")) {
01226 trap_BotLibVarSet("bot_testichat", "1");
01227 BotChatTest(bs);
01228 }
01229
01230 BotScheduleBotThink();
01231
01232 if (bot_interbreed) {
01233 trap_BotMutateGoalFuzzyLogic(bs->gs, 1);
01234 }
01235
01236 if (restart) {
01237 BotReadSessionData(bs);
01238 }
01239
01240 return qtrue;
01241 }
01242
01243
01244
01245
01246
01247
01248 int BotAIShutdownClient(int client, qboolean restart) {
01249 bot_state_t *bs;
01250
01251 bs = botstates[client];
01252 if (!bs || !bs->inuse) {
01253
01254 return qfalse;
01255 }
01256
01257 if (restart) {
01258 BotWriteSessionData(bs);
01259 }
01260
01261 if (BotChat_ExitGame(bs)) {
01262 trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL);
01263 }
01264
01265 trap_BotFreeMoveState(bs->ms);
01266
01267 trap_BotFreeGoalState(bs->gs);
01268
01269 trap_BotFreeChatState(bs->cs);
01270
01271 trap_BotFreeWeaponState(bs->ws);
01272
01273 trap_BotFreeCharacter(bs->character);
01274
01275 BotFreeWaypoints(bs->checkpoints);
01276 BotFreeWaypoints(bs->patrolpoints);
01277
01278 BotClearActivateGoalStack(bs);
01279
01280 memset(bs, 0, sizeof(bot_state_t));
01281
01282 bs->inuse = qfalse;
01283
01284 numbots--;
01285
01286 return qtrue;
01287 }
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297 void BotResetState(bot_state_t *bs) {
01298 int client, entitynum, inuse;
01299 int movestate, goalstate, chatstate, weaponstate;
01300 bot_settings_t settings;
01301 int character;
01302 playerState_t ps;
01303 float entergame_time;
01304
01305
01306 memcpy(&settings, &bs->settings, sizeof(bot_settings_t));
01307 memcpy(&ps, &bs->cur_ps, sizeof(playerState_t));
01308 inuse = bs->inuse;
01309 client = bs->client;
01310 entitynum = bs->entitynum;
01311 character = bs->character;
01312 movestate = bs->ms;
01313 goalstate = bs->gs;
01314 chatstate = bs->cs;
01315 weaponstate = bs->ws;
01316 entergame_time = bs->entergame_time;
01317
01318 BotFreeWaypoints(bs->checkpoints);
01319 BotFreeWaypoints(bs->patrolpoints);
01320
01321 memset(bs, 0, sizeof(bot_state_t));
01322
01323 bs->ms = movestate;
01324 bs->gs = goalstate;
01325 bs->cs = chatstate;
01326 bs->ws = weaponstate;
01327 memcpy(&bs->cur_ps, &ps, sizeof(playerState_t));
01328 memcpy(&bs->settings, &settings, sizeof(bot_settings_t));
01329 bs->inuse = inuse;
01330 bs->client = client;
01331 bs->entitynum = entitynum;
01332 bs->character = character;
01333 bs->entergame_time = entergame_time;
01334
01335 if (bs->ms) trap_BotResetMoveState(bs->ms);
01336 if (bs->gs) trap_BotResetGoalState(bs->gs);
01337 if (bs->ws) trap_BotResetWeaponState(bs->ws);
01338 if (bs->gs) trap_BotResetAvoidGoals(bs->gs);
01339 if (bs->ms) trap_BotResetAvoidReach(bs->ms);
01340 }
01341
01342
01343
01344
01345
01346
01347 int BotAILoadMap( int restart ) {
01348 int i;
01349 vmCvar_t mapname;
01350
01351 if (!restart) {
01352 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
01353 trap_BotLibLoadMap( mapname.string );
01354 }
01355
01356 for (i = 0; i < MAX_CLIENTS; i++) {
01357 if (botstates[i] && botstates[i]->inuse) {
01358 BotResetState( botstates[i] );
01359 botstates[i]->setupcount = 4;
01360 }
01361 }
01362
01363 BotSetupDeathmatchAI();
01364
01365 return qtrue;
01366 }
01367
01368 #ifdef MISSIONPACK
01369 void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace );
01370 #endif
01371
01372
01373
01374
01375
01376
01377 int BotAIStartFrame(int time) {
01378 int i;
01379 gentity_t *ent;
01380 bot_entitystate_t state;
01381 int elapsed_time, thinktime;
01382 static int local_time;
01383 static int botlib_residual;
01384 static int lastbotthink_time;
01385
01386 G_CheckBotSpawn();
01387
01388 trap_Cvar_Update(&bot_rocketjump);
01389 trap_Cvar_Update(&bot_grapple);
01390 trap_Cvar_Update(&bot_fastchat);
01391 trap_Cvar_Update(&bot_nochat);
01392 trap_Cvar_Update(&bot_testrchat);
01393 trap_Cvar_Update(&bot_thinktime);
01394 trap_Cvar_Update(&bot_memorydump);
01395 trap_Cvar_Update(&bot_saveroutingcache);
01396 trap_Cvar_Update(&bot_pause);
01397 trap_Cvar_Update(&bot_report);
01398
01399 if (bot_report.integer) {
01400
01401
01402 BotUpdateInfoConfigStrings();
01403 }
01404
01405 if (bot_pause.integer) {
01406
01407 for( i = 0; i < MAX_CLIENTS; i++ ) {
01408 if( !botstates[i] || !botstates[i]->inuse ) {
01409 continue;
01410 }
01411 if( g_entities[i].client->pers.connected != CON_CONNECTED ) {
01412 continue;
01413 }
01414 botstates[i]->lastucmd.forwardmove = 0;
01415 botstates[i]->lastucmd.rightmove = 0;
01416 botstates[i]->lastucmd.upmove = 0;
01417 botstates[i]->lastucmd.buttons = 0;
01418 botstates[i]->lastucmd.serverTime = time;
01419 trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
01420 }
01421 return qtrue;
01422 }
01423
01424 if (bot_memorydump.integer) {
01425 trap_BotLibVarSet("memorydump", "1");
01426 trap_Cvar_Set("bot_memorydump", "0");
01427 }
01428 if (bot_saveroutingcache.integer) {
01429 trap_BotLibVarSet("saveroutingcache", "1");
01430 trap_Cvar_Set("bot_saveroutingcache", "0");
01431 }
01432
01433 BotInterbreeding();
01434
01435 if (bot_thinktime.integer > 200) {
01436 trap_Cvar_Set("bot_thinktime", "200");
01437 }
01438
01439 if (bot_thinktime.integer != lastbotthink_time) {
01440 lastbotthink_time = bot_thinktime.integer;
01441 BotScheduleBotThink();
01442 }
01443
01444 elapsed_time = time - local_time;
01445 local_time = time;
01446
01447 botlib_residual += elapsed_time;
01448
01449 if (elapsed_time > bot_thinktime.integer) thinktime = elapsed_time;
01450 else thinktime = bot_thinktime.integer;
01451
01452
01453 if ( botlib_residual >= thinktime ) {
01454 botlib_residual -= thinktime;
01455
01456 trap_BotLibStartFrame((float) time / 1000);
01457
01458 if (!trap_AAS_Initialized()) return qfalse;
01459
01460
01461 for (i = 0; i < MAX_GENTITIES; i++) {
01462 ent = &g_entities[i];
01463 if (!ent->inuse) {
01464 trap_BotLibUpdateEntity(i, NULL);
01465 continue;
01466 }
01467 if (!ent->r.linked) {
01468 trap_BotLibUpdateEntity(i, NULL);
01469 continue;
01470 }
01471 if (ent->r.svFlags & SVF_NOCLIENT) {
01472 trap_BotLibUpdateEntity(i, NULL);
01473 continue;
01474 }
01475
01476 if (ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK) {
01477 trap_BotLibUpdateEntity(i, NULL);
01478 continue;
01479 }
01480
01481 if (ent->s.eType > ET_EVENTS) {
01482 trap_BotLibUpdateEntity(i, NULL);
01483 continue;
01484 }
01485 #ifdef MISSIONPACK
01486
01487 if (ent->r.contents == CONTENTS_TRIGGER) {
01488 if (ent->touch == ProximityMine_Trigger) {
01489 trap_BotLibUpdateEntity(i, NULL);
01490 continue;
01491 }
01492 }
01493 #endif
01494
01495 memset(&state, 0, sizeof(bot_entitystate_t));
01496
01497 VectorCopy(ent->r.currentOrigin, state.origin);
01498 if (i < MAX_CLIENTS) {
01499 VectorCopy(ent->s.apos.trBase, state.angles);
01500 } else {
01501 VectorCopy(ent->r.currentAngles, state.angles);
01502 }
01503 VectorCopy(ent->s.origin2, state.old_origin);
01504 VectorCopy(ent->r.mins, state.mins);
01505 VectorCopy(ent->r.maxs, state.maxs);
01506 state.type = ent->s.eType;
01507 state.flags = ent->s.eFlags;
01508 if (ent->r.bmodel) state.solid = SOLID_BSP;
01509 else state.solid = SOLID_BBOX;
01510 state.groundent = ent->s.groundEntityNum;
01511 state.modelindex = ent->s.modelindex;
01512 state.modelindex2 = ent->s.modelindex2;
01513 state.frame = ent->s.frame;
01514 state.event = ent->s.event;
01515 state.eventParm = ent->s.eventParm;
01516 state.powerups = ent->s.powerups;
01517 state.legsAnim = ent->s.legsAnim;
01518 state.torsoAnim = ent->s.torsoAnim;
01519 state.weapon = ent->s.weapon;
01520
01521 trap_BotLibUpdateEntity(i, &state);
01522 }
01523
01524 BotAIRegularUpdate();
01525 }
01526
01527 floattime = trap_AAS_Time();
01528
01529
01530 for( i = 0; i < MAX_CLIENTS; i++ ) {
01531 if( !botstates[i] || !botstates[i]->inuse ) {
01532 continue;
01533 }
01534
01535 botstates[i]->botthink_residual += elapsed_time;
01536
01537 if ( botstates[i]->botthink_residual >= thinktime ) {
01538 botstates[i]->botthink_residual -= thinktime;
01539
01540 if (!trap_AAS_Initialized()) return qfalse;
01541
01542 if (g_entities[i].client->pers.connected == CON_CONNECTED) {
01543 BotAI(i, (float) thinktime / 1000);
01544 }
01545 }
01546 }
01547
01548
01549
01550 for( i = 0; i < MAX_CLIENTS; i++ ) {
01551 if( !botstates[i] || !botstates[i]->inuse ) {
01552 continue;
01553 }
01554 if( g_entities[i].client->pers.connected != CON_CONNECTED ) {
01555 continue;
01556 }
01557
01558 BotUpdateInput(botstates[i], time, elapsed_time);
01559 trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
01560 }
01561
01562 return qtrue;
01563 }
01564
01565
01566
01567
01568
01569
01570 int BotInitLibrary(void) {
01571 char buf[144];
01572
01573
01574 trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf));
01575 if (!strlen(buf)) strcpy(buf, "8");
01576 trap_BotLibVarSet("maxclients", buf);
01577 Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES);
01578 trap_BotLibVarSet("maxentities", buf);
01579
01580 trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf));
01581 if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf);
01582
01583 trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf));
01584 if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf);
01585
01586 trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf));
01587 if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf);
01588
01589 trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf));
01590 if (!strlen(buf)) strcpy(buf, "0");
01591 trap_BotLibVarSet("g_gametype", buf);
01592
01593 trap_BotLibVarSet("bot_developer", bot_developer.string);
01594 trap_BotLibVarSet("log", buf);
01595
01596 trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf));
01597 if (strlen(buf)) trap_BotLibVarSet("nochat", "0");
01598
01599 trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf));
01600 if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf);
01601
01602 trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf));
01603 if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf);
01604
01605 trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf));
01606 if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf);
01607
01608 trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf));
01609 if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf);
01610
01611 trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf));
01612 if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf);
01613
01614 trap_Cvar_VariableStringBuffer("bot_saveroutingcache", buf, sizeof(buf));
01615 if (strlen(buf)) trap_BotLibVarSet("saveroutingcache", buf);
01616
01617 trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf));
01618 if (!strlen(buf)) strcpy(buf, "0");
01619 trap_BotLibVarSet("bot_reloadcharacters", buf);
01620
01621 trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf));
01622 if (strlen(buf)) trap_BotLibVarSet("basedir", buf);
01623
01624 trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf));
01625 if (strlen(buf)) trap_BotLibVarSet("gamedir", buf);
01626
01627 trap_Cvar_VariableStringBuffer("fs_cdpath", buf, sizeof(buf));
01628 if (strlen(buf)) trap_BotLibVarSet("cddir", buf);
01629
01630 #ifdef MISSIONPACK
01631 trap_BotLibDefine("MISSIONPACK");
01632 #endif
01633
01634 return trap_BotLibSetup();
01635 }
01636
01637
01638
01639
01640
01641
01642 int BotAISetup( int restart ) {
01643 int errnum;
01644
01645 trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT);
01646 trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT);
01647 trap_Cvar_Register(&bot_saveroutingcache, "bot_saveroutingcache", "0", CVAR_CHEAT);
01648 trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT);
01649 trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT);
01650 trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT);
01651 trap_Cvar_Register(&bot_testclusters, "bot_testclusters", "0", CVAR_CHEAT);
01652 trap_Cvar_Register(&bot_developer, "bot_developer", "0", CVAR_CHEAT);
01653 trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0);
01654 trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0);
01655 trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0);
01656 trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0);
01657
01658
01659 if (restart) {
01660 return qtrue;
01661 }
01662
01663
01664 memset( botstates, 0, sizeof(botstates) );
01665
01666 errnum = BotInitLibrary();
01667 if (errnum != BLERR_NOERROR) return qfalse;
01668 return qtrue;
01669 }
01670
01671
01672
01673
01674
01675
01676 int BotAIShutdown( int restart ) {
01677
01678 int i;
01679
01680
01681 if ( restart ) {
01682
01683 for (i = 0; i < MAX_CLIENTS; i++) {
01684 if (botstates[i] && botstates[i]->inuse) {
01685 BotAIShutdownClient(botstates[i]->client, restart);
01686 }
01687 }
01688
01689 }
01690 else {
01691 trap_BotLibShutdown();
01692 }
01693 return qtrue;
01694 }
01695