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

be_aas_reach.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_aas_reach.c
00025  *
00026  * desc:        reachability calculations
00027  *
00028  * $Archive: /MissionPack/code/botlib/be_aas_reach.c $
00029  *
00030  *****************************************************************************/
00031 
00032 #include "../game/q_shared.h"
00033 #include "l_log.h"
00034 #include "l_memory.h"
00035 #include "l_script.h"
00036 #include "l_libvar.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_aas_def.h"
00044 
00045 extern int Sys_MilliSeconds(void);
00046 
00047 
00048 extern botlib_import_t botimport;
00049 
00050 //#define REACH_DEBUG
00051 
00052 //NOTE: all travel times are in hundreth of a second
00053 //maximum number of reachability links
00054 #define AAS_MAX_REACHABILITYSIZE            65536
00055 //number of areas reachability is calculated for each frame
00056 #define REACHABILITYAREASPERCYCLE           15
00057 //number of units reachability points are placed inside the areas
00058 #define INSIDEUNITS                         2
00059 #define INSIDEUNITS_WALKEND                 5
00060 #define INSIDEUNITS_WALKSTART               0.1
00061 #define INSIDEUNITS_WATERJUMP               15
00062 //area flag used for weapon jumping
00063 #define AREA_WEAPONJUMP                     8192    //valid area to weapon jump to
00064 //number of reachabilities of each type
00065 int reach_swim;         //swim
00066 int reach_equalfloor;   //walk on floors with equal height
00067 int reach_step;         //step up
00068 int reach_walk;         //walk of step
00069 int reach_barrier;      //jump up to a barrier
00070 int reach_waterjump;    //jump out of water
00071 int reach_walkoffledge; //walk of a ledge
00072 int reach_jump;         //jump
00073 int reach_ladder;       //climb or descent a ladder
00074 int reach_teleport;     //teleport
00075 int reach_elevator;     //use an elevator
00076 int reach_funcbob;      //use a func bob
00077 int reach_grapple;      //grapple hook
00078 int reach_doublejump;   //double jump
00079 int reach_rampjump;     //ramp jump
00080 int reach_strafejump;   //strafe jump (just normal jump but further)
00081 int reach_rocketjump;   //rocket jump
00082 int reach_bfgjump;      //bfg jump
00083 int reach_jumppad;      //jump pads
00084 //if true grapple reachabilities are skipped
00085 int calcgrapplereach;
00086 //linked reachability
00087 typedef struct aas_lreachability_s
00088 {
00089     int areanum;                    //number of the reachable area
00090     int facenum;                    //number of the face towards the other area
00091     int edgenum;                    //number of the edge towards the other area
00092     vec3_t start;                   //start point of inter area movement
00093     vec3_t end;                     //end point of inter area movement
00094     int traveltype;                 //type of travel required to get to the area
00095     unsigned short int traveltime;  //travel time of the inter area movement
00096     //
00097     struct aas_lreachability_s *next;
00098 } aas_lreachability_t;
00099 //temporary reachabilities
00100 aas_lreachability_t *reachabilityheap;  //heap with reachabilities
00101 aas_lreachability_t *nextreachability;  //next free reachability from the heap
00102 aas_lreachability_t **areareachability; //reachability links for every area
00103 int numlreachabilities;
00104 
00105 //===========================================================================
00106 // returns the surface area of the given face
00107 //
00108 // Parameter:               -
00109 // Returns:                 -
00110 // Changes Globals:     -
00111 //===========================================================================
00112 float AAS_FaceArea(aas_face_t *face)
00113 {
00114     int i, edgenum, side;
00115     float total;
00116     vec_t *v;
00117     vec3_t d1, d2, cross;
00118     aas_edge_t *edge;
00119 
00120     edgenum = aasworld.edgeindex[face->firstedge];
00121     side = edgenum < 0;
00122     edge = &aasworld.edges[abs(edgenum)];
00123     v = aasworld.vertexes[edge->v[side]];
00124 
00125     total = 0;
00126     for (i = 1; i < face->numedges - 1; i++)
00127     {
00128         edgenum = aasworld.edgeindex[face->firstedge + i];
00129         side = edgenum < 0;
00130         edge = &aasworld.edges[abs(edgenum)];
00131         VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1);
00132         VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2);
00133         CrossProduct(d1, d2, cross);
00134         total += 0.5 * VectorLength(cross);
00135     } //end for
00136     return total;
00137 } //end of the function AAS_FaceArea
00138 //===========================================================================
00139 // returns the volume of an area
00140 //
00141 // Parameter:               -
00142 // Returns:                 -
00143 // Changes Globals:     -
00144 //===========================================================================
00145 float AAS_AreaVolume(int areanum)
00146 {
00147     int i, edgenum, facenum, side;
00148     vec_t d, a, volume;
00149     vec3_t corner;
00150     aas_plane_t *plane;
00151     aas_edge_t *edge;
00152     aas_face_t *face;
00153     aas_area_t *area;
00154 
00155     area = &aasworld.areas[areanum];
00156     facenum = aasworld.faceindex[area->firstface];
00157     face = &aasworld.faces[abs(facenum)];
00158     edgenum = aasworld.edgeindex[face->firstedge];
00159     edge = &aasworld.edges[abs(edgenum)];
00160     //
00161     VectorCopy(aasworld.vertexes[edge->v[0]], corner);
00162 
00163     //make tetrahedrons to all other faces
00164     volume = 0;
00165     for (i = 0; i < area->numfaces; i++)
00166     {
00167         facenum = abs(aasworld.faceindex[area->firstface + i]);
00168         face = &aasworld.faces[facenum];
00169         side = face->backarea != areanum;
00170         plane = &aasworld.planes[face->planenum ^ side];
00171         d = -(DotProduct (corner, plane->normal) - plane->dist);
00172         a = AAS_FaceArea(face);
00173         volume += d * a;
00174     } //end for
00175 
00176     volume /= 3;
00177     return volume;
00178 } //end of the function AAS_AreaVolume
00179 //===========================================================================
00180 //
00181 // Parameter:               -
00182 // Returns:                 -
00183 // Changes Globals:     -
00184 //===========================================================================
00185 int AAS_BestReachableLinkArea(aas_link_t *areas)
00186 {
00187     aas_link_t *link;
00188 
00189     for (link = areas; link; link = link->next_area)
00190     {
00191         if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum))
00192         {
00193             return link->areanum;
00194         } //end if
00195     } //end for
00196     //
00197     for (link = areas; link; link = link->next_area)
00198     {
00199         if (link->areanum) return link->areanum;
00200         //FIXME: this is a bad idea when the reachability is not yet
00201         // calculated when the level items are loaded
00202         if (AAS_AreaReachability(link->areanum))
00203             return link->areanum;
00204     } //end for
00205     return 0;
00206 } //end of the function AAS_BestReachableLinkArea
00207 //===========================================================================
00208 //
00209 // Parameter:           -
00210 // Returns:             -
00211 // Changes Globals:     -
00212 //===========================================================================
00213 int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity)
00214 {
00215     int modelnum, ent2;
00216     float speed, height, gravity, time, dist, forward;
00217     vec3_t origin, angles, teststart, ent2origin;
00218     aas_trace_t trace;
00219     char model[MAX_EPAIRKEY];
00220     char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
00221 
00222     //
00223     AAS_FloatForBSPEpairKey(ent, "speed", &speed);
00224     if (!speed) speed = 1000;
00225     VectorClear(angles);
00226     //get the mins, maxs and origin of the model
00227     AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
00228     if (model[0]) modelnum = atoi(model+1);
00229     else modelnum = 0;
00230     AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
00231     VectorAdd(origin, absmins, absmins);
00232     VectorAdd(origin, absmaxs, absmaxs);
00233     VectorAdd(absmins, absmaxs, origin);
00234     VectorScale (origin, 0.5, origin);
00235 
00236     //get the start areas
00237     VectorCopy(origin, teststart);
00238     teststart[2] += 64;
00239     trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
00240     if (trace.startsolid)
00241     {
00242         botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
00243         VectorCopy(origin, areastart);
00244     } //end if
00245     else
00246     {
00247         VectorCopy(trace.endpos, areastart);
00248     } //end else
00249     areastart[2] += 0.125;
00250     //
00251     //AAS_DrawPermanentCross(origin, 4, 4);
00252     //get the target entity
00253     AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
00254     for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
00255     {
00256         if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
00257         if (!strcmp(targetname, target)) break;
00258     } //end for
00259     if (!ent2)
00260     {
00261         botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
00262         return qfalse;
00263     } //end if
00264     AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
00265     //
00266     height = ent2origin[2] - origin[2];
00267     gravity = aassettings.phys_gravity;
00268     time = sqrt( height / ( 0.5 * gravity ) );
00269     if (!time)
00270     {
00271         botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
00272         return qfalse;
00273     } //end if
00274     // set s.origin2 to the push velocity
00275     VectorSubtract ( ent2origin, origin, velocity);
00276     dist = VectorNormalize( velocity);
00277     forward = dist / time;
00278     //FIXME: why multiply by 1.1
00279     forward *= 1.1f;
00280     VectorScale(velocity, forward, velocity);
00281     velocity[2] = time * gravity;
00282     return qtrue;
00283 } //end of the function AAS_GetJumpPadInfo
00284 //===========================================================================
00285 //
00286 // Parameter:           -
00287 // Returns:             -
00288 // Changes Globals:     -
00289 //===========================================================================
00290 int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs)
00291 {
00292     int area2num, ent, bot_visualizejumppads, bestareanum;
00293     float volume, bestareavolume;
00294     vec3_t areastart, cmdmove, bboxmins, bboxmaxs;
00295     vec3_t absmins, absmaxs, velocity;
00296     aas_clientmove_t move;
00297     aas_link_t *areas, *link;
00298     char classname[MAX_EPAIRKEY];
00299 
00300 #ifdef BSPC
00301     bot_visualizejumppads = 0;
00302 #else
00303     bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
00304 #endif
00305     VectorAdd(origin, mins, bboxmins);
00306     VectorAdd(origin, maxs, bboxmaxs);
00307     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
00308     {
00309         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
00310         if (strcmp(classname, "trigger_push")) continue;
00311         //
00312         if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
00313         //get the areas the jump pad brush is in
00314         areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
00315         for (link = areas; link; link = link->next_area)
00316         {
00317             if (AAS_AreaJumpPad(link->areanum)) break;
00318         } //end for
00319         if (!link)
00320         {
00321             botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
00322             AAS_UnlinkFromAreas(areas);
00323             continue;
00324         } //end if
00325         //
00326         //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
00327         //
00328         VectorSet(cmdmove, 0, 0, 0);
00329         Com_Memset(&move, 0, sizeof(aas_clientmove_t));
00330         area2num = 0;
00331         AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
00332                                 velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads);
00333         if (move.frames < 30)
00334         {
00335             bestareanum = 0;
00336             bestareavolume = 0;
00337             for (link = areas; link; link = link->next_area)
00338             {
00339                 if (!AAS_AreaJumpPad(link->areanum)) continue;
00340                 volume = AAS_AreaVolume(link->areanum);
00341                 if (volume >= bestareavolume)
00342                 {
00343                     bestareanum = link->areanum;
00344                     bestareavolume = volume;
00345                 } //end if
00346             } //end if
00347             AAS_UnlinkFromAreas(areas);
00348             return bestareanum;
00349         } //end if
00350         AAS_UnlinkFromAreas(areas);
00351     } //end for
00352     return 0;
00353 } //end of the function AAS_BestReachableFromJumpPadArea
00354 //===========================================================================
00355 //
00356 // Parameter:               -
00357 // Returns:                 -
00358 // Changes Globals:     -
00359 //===========================================================================
00360 int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin)
00361 {
00362     int areanum, i, j, k, l;
00363     aas_link_t *areas;
00364     vec3_t absmins, absmaxs;
00365     //vec3_t bbmins, bbmaxs;
00366     vec3_t start, end;
00367     aas_trace_t trace;
00368 
00369     if (!aasworld.loaded)
00370     {
00371         botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n");
00372         return 0;
00373     } //end if
00374     //find a point in an area
00375     VectorCopy(origin, start);
00376     areanum = AAS_PointAreaNum(start);
00377     //while no area found fudge around a little
00378     for (i = 0; i < 5 && !areanum; i++)
00379     {
00380         for (j = 0; j < 5 && !areanum; j++)
00381         {
00382             for (k = -1; k <= 1 && !areanum; k++)
00383             {
00384                 for (l = -1; l <= 1 && !areanum; l++)
00385                 {
00386                     VectorCopy(origin, start);
00387                     start[0] += (float) j * 4 * k;
00388                     start[1] += (float) j * 4 * l;
00389                     start[2] += (float) i * 4;
00390                     areanum = AAS_PointAreaNum(start);
00391                 } //end for
00392             } //end for
00393         } //end for
00394     } //end for
00395     //if an area was found
00396     if (areanum)
00397     {
00398         //drop client bbox down and try again
00399         VectorCopy(start, end);
00400         start[2] += 0.25;
00401         end[2] -= 50;
00402         trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
00403         if (!trace.startsolid)
00404         {
00405             areanum = AAS_PointAreaNum(trace.endpos);
00406             VectorCopy(trace.endpos, goalorigin);
00407             //FIXME: cannot enable next line right now because the reachability
00408             // does not have to be calculated when the level items are loaded
00409             //if the origin is in an area with reachability
00410             //if (AAS_AreaReachability(areanum)) return areanum;
00411             if (areanum) return areanum;
00412         } //end if
00413         else
00414         {
00415             //it can very well happen that the AAS_PointAreaNum function tells that
00416             //a point is in an area and that starting a AAS_TraceClientBBox from that
00417             //point will return trace.startsolid qtrue
00418 #if 0
00419             if (AAS_PointAreaNum(start))
00420             {
00421                 Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum);
00422                 AAS_DrawPermanentCross(start, 4, LINECOLOR_RED);
00423             } //end if
00424             botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n");
00425 #endif
00426             VectorCopy(start, goalorigin);
00427             return areanum;
00428         } //end else
00429     } //end if
00430     //
00431     //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs);
00432     //NOTE: the goal origin does not have to be in the goal area
00433     // because the bot will have to move towards the item origin anyway
00434     VectorCopy(origin, goalorigin);
00435     //
00436     VectorAdd(origin, mins, absmins);
00437     VectorAdd(origin, maxs, absmaxs);
00438     //add bounding box size
00439     //VectorSubtract(absmins, bbmaxs, absmins);
00440     //VectorSubtract(absmaxs, bbmins, absmaxs);
00441     //link an invalid (-1) entity
00442     areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
00443     //get the reachable link arae
00444     areanum = AAS_BestReachableLinkArea(areas);
00445     //unlink the invalid entity
00446     AAS_UnlinkFromAreas(areas);
00447     //
00448     return areanum;
00449 } //end of the function AAS_BestReachableArea
00450 //===========================================================================
00451 //
00452 // Parameter:               -
00453 // Returns:                 -
00454 // Changes Globals:     -
00455 //===========================================================================
00456 void AAS_SetupReachabilityHeap(void)
00457 {
00458     int i;
00459 
00460     reachabilityheap = (aas_lreachability_t *) GetClearedMemory(
00461                         AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t));
00462     for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++)
00463     {
00464         reachabilityheap[i].next = &reachabilityheap[i+1];
00465     } //end for
00466     reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL;
00467     nextreachability = reachabilityheap;
00468     numlreachabilities = 0;
00469 } //end of the function AAS_InitReachabilityHeap
00470 //===========================================================================
00471 //
00472 // Parameter:               -
00473 // Returns:                 -
00474 // Changes Globals:     -
00475 //===========================================================================
00476 void AAS_ShutDownReachabilityHeap(void)
00477 {
00478     FreeMemory(reachabilityheap);
00479     numlreachabilities = 0;
00480 } //end of the function AAS_ShutDownReachabilityHeap
00481 //===========================================================================
00482 // returns a reachability link
00483 //
00484 // Parameter:               -
00485 // Returns:                 -
00486 // Changes Globals:     -
00487 //===========================================================================
00488 aas_lreachability_t *AAS_AllocReachability(void)
00489 {
00490     aas_lreachability_t *r;
00491 
00492     if (!nextreachability) return NULL;
00493     //make sure the error message only shows up once
00494     if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE");
00495     //
00496     r = nextreachability;
00497     nextreachability = nextreachability->next;
00498     numlreachabilities++;
00499     return r;
00500 } //end of the function AAS_AllocReachability
00501 //===========================================================================
00502 // frees a reachability link
00503 //
00504 // Parameter:               -
00505 // Returns:                 -
00506 // Changes Globals:     -
00507 //===========================================================================
00508 void AAS_FreeReachability(aas_lreachability_t *lreach)
00509 {
00510     Com_Memset(lreach, 0, sizeof(aas_lreachability_t));
00511 
00512     lreach->next = nextreachability;
00513     nextreachability = lreach;
00514     numlreachabilities--;
00515 } //end of the function AAS_FreeReachability
00516 //===========================================================================
00517 // returns qtrue if the area has reachability links
00518 //
00519 // Parameter:               -
00520 // Returns:                 -
00521 // Changes Globals:     -
00522 //===========================================================================
00523 int AAS_AreaReachability(int areanum)
00524 {
00525     if (areanum < 0 || areanum >= aasworld.numareas)
00526     {
00527         AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum);
00528         return 0;
00529     } //end if
00530     return aasworld.areasettings[areanum].numreachableareas;
00531 } //end of the function AAS_AreaReachability
00532 //===========================================================================
00533 // returns the surface area of all ground faces together of the area
00534 //
00535 // Parameter:               -
00536 // Returns:                 -
00537 // Changes Globals:     -
00538 //===========================================================================
00539 float AAS_AreaGroundFaceArea(int areanum)
00540 {
00541     int i;
00542     float total;
00543     aas_area_t *area;
00544     aas_face_t *face;
00545 
00546     total = 0;
00547     area = &aasworld.areas[areanum];
00548     for (i = 0; i < area->numfaces; i++)
00549     {
00550         face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
00551         if (!(face->faceflags & FACE_GROUND)) continue;
00552         //
00553         total += AAS_FaceArea(face);
00554     } //end for
00555     return total;
00556 } //end of the function AAS_AreaGroundFaceArea
00557 //===========================================================================
00558 // returns the center of a face
00559 //
00560 // Parameter:               -
00561 // Returns:                 -
00562 // Changes Globals:     -
00563 //===========================================================================
00564 void AAS_FaceCenter(int facenum, vec3_t center)
00565 {
00566     int i;
00567     float scale;
00568     aas_face_t *face;
00569     aas_edge_t *edge;
00570 
00571     face = &aasworld.faces[facenum];
00572 
00573     VectorClear(center);
00574     for (i = 0; i < face->numedges; i++)
00575     {
00576         edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])];
00577         VectorAdd(center, aasworld.vertexes[edge->v[0]], center);
00578         VectorAdd(center, aasworld.vertexes[edge->v[1]], center);
00579     } //end for
00580     scale = 0.5 / face->numedges;
00581     VectorScale(center, scale, center);
00582 } //end of the function AAS_FaceCenter
00583 //===========================================================================
00584 // returns the maximum distance a player can fall before being damaged
00585 // damage = deltavelocity*deltavelocity  * 0.0001
00586 //
00587 // Parameter:               -
00588 // Returns:                 -
00589 // Changes Globals:     -
00590 //===========================================================================
00591 int AAS_FallDamageDistance(void)
00592 {
00593     float maxzvelocity, gravity, t;
00594 
00595     maxzvelocity = sqrt(30 * 10000);
00596     gravity = aassettings.phys_gravity;
00597     t = maxzvelocity / gravity;
00598     return 0.5 * gravity * t * t;
00599 } //end of the function AAS_FallDamageDistance
00600 //===========================================================================
00601 // distance = 0.5 * gravity * t * t
00602 // vel = t * gravity
00603 // damage = vel * vel * 0.0001
00604 //
00605 // Parameter:           -
00606 // Returns:             -
00607 // Changes Globals:     -
00608 //===========================================================================
00609 float AAS_FallDelta(float distance)
00610 {
00611     float t, delta, gravity;
00612 
00613     gravity = aassettings.phys_gravity;
00614     t = sqrt(fabs(distance) * 2 / gravity);
00615     delta = t * gravity;
00616     return delta * delta * 0.0001;
00617 } //end of the function AAS_FallDelta
00618 //===========================================================================
00619 //
00620 // Parameter:           -
00621 // Returns:             -
00622 // Changes Globals:     -
00623 //===========================================================================
00624 float AAS_MaxJumpHeight(float phys_jumpvel)
00625 {
00626     float phys_gravity;
00627 
00628     phys_gravity = aassettings.phys_gravity;
00629     //maximum height a player can jump with the given initial z velocity
00630     return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity);
00631 } //end of the function MaxJumpHeight
00632 //===========================================================================
00633 // returns true if a player can only crouch in the area
00634 //
00635 // Parameter:               -
00636 // Returns:                 -
00637 // Changes Globals:     -
00638 //===========================================================================
00639 float AAS_MaxJumpDistance(float phys_jumpvel)
00640 {
00641     float phys_gravity, phys_maxvelocity, t;
00642 
00643     phys_gravity = aassettings.phys_gravity;
00644     phys_maxvelocity = aassettings.phys_maxvelocity;
00645     //time a player takes to fall the height
00646     t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity));
00647    //maximum distance
00648     return phys_maxvelocity * (t + phys_jumpvel / phys_gravity);
00649 } //end of the function AAS_MaxJumpDistance
00650 //===========================================================================
00651 // returns true if a player can only crouch in the area
00652 //
00653 // Parameter:               -
00654 // Returns:                 -
00655 // Changes Globals:     -
00656 //===========================================================================
00657 int AAS_AreaCrouch(int areanum)
00658 {
00659     if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue;
00660     else return qfalse;
00661 } //end of the function AAS_AreaCrouch
00662 //===========================================================================
00663 // returns qtrue if it is possible to swim in the area
00664 //
00665 // Parameter:               -
00666 // Returns:                 -
00667 // Changes Globals:     -
00668 //===========================================================================
00669 int AAS_AreaSwim(int areanum)
00670 {
00671     if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
00672     else return qfalse;
00673 } //end of the function AAS_AreaSwim
00674 //===========================================================================
00675 // returns qtrue if the area contains a liquid
00676 //
00677 // Parameter:               -
00678 // Returns:                 -
00679 // Changes Globals:     -
00680 //===========================================================================
00681 int AAS_AreaLiquid(int areanum)
00682 {
00683     if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
00684     else return qfalse;
00685 } //end of the function AAS_AreaLiquid
00686 //===========================================================================
00687 //
00688 // Parameter:           -
00689 // Returns:             -
00690 // Changes Globals:     -
00691 //===========================================================================
00692 int AAS_AreaLava(int areanum)
00693 {
00694     return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA);
00695 } //end of the function AAS_AreaLava
00696 //===========================================================================
00697 //
00698 // Parameter:           -
00699 // Returns:             -
00700 // Changes Globals:     -
00701 //===========================================================================
00702 int AAS_AreaSlime(int areanum)
00703 {
00704     return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME);
00705 } //end of the function AAS_AreaSlime
00706 //===========================================================================
00707 // returns qtrue if the area contains ground faces
00708 //
00709 // Parameter:               -
00710 // Returns:                 -
00711 // Changes Globals:     -
00712 //===========================================================================
00713 int AAS_AreaGrounded(int areanum)
00714 {
00715     return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED);
00716 } //end of the function AAS_AreaGround
00717 //===========================================================================
00718 // returns true if the area contains ladder faces
00719 //
00720 // Parameter:               -
00721 // Returns:                 -
00722 // Changes Globals:     -
00723 //===========================================================================
00724 int AAS_AreaLadder(int areanum)
00725 {
00726     return (aasworld.areasettings[areanum].areaflags & AREA_LADDER);
00727 } //end of the function AAS_AreaLadder
00728 //===========================================================================
00729 //
00730 // Parameter:               -
00731 // Returns:                 -
00732 // Changes Globals:     -
00733 //===========================================================================
00734 int AAS_AreaJumpPad(int areanum)
00735 {
00736     return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD);
00737 } //end of the function AAS_AreaJumpPad
00738 //===========================================================================
00739 //
00740 // Parameter:               -
00741 // Returns:                 -
00742 // Changes Globals:     -
00743 //===========================================================================
00744 int AAS_AreaTeleporter(int areanum)
00745 {
00746     return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER);
00747 } //end of the function AAS_AreaTeleporter
00748 //===========================================================================
00749 //
00750 // Parameter:               -
00751 // Returns:                 -
00752 // Changes Globals:     -
00753 //===========================================================================
00754 int AAS_AreaClusterPortal(int areanum)
00755 {
00756     return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL);
00757 } //end of the function AAS_AreaClusterPortal
00758 //===========================================================================
00759 //
00760 // Parameter:               -
00761 // Returns:                 -
00762 // Changes Globals:     -
00763 //===========================================================================
00764 int AAS_AreaDoNotEnter(int areanum)
00765 {
00766     return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER);
00767 } //end of the function AAS_AreaDoNotEnter
00768 //===========================================================================
00769 // returns the time it takes perform a barrier jump
00770 //
00771 // Parameter:               -
00772 // Returns:                 -
00773 // Changes Globals:     -
00774 //===========================================================================
00775 unsigned short int AAS_BarrierJumpTravelTime(void)
00776 {
00777     return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1);
00778 } //end op the function AAS_BarrierJumpTravelTime
00779 //===========================================================================
00780 // returns true if there already exists a reachability from area1 to area2
00781 //
00782 // Parameter:               -
00783 // Returns:                 -
00784 // Changes Globals:     -
00785 //===========================================================================
00786 qboolean AAS_ReachabilityExists(int area1num, int area2num)
00787 {
00788     aas_lreachability_t *r;
00789 
00790     for (r = areareachability[area1num]; r; r = r->next)
00791     {
00792         if (r->areanum == area2num) return qtrue;
00793     } //end for
00794     return qfalse;
00795 } //end of the function AAS_ReachabilityExists
00796 //===========================================================================
00797 // returns true if there is a solid just after the end point when going
00798 // from start to end
00799 //
00800 // Parameter:               -
00801 // Returns:                 -
00802 // Changes Globals:     -
00803 //===========================================================================
00804 int AAS_NearbySolidOrGap(vec3_t start, vec3_t end)
00805 {
00806     vec3_t dir, testpoint;
00807     int areanum;
00808 
00809     VectorSubtract(end, start, dir);
00810     dir[2] = 0;
00811     VectorNormalize(dir);
00812     VectorMA(end, 48, dir, testpoint);
00813 
00814     areanum = AAS_PointAreaNum(testpoint);
00815     if (!areanum)
00816     {
00817         testpoint[2] += 16;
00818         areanum = AAS_PointAreaNum(testpoint);
00819         if (!areanum) return qtrue;
00820     } //end if
00821     VectorMA(end, 64, dir, testpoint);
00822     areanum = AAS_PointAreaNum(testpoint);
00823     if (areanum)
00824     {
00825         if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue;
00826     } //end if
00827     return qfalse;
00828 } //end of the function AAS_SolidGapTime
00829 //===========================================================================
00830 // searches for swim reachabilities between adjacent areas
00831 //
00832 // Parameter:               -
00833 // Returns:                 -
00834 // Changes Globals:     -
00835 //===========================================================================
00836 int AAS_Reachability_Swim(int area1num, int area2num)
00837 {
00838     int i, j, face1num, face2num, side1;
00839     aas_area_t *area1, *area2;
00840     aas_areasettings_t *areasettings;
00841     aas_lreachability_t *lreach;
00842     aas_face_t *face1;
00843     aas_plane_t *plane;
00844     vec3_t start;
00845 
00846     if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse;
00847     //if the second area is crouch only
00848     if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse;
00849 
00850     area1 = &aasworld.areas[area1num];
00851     area2 = &aasworld.areas[area2num];
00852 
00853     //if the areas are not near anough
00854     for (i = 0; i < 3; i++)
00855     {
00856         if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
00857         if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
00858     } //end for
00859     //find a shared face and create a reachability link
00860     for (i = 0; i < area1->numfaces; i++)
00861     {
00862         face1num = aasworld.faceindex[area1->firstface + i];
00863         side1 = face1num < 0;
00864         face1num = abs(face1num);
00865         //
00866         for (j = 0; j < area2->numfaces; j++)
00867         {
00868             face2num = abs(aasworld.faceindex[area2->firstface + j]);
00869             //
00870             if (face1num == face2num)
00871             {
00872                 AAS_FaceCenter(face1num, start);
00873                 //
00874                 if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))
00875                 {
00876                     //
00877                     face1 = &aasworld.faces[face1num];
00878                     areasettings = &aasworld.areasettings[area1num];
00879                     //create a new reachability link
00880                     lreach = AAS_AllocReachability();
00881                     if (!lreach) return qfalse;
00882                     lreach->areanum = area2num;
00883                     lreach->facenum = face1num;
00884                     lreach->edgenum = 0;
00885                     VectorCopy(start, lreach->start);
00886                     plane = &aasworld.planes[face1->planenum ^ side1];
00887                     VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end);
00888                     lreach->traveltype = TRAVEL_SWIM;
00889                     lreach->traveltime = 1;
00890                     //if the volume of the area is rather small
00891                     if (AAS_AreaVolume(area2num) < 800)
00892                         lreach->traveltime += 200;
00893                     //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500;
00894                     //link the reachability
00895                     lreach->next = areareachability[area1num];
00896                     areareachability[area1num] = lreach;
00897                     reach_swim++;
00898                     return qtrue;
00899                 } //end if
00900             } //end if
00901         } //end for
00902     } //end for
00903     return qfalse;
00904 } //end of the function AAS_Reachability_Swim
00905 //===========================================================================
00906 // searches for reachabilities between adjacent areas with equal floor
00907 // heights
00908 //
00909 // Parameter:               -
00910 // Returns:                 -
00911 // Changes Globals:     -
00912 //===========================================================================
00913 int AAS_Reachability_EqualFloorHeight(int area1num, int area2num)
00914 {
00915     int i, j, edgenum, edgenum1, edgenum2, foundreach, side;
00916     float height, bestheight, length, bestlength;
00917     vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1};
00918     vec3_t edgevec;
00919     aas_area_t *area1, *area2;
00920     aas_face_t *face1, *face2;
00921     aas_edge_t *edge;
00922     aas_plane_t *plane2;
00923     aas_lreachability_t lr, *lreach;
00924 
00925     if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
00926 
00927     area1 = &aasworld.areas[area1num];
00928     area2 = &aasworld.areas[area2num];
00929     //if the areas are not near anough in the x-y direction
00930     for (i = 0; i < 2; i++)
00931     {
00932         if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
00933         if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
00934     } //end for
00935     //if area 2 is too high above area 1
00936     if (area2->mins[2] > area1->maxs[2]) return qfalse;
00937     //
00938     VectorCopy(gravitydirection, invgravity);
00939     VectorInverse(invgravity);
00940     //
00941     bestheight = 99999;
00942     bestlength = 0;
00943     foundreach = qfalse;
00944     Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy
00945     //
00946     //check if the areas have ground faces with a common edge
00947     //if existing use the lowest common edge for a reachability link
00948     for (i = 0; i < area1->numfaces; i++)
00949     {
00950         face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])];
00951         if (!(face1->faceflags & FACE_GROUND)) continue;
00952         //
00953         for (j = 0; j < area2->numfaces; j++)
00954         {
00955             face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
00956             if (!(face2->faceflags & FACE_GROUND)) continue;
00957             //if there is a common edge
00958             for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++)
00959             {
00960                 for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++)
00961                 {
00962                     if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) !=
00963                             abs(aasworld.edgeindex[face2->firstedge + edgenum2]))
00964                                     continue;
00965                     edgenum = aasworld.edgeindex[face1->firstedge + edgenum1];
00966                     side = edgenum < 0;
00967                     edge = &aasworld.edges[abs(edgenum)];
00968                     //get the length of the edge
00969                     VectorSubtract(aasworld.vertexes[edge->v[1]],
00970                                 aasworld.vertexes[edge->v[0]], dir);
00971                     length = VectorLength(dir);
00972                     //get the start point
00973                     VectorAdd(aasworld.vertexes[edge->v[0]],
00974                                 aasworld.vertexes[edge->v[1]], start);
00975                     VectorScale(start, 0.5, start);
00976                     VectorCopy(start, end);
00977                     //get the end point several units inside area2
00978                     //and the start point several units inside area1
00979                     //NOTE: normal is pointing into area2 because the
00980                     //face edges are stored counter clockwise
00981                     VectorSubtract(aasworld.vertexes[edge->v[side]],
00982                                 aasworld.vertexes[edge->v[!side]], edgevec);
00983                     plane2 = &aasworld.planes[face2->planenum];
00984                     CrossProduct(edgevec, plane2->normal, normal);
00985                     VectorNormalize(normal);
00986                     //
00987                     //VectorMA(start, -1, normal, start);
00988                     VectorMA(end, INSIDEUNITS_WALKEND, normal, end);
00989                     VectorMA(start, INSIDEUNITS_WALKSTART, normal, start);
00990                     end[2] += 0.125;
00991                     //
00992                     height = DotProduct(invgravity, start);
00993                     //NOTE: if there's nearby solid or a gap area after this area
00994                     //disabled this crap
00995                     //if (AAS_NearbySolidOrGap(start, end)) height += 200;
00996                     //NOTE: disabled because it disables reachabilities to very small areas
00997                     //if (AAS_PointAreaNum(end) != area2num) continue;
00998                     //get the longest lowest edge
00999                     if (height < bestheight ||
01000                             (height < bestheight + 1 && length > bestlength))
01001                     {
01002                         bestheight = height;
01003                         bestlength = length;
01004                         //create a new reachability link
01005                         lr.areanum = area2num;
01006                         lr.facenum = 0;
01007                         lr.edgenum = edgenum;
01008                         VectorCopy(start, lr.start);
01009                         VectorCopy(end, lr.end);
01010                         lr.traveltype = TRAVEL_WALK;
01011                         lr.traveltime = 1;
01012                         foundreach = qtrue;
01013                     } //end if
01014                 } //end for
01015             } //end for
01016         } //end for
01017     } //end for
01018     if (foundreach)
01019     {
01020         //create a new reachability link
01021         lreach = AAS_AllocReachability();
01022         if (!lreach) return qfalse;
01023         lreach->areanum = lr.areanum;
01024         lreach->facenum = lr.facenum;
01025         lreach->edgenum = lr.edgenum;
01026         VectorCopy(lr.start, lreach->start);
01027         VectorCopy(lr.end, lreach->end);
01028         lreach->traveltype = lr.traveltype;
01029         lreach->traveltime = lr.traveltime;
01030         lreach->next = areareachability[area1num];
01031         areareachability[area1num] = lreach;
01032         //if going into a crouch area
01033         if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
01034         {
01035             lreach->traveltime += aassettings.rs_startcrouch;
01036         } //end if
01037         /*
01038         //NOTE: if there's nearby solid or a gap area after this area
01039         if (!AAS_NearbySolidOrGap(lreach->start, lreach->end))
01040         {
01041             lreach->traveltime += 100;
01042         } //end if
01043         */
01044         //avoid rather small areas
01045         //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;
01046         //
01047         reach_equalfloor++;
01048         return qtrue;
01049     } //end if
01050     return qfalse;
01051 } //end of the function AAS_Reachability_EqualFloorHeight
01052 //===========================================================================
01053 // searches step, barrier, waterjump and walk off ledge reachabilities
01054 //
01055 // Parameter:               -
01056 // Returns:                 -
01057 // Changes Globals:     -
01058 //===========================================================================
01059 int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num)
01060 {
01061     int i, j, k, l, edge1num, edge2num, areas[10], numareas;
01062     int ground_bestarea2groundedgenum, ground_foundreach;
01063     int water_bestarea2groundedgenum, water_foundreach;
01064     int side1, area1swim, faceside1, groundface1num;
01065     float dist, dist1, dist2, diff, invgravitydot, ortdot;
01066     float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
01067     float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist;
01068     vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
01069     vec3_t normal, ort, edgevec, start, end, dir;
01070     vec3_t ground_beststart, ground_bestend, ground_bestnormal;
01071     vec3_t water_beststart, water_bestend, water_bestnormal;
01072     vec3_t invgravity = {0, 0, 1};
01073     vec3_t testpoint;
01074     aas_plane_t *plane;
01075     aas_area_t *area1, *area2;
01076     aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1;
01077     aas_edge_t *edge1, *edge2;
01078     aas_lreachability_t *lreach;
01079     aas_trace_t trace;
01080 
01081     //must be able to walk or swim in the first area
01082     if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
01083     //
01084     if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse;
01085     //
01086     area1 = &aasworld.areas[area1num];
01087     area2 = &aasworld.areas[area2num];
01088     //if the first area contains a liquid
01089     area1swim = AAS_AreaSwim(area1num);
01090     //if the areas are not near anough in the x-y direction
01091     for (i = 0; i < 2; i++)
01092     {
01093         if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
01094         if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
01095     } //end for
01096     //
01097     ground_foundreach = qfalse;
01098     ground_bestdist = 99999;
01099     ground_bestlength = 0;
01100     ground_bestarea2groundedgenum = 0;
01101     //
01102     water_foundreach = qfalse;
01103     water_bestdist = 99999;
01104     water_bestlength = 0;
01105     water_bestarea2groundedgenum = 0;
01106     //
01107     for (i = 0; i < area1->numfaces; i++)
01108     {
01109         groundface1num = aasworld.faceindex[area1->firstface + i];
01110         faceside1 = groundface1num < 0;
01111         groundface1 = &aasworld.faces[abs(groundface1num)];
01112         //if this isn't a ground face
01113         if (!(groundface1->faceflags & FACE_GROUND))
01114         {
01115             //if we can swim in the first area
01116             if (area1swim)
01117             {
01118                 //face plane must be more or less horizontal
01119                 plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)];
01120                 if (DotProduct(plane->normal, invgravity) < 0.7) continue;
01121             } //end if
01122             else
01123             {
01124                 //if we can't swim in the area it must be a ground face
01125                 continue;
01126             } //end else
01127         } //end if
01128         //
01129         for (k = 0; k < groundface1->numedges; k++)
01130         {
01131             edge1num = aasworld.edgeindex[groundface1->firstedge + k];
01132             side1 = (edge1num < 0);
01133             //NOTE: for water faces we must take the side area 1 is
01134             // on into account because the face is shared and doesn't
01135             // have to be oriented correctly
01136             if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1);
01137             edge1num = abs(edge1num);
01138             edge1 = &aasworld.edges[edge1num];
01139             //vertexes of the edge
01140             VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1);
01141             VectorCopy(aasworld.vertexes[edge1->v[side1]], v2);
01142             //get a vertical plane through the edge
01143             //NOTE: normal is pointing into area 2 because the
01144             //face edges are stored counter clockwise
01145             VectorSubtract(v2, v1, edgevec);
01146             CrossProduct(edgevec, invgravity, normal);
01147             VectorNormalize(normal);
01148             dist = DotProduct(normal, v1);
01149             //check the faces from the second area
01150             for (j = 0; j < area2->numfaces; j++)
01151             {
01152                 groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
01153                 //must be a ground face
01154                 if (!(groundface2->faceflags & FACE_GROUND)) continue;
01155                 //check the edges of this ground face
01156                 for (l = 0; l < groundface2->numedges; l++)
01157                 {
01158                     edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]);
01159                     edge2 = &aasworld.edges[edge2num];
01160                     //vertexes of the edge
01161                     VectorCopy(aasworld.vertexes[edge2->v[0]], v3);
01162                     VectorCopy(aasworld.vertexes[edge2->v[1]], v4);
01163                     //check the distance between the two points and the vertical plane
01164                     //through the edge of area1
01165                     diff = DotProduct(normal, v3) - dist;
01166                     if (diff < -0.1 || diff > 0.1) continue;
01167                     diff = DotProduct(normal, v4) - dist;
01168                     if (diff < -0.1 || diff > 0.1) continue;
01169                     //
01170                     //project the two ground edges into the step side plane
01171                     //and calculate the shortest distance between the two
01172                     //edges if they overlap in the direction orthogonal to
01173                     //the gravity direction
01174                     CrossProduct(invgravity, normal, ort);
01175                     invgravitydot = DotProduct(invgravity, invgravity);
01176                     ortdot = DotProduct(ort, ort);
01177                     //projection into the step plane
01178                     //NOTE: since gravity is vertical this is just the z coordinate
01179                     y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot;
01180                     y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot;
01181                     y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot;
01182                     y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot;
01183                     //
01184                     x1 = DotProduct(v1, ort) / ortdot;
01185                     x2 = DotProduct(v2, ort) / ortdot;
01186                     x3 = DotProduct(v3, ort) / ortdot;
01187                     x4 = DotProduct(v4, ort) / ortdot;
01188                     //
01189                     if (x1 > x2)
01190                     {
01191                         tmp = x1; x1 = x2; x2 = tmp;
01192                         tmp = y1; y1 = y2; y2 = tmp;
01193                         VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2);
01194                     } //end if
01195                     if (x3 > x4)
01196                     {
01197                         tmp = x3; x3 = x4; x4 = tmp;
01198                         tmp = y3; y3 = y4; y4 = tmp;
01199                         VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4);
01200                     } //end if
01201                     //if the two projected edge lines have no overlap
01202                     if (x2 <= x3 || x4 <= x1)
01203                     {
01204 //                      Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num);
01205                         continue;
01206                     } //end if
01207                     //if the two lines fully overlap
01208                     if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) &&
01209                             (x3 - 0.5 < x1 && x2 < x4 + 0.5))
01210                     {
01211                         dist1 = y3 - y1;
01212                         dist2 = y4 - y2;
01213                         VectorCopy(v1, p1area1);
01214                         VectorCopy(v2, p2area1);
01215                         VectorCopy(v3, p1area2);
01216                         VectorCopy(v4, p2area2);
01217                     } //end if
01218                     else
01219                     {
01220                         //if the points are equal
01221                         if (x1 > x3 - 0.1 && x1 < x3 + 0.1)
01222                         {
01223                             dist1 = y3 - y1;
01224                             VectorCopy(v1, p1area1);
01225                             VectorCopy(v3, p1area2);
01226                         } //end if
01227                         else if (x1 < x3)
01228                         {
01229                             y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
01230                             dist1 = y3 - y;
01231                             VectorCopy(v3, p1area1);
01232                             p1area1[2] = y;
01233                             VectorCopy(v3, p1area2);
01234                         } //end if
01235                         else
01236                         {
01237                             y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
01238                             dist1 = y - y1;
01239                             VectorCopy(v1, p1area1);
01240                             VectorCopy(v1, p1area2);
01241                             p1area2[2] = y;
01242                         } //end if
01243                         //if the points are equal
01244                         if (x2 > x4 - 0.1 && x2 < x4 + 0.1)
01245                         {
01246                             dist2 = y4 - y2;
01247                             VectorCopy(v2, p2area1);
01248                             VectorCopy(v4, p2area2);
01249                         } //end if
01250                         else if (x2 < x4)
01251                         {
01252                             y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
01253                             dist2 = y - y2;
01254                             VectorCopy(v2, p2area1);
01255                             VectorCopy(v2, p2area2);
01256                             p2area2[2] = y;
01257                         } //end if
01258                         else
01259                         {
01260                             y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
01261                             dist2 = y4 - y;
01262                             VectorCopy(v4, p2area1);
01263                             p2area1[2] = y;
01264                             VectorCopy(v4, p2area2);
01265                         } //end else
01266                     } //end else
01267                     //if both distances are pretty much equal
01268                     //then we take the middle of the points
01269                     if (dist1 > dist2 - 1 && dist1 < dist2 + 1)
01270                     {
01271                         dist = dist1;
01272                         VectorAdd(p1area1, p2area1, start);
01273                         VectorScale(start, 0.5, start);
01274                         VectorAdd(p1area2, p2area2, end);
01275                         VectorScale(end, 0.5, end);
01276                     } //end if
01277                     else if (dist1 < dist2)
01278                     {
01279                         dist = dist1;
01280                         VectorCopy(p1area1, start);
01281                         VectorCopy(p1area2, end);
01282                     } //end else if
01283                     else
01284                     {
01285                         dist = dist2;
01286                         VectorCopy(p2area1, start);
01287                         VectorCopy(p2area2, end);
01288                     } //end else
01289                     //get the length of the overlapping part of the edges of the two areas
01290                     VectorSubtract(p2area2, p1area2, dir);
01291                     length = VectorLength(dir);
01292                     //
01293                     if (groundface1->faceflags & FACE_GROUND)
01294                     {
01295                         //if the vertical distance is smaller
01296                         if (dist < ground_bestdist ||
01297                                 //or the vertical distance is pretty much the same
01298                                 //but the overlapping part of the edges is longer
01299                                 (dist < ground_bestdist + 1 && length > ground_bestlength))
01300                         {
01301                             ground_bestdist = dist;
01302                             ground_bestlength = length;
01303                             ground_foundreach = qtrue;
01304                             ground_bestarea2groundedgenum = edge1num;
01305                             ground_bestface1 = groundface1;
01306                             //best point towards area1
01307                             VectorCopy(start, ground_beststart);
01308                             //normal is pointing into area2
01309                             VectorCopy(normal, ground_bestnormal);
01310                             //best point towards area2
01311                             VectorCopy(end, ground_bestend);
01312                         } //end if
01313                     } //end if
01314                     else
01315                     {
01316                         //if the vertical distance is smaller
01317                         if (dist < water_bestdist ||
01318                                 //or the vertical distance is pretty much the same
01319                                 //but the overlapping part of the edges is longer
01320                                 (dist < water_bestdist + 1 && length > water_bestlength))
01321                         {
01322                             water_bestdist = dist;
01323                             water_bestlength = length;
01324                             water_foundreach = qtrue;
01325                             water_bestarea2groundedgenum = edge1num;
01326                             water_bestface1 = groundface1;
01327                             //best point towards area1
01328                             VectorCopy(start, water_beststart);
01329                             //normal is pointing into area2
01330                             VectorCopy(normal, water_bestnormal);
01331                             //best point towards area2
01332                             VectorCopy(end, water_bestend);
01333                         } //end if
01334                     } //end else
01335                 } //end for
01336             } //end for
01337         } //end for
01338     } //end for
01339     //
01340     // NOTE: swim reachabilities are already filtered out
01341     //
01342     // Steps
01343     //
01344     //        ---------
01345     //        |          step height -> TRAVEL_WALK
01346     //--------|
01347     //
01348     //        ---------
01349     //~~~~~~~~|          step height and low water -> TRAVEL_WALK
01350     //--------|
01351     //
01352     //~~~~~~~~~~~~~~~~~~
01353     //        ---------
01354     //        |          step height and low water up to the step -> TRAVEL_WALK
01355     //--------|
01356     //
01357     //check for a step reachability
01358     if (ground_foundreach)
01359     {
01360         //if area2 is higher but lower than the maximum step height
01361         //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities
01362         if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep)
01363         {
01364             //create walk reachability from area1 to area2
01365             lreach = AAS_AllocReachability();
01366             if (!lreach) return qfalse;
01367             lreach->areanum = area2num;
01368             lreach->facenum = 0;
01369             lreach->edgenum = ground_bestarea2groundedgenum;
01370             VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
01371             VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
01372             lreach->traveltype = TRAVEL_WALK;
01373             lreach->traveltime = 0;//1;
01374             //if going into a crouch area
01375             if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
01376             {
01377                 lreach->traveltime += aassettings.rs_startcrouch;
01378             } //end if
01379             lreach->next = areareachability[area1num];
01380             areareachability[area1num] = lreach;
01381             //NOTE: if there's nearby solid or a gap area after this area
01382             /*
01383             if (!AAS_NearbySolidOrGap(lreach->start, lreach->end))
01384             {
01385                 lreach->traveltime += 100;
01386             } //end if
01387             */
01388             //avoid rather small areas
01389             //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;
01390             //
01391             reach_step++;
01392             return qtrue;
01393         } //end if
01394     } //end if
01395     //
01396     // Water Jumps
01397     //
01398     //        ---------
01399     //        |
01400     //~~~~~~~~|
01401     //        |
01402     //        |          higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP
01403     //--------|
01404     //
01405     //~~~~~~~~~~~~~~~~~~
01406     //        ---------
01407     //        |
01408     //        |
01409     //        |
01410     //        |          higher than step height and low water up to the step -> TRAVEL_WATERJUMP
01411     //--------|
01412     //
01413     //check for a waterjump reachability
01414     if (water_foundreach)
01415     {
01416         //get a test point a little bit towards area1
01417         VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint);
01418         //go down the maximum waterjump height
01419         testpoint[2] -= aassettings.phys_maxwaterjump;
01420         //if there IS water the sv_maxwaterjump height below the bestend point
01421         if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID)
01422         {
01423             //don't create rediculous water jump reachabilities from areas very far below
01424             //the water surface
01425             if (water_bestdist < aassettings.phys_maxwaterjump + 24)
01426             {
01427                 //waterjumping from or towards a crouch only area is not possible in Quake2
01428                 if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) &&
01429                         (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL))
01430                 {
01431                     //create water jump reachability from area1 to area2
01432                     lreach = AAS_AllocReachability();
01433                     if (!lreach) return qfalse;
01434                     lreach->areanum = area2num;
01435                     lreach->facenum = 0;
01436                     lreach->edgenum = water_bestarea2groundedgenum;
01437                     VectorCopy(water_beststart, lreach->start);
01438                     VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end);
01439                     lreach->traveltype = TRAVEL_WATERJUMP;
01440                     lreach->traveltime = aassettings.rs_waterjump;
01441                     lreach->next = areareachability[area1num];
01442                     areareachability[area1num] = lreach;
01443                     //we've got another waterjump reachability
01444                     reach_waterjump++;
01445                     return qtrue;
01446                 } //end if
01447             } //end if
01448         } //end if
01449     } //end if
01450     //
01451     // Barrier Jumps
01452     //
01453     //        ---------
01454     //        |
01455     //        |
01456     //        |
01457     //        |         higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP
01458     //--------|
01459     //
01460     //        ---------
01461     //        |
01462     //        |
01463     //        |
01464     //~~~~~~~~|         higher than step height lower than barrier height
01465     //--------|         and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP
01466     //
01467     //check for a barrier jump reachability
01468     if (ground_foundreach)
01469     {
01470         //if area2 is higher but lower than the maximum barrier jump height
01471         if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier)
01472         {
01473             //if no water in area1 or a very thin layer of water on the ground
01474             if (!water_foundreach || (ground_bestdist - water_bestdist < 16))
01475             {
01476                 //cannot perform a barrier jump towards or from a crouch area in Quake2
01477                 if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num))
01478                 {
01479                     //create barrier jump reachability from area1 to area2
01480                     lreach = AAS_AllocReachability();
01481                     if (!lreach) return qfalse;
01482                     lreach->areanum = area2num;
01483                     lreach->facenum = 0;
01484                     lreach->edgenum = ground_bestarea2groundedgenum;
01485                     VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
01486                     VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
01487                     lreach->traveltype = TRAVEL_BARRIERJUMP;
01488                     lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime();
01489                     lreach->next = areareachability[area1num];
01490                     areareachability[area1num] = lreach;
01491                     //we've got another barrierjump reachability
01492                     reach_barrier++;
01493                     return qtrue;
01494                 } //end if
01495             } //end if
01496         } //end if
01497     } //end if
01498     //
01499     // Walk and Walk Off Ledge
01500     //
01501     //--------|
01502     //        |          can walk or step back -> TRAVEL_WALK
01503     //        ---------
01504     //
01505     //--------|
01506     //        |
01507     //        |
01508     //        |
01509     //        |          cannot walk/step back -> TRAVEL_WALKOFFLEDGE
01510     //        ---------
01511     //
01512     //--------|
01513     //        |
01514     //        |~~~~~~~~
01515     //        |
01516     //        |          cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE
01517     //        ---------  FIXME: create TRAVEL_WALK reach??
01518     //
01519     //check for a walk or walk off ledge reachability
01520     if (ground_foundreach)
01521     {
01522         if (ground_bestdist < 0)
01523         {
01524             if (ground_bestdist > -aassettings.phys_maxstep)
01525             {
01526                 //create walk reachability from area1 to area2
01527                 lreach = AAS_AllocReachability();
01528                 if (!lreach) return qfalse;
01529                 lreach->areanum = area2num;
01530                 lreach->facenum = 0;
01531                 lreach->edgenum = ground_bestarea2groundedgenum;
01532                 VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
01533                 VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
01534                 lreach->traveltype = TRAVEL_WALK;
01535                 lreach->traveltime = 1;
01536                 lreach->next = areareachability[area1num];
01537                 areareachability[area1num] = lreach;
01538                 //we've got another walk reachability
01539                 reach_walk++;
01540                 return qtrue;
01541             } //end if
01542             // if no maximum fall height set or less than the max
01543             if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) {
01544                 //trace a bounding box vertically to check for solids
01545                 VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend);
01546                 VectorCopy(ground_bestend, start);
01547                 start[2] = ground_beststart[2];
01548                 VectorCopy(ground_bestend, end);
01549                 end[2] += 4;
01550                 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
01551                 //if no solids were found
01552                 if (!trace.startsolid && trace.fraction >= 1.0)
01553                 {
01554                     //the trace end point must be in the goal area
01555                     trace.endpos[2] += 1;
01556                     if (AAS_PointAreaNum(trace.endpos) == area2num)
01557                     {
01558                         //if not going through a cluster portal
01559                         numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int));
01560                         for (i = 0; i < numareas; i++)
01561                             if (AAS_AreaClusterPortal(areas[i]))
01562                                 break;
01563                         if (i >= numareas)
01564                         {
01565                             //create a walk off ledge reachability from area1 to area2
01566                             lreach = AAS_AllocReachability();
01567                             if (!lreach) return qfalse;
01568                             lreach->areanum = area2num;
01569                             lreach->facenum = 0;
01570                             lreach->edgenum = ground_bestarea2groundedgenum;
01571                             VectorCopy(ground_beststart, lreach->start);
01572                             VectorCopy(ground_bestend, lreach->end);
01573                             lreach->traveltype = TRAVEL_WALKOFFLEDGE;
01574                             lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity;
01575                             //if falling from too high and not falling into water
01576                             if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num))
01577                             {
01578                                 if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5)
01579                                 {
01580                                     lreach->traveltime += aassettings.rs_falldamage5;
01581                                 } //end if
01582                                 if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10)
01583                                 {
01584                                     lreach->traveltime += aassettings.rs_falldamage10;
01585                                 } //end if
01586                             } //end if
01587                             lreach->next = areareachability[area1num];
01588                             areareachability[area1num] = lreach;
01589                             //
01590                             reach_walkoffledge++;
01591                             //NOTE: don't create a weapon (rl, bfg) jump reachability here
01592                             //because it interferes with other reachabilities
01593                             //like the ladder reachability
01594                             return qtrue;
01595                         } //end if
01596                     } //end if
01597                 } //end if
01598             } //end if
01599         } //end else
01600     } //end if
01601     return qfalse;
01602 } //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge
01603 //===========================================================================
01604 // returns the distance between the two vectors
01605 //
01606 // Parameter:               -
01607 // Returns:                 -
01608 // Changes Globals:     -
01609 //===========================================================================
01610 float VectorDistance(vec3_t v1, vec3_t v2)
01611 {
01612     vec3_t dir;
01613 
01614     VectorSubtract(v2, v1, dir);
01615     return VectorLength(dir);
01616 } //end of the function VectorDistance
01617 //===========================================================================
01618 // returns true if the first vector is between the last two vectors
01619 //
01620 // Parameter:               -
01621 // Returns:                 -
01622 // Changes Globals:     -
01623 //===========================================================================
01624 int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2)
01625 {
01626     vec3_t dir1, dir2;
01627 
01628     VectorSubtract(v, v1, dir1);
01629     VectorSubtract(v, v2, dir2);
01630     return (DotProduct(dir1, dir2) <= 0);
01631 } //end of the function VectorBetweenVectors
01632 //===========================================================================
01633 // returns the mid point between the two vectors
01634 //
01635 // Parameter:               -
01636 // Returns:                 -
01637 // Changes Globals:     -
01638 //===========================================================================
01639 void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle)
01640 {
01641     VectorAdd(v1, v2, middle);
01642     VectorScale(middle, 0.5, middle);
01643 } //end of the function VectorMiddle
01644 //===========================================================================
01645 // calculate a range of points closest to each other on both edges
01646 //
01647 // Parameter:           beststart1      start of the range of points on edge v1-v2
01648 //                      beststart2      end of the range of points  on edge v1-v2
01649 //                      bestend1        start of the range of points on edge v3-v4
01650 //                      bestend2        end of the range of points  on edge v3-v4
01651 //                      bestdist        best distance so far
01652 // Returns:             -
01653 // Changes Globals:     -
01654 //===========================================================================
01655 /*
01656 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
01657                             aas_plane_t *plane1, aas_plane_t *plane2,
01658                             vec3_t beststart, vec3_t bestend, float bestdist)
01659 {
01660     vec3_t dir1, dir2, p1, p2, p3, p4;
01661     float a1, a2, b1, b2, dist;
01662     int founddist;
01663 
01664     //edge vectors
01665     VectorSubtract(v2, v1, dir1);
01666     VectorSubtract(v4, v3, dir2);
01667     //get the horizontal directions
01668     dir1[2] = 0;
01669     dir2[2] = 0;
01670     //
01671     // p1 = point on an edge vector of area2 closest to v1
01672     // p2 = point on an edge vector of area2 closest to v2
01673     // p3 = point on an edge vector of area1 closest to v3
01674     // p4 = point on an edge vector of area1 closest to v4
01675     //
01676     if (dir2[0])
01677     {
01678         a2 = dir2[1] / dir2[0];
01679         b2 = v3[1] - a2 * v3[0];
01680         //point on the edge vector of area2 closest to v1
01681         p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
01682         p1[1] = a2 * p1[0] + b2;
01683         //point on the edge vector of area2 closest to v2
01684         p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
01685         p2[1] = a2 * p2[0] + b2;
01686     } //end if
01687     else
01688     {
01689         //point on the edge vector of area2 closest to v1
01690         p1[0] = v3[0];
01691         p1[1] = v1[1];
01692         //point on the edge vector of area2 closest to v2
01693         p2[0] = v3[0];
01694         p2[1] = v2[1];
01695     } //end else
01696     //
01697     if (dir1[0])
01698     {
01699         //
01700         a1 = dir1[1] / dir1[0];
01701         b1 = v1[1] - a1 * v1[0];
01702         //point on the edge vector of area1 closest to v3
01703         p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
01704         p3[1] = a1 * p3[0] + b1;
01705         //point on the edge vector of area1 closest to v4
01706         p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
01707         p4[1] = a1 * p4[0] + b1;
01708     } //end if
01709     else
01710     {
01711         //point on the edge vector of area1 closest to v3
01712         p3[0] = v1[0];
01713         p3[1] = v3[1];
01714         //point on the edge vector of area1 closest to v4
01715         p4[0] = v1[0];
01716         p4[1] = v4[1];
01717     } //end else
01718     //start with zero z-coordinates
01719     p1[2] = 0;
01720     p2[2] = 0;
01721     p3[2] = 0;
01722     p4[2] = 0;
01723     //calculate the z-coordinates from the ground planes
01724     p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
01725     p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
01726     p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
01727     p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
01728     //
01729     founddist = qfalse;
01730     //
01731     if (VectorBetweenVectors(p1, v3, v4))
01732     {
01733         dist = VectorDistance(v1, p1);
01734         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01735         {
01736             VectorMiddle(beststart, v1, beststart);
01737             VectorMiddle(bestend, p1, bestend);
01738         } //end if
01739         else if (dist < bestdist)
01740         {
01741             bestdist = dist;
01742             VectorCopy(v1, beststart);
01743             VectorCopy(p1, bestend);
01744         } //end if
01745         founddist = qtrue;
01746     } //end if
01747     if (VectorBetweenVectors(p2, v3, v4))
01748     {
01749         dist = VectorDistance(v2, p2);
01750         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01751         {
01752             VectorMiddle(beststart, v2, beststart);
01753             VectorMiddle(bestend, p2, bestend);
01754         } //end if
01755         else if (dist < bestdist)
01756         {
01757             bestdist = dist;
01758             VectorCopy(v2, beststart);
01759             VectorCopy(p2, bestend);
01760         } //end if
01761         founddist = qtrue;
01762     } //end else if
01763     if (VectorBetweenVectors(p3, v1, v2))
01764     {
01765         dist = VectorDistance(v3, p3);
01766         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01767         {
01768             VectorMiddle(beststart, p3, beststart);
01769             VectorMiddle(bestend, v3, bestend);
01770         } //end if
01771         else if (dist < bestdist)
01772         {
01773             bestdist = dist;
01774             VectorCopy(p3, beststart);
01775             VectorCopy(v3, bestend);
01776         } //end if
01777         founddist = qtrue;
01778     } //end else if
01779     if (VectorBetweenVectors(p4, v1, v2))
01780     {
01781         dist = VectorDistance(v4, p4);
01782         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01783         {
01784             VectorMiddle(beststart, p4, beststart);
01785             VectorMiddle(bestend, v4, bestend);
01786         } //end if
01787         else if (dist < bestdist)
01788         {
01789             bestdist = dist;
01790             VectorCopy(p4, beststart);
01791             VectorCopy(v4, bestend);
01792         } //end if
01793         founddist = qtrue;
01794     } //end else if
01795     //if no shortest distance was found the shortest distance
01796     //is between one of the vertexes of edge1 and one of edge2
01797     if (!founddist)
01798     {
01799         dist = VectorDistance(v1, v3);
01800         if (dist < bestdist)
01801         {
01802             bestdist = dist;
01803             VectorCopy(v1, beststart);
01804             VectorCopy(v3, bestend);
01805         } //end if
01806         dist = VectorDistance(v1, v4);
01807         if (dist < bestdist)
01808         {
01809             bestdist = dist;
01810             VectorCopy(v1, beststart);
01811             VectorCopy(v4, bestend);
01812         } //end if
01813         dist = VectorDistance(v2, v3);
01814         if (dist < bestdist)
01815         {
01816             bestdist = dist;
01817             VectorCopy(v2, beststart);
01818             VectorCopy(v3, bestend);
01819         } //end if
01820         dist = VectorDistance(v2, v4);
01821         if (dist < bestdist)
01822         {
01823             bestdist = dist;
01824             VectorCopy(v2, beststart);
01825             VectorCopy(v4, bestend);
01826         } //end if
01827     } //end if
01828     return bestdist;
01829 } //end of the function AAS_ClosestEdgePoints*/
01830 
01831 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
01832                             aas_plane_t *plane1, aas_plane_t *plane2,
01833                             vec3_t beststart1, vec3_t bestend1,
01834                             vec3_t beststart2, vec3_t bestend2, float bestdist)
01835 {
01836     vec3_t dir1, dir2, p1, p2, p3, p4;
01837     float a1, a2, b1, b2, dist, dist1, dist2;
01838     int founddist;
01839 
01840     //edge vectors
01841     VectorSubtract(v2, v1, dir1);
01842     VectorSubtract(v4, v3, dir2);
01843     //get the horizontal directions
01844     dir1[2] = 0;
01845     dir2[2] = 0;
01846     //
01847     // p1 = point on an edge vector of area2 closest to v1
01848     // p2 = point on an edge vector of area2 closest to v2
01849     // p3 = point on an edge vector of area1 closest to v3
01850     // p4 = point on an edge vector of area1 closest to v4
01851     //
01852     if (dir2[0])
01853     {
01854         a2 = dir2[1] / dir2[0];
01855         b2 = v3[1] - a2 * v3[0];
01856         //point on the edge vector of area2 closest to v1
01857         p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
01858         p1[1] = a2 * p1[0] + b2;
01859         //point on the edge vector of area2 closest to v2
01860         p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
01861         p2[1] = a2 * p2[0] + b2;
01862     } //end if
01863     else
01864     {
01865         //point on the edge vector of area2 closest to v1
01866         p1[0] = v3[0];
01867         p1[1] = v1[1];
01868         //point on the edge vector of area2 closest to v2
01869         p2[0] = v3[0];
01870         p2[1] = v2[1];
01871     } //end else
01872     //
01873     if (dir1[0])
01874     {
01875         //
01876         a1 = dir1[1] / dir1[0];
01877         b1 = v1[1] - a1 * v1[0];
01878         //point on the edge vector of area1 closest to v3
01879         p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
01880         p3[1] = a1 * p3[0] + b1;
01881         //point on the edge vector of area1 closest to v4
01882         p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
01883         p4[1] = a1 * p4[0] + b1;
01884     } //end if
01885     else
01886     {
01887         //point on the edge vector of area1 closest to v3
01888         p3[0] = v1[0];
01889         p3[1] = v3[1];
01890         //point on the edge vector of area1 closest to v4
01891         p4[0] = v1[0];
01892         p4[1] = v4[1];
01893     } //end else
01894     //start with zero z-coordinates
01895     p1[2] = 0;
01896     p2[2] = 0;
01897     p3[2] = 0;
01898     p4[2] = 0;
01899     //calculate the z-coordinates from the ground planes
01900     p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
01901     p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
01902     p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
01903     p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
01904     //
01905     founddist = qfalse;
01906     //
01907     if (VectorBetweenVectors(p1, v3, v4))
01908     {
01909         dist = VectorDistance(v1, p1);
01910         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01911         {
01912             dist1 = VectorDistance(beststart1, v1);
01913             dist2 = VectorDistance(beststart2, v1);
01914             if (dist1 > dist2)
01915             {
01916                 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2);
01917             } //end if
01918             else
01919             {
01920                 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1);
01921             } //end else
01922             dist1 = VectorDistance(bestend1, p1);
01923             dist2 = VectorDistance(bestend2, p1);
01924             if (dist1 > dist2)
01925             {
01926                 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2);
01927             } //end if
01928             else
01929             {
01930                 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1);
01931             } //end else
01932         } //end if
01933         else if (dist < bestdist)
01934         {
01935             bestdist = dist;
01936             VectorCopy(v1, beststart1);
01937             VectorCopy(v1, beststart2);
01938             VectorCopy(p1, bestend1);
01939             VectorCopy(p1, bestend2);
01940         } //end if
01941         founddist = qtrue;
01942     } //end if
01943     if (VectorBetweenVectors(p2, v3, v4))
01944     {
01945         dist = VectorDistance(v2, p2);
01946         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01947         {
01948             dist1 = VectorDistance(beststart1, v2);
01949             dist2 = VectorDistance(beststart2, v2);
01950             if (dist1 > dist2)
01951             {
01952                 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2);
01953             } //end if
01954             else
01955             {
01956                 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1);
01957             } //end else
01958             dist1 = VectorDistance(bestend1, p2);
01959             dist2 = VectorDistance(bestend2, p2);
01960             if (dist1 > dist2)
01961             {
01962                 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2);
01963             } //end if
01964             else
01965             {
01966                 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1);
01967             } //end else
01968         } //end if
01969         else if (dist < bestdist)
01970         {
01971             bestdist = dist;
01972             VectorCopy(v2, beststart1);
01973             VectorCopy(v2, beststart2);
01974             VectorCopy(p2, bestend1);
01975             VectorCopy(p2, bestend2);
01976         } //end if
01977         founddist = qtrue;
01978     } //end else if
01979     if (VectorBetweenVectors(p3, v1, v2))
01980     {
01981         dist = VectorDistance(v3, p3);
01982         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
01983         {
01984             dist1 = VectorDistance(beststart1, p3);
01985             dist2 = VectorDistance(beststart2, p3);
01986             if (dist1 > dist2)
01987             {
01988                 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2);
01989             } //end if
01990             else
01991             {
01992                 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1);
01993             } //end else
01994             dist1 = VectorDistance(bestend1, v3);
01995             dist2 = VectorDistance(bestend2, v3);
01996             if (dist1 > dist2)
01997             {
01998                 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2);
01999             } //end if
02000             else
02001             {
02002                 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1);
02003             } //end else
02004         } //end if
02005         else if (dist < bestdist)
02006         {
02007             bestdist = dist;
02008             VectorCopy(p3, beststart1);
02009             VectorCopy(p3, beststart2);
02010             VectorCopy(v3, bestend1);
02011             VectorCopy(v3, bestend2);
02012         } //end if
02013         founddist = qtrue;
02014     } //end else if
02015     if (VectorBetweenVectors(p4, v1, v2))
02016     {
02017         dist = VectorDistance(v4, p4);
02018         if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
02019         {
02020             dist1 = VectorDistance(beststart1, p4);
02021             dist2 = VectorDistance(beststart2, p4);
02022             if (dist1 > dist2)
02023             {
02024                 if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2);
02025             } //end if
02026             else
02027             {
02028                 if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1);
02029             } //end else
02030             dist1 = VectorDistance(bestend1, v4);
02031             dist2 = VectorDistance(bestend2, v4);
02032             if (dist1 > dist2)
02033             {
02034                 if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2);
02035             } //end if
02036             else
02037             {
02038                 if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1);
02039             } //end else
02040         } //end if
02041         else if (dist < bestdist)
02042         {
02043             bestdist = dist;
02044             VectorCopy(p4, beststart1);
02045             VectorCopy(p4, beststart2);
02046             VectorCopy(v4, bestend1);
02047             VectorCopy(v4, bestend2);
02048         } //end if
02049         founddist = qtrue;
02050     } //end else if
02051     //if no shortest distance was found the shortest distance
02052     //is between one of the vertexes of edge1 and one of edge2
02053     if (!founddist)
02054     {
02055         dist = VectorDistance(v1, v3);
02056         if (dist < bestdist)
02057         {
02058             bestdist = dist;
02059             VectorCopy(v1, beststart1);
02060             VectorCopy(v1, beststart2);
02061             VectorCopy(v3, bestend1);
02062             VectorCopy(v3, bestend2);
02063         } //end if
02064         dist = VectorDistance(v1, v4);
02065         if (dist < bestdist)
02066         {
02067             bestdist = dist;
02068             VectorCopy(v1, beststart1);
02069             VectorCopy(v1, beststart2);
02070             VectorCopy(v4, bestend1);
02071             VectorCopy(v4, bestend2);
02072         } //end if
02073         dist = VectorDistance(v2, v3);
02074         if (dist < bestdist)
02075         {
02076             bestdist = dist;
02077             VectorCopy(v2, beststart1);
02078             VectorCopy(v2, beststart2);
02079             VectorCopy(v3, bestend1);
02080             VectorCopy(v3, bestend2);
02081         } //end if
02082         dist = VectorDistance(v2, v4);
02083         if (dist < bestdist)
02084         {
02085             bestdist = dist;
02086             VectorCopy(v2, beststart1);
02087             VectorCopy(v2, beststart2);
02088             VectorCopy(v4, bestend1);
02089             VectorCopy(v4, bestend2);
02090         } //end if
02091     } //end if
02092     return bestdist;
02093 } //end of the function AAS_ClosestEdgePoints
02094 //===========================================================================
02095 // creates possible jump reachabilities between the areas
02096 //
02097 // The two closest points on the ground of the areas are calculated
02098 // One of the points will be on an edge of a ground face of area1 and
02099 // one on an edge of a ground face of area2.
02100 // If there is a range of closest points the point in the middle of this range
02101 // is selected.
02102 // Between these two points there must be one or more gaps.
02103 // If the gaps exist a potential jump is predicted.
02104 //
02105 // Parameter:               -
02106 // Returns:                 -
02107 // Changes Globals:     -
02108 //===========================================================================
02109 int AAS_Reachability_Jump(int area1num, int area2num)
02110 {
02111     int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype;
02112     int stopevent, areas[10], numareas;
02113     float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed;
02114     vec_t *v1, *v2, *v3, *v4;
02115     vec3_t beststart, beststart2, bestend, bestend2;
02116     vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards;
02117     aas_area_t *area1, *area2;
02118     aas_face_t *face1, *face2;
02119     aas_edge_t *edge1, *edge2;
02120     aas_plane_t *plane1, *plane2, *plane;
02121     aas_trace_t trace;
02122     aas_clientmove_t move;
02123     aas_lreachability_t *lreach;
02124 
02125     if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
02126     //cannot jump from or to a crouch area
02127     if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse;
02128     //
02129     area1 = &aasworld.areas[area1num];
02130     area2 = &aasworld.areas[area2num];
02131     //
02132     phys_jumpvel = aassettings.phys_jumpvel;
02133     //maximum distance a player can jump
02134     maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel);
02135     //maximum height a player can jump with the given initial z velocity
02136     maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
02137 
02138     //if the areas are not near anough in the x-y direction
02139     for (i = 0; i < 2; i++)
02140     {
02141         if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse;
02142         if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse;
02143     } //end for
02144     //if area2 is way to high to jump up to
02145     if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse;
02146     //
02147     bestdist = 999999;
02148     //
02149     for (i = 0; i < area1->numfaces; i++)
02150     {
02151         face1num = aasworld.faceindex[area1->firstface + i];
02152         face1 = &aasworld.faces[abs(face1num)];
02153         //if not a ground face
02154         if (!(face1->faceflags & FACE_GROUND)) continue;
02155         //
02156         for (j = 0; j < area2->numfaces; j++)
02157         {
02158             face2num = aasworld.faceindex[area2->firstface + j];
02159             face2 = &aasworld.faces[abs(face2num)];
02160             //if not a ground face
02161             if (!(face2->faceflags & FACE_GROUND)) continue;
02162             //
02163             for (k = 0; k < face1->numedges; k++)
02164             {
02165                 edge1num = abs(aasworld.edgeindex[face1->firstedge + k]);
02166                 edge1 = &aasworld.edges[edge1num];
02167                 for (l = 0; l < face2->numedges; l++)
02168                 {
02169                     edge2num = abs(aasworld.edgeindex[face2->firstedge + l]);
02170                     edge2 = &aasworld.edges[edge2num];
02171                     //calculate the minimum distance between the two edges
02172                     v1 = aasworld.vertexes[edge1->v[0]];
02173                     v2 = aasworld.vertexes[edge1->v[1]];
02174                     v3 = aasworld.vertexes[edge2->v[0]];
02175                     v4 = aasworld.vertexes[edge2->v[1]];
02176                     //get the ground planes
02177                     plane1 = &aasworld.planes[face1->planenum];
02178                     plane2 = &aasworld.planes[face2->planenum];
02179                     //
02180                     bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2,
02181                                                         beststart, bestend,
02182                                                         beststart2, bestend2, bestdist);
02183                 } //end for
02184             } //end for
02185         } //end for
02186     } //end for
02187     VectorMiddle(beststart, beststart2, beststart);
02188     VectorMiddle(bestend, bestend2, bestend);
02189     if (bestdist > 4 && bestdist < maxjumpdistance)
02190     {
02191 //      Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist);
02192         // if very close and almost no height difference then the bot can walk
02193         if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8)
02194         {
02195             speed = 400;
02196             traveltype = TRAVEL_WALKOFFLEDGE;
02197         } //end if
02198         else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed))
02199         {
02200             //FIXME: why multiply with 1.2???
02201             speed *= 1.2f;
02202             traveltype = TRAVEL_WALKOFFLEDGE;
02203         } //end else if
02204         else
02205         {
02206             //get the horizontal speed for the jump, if it isn't possible to calculate this
02207             //speed (the jump is not possible) then there's no jump reachability created
02208             if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed))
02209                 return qfalse;
02210             speed *= 1.05f;
02211             traveltype = TRAVEL_JUMP;
02212             //
02213             //NOTE: test if the horizontal distance isn't too small
02214             VectorSubtract(bestend, beststart, dir);
02215             dir[2] = 0;
02216             if (VectorLength(dir) < 10)
02217                 return qfalse;
02218         } //end if
02219         //
02220         VectorSubtract(bestend, beststart, dir);
02221         VectorNormalize(dir);
02222         VectorMA(beststart, 1, dir, teststart);
02223         //
02224         VectorCopy(teststart, testend);
02225         testend[2] -= 100;
02226         trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
02227         //
02228         if (trace.startsolid)
02229             return qfalse;
02230         if (trace.fraction < 1)
02231         {
02232             plane = &aasworld.planes[trace.planenum];
02233             // if the bot can stand on the surface
02234             if (DotProduct(plane->normal, up) >= 0.7)
02235             {
02236                 // if no lava or slime below
02237                 if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
02238                 {
02239                     if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
02240                         return qfalse;
02241                 } //end if
02242             } //end if
02243         } //end if
02244         //
02245         VectorMA(bestend, -1, dir, teststart);
02246         //
02247         VectorCopy(teststart, testend);
02248         testend[2] -= 100;
02249         trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
02250         //
02251         if (trace.startsolid)
02252             return qfalse;
02253         if (trace.fraction < 1)
02254         {
02255             plane = &aasworld.planes[trace.planenum];
02256             // if the bot can stand on the surface
02257             if (DotProduct(plane->normal, up) >= 0.7)
02258             {
02259                 // if no lava or slime below
02260                 if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
02261                 {
02262                     if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
02263                         return qfalse;
02264                 } //end if
02265             } //end if
02266         } //end if
02267         //
02268         // get command movement
02269         VectorClear(cmdmove);
02270         if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
02271             cmdmove[2] = aassettings.phys_jumpvel;
02272         else
02273             cmdmove[2] = 0;
02274         //
02275         VectorSubtract(bestend, beststart, dir);
02276         dir[2] = 0;
02277         VectorNormalize(dir);
02278         CrossProduct(dir, up, sidewards);
02279         //
02280         stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE;
02281         if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num))
02282             stopevent |= SE_TOUCHCLUSTERPORTAL;
02283         //
02284         for (i = 0; i < 3; i++)
02285         {
02286             //
02287             if (i == 1)
02288                 VectorAdd(testend, sidewards, testend);
02289             else if (i == 2)
02290                 VectorSubtract(bestend, sidewards, testend);
02291             else
02292                 VectorCopy(bestend, testend);
02293             VectorSubtract(testend, beststart, dir);
02294             dir[2] = 0;
02295             VectorNormalize(dir);
02296             VectorScale(dir, speed, velocity);
02297             //
02298             AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue,
02299                                         velocity, cmdmove, 3, 30, 0.1f,
02300                                         stopevent, 0, qfalse);
02301             // if prediction time wasn't enough to fully predict the movement
02302             if (move.frames >= 30)
02303                 return qfalse;
02304             // don't enter slime or lava and don't fall from too high
02305             if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
02306                 return qfalse;
02307             // never jump or fall through a cluster portal
02308             if (move.stopevent & SE_TOUCHCLUSTERPORTAL)
02309                 return qfalse;
02310             //the end position should be in area2, also test a little bit back
02311             //because the predicted jump could have rushed through the area
02312             VectorMA(move.endpos, -64, dir, teststart);
02313             teststart[2] += 1;
02314             numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int));
02315             for (j = 0; j < numareas; j++)
02316             {
02317                 if (areas[j] == area2num)
02318                     break;
02319             } //end for
02320             if (j < numareas)
02321                 break;
02322         }
02323         if (i >= 3)
02324             return qfalse;
02325         //
02326 #ifdef REACH_DEBUG
02327         //create the reachability
02328         Log_Write("jump reachability between %d and %d\r\n", area1num, area2num);
02329 #endif //REACH_DEBUG
02330         //create a new reachability link
02331         lreach = AAS_AllocReachability();
02332         if (!lreach) return qfalse;
02333         lreach->areanum = area2num;
02334         lreach->facenum = 0;
02335         lreach->edgenum = 0;
02336         VectorCopy(beststart, lreach->start);
02337         VectorCopy(bestend, lreach->end);
02338         lreach->traveltype = traveltype;
02339 
02340         VectorSubtract(bestend, beststart, dir);
02341         height = dir[2];
02342         dir[2] = 0;
02343         if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir))
02344         {
02345             lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity;
02346         }
02347         else
02348         {
02349             lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity;
02350         } //end if
02351         //
02352         if (!AAS_AreaJumpPad(area2num))
02353         {
02354             if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5)
02355             {
02356                 lreach->traveltime += aassettings.rs_falldamage5;
02357             } //end if
02358             else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10)
02359             {
02360                 lreach->traveltime += aassettings.rs_falldamage10;
02361             } //end if
02362         } //end if
02363         lreach->next = areareachability[area1num];
02364         areareachability[area1num] = lreach;
02365         //
02366         if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
02367             reach_jump++;
02368         else
02369             reach_walkoffledge++;
02370     } //end if
02371     return qfalse;
02372 } //end of the function AAS_Reachability_Jump
02373 //===========================================================================
02374 // create a possible ladder reachability from area1 to area2
02375 //
02376 // Parameter:               -
02377 // Returns:                 -
02378 // Changes Globals:     -
02379 //===========================================================================
02380 int AAS_Reachability_Ladder(int area1num, int area2num)
02381 {
02382     int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum;
02383     int face1num, face2num, ladderface1num, ladderface2num;
02384     int ladderface1vertical, ladderface2vertical, firstv;
02385     float face1area, face2area, bestface1area, bestface2area;
02386     float phys_jumpvel, maxjumpheight;
02387     vec3_t area1point, area2point, v1, v2, up = {0, 0, 1};
02388     vec3_t mid, lowestpoint, start, end, sharededgevec, dir;
02389     aas_area_t *area1, *area2;
02390     aas_face_t *face1, *face2, *ladderface1, *ladderface2;
02391     aas_plane_t *plane1, *plane2;
02392     aas_edge_t *sharededge, *edge1;
02393     aas_lreachability_t *lreach;
02394     aas_trace_t trace;
02395 
02396     if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse;
02397     //
02398     phys_jumpvel = aassettings.phys_jumpvel;
02399     //maximum height a player can jump with the given initial z velocity
02400     maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
02401 
02402     area1 = &aasworld.areas[area1num];
02403     area2 = &aasworld.areas[area2num];
02404     //
02405     ladderface1 = NULL;
02406     ladderface2 = NULL;
02407     ladderface1num = 0; //make compiler happy
02408     ladderface2num = 0; //make compiler happy
02409     bestface1area = -9999;
02410     bestface2area = -9999;
02411     sharededgenum = 0; //make compiler happy
02412     lowestedgenum = 0; //make compiler happy
02413     //
02414     for (i = 0; i < area1->numfaces; i++)
02415     {
02416         face1num = aasworld.faceindex[area1->firstface + i];
02417         face1 = &aasworld.faces[abs(face1num)];
02418         //if not a ladder face
02419         if (!(face1->faceflags & FACE_LADDER)) continue;
02420         //
02421         for (j = 0; j < area2->numfaces; j++)
02422         {
02423             face2num = aasworld.faceindex[area2->firstface + j];
02424             face2 = &aasworld.faces[abs(face2num)];
02425             //if not a ladder face
02426             if (!(face2->faceflags & FACE_LADDER)) continue;
02427             //check if the faces share an edge
02428             for (k = 0; k < face1->numedges; k++)
02429             {
02430                 edge1num = aasworld.edgeindex[face1->firstedge + k];
02431                 for (l = 0; l < face2->numedges; l++)
02432                 {
02433                     edge2num = aasworld.edgeindex[face2->firstedge + l];
02434                     if (abs(edge1num) == abs(edge2num))
02435                     {
02436                         //get the face with the largest area
02437                         face1area = AAS_FaceArea(face1);
02438                         face2area = AAS_FaceArea(face2);
02439                         if (face1area > bestface1area && face2area > bestface2area)
02440                         {
02441                             bestface1area = face1area;
02442                             bestface2area = face2area;
02443                             ladderface1 = face1;
02444                             ladderface2 = face2;
02445                             ladderface1num = face1num;
02446                             ladderface2num = face2num;
02447                             sharededgenum = edge1num;
02448                         } //end if
02449                         break;
02450                     } //end if
02451                 } //end for
02452                 if (l != face2->numedges) break;
02453             } //end for
02454         } //end for
02455     } //end for
02456     //
02457     if (ladderface1 && ladderface2)
02458     {
02459         //get the middle of the shared edge
02460         sharededge = &aasworld.edges[abs(sharededgenum)];
02461         firstv = sharededgenum < 0;
02462         //
02463         VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1);
02464         VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2);
02465         VectorAdd(v1, v2, area1point);
02466         VectorScale(area1point, 0.5, area1point);
02467         VectorCopy(area1point, area2point);
02468         //
02469         //if the face plane in area 1 is pretty much vertical
02470         plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)];
02471         plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)];
02472         //
02473         //get the points really into the areas
02474         VectorSubtract(v2, v1, sharededgevec);
02475         CrossProduct(plane1->normal, sharededgevec, dir);
02476         VectorNormalize(dir);
02477         //NOTE: 32 because that's larger than 16 (bot bbox x,y)
02478         VectorMA(area1point, -32, dir, area1point);
02479         VectorMA(area2point, 32, dir, area2point);
02480         //
02481         ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1;
02482         ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1;
02483         //there's only reachability between vertical ladder faces
02484         if (!ladderface1vertical && !ladderface2vertical) return qfalse;
02485         //if both vertical ladder faces
02486         if (ladderface1vertical && ladderface2vertical
02487                     //and the ladder faces do not make a sharp corner
02488                     && DotProduct(plane1->normal, plane2->normal) > 0.7
02489                     //and the shared edge is not too vertical
02490                     && abs(DotProduct(sharededgevec, up)) < 0.7)
02491         {
02492             //create a new reachability link
02493             lreach = AAS_AllocReachability();
02494             if (!lreach) return qfalse;
02495             lreach->areanum = area2num;
02496             lreach->facenum = ladderface1num;
02497             lreach->edgenum = abs(sharededgenum);
02498             VectorCopy(area1point, lreach->start);
02499             //VectorCopy(area2point, lreach->end);
02500             VectorMA(area2point, -3, plane1->normal, lreach->end);
02501             lreach->traveltype = TRAVEL_LADDER;
02502             lreach->traveltime = 10;
02503             lreach->next = areareachability[area1num];
02504             areareachability[area1num] = lreach;
02505             //
02506             reach_ladder++;
02507             //create a new reachability link
02508             lreach = AAS_AllocReachability();
02509             if (!lreach) return qfalse;
02510             lreach->areanum = area1num;
02511             lreach->facenum = ladderface2num;
02512             lreach->edgenum = abs(sharededgenum);
02513             VectorCopy(area2point, lreach->start);
02514             //VectorCopy(area1point, lreach->end);
02515             VectorMA(area1point, -3, plane1->normal, lreach->end);
02516             lreach->traveltype = TRAVEL_LADDER;
02517             lreach->traveltime = 10;
02518             lreach->next = areareachability[area2num];
02519             areareachability[area2num] = lreach;
02520             //
02521             reach_ladder++;
02522             //
02523             return qtrue;
02524         } //end if
02525         //if the second ladder face is also a ground face
02526         //create ladder end (just ladder) reachability and
02527         //walk off a ladder (ledge) reachability
02528         if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND))
02529         {
02530             //create a new reachability link
02531             lreach = AAS_AllocReachability();
02532             if (!lreach) return qfalse;
02533             lreach->areanum = area2num;
02534             lreach->facenum = ladderface1num;
02535             lreach->edgenum = abs(sharededgenum);
02536             VectorCopy(area1point, lreach->start);
02537             VectorCopy(area2point, lreach->end);
02538             lreach->end[2] += 16;
02539             VectorMA(lreach->end, -15, plane1->normal, lreach->end);
02540             lreach->traveltype = TRAVEL_LADDER;
02541             lreach->traveltime = 10;
02542             lreach->next = areareachability[area1num];
02543             areareachability[area1num] = lreach;
02544             //
02545             reach_ladder++;
02546             //create a new reachability link
02547             lreach = AAS_AllocReachability();
02548             if (!lreach) return qfalse;
02549             lreach->areanum = area1num;
02550             lreach->facenum = ladderface2num;
02551             lreach->edgenum = abs(sharededgenum);
02552             VectorCopy(area2point, lreach->start);
02553             VectorCopy(area1point, lreach->end);
02554             lreach->traveltype = TRAVEL_WALKOFFLEDGE;
02555             lreach->traveltime = 10;
02556             lreach->next = areareachability[area2num];
02557             areareachability[area2num] = lreach;
02558             //
02559             reach_walkoffledge++;
02560             //
02561             return qtrue;
02562         } //end if
02563         //
02564         if (ladderface1vertical)
02565         {
02566             //find lowest edge of the ladder face
02567             lowestpoint[2] = 99999;
02568             for (i = 0; i < ladderface1->numedges; i++)
02569             {
02570                 edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]);
02571                 edge1 = &aasworld.edges[edge1num];
02572                 //
02573                 VectorCopy(aasworld.vertexes[edge1->v[0]], v1);
02574                 VectorCopy(aasworld.vertexes[edge1->v[1]], v2);
02575                 //
02576                 VectorAdd(v1, v2, mid);
02577                 VectorScale(mid, 0.5, mid);
02578                 //
02579                 if (mid[2] < lowestpoint[2])
02580                 {
02581                     VectorCopy(mid, lowestpoint);
02582                     lowestedgenum = edge1num;
02583                 } //end if
02584             } //end for
02585             //
02586             plane1 = &aasworld.planes[ladderface1->planenum];
02587             //trace down in the middle of this edge
02588             VectorMA(lowestpoint, 5, plane1->normal, start);
02589             VectorCopy(start, end);
02590             start[2] += 5;
02591             end[2] -= 100;
02592             //trace without entity collision
02593             trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
02594             //
02595             //
02596 #ifdef REACH_DEBUG
02597             if (trace.startsolid)
02598             {
02599                 Log_Write("trace from area %d started in solid\r\n", area1num);
02600             } //end if
02601 #endif //REACH_DEBUG
02602             //
02603             trace.endpos[2] += 1;
02604             area2num = AAS_PointAreaNum(trace.endpos);
02605             //
02606             area2 = &aasworld.areas[area2num];
02607             for (i = 0; i < area2->numfaces; i++)
02608             {
02609                 face2num = aasworld.faceindex[area2->firstface + i];
02610                 face2 = &aasworld.faces[abs(face2num)];
02611                 //
02612                 if (face2->faceflags & FACE_LADDER)
02613                 {
02614                     plane2 = &aasworld.planes[face2->planenum];
02615                     if (abs(DotProduct(plane2->normal, up)) < 0.1) break;
02616                 } //end if
02617             } //end for
02618             //if from another area without vertical ladder faces
02619             if (i >= area2->numfaces && area2num != area1num &&
02620                         //the reachabilities shouldn't exist already
02621                         !AAS_ReachabilityExists(area1num, area2num) &&
02622                         !AAS_ReachabilityExists(area2num, area1num))
02623             {
02624                 //if the height is jumpable
02625                 if (start[2] - trace.endpos[2] < maxjumpheight)
02626                 {
02627                     //create a new reachability link
02628                     lreach = AAS_AllocReachability();
02629                     if (!lreach) return qfalse;
02630                     lreach->areanum = area2num;
02631                     lreach->facenum = ladderface1num;
02632                     lreach->edgenum = lowestedgenum;
02633                     VectorCopy(lowestpoint, lreach->start);
02634                     VectorCopy(trace.endpos, lreach->end);
02635                     lreach->traveltype = TRAVEL_LADDER;
02636                     lreach->traveltime = 10;
02637                     lreach->next = areareachability[area1num];
02638                     areareachability[area1num] = lreach;
02639                     //
02640                     reach_ladder++;
02641                     //create a new reachability link
02642                     lreach = AAS_AllocReachability();
02643                     if (!lreach) return qfalse;
02644                     lreach->areanum = area1num;
02645                     lreach->facenum = ladderface1num;
02646                     lreach->edgenum = lowestedgenum;
02647                     VectorCopy(trace.endpos, lreach->start);
02648                     //get the end point a little bit into the ladder
02649                     VectorMA(lowestpoint, -5, plane1->normal, lreach->end);
02650                     //get the end point a little higher
02651                     lreach->end[2] += 10;
02652                     lreach->traveltype = TRAVEL_JUMP;
02653                     lreach->traveltime = 10;
02654                     lreach->next = areareachability[area2num];
02655                     areareachability[area2num] = lreach;
02656                     //
02657                     reach_jump++;   
02658                     //
02659                     return qtrue;
02660 #ifdef REACH_DEBUG
02661                     Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num);
02662 #endif //REACH_DEBUG
02663                 } //end if
02664 #ifdef REACH_DEBUG
02665                 else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num);
02666 #endif //REACH_DEBUG
02667             } //end if
02668             /*//if slime or lava below the ladder
02669             //try jump reachability from far towards the ladder
02670             if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME
02671                                                     | AREACONTENTS_LAVA))
02672             {
02673                 for (i = 20; i <= 120; i += 20)
02674                 {
02675                     //trace down in the middle of this edge
02676                     VectorMA(lowestpoint, i, plane1->normal, start);
02677                     VectorCopy(start, end);
02678                     start[2] += 5;
02679                     end[2] -= 100;
02680                     //trace without entity collision
02681                     trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
02682                     //
02683                     if (trace.startsolid) break;
02684                     trace.endpos[2] += 1;
02685                     area2num = AAS_PointAreaNum(trace.endpos);
02686                     if (area2num == area1num) continue;
02687                     //
02688                     if (start[2] - trace.endpos[2] > maxjumpheight) continue;
02689                     if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME
02690                                                 | AREACONTENTS_LAVA)) continue;
02691                     //
02692                     //create a new reachability link
02693                     lreach = AAS_AllocReachability();
02694                     if (!lreach) return qfalse;
02695                     lreach->areanum = area1num;
02696                     lreach->facenum = ladderface1num;
02697                     lreach->edgenum = lowestedgenum;
02698                     VectorCopy(trace.endpos, lreach->start);
02699                     VectorCopy(lowestpoint, lreach->end);
02700                     lreach->end[2] += 5;
02701                     lreach->traveltype = TRAVEL_JUMP;
02702                     lreach->traveltime = 10;
02703                     lreach->next = areareachability[area2num];
02704                     areareachability[area2num] = lreach;
02705                     //
02706                     reach_jump++;
02707                     //
02708                     Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num);
02709                     //
02710                     break;
02711                 } //end for
02712             } //end if*/
02713         } //end if
02714     } //end if
02715     return qfalse;
02716 } //end of the function AAS_Reachability_Ladder
02717 //===========================================================================
02718 //
02719 // Parameter:           -
02720 // Returns:             -
02721 // Changes Globals:     -
02722 //===========================================================================
02723 int AAS_TravelFlagsForTeam(int ent)
02724 {
02725     int notteam;
02726 
02727     if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", &notteam))
02728         return 0;
02729     if (notteam == 1)
02730         return TRAVELFLAG_NOTTEAM1;
02731     if (notteam == 2)
02732         return TRAVELFLAG_NOTTEAM2;
02733     return 0;
02734 } //end of the function AAS_TravelFlagsForTeam
02735 //===========================================================================
02736 // create possible teleporter reachabilities
02737 // this is very game dependent.... :(
02738 //
02739 // classname = trigger_multiple or trigger_teleport
02740 // target = "t1"
02741 //
02742 // classname = target_teleporter
02743 // targetname = "t1"
02744 // target = "t2"
02745 //
02746 // classname = misc_teleporter_dest
02747 // targetname = "t2"
02748 //
02749 // Parameter:               -
02750 // Returns:                 -
02751 // Changes Globals:     -
02752 //===========================================================================
02753 void AAS_Reachability_Teleport(void)
02754 {
02755     int area1num, area2num;
02756     char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
02757     char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
02758     int ent, dest;
02759     float angle;
02760     vec3_t origin, destorigin, mins, maxs, end, angles;
02761     vec3_t mid, velocity, cmdmove;
02762     aas_lreachability_t *lreach;
02763     aas_clientmove_t move;
02764     aas_trace_t trace;
02765     aas_link_t *areas, *link;
02766 
02767     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
02768     {
02769         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
02770         if (!strcmp(classname, "trigger_multiple"))
02771         {
02772             AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
02773 //#ifdef REACH_DEBUG
02774             botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model);
02775 //#endif REACH_DEBUG
02776             VectorClear(angles);
02777             AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
02778             //
02779             if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
02780             {
02781                 botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n",
02782                                     origin[0], origin[1], origin[2]);
02783                 continue;
02784             } //end if
02785             for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
02786             {
02787                 if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue;
02788                 if (!strcmp(classname, "target_teleporter"))
02789                 {
02790                     if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue;
02791                     if (!strcmp(targetname, target))
02792                     {
02793                         break;
02794                     } //end if
02795                 } //end if
02796             } //end for
02797             if (!dest)
02798             {
02799                 continue;
02800             } //end if
02801             if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY))
02802             {
02803                 botimport.Print(PRT_ERROR, "target_teleporter without target\n");
02804                 continue;
02805             } //end if
02806         } //end else
02807         else if (!strcmp(classname, "trigger_teleport"))
02808         {
02809             AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
02810 //#ifdef REACH_DEBUG
02811             botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model);
02812 //#endif REACH_DEBUG
02813             VectorClear(angles);
02814             AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
02815             //
02816             if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
02817             {
02818                 botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n",
02819                                     origin[0], origin[1], origin[2]);
02820                 continue;
02821             } //end if
02822         } //end if
02823         else
02824         {
02825             continue;
02826         } //end else
02827         //
02828         for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
02829         {
02830             //classname should be misc_teleporter_dest
02831             //but I've also seen target_position and actually any
02832             //entity could be used... burp
02833             if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY))
02834             {
02835                 if (!strcmp(targetname, target))
02836                 {
02837                     break;
02838                 } //end if
02839             } //end if
02840         } //end for
02841         if (!dest)
02842         {
02843             botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target);
02844             continue;
02845         } //end if
02846         if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin))
02847         {
02848             botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target);
02849             continue;
02850         } //end if
02851         //
02852         area2num = AAS_PointAreaNum(destorigin);
02853         //if not teleported into a teleporter or into a jumppad
02854         if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num))
02855         {
02856             VectorCopy(destorigin, end);
02857             end[2] -= 64;
02858             trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);
02859             if (trace.startsolid)
02860             {
02861                 botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target);
02862                 continue;
02863             } //end if
02864             area2num = AAS_PointAreaNum(trace.endpos);
02865             //
02866             /*
02867             if (!AAS_AreaTeleporter(area2num) &&
02868                 !AAS_AreaJumpPad(area2num) &&
02869                 !AAS_AreaGrounded(area2num))
02870             {
02871                 VectorCopy(trace.endpos, destorigin);
02872             }
02873             else*/
02874             {
02875                 //predict where you'll end up
02876                 AAS_FloatForBSPEpairKey(dest, "angle", &angle);
02877                 if (angle)
02878                 {
02879                     VectorSet(angles, 0, angle, 0);
02880                     AngleVectors(angles, velocity, NULL, NULL);
02881                     VectorScale(velocity, 400, velocity);
02882                 } //end if
02883                 else
02884                 {
02885                     VectorClear(velocity);
02886                 } //end else
02887                 VectorClear(cmdmove);
02888                 AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse,
02889                                         velocity, cmdmove, 0, 30, 0.1f,
02890                                         SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
02891                                         SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue);
02892                 area2num = AAS_PointAreaNum(move.endpos);
02893                 if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
02894                 {
02895                     botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target);
02896                 } //end if
02897                 VectorCopy(move.endpos, destorigin);
02898             } //end else
02899         } //end if
02900         //
02901         //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]);
02902         //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]);
02903         //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]);
02904         VectorAdd(origin, mins, mins);
02905         VectorAdd(origin, maxs, maxs);
02906         //
02907         VectorAdd(mins, maxs, mid);
02908         VectorScale(mid, 0.5, mid);
02909         //link an invalid (-1) entity
02910         areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH);
02911         if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n");
02912         //
02913         for (link = areas; link; link = link->next_area)
02914         {
02915             //if (!AAS_AreaGrounded(link->areanum)) continue;
02916             if (!AAS_AreaTeleporter(link->areanum)) continue;
02917             //
02918             area1num = link->areanum;
02919             //create a new reachability link
02920             lreach = AAS_AllocReachability();
02921             if (!lreach) break;
02922             lreach->areanum = area2num;
02923             lreach->facenum = 0;
02924             lreach->edgenum = 0;
02925             VectorCopy(mid, lreach->start);
02926             VectorCopy(destorigin, lreach->end);
02927             lreach->traveltype = TRAVEL_TELEPORT;
02928             lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
02929             lreach->traveltime = aassettings.rs_teleport;
02930             lreach->next = areareachability[area1num];
02931             areareachability[area1num] = lreach;
02932             //
02933             reach_teleport++;
02934         } //end for
02935         //unlink the invalid entity
02936         AAS_UnlinkFromAreas(areas);
02937     } //end for
02938 } //end of the function AAS_Reachability_Teleport
02939 //===========================================================================
02940 // create possible elevator (func_plat) reachabilities
02941 // this is very game dependent.... :(
02942 //
02943 // Parameter:           -
02944 // Returns:             -
02945 // Changes Globals:     -
02946 //===========================================================================
02947 void AAS_Reachability_Elevator(void)
02948 {
02949     int area1num, area2num, modelnum, i, j, k, l, n, p;
02950     float lip, height, speed;
02951     char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY];
02952     int ent;
02953     vec3_t mins, maxs, origin, angles = {0, 0, 0};
02954     vec3_t pos1, pos2, mids, platbottom, plattop;
02955     vec3_t bottomorg, toporg, start, end, dir;
02956     vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8];
02957     aas_lreachability_t *lreach;
02958     aas_trace_t trace;
02959 
02960 #ifdef REACH_DEBUG
02961     Log_Write("AAS_Reachability_Elevator\r\n");
02962 #endif //REACH_DEBUG
02963     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
02964     {
02965         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
02966         if (!strcmp(classname, "func_plat"))
02967         {
02968 #ifdef REACH_DEBUG
02969             Log_Write("found func plat\r\n");
02970 #endif //REACH_DEBUG
02971             if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
02972             {
02973                 botimport.Print(PRT_ERROR, "func_plat without model\n");
02974                 continue;
02975             } //end if
02976             //get the model number, and skip the leading *
02977             modelnum = atoi(model+1);
02978             if (modelnum <= 0)
02979             {
02980                 botimport.Print(PRT_ERROR, "func_plat with invalid model number\n");
02981                 continue;
02982             } //end if
02983             //get the mins, maxs and origin of the model
02984             //NOTE: the origin is usually (0,0,0) and the mins and maxs
02985             //      are the absolute mins and maxs
02986             AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
02987             //
02988             AAS_VectorForBSPEpairKey(ent, "origin", origin);
02989             //pos1 is the top position, pos2 is the bottom
02990             VectorCopy(origin, pos1);
02991             VectorCopy(origin, pos2);
02992             //get the lip of the plat
02993             AAS_FloatForBSPEpairKey(ent, "lip", &lip);
02994             if (!lip) lip = 8;
02995             //get the movement height of the plat
02996             AAS_FloatForBSPEpairKey(ent, "height", &height);
02997             if (!height) height = (maxs[2] - mins[2]) - lip;
02998             //get the speed of the plat
02999             AAS_FloatForBSPEpairKey(ent, "speed", &speed);
03000             if (!speed) speed = 200;
03001             //get bottom position below pos1
03002             pos2[2] -= height;
03003             //
03004             //get a point just above the plat in the bottom position
03005             VectorAdd(mins, maxs, mids);
03006             VectorMA(pos2, 0.5, mids, platbottom);
03007             platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2;
03008             //get a point just above the plat in the top position
03009             VectorAdd(mins, maxs, mids);
03010             VectorMA(pos2, 0.5, mids, plattop);
03011             plattop[2] = maxs[2] + 2;
03012             //
03013             /*if (!area1num)
03014             {
03015                 Log_Write("no grounded area near plat bottom\r\n");
03016                 continue;
03017             } //end if*/
03018             //get the mins and maxs a little larger
03019             for (i = 0; i < 3; i++)
03020             {
03021                 mins[i] -= 1;
03022                 maxs[i] += 1;
03023             } //end for
03024             //
03025             //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]);
03026             //
03027             VectorAdd(mins, maxs, mids);
03028             VectorScale(mids, 0.5, mids);
03029             //
03030             xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0];
03031             yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1];
03032             //
03033             xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0];
03034             yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1];
03035             //find adjacent areas around the bottom of the plat
03036             for (i = 0; i < 9; i++)
03037             {
03038                 if (i < 8) //check at the sides of the plat
03039                 {
03040                     bottomorg[0] = origin[0] + xvals[i];
03041                     bottomorg[1] = origin[1] + yvals[i];
03042                     bottomorg[2] = platbottom[2] + 16;
03043                     //get a grounded or swim area near the plat in the bottom position
03044                     area1num = AAS_PointAreaNum(bottomorg);
03045                     for (k = 0; k < 16; k++)
03046                     {
03047                         if (area1num)
03048                         {
03049                             if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break;
03050                         } //end if
03051                         bottomorg[2] += 4;
03052                         area1num = AAS_PointAreaNum(bottomorg);
03053                     } //end if
03054                     //if in solid
03055                     if (k >= 16)
03056                     {
03057                         continue;
03058                     } //end if
03059                 } //end if
03060                 else //at the middle of the plat
03061                 {
03062                     VectorCopy(plattop, bottomorg);
03063                     bottomorg[2] += 24;
03064                     area1num = AAS_PointAreaNum(bottomorg);
03065                     if (!area1num) continue;
03066                     VectorCopy(platbottom, bottomorg);
03067                     bottomorg[2] += 24;
03068                 } //end else
03069                 //look at adjacent areas around the top of the plat
03070                 //make larger steps to outside the plat everytime
03071                 for (n = 0; n < 3; n++)
03072                 {
03073                     for (k = 0; k < 3; k++)
03074                     {
03075                         mins[k] -= 4;
03076                         maxs[k] += 4;
03077                     } //end for
03078                     xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0];
03079                     yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1];
03080                     //
03081                     xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0];
03082                     yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1];
03083                     //
03084                     for (j = 0; j < 8; j++)
03085                     {
03086                         toporg[0] = origin[0] + xvals_top[j];
03087                         toporg[1] = origin[1] + yvals_top[j];
03088                         toporg[2] = plattop[2] + 16;
03089                         //get a grounded or swim area near the plat in the top position
03090                         area2num = AAS_PointAreaNum(toporg);
03091                         for (l = 0; l < 16; l++)
03092                         {
03093                             if (area2num)
03094                             {
03095                                 if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num))
03096                                 {
03097                                     VectorCopy(plattop, start);
03098                                     start[2] += 32;
03099                                     VectorCopy(toporg, end);
03100                                     end[2] += 1;
03101                                     trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
03102                                     if (trace.fraction >= 1) break;
03103                                 } //end if
03104                             } //end if
03105                             toporg[2] += 4;
03106                             area2num = AAS_PointAreaNum(toporg);
03107                         } //end if
03108                         //if in solid
03109                         if (l >= 16) continue;
03110                         //never create a reachability in the same area
03111                         if (area2num == area1num) continue;
03112                         //if the area isn't grounded
03113                         if (!AAS_AreaGrounded(area2num)) continue;
03114                         //if there already exists reachability between the areas
03115                         if (AAS_ReachabilityExists(area1num, area2num)) continue;
03116                         //if the reachability start is within the elevator bounding box
03117                         VectorSubtract(bottomorg, platbottom, dir);
03118                         VectorNormalize(dir);
03119                         dir[0] = bottomorg[0] + 24 * dir[0];
03120                         dir[1] = bottomorg[1] + 24 * dir[1];
03121                         dir[2] = bottomorg[2];
03122                         //
03123                         for (p = 0; p < 3; p++)
03124                             if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break;
03125                         if (p >= 3) continue;
03126                         //create a new reachability link
03127                         lreach = AAS_AllocReachability();
03128                         if (!lreach) continue;
03129                         lreach->areanum = area2num;
03130                         //the facenum is the model number
03131                         lreach->facenum = modelnum;
03132                         //the edgenum is the height
03133                         lreach->edgenum = (int) height;
03134                         //
03135                         VectorCopy(dir, lreach->start);
03136                         VectorCopy(toporg, lreach->end);
03137                         lreach->traveltype = TRAVEL_ELEVATOR;
03138                         lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03139                         lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed;
03140                         lreach->next = areareachability[area1num];
03141                         areareachability[area1num] = lreach;
03142                         //don't go any further to the outside
03143                         n = 9999;
03144                         //
03145 #ifdef REACH_DEBUG
03146                         Log_Write("elevator reach from %d to %d\r\n", area1num, area2num);
03147 #endif //REACH_DEBUG
03148                         //
03149                         reach_elevator++;
03150                     } //end for
03151                 } //end for
03152             } //end for
03153         } //end if
03154     } //end for
03155 } //end of the function AAS_Reachability_Elevator
03156 //===========================================================================
03157 //
03158 // Parameter:           -
03159 // Returns:             -
03160 // Changes Globals:     -
03161 //===========================================================================
03162 aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface)
03163 {
03164     int i, j, k, l;
03165     int facenum, edgenum, bestfacenum;
03166     float *v1, *v2, *v3, *v4;
03167     float bestdist, speed, hordist, dist;
03168     vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint;
03169     aas_lreachability_t *lreach, *lreachabilities;
03170     aas_area_t *area;
03171     aas_face_t *face;
03172     aas_edge_t *edge;
03173     aas_plane_t *faceplane, *bestfaceplane;
03174 
03175     //
03176     lreachabilities = NULL;
03177     bestfacenum = 0;
03178     bestfaceplane = NULL;
03179     //
03180     for (i = 1; i < aasworld.numareas; i++)
03181     {
03182         area = &aasworld.areas[i];
03183         // get the shortest distance between one of the func_bob start edges and
03184         // one of the face edges of area1
03185         bestdist = 999999;
03186         for (j = 0; j < area->numfaces; j++)
03187         {
03188             facenum = aasworld.faceindex[area->firstface + j];
03189             face = &aasworld.faces[abs(facenum)];
03190             //if not a ground face
03191             if (!(face->faceflags & FACE_GROUND)) continue;
03192             //get the ground planes
03193             faceplane = &aasworld.planes[face->planenum];
03194             //
03195             for (k = 0; k < face->numedges; k++)
03196             {
03197                 edgenum = abs(aasworld.edgeindex[face->firstedge + k]);
03198                 edge = &aasworld.edges[edgenum];
03199                 //calculate the minimum distance between the two edges
03200                 v1 = aasworld.vertexes[edge->v[0]];
03201                 v2 = aasworld.vertexes[edge->v[1]];
03202                 //
03203                 for (l = 0; l < numpoints; l++)
03204                 {
03205                     v3 = facepoints[l];
03206                     v4 = facepoints[(l+1) % numpoints];
03207                     dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane,
03208                                                     beststart, bestend,
03209                                                     beststart2, bestend2, bestdist);
03210                     if (dist < bestdist)
03211                     {
03212                         bestfacenum = facenum;
03213                         bestfaceplane = faceplane;
03214                         bestdist = dist;
03215                     } //end if
03216                 } //end for
03217             } //end for
03218         } //end for
03219         //
03220         if (bestdist > 192) continue;
03221         //
03222         VectorMiddle(beststart, beststart2, beststart);
03223         VectorMiddle(bestend, bestend2, bestend);
03224         //
03225         if (!towardsface)
03226         {
03227             VectorCopy(beststart, tmp);
03228             VectorCopy(bestend, beststart);
03229             VectorCopy(tmp, bestend);
03230         } //end if
03231         //
03232         VectorSubtract(bestend, beststart, hordir);
03233         hordir[2] = 0;
03234         hordist = VectorLength(hordir);
03235         //
03236         if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue;
03237         //the end point should not be significantly higher than the start point
03238         if (bestend[2] - 32 > beststart[2]) continue;
03239         //don't fall down too far
03240         if (bestend[2] < beststart[2] - 128) continue;
03241         //the distance should not be too far
03242         if (hordist > 32)
03243         {
03244             //check for walk off ledge
03245             if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue;
03246         } //end if
03247         //
03248         beststart[2] += 1;
03249         bestend[2] += 1;
03250         //
03251         if (towardsface) VectorCopy(bestend, testpoint);
03252         else VectorCopy(beststart, testpoint);
03253         testpoint[2] = 0;
03254         testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2];
03255         //
03256         if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f))
03257         {
03258             //if the faces are not overlapping then only go down
03259             if (bestend[2] - 16 > beststart[2]) continue;
03260         } //end if
03261         lreach = AAS_AllocReachability();
03262         if (!lreach) return lreachabilities;
03263         lreach->areanum = i;
03264         lreach->facenum = 0;
03265         lreach->edgenum = 0;
03266         VectorCopy(beststart, lreach->start);
03267         VectorCopy(bestend, lreach->end);
03268         lreach->traveltype = 0;
03269         lreach->traveltime = 0;
03270         lreach->next = lreachabilities;
03271         lreachabilities = lreach;
03272 #ifndef BSPC
03273         if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1);
03274         else AAS_PermanentLine(lreach->start, lreach->end, 2);
03275 #endif
03276     } //end for
03277     return lreachabilities;
03278 } //end of the function AAS_FindFaceReachabilities
03279 //===========================================================================
03280 //
03281 // Parameter:           -
03282 // Returns:             -
03283 // Changes Globals:     -
03284 //===========================================================================
03285 void AAS_Reachability_FuncBobbing(void)
03286 {
03287     int ent, spawnflags, modelnum, axis;
03288     int i, numareas, areas[10];
03289     char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
03290     vec3_t origin, move_end, move_start, move_start_top, move_end_top;
03291     vec3_t mins, maxs, angles = {0, 0, 0};
03292     vec3_t start_edgeverts[4], end_edgeverts[4], mid;
03293     vec3_t org, start, end, dir, points[10];
03294     float height;
03295     aas_plane_t start_plane, end_plane;
03296     aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach;
03297     aas_lreachability_t *firststartreach, *firstendreach;
03298 
03299     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
03300     {
03301         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
03302         if (strcmp(classname, "func_bobbing")) continue;
03303         AAS_FloatForBSPEpairKey(ent, "height", &height);
03304         if (!height) height = 32;
03305         //
03306         if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
03307         {
03308             botimport.Print(PRT_ERROR, "func_bobbing without model\n");
03309             continue;
03310         } //end if
03311         //get the model number, and skip the leading *
03312         modelnum = atoi(model+1);
03313         if (modelnum <= 0)
03314         {
03315             botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n");
03316             continue;
03317         } //end if
03318         //if the entity has an origin set then use it
03319         if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
03320             VectorSet(origin, 0, 0, 0);
03321         //
03322         AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
03323         //
03324         VectorAdd(mins, origin, mins);
03325         VectorAdd(maxs, origin, maxs);
03326         //
03327         VectorAdd(mins, maxs, mid);
03328         VectorScale(mid, 0.5, mid);
03329         VectorCopy(mid, origin);
03330         //
03331         VectorCopy(origin, move_end);
03332         VectorCopy(origin, move_start);
03333         //
03334         AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
03335         // set the axis of bobbing
03336         if (spawnflags & 1) axis = 0;
03337         else if (spawnflags & 2) axis = 1;
03338         else axis = 2;
03339         //
03340         move_start[axis] -= height;
03341         move_end[axis] += height;
03342         //
03343         Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n",
03344                     modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]);
03345         //
03346 #ifndef BSPC
03347         /*
03348         AAS_DrawPermanentCross(move_start, 4, 1);
03349         AAS_DrawPermanentCross(move_end, 4, 2);
03350         */
03351 #endif
03352         //
03353         for (i = 0; i < 4; i++)
03354         {
03355             VectorCopy(move_start, start_edgeverts[i]);
03356             start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z
03357             start_edgeverts[i][2] += 24;    //+ player origin to ground dist
03358         } //end for
03359         start_edgeverts[0][0] += maxs[0] - mid[0];
03360         start_edgeverts[0][1] += maxs[1] - mid[1];
03361         start_edgeverts[1][0] += maxs[0] - mid[0];
03362         start_edgeverts[1][1] += mins[1] - mid[1];
03363         start_edgeverts[2][0] += mins[0] - mid[0];
03364         start_edgeverts[2][1] += mins[1] - mid[1];
03365         start_edgeverts[3][0] += mins[0] - mid[0];
03366         start_edgeverts[3][1] += maxs[1] - mid[1];
03367         //
03368         start_plane.dist = start_edgeverts[0][2];
03369         VectorSet(start_plane.normal, 0, 0, 1);
03370         //
03371         for (i = 0; i < 4; i++)
03372         {
03373             VectorCopy(move_end, end_edgeverts[i]);
03374             end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z
03375             end_edgeverts[i][2] += 24;  //+ player origin to ground dist
03376         } //end for
03377         end_edgeverts[0][0] += maxs[0] - mid[0];
03378         end_edgeverts[0][1] += maxs[1] - mid[1];
03379         end_edgeverts[1][0] += maxs[0] - mid[0];
03380         end_edgeverts[1][1] += mins[1] - mid[1];
03381         end_edgeverts[2][0] += mins[0] - mid[0];
03382         end_edgeverts[2][1] += mins[1] - mid[1];
03383         end_edgeverts[3][0] += mins[0] - mid[0];
03384         end_edgeverts[3][1] += maxs[1] - mid[1];
03385         //
03386         end_plane.dist = end_edgeverts[0][2];
03387         VectorSet(end_plane.normal, 0, 0, 1);
03388         //
03389 #ifndef BSPC
03390 #if 0
03391         for (i = 0; i < 4; i++)
03392         {
03393             AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1);
03394             AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1);
03395         } //end for
03396 #endif
03397 #endif
03398         VectorCopy(move_start, move_start_top);
03399         move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z
03400         VectorCopy(move_end, move_end_top);
03401         move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z
03402         //
03403         if (!AAS_PointAreaNum(move_start_top)) continue;
03404         if (!AAS_PointAreaNum(move_end_top)) continue;
03405         //
03406         for (i = 0; i < 2; i++)
03407         {
03408             firststartreach = firstendreach = NULL;
03409             //
03410             if (i == 0)
03411             {
03412                 firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue);
03413                 firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse);
03414             } //end if
03415             else
03416             {
03417                 firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue);
03418                 firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse);
03419             } //end else
03420             //
03421             //create reachabilities from start to end
03422             for (startreach = firststartreach; startreach; startreach = nextstartreach)
03423             {
03424                 nextstartreach = startreach->next;
03425                 //
03426                 //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1);
03427                 //if (trace.fraction < 1) continue;
03428                 //
03429                 for (endreach = firstendreach; endreach; endreach = nextendreach)
03430                 {
03431                     nextendreach = endreach->next;
03432                     //
03433                     //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1);
03434                     //if (trace.fraction < 1) continue;
03435                     //
03436                     Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum);
03437                     //
03438                     //
03439                     if (i == 0) VectorCopy(move_start_top, org);
03440                     else VectorCopy(move_end_top, org);
03441                     VectorSubtract(startreach->start, org, dir);
03442                     dir[2] = 0;
03443                     VectorNormalize(dir);
03444                     VectorCopy(startreach->start, start);
03445                     VectorMA(startreach->start, 1, dir, start);
03446                     start[2] += 1;
03447                     VectorMA(startreach->start, 16, dir, end);
03448                     end[2] += 1;
03449                     //
03450                     numareas = AAS_TraceAreas(start, end, areas, points, 10);
03451                     if (numareas <= 0) continue;
03452                     if (numareas > 1) VectorCopy(points[1], startreach->start);
03453                     else VectorCopy(end, startreach->start);
03454                     //
03455                     if (!AAS_PointAreaNum(startreach->start)) continue;
03456                     if (!AAS_PointAreaNum(endreach->end)) continue;
03457                     //
03458                     lreach = AAS_AllocReachability();
03459                     lreach->areanum = endreach->areanum;
03460                     if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff);
03461                     else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff);
03462                     lreach->facenum = (spawnflags << 16) | modelnum;
03463                     VectorCopy(startreach->start, lreach->start);
03464                     VectorCopy(endreach->end, lreach->end);
03465 #ifndef BSPC
03466 //                  AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW);
03467 //                  AAS_PermanentLine(lreach->start, lreach->end, 1);
03468 #endif
03469                     lreach->traveltype = TRAVEL_FUNCBOB;
03470                     lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03471                     lreach->traveltime = aassettings.rs_funcbob;
03472                     reach_funcbob++;
03473                     lreach->next = areareachability[startreach->areanum];
03474                     areareachability[startreach->areanum] = lreach;
03475                     //
03476                 } //end for
03477             } //end for
03478             for (startreach = firststartreach; startreach; startreach = nextstartreach)
03479             {
03480                 nextstartreach = startreach->next;
03481                 AAS_FreeReachability(startreach);
03482             } //end for
03483             for (endreach = firstendreach; endreach; endreach = nextendreach)
03484             {
03485                 nextendreach = endreach->next;
03486                 AAS_FreeReachability(endreach);
03487             } //end for
03488             //only go up with func_bobbing entities that go up and down
03489             if (!(spawnflags & 1) && !(spawnflags & 2)) break;
03490         } //end for
03491     } //end for
03492 } //end of the function AAS_Reachability_FuncBobbing
03493 //===========================================================================
03494 //
03495 // Parameter:           -
03496 // Returns:             -
03497 // Changes Globals:     -
03498 //===========================================================================
03499 void AAS_Reachability_JumpPad(void)
03500 {
03501     int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads;
03502     //int modelnum, ent2;
03503     //float dist, time, height, gravity, forward;
03504     float speed, zvel, hordist;
03505     aas_face_t *face2;
03506     aas_area_t *area2;
03507     aas_lreachability_t *lreach;
03508     vec3_t areastart, facecenter, dir, cmdmove;
03509     vec3_t velocity, absmins, absmaxs;
03510     //vec3_t origin, ent2origin, angles, teststart;
03511     aas_clientmove_t move;
03512     //aas_trace_t trace;
03513     aas_link_t *areas, *link;
03514     //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
03515     char classname[MAX_EPAIRKEY];
03516 
03517 #ifdef BSPC
03518     bot_visualizejumppads = 0;
03519 #else
03520     bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
03521 #endif
03522     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
03523     {
03524         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
03525         if (strcmp(classname, "trigger_push")) continue;
03526         //
03527         if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
03528         /*
03529         //
03530         AAS_FloatForBSPEpairKey(ent, "speed", &speed);
03531         if (!speed) speed = 1000;
03532 //      AAS_VectorForBSPEpairKey(ent, "angles", angles);
03533 //      AAS_SetMovedir(angles, velocity);
03534 //      VectorScale(velocity, speed, velocity);
03535         VectorClear(angles);
03536         //get the mins, maxs and origin of the model
03537         AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
03538         if (model[0]) modelnum = atoi(model+1);
03539         else modelnum = 0;
03540         AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
03541         VectorAdd(origin, absmins, absmins);
03542         VectorAdd(origin, absmaxs, absmaxs);
03543         //
03544 #ifdef REACH_DEBUG
03545         botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]);
03546         botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]);
03547 #endif REACH_DEBUG
03548         VectorAdd(absmins, absmaxs, origin);
03549         VectorScale (origin, 0.5, origin);
03550 
03551         //get the start areas
03552         VectorCopy(origin, teststart);
03553         teststart[2] += 64;
03554         trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
03555         if (trace.startsolid)
03556         {
03557             botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
03558             VectorCopy(origin, areastart);
03559         } //end if
03560         else
03561         {
03562             VectorCopy(trace.endpos, areastart);
03563         } //end else
03564         areastart[2] += 0.125;
03565         //
03566         //AAS_DrawPermanentCross(origin, 4, 4);
03567         //get the target entity
03568         AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
03569         for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
03570         {
03571             if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
03572             if (!strcmp(targetname, target)) break;
03573         } //end for
03574         if (!ent2)
03575         {
03576             botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
03577             continue;
03578         } //end if
03579         AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
03580         //
03581         height = ent2origin[2] - origin[2];
03582         gravity = aassettings.sv_gravity;
03583         time = sqrt( height / ( 0.5 * gravity ) );
03584         if (!time)
03585         {
03586             botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
03587             continue;
03588         } //end if
03589         // set s.origin2 to the push velocity
03590         VectorSubtract ( ent2origin, origin, velocity);
03591         dist = VectorNormalize( velocity);
03592         forward = dist / time;
03593         //FIXME: why multiply by 1.1
03594         forward *= 1.1;
03595         VectorScale(velocity, forward, velocity);
03596         velocity[2] = time * gravity;
03597         */
03598         //get the areas the jump pad brush is in
03599         areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
03600         /*
03601         for (link = areas; link; link = link->next_area)
03602         {
03603             if (link->areanum == 563)
03604             {
03605                 ret = qfalse;
03606             }
03607         }
03608         */
03609         for (link = areas; link; link = link->next_area)
03610         {
03611             if (AAS_AreaJumpPad(link->areanum)) break;
03612         } //end for
03613         if (!link)
03614         {
03615             botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
03616             AAS_UnlinkFromAreas(areas);
03617             continue;
03618         } //end if
03619         //
03620         botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
03621         //if there is a horizontal velocity check for a reachability without air control
03622         if (velocity[0] || velocity[1])
03623         {
03624             VectorSet(cmdmove, 0, 0, 0);
03625             //VectorCopy(velocity, cmdmove);
03626             //cmdmove[2] = 0;
03627             Com_Memset(&move, 0, sizeof(aas_clientmove_t));
03628             area2num = 0;
03629             for (i = 0; i < 20; i++)
03630             {
03631                 AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
03632                                         velocity, cmdmove, 0, 30, 0.1f,
03633                                         SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
03634                                         SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads);
03635                 area2num = move.endarea;
03636                 for (link = areas; link; link = link->next_area)
03637                 {
03638                     if (!AAS_AreaJumpPad(link->areanum)) continue;
03639                     if (link->areanum == area2num) break;
03640                 } //end if
03641                 if (!link) break;
03642                 VectorCopy(move.endpos, areastart);
03643                 VectorCopy(move.velocity, velocity);
03644             } //end for
03645             if (area2num && i < 20)
03646             {
03647                 for (link = areas; link; link = link->next_area)
03648                 {
03649                     if (!AAS_AreaJumpPad(link->areanum)) continue;
03650                     if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
03651                     //create a rocket or bfg jump reachability from area1 to area2
03652                     lreach = AAS_AllocReachability();
03653                     if (!lreach)
03654                     {
03655                         AAS_UnlinkFromAreas(areas);
03656                         return;
03657                     } //end if
03658                     lreach->areanum = area2num;
03659                     //NOTE: the facenum is the Z velocity
03660                     lreach->facenum = velocity[2];
03661                     //NOTE: the edgenum is the horizontal velocity
03662                     lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]);
03663                     VectorCopy(areastart, lreach->start);
03664                     VectorCopy(move.endpos, lreach->end);
03665                     lreach->traveltype = TRAVEL_JUMPPAD;
03666                     lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03667                     lreach->traveltime = aassettings.rs_jumppad;
03668                     lreach->next = areareachability[link->areanum];
03669                     areareachability[link->areanum] = lreach;
03670                     //
03671                     reach_jumppad++;
03672                 } //end for
03673             } //end if
03674         } //end if
03675         //
03676         if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue;
03677         //check for areas we can reach with air control
03678         for (area2num = 1; area2num < aasworld.numareas; area2num++)
03679         {
03680             visualize = qfalse;
03681             /*
03682             if (area2num == 3568)
03683             {
03684                 for (link = areas; link; link = link->next_area)
03685                 {
03686                     if (link->areanum == 3380)
03687                     {
03688                         visualize = qtrue;
03689                         botimport.Print(PRT_MESSAGE, "bah\n");
03690                     } //end if
03691                 } //end for
03692             } //end if*/
03693             //never try to go back to one of the original jumppad areas
03694             //and don't create reachabilities if they already exist
03695             for (link = areas; link; link = link->next_area)
03696             {
03697                 if (AAS_ReachabilityExists(link->areanum, area2num)) break;
03698                 if (AAS_AreaJumpPad(link->areanum))
03699                 {
03700                     if (link->areanum == area2num) break;
03701                 } //end if
03702             } //end if
03703             if (link) continue;
03704             //
03705             area2 = &aasworld.areas[area2num];
03706             for (i = 0; i < area2->numfaces; i++)
03707             {
03708                 face2num = aasworld.faceindex[area2->firstface + i];
03709                 face2 = &aasworld.faces[abs(face2num)];
03710                 //if it is not a ground face
03711                 if (!(face2->faceflags & FACE_GROUND)) continue;
03712                 //get the center of the face
03713                 AAS_FaceCenter(face2num, facecenter);
03714                 //only go higher up
03715                 if (facecenter[2] < areastart[2]) continue;
03716                 //get the jumppad jump z velocity
03717                 zvel = velocity[2];
03718                 //get the horizontal speed for the jump, if it isn't possible to calculate this
03719                 //speed
03720                 ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
03721                 if (ret && speed < 150)
03722                 {
03723                     //direction towards the face center
03724                     VectorSubtract(facecenter, areastart, dir);
03725                     dir[2] = 0;
03726                     hordist = VectorNormalize(dir);
03727                     //if (hordist < 1.6 * facecenter[2] - areastart[2])
03728                     {
03729                         //get command movement
03730                         VectorScale(dir, speed, cmdmove);
03731                         //
03732                         AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
03733                                                     velocity, cmdmove, 30, 30, 0.1f,
03734                                                     SE_ENTERWATER|SE_ENTERSLIME|
03735                                                     SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
03736                                                     SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize);
03737                         //if prediction time wasn't enough to fully predict the movement
03738                         //don't enter slime or lava and don't fall from too high
03739                         if (move.frames < 30 && 
03740                                 !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
03741                                 && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER)))
03742                         {
03743                             //never go back to the same jumppad
03744                             for (link = areas; link; link = link->next_area)
03745                             {
03746                                 if (link->areanum == move.endarea) break;
03747                             }
03748                             if (!link)
03749                             {
03750                                 for (link = areas; link; link = link->next_area)
03751                                 {
03752                                     if (!AAS_AreaJumpPad(link->areanum)) continue;
03753                                     if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
03754                                     //create a jumppad reachability from area1 to area2
03755                                     lreach = AAS_AllocReachability();
03756                                     if (!lreach)
03757                                     {
03758                                         AAS_UnlinkFromAreas(areas);
03759                                         return;
03760                                     } //end if
03761                                     lreach->areanum = move.endarea;
03762                                     //NOTE: the facenum is the Z velocity
03763                                     lreach->facenum = velocity[2];
03764                                     //NOTE: the edgenum is the horizontal velocity
03765                                     lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]);
03766                                     VectorCopy(areastart, lreach->start);
03767                                     VectorCopy(facecenter, lreach->end);
03768                                     lreach->traveltype = TRAVEL_JUMPPAD;
03769                                     lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
03770                                     lreach->traveltime = aassettings.rs_aircontrolledjumppad;
03771                                     lreach->next = areareachability[link->areanum];
03772                                     areareachability[link->areanum] = lreach;
03773                                     //
03774                                     reach_jumppad++;
03775                                 } //end for
03776                             }
03777                         } //end if
03778                     } //end if
03779                 } //end for
03780             } //end for
03781         } //end for
03782         AAS_UnlinkFromAreas(areas);
03783     } //end for
03784 } //end of the function AAS_Reachability_JumpPad
03785 //===========================================================================
03786 // never point at ground faces
03787 // always a higher and pretty far area
03788 //
03789 // Parameter:               -
03790 // Returns:                 -
03791 // Changes Globals:     -
03792 //===========================================================================
03793 int AAS_Reachability_Grapple(int area1num, int area2num)
03794 {
03795     int face2num, i, j, areanum, numareas, areas[20];
03796     float mingrappleangle, z, hordist;
03797     bsp_trace_t bsptrace;
03798     aas_trace_t trace;
03799     aas_face_t *face2;
03800     aas_area_t *area1, *area2;
03801     aas_lreachability_t *lreach;
03802     vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1};
03803     vec_t *v;
03804 
03805     //only grapple when on the ground or swimming
03806     if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
03807     //don't grapple from a crouch area
03808     if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse;
03809     //NOTE: disabled area swim it doesn't work right
03810     if (AAS_AreaSwim(area1num)) return qfalse;
03811     //
03812     area1 = &aasworld.areas[area1num];
03813     area2 = &aasworld.areas[area2num];
03814     //don't grapple towards way lower areas
03815     if (area2->maxs[2] < area1->mins[2]) return qfalse;
03816     //
03817     VectorCopy(aasworld.areas[area1num].center, start);
03818     //if not a swim area
03819     if (!AAS_AreaSwim(area1num))
03820     {
03821         if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
03822                                 start[0], start[1], start[2]);
03823         VectorCopy(start, end);
03824         end[2] -= 1000;
03825         trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
03826         if (trace.startsolid) return qfalse;
03827         VectorCopy(trace.endpos, areastart);
03828     } //end if
03829     else
03830     {
03831         if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse;
03832     } //end else
03833     //
03834     //start is now the start point
03835     //
03836     for (i = 0; i < area2->numfaces; i++)
03837     {
03838         face2num = aasworld.faceindex[area2->firstface + i];
03839         face2 = &aasworld.faces[abs(face2num)];
03840         //if it is not a solid face
03841         if (!(face2->faceflags & FACE_SOLID)) continue;
03842         //direction towards the first vertex of the face
03843         v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]];
03844         VectorSubtract(v, areastart, dir);
03845         //if the face plane is facing away
03846         if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue;
03847         //get the center of the face
03848         AAS_FaceCenter(face2num, facecenter);
03849         //only go higher up with the grapple
03850         if (facecenter[2] < areastart[2] + 64) continue;
03851         //only use vertical faces or downward facing faces
03852         if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue;
03853         //direction towards the face center
03854         VectorSubtract(facecenter, areastart, dir);
03855         //
03856         z = dir[2];
03857         dir[2] = 0;
03858         hordist = VectorLength(dir);
03859         if (!hordist) continue;
03860         //if too far
03861         if (hordist > 2000) continue;
03862         //check the minimal angle of the movement
03863         mingrappleangle = 15; //15 degrees
03864         if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue;
03865         //
03866         VectorCopy(facecenter, start);
03867         VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end);
03868         //
03869         bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID);
03870         //the grapple won't stick to the sky and the grapple point should be near the AAS wall
03871         if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue;
03872         //trace a full bounding box from the area center on the ground to
03873         //the center of the face
03874         VectorSubtract(facecenter, areastart, dir);
03875         VectorNormalize(dir);
03876         VectorMA(areastart, 4, dir, start);
03877         VectorCopy(bsptrace.endpos, end);
03878         trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
03879         VectorSubtract(trace.endpos, facecenter, dir);
03880         if (VectorLength(dir) > 24) continue;
03881         //
03882         VectorCopy(trace.endpos, start);
03883         VectorCopy(trace.endpos, end);
03884         end[2] -= AAS_FallDamageDistance();
03885         trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
03886         if (trace.fraction >= 1) continue;
03887         //area to end in
03888         areanum = AAS_PointAreaNum(trace.endpos);
03889         //if not in lava or slime
03890         if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA))
03891         {
03892             continue;
03893         } //end if
03894         //do not go the the source area
03895         if (areanum == area1num) continue;
03896         //don't create reachabilities if they already exist
03897         if (AAS_ReachabilityExists(area1num, areanum)) continue;
03898         //only end in areas we can stand
03899         if (!AAS_AreaGrounded(areanum)) continue;
03900         //never go through cluster portals!!
03901         numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20);
03902         if (numareas >= 20) continue;
03903         for (j = 0; j < numareas; j++)
03904         {
03905             if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break;
03906         } //end for
03907         if (j < numareas) continue;
03908         //create a new reachability link
03909         lreach = AAS_AllocReachability();
03910         if (!lreach) return qfalse;
03911         lreach->areanum = areanum;
03912         lreach->facenum = face2num;
03913         lreach->edgenum = 0;
03914         VectorCopy(areastart, lreach->start);
03915         //VectorCopy(facecenter, lreach->end);
03916         VectorCopy(bsptrace.endpos, lreach->end);
03917         lreach->traveltype = TRAVEL_GRAPPLEHOOK;
03918         VectorSubtract(lreach->end, lreach->start, dir);
03919         lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25;
03920         lreach->next = areareachability[area1num];
03921         areareachability[area1num] = lreach;
03922         //
03923         reach_grapple++;
03924     } //end for
03925     //
03926     return qfalse;
03927 } //end of the function AAS_Reachability_Grapple
03928 //===========================================================================
03929 //
03930 // Parameter:               -
03931 // Returns:                 -
03932 // Changes Globals:     -
03933 //===========================================================================
03934 void AAS_SetWeaponJumpAreaFlags(void)
03935 {
03936     int ent, i;
03937     vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15};
03938     vec3_t origin;
03939     int areanum, weaponjumpareas, spawnflags;
03940     char classname[MAX_EPAIRKEY];
03941 
03942     weaponjumpareas = 0;
03943     for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
03944     {
03945         if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
03946         if (
03947             !strcmp(classname, "item_armor_body") ||
03948             !strcmp(classname, "item_armor_combat") ||
03949             !strcmp(classname, "item_health_mega") ||
03950             !strcmp(classname, "weapon_grenadelauncher") ||
03951             !strcmp(classname, "weapon_rocketlauncher") ||
03952             !strcmp(classname, "weapon_lightning") ||
03953             !strcmp(classname, "weapon_plasmagun") ||
03954             !strcmp(classname, "weapon_railgun") ||
03955             !strcmp(classname, "weapon_bfg") ||
03956             !strcmp(classname, "item_quad") ||
03957             !strcmp(classname, "item_regen") ||
03958             !strcmp(classname, "item_invulnerability"))
03959         {
03960             if (AAS_VectorForBSPEpairKey(ent, "origin", origin))
03961             {
03962                 spawnflags = 0;
03963                 AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
03964                 //if not a stationary item
03965                 if (!(spawnflags & 1))
03966                 {
03967                     if (!AAS_DropToFloor(origin, mins, maxs))
03968                     {
03969                         botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
03970                                                         classname, origin[0], origin[1], origin[2]);
03971                     } //end if
03972                 } //end if
03973                 //areanum = AAS_PointAreaNum(origin);
03974                 areanum = AAS_BestReachableArea(origin, mins, maxs, origin);
03975                 //the bot may rocket jump towards this area
03976                 aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP;
03977                 //
03978                 //if (!AAS_AreaGrounded(areanum))
03979                 //  botimport.Print(PRT_MESSAGE, "area not grounded\n");
03980                 //
03981                 weaponjumpareas++;
03982             } //end if
03983         } //end if
03984     } //end for
03985     for (i = 1; i < aasworld.numareas; i++)
03986     {
03987         if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
03988         {
03989             aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP;
03990             weaponjumpareas++;
03991         } //end if
03992     } //end for
03993     botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas);
03994 } //end of the function AAS_SetWeaponJumpAreaFlags
03995 //===========================================================================
03996 // create a possible weapon jump reachability from area1 to area2
03997 //
03998 // check if there's a cool item in the second area
03999 // check if area1 is lower than area2
04000 // check if the bot can rocketjump from area1 to area2
04001 //
04002 // Parameter:               -
04003 // Returns:                 -
04004 // Changes Globals:     -
04005 //===========================================================================
04006 int AAS_Reachability_WeaponJump(int area1num, int area2num)
04007 {
04008     int face2num, i, n, ret, visualize;
04009     float speed, zvel, hordist;
04010     aas_face_t *face2;
04011     aas_area_t *area1, *area2;
04012     aas_lreachability_t *lreach;
04013     vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart;
04014     vec3_t velocity;
04015     aas_clientmove_t move;
04016     aas_trace_t trace;
04017 
04018     visualize = qfalse;
04019 //  if (area1num == 4436 && area2num == 4318)
04020 //  {
04021 //      visualize = qtrue;
04022 //  }
04023     if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse;
04024     if (!AAS_AreaGrounded(area2num)) return qfalse;
04025     //NOTE: only weapon jump towards areas with an interesting item in it??
04026     if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse;
04027     //
04028     area1 = &aasworld.areas[area1num];
04029     area2 = &aasworld.areas[area2num];
04030     //don't weapon jump towards way lower areas
04031     if (area2->maxs[2] < area1->mins[2]) return qfalse;
04032     //
04033     VectorCopy(aasworld.areas[area1num].center, start);
04034     //if not a swim area
04035     if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
04036                             start[0], start[1], start[2]);
04037     VectorCopy(start, end);
04038     end[2] -= 1000;
04039     trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
04040     if (trace.startsolid) return qfalse;
04041     VectorCopy(trace.endpos, areastart);
04042     //
04043     //areastart is now the start point
04044     //
04045     for (i = 0; i < area2->numfaces; i++)
04046     {
04047         face2num = aasworld.faceindex[area2->firstface + i];
04048         face2 = &aasworld.faces[abs(face2num)];
04049         //if it is not a solid face
04050         if (!(face2->faceflags & FACE_GROUND)) continue;
04051         //get the center of the face
04052         AAS_FaceCenter(face2num, facecenter);
04053         //only go higher up with weapon jumps
04054         if (facecenter[2] < areastart[2] + 64) continue;
04055         //NOTE: set to 2 to allow bfg jump reachabilities
04056         for (n = 0; n < 1/*2*/; n++)
04057         {
04058             //get the rocket jump z velocity
04059             if (n) zvel = AAS_BFGJumpZVelocity(areastart);
04060             else zvel = AAS_RocketJumpZVelocity(areastart);
04061             //get the horizontal speed for the jump, if it isn't possible to calculate this
04062             //speed (the jump is not possible) then there's no jump reachability created
04063             ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
04064             if (ret && speed < 300)
04065             {
04066                 //direction towards the face center
04067                 VectorSubtract(facecenter, areastart, dir);
04068                 dir[2] = 0;
04069                 hordist = VectorNormalize(dir);
04070                 //if (hordist < 1.6 * (facecenter[2] - areastart[2]))
04071                 {
04072                     //get command movement
04073                     VectorScale(dir, speed, cmdmove);
04074                     VectorSet(velocity, 0, 0, zvel);
04075                     /*
04076                     //get command movement
04077                     VectorScale(dir, speed, velocity);
04078                     velocity[2] = zvel;
04079                     VectorSet(cmdmove, 0, 0, 0);
04080                     */
04081                     //
04082                     AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue,
04083                                                 velocity, cmdmove, 30, 30, 0.1f,
04084                                                 SE_ENTERWATER|SE_ENTERSLIME|
04085                                                 SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
04086                                                 SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize);
04087                     //if prediction time wasn't enough to fully predict the movement
04088                     //don't enter slime or lava and don't fall from too high
04089                     if (move.frames < 30 && 
04090                             !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
04091                                 && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD)))
04092                     {
04093                         //create a rocket or bfg jump reachability from area1 to area2
04094                         lreach = AAS_AllocReachability();
04095                         if (!lreach) return qfalse;
04096                         lreach->areanum = area2num;
04097                         lreach->facenum = 0;
04098                         lreach->edgenum = 0;
04099                         VectorCopy(areastart, lreach->start);
04100                         VectorCopy(facecenter, lreach->end);
04101                         if (n)
04102                         {
04103                             lreach->traveltype = TRAVEL_BFGJUMP;
04104                             lreach->traveltime = aassettings.rs_bfgjump;
04105                         } //end if
04106                         else
04107                         {
04108                             lreach->traveltype = TRAVEL_ROCKETJUMP;
04109                             lreach->traveltime = aassettings.rs_rocketjump;
04110                         } //end else
04111                         lreach->next = areareachability[area1num];
04112                         areareachability[area1num] = lreach;
04113                         //
04114                         reach_rocketjump++;
04115                         return qtrue;
04116                     } //end if
04117                 } //end if
04118             } //end if
04119         } //end for
04120     } //end for
04121     //
04122     return qfalse;
04123 } //end of the function AAS_Reachability_WeaponJump
04124 //===========================================================================
04125 // calculates additional walk off ledge reachabilities for the given area
04126 //
04127 // Parameter:               -
04128 // Returns:                 -
04129 // Changes Globals:     -
04130 //===========================================================================
04131 void AAS_Reachability_WalkOffLedge(int areanum)
04132 {
04133     int i, j, k, l, m, n, p, areas[10], numareas;
04134     int face1num, face2num, face3num, edge1num, edge2num, edge3num;
04135     int otherareanum, gap, reachareanum, side;
04136     aas_area_t *area, *area2;
04137     aas_face_t *face1, *face2, *face3;
04138     aas_edge_t *edge;
04139     aas_plane_t *plane;
04140     vec_t *v1, *v2;
04141     vec3_t sharededgevec, mid, dir, testend;
04142     aas_lreachability_t *lreach;
04143     aas_trace_t trace;
04144 
04145     if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return;
04146     //
04147     area = &aasworld.areas[areanum];
04148     //
04149     for (i = 0; i < area->numfaces; i++)
04150     {
04151         face1num = aasworld.faceindex[area->firstface + i];
04152         face1 = &aasworld.faces[abs(face1num)];
04153         //face 1 must be a ground face
04154         if (!(face1->faceflags & FACE_GROUND)) continue;
04155         //go through all the edges of this ground face
04156         for (k = 0; k < face1->numedges; k++)
04157         {
04158             edge1num = aasworld.edgeindex[face1->firstedge + k];
04159             //find another not ground face using this same edge
04160             for (j = 0; j < area->numfaces; j++)
04161             {
04162                 face2num = aasworld.faceindex[area->firstface + j];
04163                 face2 = &aasworld.faces[abs(face2num)];
04164                 //face 2 may not be a ground face
04165                 if (face2->faceflags & FACE_GROUND) continue;
04166                 //compare all the edges
04167                 for (l = 0; l < face2->numedges; l++)
04168                 {
04169                     edge2num = aasworld.edgeindex[face2->firstedge + l];
04170                     if (abs(edge1num) == abs(edge2num))
04171                     {
04172                         //get the area at the other side of the face
04173                         if (face2->frontarea == areanum) otherareanum = face2->backarea;
04174                         else otherareanum = face2->frontarea;
04175                         //
04176                         area2 = &aasworld.areas[otherareanum];
04177                         //if the other area is grounded!
04178                         if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED)
04179                         {
04180                             //check for a possible gap
04181                             gap = qfalse;
04182                             for (n = 0; n < area2->numfaces; n++)
04183                             {
04184                                 face3num = aasworld.faceindex[area2->firstface + n];
04185                                 //may not be the shared face of the two areas
04186                                 if (abs(face3num) == abs(face2num)) continue;
04187                                 //
04188                                 face3 = &aasworld.faces[abs(face3num)];
04189                                 //find an edge shared by all three faces
04190                                 for (m = 0; m < face3->numedges; m++)
04191                                 {
04192                                     edge3num = aasworld.edgeindex[face3->firstedge + m];
04193                                     //but the edge should be shared by all three faces
04194                                     if (abs(edge3num) == abs(edge1num))
04195                                     {
04196                                         if (!(face3->faceflags & FACE_SOLID))
04197                                         {
04198                                             gap = qtrue;
04199                                             break;
04200                                         } //end if
04201                                         //
04202                                         if (face3->faceflags & FACE_GROUND)
04203                                         {
04204                                             gap = qfalse;
04205                                             break;
04206                                         } //end if
04207                                         //FIXME: there are more situations to be handled
04208                                         gap = qtrue;
04209                                         break;
04210                                     } //end if
04211                                 } //end for
04212                                 if (m < face3->numedges) break;
04213                             } //end for
04214                             if (!gap) break;
04215                         } //end if
04216                         //check for a walk off ledge reachability
04217                         edge = &aasworld.edges[abs(edge1num)];
04218                         side = edge1num < 0;
04219                         //
04220                         v1 = aasworld.vertexes[edge->v[side]];
04221                         v2 = aasworld.vertexes[edge->v[!side]];
04222                         //
04223                         plane = &aasworld.planes[face1->planenum];
04224                         //get the points really into the areas
04225                         VectorSubtract(v2, v1, sharededgevec);
04226                         CrossProduct(plane->normal, sharededgevec, dir);
04227                         VectorNormalize(dir);
04228                         //
04229                         VectorAdd(v1, v2, mid);
04230                         VectorScale(mid, 0.5, mid);
04231                         VectorMA(mid, 8, dir, mid);
04232                         //
04233                         VectorCopy(mid, testend);
04234                         testend[2] -= 1000;
04235                         trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1);
04236                         //
04237                         if (trace.startsolid)
04238                         {
04239                             //Log_Write("area %d: trace.startsolid\r\n", areanum);
04240                             break;
04241                         } //end if
04242                         reachareanum = AAS_PointAreaNum(trace.endpos);
04243                         if (reachareanum == areanum)
04244                         {
04245                             //Log_Write("area %d: same area\r\n", areanum);
04246                             break;
04247                         } //end if
04248                         if (AAS_ReachabilityExists(areanum, reachareanum))
04249                         {
04250                             //Log_Write("area %d: reachability already exists\r\n", areanum);
04251                             break;
04252                         } //end if
04253                         if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum))
04254                         {
04255                             //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum);
04256                             break;
04257                         } //end if
04258                         //
04259                         if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME
04260                                                                                         | AREACONTENTS_LAVA))
04261                         {
04262                             //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum);
04263                             break;
04264                         } //end if
04265                         //if not going through a cluster portal
04266                         numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int));
04267                         for (p = 0; p < numareas; p++)
04268                             if (AAS_AreaClusterPortal(areas[p]))
04269                                 break;
04270                         if (p < numareas)
04271                             break;
04272                         // if a maximum fall height is set and the bot would fall down further
04273                         if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight)
04274                             break;
04275                         //
04276                         lreach = AAS_AllocReachability();
04277                         if (!lreach) break;
04278                         lreach->areanum = reachareanum;
04279                         lreach->facenum = 0;
04280                         lreach->edgenum = edge1num;
04281                         VectorCopy(mid, lreach->start);
04282                         VectorCopy(trace.endpos, lreach->end);
04283                         lreach->traveltype = TRAVEL_WALKOFFLEDGE;
04284                         lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity;
04285                         if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum))
04286                         {
04287                             if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5)
04288                             {
04289                                 lreach->traveltime += aassettings.rs_falldamage5;
04290                             } //end if
04291                             else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10)
04292                             {
04293                                 lreach->traveltime += aassettings.rs_falldamage10;
04294                             } //end if
04295                         } //end if
04296                         lreach->next = areareachability[areanum];
04297                         areareachability[areanum] = lreach;
04298                         //we've got another walk off ledge reachability
04299                         reach_walkoffledge++;
04300                     } //end if
04301                 } //end for
04302             } //end for
04303         } //end for
04304     } //end for
04305 } //end of the function AAS_Reachability_WalkOffLedge
04306 //===========================================================================
04307 //
04308 // Parameter:               -
04309 // Returns:                 -
04310 // Changes Globals:     -
04311 //===========================================================================
04312 void AAS_StoreReachability(void)
04313 {
04314     int i;
04315     aas_areasettings_t *areasettings;
04316     aas_lreachability_t *lreach;
04317     aas_reachability_t *reach;
04318 
04319     if (aasworld.reachability) FreeMemory(aasworld.reachability);
04320     aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t));
04321     aasworld.reachabilitysize = 1;
04322     for (i = 0; i < aasworld.numareas; i++)
04323     {
04324         areasettings = &aasworld.areasettings[i];
04325         areasettings->firstreachablearea = aasworld.reachabilitysize;
04326         areasettings->numreachableareas = 0;
04327         for (lreach = areareachability[i]; lreach; lreach = lreach->next)
04328         {
04329             reach = &aasworld.reachability[areasettings->firstreachablearea +
04330                                                     areasettings->numreachableareas];
04331             reach->areanum = lreach->areanum;
04332             reach->facenum = lreach->facenum;
04333             reach->edgenum = lreach->edgenum;
04334             VectorCopy(lreach->start, reach->start);
04335             VectorCopy(lreach->end, reach->end);
04336             reach->traveltype = lreach->traveltype;
04337             reach->traveltime = lreach->traveltime;
04338             //
04339             areasettings->numreachableareas++;
04340         } //end for
04341         aasworld.reachabilitysize += areasettings->numreachableareas;
04342     } //end for
04343 } //end of the function AAS_StoreReachability
04344 //===========================================================================
04345 //
04346 // TRAVEL_WALK                  100%    equal floor height + steps
04347 // TRAVEL_CROUCH                100%
04348 // TRAVEL_BARRIERJUMP           100%
04349 // TRAVEL_JUMP                   80%
04350 // TRAVEL_LADDER                100%    + fall down from ladder + jump up to ladder
04351 // TRAVEL_WALKOFFLEDGE           90%    walk off very steep walls?
04352 // TRAVEL_SWIM                  100%
04353 // TRAVEL_WATERJUMP             100%
04354 // TRAVEL_TELEPORT              100%
04355 // TRAVEL_ELEVATOR              100%
04356 // TRAVEL_GRAPPLEHOOK           100%
04357 // TRAVEL_DOUBLEJUMP              0%
04358 // TRAVEL_RAMPJUMP                0%
04359 // TRAVEL_STRAFEJUMP              0%
04360 // TRAVEL_ROCKETJUMP            100%    (currently limited towards areas with items)
04361 // TRAVEL_BFGJUMP                 0%    (currently disabled)
04362 // TRAVEL_JUMPPAD               100%
04363 // TRAVEL_FUNCBOB               100%
04364 //
04365 // Parameter:           -
04366 // Returns:             true if NOT finished
04367 // Changes Globals:     -
04368 //===========================================================================
04369 int AAS_ContinueInitReachability(float time)
04370 {
04371     int i, j, todo, start_time;
04372     static float framereachability, reachability_delay;
04373     static int lastpercentage;
04374 
04375     if (!aasworld.loaded) return qfalse;
04376     //if reachability is calculated for all areas
04377     if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse;
04378     //if starting with area 1 (area 0 is a dummy)
04379     if (aasworld.numreachabilityareas == 1)
04380     {
04381         botimport.Print(PRT_MESSAGE, "calculating reachability...\n");
04382         lastpercentage = 0;
04383         framereachability = 2000;
04384         reachability_delay = 1000;
04385     } //end if
04386     //number of areas to calculate reachability for this cycle
04387     todo = aasworld.numreachabilityareas + (int) framereachability;
04388     start_time = Sys_MilliSeconds();
04389     //loop over the areas
04390     for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++)
04391     {
04392         aasworld.numreachabilityareas++;
04393         //only create jumppad reachabilities from jumppad areas
04394         if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
04395         {
04396             continue;
04397         } //end if
04398         //loop over the areas
04399         for (j = 1; j < aasworld.numareas; j++)
04400         {
04401             if (i == j) continue;
04402             //never create reachabilities from teleporter or jumppad areas to regular areas
04403             if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
04404             {
04405                 if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)))
04406                 {
04407                     continue;
04408                 } //end if
04409             } //end if
04410             //if there already is a reachability link from area i to j
04411             if (AAS_ReachabilityExists(i, j)) continue;
04412             //check for a swim reachability
04413             if (AAS_Reachability_Swim(i, j)) continue;
04414             //check for a simple walk on equal floor height reachability
04415             if (AAS_Reachability_EqualFloorHeight(i, j)) continue;
04416             //check for step, barrier, waterjump and walk off ledge reachabilities
04417             if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue;
04418             //check for ladder reachabilities
04419             if (AAS_Reachability_Ladder(i, j)) continue;
04420             //check for a jump reachability
04421             if (AAS_Reachability_Jump(i, j)) continue;
04422         } //end for
04423         //never create these reachabilities from teleporter or jumppad areas
04424         if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
04425         {
04426             continue;
04427         } //end if
04428         //loop over the areas
04429         for (j = 1; j < aasworld.numareas; j++)
04430         {
04431             if (i == j) continue;
04432             //
04433             if (AAS_ReachabilityExists(i, j)) continue;
04434             //check for a grapple hook reachability
04435             if (calcgrapplereach) AAS_Reachability_Grapple(i, j);
04436             //check for a weapon jump reachability
04437             AAS_Reachability_WeaponJump(i, j);
04438         } //end for
04439         //if the calculation took more time than the max reachability delay
04440         if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break;
04441         //
04442         if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break;
04443     } //end for
04444     //
04445     if (aasworld.numreachabilityareas == aasworld.numareas)
04446     {
04447         botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0);
04448         botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n");
04449         aasworld.numreachabilityareas++;
04450     } //end if
04451     //if this is the last step in the reachability calculations
04452     else if (aasworld.numreachabilityareas == aasworld.numareas + 1)
04453     {
04454         //create additional walk off ledge reachabilities for every area
04455         for (i = 1; i < aasworld.numareas; i++)
04456         {
04457             //only create jumppad reachabilities from jumppad areas
04458             if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
04459             {
04460                 continue;
04461             } //end if
04462             AAS_Reachability_WalkOffLedge(i);
04463         } //end for
04464         //create jump pad reachabilities
04465         AAS_Reachability_JumpPad();
04466         //create teleporter reachabilities
04467         AAS_Reachability_Teleport();
04468         //create elevator (func_plat) reachabilities
04469         AAS_Reachability_Elevator();
04470         //create func_bobbing reachabilities
04471         AAS_Reachability_FuncBobbing();
04472         //
04473 #ifdef DEBUG
04474         botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim);
04475         botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor);
04476         botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step);
04477         botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier);
04478         botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump);
04479         botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge);
04480         botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump);
04481         botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder);
04482         botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk);
04483         botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport);
04484         botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob);
04485         botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator);
04486         botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple);
04487         botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump);
04488         botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad);
04489 #endif
04490         //*/
04491         //store all the reachabilities
04492         AAS_StoreReachability();
04493         //free the reachability link heap
04494         AAS_ShutDownReachabilityHeap();
04495         //
04496         FreeMemory(areareachability);
04497         //
04498         aasworld.numreachabilityareas++;
04499         //
04500         botimport.Print(PRT_MESSAGE, "calculating clusters...\n");
04501     } //end if
04502     else
04503     {
04504         lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas;
04505         botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10);
04506     } //end else
04507     //not yet finished
04508     return qtrue;
04509 } //end of the function AAS_ContinueInitReachability
04510 //===========================================================================
04511 //
04512 // Parameter:               -
04513 // Returns:                 -
04514 // Changes Globals:     -
04515 //===========================================================================
04516 void AAS_InitReachability(void)
04517 {
04518     if (!aasworld.loaded) return;
04519 
04520     if (aasworld.reachabilitysize)
04521     {
04522 #ifndef BSPC
04523         if (!((int)LibVarGetValue("forcereachability")))
04524         {
04525             aasworld.numreachabilityareas = aasworld.numareas + 2;
04526             return;
04527         } //end if
04528 #else
04529         aasworld.numreachabilityareas = aasworld.numareas + 2;
04530         return;
04531 #endif //BSPC
04532     } //end if
04533 #ifndef BSPC
04534     calcgrapplereach = LibVarGetValue("grapplereach");
04535 #endif
04536     aasworld.savefile = qtrue;
04537     //start with area 1 because area zero is a dummy
04538     aasworld.numreachabilityareas = 1;
04540     //setup the heap with reachability links
04541     AAS_SetupReachabilityHeap();
04542     //allocate area reachability link array
04543     areareachability = (aas_lreachability_t **) GetClearedMemory(
04544                                     aasworld.numareas * sizeof(aas_lreachability_t *));
04545     //
04546     AAS_SetWeaponJumpAreaFlags();
04547 } //end of the function AAS_InitReachable

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