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

ai_dmnet.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_dmnet.c
00026  *
00027  * desc:        Quake3 bot AI
00028  *
00029  * $Archive: /MissionPack/code/game/ai_dmnet.c $
00030  *
00031  *****************************************************************************/
00032 
00033 #include "g_local.h"
00034 #include "botlib.h"
00035 #include "be_aas.h"
00036 #include "be_ea.h"
00037 #include "be_ai_char.h"
00038 #include "be_ai_chat.h"
00039 #include "be_ai_gen.h"
00040 #include "be_ai_goal.h"
00041 #include "be_ai_move.h"
00042 #include "be_ai_weap.h"
00043 //
00044 #include "ai_main.h"
00045 #include "ai_dmq3.h"
00046 #include "ai_chat.h"
00047 #include "ai_cmd.h"
00048 #include "ai_dmnet.h"
00049 #include "ai_team.h"
00050 //data file headers
00051 #include "chars.h"          //characteristics
00052 #include "inv.h"            //indexes into the inventory
00053 #include "syn.h"            //synonyms
00054 #include "match.h"          //string matching types and vars
00055 
00056 // for the voice chats
00057 #include "../../ui/menudef.h"
00058 
00059 //goal flag, see be_ai_goal.h for the other GFL_*
00060 #define GFL_AIR         128
00061 
00062 int numnodeswitches;
00063 char nodeswitch[MAX_NODESWITCHES+1][144];
00064 
00065 #define LOOKAHEAD_DISTANCE          300
00066 
00067 /*
00068 ==================
00069 BotResetNodeSwitches
00070 ==================
00071 */
00072 void BotResetNodeSwitches(void) {
00073     numnodeswitches = 0;
00074 }
00075 
00076 /*
00077 ==================
00078 BotDumpNodeSwitches
00079 ==================
00080 */
00081 void BotDumpNodeSwitches(bot_state_t *bs) {
00082     int i;
00083     char netname[MAX_NETNAME];
00084 
00085     ClientName(bs->client, netname, sizeof(netname));
00086     BotAI_Print(PRT_MESSAGE, "%s at %1.1f switched more than %d AI nodes\n", netname, FloatTime(), MAX_NODESWITCHES);
00087     for (i = 0; i < numnodeswitches; i++) {
00088         BotAI_Print(PRT_MESSAGE, nodeswitch[i]);
00089     }
00090     BotAI_Print(PRT_FATAL, "");
00091 }
00092 
00093 /*
00094 ==================
00095 BotRecordNodeSwitch
00096 ==================
00097 */
00098 void BotRecordNodeSwitch(bot_state_t *bs, char *node, char *str, char *s) {
00099     char netname[MAX_NETNAME];
00100 
00101     ClientName(bs->client, netname, sizeof(netname));
00102     Com_sprintf(nodeswitch[numnodeswitches], 144, "%s at %2.1f entered %s: %s from %s\n", netname, FloatTime(), node, str, s);
00103 #ifdef DEBUG
00104     if (0) {
00105         BotAI_Print(PRT_MESSAGE, nodeswitch[numnodeswitches]);
00106     }
00107 #endif //DEBUG
00108     numnodeswitches++;
00109 }
00110 
00111 /*
00112 ==================
00113 BotGetAirGoal
00114 ==================
00115 */
00116 int BotGetAirGoal(bot_state_t *bs, bot_goal_t *goal) {
00117     bsp_trace_t bsptrace;
00118     vec3_t end, mins = {-15, -15, -2}, maxs = {15, 15, 2};
00119     int areanum;
00120 
00121     //trace up until we hit solid
00122     VectorCopy(bs->origin, end);
00123     end[2] += 1000;
00124     BotAI_Trace(&bsptrace, bs->origin, mins, maxs, end, bs->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00125     //trace down until we hit water
00126     VectorCopy(bsptrace.endpos, end);
00127     BotAI_Trace(&bsptrace, end, mins, maxs, bs->origin, bs->entitynum, CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
00128     //if we found the water surface
00129     if (bsptrace.fraction > 0) {
00130         areanum = BotPointAreaNum(bsptrace.endpos);
00131         if (areanum) {
00132             VectorCopy(bsptrace.endpos, goal->origin);
00133             goal->origin[2] -= 2;
00134             goal->areanum = areanum;
00135             goal->mins[0] = -15;
00136             goal->mins[1] = -15;
00137             goal->mins[2] = -1;
00138             goal->maxs[0] = 15;
00139             goal->maxs[1] = 15;
00140             goal->maxs[2] = 1;
00141             goal->flags = GFL_AIR;
00142             goal->number = 0;
00143             goal->iteminfo = 0;
00144             goal->entitynum = 0;
00145             return qtrue;
00146         }
00147     }
00148     return qfalse;
00149 }
00150 
00151 /*
00152 ==================
00153 BotGoForAir
00154 ==================
00155 */
00156 int BotGoForAir(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) {
00157     bot_goal_t goal;
00158 
00159     //if the bot needs air
00160     if (bs->lastair_time < FloatTime() - 6) {
00161         //
00162 #ifdef DEBUG
00163         //BotAI_Print(PRT_MESSAGE, "going for air\n");
00164 #endif //DEBUG
00165         //if we can find an air goal
00166         if (BotGetAirGoal(bs, &goal)) {
00167             trap_BotPushGoal(bs->gs, &goal);
00168             return qtrue;
00169         }
00170         else {
00171             //get a nearby goal outside the water
00172             while(trap_BotChooseNBGItem(bs->gs, bs->origin, bs->inventory, tfl, ltg, range)) {
00173                 trap_BotGetTopGoal(bs->gs, &goal);
00174                 //if the goal is not in water
00175                 if (!(trap_AAS_PointContents(goal.origin) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))) {
00176                     return qtrue;
00177                 }
00178                 trap_BotPopGoal(bs->gs);
00179             }
00180             trap_BotResetAvoidGoals(bs->gs);
00181         }
00182     }
00183     return qfalse;
00184 }
00185 
00186 /*
00187 ==================
00188 BotNearbyGoal
00189 ==================
00190 */
00191 int BotNearbyGoal(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) {
00192     int ret;
00193 
00194     //check if the bot should go for air
00195     if (BotGoForAir(bs, tfl, ltg, range)) return qtrue;
00196     //if the bot is carrying the enemy flag
00197     if (BotCTFCarryingFlag(bs)) {
00198         //if the bot is just a few secs away from the base 
00199         if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin,
00200                 bs->teamgoal.areanum, TFL_DEFAULT) < 300) {
00201             //make the range really small
00202             range = 50;
00203         }
00204     }
00205     //
00206     ret = trap_BotChooseNBGItem(bs->gs, bs->origin, bs->inventory, tfl, ltg, range);
00207     /*
00208     if (ret)
00209     {
00210         char buf[128];
00211         //get the goal at the top of the stack
00212         trap_BotGetTopGoal(bs->gs, &goal);
00213         trap_BotGoalName(goal.number, buf, sizeof(buf));
00214         BotAI_Print(PRT_MESSAGE, "%1.1f: new nearby goal %s\n", FloatTime(), buf);
00215     }
00216     */
00217     return ret;
00218 }
00219 
00220 /*
00221 ==================
00222 BotReachedGoal
00223 ==================
00224 */
00225 int BotReachedGoal(bot_state_t *bs, bot_goal_t *goal) {
00226     if (goal->flags & GFL_ITEM) {
00227         //if touching the goal
00228         if (trap_BotTouchingGoal(bs->origin, goal)) {
00229             if (!(goal->flags & GFL_DROPPED)) {
00230                 trap_BotSetAvoidGoalTime(bs->gs, goal->number, -1);
00231             }
00232             return qtrue;
00233         }
00234         //if the goal isn't there
00235         if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) {
00236             /*
00237             float avoidtime;
00238             int t;
00239 
00240             avoidtime = trap_BotAvoidGoalTime(bs->gs, goal->number);
00241             if (avoidtime > 0) {
00242                 t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal->areanum, bs->tfl);
00243                 if ((float) t * 0.009 < avoidtime)
00244                     return qtrue;
00245             }
00246             */
00247             return qtrue;
00248         }
00249         //if in the goal area and below or above the goal and not swimming
00250         if (bs->areanum == goal->areanum) {
00251             if (bs->origin[0] > goal->origin[0] + goal->mins[0] && bs->origin[0] < goal->origin[0] + goal->maxs[0]) {
00252                 if (bs->origin[1] > goal->origin[1] + goal->mins[1] && bs->origin[1] < goal->origin[1] + goal->maxs[1]) {
00253                     if (!trap_AAS_Swimming(bs->origin)) {
00254                         return qtrue;
00255                     }
00256                 }
00257             }
00258         }
00259     }
00260     else if (goal->flags & GFL_AIR) {
00261         //if touching the goal
00262         if (trap_BotTouchingGoal(bs->origin, goal)) return qtrue;
00263         //if the bot got air
00264         if (bs->lastair_time > FloatTime() - 1) return qtrue;
00265     }
00266     else {
00267         //if touching the goal
00268         if (trap_BotTouchingGoal(bs->origin, goal)) return qtrue;
00269     }
00270     return qfalse;
00271 }
00272 
00273 /*
00274 ==================
00275 BotGetItemLongTermGoal
00276 ==================
00277 */
00278 int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) {
00279     //if the bot has no goal
00280     if (!trap_BotGetTopGoal(bs->gs, goal)) {
00281         //BotAI_Print(PRT_MESSAGE, "no ltg on stack\n");
00282         bs->ltg_time = 0;
00283     }
00284     //if the bot touches the current goal
00285     else if (BotReachedGoal(bs, goal)) {
00286         BotChooseWeapon(bs);
00287         bs->ltg_time = 0;
00288     }
00289     //if it is time to find a new long term goal
00290     if (bs->ltg_time < FloatTime()) {
00291         //pop the current goal from the stack
00292         trap_BotPopGoal(bs->gs);
00293         //BotAI_Print(PRT_MESSAGE, "%s: choosing new ltg\n", ClientName(bs->client, netname, sizeof(netname)));
00294         //choose a new goal
00295         //BotAI_Print(PRT_MESSAGE, "%6.1f client %d: BotChooseLTGItem\n", FloatTime(), bs->client);
00296         if (trap_BotChooseLTGItem(bs->gs, bs->origin, bs->inventory, tfl)) {
00297             /*
00298             char buf[128];
00299             //get the goal at the top of the stack
00300             trap_BotGetTopGoal(bs->gs, goal);
00301             trap_BotGoalName(goal->number, buf, sizeof(buf));
00302             BotAI_Print(PRT_MESSAGE, "%1.1f: new long term goal %s\n", FloatTime(), buf);
00303             */
00304             bs->ltg_time = FloatTime() + 20;
00305         }
00306         else {//the bot gets sorta stuck with all the avoid timings, shouldn't happen though
00307             //
00308 #ifdef DEBUG
00309             char netname[128];
00310 
00311             BotAI_Print(PRT_MESSAGE, "%s: no valid ltg (probably stuck)\n", ClientName(bs->client, netname, sizeof(netname)));
00312 #endif
00313             //trap_BotDumpAvoidGoals(bs->gs);
00314             //reset the avoid goals and the avoid reach
00315             trap_BotResetAvoidGoals(bs->gs);
00316             trap_BotResetAvoidReach(bs->ms);
00317         }
00318         //get the goal at the top of the stack
00319         return trap_BotGetTopGoal(bs->gs, goal);
00320     }
00321     return qtrue;
00322 }
00323 
00324 /*
00325 ==================
00326 BotGetLongTermGoal
00327 
00328 we could also create a seperate AI node for every long term goal type
00329 however this saves us a lot of code
00330 ==================
00331 */
00332 int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal) {
00333     vec3_t target, dir, dir2;
00334     char netname[MAX_NETNAME];
00335     char buf[MAX_MESSAGE_SIZE];
00336     int areanum;
00337     float croucher;
00338     aas_entityinfo_t entinfo, botinfo;
00339     bot_waypoint_t *wp;
00340 
00341     if (bs->ltgtype == LTG_TEAMHELP && !retreat) {
00342         //check for bot typing status message
00343         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00344             BotAI_BotInitialChat(bs, "help_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00345             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00346             BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
00347             trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
00348             bs->teammessage_time = 0;
00349         }
00350         //if trying to help the team mate for more than a minute
00351         if (bs->teamgoal_time < FloatTime())
00352             bs->ltgtype = 0;
00353         //if the team mate IS visible for quite some time
00354         if (bs->teammatevisible_time < FloatTime() - 10) bs->ltgtype = 0;
00355         //get entity information of the companion
00356         BotEntityInfo(bs->teammate, &entinfo);
00357         //if the team mate is visible
00358         if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate)) {
00359             //if close just stand still there
00360             VectorSubtract(entinfo.origin, bs->origin, dir);
00361             if (VectorLengthSquared(dir) < Square(100)) {
00362                 trap_BotResetAvoidReach(bs->ms);
00363                 return qfalse;
00364             }
00365         }
00366         else {
00367             //last time the bot was NOT visible
00368             bs->teammatevisible_time = FloatTime();
00369         }
00370         //if the entity information is valid (entity in PVS)
00371         if (entinfo.valid) {
00372             areanum = BotPointAreaNum(entinfo.origin);
00373             if (areanum && trap_AAS_AreaReachability(areanum)) {
00374                 //update team goal
00375                 bs->teamgoal.entitynum = bs->teammate;
00376                 bs->teamgoal.areanum = areanum;
00377                 VectorCopy(entinfo.origin, bs->teamgoal.origin);
00378                 VectorSet(bs->teamgoal.mins, -8, -8, -8);
00379                 VectorSet(bs->teamgoal.maxs, 8, 8, 8);
00380             }
00381         }
00382         memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t));
00383         return qtrue;
00384     }
00385     //if the bot accompanies someone
00386     if (bs->ltgtype == LTG_TEAMACCOMPANY && !retreat) {
00387         //check for bot typing status message
00388         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00389             BotAI_BotInitialChat(bs, "accompany_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00390             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00391             BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
00392             trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
00393             bs->teammessage_time = 0;
00394         }
00395         //if accompanying the companion for 3 minutes
00396         if (bs->teamgoal_time < FloatTime()) {
00397             BotAI_BotInitialChat(bs, "accompany_stop", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00398             trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL);
00399             bs->ltgtype = 0;
00400         }
00401         //get entity information of the companion
00402         BotEntityInfo(bs->teammate, &entinfo);
00403         //if the companion is visible
00404         if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate)) {
00405             //update visible time
00406             bs->teammatevisible_time = FloatTime();
00407             VectorSubtract(entinfo.origin, bs->origin, dir);
00408             if (VectorLengthSquared(dir) < Square(bs->formation_dist)) {
00409                 //
00410                 // if the client being followed bumps into this bot then
00411                 // the bot should back up
00412                 BotEntityInfo(bs->entitynum, &botinfo);
00413                 // if the followed client is not standing ontop of the bot
00414                 if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2]) {
00415                     // if the bounding boxes touch each other
00416                     if (botinfo.origin[0] + botinfo.maxs[0] > entinfo.origin[0] + entinfo.mins[0] - 4&&
00417                         botinfo.origin[0] + botinfo.mins[0] < entinfo.origin[0] + entinfo.maxs[0] + 4) {
00418                         if (botinfo.origin[1] + botinfo.maxs[1] > entinfo.origin[1] + entinfo.mins[1] - 4 &&
00419                             botinfo.origin[1] + botinfo.mins[1] < entinfo.origin[1] + entinfo.maxs[1] + 4) {
00420                             if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2] - 4 &&
00421                                 botinfo.origin[2] + botinfo.mins[2] < entinfo.origin[2] + entinfo.maxs[2] + 4) {
00422                                 // if the followed client looks in the direction of this bot
00423                                 AngleVectors(entinfo.angles, dir, NULL, NULL);
00424                                 dir[2] = 0;
00425                                 VectorNormalize(dir);
00426                                 //VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir);
00427                                 VectorSubtract(bs->origin, entinfo.origin, dir2);
00428                                 VectorNormalize(dir2);
00429                                 if (DotProduct(dir, dir2) > 0.7) {
00430                                     // back up
00431                                     BotSetupForMovement(bs);
00432                                     trap_BotMoveInDirection(bs->ms, dir2, 400, MOVE_WALK);
00433                                 }
00434                             }
00435                         }
00436                     }
00437                 }
00438                 //check if the bot wants to crouch
00439                 //don't crouch if crouched less than 5 seconds ago
00440                 if (bs->attackcrouch_time < FloatTime() - 5) {
00441                     croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1);
00442                     if (random() < bs->thinktime * croucher) {
00443                         bs->attackcrouch_time = FloatTime() + 5 + croucher * 15;
00444                     }
00445                 }
00446                 //don't crouch when swimming
00447                 if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1;
00448                 //if not arrived yet or arived some time ago
00449                 if (bs->arrive_time < FloatTime() - 2) {
00450                     //if not arrived yet
00451                     if (!bs->arrive_time) {
00452                         trap_EA_Gesture(bs->client);
00453                         BotAI_BotInitialChat(bs, "accompany_arrive", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00454                         trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL);
00455                         bs->arrive_time = FloatTime();
00456                     }
00457                     //if the bot wants to crouch
00458                     else if (bs->attackcrouch_time > FloatTime()) {
00459                         trap_EA_Crouch(bs->client);
00460                     }
00461                     //else do some model taunts
00462                     else if (random() < bs->thinktime * 0.05) {
00463                         //do a gesture :)
00464                         trap_EA_Gesture(bs->client);
00465                     }
00466                 }
00467                 //if just arrived look at the companion
00468                 if (bs->arrive_time > FloatTime() - 2) {
00469                     VectorSubtract(entinfo.origin, bs->origin, dir);
00470                     vectoangles(dir, bs->ideal_viewangles);
00471                     bs->ideal_viewangles[2] *= 0.5;
00472                 }
00473                 //else look strategically around for enemies
00474                 else if (random() < bs->thinktime * 0.8) {
00475                     BotRoamGoal(bs, target);
00476                     VectorSubtract(target, bs->origin, dir);
00477                     vectoangles(dir, bs->ideal_viewangles);
00478                     bs->ideal_viewangles[2] *= 0.5;
00479                 }
00480                 //check if the bot wants to go for air
00481                 if (BotGoForAir(bs, bs->tfl, &bs->teamgoal, 400)) {
00482                     trap_BotResetLastAvoidReach(bs->ms);
00483                     //get the goal at the top of the stack
00484                     //trap_BotGetTopGoal(bs->gs, &tmpgoal);
00485                     //trap_BotGoalName(tmpgoal.number, buf, 144);
00486                     //BotAI_Print(PRT_MESSAGE, "new nearby goal %s\n", buf);
00487                     //time the bot gets to pick up the nearby goal item
00488                     bs->nbg_time = FloatTime() + 8;
00489                     AIEnter_Seek_NBG(bs, "BotLongTermGoal: go for air");
00490                     return qfalse;
00491                 }
00492                 //
00493                 trap_BotResetAvoidReach(bs->ms);
00494                 return qfalse;
00495             }
00496         }
00497         //if the entity information is valid (entity in PVS)
00498         if (entinfo.valid) {
00499             areanum = BotPointAreaNum(entinfo.origin);
00500             if (areanum && trap_AAS_AreaReachability(areanum)) {
00501                 //update team goal
00502                 bs->teamgoal.entitynum = bs->teammate;
00503                 bs->teamgoal.areanum = areanum;
00504                 VectorCopy(entinfo.origin, bs->teamgoal.origin);
00505                 VectorSet(bs->teamgoal.mins, -8, -8, -8);
00506                 VectorSet(bs->teamgoal.maxs, 8, 8, 8);
00507             }
00508         }
00509         //the goal the bot should go for
00510         memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t));
00511         //if the companion is NOT visible for too long
00512         if (bs->teammatevisible_time < FloatTime() - 60) {
00513             BotAI_BotInitialChat(bs, "accompany_cannotfind", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00514             trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL);
00515             bs->ltgtype = 0;
00516             // just to make sure the bot won't spam this message
00517             bs->teammatevisible_time = FloatTime();
00518         }
00519         return qtrue;
00520     }
00521     //
00522     if (bs->ltgtype == LTG_DEFENDKEYAREA) {
00523         if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin,
00524                 bs->teamgoal.areanum, TFL_DEFAULT) > bs->defendaway_range) {
00525             bs->defendaway_time = 0;
00526         }
00527     }
00528     //if defending a key area
00529     if (bs->ltgtype == LTG_DEFENDKEYAREA && !retreat &&
00530                 bs->defendaway_time < FloatTime()) {
00531         //check for bot typing status message
00532         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00533             trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf));
00534             BotAI_BotInitialChat(bs, "defend_start", buf, NULL);
00535             trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00536             BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE);
00537             bs->teammessage_time = 0;
00538         }
00539         //set the bot goal
00540         memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t));
00541         //stop after 2 minutes
00542         if (bs->teamgoal_time < FloatTime()) {
00543             trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf));
00544             BotAI_BotInitialChat(bs, "defend_stop", buf, NULL);
00545             trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00546             bs->ltgtype = 0;
00547         }
00548         //if very close... go away for some time
00549         VectorSubtract(goal->origin, bs->origin, dir);
00550         if (VectorLengthSquared(dir) < Square(70)) {
00551             trap_BotResetAvoidReach(bs->ms);
00552             bs->defendaway_time = FloatTime() + 3 + 3 * random();
00553             if (BotHasPersistantPowerupAndWeapon(bs)) {
00554                 bs->defendaway_range = 100;
00555             }
00556             else {
00557                 bs->defendaway_range = 350;
00558             }
00559         }
00560         return qtrue;
00561     }
00562     //going to kill someone
00563     if (bs->ltgtype == LTG_KILL && !retreat) {
00564         //check for bot typing status message
00565         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00566             EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf));
00567             BotAI_BotInitialChat(bs, "kill_start", buf, NULL);
00568             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00569             bs->teammessage_time = 0;
00570         }
00571         //
00572         if (bs->lastkilledplayer == bs->teamgoal.entitynum) {
00573             EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf));
00574             BotAI_BotInitialChat(bs, "kill_done", buf, NULL);
00575             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00576             bs->lastkilledplayer = -1;
00577             bs->ltgtype = 0;
00578         }
00579         //
00580         if (bs->teamgoal_time < FloatTime()) {
00581             bs->ltgtype = 0;
00582         }
00583         //just roam around
00584         return BotGetItemLongTermGoal(bs, tfl, goal);
00585     }
00586     //get an item
00587     if (bs->ltgtype == LTG_GETITEM && !retreat) {
00588         //check for bot typing status message
00589         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00590             trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf));
00591             BotAI_BotInitialChat(bs, "getitem_start", buf, NULL);
00592             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00593             BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
00594             trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
00595             bs->teammessage_time = 0;
00596         }
00597         //set the bot goal
00598         memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t));
00599         //stop after some time
00600         if (bs->teamgoal_time < FloatTime()) {
00601             bs->ltgtype = 0;
00602         }
00603         //
00604         if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) {
00605             trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf));
00606             BotAI_BotInitialChat(bs, "getitem_notthere", buf, NULL);
00607             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00608             bs->ltgtype = 0;
00609         }
00610         else if (BotReachedGoal(bs, goal)) {
00611             trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf));
00612             BotAI_BotInitialChat(bs, "getitem_gotit", buf, NULL);
00613             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00614             bs->ltgtype = 0;
00615         }
00616         return qtrue;
00617     }
00618     //if camping somewhere
00619     if ((bs->ltgtype == LTG_CAMP || bs->ltgtype == LTG_CAMPORDER) && !retreat) {
00620         //check for bot typing status message
00621         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00622             if (bs->ltgtype == LTG_CAMPORDER) {
00623                 BotAI_BotInitialChat(bs, "camp_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00624                 trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00625                 BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
00626                 trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
00627             }
00628             bs->teammessage_time = 0;
00629         }
00630         //set the bot goal
00631         memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t));
00632         //
00633         if (bs->teamgoal_time < FloatTime()) {
00634             if (bs->ltgtype == LTG_CAMPORDER) {
00635                 BotAI_BotInitialChat(bs, "camp_stop", NULL);
00636                 trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00637             }
00638             bs->ltgtype = 0;
00639         }
00640         //if really near the camp spot
00641         VectorSubtract(goal->origin, bs->origin, dir);
00642         if (VectorLengthSquared(dir) < Square(60))
00643         {
00644             //if not arrived yet
00645             if (!bs->arrive_time) {
00646                 if (bs->ltgtype == LTG_CAMPORDER) {
00647                     BotAI_BotInitialChat(bs, "camp_arrive", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
00648                     trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00649                     BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_INPOSITION);
00650                 }
00651                 bs->arrive_time = FloatTime();
00652             }
00653             //look strategically around for enemies
00654             if (random() < bs->thinktime * 0.8) {
00655                 BotRoamGoal(bs, target);
00656                 VectorSubtract(target, bs->origin, dir);
00657                 vectoangles(dir, bs->ideal_viewangles);
00658                 bs->ideal_viewangles[2] *= 0.5;
00659             }
00660             //check if the bot wants to crouch
00661             //don't crouch if crouched less than 5 seconds ago
00662             if (bs->attackcrouch_time < FloatTime() - 5) {
00663                 croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1);
00664                 if (random() < bs->thinktime * croucher) {
00665                     bs->attackcrouch_time = FloatTime() + 5 + croucher * 15;
00666                 }
00667             }
00668             //if the bot wants to crouch
00669             if (bs->attackcrouch_time > FloatTime()) {
00670                 trap_EA_Crouch(bs->client);
00671             }
00672             //don't crouch when swimming
00673             if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1;
00674             //make sure the bot is not gonna drown
00675             if (trap_PointContents(bs->eye,bs->entitynum) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) {
00676                 if (bs->ltgtype == LTG_CAMPORDER) {
00677                     BotAI_BotInitialChat(bs, "camp_stop", NULL);
00678                     trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00679                     //
00680                     if (bs->lastgoal_ltgtype == LTG_CAMPORDER) {
00681                         bs->lastgoal_ltgtype = 0;
00682                     }
00683                 }
00684                 bs->ltgtype = 0;
00685             }
00686             //
00687             if (bs->camp_range > 0) {
00688                 //FIXME: move around a bit
00689             }
00690             //
00691             trap_BotResetAvoidReach(bs->ms);
00692             return qfalse;
00693         }
00694         return qtrue;
00695     }
00696     //patrolling along several waypoints
00697     if (bs->ltgtype == LTG_PATROL && !retreat) {
00698         //check for bot typing status message
00699         if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00700             strcpy(buf, "");
00701             for (wp = bs->patrolpoints; wp; wp = wp->next) {
00702                 strcat(buf, wp->name);
00703                 if (wp->next) strcat(buf, " to ");
00704             }
00705             BotAI_BotInitialChat(bs, "patrol_start", buf, NULL);
00706             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00707             BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
00708             trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
00709             bs->teammessage_time = 0;
00710         }
00711         //
00712         if (!bs->curpatrolpoint) {
00713             bs->ltgtype = 0;
00714             return qfalse;
00715         }
00716         //if the bot touches the current goal
00717         if (trap_BotTouchingGoal(bs->origin, &bs->curpatrolpoint->goal)) {
00718             if (bs->patrolflags & PATROL_BACK) {
00719                 if (bs->curpatrolpoint->prev) {
00720                     bs->curpatrolpoint = bs->curpatrolpoint->prev;
00721                 }
00722                 else {
00723                     bs->curpatrolpoint = bs->curpatrolpoint->next;
00724                     bs->patrolflags &= ~PATROL_BACK;
00725                 }
00726             }
00727             else {
00728                 if (bs->curpatrolpoint->next) {
00729                     bs->curpatrolpoint = bs->curpatrolpoint->next;
00730                 }
00731                 else {
00732                     bs->curpatrolpoint = bs->curpatrolpoint->prev;
00733                     bs->patrolflags |= PATROL_BACK;
00734                 }
00735             }
00736         }
00737         //stop after 5 minutes
00738         if (bs->teamgoal_time < FloatTime()) {
00739             BotAI_BotInitialChat(bs, "patrol_stop", NULL);
00740             trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
00741             bs->ltgtype = 0;
00742         }
00743         if (!bs->curpatrolpoint) {
00744             bs->ltgtype = 0;
00745             return qfalse;
00746         }
00747         memcpy(goal, &bs->curpatrolpoint->goal, sizeof(bot_goal_t));
00748         return qtrue;
00749     }
00750 #ifdef CTF
00751     if (gametype == GT_CTF) {
00752         //if going for enemy flag
00753         if (bs->ltgtype == LTG_GETFLAG) {
00754             //check for bot typing status message
00755             if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00756                 BotAI_BotInitialChat(bs, "captureflag_start", NULL);
00757                 trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00758                 BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG);
00759                 bs->teammessage_time = 0;
00760             }
00761             //
00762             switch(BotTeam(bs)) {
00763                 case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break;
00764                 case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break;
00765                 default: bs->ltgtype = 0; return qfalse;
00766             }
00767             //if touching the flag
00768             if (trap_BotTouchingGoal(bs->origin, goal)) {
00769                 // make sure the bot knows the flag isn't there anymore
00770                 switch(BotTeam(bs)) {
00771                     case TEAM_RED: bs->blueflagstatus = 1; break;
00772                     case TEAM_BLUE: bs->redflagstatus = 1; break;
00773                 }
00774                 bs->ltgtype = 0;
00775             }
00776             //stop after 3 minutes
00777             if (bs->teamgoal_time < FloatTime()) {
00778                 bs->ltgtype = 0;
00779             }
00780             BotAlternateRoute(bs, goal);
00781             return qtrue;
00782         }
00783         //if rushing to the base
00784         if (bs->ltgtype == LTG_RUSHBASE && bs->rushbaseaway_time < FloatTime()) {
00785             switch(BotTeam(bs)) {
00786                 case TEAM_RED: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break;
00787                 case TEAM_BLUE: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break;
00788                 default: bs->ltgtype = 0; return qfalse;
00789             }
00790             //if not carrying the flag anymore
00791             if (!BotCTFCarryingFlag(bs)) bs->ltgtype = 0;
00792             //quit rushing after 2 minutes
00793             if (bs->teamgoal_time < FloatTime()) bs->ltgtype = 0;
00794             //if touching the base flag the bot should loose the enemy flag
00795             if (trap_BotTouchingGoal(bs->origin, goal)) {
00796                 //if the bot is still carrying the enemy flag then the
00797                 //base flag is gone, now just walk near the base a bit
00798                 if (BotCTFCarryingFlag(bs)) {
00799                     trap_BotResetAvoidReach(bs->ms);
00800                     bs->rushbaseaway_time = FloatTime() + 5 + 10 * random();
00801                     //FIXME: add chat to tell the others to get back the flag
00802                 }
00803                 else {
00804                     bs->ltgtype = 0;
00805                 }
00806             }
00807             BotAlternateRoute(bs, goal);
00808             return qtrue;
00809         }
00810         //returning flag
00811         if (bs->ltgtype == LTG_RETURNFLAG) {
00812             //check for bot typing status message
00813             if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00814                 BotAI_BotInitialChat(bs, "returnflag_start", NULL);
00815                 trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00816                 BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG);
00817                 bs->teammessage_time = 0;
00818             }
00819             //
00820             switch(BotTeam(bs)) {
00821                 case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break;
00822                 case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break;
00823                 default: bs->ltgtype = 0; return qfalse;
00824             }
00825             //if touching the flag
00826             if (trap_BotTouchingGoal(bs->origin, goal)) bs->ltgtype = 0;
00827             //stop after 3 minutes
00828             if (bs->teamgoal_time < FloatTime()) {
00829                 bs->ltgtype = 0;
00830             }
00831             BotAlternateRoute(bs, goal);
00832             return qtrue;
00833         }
00834     }
00835 #endif //CTF
00836 #ifdef MISSIONPACK
00837     else if (gametype == GT_1FCTF) {
00838         if (bs->ltgtype == LTG_GETFLAG) {
00839             //check for bot typing status message
00840             if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00841                 BotAI_BotInitialChat(bs, "captureflag_start", NULL);
00842                 trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00843                 BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG);
00844                 bs->teammessage_time = 0;
00845             }
00846             memcpy(goal, &ctf_neutralflag, sizeof(bot_goal_t));
00847             //if touching the flag
00848             if (trap_BotTouchingGoal(bs->origin, goal)) {
00849                 bs->ltgtype = 0;
00850             }
00851             //stop after 3 minutes
00852             if (bs->teamgoal_time < FloatTime()) {
00853                 bs->ltgtype = 0;
00854             }
00855             return qtrue;
00856         }
00857         //if rushing to the base
00858         if (bs->ltgtype == LTG_RUSHBASE) {
00859             switch(BotTeam(bs)) {
00860                 case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break;
00861                 case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break;
00862                 default: bs->ltgtype = 0; return qfalse;
00863             }
00864             //if not carrying the flag anymore
00865             if (!Bot1FCTFCarryingFlag(bs)) {
00866                 bs->ltgtype = 0;
00867             }
00868             //quit rushing after 2 minutes
00869             if (bs->teamgoal_time < FloatTime()) {
00870                 bs->ltgtype = 0;
00871             }
00872             //if touching the base flag the bot should loose the enemy flag
00873             if (trap_BotTouchingGoal(bs->origin, goal)) {
00874                 bs->ltgtype = 0;
00875             }
00876             BotAlternateRoute(bs, goal);
00877             return qtrue;
00878         }
00879         //attack the enemy base
00880         if (bs->ltgtype == LTG_ATTACKENEMYBASE &&
00881                 bs->attackaway_time < FloatTime()) {
00882             //check for bot typing status message
00883             if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00884                 BotAI_BotInitialChat(bs, "attackenemybase_start", NULL);
00885                 trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00886                 BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE);
00887                 bs->teammessage_time = 0;
00888             }
00889             switch(BotTeam(bs)) {
00890                 case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break;
00891                 case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break;
00892                 default: bs->ltgtype = 0; return qfalse;
00893             }
00894             //quit rushing after 2 minutes
00895             if (bs->teamgoal_time < FloatTime()) {
00896                 bs->ltgtype = 0;
00897             }
00898             //if touching the base flag the bot should loose the enemy flag
00899             if (trap_BotTouchingGoal(bs->origin, goal)) {
00900                 bs->attackaway_time = FloatTime() + 2 + 5 * random();
00901             }
00902             return qtrue;
00903         }
00904         //returning flag
00905         if (bs->ltgtype == LTG_RETURNFLAG) {
00906             //check for bot typing status message
00907             if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00908                 BotAI_BotInitialChat(bs, "returnflag_start", NULL);
00909                 trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00910                 BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG);
00911                 bs->teammessage_time = 0;
00912             }
00913             //
00914             if (bs->teamgoal_time < FloatTime()) {
00915                 bs->ltgtype = 0;
00916             }
00917             //just roam around
00918             return BotGetItemLongTermGoal(bs, tfl, goal);
00919         }
00920     }
00921     else if (gametype == GT_OBELISK) {
00922         if (bs->ltgtype == LTG_ATTACKENEMYBASE &&
00923                 bs->attackaway_time < FloatTime()) {
00924 
00925             //check for bot typing status message
00926             if (bs->teammessage_time && bs->teammessage_time < FloatTime()) {
00927                 BotAI_BotInitialChat(bs, "attackenemybase_start", NULL);
00928                 trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
00929                 BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE);
00930                 bs->teammessage_time = 0;
00931             }
00932             switch(BotTeam(bs)) {
00933                 case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break;
00934                 case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break;
00935                 default: bs->ltgtype = 0; return qfalse;
00936             }
00937             //if the bot no longer wants to attack the obelisk
00938             if (BotFeelingBad(bs) > 50) {
00939                 return BotGetItemLongTermGoal(bs, tfl, goal);
00940             }
00941             //if touching the obelisk
00942             if (trap_BotTouchingGoal(bs->origin, goal)) {
00943                 bs->attackaway_time = FloatTime() + 3 + 5 * random();
00944             }
00945             // or very close to the obelisk
00946             VectorSubtract(bs->origin, goal->origin, dir);
00947             if (VectorLengthSquared(dir) < Square(60)) {
00948                 bs->attackaway_time = FloatTime() + 3 + 5 * random();
00949             }
00950             //quit rushing after 2 minutes
00951             if (bs->teamgoal_time < FloatTime()) {
00952                 bs->ltgtype = 0;
00953             }
00954             BotAlternateRoute(bs, goal);
00955             //just move towards the obelisk
00956             return qtrue;
00957         }
00958     }
00959     else if (gametype == GT_HARVESTER) {
00960         //if rushing to the base
00961         if (bs->ltgtype == LTG_RUSHBASE) {
00962             switch(BotTeam(bs)) {
00963                 case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break;
00964                 case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break;
00965                 default: BotGoHarvest(bs); return qfalse;
00966             }
00967             //if not carrying any cubes
00968             if (!BotHarvesterCarryingCubes(bs)) {
00969                 BotGoHarvest(bs);
00970                 return qfalse;
00971             }
00972             //quit rushing after 2 minutes
00973             if (bs->teamgoal_time < FloatTime()) {
00974                 BotGoHarvest(bs);
00975                 return qfalse;
00976             }
00977             //if touching the base flag the bot should loose the enemy flag
00978             if (trap_BotTouchingGoal(bs->origin, goal)) {
00979                 BotGoHarvest(bs);
00980                 return qfalse;
00981             }
00982             BotAlternateRoute(bs, goal);
00983             return qtrue;
00984         }
00985         //attack the enemy base
00986         if (bs->ltgtype ==