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,