Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

ai_main.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 
00024 /*****************************************************************************
00025  * name:        ai_main.c
00026  *
00027  * desc:        Quake3 bot AI
00028  *
00029  * $Archive: /MissionPack/code/game/ai_main.c $
00030  *
00031  *****************************************************************************/
00032 
00033 
00034 #include "g_local.h"
00035 #include "q_shared.h"
00036 #include "botlib.h"     //bot lib interface
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 //bot states
00062 bot_state_t *botstates[MAX_CLIENTS];
00063 //number of bots
00064 int numbots;
00065 //floating point time
00066 float floattime;
00067 //time to do a regular update
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 BotAI_Print
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 BotAI_Trace
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     //copy the trace information
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 BotAI_GetClientState
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 BotAI_GetEntityState
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 BotAI_GetSnapshotEntity
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 BotAI_BotInitialChat
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 BotTestAAS
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 BotReportStatus
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 BotTeamplayReport
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         //if no config string or no name
00398         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
00399         //skip spectators
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         //if no config string or no name
00411         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
00412         //skip spectators
00413         if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) {
00414             BotReportStatus(botstates[i]);
00415         }
00416     }
00417 }
00418 
00419 /*
00420 ==================
00421 BotSetInfoConfigString
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 BotUpdateInfoConfigStrings
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         //if no config string or no name
00553         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n")))
00554             continue;
00555         BotSetInfoConfigString(botstates[i]);
00556     }
00557 }
00558 
00559 /*
00560 ==============
00561 BotInterbreedBots
00562 ==============
00563 */
00564 void BotInterbreedBots(void) {
00565     float ranks[MAX_CLIENTS];
00566     int parent1, parent2, child;
00567     int i;
00568 
00569     // get rankings for all the bots
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     // reset the kills and deaths
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 BotWriteInterbreeded
00595 ==============
00596 */
00597 void BotWriteInterbreeded(char *filename) {
00598     float rank, bestrank;
00599     int i, bestbot;
00600 
00601     bestrank = 0;
00602     bestbot = -1;
00603     // get the best bot
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         //write out the new goal fuzzy logic
00618         trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename);
00619     }
00620 }
00621 
00622 /*
00623 ==============
00624 BotInterbreedEndMatch
00625 
00626 add link back into ExitLevel?
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 BotInterbreeding
00648 ==============
00649 */
00650 void BotInterbreeding(void) {
00651     int i;
00652 
00653     trap_Cvar_Update(&bot_interbreedchar);
00654     if (!strlen(bot_interbreedchar.string)) return;
00655     //make sure we are in tournament mode
00656     if (gametype != GT_TOURNAMENT) {
00657         trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT));
00658         ExitLevel();
00659         return;
00660     }
00661     //shutdown all the bots
00662     for (i = 0; i < MAX_CLIENTS; i++) {
00663         if (botstates[i] && botstates[i]->inuse) {
00664             BotAIShutdownClient(botstates[i]->client, qfalse);
00665         }
00666     }
00667     //make sure all item weight configs are reloaded and Not shared
00668     trap_BotLibVarSet("bot_reloadcharacters", "1");
00669     //add a number of bots using the desired bot character
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 BotEntityInfo
00682 ==============
00683 */
00684 void BotEntityInfo(int entnum, aas_entityinfo_t *info) {
00685     trap_AAS_EntityInfo(entnum, info);
00686 }
00687 
00688 /*
00689 ==============
00690 NumBots
00691 ==============
00692 */
00693 int NumBots(void) {
00694     return numbots;
00695 }
00696 
00697 /*
00698 ==============
00699 BotTeamLeader
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 AngleDifference
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 BotChangeViewAngle
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 BotChangeViewAngles
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             //smooth slowdown view model
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             //over reaction view model
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             //demping
00802             bs->viewanglespeed[i] *= 0.45 * (1 - factor);
00803         }
00804         //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);`
00805         //bs->viewangles[i] = bs->ideal_viewangles[i];
00806     }
00807     //bs->viewangles[PITCH] = 0;
00808     if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360;
00809     //elementary action: view
00810     trap_EA_View(bs->client, bs->viewangles);
00811 }
00812 
00813 /*
00814 ==============
00815 BotInputToUserCommand
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     //clear the whole structure
00824     memset(ucmd, 0, sizeof(usercmd_t));
00825     //
00826     //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed);
00827     //the duration for the user command in milli seconds
00828     ucmd->serverTime = time;
00829     //
00830     if (bi->actionflags & ACTION_DELAYEDJUMP) {
00831         bi->actionflags |= ACTION_JUMP;
00832         bi->actionflags &= ~ACTION_DELAYEDJUMP;
00833     }
00834     //set the buttons
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     //set the view angles
00850     //NOTE: the ucmd->angles are the angles WITHOUT the delta angles
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     //subtract the delta angles
00855     for (j = 0; j < 3; j++) {
00856         temp = ucmd->angles[j] - delta_angles[j];
00857         /*NOTE: disabled because temp should be mod first
00858         if ( j == PITCH ) {
00859             // don't let the player look up or down more than 90 degrees
00860             if ( temp > 16000 ) temp = 16000;
00861             else if ( temp < -16000 ) temp = -16000;
00862         }
00863         */
00864         ucmd->angles[j] = temp;
00865     }
00866     //NOTE: movement is relative to the REAL view angles
00867     //get the horizontal forward and right vector
00868     //get the pitch in the range [-180, 180]
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     //bot input speed is in the range [0, 400]
00875     bi->speed = bi->speed * 127 / 400;
00876     //set the view independent movement
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     //normal keyboard movement
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     //jump/moveup
00886     if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127;
00887     //crouch/movedown
00888     if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127;
00889     //
00890     //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
00891     //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime);
00892 }
00893 
00894 /*
00895 ==============
00896 BotUpdateInput
00897 ==============
00898 */
00899 void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) {
00900     bot_input_t bi;
00901     int j;
00902 
00903     //add the delta angles to the bot's current view angles
00904     for (j = 0; j < 3; j++) {
00905         bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00906     }
00907     //change the bot view angles
00908     BotChangeViewAngles(bs, (float) elapsed_time / 1000);
00909     //retrieve the bot input
00910     trap_EA_GetInput(bs->client, (float) time / 1000, &bi);
00911     //respawn hack
00912     if (bi.actionflags & ACTION_RESPAWN) {
00913         if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK);
00914     }
00915     //convert the bot input to a usercmd
00916     BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time);
00917     //subtract the delta angles
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 BotAIRegularUpdate
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 RemoveColorEscapeSequences
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 BotAI
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     //retrieve the current client state
00975     BotAI_GetClientState( client, &bs->cur_ps );
00976 
00977     //retrieve any waiting server commands
00978     while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) {
00979         //have buf point to the command and args to the command arguments
00980         args = strchr( buf, ' ');
00981         if (!args) continue;
00982         *args++ = '\0';
00983 
00984         //remove color espace sequences from the arguments
00985         RemoveColorEscapeSequences( args );
00986 
00987         if (!Q_stricmp(buf, "cp "))
00988             { /*CenterPrintf*/ }
00989         else if (!Q_stricmp(buf, "cs"))
00990             { /*ConfigStringModified*/ }
00991         else if (!Q_stricmp(buf, "print")) {
00992             //remove first and last quote from the chat message
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             //remove first and last quote from the chat message
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             //remove first and last quote from the chat message
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             { /*FIXME: parse scores?*/ }
01022         else if (!Q_stricmp(buf, "clientLevelShot"))
01023             { /*ignore*/ }
01024     }
01025     //add the delta angles to the bot's current view angles
01026     for (j = 0; j < 3; j++) {
01027         bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
01028     }
01029     //increase the local time of the bot
01030     bs->ltime += thinktime;
01031     //
01032     bs->thinktime = thinktime;
01033     //origin of the bot
01034     VectorCopy(bs->cur_ps.origin, bs->origin);
01035     //eye coordinates of the bot
01036     VectorCopy(bs->cur_ps.origin, bs->eye);
01037     bs->eye[2] += bs->cur_ps.viewheight;
01038     //get the area the bot is in
01039     bs->areanum = BotPointAreaNum(bs->origin);
01040     //the real AI
01041     BotDeathmatchAI(bs, thinktime);
01042     //set the weapon selection every AI frame
01043     trap_EA_SelectWeapon(bs->client, bs->weaponnum);
01044     //subtract the delta angles
01045     for (j = 0; j < 3; j++) {
01046         bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
01047     }
01048     //everything was ok
01049     return qtrue;
01050 }
01051 
01052 /*
01053 ==================
01054 BotScheduleBotThink
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         //initialize the bot think residual time
01067         botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots;
01068         botnum++;
01069     }
01070 }
01071 
01072 /*
01073 ==============
01074 BotWriteSessionData
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 BotReadSessionData
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 BotAISetupClient
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     //load the bot character
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     //copy the settings
01177     memcpy(&bs->settings, settings, sizeof(bot_settings_t));
01178     //allocate a goal state
01179     bs->gs = trap_BotAllocGoalState(client);
01180     //load the item weights
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     //allocate a weapon state
01188     bs->ws = trap_BotAllocWeaponState();
01189     //load the weapon weights
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     //allocate a chat state
01198     bs->cs = trap_BotAllocChatState();
01199     //load the chat file
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     //get the gender characteristic
01210     trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH);
01211     //set the chat gender
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     //NOTE: reschedule the bot thinking
01230     BotScheduleBotThink();
01231     //if interbreeding start with a mutation
01232     if (bot_interbreed) {
01233         trap_BotMutateGoalFuzzyLogic(bs->gs, 1);
01234     }
01235     // if we kept the bot client
01236     if (restart) {
01237         BotReadSessionData(bs);
01238     }
01239     //bot has been setup succesfully
01240     return qtrue;
01241 }
01242 
01243 /*
01244 ==============
01245 BotAIShutdownClient
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         //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client);
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     //free the goal state`          
01267     trap_BotFreeGoalState(bs->gs);
01268     //free the chat file
01269     trap_BotFreeChatState(bs->cs);
01270     //free the weapon weights
01271     trap_BotFreeWeaponState(bs->ws);
01272     //free the bot character
01273     trap_BotFreeCharacter(bs->character);
01274     //
01275     BotFreeWaypoints(bs->checkpoints);
01276     BotFreeWaypoints(bs->patrolpoints);
01277     //clear activate goal stack
01278     BotClearActivateGoalStack(bs);
01279     //clear the bot state
01280     memset(bs, 0, sizeof(bot_state_t));
01281     //set the inuse flag to qfalse
01282     bs->inuse = qfalse;
01283     //there's one bot less
01284     numbots--;
01285     //everything went ok
01286     return qtrue;
01287 }
01288 
01289 /*
01290 ==============
01291 BotResetState
01292 
01293 called when a bot enters the intermission or observer mode and
01294 when the level is changed
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;                           //current player state
01303     float entergame_time;
01304 
01305     //save some things that should not be reset here
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     //free checkpoints and patrol points
01318     BotFreeWaypoints(bs->checkpoints);
01319     BotFreeWaypoints(bs->patrolpoints);
01320     //reset the whole state
01321     memset(bs, 0, sizeof(bot_state_t));
01322     //copy back some state stuff that should not be reset
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     //reset several states
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 BotAILoadMap
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 BotAIStartFrame
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 //      BotTeamplayReport();
01401 //      trap_Cvar_Set("bot_report", "0");
01402         BotUpdateInfoConfigStrings();
01403     }
01404 
01405     if (bot_pause.integer) {
01406         // execute bot user commands every frame
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     //check if bot interbreeding is activated
01433     BotInterbreeding();
01434     //cap the bot think time
01435     if (bot_thinktime.integer > 200) {
01436         trap_Cvar_Set("bot_thinktime", "200");
01437     }
01438     //if the bot think time changed we should reschedule the bots
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     // update the bot library
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         //update entities in the botlib
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             // do not update missiles
01476             if (ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK) {
01477                 trap_BotLibUpdateEntity(i, NULL);
01478                 continue;
01479             }
01480             // do not update event only entities
01481             if (ent->s.eType > ET_EVENTS) {
01482                 trap_BotLibUpdateEntity(i, NULL);
01483                 continue;
01484             }
01485 #ifdef MISSIONPACK
01486             // never link prox mine triggers
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     // execute scheduled bot AI
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     // execute bot user commands every frame
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 BotInitLibrary
01568 ==============
01569 */
01570 int BotInitLibrary(void) {
01571     char buf[144];
01572 
01573     //set the maxclients and maxentities library variables before calling BotSetupLibrary
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     //bsp checksum
01580     trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf));
01581     if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf);
01582     //maximum number of aas links
01583     trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf));
01584     if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf);
01585     //maximum number of items in a level
01586     trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf));
01587     if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf);
01588     //game type
01589     trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf));
01590     if (!strlen(buf)) strcpy(buf, "0");
01591     trap_BotLibVarSet("g_gametype", buf);
01592     //bot developer mode and log file
01593     trap_BotLibVarSet("bot_developer", bot_developer.string);
01594     trap_BotLibVarSet("log", buf);
01595     //no chatting
01596     trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf));
01597     if (strlen(buf)) trap_BotLibVarSet("nochat", "0");
01598     //visualize jump pads
01599     trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf));
01600     if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf);
01601     //forced clustering calculations
01602     trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf));
01603     if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf);
01604     //forced reachability calculations
01605     trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf));
01606     if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf);
01607     //force writing of AAS to file
01608     trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf));
01609     if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf);
01610     //no AAS optimization
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     //reload instead of cache bot character files
01617     trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf));
01618     if (!strlen(buf)) strcpy(buf, "0");
01619     trap_BotLibVarSet("bot_reloadcharacters", buf);
01620     //base directory
01621     trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf));
01622     if (strlen(buf)) trap_BotLibVarSet("basedir", buf);
01623     //game directory
01624     trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf));
01625     if (strlen(buf)) trap_BotLibVarSet("gamedir", buf);
01626     //cd directory
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     //setup the bot library
01634     return trap_BotLibSetup();
01635 }
01636 
01637 /*
01638 ==============
01639 BotAISetup
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     //if the game is restarted for a tournament
01659     if (restart) {
01660         return qtrue;
01661     }
01662 
01663     //initialize the bot states
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 BotAIShutdown
01674 ==============
01675 */
01676 int BotAIShutdown( int restart ) {
01677 
01678     int i;
01679 
01680     //if the game is restarted for a tournament
01681     if ( restart ) {
01682         //shutdown all the bots in the botlib
01683         for (i = 0; i < MAX_CLIENTS; i++) {
01684             if (botstates[i] && botstates[i]->inuse) {
01685                 BotAIShutdownClient(botstates[i]->client, restart);
01686             }
01687         }
01688         //don't shutdown the bot library
01689     }
01690     else {
01691         trap_BotLibShutdown();
01692     }
01693     return qtrue;
01694 }
01695 

Generated on Thu Aug 25 12:37:29 2005 for Quake III Arena by  doxygen 1.3.9.1