Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

be_aas_sample.c (44151B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 /*****************************************************************************
     24  * name:		be_aas_sample.c
     25  *
     26  * desc:		AAS environment sampling
     27  *
     28  * $Archive: /MissionPack/code/botlib/be_aas_sample.c $
     29  *
     30  *****************************************************************************/
     31 
     32 #include "../game/q_shared.h"
     33 #include "l_memory.h"
     34 #include "l_script.h"
     35 #include "l_precomp.h"
     36 #include "l_struct.h"
     37 #ifndef BSPC
     38 #include "l_libvar.h"
     39 #endif
     40 #include "aasfile.h"
     41 #include "../game/botlib.h"
     42 #include "../game/be_aas.h"
     43 #include "be_interface.h"
     44 #include "be_aas_funcs.h"
     45 #include "be_aas_def.h"
     46 
     47 extern botlib_import_t botimport;
     48 
     49 //#define AAS_SAMPLE_DEBUG
     50 
     51 #define BBOX_NORMAL_EPSILON		0.001
     52 
     53 #define ON_EPSILON					0 //0.0005
     54 
     55 #define TRACEPLANE_EPSILON			0.125
     56 
     57 typedef struct aas_tracestack_s
     58 {
     59 	vec3_t start;		//start point of the piece of line to trace
     60 	vec3_t end;			//end point of the piece of line to trace
     61 	int planenum;		//last plane used as splitter
     62 	int nodenum;		//node found after splitting with planenum
     63 } aas_tracestack_t;
     64 
     65 int numaaslinks;
     66 
     67 //===========================================================================
     68 //
     69 // Parameter:				-
     70 // Returns:					-
     71 // Changes Globals:		-
     72 //===========================================================================
     73 void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs)
     74 {
     75 	int index;
     76 	//bounding box size for each presence type
     77 	vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}};
     78 	vec3_t boxmaxs[3] = {{0, 0, 0}, { 15,  15,  32}, { 15,  15,   8}};
     79 
     80 	if (presencetype == PRESENCE_NORMAL) index = 1;
     81 	else if (presencetype == PRESENCE_CROUCH) index = 2;
     82 	else
     83 	{
     84 		botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n");
     85 		index = 2;
     86 	} //end if
     87 	VectorCopy(boxmins[index], mins);
     88 	VectorCopy(boxmaxs[index], maxs);
     89 } //end of the function AAS_PresenceTypeBoundingBox
     90 //===========================================================================
     91 //
     92 // Parameter:				-
     93 // Returns:					-
     94 // Changes Globals:		-
     95 //===========================================================================
     96 void AAS_InitAASLinkHeap(void)
     97 {
     98 	int i, max_aaslinks;
     99 
    100 	max_aaslinks = aasworld.linkheapsize;
    101 	//if there's no link heap present
    102 	if (!aasworld.linkheap)
    103 	{
    104 #ifdef BSPC
    105 		max_aaslinks = 6144;
    106 #else
    107 		max_aaslinks = (int) LibVarValue("max_aaslinks", "6144");
    108 #endif
    109 		if (max_aaslinks < 0) max_aaslinks = 0;
    110 		aasworld.linkheapsize = max_aaslinks;
    111 		aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t));
    112 	} //end if
    113 	//link the links on the heap
    114 	aasworld.linkheap[0].prev_ent = NULL;
    115 	aasworld.linkheap[0].next_ent = &aasworld.linkheap[1];
    116 	for (i = 1; i < max_aaslinks-1; i++)
    117 	{
    118 		aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1];
    119 		aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1];
    120 	} //end for
    121 	aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2];
    122 	aasworld.linkheap[max_aaslinks-1].next_ent = NULL;
    123 	//pointer to the first free link
    124 	aasworld.freelinks = &aasworld.linkheap[0];
    125 	//
    126 	numaaslinks = max_aaslinks;
    127 } //end of the function AAS_InitAASLinkHeap
    128 //===========================================================================
    129 //
    130 // Parameter:				-
    131 // Returns:					-
    132 // Changes Globals:		-
    133 //===========================================================================
    134 void AAS_FreeAASLinkHeap(void)
    135 {
    136 	if (aasworld.linkheap) FreeMemory(aasworld.linkheap);
    137 	aasworld.linkheap = NULL;
    138 	aasworld.linkheapsize = 0;
    139 } //end of the function AAS_FreeAASLinkHeap
    140 //===========================================================================
    141 //
    142 // Parameter:				-
    143 // Returns:					-
    144 // Changes Globals:		-
    145 //===========================================================================
    146 aas_link_t *AAS_AllocAASLink(void)
    147 {
    148 	aas_link_t *link;
    149 
    150 	link = aasworld.freelinks;
    151 	if (!link)
    152 	{
    153 #ifndef BSPC
    154 		if (bot_developer)
    155 #endif
    156 		{
    157 			botimport.Print(PRT_FATAL, "empty aas link heap\n");
    158 		} //end if
    159 		return NULL;
    160 	} //end if
    161 	if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent;
    162 	if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL;
    163 	numaaslinks--;
    164 	return link;
    165 } //end of the function AAS_AllocAASLink
    166 //===========================================================================
    167 //
    168 // Parameter:				-
    169 // Returns:					-
    170 // Changes Globals:		-
    171 //===========================================================================
    172 void AAS_DeAllocAASLink(aas_link_t *link)
    173 {
    174 	if (aasworld.freelinks) aasworld.freelinks->prev_ent = link;
    175 	link->prev_ent = NULL;
    176 	link->next_ent = aasworld.freelinks;
    177 	link->prev_area = NULL;
    178 	link->next_area = NULL;
    179 	aasworld.freelinks = link;
    180 	numaaslinks++;
    181 } //end of the function AAS_DeAllocAASLink
    182 //===========================================================================
    183 //
    184 // Parameter:				-
    185 // Returns:					-
    186 // Changes Globals:		-
    187 //===========================================================================
    188 void AAS_InitAASLinkedEntities(void)
    189 {
    190 	if (!aasworld.loaded) return;
    191 	if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
    192 	aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory(
    193 						aasworld.numareas * sizeof(aas_link_t *));
    194 } //end of the function AAS_InitAASLinkedEntities
    195 //===========================================================================
    196 //
    197 // Parameter:				-
    198 // Returns:					-
    199 // Changes Globals:		-
    200 //===========================================================================
    201 void AAS_FreeAASLinkedEntities(void)
    202 {
    203 	if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
    204 	aasworld.arealinkedentities = NULL;
    205 } //end of the function AAS_InitAASLinkedEntities
    206 //===========================================================================
    207 // returns the AAS area the point is in
    208 //
    209 // Parameter:				-
    210 // Returns:					-
    211 // Changes Globals:		-
    212 //===========================================================================
    213 int AAS_PointAreaNum(vec3_t point)
    214 {
    215 	int nodenum;
    216 	vec_t	dist;
    217 	aas_node_t *node;
    218 	aas_plane_t *plane;
    219 
    220 	if (!aasworld.loaded)
    221 	{
    222 		botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n");
    223 		return 0;
    224 	} //end if
    225 
    226 	//start with node 1 because node zero is a dummy used for solid leafs
    227 	nodenum = 1;
    228 	while (nodenum > 0)
    229 	{
    230 //		botimport.Print(PRT_MESSAGE, "[%d]", nodenum);
    231 #ifdef AAS_SAMPLE_DEBUG
    232 		if (nodenum >= aasworld.numnodes)
    233 		{
    234 			botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes);
    235 			return 0;
    236 		} //end if
    237 #endif //AAS_SAMPLE_DEBUG
    238 		node = &aasworld.nodes[nodenum];
    239 #ifdef AAS_SAMPLE_DEBUG
    240 		if (node->planenum < 0 || node->planenum >= aasworld.numplanes)
    241 		{
    242 			botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes);
    243 			return 0;
    244 		} //end if
    245 #endif //AAS_SAMPLE_DEBUG
    246 		plane = &aasworld.planes[node->planenum];
    247 		dist = DotProduct(point, plane->normal) - plane->dist;
    248 		if (dist > 0) nodenum = node->children[0];
    249 		else nodenum = node->children[1];
    250 	} //end while
    251 	if (!nodenum)
    252 	{
    253 #ifdef AAS_SAMPLE_DEBUG
    254 		botimport.Print(PRT_MESSAGE, "in solid\n");
    255 #endif //AAS_SAMPLE_DEBUG
    256 		return 0;
    257 	} //end if
    258 	return -nodenum;
    259 } //end of the function AAS_PointAreaNum
    260 //===========================================================================
    261 //
    262 // Parameter:			-
    263 // Returns:				-
    264 // Changes Globals:		-
    265 //===========================================================================
    266 int AAS_PointReachabilityAreaIndex( vec3_t origin )
    267 {
    268 	int areanum, cluster, i, index;
    269 
    270 	if (!aasworld.initialized)
    271 		return 0;
    272 
    273 	if ( !origin )
    274 	{
    275 		index = 0;
    276 		for (i = 0; i < aasworld.numclusters; i++)
    277 		{
    278 			index += aasworld.clusters[i].numreachabilityareas;
    279 		} //end for
    280 		return index;
    281 	} //end if
    282 
    283 	areanum = AAS_PointAreaNum( origin );
    284 	if ( !areanum || !AAS_AreaReachability(areanum) )
    285 		return 0;
    286 	cluster = aasworld.areasettings[areanum].cluster;
    287 	areanum = aasworld.areasettings[areanum].clusterareanum;
    288 	if (cluster < 0)
    289 	{
    290 		cluster = aasworld.portals[-cluster].frontcluster;
    291 		areanum = aasworld.portals[-cluster].clusterareanum[0];
    292 	} //end if
    293 
    294 	index = 0;
    295 	for (i = 0; i < cluster; i++)
    296 	{
    297 		index += aasworld.clusters[i].numreachabilityareas;
    298 	} //end for
    299 	index += areanum;
    300 	return index;
    301 } //end of the function AAS_PointReachabilityAreaIndex
    302 //===========================================================================
    303 //
    304 // Parameter:				-
    305 // Returns:					-
    306 // Changes Globals:		-
    307 //===========================================================================
    308 int AAS_AreaCluster(int areanum)
    309 {
    310 	if (areanum <= 0 || areanum >= aasworld.numareas)
    311 	{
    312 		botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n");
    313 		return 0;
    314 	} //end if
    315 	return aasworld.areasettings[areanum].cluster;
    316 } //end of the function AAS_AreaCluster
    317 //===========================================================================
    318 // returns the presence types of the given area
    319 //
    320 // Parameter:				-
    321 // Returns:					-
    322 // Changes Globals:		-
    323 //===========================================================================
    324 int AAS_AreaPresenceType(int areanum)
    325 {
    326 	if (!aasworld.loaded) return 0;
    327 	if (areanum <= 0 || areanum >= aasworld.numareas)
    328 	{
    329 		botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n");
    330 		return 0;
    331 	} //end if
    332 	return aasworld.areasettings[areanum].presencetype;
    333 } //end of the function AAS_AreaPresenceType
    334 //===========================================================================
    335 // returns the presence type at the given point
    336 //
    337 // Parameter:				-
    338 // Returns:					-
    339 // Changes Globals:		-
    340 //===========================================================================
    341 int AAS_PointPresenceType(vec3_t point)
    342 {
    343 	int areanum;
    344 
    345 	if (!aasworld.loaded) return 0;
    346 
    347 	areanum = AAS_PointAreaNum(point);
    348 	if (!areanum) return PRESENCE_NONE;
    349 	return aasworld.areasettings[areanum].presencetype;
    350 } //end of the function AAS_PointPresenceType
    351 //===========================================================================
    352 // calculates the minimum distance between the origin of the box and the
    353 // given plane when both will collide on the given side of the plane
    354 //
    355 // normal	=	normal vector of plane to calculate distance from
    356 // mins		=	minimums of box relative to origin
    357 // maxs		=	maximums of box relative to origin
    358 // side		=	side of the plane we want to calculate the distance from
    359 //					0 normal vector side
    360 //					1 not normal vector side
    361 //
    362 // Parameter:				-
    363 // Returns:					-
    364 // Changes Globals:		-
    365 //===========================================================================
    366 vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side)
    367 {
    368 	vec3_t v1, v2;
    369 	int i;
    370 
    371 	//swap maxs and mins when on the other side of the plane
    372 	if (side)
    373 	{
    374 		//get a point of the box that would be one of the first
    375 		//to collide with the plane
    376 		for (i = 0; i < 3; i++)
    377 		{
    378 			if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
    379 			else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i];
    380 			else v1[i] = 0;
    381 		} //end for
    382 	} //end if
    383 	else
    384 	{
    385 		//get a point of the box that would be one of the first
    386 		//to collide with the plane
    387 		for (i = 0; i < 3; i++)
    388 		{
    389 			if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i];
    390 			else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
    391 			else v1[i] = 0;
    392 		} //end for
    393 	} //end else
    394 	//
    395 	VectorCopy(normal, v2);
    396 	VectorInverse(v2);
    397 //	VectorNegate(normal, v2);
    398 	return DotProduct(v1, v2);
    399 } //end of the function AAS_BoxOriginDistanceFromPlane
    400 //===========================================================================
    401 //
    402 // Parameter:				-
    403 // Returns:					-
    404 // Changes Globals:		-
    405 //===========================================================================
    406 qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end,
    407 										int presencetype, int passent, aas_trace_t *trace)
    408 {
    409 	int collision;
    410 	vec3_t boxmins, boxmaxs;
    411 	aas_link_t *link;
    412 	bsp_trace_t bsptrace;
    413 
    414 	AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs);
    415 
    416 	Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy
    417 	//assume no collision
    418 	bsptrace.fraction = 1;
    419 	collision = qfalse;
    420 	for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent)
    421 	{
    422 		//ignore the pass entity
    423 		if (link->entnum == passent) continue;
    424 		//
    425 		if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end,
    426 												CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace))
    427 		{
    428 			collision = qtrue;
    429 		} //end if
    430 	} //end for
    431 	if (collision)
    432 	{
    433 		trace->startsolid = bsptrace.startsolid;
    434 		trace->ent = bsptrace.ent;
    435 		VectorCopy(bsptrace.endpos, trace->endpos);
    436 		trace->area = 0;
    437 		trace->planenum = 0;
    438 		return qtrue;
    439 	} //end if
    440 	return qfalse;
    441 } //end of the function AAS_AreaEntityCollision
    442 //===========================================================================
    443 // recursive subdivision of the line by the BSP tree.
    444 //
    445 // Parameter:				-
    446 // Returns:					-
    447 // Changes Globals:		-
    448 //===========================================================================
    449 aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype,
    450 																				int passent)
    451 {
    452 	int side, nodenum, tmpplanenum;
    453 	float front, back, frac;
    454 	vec3_t cur_start, cur_end, cur_mid, v1, v2;
    455 	aas_tracestack_t tracestack[127];
    456 	aas_tracestack_t *tstack_p;
    457 	aas_node_t *aasnode;
    458 	aas_plane_t *plane;
    459 	aas_trace_t trace;
    460 
    461 	//clear the trace structure
    462 	Com_Memset(&trace, 0, sizeof(aas_trace_t));
    463 
    464 	if (!aasworld.loaded) return trace;
    465 	
    466 	tstack_p = tracestack;
    467 	//we start with the whole line on the stack
    468 	VectorCopy(start, tstack_p->start);
    469 	VectorCopy(end, tstack_p->end);
    470 	tstack_p->planenum = 0;
    471 	//start with node 1 because node zero is a dummy for a solid leaf
    472 	tstack_p->nodenum = 1;		//starting at the root of the tree
    473 	tstack_p++;
    474 	
    475 	while (1)
    476 	{
    477 		//pop up the stack
    478 		tstack_p--;
    479 		//if the trace stack is empty (ended up with a piece of the
    480 		//line to be traced in an area)
    481 		if (tstack_p < tracestack)
    482 		{
    483 			tstack_p++;
    484 			//nothing was hit
    485 			trace.startsolid = qfalse;
    486 			trace.fraction = 1.0;
    487 			//endpos is the end of the line
    488 			VectorCopy(end, trace.endpos);
    489 			//nothing hit
    490 			trace.ent = 0;
    491 			trace.area = 0;
    492 			trace.planenum = 0;
    493 			return trace;
    494 		} //end if
    495 		//number of the current node to test the line against
    496 		nodenum = tstack_p->nodenum;
    497 		//if it is an area
    498 		if (nodenum < 0)
    499 		{
    500 #ifdef AAS_SAMPLE_DEBUG
    501 			if (-nodenum > aasworld.numareasettings)
    502 			{
    503 				botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n");
    504 				return trace;
    505 			} //end if
    506 #endif //AAS_SAMPLE_DEBUG
    507 			//botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
    508 			//if can't enter the area because it hasn't got the right presence type
    509 			if (!(aasworld.areasettings[-nodenum].presencetype & presencetype))
    510 			{
    511 				//if the start point is still the initial start point
    512 				//NOTE: no need for epsilons because the points will be
    513 				//exactly the same when they're both the start point
    514 				if (tstack_p->start[0] == start[0] &&
    515 						tstack_p->start[1] == start[1] &&
    516 						tstack_p->start[2] == start[2])
    517 				{
    518 					trace.startsolid = qtrue;
    519 					trace.fraction = 0.0;
    520 					VectorClear(v1);
    521 				} //end if
    522 				else
    523 				{
    524 					trace.startsolid = qfalse;
    525 					VectorSubtract(end, start, v1);
    526 					VectorSubtract(tstack_p->start, start, v2);
    527 					trace.fraction = VectorLength(v2) / VectorNormalize(v1);
    528 					VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
    529 				} //end else
    530 				VectorCopy(tstack_p->start, trace.endpos);
    531 				trace.ent = 0;
    532 				trace.area = -nodenum;
    533 //				VectorSubtract(end, start, v1);
    534 				trace.planenum = tstack_p->planenum;
    535 				//always take the plane with normal facing towards the trace start
    536 				plane = &aasworld.planes[trace.planenum];
    537 				if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
    538 				return trace;
    539 			} //end if
    540 			else
    541 			{
    542 				if (passent >= 0)
    543 				{
    544 					if (AAS_AreaEntityCollision(-nodenum, tstack_p->start,
    545 													tstack_p->end, presencetype, passent,
    546 													&trace))
    547 					{
    548 						if (!trace.startsolid)
    549 						{
    550 							VectorSubtract(end, start, v1);
    551 							VectorSubtract(trace.endpos, start, v2);
    552 							trace.fraction = VectorLength(v2) / VectorLength(v1);
    553 						} //end if
    554 						return trace;
    555 					} //end if
    556 				} //end if
    557 			} //end else
    558 			trace.lastarea = -nodenum;
    559 			continue;
    560 		} //end if
    561 		//if it is a solid leaf
    562 		if (!nodenum)
    563 		{
    564 			//if the start point is still the initial start point
    565 			//NOTE: no need for epsilons because the points will be
    566 			//exactly the same when they're both the start point
    567 			if (tstack_p->start[0] == start[0] &&
    568 					tstack_p->start[1] == start[1] &&
    569 					tstack_p->start[2] == start[2])
    570 			{
    571 				trace.startsolid = qtrue;
    572 				trace.fraction = 0.0;
    573 				VectorClear(v1);
    574 			} //end if
    575 			else
    576 			{
    577 				trace.startsolid = qfalse;
    578 				VectorSubtract(end, start, v1);
    579 				VectorSubtract(tstack_p->start, start, v2);
    580 				trace.fraction = VectorLength(v2) / VectorNormalize(v1);
    581 				VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
    582 			} //end else
    583 			VectorCopy(tstack_p->start, trace.endpos);
    584 			trace.ent = 0;
    585 			trace.area = 0;	//hit solid leaf
    586 //			VectorSubtract(end, start, v1);
    587 			trace.planenum = tstack_p->planenum;
    588 			//always take the plane with normal facing towards the trace start
    589 			plane = &aasworld.planes[trace.planenum];
    590 			if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
    591 			return trace;
    592 		} //end if
    593 #ifdef AAS_SAMPLE_DEBUG
    594 		if (nodenum > aasworld.numnodes)
    595 		{
    596 			botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n");
    597 			return trace;
    598 		} //end if
    599 #endif //AAS_SAMPLE_DEBUG
    600 		//the node to test against
    601 		aasnode = &aasworld.nodes[nodenum];
    602 		//start point of current line to test against node
    603 		VectorCopy(tstack_p->start, cur_start);
    604 		//end point of the current line to test against node
    605 		VectorCopy(tstack_p->end, cur_end);
    606 		//the current node plane
    607 		plane = &aasworld.planes[aasnode->planenum];
    608 
    609 		switch(plane->type)
    610 		{/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!!
    611 			//check for axial planes
    612 			case PLANE_X:
    613 			{
    614 				front = cur_start[0] - plane->dist;
    615 				back = cur_end[0] - plane->dist;
    616 				break;
    617 			} //end case
    618 			case PLANE_Y:
    619 			{
    620 				front = cur_start[1] - plane->dist;
    621 				back = cur_end[1] - plane->dist;
    622 				break;
    623 			} //end case
    624 			case PLANE_Z:
    625 			{
    626 				front = cur_start[2] - plane->dist;
    627 				back = cur_end[2] - plane->dist;
    628 				break;
    629 			} //end case*/
    630 			default: //gee it's not an axial plane
    631 			{
    632 				front = DotProduct(cur_start, plane->normal) - plane->dist;
    633 				back = DotProduct(cur_end, plane->normal) - plane->dist;
    634 				break;
    635 			} //end default
    636 		} //end switch
    637 		// bk010221 - old location of FPE hack and divide by zero expression
    638 		//if the whole to be traced line is totally at the front of this node
    639 		//only go down the tree with the front child
    640 		if ((front >= -ON_EPSILON && back >= -ON_EPSILON))
    641 		{
    642 			//keep the current start and end point on the stack
    643 			//and go down the tree with the front child
    644 			tstack_p->nodenum = aasnode->children[0];
    645 			tstack_p++;
    646 			if (tstack_p >= &tracestack[127])
    647 			{
    648 				botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
    649 				return trace;
    650 			} //end if
    651 		} //end if
    652 		//if the whole to be traced line is totally at the back of this node
    653 		//only go down the tree with the back child
    654 		else if ((front < ON_EPSILON && back < ON_EPSILON))
    655 		{
    656 			//keep the current start and end point on the stack
    657 			//and go down the tree with the back child
    658 			tstack_p->nodenum = aasnode->children[1];
    659 			tstack_p++;
    660 			if (tstack_p >= &tracestack[127])
    661 			{
    662 				botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
    663 				return trace;
    664 			} //end if
    665 		} //end if
    666 		//go down the tree both at the front and back of the node
    667 		else
    668 		{
    669 			tmpplanenum = tstack_p->planenum;
    670 			// bk010221 - new location of divide by zero (see above)
    671 			if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE 
    672                 	//calculate the hitpoint with the node (split point of the line)
    673 			//put the crosspoint TRACEPLANE_EPSILON pixels on the near side
    674 			if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back);
    675 			else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221
    676 			//
    677 			if (frac < 0)
    678 				frac = 0.001f; //0
    679 			else if (frac > 1)
    680 				frac = 0.999f; //1
    681 			//frac = front / (front-back);
    682 			//
    683 			cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
    684 			cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
    685 			cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
    686 
    687 //			AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
    688 			//side the front part of the line is on
    689 			side = front < 0;
    690 			//first put the end part of the line on the stack (back side)
    691 			VectorCopy(cur_mid, tstack_p->start);
    692 			//not necesary to store because still on stack
    693 			//VectorCopy(cur_end, tstack_p->end);
    694 			tstack_p->planenum = aasnode->planenum;
    695 			tstack_p->nodenum = aasnode->children[!side];
    696 			tstack_p++;
    697 			if (tstack_p >= &tracestack[127])
    698 			{
    699 				botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
    700 				return trace;
    701 			} //end if
    702 			//now put the part near the start of the line on the stack so we will
    703 			//continue with thats part first. This way we'll find the first
    704 			//hit of the bbox
    705 			VectorCopy(cur_start, tstack_p->start);
    706 			VectorCopy(cur_mid, tstack_p->end);
    707 			tstack_p->planenum = tmpplanenum;
    708 			tstack_p->nodenum = aasnode->children[side];
    709 			tstack_p++;
    710 			if (tstack_p >= &tracestack[127])
    711 			{
    712 				botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
    713 				return trace;
    714 			} //end if
    715 		} //end else
    716 	} //end while
    717 //	return trace;
    718 } //end of the function AAS_TraceClientBBox
    719 //===========================================================================
    720 // recursive subdivision of the line by the BSP tree.
    721 //
    722 // Parameter:				-
    723 // Returns:					-
    724 // Changes Globals:		-
    725 //===========================================================================
    726 int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas)
    727 {
    728 	int side, nodenum, tmpplanenum;
    729 	int numareas;
    730 	float front, back, frac;
    731 	vec3_t cur_start, cur_end, cur_mid;
    732 	aas_tracestack_t tracestack[127];
    733 	aas_tracestack_t *tstack_p;
    734 	aas_node_t *aasnode;
    735 	aas_plane_t *plane;
    736 
    737 	numareas = 0;
    738 	areas[0] = 0;
    739 	if (!aasworld.loaded) return numareas;
    740 
    741 	tstack_p = tracestack;
    742 	//we start with the whole line on the stack
    743 	VectorCopy(start, tstack_p->start);
    744 	VectorCopy(end, tstack_p->end);
    745 	tstack_p->planenum = 0;
    746 	//start with node 1 because node zero is a dummy for a solid leaf
    747 	tstack_p->nodenum = 1;		//starting at the root of the tree
    748 	tstack_p++;
    749 
    750 	while (1)
    751 	{
    752 		//pop up the stack
    753 		tstack_p--;
    754 		//if the trace stack is empty (ended up with a piece of the
    755 		//line to be traced in an area)
    756 		if (tstack_p < tracestack)
    757 		{
    758 			return numareas;
    759 		} //end if
    760 		//number of the current node to test the line against
    761 		nodenum = tstack_p->nodenum;
    762 		//if it is an area
    763 		if (nodenum < 0)
    764 		{
    765 #ifdef AAS_SAMPLE_DEBUG
    766 			if (-nodenum > aasworld.numareasettings)
    767 			{
    768 				botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum);
    769 				return numareas;
    770 			} //end if
    771 #endif //AAS_SAMPLE_DEBUG
    772 			//botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
    773 			areas[numareas] = -nodenum;
    774 			if (points) VectorCopy(tstack_p->start, points[numareas]);
    775 			numareas++;
    776 			if (numareas >= maxareas) return numareas;
    777 			continue;
    778 		} //end if
    779 		//if it is a solid leaf
    780 		if (!nodenum)
    781 		{
    782 			continue;
    783 		} //end if
    784 #ifdef AAS_SAMPLE_DEBUG
    785 		if (nodenum > aasworld.numnodes)
    786 		{
    787 			botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n");
    788 			return numareas;
    789 		} //end if
    790 #endif //AAS_SAMPLE_DEBUG
    791 		//the node to test against
    792 		aasnode = &aasworld.nodes[nodenum];
    793 		//start point of current line to test against node
    794 		VectorCopy(tstack_p->start, cur_start);
    795 		//end point of the current line to test against node
    796 		VectorCopy(tstack_p->end, cur_end);
    797 		//the current node plane
    798 		plane = &aasworld.planes[aasnode->planenum];
    799 
    800 		switch(plane->type)
    801 		{/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!!
    802 			//check for axial planes
    803 			case PLANE_X:
    804 			{
    805 				front = cur_start[0] - plane->dist;
    806 				back = cur_end[0] - plane->dist;
    807 				break;
    808 			} //end case
    809 			case PLANE_Y:
    810 			{
    811 				front = cur_start[1] - plane->dist;
    812 				back = cur_end[1] - plane->dist;
    813 				break;
    814 			} //end case
    815 			case PLANE_Z:
    816 			{
    817 				front = cur_start[2] - plane->dist;
    818 				back = cur_end[2] - plane->dist;
    819 				break;
    820 			} //end case*/
    821 			default: //gee it's not an axial plane
    822 			{
    823 				front = DotProduct(cur_start, plane->normal) - plane->dist;
    824 				back = DotProduct(cur_end, plane->normal) - plane->dist;
    825 				break;
    826 			} //end default
    827 		} //end switch
    828 
    829 		//if the whole to be traced line is totally at the front of this node
    830 		//only go down the tree with the front child
    831 		if (front > 0 && back > 0)
    832 		{
    833 			//keep the current start and end point on the stack
    834 			//and go down the tree with the front child
    835 			tstack_p->nodenum = aasnode->children[0];
    836 			tstack_p++;
    837 			if (tstack_p >= &tracestack[127])
    838 			{
    839 				botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
    840 				return numareas;
    841 			} //end if
    842 		} //end if
    843 		//if the whole to be traced line is totally at the back of this node
    844 		//only go down the tree with the back child
    845 		else if (front <= 0 && back <= 0)
    846 		{
    847 			//keep the current start and end point on the stack
    848 			//and go down the tree with the back child
    849 			tstack_p->nodenum = aasnode->children[1];
    850 			tstack_p++;
    851 			if (tstack_p >= &tracestack[127])
    852 			{
    853 				botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
    854 				return numareas;
    855 			} //end if
    856 		} //end if
    857 		//go down the tree both at the front and back of the node
    858 		else
    859 		{
    860 			tmpplanenum = tstack_p->planenum;
    861 			//calculate the hitpoint with the node (split point of the line)
    862 			//put the crosspoint TRACEPLANE_EPSILON pixels on the near side
    863 			if (front < 0) frac = (front)/(front-back);
    864 			else frac = (front)/(front-back);
    865 			if (frac < 0) frac = 0;
    866 			else if (frac > 1) frac = 1;
    867 			//frac = front / (front-back);
    868 			//
    869 			cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
    870 			cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
    871 			cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
    872 
    873 //			AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
    874 			//side the front part of the line is on
    875 			side = front < 0;
    876 			//first put the end part of the line on the stack (back side)
    877 			VectorCopy(cur_mid, tstack_p->start);
    878 			//not necesary to store because still on stack
    879 			//VectorCopy(cur_end, tstack_p->end);
    880 			tstack_p->planenum = aasnode->planenum;
    881 			tstack_p->nodenum = aasnode->children[!side];
    882 			tstack_p++;
    883 			if (tstack_p >= &tracestack[127])
    884 			{
    885 				botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
    886 				return numareas;
    887 			} //end if
    888 			//now put the part near the start of the line on the stack so we will
    889 			//continue with thats part first. This way we'll find the first
    890 			//hit of the bbox
    891 			VectorCopy(cur_start, tstack_p->start);
    892 			VectorCopy(cur_mid, tstack_p->end);
    893 			tstack_p->planenum = tmpplanenum;
    894 			tstack_p->nodenum = aasnode->children[side];
    895 			tstack_p++;
    896 			if (tstack_p >= &tracestack[127])
    897 			{
    898 				botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
    899 				return numareas;
    900 			} //end if
    901 		} //end else
    902 	} //end while
    903 //	return numareas;
    904 } //end of the function AAS_TraceAreas
    905 //===========================================================================
    906 // a simple cross product
    907 //
    908 // Parameter:				-
    909 // Returns:					-
    910 // Changes Globals:		-
    911 //===========================================================================
    912 // void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res)
    913 #define AAS_OrthogonalToVectors(v1, v2, res) \
    914 	(res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\
    915 	(res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\
    916 	(res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]);
    917 //===========================================================================
    918 // tests if the given point is within the face boundaries
    919 //
    920 // Parameter:				face		: face to test if the point is in it
    921 //								pnormal	: normal of the plane to use for the face
    922 //								point		: point to test if inside face boundaries
    923 // Returns:					qtrue if the point is within the face boundaries
    924 // Changes Globals:		-
    925 //===========================================================================
    926 qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon)
    927 {
    928 	int i, firstvertex, edgenum;
    929 	vec3_t v0;
    930 	vec3_t edgevec, pointvec, sepnormal;
    931 	aas_edge_t *edge;
    932 #ifdef AAS_SAMPLE_DEBUG
    933 	int lastvertex = 0;
    934 #endif //AAS_SAMPLE_DEBUG
    935 
    936 	if (!aasworld.loaded) return qfalse;
    937 
    938 	for (i = 0; i < face->numedges; i++)
    939 	{
    940 		edgenum = aasworld.edgeindex[face->firstedge + i];
    941 		edge = &aasworld.edges[abs(edgenum)];
    942 		//get the first vertex of the edge
    943 		firstvertex = edgenum < 0;
    944 		VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0);
    945 		//edge vector
    946 		VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec);
    947 		//
    948 #ifdef AAS_SAMPLE_DEBUG
    949 		if (lastvertex && lastvertex != edge->v[firstvertex])
    950 		{
    951 			botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n");
    952 		} //end if
    953 		lastvertex = edge->v[!firstvertex];
    954 #endif //AAS_SAMPLE_DEBUG
    955 		//vector from first edge point to point possible in face
    956 		VectorSubtract(point, v0, pointvec);
    957 		//get a vector pointing inside the face orthogonal to both the
    958 		//edge vector and the normal vector of the plane the face is in
    959 		//this vector defines a plane through the origin (first vertex of
    960 		//edge) and through both the edge vector and the normal vector
    961 		//of the plane
    962 		AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal);
    963 		//check on wich side of the above plane the point is
    964 		//this is done by checking the sign of the dot product of the
    965 		//vector orthogonal vector from above and the vector from the
    966 		//origin (first vertex of edge) to the point 
    967 		//if the dotproduct is smaller than zero the point is outside the face
    968 		if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
    969 	} //end for
    970 	return qtrue;
    971 } //end of the function AAS_InsideFace
    972 //===========================================================================
    973 //
    974 // Parameter:				-
    975 // Returns:					-
    976 // Changes Globals:		-
    977 //===========================================================================
    978 qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon)
    979 {
    980 	int i, firstvertex, edgenum;
    981 	vec_t *v1, *v2;
    982 	vec3_t edgevec, pointvec, sepnormal;
    983 	aas_edge_t *edge;
    984 	aas_plane_t *plane;
    985 	aas_face_t *face;
    986 
    987 	if (!aasworld.loaded) return qfalse;
    988 
    989 	face = &aasworld.faces[facenum];
    990 	plane = &aasworld.planes[face->planenum];
    991 	//
    992 	for (i = 0; i < face->numedges; i++)
    993 	{
    994 		edgenum = aasworld.edgeindex[face->firstedge + i];
    995 		edge = &aasworld.edges[abs(edgenum)];
    996 		//get the first vertex of the edge
    997 		firstvertex = edgenum < 0;
    998 		v1 = aasworld.vertexes[edge->v[firstvertex]];
    999 		v2 = aasworld.vertexes[edge->v[!firstvertex]];
   1000 		//edge vector
   1001 		VectorSubtract(v2, v1, edgevec);
   1002 		//vector from first edge point to point possible in face
   1003 		VectorSubtract(point, v1, pointvec);
   1004 		//
   1005 		CrossProduct(edgevec, plane->normal, sepnormal);
   1006 		//
   1007 		if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
   1008 	} //end for
   1009 	return qtrue;
   1010 } //end of the function AAS_PointInsideFace
   1011 //===========================================================================
   1012 // returns the ground face the given point is above in the given area
   1013 //
   1014 // Parameter:				-
   1015 // Returns:					-
   1016 // Changes Globals:		-
   1017 //===========================================================================
   1018 aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point)
   1019 {
   1020 	int i, facenum;
   1021 	vec3_t up = {0, 0, 1};
   1022 	vec3_t normal;
   1023 	aas_area_t *area;
   1024 	aas_face_t *face;
   1025 
   1026 	if (!aasworld.loaded) return NULL;
   1027 
   1028 	area = &aasworld.areas[areanum];
   1029 	for (i = 0; i < area->numfaces; i++)
   1030 	{
   1031 		facenum = aasworld.faceindex[area->firstface + i];
   1032 		face = &aasworld.faces[abs(facenum)];
   1033 		//if this is a ground face
   1034 		if (face->faceflags & FACE_GROUND)
   1035 		{
   1036 			//get the up or down normal
   1037 			if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal);
   1038 			else VectorCopy(up, normal);
   1039 			//check if the point is in the face
   1040 			if (AAS_InsideFace(face, normal, point, 0.01f)) return face;
   1041 		} //end if
   1042 	} //end for
   1043 	return NULL;
   1044 } //end of the function AAS_AreaGroundFace
   1045 //===========================================================================
   1046 // returns the face the trace end position is situated in
   1047 //
   1048 // Parameter:				-
   1049 // Returns:					-
   1050 // Changes Globals:		-
   1051 //===========================================================================
   1052 void AAS_FacePlane(int facenum, vec3_t normal, float *dist)
   1053 {
   1054 	aas_plane_t *plane;
   1055 
   1056 	plane = &aasworld.planes[aasworld.faces[facenum].planenum];
   1057 	VectorCopy(plane->normal, normal);
   1058 	*dist = plane->dist;
   1059 } //end of the function AAS_FacePlane
   1060 //===========================================================================
   1061 // returns the face the trace end position is situated in
   1062 //
   1063 // Parameter:				-
   1064 // Returns:					-
   1065 // Changes Globals:		-
   1066 //===========================================================================
   1067 aas_face_t *AAS_TraceEndFace(aas_trace_t *trace)
   1068 {
   1069 	int i, facenum;
   1070 	aas_area_t *area;
   1071 	aas_face_t *face, *firstface = NULL;
   1072 
   1073 	if (!aasworld.loaded) return NULL;
   1074 
   1075 	//if started in solid no face was hit
   1076 	if (trace->startsolid) return NULL;
   1077 	//trace->lastarea is the last area the trace was in
   1078 	area = &aasworld.areas[trace->lastarea];
   1079 	//check which face the trace.endpos was in
   1080 	for (i = 0; i < area->numfaces; i++)
   1081 	{
   1082 		facenum = aasworld.faceindex[area->firstface + i];
   1083 		face = &aasworld.faces[abs(facenum)];
   1084 		//if the face is in the same plane as the trace end point
   1085 		if ((face->planenum & ~1) == (trace->planenum & ~1))
   1086 		{
   1087 			//firstface is used for optimization, if theres only one
   1088 			//face in the plane then it has to be the good one
   1089 			//if there are more faces in the same plane then always
   1090 			//check the one with the fewest edges first
   1091 /*			if (firstface)
   1092 			{
   1093 				if (firstface->numedges < face->numedges)
   1094 				{
   1095 					if (AAS_InsideFace(firstface,
   1096 						aasworld.planes[face->planenum].normal, trace->endpos))
   1097 					{
   1098 						return firstface;
   1099 					} //end if
   1100 					firstface = face;
   1101 				} //end if
   1102 				else
   1103 				{
   1104 					if (AAS_InsideFace(face,
   1105 						aasworld.planes[face->planenum].normal, trace->endpos))
   1106 					{
   1107 						return face;
   1108 					} //end if
   1109 				} //end else
   1110 			} //end if
   1111 			else
   1112 			{
   1113 				firstface = face;
   1114 			} //end else*/
   1115 			if (AAS_InsideFace(face,
   1116 						aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face;
   1117 		} //end if
   1118 	} //end for
   1119 	return firstface;
   1120 } //end of the function AAS_TraceEndFace
   1121 //===========================================================================
   1122 //
   1123 // Parameter:				-
   1124 // Returns:					-
   1125 // Changes Globals:		-
   1126 //===========================================================================
   1127 int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
   1128 {
   1129 	int i, sides;
   1130 	float dist1, dist2;
   1131 	vec3_t corners[2];
   1132 
   1133 	for (i = 0; i < 3; i++)
   1134 	{
   1135 		if (p->normal[i] < 0)
   1136 		{
   1137 			corners[0][i] = absmins[i];
   1138 			corners[1][i] = absmaxs[i];
   1139 		} //end if
   1140 		else
   1141 		{
   1142 			corners[1][i] = absmins[i];
   1143 			corners[0][i] = absmaxs[i];
   1144 		} //end else
   1145 	} //end for
   1146 	dist1 = DotProduct(p->normal, corners[0]) - p->dist;
   1147 	dist2 = DotProduct(p->normal, corners[1]) - p->dist;
   1148 	sides = 0;
   1149 	if (dist1 >= 0) sides = 1;
   1150 	if (dist2 < 0) sides |= 2;
   1151 
   1152 	return sides;
   1153 } //end of the function AAS_BoxOnPlaneSide2
   1154 //===========================================================================
   1155 //
   1156 // Parameter:				-
   1157 // Returns:					-
   1158 // Changes Globals:		-
   1159 //===========================================================================
   1160 //int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
   1161 #define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\
   1162 	( (p)->type < 3) ?\
   1163 	(\
   1164 		( (p)->dist <= (absmins)[(p)->type]) ?\
   1165 		(\
   1166 			1\
   1167 		)\
   1168 		:\
   1169 		(\
   1170 			( (p)->dist >= (absmaxs)[(p)->type]) ?\
   1171 			(\
   1172 				2\
   1173 			)\
   1174 			:\
   1175 			(\
   1176 				3\
   1177 			)\
   1178 		)\
   1179 	)\
   1180 	:\
   1181 	(\
   1182 		AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\
   1183 	)\
   1184 ) //end of the function AAS_BoxOnPlaneSide
   1185 //===========================================================================
   1186 // remove the links to this entity from all areas
   1187 //
   1188 // Parameter:				-
   1189 // Returns:					-
   1190 // Changes Globals:		-
   1191 //===========================================================================
   1192 void AAS_UnlinkFromAreas(aas_link_t *areas)
   1193 {
   1194 	aas_link_t *link, *nextlink;
   1195 
   1196 	for (link = areas; link; link = nextlink)
   1197 	{
   1198 		//next area the entity is linked in
   1199 		nextlink = link->next_area;
   1200 		//remove the entity from the linked list of this area
   1201 		if (link->prev_ent) link->prev_ent->next_ent = link->next_ent;
   1202 		else aasworld.arealinkedentities[link->areanum] = link->next_ent;
   1203 		if (link->next_ent) link->next_ent->prev_ent = link->prev_ent;
   1204 		//deallocate the link structure
   1205 		AAS_DeAllocAASLink(link);
   1206 	} //end for
   1207 } //end of the function AAS_UnlinkFromAreas
   1208 //===========================================================================
   1209 // link the entity to the areas the bounding box is totally or partly
   1210 // situated in. This is done with recursion down the tree using the
   1211 // bounding box to test for plane sides
   1212 //
   1213 // Parameter:				-
   1214 // Returns:					-
   1215 // Changes Globals:		-
   1216 //===========================================================================
   1217 
   1218 typedef struct
   1219 {
   1220 	int nodenum;		//node found after splitting
   1221 } aas_linkstack_t;
   1222 
   1223 aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum)
   1224 {
   1225 	int side, nodenum;
   1226 	aas_linkstack_t linkstack[128];
   1227 	aas_linkstack_t *lstack_p;
   1228 	aas_node_t *aasnode;
   1229 	aas_plane_t *plane;
   1230 	aas_link_t *link, *areas;
   1231 
   1232 	if (!aasworld.loaded)
   1233 	{
   1234 		botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n");
   1235 		return NULL;
   1236 	} //end if
   1237 
   1238 	areas = NULL;
   1239 	//
   1240 	lstack_p = linkstack;
   1241 	//we start with the whole line on the stack
   1242 	//start with node 1 because node zero is a dummy used for solid leafs
   1243 	lstack_p->nodenum = 1;		//starting at the root of the tree
   1244 	lstack_p++;
   1245 	
   1246 	while (1)
   1247 	{
   1248 		//pop up the stack
   1249 		lstack_p--;
   1250 		//if the trace stack is empty (ended up with a piece of the
   1251 		//line to be traced in an area)
   1252 		if (lstack_p < linkstack) break;
   1253 		//number of the current node to test the line against
   1254 		nodenum = lstack_p->nodenum;
   1255 		//if it is an area
   1256 		if (nodenum < 0)
   1257 		{
   1258 			//NOTE: the entity might have already been linked into this area
   1259 			// because several node children can point to the same area
   1260 			for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent)
   1261 			{
   1262 				if (link->entnum == entnum) break;
   1263 			} //end for
   1264 			if (link) continue;
   1265 			//
   1266 			link = AAS_AllocAASLink();
   1267 			if (!link) return areas;
   1268 			link->entnum = entnum;
   1269 			link->areanum = -nodenum;
   1270 			//put the link into the double linked area list of the entity
   1271 			link->prev_area = NULL;
   1272 			link->next_area = areas;
   1273 			if (areas) areas->prev_area = link;
   1274 			areas = link;
   1275 			//put the link into the double linked entity list of the area
   1276 			link->prev_ent = NULL;
   1277 			link->next_ent = aasworld.arealinkedentities[-nodenum];
   1278 			if (aasworld.arealinkedentities[-nodenum])
   1279 					aasworld.arealinkedentities[-nodenum]->prev_ent = link;
   1280 			aasworld.arealinkedentities[-nodenum] = link;
   1281 			//
   1282 			continue;
   1283 		} //end if
   1284 		//if solid leaf
   1285 		if (!nodenum) continue;
   1286 		//the node to test against
   1287 		aasnode = &aasworld.nodes[nodenum];
   1288 		//the current node plane
   1289 		plane = &aasworld.planes[aasnode->planenum];
   1290 		//get the side(s) the box is situated relative to the plane
   1291 		side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane);
   1292 		//if on the front side of the node
   1293 		if (side & 1)
   1294 		{
   1295 			lstack_p->nodenum = aasnode->children[0];
   1296 			lstack_p++;
   1297 		} //end if
   1298 		if (lstack_p >= &linkstack[127])
   1299 		{
   1300 			botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n");
   1301 			break;
   1302 		} //end if
   1303 		//if on the back side of the node
   1304 		if (side & 2)
   1305 		{
   1306 			lstack_p->nodenum = aasnode->children[1];
   1307 			lstack_p++;
   1308 		} //end if
   1309 		if (lstack_p >= &linkstack[127])
   1310 		{
   1311 			botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n");
   1312 			break;
   1313 		} //end if
   1314 	} //end while
   1315 	return areas;
   1316 } //end of the function AAS_AASLinkEntity
   1317 //===========================================================================
   1318 //
   1319 // Parameter:				-
   1320 // Returns:					-
   1321 // Changes Globals:		-
   1322 //===========================================================================
   1323 aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype)
   1324 {
   1325 	vec3_t mins, maxs;
   1326 	vec3_t newabsmins, newabsmaxs;
   1327 
   1328 	AAS_PresenceTypeBoundingBox(presencetype, mins, maxs);
   1329 	VectorSubtract(absmins, maxs, newabsmins);
   1330 	VectorSubtract(absmaxs, mins, newabsmaxs);
   1331 	//relink the entity
   1332 	return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum);
   1333 } //end of the function AAS_LinkEntityClientBBox
   1334 //===========================================================================
   1335 //
   1336 // Parameter:				-
   1337 // Returns:					-
   1338 // Changes Globals:		-
   1339 //===========================================================================
   1340 int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas)
   1341 {
   1342 	aas_link_t *linkedareas, *link;
   1343 	int num;
   1344 
   1345 	linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1);
   1346 	num = 0;
   1347 	for (link = linkedareas; link; link = link->next_area)
   1348 	{
   1349 		areas[num] = link->areanum;
   1350 		num++;
   1351 		if (num >= maxareas)
   1352 			break;
   1353 	} //end for
   1354 	AAS_UnlinkFromAreas(linkedareas);
   1355 	return num;
   1356 } //end of the function AAS_BBoxAreas
   1357 //===========================================================================
   1358 //
   1359 // Parameter:				-
   1360 // Returns:					-
   1361 // Changes Globals:		-
   1362 //===========================================================================
   1363 int AAS_AreaInfo( int areanum, aas_areainfo_t *info )
   1364 {
   1365 	aas_areasettings_t *settings;
   1366 	if (!info)
   1367 		return 0;
   1368 	if (areanum <= 0 || areanum >= aasworld.numareas)
   1369 	{
   1370 		botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum);
   1371 		return 0;
   1372 	} //end if
   1373 	settings = &aasworld.areasettings[areanum];
   1374 	info->cluster = settings->cluster;
   1375 	info->contents = settings->contents;
   1376 	info->flags = settings->areaflags;
   1377 	info->presencetype = settings->presencetype;
   1378 	VectorCopy(aasworld.areas[areanum].mins, info->mins);
   1379 	VectorCopy(aasworld.areas[areanum].maxs, info->maxs);
   1380 	VectorCopy(aasworld.areas[areanum].center, info->center);
   1381 	return sizeof(aas_areainfo_t);
   1382 } //end of the function AAS_AreaInfo
   1383 //===========================================================================
   1384 //
   1385 // Parameter:				-
   1386 // Returns:					-
   1387 // Changes Globals:		-
   1388 //===========================================================================
   1389 aas_plane_t *AAS_PlaneFromNum(int planenum)
   1390 {
   1391 	if (!aasworld.loaded) return 0;
   1392 
   1393 	return &aasworld.planes[planenum];
   1394 } //end of the function AAS_PlaneFromNum