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

be_ai_move.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  * name:        be_ai_move.c
00025  *
00026  * desc:        bot movement AI
00027  *
00028  * $Archive: /MissionPack/code/botlib/be_ai_move.c $
00029  *
00030  *****************************************************************************/
00031 
00032 #include "../game/q_shared.h"
00033 #include "l_memory.h"
00034 #include "l_libvar.h"
00035 #include "l_utils.h"
00036 #include "l_script.h"
00037 #include "l_precomp.h"
00038 #include "l_struct.h"
00039 #include "aasfile.h"
00040 #include "../game/botlib.h"
00041 #include "../game/be_aas.h"
00042 #include "be_aas_funcs.h"
00043 #include "be_interface.h"
00044 
00045 #include "../game/be_ea.h"
00046 #include "../game/be_ai_goal.h"
00047 #include "../game/be_ai_move.h"
00048 
00049 
00050 //#define DEBUG_AI_MOVE
00051 //#define DEBUG_ELEVATOR
00052 //#define DEBUG_GRAPPLE
00053 
00054 // bk001204 - redundant bot_avoidspot_t, see ../game/be_ai_move.h
00055 
00056 //movement state
00057 //NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and
00058 //      MFL_GRAPPLEPULL must be set outside the movement code
00059 typedef struct bot_movestate_s
00060 {
00061     //input vars (all set outside the movement code)
00062     vec3_t origin;                              //origin of the bot
00063     vec3_t velocity;                            //velocity of the bot
00064     vec3_t viewoffset;                          //view offset
00065     int entitynum;                              //entity number of the bot
00066     int client;                                 //client number of the bot
00067     float thinktime;                            //time the bot thinks
00068     int presencetype;                           //presencetype of the bot
00069     vec3_t viewangles;                          //view angles of the bot
00070     //state vars
00071     int areanum;                                //area the bot is in
00072     int lastareanum;                            //last area the bot was in
00073     int lastgoalareanum;                        //last goal area number
00074     int lastreachnum;                           //last reachability number
00075     vec3_t lastorigin;                          //origin previous cycle
00076     int reachareanum;                           //area number of the reachabilty
00077     int moveflags;                              //movement flags
00078     int jumpreach;                              //set when jumped
00079     float grapplevisible_time;                  //last time the grapple was visible
00080     float lastgrappledist;                      //last distance to the grapple end
00081     float reachability_time;                    //time to use current reachability
00082     int avoidreach[MAX_AVOIDREACH];             //reachabilities to avoid
00083     float avoidreachtimes[MAX_AVOIDREACH];      //times to avoid the reachabilities
00084     int avoidreachtries[MAX_AVOIDREACH];        //number of tries before avoiding
00085     //
00086     bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid
00087     int numavoidspots;
00088 } bot_movestate_t;
00089 
00090 //used to avoid reachability links for some time after being used
00091 #define AVOIDREACH
00092 #define AVOIDREACH_TIME         6       //avoid links for 6 seconds after use
00093 #define AVOIDREACH_TRIES        4
00094 //prediction times
00095 #define PREDICTIONTIME_JUMP 3       //in seconds
00096 #define PREDICTIONTIME_MOVE 2       //in seconds
00097 //weapon indexes for weapon jumping
00098 #define WEAPONINDEX_ROCKET_LAUNCHER     5
00099 #define WEAPONINDEX_BFG                 9
00100 
00101 #define MODELTYPE_FUNC_PLAT     1
00102 #define MODELTYPE_FUNC_BOB      2
00103 #define MODELTYPE_FUNC_DOOR     3
00104 #define MODELTYPE_FUNC_STATIC   4
00105 
00106 libvar_t *sv_maxstep;
00107 libvar_t *sv_maxbarrier;
00108 libvar_t *sv_gravity;
00109 libvar_t *weapindex_rocketlauncher;
00110 libvar_t *weapindex_bfg10k;
00111 libvar_t *weapindex_grapple;
00112 libvar_t *entitytypemissile;
00113 libvar_t *offhandgrapple;
00114 libvar_t *cmd_grappleoff;
00115 libvar_t *cmd_grappleon;
00116 //type of model, func_plat or func_bobbing
00117 int modeltypes[MAX_MODELS];
00118 
00119 bot_movestate_t *botmovestates[MAX_CLIENTS+1];
00120 
00121 //========================================================================
00122 //
00123 // Parameter:           -
00124 // Returns:             -
00125 // Changes Globals:     -
00126 //========================================================================
00127 int BotAllocMoveState(void)
00128 {
00129     int i;
00130 
00131     for (i = 1; i <= MAX_CLIENTS; i++)
00132     {
00133         if (!botmovestates[i])
00134         {
00135             botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t));
00136             return i;
00137         } //end if
00138     } //end for
00139     return 0;
00140 } //end of the function BotAllocMoveState
00141 //========================================================================
00142 //
00143 // Parameter:           -
00144 // Returns:             -
00145 // Changes Globals:     -
00146 //========================================================================
00147 void BotFreeMoveState(int handle)
00148 {
00149     if (handle <= 0 || handle > MAX_CLIENTS)
00150     {
00151         botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
00152         return;
00153     } //end if
00154     if (!botmovestates[handle])
00155     {
00156         botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
00157         return;
00158     } //end if
00159     FreeMemory(botmovestates[handle]);
00160     botmovestates[handle] = NULL;
00161 } //end of the function BotFreeMoveState
00162 //========================================================================
00163 //
00164 // Parameter:               -
00165 // Returns:                 -
00166 // Changes Globals:     -
00167 //========================================================================
00168 bot_movestate_t *BotMoveStateFromHandle(int handle)
00169 {
00170     if (handle <= 0 || handle > MAX_CLIENTS)
00171     {
00172         botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
00173         return NULL;
00174     } //end if
00175     if (!botmovestates[handle])
00176     {
00177         botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
00178         return NULL;
00179     } //end if
00180     return botmovestates[handle];
00181 } //end of the function BotMoveStateFromHandle
00182 //========================================================================
00183 //
00184 // Parameter:           -
00185 // Returns:             -
00186 // Changes Globals:     -
00187 //========================================================================
00188 void BotInitMoveState(int handle, bot_initmove_t *initmove)
00189 {
00190     bot_movestate_t *ms;
00191 
00192     ms = BotMoveStateFromHandle(handle);
00193     if (!ms) return;
00194     VectorCopy(initmove->origin, ms->origin);
00195     VectorCopy(initmove->velocity, ms->velocity);
00196     VectorCopy(initmove->viewoffset, ms->viewoffset);
00197     ms->entitynum = initmove->entitynum;
00198     ms->client = initmove->client;
00199     ms->thinktime = initmove->thinktime;
00200     ms->presencetype = initmove->presencetype;
00201     VectorCopy(initmove->viewangles, ms->viewangles);
00202     //
00203     ms->moveflags &= ~MFL_ONGROUND;
00204     if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND;
00205     ms->moveflags &= ~MFL_TELEPORTED;   
00206     if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED;
00207     ms->moveflags &= ~MFL_WATERJUMP;
00208     if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP;
00209     ms->moveflags &= ~MFL_WALK;
00210     if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK;
00211     ms->moveflags &= ~MFL_GRAPPLEPULL;
00212     if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL;
00213 } //end of the function BotInitMoveState
00214 //========================================================================
00215 //
00216 // Parameter:           -
00217 // Returns:             -
00218 // Changes Globals:     -
00219 //========================================================================
00220 float AngleDiff(float ang1, float ang2)
00221 {
00222     float diff;
00223 
00224     diff = ang1 - ang2;
00225     if (ang1 > ang2)
00226     {
00227         if (diff > 180.0) diff -= 360.0;
00228     } //end if
00229     else
00230     {
00231         if (diff < -180.0) diff += 360.0;
00232     } //end else
00233     return diff;
00234 } //end of the function AngleDiff
00235 //===========================================================================
00236 //
00237 // Parameter:           -
00238 // Returns:             -
00239 // Changes Globals:     -
00240 //===========================================================================
00241 int BotFuzzyPointReachabilityArea(vec3_t origin)
00242 {
00243     int firstareanum, j, x, y, z;
00244     int areas[10], numareas, areanum, bestareanum;
00245     float dist, bestdist;
00246     vec3_t points[10], v, end;
00247 
00248     firstareanum = 0;
00249     areanum = AAS_PointAreaNum(origin);
00250     if (areanum)
00251     {
00252         firstareanum = areanum;
00253         if (AAS_AreaReachability(areanum)) return areanum;
00254     } //end if
00255     VectorCopy(origin, end);
00256     end[2] += 4;
00257     numareas = AAS_TraceAreas(origin, end, areas, points, 10);
00258     for (j = 0; j < numareas; j++)
00259     {
00260         if (AAS_AreaReachability(areas[j])) return areas[j];
00261     } //end for
00262     bestdist = 999999;
00263     bestareanum = 0;
00264     for (z = 1; z >= -1; z -= 1)
00265     {
00266         for (x = 1; x >= -1; x -= 1)
00267         {
00268             for (y = 1; y >= -1; y -= 1)
00269             {
00270                 VectorCopy(origin, end);
00271                 end[0] += x * 8;
00272                 end[1] += y * 8;
00273                 end[2] += z * 12;
00274                 numareas = AAS_TraceAreas(origin, end, areas, points, 10);
00275                 for (j = 0; j < numareas; j++)
00276                 {
00277                     if (AAS_AreaReachability(areas[j]))
00278                     {
00279                         VectorSubtract(points[j], origin, v);
00280                         dist = VectorLength(v);
00281                         if (dist < bestdist)
00282                         {
00283                             bestareanum = areas[j];
00284                             bestdist = dist;
00285                         } //end if
00286                     } //end if
00287                     if (!firstareanum) firstareanum = areas[j];
00288                 } //end for
00289             } //end for
00290         } //end for
00291         if (bestareanum) return bestareanum;
00292     } //end for
00293     return firstareanum;
00294 } //end of the function BotFuzzyPointReachabilityArea
00295 //===========================================================================
00296 //
00297 // Parameter:           -
00298 // Returns:             -
00299 // Changes Globals:     -
00300 //===========================================================================
00301 int BotReachabilityArea(vec3_t origin, int client)
00302 {
00303     int modelnum, modeltype, reachnum, areanum;
00304     aas_reachability_t reach;
00305     vec3_t org, end, mins, maxs, up = {0, 0, 1};
00306     bsp_trace_t bsptrace;
00307     aas_trace_t trace;
00308 
00309     //check if the bot is standing on something
00310     AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
00311     VectorMA(origin, -3, up, end);
00312     bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00313     if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE)
00314     {
00315         //if standing on the world the bot should be in a valid area
00316         if (bsptrace.ent == ENTITYNUM_WORLD)
00317         {
00318             return BotFuzzyPointReachabilityArea(origin);
00319         } //end if
00320 
00321         modelnum = AAS_EntityModelindex(bsptrace.ent);
00322         modeltype = modeltypes[modelnum];
00323 
00324         //if standing on a func_plat or func_bobbing then the bot is assumed to be
00325         //in the area the reachability points to
00326         if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB)
00327         {
00328             reachnum = AAS_NextModelReachability(0, modelnum);
00329             if (reachnum)
00330             {
00331                 AAS_ReachabilityFromNum(reachnum, &reach);
00332                 return reach.areanum;
00333             } //end if
00334         } //end else if
00335 
00336         //if the bot is swimming the bot should be in a valid area
00337         if (AAS_Swimming(origin))
00338         {
00339             return BotFuzzyPointReachabilityArea(origin);
00340         } //end if
00341         //
00342         areanum = BotFuzzyPointReachabilityArea(origin);
00343         //if the bot is in an area with reachabilities
00344         if (areanum && AAS_AreaReachability(areanum)) return areanum;
00345         //trace down till the ground is hit because the bot is standing on some other entity
00346         VectorCopy(origin, org);
00347         VectorCopy(org, end);
00348         end[2] -= 800;
00349         trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1);
00350         if (!trace.startsolid)
00351         {
00352             VectorCopy(trace.endpos, org);
00353         } //end if
00354         //
00355         return BotFuzzyPointReachabilityArea(org);
00356     } //end if
00357     //
00358     return BotFuzzyPointReachabilityArea(origin);
00359 } //end of the function BotReachabilityArea
00360 //===========================================================================
00361 // returns the reachability area the bot is in
00362 //
00363 // Parameter:               -
00364 // Returns:                 -
00365 // Changes Globals:     -
00366 //===========================================================================
00367 /*
00368 int BotReachabilityArea(vec3_t origin, int testground)
00369 {
00370     int firstareanum, i, j, x, y, z;
00371     int areas[10], numareas, areanum, bestareanum;
00372     float dist, bestdist;
00373     vec3_t org, end, points[10], v;
00374     aas_trace_t trace;
00375 
00376     firstareanum = 0;
00377     for (i = 0; i < 2; i++)
00378     {
00379         VectorCopy(origin, org);
00380         //if test at the ground (used when bot is standing on an entity)
00381         if (i > 0)
00382         {
00383             VectorCopy(origin, end);
00384             end[2] -= 800;
00385             trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);
00386             if (!trace.startsolid)
00387             {
00388                 VectorCopy(trace.endpos, org);
00389             } //end if
00390         } //end if
00391 
00392         firstareanum = 0;
00393         areanum = AAS_PointAreaNum(org);
00394         if (areanum)
00395         {
00396             firstareanum = areanum;
00397             if (AAS_AreaReachability(areanum)) return areanum;
00398         } //end if
00399         bestdist = 999999;
00400         bestareanum = 0;
00401         for (z = 1; z >= -1; z -= 1)
00402         {
00403             for (x = 1; x >= -1; x -= 1)
00404             {
00405                 for (y = 1; y >= -1; y -= 1)
00406                 {
00407                     VectorCopy(org, end);
00408                     end[0] += x * 8;
00409                     end[1] += y * 8;
00410                     end[2] += z * 12;
00411                     numareas = AAS_TraceAreas(org, end, areas, points, 10);
00412                     for (j = 0; j < numareas; j++)
00413                     {
00414                         if (AAS_AreaReachability(areas[j]))
00415                         {
00416                             VectorSubtract(points[j], org, v);
00417                             dist = VectorLength(v);
00418                             if (dist < bestdist)
00419                             {
00420                                 bestareanum = areas[j];
00421                                 bestdist = dist;
00422                             } //end if
00423                         } //end if
00424                     } //end for
00425                 } //end for
00426             } //end for
00427             if (bestareanum) return bestareanum;
00428         } //end for
00429         if (!testground) break;
00430     } //end for
00431 //#ifdef DEBUG
00432     //botimport.Print(PRT_MESSAGE, "no reachability area\n");
00433 //#endif //DEBUG
00434     return firstareanum;
00435 } //end of the function BotReachabilityArea*/
00436 //===========================================================================
00437 //
00438 // Parameter:           -
00439 // Returns:             -
00440 // Changes Globals:     -
00441 //===========================================================================
00442 int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach)
00443 {
00444     int i, modelnum;
00445     vec3_t mins, maxs, modelorigin, org, end;
00446     vec3_t angles = {0, 0, 0};
00447     vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8};
00448     bsp_trace_t trace;
00449 
00450     modelnum = reach->facenum & 0x0000FFFF;
00451     //get some bsp model info
00452     AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
00453     //
00454     if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin))
00455     {
00456         botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
00457         return qfalse;
00458     } //end if
00459     //
00460     for (i = 0; i < 2; i++)
00461     {
00462         if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse;
00463         if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse;
00464     } //end for
00465     //
00466     VectorCopy(origin, org);
00467     org[2] += 24;
00468     VectorCopy(origin, end);
00469     end[2] -= 48;
00470     //
00471     trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00472     if (!trace.startsolid && !trace.allsolid)
00473     {
00474         //NOTE: the reachability face number is the model number of the elevator
00475         if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum)
00476         {
00477             return qtrue;
00478         } //end if
00479     } //end if
00480     return qfalse;
00481 } //end of the function BotOnMover
00482 //===========================================================================
00483 //
00484 // Parameter:           -
00485 // Returns:             -
00486 // Changes Globals:     -
00487 //===========================================================================
00488 int MoverDown(aas_reachability_t *reach)
00489 {
00490     int modelnum;
00491     vec3_t mins, maxs, origin;
00492     vec3_t angles = {0, 0, 0};
00493 
00494     modelnum = reach->facenum & 0x0000FFFF;
00495     //get some bsp model info
00496     AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
00497     //
00498     if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
00499     {
00500         botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
00501         return qfalse;
00502     } //end if
00503     //if the top of the plat is below the reachability start point
00504     if (origin[2] + maxs[2] < reach->start[2]) return qtrue;
00505     return qfalse;
00506 } //end of the function MoverDown
00507 //========================================================================
00508 //
00509 // Parameter:           -
00510 // Returns:             -
00511 // Changes Globals:     -
00512 //========================================================================
00513 void BotSetBrushModelTypes(void)
00514 {
00515     int ent, modelnum;
00516     char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
00517 
00518     Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int));
00519     //
00520     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
00521     {
00522         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
00523         if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue;
00524         if (model[0]) modelnum = atoi(model+1);
00525         else modelnum = 0;
00526 
00527         if (modelnum < 0 || modelnum > MAX_MODELS)
00528         {
00529             botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname);
00530             continue;
00531         } //end if
00532 
00533         if (!Q_stricmp(classname, "func_bobbing"))
00534             modeltypes[modelnum] = MODELTYPE_FUNC_BOB;
00535         else if (!Q_stricmp(classname, "func_plat"))
00536             modeltypes[modelnum] = MODELTYPE_FUNC_PLAT;
00537         else if (!Q_stricmp(classname, "func_door"))
00538             modeltypes[modelnum] = MODELTYPE_FUNC_DOOR;
00539         else if (!Q_stricmp(classname, "func_static"))
00540             modeltypes[modelnum] = MODELTYPE_FUNC_STATIC;
00541     } //end for
00542 } //end of the function BotSetBrushModelTypes
00543 //===========================================================================
00544 //
00545 // Parameter:           -
00546 // Returns:             -
00547 // Changes Globals:     -
00548 //===========================================================================
00549 int BotOnTopOfEntity(bot_movestate_t *ms)
00550 {
00551     vec3_t mins, maxs, end, up = {0, 0, 1};
00552     bsp_trace_t trace;
00553 
00554     AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
00555     VectorMA(ms->origin, -3, up, end);
00556     trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00557     if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
00558     {
00559         return trace.ent;
00560     } //end if
00561     return -1;
00562 } //end of the function BotOnTopOfEntity
00563 //===========================================================================
00564 //
00565 // Parameter:           -
00566 // Returns:             -
00567 // Changes Globals:     -
00568 //===========================================================================
00569 int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags)
00570 {
00571     //if the reachability uses an unwanted travel type
00572     if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse;
00573     //don't go into areas with bad travel types
00574     if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse;
00575     return qtrue;
00576 } //end of the function BotValidTravel
00577 //===========================================================================
00578 //
00579 // Parameter:               -
00580 // Returns:                 -
00581 // Changes Globals:     -
00582 //===========================================================================
00583 void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime)
00584 {
00585     int i;
00586 
00587     for (i = 0; i < MAX_AVOIDREACH; i++)
00588     {
00589         if (ms->avoidreach[i] == number)
00590         {
00591             if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++;
00592             else ms->avoidreachtries[i] = 1;
00593             ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
00594             return;
00595         } //end if
00596     } //end for
00597     //add the reachability to the reachabilities to avoid for a while
00598     for (i = 0; i < MAX_AVOIDREACH; i++)
00599     {
00600         if (ms->avoidreachtimes[i] < AAS_Time())
00601         {
00602             ms->avoidreach[i] = number;
00603             ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
00604             ms->avoidreachtries[i] = 1;
00605             return;
00606         } //end if
00607     } //end for
00608 } //end of the function BotAddToAvoidReach
00609 //===========================================================================
00610 //
00611 // Parameter:           -
00612 // Returns:             -
00613 // Changes Globals:     -
00614 //===========================================================================
00615 float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2)
00616 {
00617     vec3_t proj, dir;
00618     int j;
00619 
00620     AAS_ProjectPointOntoVector(p, lp1, lp2, proj);
00621     for (j = 0; j < 3; j++)
00622         if ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||
00623             (proj[j] < lp1[j] && proj[j] < lp2[j]))
00624             break;
00625     if (j < 3) {
00626         if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
00627             VectorSubtract(p, lp1, dir);
00628         else
00629             VectorSubtract(p, lp2, dir);
00630         return VectorLengthSquared(dir);
00631     }
00632     VectorSubtract(p, proj, dir);
00633     return VectorLengthSquared(dir);
00634 } //end of the function DistanceFromLineSquared
00635 //===========================================================================
00636 //
00637 // Parameter:           -
00638 // Returns:             -
00639 // Changes Globals:     -
00640 //===========================================================================
00641 float VectorDistanceSquared(vec3_t p1, vec3_t p2)
00642 {
00643     vec3_t dir;
00644     VectorSubtract(p2, p1, dir);
00645     return VectorLengthSquared(dir);
00646 } //end of the function VectorDistanceSquared
00647 //===========================================================================
00648 //
00649 // Parameter:           -
00650 // Returns:             -
00651 // Changes Globals:     -
00652 //===========================================================================
00653 int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots)
00654 {
00655     int checkbetween, i, type;
00656     float squareddist, squaredradius;
00657 
00658     switch(reach->traveltype & TRAVELTYPE_MASK)
00659     {
00660         case TRAVEL_WALK: checkbetween = qtrue; break;
00661         case TRAVEL_CROUCH: checkbetween = qtrue; break;
00662         case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break;
00663         case TRAVEL_LADDER: checkbetween = qtrue; break;
00664         case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break;
00665         case TRAVEL_JUMP: checkbetween = qfalse; break;
00666         case TRAVEL_SWIM: checkbetween = qtrue; break;
00667         case TRAVEL_WATERJUMP: checkbetween = qtrue; break;
00668         case TRAVEL_TELEPORT: checkbetween = qfalse; break;
00669         case TRAVEL_ELEVATOR: checkbetween = qfalse; break;
00670         case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break;
00671         case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break;
00672         case TRAVEL_BFGJUMP: checkbetween = qfalse; break;
00673         case TRAVEL_JUMPPAD: checkbetween = qfalse; break;
00674         case TRAVEL_FUNCBOB: checkbetween = qfalse; break;
00675         default: checkbetween = qtrue; break;
00676     } //end switch
00677 
00678     type = AVOID_CLEAR;
00679     for (i = 0; i < numavoidspots; i++)
00680     {
00681         squaredradius = Square(avoidspots[i].radius);
00682         squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start);
00683         // if moving towards the avoid spot
00684         if (squareddist < squaredradius &&
00685             VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist)
00686         {
00687             type = avoidspots[i].type;
00688         } //end if
00689         else if (checkbetween) {
00690             squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end);
00691             // if moving towards the avoid spot
00692             if (squareddist < squaredradius &&
00693                 VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
00694             {
00695                 type = avoidspots[i].type;
00696             } //end if
00697         } //end if
00698         else
00699         {
00700             VectorDistanceSquared(avoidspots[i].origin, reach->end);
00701             // if the reachability leads closer to the avoid spot
00702             if (squareddist < squaredradius && 
00703                 VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
00704             {
00705                 type = avoidspots[i].type;
00706             } //end if
00707         } //end else
00708         if (type == AVOID_ALWAYS)
00709             return type;
00710     } //end for
00711     return type;
00712 } //end of the function BotAvoidSpots
00713 //===========================================================================
00714 //
00715 // Parameter:           -
00716 // Returns:             -
00717 // Changes Globals:     -
00718 //===========================================================================
00719 void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type)
00720 {
00721     bot_movestate_t *ms;
00722 
00723     ms = BotMoveStateFromHandle(movestate);
00724     if (!ms) return;
00725     if (type == AVOID_CLEAR)
00726     {
00727         ms->numavoidspots = 0;
00728         return;
00729     } //end if
00730 
00731     if (ms->numavoidspots >= MAX_AVOIDSPOTS)
00732         return;
00733     VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin);
00734     ms->avoidspots[ms->numavoidspots].radius = radius;
00735     ms->avoidspots[ms->numavoidspots].type = type;
00736     ms->numavoidspots++;
00737 } //end of the function BotAddAvoidSpot
00738 //===========================================================================
00739 //
00740 // Parameter:           -
00741 // Returns:             -
00742 // Changes Globals:     -
00743 //===========================================================================
00744 int BotGetReachabilityToGoal(vec3_t origin, int areanum,
00745                                       int lastgoalareanum, int lastareanum,
00746                                       int *avoidreach, float *avoidreachtimes, int *avoidreachtries,
00747                                       bot_goal_t *goal, int travelflags, int movetravelflags,
00748                                       struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags)
00749 {
00750     int i, t, besttime, bestreachnum, reachnum;
00751     aas_reachability_t reach;
00752 
00753     //if not in a valid area
00754     if (!areanum) return 0;
00755     //
00756     if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum))
00757     {
00758         travelflags |= TFL_DONOTENTER;
00759         movetravelflags |= TFL_DONOTENTER;
00760     } //end if
00761     //use the routing to find the next area to go to
00762     besttime = 0;
00763     bestreachnum = 0;
00764     //
00765     for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum;
00766         reachnum = AAS_NextAreaReachability(areanum, reachnum))
00767     {
00768 #ifdef AVOIDREACH
00769         //check if it isn't an reachability to avoid
00770         for (i = 0; i < MAX_AVOIDREACH; i++)
00771         {
00772             if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break;
00773         } //end for
00774         if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES)
00775         {
00776 #ifdef DEBUG
00777             if (bot_developer)
00778             {
00779                 botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]);
00780             } //end if
00781 #endif //DEBUG
00782             continue;
00783         } //end if
00784 #endif //AVOIDREACH
00785         //get the reachability from the number
00786         AAS_ReachabilityFromNum(reachnum, &reach);
00787         //NOTE: do not go back to the previous area if the goal didn't change
00788         //NOTE: is this actually avoidance of local routing minima between two areas???
00789         if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue;
00790         //if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue;
00791         //if the travel isn't valid
00792         if (!BotValidTravel(origin, &reach, movetravelflags)) continue;
00793         //get the travel time
00794         t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags);
00795         //if the goal area isn't reachable from the reachable area
00796         if (!t) continue;
00797         //if the bot should not use this reachability to avoid bad spots
00798         if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) {
00799             if (flags) {
00800                 *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT;
00801             }
00802             continue;
00803         }
00804         //add the travel time towards the area
00805         t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start);
00806         //if the travel time is better than the ones already found
00807         if (!besttime || t < besttime)
00808         {
00809             besttime = t;
00810             bestreachnum = reachnum;
00811         } //end if
00812     } //end for
00813     //
00814     return bestreachnum;
00815 } //end of the function BotGetReachabilityToGoal
00816 //===========================================================================
00817 //
00818 // Parameter:               -
00819 // Returns:                 -
00820 // Changes Globals:     -
00821 //===========================================================================
00822 int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target)
00823 {
00824     vec3_t dir;
00825     float curdist;
00826 
00827     VectorSubtract(end, start, dir);
00828     curdist = VectorNormalize(dir);
00829     if (*dist + curdist < maxdist)
00830     {
00831         VectorCopy(end, target);
00832         *dist += curdist;
00833         return qfalse;
00834     } //end if
00835     else
00836     {
00837         VectorMA(start, maxdist - *dist, dir, target);
00838         *dist = maxdist;
00839         return qtrue;
00840     } //end else
00841 } //end of the function BotAddToTarget
00842 
00843 int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target)
00844 {
00845     aas_reachability_t reach;
00846     int reachnum, lastareanum;
00847     bot_movestate_t *ms;
00848     vec3_t end;
00849     float dist;
00850 
00851     ms = BotMoveStateFromHandle(movestate);
00852     if (!ms) return qfalse;
00853     reachnum = 0;
00854     //if the bot has no goal or no last reachability
00855     if (!ms->lastreachnum || !goal) return qfalse;
00856 
00857     reachnum = ms->lastreachnum;
00858     VectorCopy(ms->origin, end);
00859     lastareanum = ms->lastareanum;
00860     dist = 0;
00861     while(reachnum && dist < lookahead)
00862     {
00863         AAS_ReachabilityFromNum(reachnum, &reach);
00864         if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue;
00865         //never look beyond teleporters
00866         if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue;
00867         //never look beyond the weapon jump point
00868         if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue;
00869         if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue;
00870         //don't add jump pad distances
00871         if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD &&
00872             (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR &&
00873             (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB)
00874         {
00875             if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue;
00876         } //end if
00877         reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum,
00878                         ms->lastgoalareanum, lastareanum,
00879                             ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
00880                                     goal, travelflags, travelflags, NULL, 0, NULL);
00881         VectorCopy(reach.end, end);
00882         lastareanum = reach.areanum;
00883         if (lastareanum == goal->areanum)
00884         {
00885             BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target);
00886             return qtrue;
00887         } //end if
00888     } //end while
00889     //
00890     return qfalse;
00891 } //end of the function BotMovementViewTarget
00892 //===========================================================================
00893 //
00894 // Parameter:           -
00895 // Returns:             -
00896 // Changes Globals:     -
00897 //===========================================================================
00898 int BotVisible(int ent, vec3_t eye, vec3_t target)
00899 {
00900     bsp_trace_t trace;
00901 
00902     trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00903     if (trace.fraction >= 1) return qtrue;
00904     return qfalse;
00905 } //end of the function BotVisible
00906 //===========================================================================
00907 //
00908 // Parameter:           -
00909 // Returns:             -
00910 // Changes Globals:     -
00911 //===========================================================================
00912 int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target)
00913 {
00914     aas_reachability_t reach;
00915     int reachnum, lastgoalareanum, lastareanum, i;
00916     int avoidreach[MAX_AVOIDREACH];
00917     float avoidreachtimes[MAX_AVOIDREACH];
00918     int avoidreachtries[MAX_AVOIDREACH];
00919     vec3_t end;
00920 
00921     //if the bot has no goal or no last reachability
00922     if (!goal) return qfalse;
00923     //if the areanum is not valid
00924     if (!areanum) return qfalse;
00925     //if the goal areanum is not valid
00926     if (!goal->areanum) return qfalse;
00927 
00928     Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
00929     lastgoalareanum = goal->areanum;
00930     lastareanum = areanum;
00931     VectorCopy(origin, end);
00932     //only do 20 hops
00933     for (i = 0; i < 20 && (areanum != goal->areanum); i++)
00934     {
00935         //
00936         reachnum = BotGetReachabilityToGoal(end, areanum,
00937                         lastgoalareanum, lastareanum,
00938                             avoidreach, avoidreachtimes, avoidreachtries,
00939                                     goal, travelflags, travelflags, NULL, 0, NULL);
00940         if (!reachnum) return qfalse;
00941         AAS_ReachabilityFromNum(reachnum, &reach);
00942         //
00943         if (BotVisible(goal->entitynum, goal->origin, reach.start))
00944         {
00945             VectorCopy(reach.start, target);
00946             return qtrue;
00947         } //end if
00948         //
00949         if (BotVisible(goal->entitynum, goal->origin, reach.end))
00950         {
00951             VectorCopy(reach.end, target);
00952             return qtrue;
00953         } //end if
00954         //
00955         if (reach.areanum == goal->areanum)
00956         {
00957             VectorCopy(reach.end, target);
00958             return qtrue;
00959         } //end if
00960         //
00961         lastareanum = areanum;
00962         areanum = reach.areanum;
00963         VectorCopy(reach.end, end);
00964         //
00965     } //end while
00966     //
00967     return qfalse;
00968 } //end of the function BotPredictVisiblePosition
00969 //===========================================================================
00970 //
00971 // Parameter:           -
00972 // Returns:             -
00973 // Changes Globals:     -
00974 //===========================================================================
00975 void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter)
00976 {
00977     int modelnum;
00978     vec3_t mins, maxs, origin, mids;
00979     vec3_t angles = {0, 0, 0};
00980 
00981     modelnum = reach->facenum & 0x0000FFFF;
00982     //get some bsp model info
00983     AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
00984     //
00985     if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
00986     {
00987         botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
00988     } //end if
00989     //get a point just above the plat in the bottom position
00990     VectorAdd(mins, maxs, mids);
00991     VectorMA(origin, 0.5, mids, bottomcenter);
00992     bottomcenter[2] = reach->start[2];
00993 } //end of the function MoverBottomCenter
00994 //===========================================================================
00995 //
00996 // Parameter:           -
00997 // Returns:             -
00998 // Changes Globals:     -
00999 //===========================================================================
01000 float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum)
01001 {
01002     float dist, startz;
01003     vec3_t start, end;
01004     aas_trace_t trace;
01005 
01006     //do gap checking
01007     startz = origin[2];
01008     //this enables walking down stairs more fluidly
01009     {
01010         VectorCopy(origin, start);
01011         VectorCopy(origin, end);
01012         end[2] -= 60;
01013         trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
01014         if (trace.fraction >= 1) return 1;
01015         startz = trace.endpos[2] + 1;
01016     }
01017     //
01018     for (dist = 8; dist <= 100; dist += 8)
01019     {
01020         VectorMA(origin, dist, hordir, start);
01021         start[2] = startz + 24;
01022         VectorCopy(start, end);
01023         end[2] -= 48 + sv_maxbarrier->value;
01024         trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
01025         //if solid is found the bot can't walk any further and fall into a gap
01026         if (!trace.startsolid)
01027         {
01028             //if it is a gap
01029             if (trace.endpos[2] < startz - sv_maxstep->value - 8)
01030             {
01031                 VectorCopy(trace.endpos, end);
01032                 end[2] -= 20;
01033                 if (AAS_PointContents(end) & CONTENTS_WATER) break;
01034                 //if a gap is found slow down
01035                 //botimport.Print(PRT_MESSAGE, "gap at %f\n", dist);
01036                 return dist;
01037             } //end if
01038             startz = trace.endpos[2];
01039         } //end if
01040     } //end for
01041     return 0;
01042 } //end of the function BotGapDistance
01043 //===========================================================================
01044 //
01045 // Parameter:           -
01046 // Returns:             -
01047 // Changes Globals:     -
01048 //===========================================================================
01049 int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed)
01050 {
01051     vec3_t start, hordir, end;
01052     aas_trace_t trace;
01053 
01054     VectorCopy(ms->origin, end);
01055     end[2] += sv_maxbarrier->value;
01056     //trace right up
01057     trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum);
01058     //this shouldn't happen... but we check anyway
01059     if (trace.startsolid) return qfalse;
01060     //if very low ceiling it isn't possible to jump up to a barrier
01061     if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
01062     //
01063     hordir[0] = dir[0];
01064     hordir[1] = dir[1];
01065     hordir[2] = 0;
01066     VectorNormalize(hordir);
01067     VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end);
01068     VectorCopy(trace.endpos, start);
01069     end[2] = trace.endpos[2];
01070     //trace from previous trace end pos horizontally in the move direction
01071     trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
01072     //again this shouldn't happen
01073     if (trace.startsolid) return qfalse;
01074     //
01075     VectorCopy(trace.endpos, start);
01076     VectorCopy(trace.endpos, end);
01077     end[2] = ms->origin[2];
01078     //trace down from the previous trace end pos
01079     trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
01080     //if solid
01081     if (trace.startsolid) return qfalse;
01082     //if no obstacle at all
01083     if (trace.fraction >= 1.0) return qfalse;
01084     //if less than the maximum step height
01085     if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
01086     //
01087     EA_Jump(ms->client);
01088     EA_Move(ms->client, hordir, speed);
01089     ms->moveflags |= MFL_BARRIERJUMP;
01090     //there is a barrier
01091     return qtrue;
01092 } //end of the function BotCheckBarrierJump
01093 //===========================================================================
01094 //
01095 // Parameter:           -
01096 // Returns:             -
01097 // Changes Globals:     -
01098 //===========================================================================
01099 int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
01100 {
01101     vec3_t normdir;
01102 
01103     VectorCopy(dir, normdir);
01104     VectorNormalize(normdir);
01105     EA_Move(ms->client, normdir, speed);
01106     return qtrue;
01107 } //end of the function BotSwimInDirection
01108 //===========================================================================
01109 //
01110 // Parameter:           -
01111 // Returns:             -
01112 // Changes Globals:     -
01113 //===========================================================================
01114 int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
01115 {
01116     vec3_t hordir, cmdmove, velocity, tmpdir, origin;
01117     int presencetype, maxframes, cmdframes, stopevent;
01118     aas_clientmove_t move;
01119     float dist;
01120 
01121     if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
01122     //if the bot is on the ground
01123     if (ms->moveflags & MFL_ONGROUND)
01124     {
01125         //if there is a barrier the bot can jump on
01126         if (BotCheckBarrierJump(ms, dir, speed)) return qtrue;
01127         //remove barrier jump flag
01128         ms->moveflags &= ~MFL_BARRIERJUMP;
01129         //get the presence type for the movement
01130         if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH;
01131         else presencetype = PRESENCE_NORMAL;
01132         //horizontal direction
01133         hordir[0] = dir[0];
01134         hordir[1] = dir[1];
01135         hordir[2] = 0;
01136         VectorNormalize(hordir);
01137         //if the bot is not supposed to jump
01138         if (!(type & MOVE_JUMP))
01139         {
01140             //if there is a gap, try to jump over it
01141             if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP;
01142         } //end if
01143         //get command movement
01144         VectorScale(hordir, speed, cmdmove);
01145         VectorCopy(ms->velocity, velocity);
01146         //
01147         if (type & MOVE_JUMP)
01148         {
01149             //botimport.Print(PRT_MESSAGE, "trying jump\n");
01150             cmdmove[2] = 400;
01151             maxframes = PREDICTIONTIME_JUMP / 0.1;
01152             cmdframes = 1;
01153             stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE|
01154                         SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
01155         } //end if
01156         else
01157         {
01158             maxframes = 2;
01159             cmdframes = 2;
01160             stopevent = SE_HITGROUNDDAMAGE|
01161                         SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
01162         } //end else
01163         //AAS_ClearShownDebugLines();
01164         //
01165         VectorCopy(ms->origin, origin);
01166         origin[2] += 0.5;
01167         AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue,
01168                                     velocity, cmdmove, cmdframes, maxframes, 0.1f,
01169                                     stopevent, 0, qfalse);//qtrue);
01170         //if prediction time wasn't enough to fully predict the movement
01171         if (move.frames >= maxframes && (type & MOVE_JUMP))
01172         {
01173             //botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client);
01174             return qfalse;
01175         } //end if
01176         //don't enter slime or lava and don't fall from too high
01177         if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
01178         {
01179             //botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client);
01180             //if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n");
01181             //if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n");
01182             //if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n");
01183             return qfalse;
01184         } //end if
01185         //if ground was hit
01186         if (move.stopevent & SE_HITGROUND)
01187         {
01188             //check for nearby gap
01189             VectorNormalize2(move.velocity, tmpdir);
01190             dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum);
01191             if (dist > 0) return qfalse;
01192             //
01193             dist = BotGapDistance(move.endpos, hordir, ms->entitynum);
01194             if (dist > 0) return qfalse;
01195         } //end if
01196         //get horizontal movement
01197         tmpdir[0] = move.endpos[0] - ms->origin[0];
01198         tmpdir[1] = move.endpos[1] - ms->origin[1];
01199         tmpdir[2] = 0;
01200         //
01201         //AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE);
01202         //the bot is blocked by something
01203         if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse;
01204         //perform the movement
01205         if (type & MOVE_JUMP) EA_Jump(ms->client);
01206         if (type & MOVE_CROUCH) EA_Crouch(ms->client);
01207         EA_Move(ms->client, hordir, speed);
01208         //movement was succesfull
01209         return qtrue;
01210     } //end if
01211     else
01212     {
01213         if (ms->moveflags & MFL_BARRIERJUMP)
01214         {
01215             //if near the top or going down
01216             if (ms->velocity[2] < 50)
01217             {
01218                 EA_Move(ms->client, dir, speed);
01219             } //end if
01220         } //end if
01221         //FIXME: do air control to avoid hazards
01222         return qtrue;
01223     } //end else
01224 } //end of the function BotWalkInDirection
01225 //===========================================================================
01226 //
01227 // Parameter:           -
01228 // Returns:             -
01229 // Changes Globals:     -
01230 //===========================================================================
01231 int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type)
01232 {
01233     bot_movestate_t *ms;
01234 
01235     ms = BotMoveStateFromHandle(movestate);
01236     if (!ms) return qfalse;
01237     //if swimming
01238     if (AAS_Swimming(ms->origin))
01239     {
01240         return BotSwimInDirection(ms, dir, speed, ty