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)