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

be_aas_sample.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_sample.c
00025  *
00026  * desc:        AAS environment sampling
00027  *
00028  * $Archive: /MissionPack/code/botlib/be_aas_sample.c $
00029  *
00030  *****************************************************************************/
00031 
00032 #include "../game/q_shared.h"
00033 #include "l_memory.h"
00034 #include "l_script.h"
00035 #include "l_precomp.h"
00036 #include "l_struct.h"
00037 #ifndef BSPC
00038 #include "l_libvar.h"
00039 #endif
00040 #include "aasfile.h"
00041 #include "../game/botlib.h"
00042 #include "../game/be_aas.h"
00043 #include "be_interface.h"
00044 #include "be_aas_funcs.h"
00045 #include "be_aas_def.h"
00046 
00047 extern botlib_import_t botimport;
00048 
00049 //#define AAS_SAMPLE_DEBUG
00050 
00051 #define BBOX_NORMAL_EPSILON     0.001
00052 
00053 #define ON_EPSILON                  0 //0.0005
00054 
00055 #define TRACEPLANE_EPSILON          0.125
00056 
00057 typedef struct aas_tracestack_s
00058 {
00059     vec3_t start;       //start point of the piece of line to trace
00060     vec3_t end;         //end point of the piece of line to trace
00061     int planenum;       //last plane used as splitter
00062     int nodenum;        //node found after splitting with planenum
00063 } aas_tracestack_t;
00064 
00065 int numaaslinks;
00066 
00067 //===========================================================================
00068 //
00069 // Parameter:               -
00070 // Returns:                 -
00071 // Changes Globals:     -
00072 //===========================================================================
00073 void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs)
00074 {
00075     int index;
00076     //bounding box size for each presence type
00077     vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}};
00078     vec3_t boxmaxs[3] = {{0, 0, 0}, { 15,  15,  32}, { 15,  15,   8}};
00079 
00080     if (presencetype == PRESENCE_NORMAL) index = 1;
00081     else if (presencetype == PRESENCE_CROUCH) index = 2;
00082     else
00083     {
00084         botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n");
00085         index = 2;
00086     } //end if
00087     VectorCopy(boxmins[index], mins);
00088     VectorCopy(boxmaxs[index], maxs);
00089 } //end of the function AAS_PresenceTypeBoundingBox
00090 //===========================================================================
00091 //
00092 // Parameter:               -
00093 // Returns:                 -
00094 // Changes Globals:     -
00095 //===========================================================================
00096 void AAS_InitAASLinkHeap(void)
00097 {
00098     int i, max_aaslinks;
00099 
00100     max_aaslinks = aasworld.linkheapsize;
00101     //if there's no link heap present
00102     if (!aasworld.linkheap)
00103     {
00104 #ifdef BSPC
00105         max_aaslinks = 6144;
00106 #else
00107         max_aaslinks = (int) LibVarValue("max_aaslinks", "6144");
00108 #endif
00109         if (max_aaslinks < 0) max_aaslinks = 0;
00110         aasworld.linkheapsize = max_aaslinks;
00111         aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t));
00112     } //end if
00113     //link the links on the heap
00114     aasworld.linkheap[0].prev_ent = NULL;
00115     aasworld.linkheap[0].next_ent = &aasworld.linkheap[1];
00116     for (i = 1; i < max_aaslinks-1; i++)
00117     {
00118         aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1];
00119         aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1];
00120     } //end for
00121     aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2];
00122     aasworld.linkheap[max_aaslinks-1].next_ent = NULL;
00123     //pointer to the first free link
00124     aasworld.freelinks = &aasworld.linkheap[0];
00125     //
00126     numaaslinks = max_aaslinks;
00127 } //end of the function AAS_InitAASLinkHeap
00128 //===========================================================================
00129 //
00130 // Parameter:               -
00131 // Returns:                 -
00132 // Changes Globals:     -
00133 //===========================================================================
00134 void AAS_FreeAASLinkHeap(void)
00135 {
00136     if (aasworld.linkheap) FreeMemory(aasworld.linkheap);
00137     aasworld.linkheap = NULL;
00138     aasworld.linkheapsize = 0;
00139 } //end of the function AAS_FreeAASLinkHeap
00140 //===========================================================================
00141 //
00142 // Parameter:               -
00143 // Returns:                 -
00144 // Changes Globals:     -
00145 //===========================================================================
00146 aas_link_t *AAS_AllocAASLink(void)
00147 {
00148     aas_link_t *link;
00149 
00150     link = aasworld.freelinks;
00151     if (!link)
00152     {
00153 #ifndef BSPC
00154         if (bot_developer)
00155 #endif
00156         {
00157             botimport.Print(PRT_FATAL, "empty aas link heap\n");
00158         } //end if
00159         return NULL;
00160     } //end if
00161     if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent;
00162     if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL;
00163     numaaslinks--;
00164     return link;
00165 } //end of the function AAS_AllocAASLink
00166 //===========================================================================
00167 //
00168 // Parameter:               -
00169 // Returns:                 -
00170 // Changes Globals:     -
00171 //===========================================================================
00172 void AAS_DeAllocAASLink(aas_link_t *link)
00173 {
00174     if (aasworld.freelinks) aasworld.freelinks->prev_ent = link;
00175     link->prev_ent = NULL;
00176     link->next_ent = aasworld.freelinks;
00177     link->prev_area = NULL;
00178     link->next_area = NULL;
00179     aasworld.freelinks = link;
00180     numaaslinks++;
00181 } //end of the function AAS_DeAllocAASLink
00182 //===========================================================================
00183 //
00184 // Parameter:               -
00185 // Returns:                 -
00186 // Changes Globals:     -
00187 //===========================================================================
00188 void AAS_InitAASLinkedEntities(void)
00189 {
00190     if (!aasworld.loaded) return;
00191     if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
00192     aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory(
00193                         aasworld.numareas * sizeof(aas_link_t *));
00194 } //end of the function AAS_InitAASLinkedEntities
00195 //===========================================================================
00196 //
00197 // Parameter:               -
00198 // Returns:                 -
00199 // Changes Globals:     -
00200 //===========================================================================
00201 void AAS_FreeAASLinkedEntities(void)
00202 {
00203     if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
00204     aasworld.arealinkedentities = NULL;
00205 } //end of the function AAS_InitAASLinkedEntities
00206 //===========================================================================
00207 // returns the AAS area the point is in
00208 //
00209 // Parameter:               -
00210 // Returns:                 -
00211 // Changes Globals:     -
00212 //===========================================================================
00213 int AAS_PointAreaNum(vec3_t point)
00214 {
00215     int nodenum;
00216     vec_t   dist;
00217     aas_node_t *node;
00218     aas_plane_t *plane;
00219 
00220     if (!aasworld.loaded)
00221     {
00222         botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n");
00223         return 0;
00224     } //end if
00225 
00226     //start with node 1 because node zero is a dummy used for solid leafs
00227     nodenum = 1;
00228     while (nodenum > 0)
00229     {
00230 //      botimport.Print(PRT_MESSAGE, "[%d]", nodenum);
00231 #ifdef AAS_SAMPLE_DEBUG
00232         if (nodenum >= aasworld.numnodes)
00233         {
00234             botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes);
00235             return 0;
00236         } //end if
00237 #endif //AAS_SAMPLE_DEBUG
00238         node = &aasworld.nodes[nodenum];
00239 #ifdef AAS_SAMPLE_DEBUG
00240         if (node->planenum < 0 || node->planenum >= aasworld.numplanes)
00241         {
00242             botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes);
00243             return 0;
00244         } //end if
00245 #endif //AAS_SAMPLE_DEBUG
00246         plane = &aasworld.planes[node->planenum];
00247         dist = DotProduct(point, plane->normal) - plane->dist;
00248         if (dist > 0) nodenum = node->children[0];
00249         else nodenum = node->children[1];
00250     } //end while
00251     if (!nodenum)
00252     {
00253 #ifdef AAS_SAMPLE_DEBUG
00254         botimport.Print(PRT_MESSAGE, "in solid\n");
00255 #endif //AAS_SAMPLE_DEBUG
00256         return 0;
00257     } //end if
00258     return -nodenum;
00259 } //end of the function AAS_PointAreaNum
00260 //===========================================================================
00261 //
00262 // Parameter:           -
00263 // Returns:             -
00264 // Changes Globals:     -
00265 //===========================================================================
00266 int AAS_PointReachabilityAreaIndex( vec3_t origin )
00267 {
00268     int areanum, cluster, i, index;
00269 
00270     if (!aasworld.initialized)
00271         return 0;
00272 
00273     if ( !origin )
00274     {
00275         index = 0;
00276         for (i = 0; i < aasworld.numclusters; i++)
00277         {
00278             index += aasworld.clusters[i].numreachabilityareas;
00279         } //end for
00280         return index;
00281     } //end if
00282 
00283     areanum = AAS_PointAreaNum( origin );
00284     if ( !areanum || !AAS_AreaReachability(areanum) )
00285         return 0;
00286     cluster = aasworld.areasettings[areanum].cluster;
00287     areanum = aasworld.areasettings[areanum].clusterareanum;
00288     if (cluster < 0)
00289     {
00290         cluster = aasworld.portals[-cluster].frontcluster;
00291         areanum = aasworld.portals[-cluster].clusterareanum[0];
00292     } //end if
00293 
00294     index = 0;
00295     for (i = 0; i < cluster; i++)
00296     {
00297         index += aasworld.clusters[i].numreachabilityareas;
00298     } //end for
00299     index += areanum;
00300     return index;
00301 } //end of the function AAS_PointReachabilityAreaIndex
00302 //===========================================================================
00303 //
00304 // Parameter:               -
00305 // Returns:                 -
00306 // Changes Globals:     -
00307 //===========================================================================
00308 int AAS_AreaCluster(int areanum)
00309 {
00310     if (areanum <= 0 || areanum >= aasworld.numareas)
00311     {
00312         botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n");
00313         return 0;
00314     } //end if
00315     return aasworld.areasettings[areanum].cluster;
00316 } //end of the function AAS_AreaCluster
00317 //===========================================================================
00318 // returns the presence types of the given area
00319 //
00320 // Parameter:               -
00321 // Returns:                 -
00322 // Changes Globals:     -
00323 //===========================================================================
00324 int AAS_AreaPresenceType(int areanum)
00325 {
00326     if (!aasworld.loaded) return 0;
00327     if (areanum <= 0 || areanum >= aasworld.numareas)
00328     {
00329         botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n");
00330         return 0;
00331     } //end if
00332     return aasworld.areasettings[areanum].presencetype;
00333 } //end of the function AAS_AreaPresenceType
00334 //===========================================================================
00335 // returns the presence type at the given point
00336 //
00337 // Parameter:               -
00338 // Returns:                 -
00339 // Changes Globals:     -
00340 //===========================================================================
00341 int AAS_PointPresenceType(vec3_t point)
00342 {
00343     int areanum;
00344 
00345     if (!aasworld.loaded) return 0;
00346 
00347     areanum = AAS_PointAreaNum(point);
00348     if (!areanum) return PRESENCE_NONE;
00349     return aasworld.areasettings[areanum].presencetype;
00350 } //end of the function AAS_PointPresenceType
00351 //===========================================================================
00352 // calculates the minimum distance between the origin of the box and the
00353 // given plane when both will collide on the given side of the plane
00354 //
00355 // normal   =   normal vector of plane to calculate distance from
00356 // mins     =   minimums of box relative to origin
00357 // maxs     =   maximums of box relative to origin
00358 // side     =   side of the plane we want to calculate the distance from
00359 //                  0 normal vector side
00360 //                  1 not normal vector side
00361 //
00362 // Parameter:               -
00363 // Returns:                 -
00364 // Changes Globals:     -
00365 //===========================================================================
00366 vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side)
00367 {
00368     vec3_t v1, v2;
00369     int i;
00370 
00371     //swap maxs and mins when on the other side of the plane
00372     if (side)
00373     {
00374         //get a point of the box that would be one of the first
00375         //to collide with the plane
00376         for (i = 0; i < 3; i++)
00377         {
00378             if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
00379             else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i];
00380             else v1[i] = 0;
00381         } //end for
00382     } //end if
00383     else
00384     {
00385         //get a point of the box that would be one of the first
00386         //to collide with the plane
00387         for (i = 0; i < 3; i++)
00388         {
00389             if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i];
00390             else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
00391             else v1[i] = 0;
00392         } //end for
00393     } //end else
00394     //
00395     VectorCopy(normal, v2);
00396     VectorInverse(v2);
00397 //  VectorNegate(normal, v2);
00398     return DotProduct(v1, v2);
00399 } //end of the function AAS_BoxOriginDistanceFromPlane
00400 //===========================================================================
00401 //
00402 // Parameter:               -
00403 // Returns:                 -
00404 // Changes Globals:     -
00405 //===========================================================================
00406 qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end,
00407                                         int presencetype, int passent, aas_trace_t *trace)
00408 {
00409     int collision;
00410     vec3_t boxmins, boxmaxs;
00411     aas_link_t *link;
00412     bsp_trace_t bsptrace;
00413 
00414     AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs);
00415 
00416     Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy
00417     //assume no collision
00418     bsptrace.fraction = 1;
00419     collision = qfalse;
00420     for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent)
00421     {
00422         //ignore the pass entity
00423         if (link->entnum == passent) continue;
00424         //
00425         if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end,
00426                                                 CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace))
00427         {
00428             collision = qtrue;
00429         } //end if
00430     } //end for
00431     if (collision)
00432     {
00433         trace->startsolid = bsptrace.startsolid;
00434         trace->ent = bsptrace.ent;
00435         VectorCopy(bsptrace.endpos, trace->endpos);
00436         trace->area = 0;
00437         trace->planenum = 0;
00438         return qtrue;
00439     } //end if
00440     return qfalse;
00441 } //end of the function AAS_AreaEntityCollision
00442 //===========================================================================
00443 // recursive subdivision of the line by the BSP tree.
00444 //
00445 // Parameter:               -
00446 // Returns:                 -
00447 // Changes Globals:     -
00448 //===========================================================================
00449 aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype,
00450                                                                                 int passent)
00451 {
00452     int side, nodenum, tmpplanenum;
00453     float front, back, frac;
00454     vec3_t cur_start, cur_end, cur_mid, v1, v2;
00455     aas_tracestack_t tracestack[127];
00456     aas_tracestack_t *tstack_p;
00457     aas_node_t *aasnode;
00458     aas_plane_t *plane;
00459     aas_trace_t trace;
00460 
00461     //clear the trace structure
00462     Com_Memset(&trace, 0, sizeof(aas_trace_t));
00463 
00464     if (!aasworld.loaded) return trace;
00465     
00466     tstack_p = tracestack;
00467     //we start with the whole line on the stack
00468     VectorCopy(start, tstack_p->start);
00469     VectorCopy(end, tstack_p->end);
00470     tstack_p->planenum = 0;
00471     //start with node 1 because node zero is a dummy for a solid leaf
00472     tstack_p->nodenum = 1;      //starting at the root of the tree
00473     tstack_p++;
00474     
00475     while (1)
00476     {
00477         //pop up the stack
00478         tstack_p--;
00479         //if the trace stack is empty (ended up with a piece of the
00480         //line to be traced in an area)
00481         if (tstack_p < tracestack)
00482         {
00483             tstack_p++;
00484             //nothing was hit
00485             trace.startsolid = qfalse;
00486             trace.fraction = 1.0;
00487             //endpos is the end of the line
00488             VectorCopy(end, trace.endpos);
00489             //nothing hit
00490             trace.ent = 0;
00491             trace.area = 0;
00492             trace.planenum = 0;
00493             return trace;
00494         } //end if
00495         //number of the current node to test the line against
00496         nodenum = tstack_p->nodenum;
00497         //if it is an area
00498         if (nodenum < 0)
00499         {
00500 #ifdef AAS_SAMPLE_DEBUG
00501             if (-nodenum > aasworld.numareasettings)
00502             {
00503                 botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n");
00504                 return trace;
00505             } //end if
00506 #endif //AAS_SAMPLE_DEBUG
00507             //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
00508             //if can't enter the area because it hasn't got the right presence type
00509             if (!(aasworld.areasettings[-nodenum].presencetype & presencetype))
00510             {
00511                 //if the start point is still the initial start point
00512                 //NOTE: no need for epsilons because the points will be
00513                 //exactly the same when they're both the start point
00514                 if (tstack_p->start[0] == start[0] &&
00515                         tstack_p->start[1] == start[1] &&
00516                         tstack_p->start[2] == start[2])
00517                 {
00518                     trace.startsolid = qtrue;
00519                     trace.fraction = 0.0;
00520                     VectorClear(v1);
00521                 } //end if
00522                 else
00523                 {
00524                     trace.startsolid = qfalse;
00525                     VectorSubtract(end, start, v1);
00526                     VectorSubtract(tstack_p->start, start, v2);
00527                     trace.fraction = VectorLength(v2) / VectorNormalize(v1);
00528                     VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
00529                 } //end else
00530                 VectorCopy(tstack_p->start, trace.endpos);
00531                 trace.ent = 0;
00532                 trace.area = -nodenum;
00533 //              VectorSubtract(end, start, v1);
00534                 trace.planenum = tstack_p->planenum;
00535                 //always take the plane with normal facing towards the trace start
00536                 plane = &aasworld.planes[trace.planenum];
00537                 if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
00538                 return trace;
00539             } //end if
00540             else
00541             {
00542                 if (passent >= 0)
00543                 {
00544                     if (AAS_AreaEntityCollision(-nodenum, tstack_p->start,
00545                                                     tstack_p->end, presencetype, passent,
00546                                                     &trace))
00547                     {
00548                         if (!trace.startsolid)
00549                         {
00550                             VectorSubtract(end, start, v1);
00551                             VectorSubtract(trace.endpos, start, v2);
00552                             trace.fraction = VectorLength(v2) / VectorLength(v1);
00553                         } //end if
00554                         return trace;
00555                     } //end if
00556                 } //end if
00557             } //end else
00558             trace.lastarea = -nodenum;
00559             continue;
00560         } //end if
00561         //if it is a solid leaf
00562         if (!nodenum)
00563         {
00564             //if the start point is still the initial start point
00565             //NOTE: no need for epsilons because the points will be
00566             //exactly the same when they're both the start point
00567             if (tstack_p->start[0] == start[0] &&
00568                     tstack_p->start[1] == start[1] &&
00569                     tstack_p->start[2] == start[2])
00570             {
00571                 trace.startsolid = qtrue;
00572                 trace.fraction = 0.0;
00573                 VectorClear(v1);
00574             } //end if
00575             else
00576             {
00577                 trace.startsolid = qfalse;
00578                 VectorSubtract(end, start, v1);
00579                 VectorSubtract(tstack_p->start, start, v2);
00580                 trace.fraction = VectorLength(v2) / VectorNormalize(v1);
00581                 VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
00582             } //end else
00583             VectorCopy(tstack_p->start, trace.endpos);
00584             trace.ent = 0;
00585             trace.area = 0; //hit solid leaf
00586 //          VectorSubtract(end, start, v1);
00587             trace.planenum = tstack_p->planenum;
00588             //always take the plane with normal facing towards the trace start
00589             plane = &aasworld.planes[trace.planenum];
00590             if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
00591             return trace;
00592         } //end if
00593 #ifdef AAS_SAMPLE_DEBUG
00594         if (nodenum > aasworld.numnodes)
00595         {
00596             botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n");
00597             return trace;
00598         } //end if
00599 #endif //AAS_SAMPLE_DEBUG
00600         //the node to test against
00601         aasnode = &aasworld.nodes[nodenum];
00602         //start point of current line to test against node
00603         VectorCopy(tstack_p->start, cur_start);
00604         //end point of the current line to test against node
00605         VectorCopy(tstack_p->end, cur_end);
00606         //the current node plane
00607         plane = &aasworld.planes[aasnode->planenum];
00608 
00609         switch(plane->type)
00610         {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!!
00611             //check for axial planes
00612             case PLANE_X:
00613             {
00614                 front = cur_start[0] - plane->dist;
00615                 back = cur_end[0] - plane->dist;
00616                 break;
00617             } //end case
00618             case PLANE_Y:
00619             {
00620                 front = cur_start[1] - plane->dist;
00621                 back = cur_end[1] - plane->dist;
00622                 break;
00623             } //end case
00624             case PLANE_Z:
00625             {
00626                 front = cur_start[2] - plane->dist;
00627                 back = cur_end[2] - plane->dist;
00628                 break;
00629             } //end case*/
00630             default: //gee it's not an axial plane
00631             {
00632                 front = DotProduct(cur_start, plane->normal) - plane->dist;
00633                 back = DotProduct(cur_end, plane->normal) - plane->dist;
00634                 break;
00635             } //end default
00636         } //end switch
00637         // bk010221 - old location of FPE hack and divide by zero expression
00638         //if the whole to be traced line is totally at the front of this node
00639         //only go down the tree with the front child
00640         if ((front >= -ON_EPSILON && back >= -ON_EPSILON))
00641         {
00642             //keep the current start and end point on the stack
00643             //and go down the tree with the front child
00644             tstack_p->nodenum = aasnode->children[0];
00645             tstack_p++;
00646             if (tstack_p >= &tracestack[127])
00647             {
00648                 botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
00649                 return trace;
00650             } //end if
00651         } //end if
00652         //if the whole to be traced line is totally at the back of this node
00653         //only go down the tree with the back child
00654         else if ((front < ON_EPSILON && back < ON_EPSILON))
00655         {
00656             //keep the current start and end point on the stack
00657             //and go down the tree with the back child
00658             tstack_p->nodenum = aasnode->children[1];
00659             tstack_p++;
00660             if (tstack_p >= &tracestack[127])
00661             {
00662                 botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
00663                 return trace;
00664             } //end if
00665         } //end if
00666         //go down the tree both at the front and back of the node
00667         else
00668         {
00669             tmpplanenum = tstack_p->planenum;
00670             // bk010221 - new location of divide by zero (see above)
00671             if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE 
00672                     //calculate the hitpoint with the node (split point of the line)
00673             //put the crosspoint TRACEPLANE_EPSILON pixels on the near side
00674             if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back);
00675             else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221
00676             //
00677             if (frac < 0)
00678                 frac = 0.001f; //0
00679             else if (frac > 1)
00680                 frac = 0.999f; //1
00681             //frac = front / (front-back);
00682             //
00683             cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
00684             cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
00685             cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
00686 
00687 //          AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
00688             //side the front part of the line is on
00689             side = front < 0;
00690             //first put the end part of the line on the stack (back side)
00691             VectorCopy(cur_mid, tstack_p->start);
00692             //not necesary to store because still on stack
00693             //VectorCopy(cur_end, tstack_p->end);
00694             tstack_p->planenum = aasnode->planenum;
00695             tstack_p->nodenum = aasnode->children[!side];
00696             tstack_p++;
00697             if (tstack_p >= &tracestack[127])
00698             {
00699                 botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
00700                 return trace;
00701             } //end if
00702             //now put the part near the start of the line on the stack so we will
00703             //continue with thats part first. This way we'll find the first
00704             //hit of the bbox
00705             VectorCopy(cur_start, tstack_p->start);
00706             VectorCopy(cur_mid, tstack_p->end);
00707             tstack_p->planenum = tmpplanenum;
00708             tstack_p->nodenum = aasnode->children[side];
00709             tstack_p++;
00710             if (tstack_p >= &tracestack[127])
00711             {
00712                 botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
00713                 return trace;
00714             } //end if
00715         } //end else
00716     } //end while
00717 //  return trace;
00718 } //end of the function AAS_TraceClientBBox
00719 //===========================================================================
00720 // recursive subdivision of the line by the BSP tree.
00721 //
00722 // Parameter:               -
00723 // Returns:                 -
00724 // Changes Globals:     -
00725 //===========================================================================
00726 int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas)
00727 {
00728     int side, nodenum, tmpplanenum;
00729     int numareas;
00730     float front, back, frac;
00731     vec3_t cur_start, cur_end, cur_mid;
00732     aas_tracestack_t tracestack[127];
00733     aas_tracestack_t *tstack_p;
00734     aas_node_t *aasnode;
00735     aas_plane_t *plane;
00736 
00737     numareas = 0;
00738     areas[0] = 0;
00739     if (!aasworld.loaded) return numareas;
00740 
00741     tstack_p = tracestack;
00742     //we start with the whole line on the stack
00743     VectorCopy(start, tstack_p->start);
00744     VectorCopy(end, tstack_p->end);
00745     tstack_p->planenum = 0;
00746     //start with node 1 because node zero is a dummy for a solid leaf
00747     tstack_p->nodenum = 1;      //starting at the root of the tree
00748     tstack_p++;
00749 
00750     while (1)
00751     {
00752         //pop up the stack
00753         tstack_p--;
00754         //if the trace stack is empty (ended up with a piece of the
00755         //line to be traced in an area)
00756         if (tstack_p < tracestack)
00757         {
00758             return numareas;
00759         } //end if
00760         //number of the current node to test the line against
00761         nodenum = tstack_p->nodenum;
00762         //if it is an area
00763         if (nodenum < 0)
00764         {
00765 #ifdef AAS_SAMPLE_DEBUG
00766             if (-nodenum > aasworld.numareasettings)
00767             {
00768                 botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum);
00769                 return numareas;
00770             } //end if
00771 #endif //AAS_SAMPLE_DEBUG
00772             //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
00773             areas[numareas] = -nodenum;
00774             if (points) VectorCopy(tstack_p->start, points[numareas]);
00775             numareas++;
00776             if (numareas >= maxareas) return numareas;
00777             continue;
00778         } //end if
00779         //if it is a solid leaf
00780         if (!nodenum)
00781         {
00782             continue;
00783         } //end if
00784 #ifdef AAS_SAMPLE_DEBUG
00785         if (nodenum > aasworld.numnodes)
00786         {
00787             botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n");
00788             return numareas;
00789         } //end if
00790 #endif //AAS_SAMPLE_DEBUG
00791         //the node to test against
00792         aasnode = &aasworld.nodes[nodenum];
00793         //start point of current line to test against node
00794         VectorCopy(tstack_p->start, cur_start);
00795         //end point of the current line to test against node
00796         VectorCopy(tstack_p->end, cur_end);
00797         //the current node plane
00798         plane = &aasworld.planes[aasnode->planenum];
00799 
00800         switch(plane->type)
00801         {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!!
00802             //check for axial planes
00803             case PLANE_X:
00804             {
00805                 front = cur_start[0] - plane->dist;
00806                 back = cur_end[0] - plane->dist;
00807                 break;
00808             } //end case
00809             case PLANE_Y:
00810             {
00811                 front = cur_start[1] - plane->dist;
00812                 back = cur_end[1] - plane->dist;
00813                 break;
00814             } //end case
00815             case PLANE_Z:
00816             {
00817                 front = cur_start[2] - plane->dist;
00818                 back = cur_end[2] - plane->dist;
00819                 break;
00820             } //end case*/
00821             default: //gee it's not an axial plane
00822             {
00823                 front = DotProduct(cur_start, plane->normal) - plane->dist;
00824                 back = DotProduct(cur_end, plane->normal) - plane->dist;
00825                 break;
00826             } //end default
00827         } //end switch
00828 
00829         //if the whole to be traced line is totally at the front of this node
00830         //only go down the tree with the front child
00831         if (front > 0 && back > 0)
00832         {
00833             //keep the current start and end point on the stack
00834             //and go down the tree with the front child
00835             tstack_p->nodenum = aasnode->children[0];
00836             tstack_p++;
00837             if (tstack_p >= &tracestack[127])
00838             {
00839                 botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
00840                 return numareas;
00841             } //end if
00842         } //end if
00843         //if the whole to be traced line is totally at the back of this node
00844         //only go down the tree with the back child
00845         else if (front <= 0 && back <= 0)
00846         {
00847             //keep the current start and end point on the stack
00848             //and go down the tree with the back child
00849             tstack_p->nodenum = aasnode->children[1];
00850             tstack_p++;
00851             if (tstack_p >= &tracestack[127])
00852             {
00853                 botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
00854                 return numareas;
00855             } //end if
00856         } //end if
00857         //go down the tree both at the front and back of the node
00858         else
00859         {
00860             tmpplanenum = tstack_p->planenum;
00861             //calculate the hitpoint with the node (split point of the line)
00862             //put the crosspoint TRACEPLANE_EPSILON pixels on the near side
00863             if (front < 0) frac = (front)/(front-back);
00864             else frac = (front)/(front-back);
00865             if (frac < 0) frac = 0;
00866             else if (frac > 1) frac = 1;
00867             //frac = front / (front-back);
00868             //
00869             cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
00870             cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
00871             cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
00872 
00873 //          AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
00874             //side the front part of the line is on
00875             side = front < 0;
00876             //first put the end part of the line on the stack (back side)
00877             VectorCopy(cur_mid, tstack_p->start);
00878             //not necesary to store because still on stack
00879             //VectorCopy(cur_end, tstack_p->end);
00880             tstack_p->planenum = aasnode->planenum;
00881             tstack_p->nodenum = aasnode->children[!side];
00882             tstack_p++;
00883             if (tstack_p >= &tracestack[127])
00884             {
00885                 botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
00886                 return numareas;
00887             } //end if
00888             //now put the part near the start of the line on the stack so we will
00889             //continue with thats part first. This way we'll find the first
00890             //hit of the bbox
00891             VectorCopy(cur_start, tstack_p->start);
00892             VectorCopy(cur_mid, tstack_p->end);
00893             tstack_p->planenum = tmpplanenum;
00894             tstack_p->nodenum = aasnode->children[side];
00895             tstack_p++;
00896             if (tstack_p >= &tracestack[127])
00897             {
00898                 botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
00899                 return numareas;
00900             } //end if
00901         } //end else
00902     } //end while
00903 //  return numareas;
00904 } //end of the function AAS_TraceAreas
00905 //===========================================================================
00906 // a simple cross product
00907 //
00908 // Parameter:               -
00909 // Returns:                 -
00910 // Changes Globals:     -
00911 //===========================================================================
00912 // void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res)
00913 #define AAS_OrthogonalToVectors(v1, v2, res) \
00914     (res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\
00915     (res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\
00916     (res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]);
00917 //===========================================================================
00918 // tests if the given point is within the face boundaries
00919 //
00920 // Parameter:               face        : face to test if the point is in it
00921 //                              pnormal : normal of the plane to use for the face
00922 //                              point       : point to test if inside face boundaries
00923 // Returns:                 qtrue if the point is within the face boundaries
00924 // Changes Globals:     -
00925 //===========================================================================
00926 qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon)
00927 {
00928     int i, firstvertex, edgenum;
00929     vec3_t v0;
00930     vec3_t edgevec, pointvec, sepnormal;
00931     aas_edge_t *edge;
00932 #ifdef AAS_SAMPLE_DEBUG
00933     int lastvertex = 0;
00934 #endif //AAS_SAMPLE_DEBUG
00935 
00936     if (!aasworld.loaded) return qfalse;
00937 
00938     for (i = 0; i < face->numedges; i++)
00939     {
00940         edgenum = aasworld.edgeindex[face->firstedge + i];
00941         edge = &aasworld.edges[abs(edgenum)];
00942         //get the first vertex of the edge
00943         firstvertex = edgenum < 0;
00944         VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0);
00945         //edge vector
00946         VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec);
00947         //
00948 #ifdef AAS_SAMPLE_DEBUG
00949         if (lastvertex && lastvertex != edge->v[firstvertex])
00950         {
00951             botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n");
00952         } //end if
00953         lastvertex = edge->v[!firstvertex];
00954 #endif //AAS_SAMPLE_DEBUG
00955         //vector from first edge point to point possible in face
00956         VectorSubtract(point, v0, pointvec);
00957         //get a vector pointing inside the face orthogonal to both the
00958         //edge vector and the normal vector of the plane the face is in
00959         //this vector defines a plane through the origin (first vertex of
00960         //edge) and through both the edge vector and the normal vector
00961         //of the plane
00962         AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal);
00963         //check on wich side of the above plane the point is
00964         //this is done by checking the sign of the dot product of the
00965         //vector orthogonal vector from above and the vector from the
00966         //origin (first vertex of edge) to the point 
00967         //if the dotproduct is smaller than zero the point is outside the face
00968         if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
00969     } //end for
00970     return qtrue;
00971 } //end of the function AAS_InsideFace
00972 //===========================================================================
00973 //
00974 // Parameter:               -
00975 // Returns:                 -
00976 // Changes Globals:     -
00977 //===========================================================================
00978 qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon)
00979 {
00980     int i, firstvertex, edgenum;
00981     vec_t *v1, *v2;
00982     vec3_t edgevec, pointvec, sepnormal;
00983     aas_edge_t *edge;
00984     aas_plane_t *plane;
00985     aas_face_t *face;
00986 
00987     if (!aasworld.loaded) return qfalse;
00988 
00989     face = &aasworld.faces[facenum];
00990     plane = &aasworld.planes[face->planenum];
00991     //
00992     for (i = 0; i < face->numedges; i++)
00993     {
00994         edgenum = aasworld.edgeindex[face->firstedge + i];
00995         edge = &aasworld.edges[abs(edgenum)];
00996         //get the first vertex of the edge
00997         firstvertex = edgenum < 0;
00998         v1 = aasworld.vertexes[edge->v[firstvertex]];
00999         v2 = aasworld.vertexes[edge->v[!firstvertex]];
01000         //edge vector
01001         VectorSubtract(v2, v1, edgevec);
01002         //vector from first edge point to point possible in face
01003         VectorSubtract(point, v1, pointvec);
01004         //
01005         CrossProduct(edgevec, plane->normal, sepnormal);
01006         //
01007         if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
01008     } //end for
01009     return qtrue;
01010 } //end of the function AAS_PointInsideFace
01011 //===========================================================================
01012 // returns the ground face the given point is above in the given area
01013 //
01014 // Parameter:               -
01015 // Returns:                 -
01016 // Changes Globals:     -
01017 //===========================================================================
01018 aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point)
01019 {
01020     int i, facenum;
01021     vec3_t up = {0, 0, 1};
01022     vec3_t normal;
01023     aas_area_t *area;
01024     aas_face_t *face;
01025 
01026     if (!aasworld.loaded) return NULL;
01027 
01028     area = &aasworld.areas[areanum];
01029     for (i = 0; i < area->numfaces; i++)
01030     {
01031         facenum = aasworld.faceindex[area->firstface + i];
01032         face = &aasworld.faces[abs(facenum)];
01033         //if this is a ground face
01034         if (face->faceflags & FACE_GROUND)
01035         {
01036             //get the up or down normal
01037             if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal);
01038             else VectorCopy(up, normal);
01039             //check if the point is in the face
01040             if (AAS_InsideFace(face, normal, point, 0.01f)) return face;
01041         } //end if
01042     } //end for
01043     return NULL;
01044 } //end of the function AAS_AreaGroundFace
01045 //===========================================================================
01046 // returns the face the trace end position is situated in
01047 //
01048 // Parameter:               -
01049 // Returns:                 -
01050 // Changes Globals:     -
01051 //===========================================================================
01052 void AAS_FacePlane(int facenum, vec3_t normal, float *dist)
01053 {
01054     aas_plane_t *plane;
01055 
01056     plane = &aasworld.planes[aasworld.faces[facenum].planenum];
01057     VectorCopy(plane->normal, normal);
01058     *dist = plane->dist;
01059 } //end of the function AAS_FacePlane
01060 //===========================================================================
01061 // returns the face the trace end position is situated in
01062 //
01063 // Parameter:               -
01064 // Returns:                 -
01065 // Changes Globals:     -
01066 //===========================================================================
01067 aas_face_t *AAS_TraceEndFace(aas_trace_t *trace)
01068 {
01069     int i, facenum;
01070     aas_area_t *area;
01071     aas_face_t *face, *firstface = NULL;
01072 
01073     if (!aasworld.loaded) return NULL;
01074 
01075     //if started in solid no face was hit
01076     if (trace->startsolid) return NULL;
01077     //trace->lastarea is the last area the trace was in
01078     area = &aasworld.areas[trace->lastarea];
01079     //check which face the trace.endpos was in
01080     for (i = 0; i < area->numfaces; i++)
01081     {
01082         facenum = aasworld.faceindex[area->firstface + i];
01083         face = &aasworld.faces[abs(facenum)];
01084         //if the face is in the same plane as the trace end point
01085         if ((face->planenum & ~1) == (trace->planenum & ~1))
01086         {
01087             //firstface is used for optimization, if theres only one
01088             //face in the plane then it has to be the good one
01089             //if there are more faces in the same plane then always
01090             //check the one with the fewest edges first
01091 /*          if (firstface)
01092             {
01093                 if (firstface->numedges < face->numedges)
01094                 {
01095                     if (AAS_InsideFace(firstface,
01096                         aasworld.planes[face->planenum].normal, trace->endpos))
01097                     {
01098                         return firstface;
01099                     } //end if
01100                     firstface = face;
01101                 } //end if
01102                 else
01103                 {
01104                     if (AAS_InsideFace(face,
01105                         aasworld.planes[face->planenum].normal, trace->endpos))
01106                     {
01107                         return face;
01108                     } //end if
01109                 } //end else
01110             } //end if
01111             else
01112             {
01113                 firstface = face;
01114             } //end else*/
01115             if (AAS_InsideFace(face,
01116                         aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face;
01117         } //end if
01118     } //end for
01119     return firstface;
01120 } //end of the function AAS_TraceEndFace
01121 //===========================================================================
01122 //
01123 // Parameter:               -
01124 // Returns:                 -
01125 // Changes Globals:     -
01126 //===========================================================================
01127 int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
01128 {
01129     int i, sides;
01130     float dist1, dist2;
01131     vec3_t corners[2];
01132 
01133     for (i = 0; i < 3; i++)
01134     {
01135         if (p->normal[i] < 0)
01136         {
01137             corners[0][i] = absmins[i];
01138             corners[1][i] = absmaxs[i];
01139         } //end if
01140         else
01141         {
01142             corners[1][i] = absmins[i];
01143             corners[0][i] = absmaxs[i];
01144         } //end else
01145     } //end for
01146     dist1 = DotProduct(p->normal, corners[0]) - p->dist;
01147     dist2 = DotProduct(p->normal, corners[1]) - p->dist;
01148     sides = 0;
01149     if (dist1 >= 0) sides = 1;
01150     if (dist2 < 0) sides |= 2;
01151 
01152     return sides;
01153 } //end of the function AAS_BoxOnPlaneSide2
01154 //===========================================================================
01155 //
01156 // Parameter:               -
01157 // Returns:                 -
01158 // Changes Globals:     -
01159 //===========================================================================
01160 //int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
01161 #define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\
01162     ( (p)->type < 3) ?\
01163     (\
01164         ( (p)->dist <= (absmins)[(p)->type]) ?\
01165         (\
01166             1\
01167         )\
01168         :\
01169         (\
01170             ( (p)->dist >= (absmaxs)[(p)->type]) ?\
01171             (\
01172                 2\
01173             )\
01174             :\
01175             (\
01176                 3\
01177             )\
01178         )\
01179     )\
01180     :\
01181     (\
01182         AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\
01183     )\
01184 ) //end of the function AAS_BoxOnPlaneSide
01185 //===========================================================================
01186 // remove the links to this entity from all areas
01187 //
01188 // Parameter:               -
01189 // Returns:                 -
01190 // Changes Globals:     -
01191 //===========================================================================
01192 void AAS_UnlinkFromAreas(aas_link_t *areas)
01193 {
01194     aas_link_t *link, *nextlink;
01195 
01196     for (link = areas; link; link = nextlink)
01197     {
01198         //next area the entity is linked in
01199         nextlink = link->next_area;
01200         //remove the entity from the linked list of this area
01201         if (link->prev_ent) link->prev_ent->next_ent = link->next_ent;
01202         else aasworld.arealinkedentities[link->areanum] = link->next_ent;
01203         if (link->next_ent) link->next_ent->prev_ent = link->prev_ent;
01204         //deallocate the link structure
01205         AAS_DeAllocAASLink(link);
01206     } //end for
01207 } //end of the function AAS_UnlinkFromAreas
01208 //===========================================================================
01209 // link the entity to the areas the bounding box is totally or partly
01210 // situated in. This is done with recursion down the tree using the
01211 // bounding box to test for plane sides
01212 //
01213 // Parameter:               -
01214 // Returns:                 -
01215 // Changes Globals:     -
01216 //===========================================================================
01217 
01218 typedef struct
01219 {
01220     int nodenum;        //node found after splitting
01221 } aas_linkstack_t;
01222 
01223 aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum)
01224 {
01225     int side, nodenum;
01226     aas_linkstack_t linkstack[128];
01227     aas_linkstack_t *lstack_p;
01228     aas_node_t *aasnode;
01229     aas_plane_t *plane;
01230     aas_link_t *link, *areas;
01231 
01232     if (!aasworld.loaded)
01233     {
01234         botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n");
01235         return NULL;
01236     } //end if
01237 
01238     areas = NULL;
01239     //
01240     lstack_p = linkstack;
01241     //we start with the whole line on the stack
01242     //start with node 1 because node zero is a dummy used for solid leafs
01243     lstack_p->nodenum = 1;      //starting at the root of the tree
01244     lstack_p++;
01245     
01246     while (1)
01247     {
01248         //pop up the stack
01249         lstack_p--;
01250         //if the trace stack is empty (ended up with a piece of the
01251         //line to be traced in an area)
01252         if (lstack_p < linkstack) break;
01253         //number of the current node to test the line against
01254         nodenum = lstack_p->nodenum;
01255         //if it is an area
01256         if (nodenum < 0)
01257         {
01258             //NOTE: the entity might have already been linked into this area
01259             // because several node children can point to the same area
01260             for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent)
01261             {
01262                 if (link->entnum == entnum) break;
01263             } //end for
01264             if (link) continue;
01265             //
01266             link = AAS_AllocAASLink();
01267             if (!link) return areas;
01268             link->entnum = entnum;
01269             link->areanum = -nodenum;
01270             //put the link into the double linked area list of the entity
01271             link->prev_area = NULL;
01272             link->next_area = areas;
01273             if (areas) areas->prev_area = link;
01274             areas = link;
01275             //put the link into the double linked entity list of the area
01276             link->prev_ent = NULL;
01277             link->next_ent = aasworld.