Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

be_aas_reach.c (152877B)


      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_reach.c
     25  *
     26  * desc:		reachability calculations
     27  *
     28  * $Archive: /MissionPack/code/botlib/be_aas_reach.c $
     29  *
     30  *****************************************************************************/
     31 
     32 #include "../game/q_shared.h"
     33 #include "l_log.h"
     34 #include "l_memory.h"
     35 #include "l_script.h"
     36 #include "l_libvar.h"
     37 #include "l_precomp.h"
     38 #include "l_struct.h"
     39 #include "aasfile.h"
     40 #include "../game/botlib.h"
     41 #include "../game/be_aas.h"
     42 #include "be_aas_funcs.h"
     43 #include "be_aas_def.h"
     44 
     45 extern int Sys_MilliSeconds(void);
     46 
     47 
     48 extern botlib_import_t botimport;
     49 
     50 //#define REACH_DEBUG
     51 
     52 //NOTE: all travel times are in hundreth of a second
     53 //maximum number of reachability links
     54 #define AAS_MAX_REACHABILITYSIZE			65536
     55 //number of areas reachability is calculated for each frame
     56 #define REACHABILITYAREASPERCYCLE			15
     57 //number of units reachability points are placed inside the areas
     58 #define INSIDEUNITS							2
     59 #define INSIDEUNITS_WALKEND					5
     60 #define INSIDEUNITS_WALKSTART				0.1
     61 #define INSIDEUNITS_WATERJUMP				15
     62 //area flag used for weapon jumping
     63 #define AREA_WEAPONJUMP						8192	//valid area to weapon jump to
     64 //number of reachabilities of each type
     65 int reach_swim;			//swim
     66 int reach_equalfloor;	//walk on floors with equal height
     67 int reach_step;			//step up
     68 int reach_walk;			//walk of step
     69 int reach_barrier;		//jump up to a barrier
     70 int reach_waterjump;	//jump out of water
     71 int reach_walkoffledge;	//walk of a ledge
     72 int reach_jump;			//jump
     73 int reach_ladder;		//climb or descent a ladder
     74 int reach_teleport;		//teleport
     75 int reach_elevator;		//use an elevator
     76 int reach_funcbob;		//use a func bob
     77 int reach_grapple;		//grapple hook
     78 int reach_doublejump;	//double jump
     79 int reach_rampjump;		//ramp jump
     80 int reach_strafejump;	//strafe jump (just normal jump but further)
     81 int reach_rocketjump;	//rocket jump
     82 int reach_bfgjump;		//bfg jump
     83 int reach_jumppad;		//jump pads
     84 //if true grapple reachabilities are skipped
     85 int calcgrapplereach;
     86 //linked reachability
     87 typedef struct aas_lreachability_s
     88 {
     89 	int areanum;					//number of the reachable area
     90 	int facenum;					//number of the face towards the other area
     91 	int edgenum;					//number of the edge towards the other area
     92 	vec3_t start;					//start point of inter area movement
     93 	vec3_t end;						//end point of inter area movement
     94 	int traveltype;					//type of travel required to get to the area
     95 	unsigned short int traveltime;	//travel time of the inter area movement
     96 	//
     97 	struct aas_lreachability_s *next;
     98 } aas_lreachability_t;
     99 //temporary reachabilities
    100 aas_lreachability_t *reachabilityheap;	//heap with reachabilities
    101 aas_lreachability_t *nextreachability;	//next free reachability from the heap
    102 aas_lreachability_t **areareachability;	//reachability links for every area
    103 int numlreachabilities;
    104 
    105 //===========================================================================
    106 // returns the surface area of the given face
    107 //
    108 // Parameter:				-
    109 // Returns:					-
    110 // Changes Globals:		-
    111 //===========================================================================
    112 float AAS_FaceArea(aas_face_t *face)
    113 {
    114 	int i, edgenum, side;
    115 	float total;
    116 	vec_t *v;
    117 	vec3_t d1, d2, cross;
    118 	aas_edge_t *edge;
    119 
    120 	edgenum = aasworld.edgeindex[face->firstedge];
    121 	side = edgenum < 0;
    122 	edge = &aasworld.edges[abs(edgenum)];
    123 	v = aasworld.vertexes[edge->v[side]];
    124 
    125 	total = 0;
    126 	for (i = 1; i < face->numedges - 1; i++)
    127 	{
    128 		edgenum = aasworld.edgeindex[face->firstedge + i];
    129 		side = edgenum < 0;
    130 		edge = &aasworld.edges[abs(edgenum)];
    131 		VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1);
    132 		VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2);
    133 		CrossProduct(d1, d2, cross);
    134 		total += 0.5 * VectorLength(cross);
    135 	} //end for
    136 	return total;
    137 } //end of the function AAS_FaceArea
    138 //===========================================================================
    139 // returns the volume of an area
    140 //
    141 // Parameter:				-
    142 // Returns:					-
    143 // Changes Globals:		-
    144 //===========================================================================
    145 float AAS_AreaVolume(int areanum)
    146 {
    147 	int i, edgenum, facenum, side;
    148 	vec_t d, a, volume;
    149 	vec3_t corner;
    150 	aas_plane_t *plane;
    151 	aas_edge_t *edge;
    152 	aas_face_t *face;
    153 	aas_area_t *area;
    154 
    155 	area = &aasworld.areas[areanum];
    156 	facenum = aasworld.faceindex[area->firstface];
    157 	face = &aasworld.faces[abs(facenum)];
    158 	edgenum = aasworld.edgeindex[face->firstedge];
    159 	edge = &aasworld.edges[abs(edgenum)];
    160 	//
    161 	VectorCopy(aasworld.vertexes[edge->v[0]], corner);
    162 
    163 	//make tetrahedrons to all other faces
    164 	volume = 0;
    165 	for (i = 0; i < area->numfaces; i++)
    166 	{
    167 		facenum = abs(aasworld.faceindex[area->firstface + i]);
    168 		face = &aasworld.faces[facenum];
    169 		side = face->backarea != areanum;
    170 		plane = &aasworld.planes[face->planenum ^ side];
    171 		d = -(DotProduct (corner, plane->normal) - plane->dist);
    172 		a = AAS_FaceArea(face);
    173 		volume += d * a;
    174 	} //end for
    175 
    176 	volume /= 3;
    177 	return volume;
    178 } //end of the function AAS_AreaVolume
    179 //===========================================================================
    180 //
    181 // Parameter:				-
    182 // Returns:					-
    183 // Changes Globals:		-
    184 //===========================================================================
    185 int AAS_BestReachableLinkArea(aas_link_t *areas)
    186 {
    187 	aas_link_t *link;
    188 
    189 	for (link = areas; link; link = link->next_area)
    190 	{
    191 		if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum))
    192 		{
    193 			return link->areanum;
    194 		} //end if
    195 	} //end for
    196 	//
    197 	for (link = areas; link; link = link->next_area)
    198 	{
    199 		if (link->areanum) return link->areanum;
    200 		//FIXME: this is a bad idea when the reachability is not yet
    201 		// calculated when the level items are loaded
    202 		if (AAS_AreaReachability(link->areanum))
    203 			return link->areanum;
    204 	} //end for
    205 	return 0;
    206 } //end of the function AAS_BestReachableLinkArea
    207 //===========================================================================
    208 //
    209 // Parameter:			-
    210 // Returns:				-
    211 // Changes Globals:		-
    212 //===========================================================================
    213 int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity)
    214 {
    215 	int modelnum, ent2;
    216 	float speed, height, gravity, time, dist, forward;
    217 	vec3_t origin, angles, teststart, ent2origin;
    218 	aas_trace_t trace;
    219 	char model[MAX_EPAIRKEY];
    220 	char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
    221 
    222 	//
    223 	AAS_FloatForBSPEpairKey(ent, "speed", &speed);
    224 	if (!speed) speed = 1000;
    225 	VectorClear(angles);
    226 	//get the mins, maxs and origin of the model
    227 	AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
    228 	if (model[0]) modelnum = atoi(model+1);
    229 	else modelnum = 0;
    230 	AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
    231 	VectorAdd(origin, absmins, absmins);
    232 	VectorAdd(origin, absmaxs, absmaxs);
    233 	VectorAdd(absmins, absmaxs, origin);
    234 	VectorScale (origin, 0.5, origin);
    235 
    236 	//get the start areas
    237 	VectorCopy(origin, teststart);
    238 	teststart[2] += 64;
    239 	trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
    240 	if (trace.startsolid)
    241 	{
    242 		botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
    243 		VectorCopy(origin, areastart);
    244 	} //end if
    245 	else
    246 	{
    247 		VectorCopy(trace.endpos, areastart);
    248 	} //end else
    249 	areastart[2] += 0.125;
    250 	//
    251 	//AAS_DrawPermanentCross(origin, 4, 4);
    252 	//get the target entity
    253 	AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
    254 	for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
    255 	{
    256 		if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
    257 		if (!strcmp(targetname, target)) break;
    258 	} //end for
    259 	if (!ent2)
    260 	{
    261 		botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
    262 		return qfalse;
    263 	} //end if
    264 	AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
    265 	//
    266 	height = ent2origin[2] - origin[2];
    267 	gravity = aassettings.phys_gravity;
    268 	time = sqrt( height / ( 0.5 * gravity ) );
    269 	if (!time)
    270 	{
    271 		botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
    272 		return qfalse;
    273 	} //end if
    274 	// set s.origin2 to the push velocity
    275 	VectorSubtract ( ent2origin, origin, velocity);
    276 	dist = VectorNormalize( velocity);
    277 	forward = dist / time;
    278 	//FIXME: why multiply by 1.1
    279 	forward *= 1.1f;
    280 	VectorScale(velocity, forward, velocity);
    281 	velocity[2] = time * gravity;
    282 	return qtrue;
    283 } //end of the function AAS_GetJumpPadInfo
    284 //===========================================================================
    285 //
    286 // Parameter:			-
    287 // Returns:				-
    288 // Changes Globals:		-
    289 //===========================================================================
    290 int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs)
    291 {
    292 	int area2num, ent, bot_visualizejumppads, bestareanum;
    293 	float volume, bestareavolume;
    294 	vec3_t areastart, cmdmove, bboxmins, bboxmaxs;
    295 	vec3_t absmins, absmaxs, velocity;
    296 	aas_clientmove_t move;
    297 	aas_link_t *areas, *link;
    298 	char classname[MAX_EPAIRKEY];
    299 
    300 #ifdef BSPC
    301 	bot_visualizejumppads = 0;
    302 #else
    303 	bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
    304 #endif
    305 	VectorAdd(origin, mins, bboxmins);
    306 	VectorAdd(origin, maxs, bboxmaxs);
    307 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
    308 	{
    309 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
    310 		if (strcmp(classname, "trigger_push")) continue;
    311 		//
    312 		if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
    313 		//get the areas the jump pad brush is in
    314 		areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
    315 		for (link = areas; link; link = link->next_area)
    316 		{
    317 			if (AAS_AreaJumpPad(link->areanum)) break;
    318 		} //end for
    319 		if (!link)
    320 		{
    321 			botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
    322 			AAS_UnlinkFromAreas(areas);
    323 			continue;
    324 		} //end if
    325 		//
    326 		//botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
    327 		//
    328 		VectorSet(cmdmove, 0, 0, 0);
    329 		Com_Memset(&move, 0, sizeof(aas_clientmove_t));
    330 		area2num = 0;
    331 		AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
    332 								velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads);
    333 		if (move.frames < 30)
    334 		{
    335 			bestareanum = 0;
    336 			bestareavolume = 0;
    337 			for (link = areas; link; link = link->next_area)
    338 			{
    339 				if (!AAS_AreaJumpPad(link->areanum)) continue;
    340 				volume = AAS_AreaVolume(link->areanum);
    341 				if (volume >= bestareavolume)
    342 				{
    343 					bestareanum = link->areanum;
    344 					bestareavolume = volume;
    345 				} //end if
    346 			} //end if
    347 			AAS_UnlinkFromAreas(areas);
    348 			return bestareanum;
    349 		} //end if
    350 		AAS_UnlinkFromAreas(areas);
    351 	} //end for
    352 	return 0;
    353 } //end of the function AAS_BestReachableFromJumpPadArea
    354 //===========================================================================
    355 //
    356 // Parameter:				-
    357 // Returns:					-
    358 // Changes Globals:		-
    359 //===========================================================================
    360 int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin)
    361 {
    362 	int areanum, i, j, k, l;
    363 	aas_link_t *areas;
    364 	vec3_t absmins, absmaxs;
    365 	//vec3_t bbmins, bbmaxs;
    366 	vec3_t start, end;
    367 	aas_trace_t trace;
    368 
    369 	if (!aasworld.loaded)
    370 	{
    371 		botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n");
    372 		return 0;
    373 	} //end if
    374 	//find a point in an area
    375 	VectorCopy(origin, start);
    376 	areanum = AAS_PointAreaNum(start);
    377 	//while no area found fudge around a little
    378 	for (i = 0; i < 5 && !areanum; i++)
    379 	{
    380 		for (j = 0; j < 5 && !areanum; j++)
    381 		{
    382 			for (k = -1; k <= 1 && !areanum; k++)
    383 			{
    384 				for (l = -1; l <= 1 && !areanum; l++)
    385 				{
    386 					VectorCopy(origin, start);
    387 					start[0] += (float) j * 4 * k;
    388 					start[1] += (float) j * 4 * l;
    389 					start[2] += (float) i * 4;
    390 					areanum = AAS_PointAreaNum(start);
    391 				} //end for
    392 			} //end for
    393 		} //end for
    394 	} //end for
    395 	//if an area was found
    396 	if (areanum)
    397 	{
    398 		//drop client bbox down and try again
    399 		VectorCopy(start, end);
    400 		start[2] += 0.25;
    401 		end[2] -= 50;
    402 		trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
    403 		if (!trace.startsolid)
    404 		{
    405 			areanum = AAS_PointAreaNum(trace.endpos);
    406 			VectorCopy(trace.endpos, goalorigin);
    407 			//FIXME: cannot enable next line right now because the reachability
    408 			// does not have to be calculated when the level items are loaded
    409 			//if the origin is in an area with reachability
    410 			//if (AAS_AreaReachability(areanum)) return areanum;
    411 			if (areanum) return areanum;
    412 		} //end if
    413 		else
    414 		{
    415 			//it can very well happen that the AAS_PointAreaNum function tells that
    416 			//a point is in an area and that starting a AAS_TraceClientBBox from that
    417 			//point will return trace.startsolid qtrue
    418 #if 0
    419 			if (AAS_PointAreaNum(start))
    420 			{
    421 				Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum);
    422 				AAS_DrawPermanentCross(start, 4, LINECOLOR_RED);
    423 			} //end if
    424 			botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n");
    425 #endif
    426 			VectorCopy(start, goalorigin);
    427 			return areanum;
    428 		} //end else
    429 	} //end if
    430 	//
    431 	//AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs);
    432 	//NOTE: the goal origin does not have to be in the goal area
    433 	// because the bot will have to move towards the item origin anyway
    434 	VectorCopy(origin, goalorigin);
    435 	//
    436 	VectorAdd(origin, mins, absmins);
    437 	VectorAdd(origin, maxs, absmaxs);
    438 	//add bounding box size
    439 	//VectorSubtract(absmins, bbmaxs, absmins);
    440 	//VectorSubtract(absmaxs, bbmins, absmaxs);
    441 	//link an invalid (-1) entity
    442 	areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
    443 	//get the reachable link arae
    444 	areanum = AAS_BestReachableLinkArea(areas);
    445 	//unlink the invalid entity
    446 	AAS_UnlinkFromAreas(areas);
    447 	//
    448 	return areanum;
    449 } //end of the function AAS_BestReachableArea
    450 //===========================================================================
    451 //
    452 // Parameter:				-
    453 // Returns:					-
    454 // Changes Globals:		-
    455 //===========================================================================
    456 void AAS_SetupReachabilityHeap(void)
    457 {
    458 	int i;
    459 
    460 	reachabilityheap = (aas_lreachability_t *) GetClearedMemory(
    461 						AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t));
    462 	for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++)
    463 	{
    464 		reachabilityheap[i].next = &reachabilityheap[i+1];
    465 	} //end for
    466 	reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL;
    467 	nextreachability = reachabilityheap;
    468 	numlreachabilities = 0;
    469 } //end of the function AAS_InitReachabilityHeap
    470 //===========================================================================
    471 //
    472 // Parameter:				-
    473 // Returns:					-
    474 // Changes Globals:		-
    475 //===========================================================================
    476 void AAS_ShutDownReachabilityHeap(void)
    477 {
    478 	FreeMemory(reachabilityheap);
    479 	numlreachabilities = 0;
    480 } //end of the function AAS_ShutDownReachabilityHeap
    481 //===========================================================================
    482 // returns a reachability link
    483 //
    484 // Parameter:				-
    485 // Returns:					-
    486 // Changes Globals:		-
    487 //===========================================================================
    488 aas_lreachability_t *AAS_AllocReachability(void)
    489 {
    490 	aas_lreachability_t *r;
    491 
    492 	if (!nextreachability) return NULL;
    493 	//make sure the error message only shows up once
    494 	if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE");
    495 	//
    496 	r = nextreachability;
    497 	nextreachability = nextreachability->next;
    498 	numlreachabilities++;
    499 	return r;
    500 } //end of the function AAS_AllocReachability
    501 //===========================================================================
    502 // frees a reachability link
    503 //
    504 // Parameter:				-
    505 // Returns:					-
    506 // Changes Globals:		-
    507 //===========================================================================
    508 void AAS_FreeReachability(aas_lreachability_t *lreach)
    509 {
    510 	Com_Memset(lreach, 0, sizeof(aas_lreachability_t));
    511 
    512 	lreach->next = nextreachability;
    513 	nextreachability = lreach;
    514 	numlreachabilities--;
    515 } //end of the function AAS_FreeReachability
    516 //===========================================================================
    517 // returns qtrue if the area has reachability links
    518 //
    519 // Parameter:				-
    520 // Returns:					-
    521 // Changes Globals:		-
    522 //===========================================================================
    523 int AAS_AreaReachability(int areanum)
    524 {
    525 	if (areanum < 0 || areanum >= aasworld.numareas)
    526 	{
    527 		AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum);
    528 		return 0;
    529 	} //end if
    530 	return aasworld.areasettings[areanum].numreachableareas;
    531 } //end of the function AAS_AreaReachability
    532 //===========================================================================
    533 // returns the surface area of all ground faces together of the area
    534 //
    535 // Parameter:				-
    536 // Returns:					-
    537 // Changes Globals:		-
    538 //===========================================================================
    539 float AAS_AreaGroundFaceArea(int areanum)
    540 {
    541 	int i;
    542 	float total;
    543 	aas_area_t *area;
    544 	aas_face_t *face;
    545 
    546 	total = 0;
    547 	area = &aasworld.areas[areanum];
    548 	for (i = 0; i < area->numfaces; i++)
    549 	{
    550 		face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
    551 		if (!(face->faceflags & FACE_GROUND)) continue;
    552 		//
    553 		total += AAS_FaceArea(face);
    554 	} //end for
    555 	return total;
    556 } //end of the function AAS_AreaGroundFaceArea
    557 //===========================================================================
    558 // returns the center of a face
    559 //
    560 // Parameter:				-
    561 // Returns:					-
    562 // Changes Globals:		-
    563 //===========================================================================
    564 void AAS_FaceCenter(int facenum, vec3_t center)
    565 {
    566 	int i;
    567 	float scale;
    568 	aas_face_t *face;
    569 	aas_edge_t *edge;
    570 
    571 	face = &aasworld.faces[facenum];
    572 
    573 	VectorClear(center);
    574 	for (i = 0; i < face->numedges; i++)
    575 	{
    576 		edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])];
    577 		VectorAdd(center, aasworld.vertexes[edge->v[0]], center);
    578 		VectorAdd(center, aasworld.vertexes[edge->v[1]], center);
    579 	} //end for
    580 	scale = 0.5 / face->numedges;
    581 	VectorScale(center, scale, center);
    582 } //end of the function AAS_FaceCenter
    583 //===========================================================================
    584 // returns the maximum distance a player can fall before being damaged
    585 // damage = deltavelocity*deltavelocity  * 0.0001
    586 //
    587 // Parameter:				-
    588 // Returns:					-
    589 // Changes Globals:		-
    590 //===========================================================================
    591 int AAS_FallDamageDistance(void)
    592 {
    593 	float maxzvelocity, gravity, t;
    594 
    595 	maxzvelocity = sqrt(30 * 10000);
    596 	gravity = aassettings.phys_gravity;
    597 	t = maxzvelocity / gravity;
    598 	return 0.5 * gravity * t * t;
    599 } //end of the function AAS_FallDamageDistance
    600 //===========================================================================
    601 // distance = 0.5 * gravity * t * t
    602 // vel = t * gravity
    603 // damage = vel * vel * 0.0001
    604 //
    605 // Parameter:			-
    606 // Returns:				-
    607 // Changes Globals:		-
    608 //===========================================================================
    609 float AAS_FallDelta(float distance)
    610 {
    611 	float t, delta, gravity;
    612 
    613 	gravity = aassettings.phys_gravity;
    614 	t = sqrt(fabs(distance) * 2 / gravity);
    615 	delta = t * gravity;
    616 	return delta * delta * 0.0001;
    617 } //end of the function AAS_FallDelta
    618 //===========================================================================
    619 //
    620 // Parameter:			-
    621 // Returns:				-
    622 // Changes Globals:		-
    623 //===========================================================================
    624 float AAS_MaxJumpHeight(float phys_jumpvel)
    625 {
    626 	float phys_gravity;
    627 
    628 	phys_gravity = aassettings.phys_gravity;
    629 	//maximum height a player can jump with the given initial z velocity
    630 	return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity);
    631 } //end of the function MaxJumpHeight
    632 //===========================================================================
    633 // returns true if a player can only crouch in the area
    634 //
    635 // Parameter:				-
    636 // Returns:					-
    637 // Changes Globals:		-
    638 //===========================================================================
    639 float AAS_MaxJumpDistance(float phys_jumpvel)
    640 {
    641 	float phys_gravity, phys_maxvelocity, t;
    642 
    643 	phys_gravity = aassettings.phys_gravity;
    644 	phys_maxvelocity = aassettings.phys_maxvelocity;
    645 	//time a player takes to fall the height
    646 	t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity));
    647    //maximum distance
    648 	return phys_maxvelocity * (t + phys_jumpvel / phys_gravity);
    649 } //end of the function AAS_MaxJumpDistance
    650 //===========================================================================
    651 // returns true if a player can only crouch in the area
    652 //
    653 // Parameter:				-
    654 // Returns:					-
    655 // Changes Globals:		-
    656 //===========================================================================
    657 int AAS_AreaCrouch(int areanum)
    658 {
    659 	if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue;
    660 	else return qfalse;
    661 } //end of the function AAS_AreaCrouch
    662 //===========================================================================
    663 // returns qtrue if it is possible to swim in the area
    664 //
    665 // Parameter:				-
    666 // Returns:					-
    667 // Changes Globals:		-
    668 //===========================================================================
    669 int AAS_AreaSwim(int areanum)
    670 {
    671 	if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
    672 	else return qfalse;
    673 } //end of the function AAS_AreaSwim
    674 //===========================================================================
    675 // returns qtrue if the area contains a liquid
    676 //
    677 // Parameter:				-
    678 // Returns:					-
    679 // Changes Globals:		-
    680 //===========================================================================
    681 int AAS_AreaLiquid(int areanum)
    682 {
    683 	if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
    684 	else return qfalse;
    685 } //end of the function AAS_AreaLiquid
    686 //===========================================================================
    687 //
    688 // Parameter:			-
    689 // Returns:				-
    690 // Changes Globals:		-
    691 //===========================================================================
    692 int AAS_AreaLava(int areanum)
    693 {
    694 	return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA);
    695 } //end of the function AAS_AreaLava
    696 //===========================================================================
    697 //
    698 // Parameter:			-
    699 // Returns:				-
    700 // Changes Globals:		-
    701 //===========================================================================
    702 int AAS_AreaSlime(int areanum)
    703 {
    704 	return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME);
    705 } //end of the function AAS_AreaSlime
    706 //===========================================================================
    707 // returns qtrue if the area contains ground faces
    708 //
    709 // Parameter:				-
    710 // Returns:					-
    711 // Changes Globals:		-
    712 //===========================================================================
    713 int AAS_AreaGrounded(int areanum)
    714 {
    715 	return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED);
    716 } //end of the function AAS_AreaGround
    717 //===========================================================================
    718 // returns true if the area contains ladder faces
    719 //
    720 // Parameter:				-
    721 // Returns:					-
    722 // Changes Globals:		-
    723 //===========================================================================
    724 int AAS_AreaLadder(int areanum)
    725 {
    726 	return (aasworld.areasettings[areanum].areaflags & AREA_LADDER);
    727 } //end of the function AAS_AreaLadder
    728 //===========================================================================
    729 //
    730 // Parameter:				-
    731 // Returns:					-
    732 // Changes Globals:		-
    733 //===========================================================================
    734 int AAS_AreaJumpPad(int areanum)
    735 {
    736 	return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD);
    737 } //end of the function AAS_AreaJumpPad
    738 //===========================================================================
    739 //
    740 // Parameter:				-
    741 // Returns:					-
    742 // Changes Globals:		-
    743 //===========================================================================
    744 int AAS_AreaTeleporter(int areanum)
    745 {
    746 	return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER);
    747 } //end of the function AAS_AreaTeleporter
    748 //===========================================================================
    749 //
    750 // Parameter:				-
    751 // Returns:					-
    752 // Changes Globals:		-
    753 //===========================================================================
    754 int AAS_AreaClusterPortal(int areanum)
    755 {
    756 	return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL);
    757 } //end of the function AAS_AreaClusterPortal
    758 //===========================================================================
    759 //
    760 // Parameter:				-
    761 // Returns:					-
    762 // Changes Globals:		-
    763 //===========================================================================
    764 int AAS_AreaDoNotEnter(int areanum)
    765 {
    766 	return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER);
    767 } //end of the function AAS_AreaDoNotEnter
    768 //===========================================================================
    769 // returns the time it takes perform a barrier jump
    770 //
    771 // Parameter:				-
    772 // Returns:					-
    773 // Changes Globals:		-
    774 //===========================================================================
    775 unsigned short int AAS_BarrierJumpTravelTime(void)
    776 {
    777 	return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1);
    778 } //end op the function AAS_BarrierJumpTravelTime
    779 //===========================================================================
    780 // returns true if there already exists a reachability from area1 to area2
    781 //
    782 // Parameter:				-
    783 // Returns:					-
    784 // Changes Globals:		-
    785 //===========================================================================
    786 qboolean AAS_ReachabilityExists(int area1num, int area2num)
    787 {
    788 	aas_lreachability_t *r;
    789 
    790 	for (r = areareachability[area1num]; r; r = r->next)
    791 	{
    792 		if (r->areanum == area2num) return qtrue;
    793 	} //end for
    794 	return qfalse;
    795 } //end of the function AAS_ReachabilityExists
    796 //===========================================================================
    797 // returns true if there is a solid just after the end point when going
    798 // from start to end
    799 //
    800 // Parameter:				-
    801 // Returns:					-
    802 // Changes Globals:		-
    803 //===========================================================================
    804 int AAS_NearbySolidOrGap(vec3_t start, vec3_t end)
    805 {
    806 	vec3_t dir, testpoint;
    807 	int areanum;
    808 
    809 	VectorSubtract(end, start, dir);
    810 	dir[2] = 0;
    811 	VectorNormalize(dir);
    812 	VectorMA(end, 48, dir, testpoint);
    813 
    814 	areanum = AAS_PointAreaNum(testpoint);
    815 	if (!areanum)
    816 	{
    817 		testpoint[2] += 16;
    818 		areanum = AAS_PointAreaNum(testpoint);
    819 		if (!areanum) return qtrue;
    820 	} //end if
    821 	VectorMA(end, 64, dir, testpoint);
    822 	areanum = AAS_PointAreaNum(testpoint);
    823 	if (areanum)
    824 	{
    825 		if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue;
    826 	} //end if
    827 	return qfalse;
    828 } //end of the function AAS_SolidGapTime
    829 //===========================================================================
    830 // searches for swim reachabilities between adjacent areas
    831 //
    832 // Parameter:				-
    833 // Returns:					-
    834 // Changes Globals:		-
    835 //===========================================================================
    836 int AAS_Reachability_Swim(int area1num, int area2num)
    837 {
    838 	int i, j, face1num, face2num, side1;
    839 	aas_area_t *area1, *area2;
    840 	aas_areasettings_t *areasettings;
    841 	aas_lreachability_t *lreach;
    842 	aas_face_t *face1;
    843 	aas_plane_t *plane;
    844 	vec3_t start;
    845 
    846 	if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse;
    847 	//if the second area is crouch only
    848 	if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse;
    849 
    850 	area1 = &aasworld.areas[area1num];
    851 	area2 = &aasworld.areas[area2num];
    852 
    853 	//if the areas are not near anough
    854 	for (i = 0; i < 3; i++)
    855 	{
    856 		if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
    857 		if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
    858 	} //end for
    859 	//find a shared face and create a reachability link
    860 	for (i = 0; i < area1->numfaces; i++)
    861 	{
    862 		face1num = aasworld.faceindex[area1->firstface + i];
    863 		side1 = face1num < 0;
    864 		face1num = abs(face1num);
    865 		//
    866 		for (j = 0; j < area2->numfaces; j++)
    867 		{
    868 			face2num = abs(aasworld.faceindex[area2->firstface + j]);
    869 			//
    870 			if (face1num == face2num)
    871 			{
    872 				AAS_FaceCenter(face1num, start);
    873 				//
    874 				if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))
    875 				{
    876 					//
    877 					face1 = &aasworld.faces[face1num];
    878 					areasettings = &aasworld.areasettings[area1num];
    879 					//create a new reachability link
    880 					lreach = AAS_AllocReachability();
    881 					if (!lreach) return qfalse;
    882 					lreach->areanum = area2num;
    883 					lreach->facenum = face1num;
    884 					lreach->edgenum = 0;
    885 					VectorCopy(start, lreach->start);
    886 					plane = &aasworld.planes[face1->planenum ^ side1];
    887 					VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end);
    888 					lreach->traveltype = TRAVEL_SWIM;
    889 					lreach->traveltime = 1;
    890 					//if the volume of the area is rather small
    891 					if (AAS_AreaVolume(area2num) < 800)
    892 						lreach->traveltime += 200;
    893 					//if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500;
    894 					//link the reachability
    895 					lreach->next = areareachability[area1num];
    896 					areareachability[area1num] = lreach;
    897 					reach_swim++;
    898 					return qtrue;
    899 				} //end if
    900 			} //end if
    901 		} //end for
    902 	} //end for
    903 	return qfalse;
    904 } //end of the function AAS_Reachability_Swim
    905 //===========================================================================
    906 // searches for reachabilities between adjacent areas with equal floor
    907 // heights
    908 //
    909 // Parameter:				-
    910 // Returns:					-
    911 // Changes Globals:		-
    912 //===========================================================================
    913 int AAS_Reachability_EqualFloorHeight(int area1num, int area2num)
    914 {
    915 	int i, j, edgenum, edgenum1, edgenum2, foundreach, side;
    916 	float height, bestheight, length, bestlength;
    917 	vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1};
    918 	vec3_t edgevec;
    919 	aas_area_t *area1, *area2;
    920 	aas_face_t *face1, *face2;
    921 	aas_edge_t *edge;
    922 	aas_plane_t *plane2;
    923 	aas_lreachability_t lr, *lreach;
    924 
    925 	if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
    926 
    927 	area1 = &aasworld.areas[area1num];
    928 	area2 = &aasworld.areas[area2num];
    929 	//if the areas are not near anough in the x-y direction
    930 	for (i = 0; i < 2; i++)
    931 	{
    932 		if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
    933 		if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
    934 	} //end for
    935 	//if area 2 is too high above area 1
    936 	if (area2->mins[2] > area1->maxs[2]) return qfalse;
    937 	//
    938 	VectorCopy(gravitydirection, invgravity);
    939 	VectorInverse(invgravity);
    940 	//
    941 	bestheight = 99999;
    942 	bestlength = 0;
    943 	foundreach = qfalse;
    944 	Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy
    945 	//
    946 	//check if the areas have ground faces with a common edge
    947 	//if existing use the lowest common edge for a reachability link
    948 	for (i = 0; i < area1->numfaces; i++)
    949 	{
    950 		face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])];
    951 		if (!(face1->faceflags & FACE_GROUND)) continue;
    952 		//
    953 		for (j = 0; j < area2->numfaces; j++)
    954 		{
    955 			face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
    956 			if (!(face2->faceflags & FACE_GROUND)) continue;
    957 			//if there is a common edge
    958 			for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++)
    959 			{
    960 				for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++)
    961 				{
    962 					if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) !=
    963 							abs(aasworld.edgeindex[face2->firstedge + edgenum2]))
    964 									continue;
    965 					edgenum = aasworld.edgeindex[face1->firstedge + edgenum1];
    966 					side = edgenum < 0;
    967 					edge = &aasworld.edges[abs(edgenum)];
    968 					//get the length of the edge
    969 					VectorSubtract(aasworld.vertexes[edge->v[1]],
    970 								aasworld.vertexes[edge->v[0]], dir);
    971 					length = VectorLength(dir);
    972 					//get the start point
    973 					VectorAdd(aasworld.vertexes[edge->v[0]],
    974 								aasworld.vertexes[edge->v[1]], start);
    975 					VectorScale(start, 0.5, start);
    976 					VectorCopy(start, end);
    977 					//get the end point several units inside area2
    978 					//and the start point several units inside area1
    979 					//NOTE: normal is pointing into area2 because the
    980 					//face edges are stored counter clockwise
    981 					VectorSubtract(aasworld.vertexes[edge->v[side]],
    982 								aasworld.vertexes[edge->v[!side]], edgevec);
    983 					plane2 = &aasworld.planes[face2->planenum];
    984 					CrossProduct(edgevec, plane2->normal, normal);
    985 					VectorNormalize(normal);
    986 					//
    987 					//VectorMA(start, -1, normal, start);
    988 					VectorMA(end, INSIDEUNITS_WALKEND, normal, end);
    989 					VectorMA(start, INSIDEUNITS_WALKSTART, normal, start);
    990 					end[2] += 0.125;
    991 					//
    992 					height = DotProduct(invgravity, start);
    993 					//NOTE: if there's nearby solid or a gap area after this area
    994 					//disabled this crap
    995 					//if (AAS_NearbySolidOrGap(start, end)) height += 200;
    996 					//NOTE: disabled because it disables reachabilities to very small areas
    997 					//if (AAS_PointAreaNum(end) != area2num) continue;
    998 					//get the longest lowest edge
    999 					if (height < bestheight ||
   1000 							(height < bestheight + 1 && length > bestlength))
   1001 					{
   1002 						bestheight = height;
   1003 						bestlength = length;
   1004 						//create a new reachability link
   1005 						lr.areanum = area2num;
   1006 						lr.facenum = 0;
   1007 						lr.edgenum = edgenum;
   1008 						VectorCopy(start, lr.start);
   1009 						VectorCopy(end, lr.end);
   1010 						lr.traveltype = TRAVEL_WALK;
   1011 						lr.traveltime = 1;
   1012 						foundreach = qtrue;
   1013 					} //end if
   1014 				} //end for
   1015 			} //end for
   1016 		} //end for
   1017 	} //end for
   1018 	if (foundreach)
   1019 	{
   1020 		//create a new reachability link
   1021 		lreach = AAS_AllocReachability();
   1022 		if (!lreach) return qfalse;
   1023 		lreach->areanum = lr.areanum;
   1024 		lreach->facenum = lr.facenum;
   1025 		lreach->edgenum = lr.edgenum;
   1026 		VectorCopy(lr.start, lreach->start);
   1027 		VectorCopy(lr.end, lreach->end);
   1028 		lreach->traveltype = lr.traveltype;
   1029 		lreach->traveltime = lr.traveltime;
   1030 		lreach->next = areareachability[area1num];
   1031 		areareachability[area1num] = lreach;
   1032 		//if going into a crouch area
   1033 		if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
   1034 		{
   1035 			lreach->traveltime += aassettings.rs_startcrouch;
   1036 		} //end if
   1037 		/*
   1038 		//NOTE: if there's nearby solid or a gap area after this area
   1039 		if (!AAS_NearbySolidOrGap(lreach->start, lreach->end))
   1040 		{
   1041 			lreach->traveltime += 100;
   1042 		} //end if
   1043 		*/
   1044 		//avoid rather small areas
   1045 		//if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;
   1046 		//
   1047 		reach_equalfloor++;
   1048 		return qtrue;
   1049 	} //end if
   1050 	return qfalse;
   1051 } //end of the function AAS_Reachability_EqualFloorHeight
   1052 //===========================================================================
   1053 // searches step, barrier, waterjump and walk off ledge reachabilities
   1054 //
   1055 // Parameter:				-
   1056 // Returns:					-
   1057 // Changes Globals:		-
   1058 //===========================================================================
   1059 int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num)
   1060 {
   1061 	int i, j, k, l, edge1num, edge2num, areas[10], numareas;
   1062 	int ground_bestarea2groundedgenum, ground_foundreach;
   1063 	int water_bestarea2groundedgenum, water_foundreach;
   1064 	int side1, area1swim, faceside1, groundface1num;
   1065 	float dist, dist1, dist2, diff, invgravitydot, ortdot;
   1066 	float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
   1067 	float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist;
   1068 	vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
   1069 	vec3_t normal, ort, edgevec, start, end, dir;
   1070 	vec3_t ground_beststart, ground_bestend, ground_bestnormal;
   1071 	vec3_t water_beststart, water_bestend, water_bestnormal;
   1072 	vec3_t invgravity = {0, 0, 1};
   1073 	vec3_t testpoint;
   1074 	aas_plane_t *plane;
   1075 	aas_area_t *area1, *area2;
   1076 	aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1;
   1077 	aas_edge_t *edge1, *edge2;
   1078 	aas_lreachability_t *lreach;
   1079 	aas_trace_t trace;
   1080 
   1081 	//must be able to walk or swim in the first area
   1082 	if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
   1083 	//
   1084 	if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse;
   1085 	//
   1086 	area1 = &aasworld.areas[area1num];
   1087 	area2 = &aasworld.areas[area2num];
   1088 	//if the first area contains a liquid
   1089 	area1swim = AAS_AreaSwim(area1num);
   1090 	//if the areas are not near anough in the x-y direction
   1091 	for (i = 0; i < 2; i++)
   1092 	{
   1093 		if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
   1094 		if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
   1095 	} //end for
   1096 	//
   1097 	ground_foundreach = qfalse;
   1098 	ground_bestdist = 99999;
   1099 	ground_bestlength = 0;
   1100 	ground_bestarea2groundedgenum = 0;
   1101 	//
   1102 	water_foundreach = qfalse;
   1103 	water_bestdist = 99999;
   1104 	water_bestlength = 0;
   1105 	water_bestarea2groundedgenum = 0;
   1106 	//
   1107 	for (i = 0; i < area1->numfaces; i++)
   1108 	{
   1109 		groundface1num = aasworld.faceindex[area1->firstface + i];
   1110 		faceside1 = groundface1num < 0;
   1111 		groundface1 = &aasworld.faces[abs(groundface1num)];
   1112 		//if this isn't a ground face
   1113 		if (!(groundface1->faceflags & FACE_GROUND))
   1114 		{
   1115 			//if we can swim in the first area
   1116 			if (area1swim)
   1117 			{
   1118 				//face plane must be more or less horizontal
   1119 				plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)];
   1120 				if (DotProduct(plane->normal, invgravity) < 0.7) continue;
   1121 			} //end if
   1122 			else
   1123 			{
   1124 				//if we can't swim in the area it must be a ground face
   1125 				continue;
   1126 			} //end else
   1127 		} //end if
   1128 		//
   1129 		for (k = 0; k < groundface1->numedges; k++)
   1130 		{
   1131 			edge1num = aasworld.edgeindex[groundface1->firstedge + k];
   1132 			side1 = (edge1num < 0);
   1133 			//NOTE: for water faces we must take the side area 1 is
   1134 			// on into account because the face is shared and doesn't
   1135 			// have to be oriented correctly
   1136 			if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1);
   1137 			edge1num = abs(edge1num);
   1138 			edge1 = &aasworld.edges[edge1num];
   1139 			//vertexes of the edge
   1140 			VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1);
   1141 			VectorCopy(aasworld.vertexes[edge1->v[side1]], v2);
   1142 			//get a vertical plane through the edge
   1143 			//NOTE: normal is pointing into area 2 because the
   1144 			//face edges are stored counter clockwise
   1145 			VectorSubtract(v2, v1, edgevec);
   1146 			CrossProduct(edgevec, invgravity, normal);
   1147 			VectorNormalize(normal);
   1148 			dist = DotProduct(normal, v1);
   1149 			//check the faces from the second area
   1150 			for (j = 0; j < area2->numfaces; j++)
   1151 			{
   1152 				groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
   1153 				//must be a ground face
   1154 				if (!(groundface2->faceflags & FACE_GROUND)) continue;
   1155 				//check the edges of this ground face
   1156 				for (l = 0; l < groundface2->numedges; l++)
   1157 				{
   1158 					edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]);
   1159 					edge2 = &aasworld.edges[edge2num];
   1160 					//vertexes of the edge
   1161 					VectorCopy(aasworld.vertexes[edge2->v[0]], v3);
   1162 					VectorCopy(aasworld.vertexes[edge2->v[1]], v4);
   1163 					//check the distance between the two points and the vertical plane
   1164 					//through the edge of area1
   1165 					diff = DotProduct(normal, v3) - dist;
   1166 					if (diff < -0.1 || diff > 0.1) continue;
   1167 					diff = DotProduct(normal, v4) - dist;
   1168 					if (diff < -0.1 || diff > 0.1) continue;
   1169 					//
   1170 					//project the two ground edges into the step side plane
   1171 					//and calculate the shortest distance between the two
   1172 					//edges if they overlap in the direction orthogonal to
   1173 					//the gravity direction
   1174 					CrossProduct(invgravity, normal, ort);
   1175 					invgravitydot = DotProduct(invgravity, invgravity);
   1176 					ortdot = DotProduct(ort, ort);
   1177 					//projection into the step plane
   1178 					//NOTE: since gravity is vertical this is just the z coordinate
   1179 					y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot;
   1180 					y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot;
   1181 					y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot;
   1182 					y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot;
   1183 					//
   1184 					x1 = DotProduct(v1, ort) / ortdot;
   1185 					x2 = DotProduct(v2, ort) / ortdot;
   1186 					x3 = DotProduct(v3, ort) / ortdot;
   1187 					x4 = DotProduct(v4, ort) / ortdot;
   1188 					//
   1189 					if (x1 > x2)
   1190 					{
   1191 						tmp = x1; x1 = x2; x2 = tmp;
   1192 						tmp = y1; y1 = y2; y2 = tmp;
   1193 						VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2);
   1194 					} //end if
   1195 					if (x3 > x4)
   1196 					{
   1197 						tmp = x3; x3 = x4; x4 = tmp;
   1198 						tmp = y3; y3 = y4; y4 = tmp;
   1199 						VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4);
   1200 					} //end if
   1201 					//if the two projected edge lines have no overlap
   1202 					if (x2 <= x3 || x4 <= x1)
   1203 					{
   1204 //						Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num);
   1205 						continue;
   1206 					} //end if
   1207 					//if the two lines fully overlap
   1208 					if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) &&
   1209 							(x3 - 0.5 < x1 && x2 < x4 + 0.5))
   1210 					{
   1211 						dist1 = y3 - y1;
   1212 						dist2 = y4 - y2;
   1213 						VectorCopy(v1, p1area1);
   1214 						VectorCopy(v2, p2area1);
   1215 						VectorCopy(v3, p1area2);
   1216 						VectorCopy(v4, p2area2);
   1217 					} //end if
   1218 					else
   1219 					{
   1220 						//if the points are equal
   1221 						if (x1 > x3 - 0.1 && x1 < x3 + 0.1)
   1222 						{
   1223 							dist1 = y3 - y1;
   1224 							VectorCopy(v1, p1area1);
   1225 							VectorCopy(v3, p1area2);
   1226 						} //end if
   1227 						else if (x1 < x3)
   1228 						{
   1229 							y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
   1230 							dist1 = y3 - y;
   1231 							VectorCopy(v3, p1area1);
   1232 							p1area1[2] = y;
   1233 							VectorCopy(v3, p1area2);
   1234 						} //end if
   1235 						else
   1236 						{
   1237 							y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
   1238 							dist1 = y - y1;
   1239 							VectorCopy(v1, p1area1);
   1240 							VectorCopy(v1, p1area2);
   1241 							p1area2[2] = y;
   1242 						} //end if
   1243 						//if the points are equal
   1244 						if (x2 > x4 - 0.1 && x2 < x4 + 0.1)
   1245 						{
   1246 							dist2 = y4 - y2;
   1247 							VectorCopy(v2, p2area1);
   1248 							VectorCopy(v4, p2area2);
   1249 						} //end if
   1250 						else if (x2 < x4)
   1251 						{
   1252 							y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
   1253 							dist2 = y - y2;
   1254 							VectorCopy(v2, p2area1);
   1255 							VectorCopy(v2, p2area2);
   1256 							p2area2[2] = y;
   1257 						} //end if
   1258 						else
   1259 						{
   1260 							y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
   1261 							dist2 = y4 - y;
   1262 							VectorCopy(v4, p2area1);
   1263 							p2area1[2] = y;
   1264 							VectorCopy(v4, p2area2);
   1265 						} //end else
   1266 					} //end else
   1267 					//if both distances are pretty much equal
   1268 					//then we take the middle of the points
   1269 					if (dist1 > dist2 - 1 && dist1 < dist2 + 1)
   1270 					{
   1271 						dist = dist1;
   1272 						VectorAdd(p1area1, p2area1, start);
   1273 						VectorScale(start, 0.5, start);
   1274 						VectorAdd(p1area2, p2area2, end);
   1275 						VectorScale(end, 0.5, end);
   1276 					} //end if
   1277 					else if (dist1 < dist2)
   1278 					{
   1279 						dist = dist1;
   1280 						VectorCopy(p1area1, start);
   1281 						VectorCopy(p1area2, end);
   1282 					} //end else if
   1283 					else
   1284 					{
   1285 						dist = dist2;
   1286 						VectorCopy(p2area1, start);
   1287 						VectorCopy(p2area2, end);
   1288 					} //end else
   1289 					//get the length of the overlapping part of the edges of the two areas
   1290 					VectorSubtract(p2area2, p1area2, dir);
   1291 					length = VectorLength(dir);
   1292 					//
   1293 					if (groundface1->faceflags & FACE_GROUND)
   1294 					{
   1295 						//if the vertical distance is smaller
   1296 						if (dist < ground_bestdist ||
   1297 								//or the vertical distance is pretty much the same
   1298 								//but the overlapping part of the edges is longer
   1299 								(dist < ground_bestdist + 1 && length > ground_bestlength))
   1300 						{
   1301 							ground_bestdist = dist;
   1302 							ground_bestlength = length;
   1303 							ground_foundreach = qtrue;
   1304 							ground_bestarea2groundedgenum = edge1num;
   1305 							ground_bestface1 = groundface1;
   1306 							//best point towards area1
   1307 							VectorCopy(start, ground_beststart);
   1308 							//normal is pointing into area2
   1309 							VectorCopy(normal, ground_bestnormal);
   1310 							//best point towards area2
   1311 							VectorCopy(end, ground_bestend);
   1312 						} //end if
   1313 					} //end if
   1314 					else
   1315 					{
   1316 						//if the vertical distance is smaller
   1317 						if (dist < water_bestdist ||
   1318 								//or the vertical distance is pretty much the same
   1319 								//but the overlapping part of the edges is longer
   1320 								(dist < water_bestdist + 1 && length > water_bestlength))
   1321 						{
   1322 							water_bestdist = dist;
   1323 							water_bestlength = length;
   1324 							water_foundreach = qtrue;
   1325 							water_bestarea2groundedgenum = edge1num;
   1326 							water_bestface1 = groundface1;
   1327 							//best point towards area1
   1328 							VectorCopy(start, water_beststart);
   1329 							//normal is pointing into area2
   1330 							VectorCopy(normal, water_bestnormal);
   1331 							//best point towards area2
   1332 							VectorCopy(end, water_bestend);
   1333 						} //end if
   1334 					} //end else
   1335 				} //end for
   1336 			} //end for
   1337 		} //end for
   1338 	} //end for
   1339 	//
   1340 	// NOTE: swim reachabilities are already filtered out
   1341 	//
   1342 	// Steps
   1343 	//
   1344 	//        ---------
   1345 	//        |          step height -> TRAVEL_WALK
   1346 	//--------|
   1347 	//
   1348 	//        ---------
   1349 	//~~~~~~~~|          step height and low water -> TRAVEL_WALK
   1350 	//--------|
   1351 	//
   1352 	//~~~~~~~~~~~~~~~~~~
   1353 	//        ---------
   1354 	//        |          step height and low water up to the step -> TRAVEL_WALK
   1355 	//--------|
   1356 	//
   1357 	//check for a step reachability
   1358 	if (ground_foundreach)
   1359 	{
   1360 		//if area2 is higher but lower than the maximum step height
   1361 		//NOTE: ground_bestdist >= 0 also catches equal floor reachabilities
   1362 		if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep)
   1363 		{
   1364 			//create walk reachability from area1 to area2
   1365 			lreach = AAS_AllocReachability();
   1366 			if (!lreach) return qfalse;
   1367 			lreach->areanum = area2num;
   1368 			lreach->facenum = 0;
   1369 			lreach->edgenum = ground_bestarea2groundedgenum;
   1370 			VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
   1371 			VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
   1372 			lreach->traveltype = TRAVEL_WALK;
   1373 			lreach->traveltime = 0;//1;
   1374 			//if going into a crouch area
   1375 			if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
   1376 			{
   1377 				lreach->traveltime += aassettings.rs_startcrouch;
   1378 			} //end if
   1379 			lreach->next = areareachability[area1num];
   1380 			areareachability[area1num] = lreach;
   1381 			//NOTE: if there's nearby solid or a gap area after this area
   1382 			/*
   1383 			if (!AAS_NearbySolidOrGap(lreach->start, lreach->end))
   1384 			{
   1385 				lreach->traveltime += 100;
   1386 			} //end if
   1387 			*/
   1388 			//avoid rather small areas
   1389 			//if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;
   1390 			//
   1391 			reach_step++;
   1392 			return qtrue;
   1393 		} //end if
   1394 	} //end if
   1395 	//
   1396 	// Water Jumps
   1397 	//
   1398 	//        ---------
   1399 	//        |
   1400 	//~~~~~~~~|
   1401 	//        |
   1402 	//        |          higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP
   1403 	//--------|
   1404 	//
   1405 	//~~~~~~~~~~~~~~~~~~
   1406 	//        ---------
   1407 	//        |
   1408 	//        |
   1409 	//        |
   1410 	//        |          higher than step height and low water up to the step -> TRAVEL_WATERJUMP
   1411 	//--------|
   1412 	//
   1413 	//check for a waterjump reachability
   1414 	if (water_foundreach)
   1415 	{
   1416 		//get a test point a little bit towards area1
   1417 		VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint);
   1418 		//go down the maximum waterjump height
   1419 		testpoint[2] -= aassettings.phys_maxwaterjump;
   1420 		//if there IS water the sv_maxwaterjump height below the bestend point
   1421 		if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID)
   1422 		{
   1423 			//don't create rediculous water jump reachabilities from areas very far below
   1424 			//the water surface
   1425 			if (water_bestdist < aassettings.phys_maxwaterjump + 24)
   1426 			{
   1427 				//waterjumping from or towards a crouch only area is not possible in Quake2
   1428 				if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) &&
   1429 						(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL))
   1430 				{
   1431 					//create water jump reachability from area1 to area2
   1432 					lreach = AAS_AllocReachability();
   1433 					if (!lreach) return qfalse;
   1434 					lreach->areanum = area2num;
   1435 					lreach->facenum = 0;
   1436 					lreach->edgenum = water_bestarea2groundedgenum;
   1437 					VectorCopy(water_beststart, lreach->start);
   1438 					VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end);
   1439 					lreach->traveltype = TRAVEL_WATERJUMP;
   1440 					lreach->traveltime = aassettings.rs_waterjump;
   1441 					lreach->next = areareachability[area1num];
   1442 					areareachability[area1num] = lreach;
   1443 					//we've got another waterjump reachability
   1444 					reach_waterjump++;
   1445 					return qtrue;
   1446 				} //end if
   1447 			} //end if
   1448 		} //end if
   1449 	} //end if
   1450 	//
   1451 	// Barrier Jumps
   1452 	//
   1453 	//        ---------
   1454 	//        |
   1455 	//        |
   1456 	//        |
   1457 	//        |         higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP
   1458 	//--------|
   1459 	//
   1460 	//        ---------
   1461 	//        |
   1462 	//        |
   1463 	//        |
   1464 	//~~~~~~~~|         higher than step height lower than barrier height
   1465 	//--------|         and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP
   1466 	//
   1467 	//check for a barrier jump reachability
   1468 	if (ground_foundreach)
   1469 	{
   1470 		//if area2 is higher but lower than the maximum barrier jump height
   1471 		if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier)
   1472 		{
   1473 			//if no water in area1 or a very thin layer of water on the ground
   1474 			if (!water_foundreach || (ground_bestdist - water_bestdist < 16))
   1475 			{
   1476 				//cannot perform a barrier jump towards or from a crouch area in Quake2
   1477 				if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num))
   1478 				{
   1479 					//create barrier jump reachability from area1 to area2
   1480 					lreach = AAS_AllocReachability();
   1481 					if (!lreach) return qfalse;
   1482 					lreach->areanum = area2num;
   1483 					lreach->facenum = 0;
   1484 					lreach->edgenum = ground_bestarea2groundedgenum;
   1485 					VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
   1486 					VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
   1487 					lreach->traveltype = TRAVEL_BARRIERJUMP;
   1488 					lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime();
   1489 					lreach->next = areareachability[area1num];
   1490 					areareachability[area1num] = lreach;
   1491 					//we've got another barrierjump reachability
   1492 					reach_barrier++;
   1493 					return qtrue;
   1494 				} //end if
   1495 			} //end if
   1496 		} //end if
   1497 	} //end if
   1498 	//
   1499 	// Walk and Walk Off Ledge
   1500 	//
   1501 	//--------|
   1502 	//        |          can walk or step back -> TRAVEL_WALK
   1503 	//        ---------
   1504 	//
   1505 	//--------|
   1506 	//        |
   1507 	//        |
   1508 	//        |
   1509 	//        |          cannot walk/step back -> TRAVEL_WALKOFFLEDGE
   1510 	//        ---------
   1511 	//
   1512 	//--------|
   1513 	//        |
   1514 	//        |~~~~~~~~
   1515 	//        |
   1516 	//        |          cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE
   1517 	//        ---------  FIXME: create TRAVEL_WALK reach??
   1518 	//
   1519 	//check for a walk or walk off ledge reachability
   1520 	if (ground_foundreach)
   1521 	{
   1522 		if (ground_bestdist < 0)
   1523 		{
   1524 			if (ground_bestdist > -aassettings.phys_maxstep)
   1525 			{
   1526 				//create walk reachability from area1 to area2
   1527 				lreach = AAS_AllocReachability();
   1528 				if (!lreach) return qfalse;
   1529 				lreach->areanum = area2num;
   1530 				lreach->facenum = 0;
   1531 				lreach->edgenum = ground_bestarea2groundedgenum;
   1532 				VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
   1533 				VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
   1534 				lreach->traveltype = TRAVEL_WALK;
   1535 				lreach->traveltime = 1;
   1536 				lreach->next = areareachability[area1num];
   1537 				areareachability[area1num] = lreach;
   1538 				//we've got another walk reachability
   1539 				reach_walk++;
   1540 				return qtrue;
   1541 			} //end if
   1542 			// if no maximum fall height set or less than the max
   1543 			if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) {
   1544 				//trace a bounding box vertically to check for solids
   1545 				VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend);
   1546 				VectorCopy(ground_bestend, start);
   1547 				start[2] = ground_beststart[2];
   1548 				VectorCopy(ground_bestend, end);
   1549 				end[2] += 4;
   1550 				trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
   1551 				//if no solids were found
   1552 				if (!trace.startsolid && trace.fraction >= 1.0)
   1553 				{
   1554 					//the trace end point must be in the goal area
   1555 					trace.endpos[2] += 1;
   1556 					if (AAS_PointAreaNum(trace.endpos) == area2num)
   1557 					{
   1558 						//if not going through a cluster portal
   1559 						numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int));
   1560 						for (i = 0; i < numareas; i++)
   1561 							if (AAS_AreaClusterPortal(areas[i]))
   1562 								break;
   1563 						if (i >= numareas)
   1564 						{
   1565 							//create a walk off ledge reachability from area1 to area2
   1566 							lreach = AAS_AllocReachability();
   1567 							if (!lreach) return qfalse;
   1568 							lreach->areanum = area2num;
   1569 							lreach->facenum = 0;
   1570 							lreach->edgenum = ground_bestarea2groundedgenum;
   1571 							VectorCopy(ground_beststart, lreach->start);
   1572 							VectorCopy(ground_bestend, lreach->end);
   1573 							lreach->traveltype = TRAVEL_WALKOFFLEDGE;
   1574 							lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity;
   1575 							//if falling from too high and not falling into water
   1576 							if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num))
   1577 							{
   1578 								if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5)
   1579 								{
   1580 									lreach->traveltime += aassettings.rs_falldamage5;
   1581 								} //end if
   1582 								if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10)
   1583 								{
   1584 									lreach->traveltime += aassettings.rs_falldamage10;
   1585 								} //end if
   1586 							} //end if
   1587 							lreach->next = areareachability[area1num];
   1588 							areareachability[area1num] = lreach;
   1589 							//
   1590 							reach_walkoffledge++;
   1591 							//NOTE: don't create a weapon (rl, bfg) jump reachability here
   1592 							//because it interferes with other reachabilities
   1593 							//like the ladder reachability
   1594 							return qtrue;
   1595 						} //end if
   1596 					} //end if
   1597 				} //end if
   1598 			} //end if
   1599 		} //end else
   1600 	} //end if
   1601 	return qfalse;
   1602 } //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge
   1603 //===========================================================================
   1604 // returns the distance between the two vectors
   1605 //
   1606 // Parameter:				-
   1607 // Returns:					-
   1608 // Changes Globals:		-
   1609 //===========================================================================
   1610 float VectorDistance(vec3_t v1, vec3_t v2)
   1611 {
   1612 	vec3_t dir;
   1613 
   1614 	VectorSubtract(v2, v1, dir);
   1615 	return VectorLength(dir);
   1616 } //end of the function VectorDistance
   1617 //===========================================================================
   1618 // returns true if the first vector is between the last two vectors
   1619 //
   1620 // Parameter:				-
   1621 // Returns:					-
   1622 // Changes Globals:		-
   1623 //===========================================================================
   1624 int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2)
   1625 {
   1626 	vec3_t dir1, dir2;
   1627 
   1628 	VectorSubtract(v, v1, dir1);
   1629 	VectorSubtract(v, v2, dir2);
   1630 	return (DotProduct(dir1, dir2) <= 0);
   1631 } //end of the function VectorBetweenVectors
   1632 //===========================================================================
   1633 // returns the mid point between the two vectors
   1634 //
   1635 // Parameter:				-
   1636 // Returns:					-
   1637 // Changes Globals:		-
   1638 //===========================================================================
   1639 void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle)
   1640 {
   1641 	VectorAdd(v1, v2, middle);
   1642 	VectorScale(middle, 0.5, middle);
   1643 } //end of the function VectorMiddle
   1644 //===========================================================================
   1645 // calculate a range of points closest to each other on both edges
   1646 //
   1647 // Parameter:			beststart1		start of the range of points on edge v1-v2
   1648 //						beststart2		end of the range of points  on edge v1-v2
   1649 //						bestend1		start of the range of points on edge v3-v4
   1650 //						bestend2		end of the range of points  on edge v3-v4
   1651 //						bestdist		best distance so far
   1652 // Returns:				-
   1653 // Changes Globals:		-
   1654 //===========================================================================
   1655 /*
   1656 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
   1657 							aas_plane_t *plane1, aas_plane_t *plane2,
   1658 							vec3_t beststart, vec3_t bestend, float bestdist)
   1659 {
   1660 	vec3_t dir1, dir2, p1, p2, p3, p4;
   1661 	float a1, a2, b1, b2, dist;
   1662 	int founddist;
   1663 
   1664 	//edge vectors
   1665 	VectorSubtract(v2, v1, dir1);
   1666 	VectorSubtract(v4, v3, dir2);
   1667 	//get the horizontal directions
   1668 	dir1[2] = 0;
   1669 	dir2[2] = 0;
   1670 	//
   1671 	// p1 = point on an edge vector of area2 closest to v1
   1672 	// p2 = point on an edge vector of area2 closest to v2
   1673 	// p3 = point on an edge vector of area1 closest to v3
   1674 	// p4 = point on an edge vector of area1 closest to v4
   1675 	//
   1676 	if (dir2[0])
   1677 	{
   1678 		a2 = dir2[1] / dir2[0];
   1679 		b2 = v3[1] - a2 * v3[0];
   1680 		//point on the edge vector of area2 closest to v1
   1681 		p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
   1682 		p1[1] = a2 * p1[0] + b2;
   1683 		//point on the edge vector of area2 closest to v2
   1684 		p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
   1685 		p2[1] = a2 * p2[0] + b2;
   1686 	} //end if
   1687 	else
   1688 	{
   1689 		//point on the edge vector of area2 closest to v1
   1690 		p1[0] = v3[0];
   1691 		p1[1] = v1[1];
   1692 		//point on the edge vector of area2 closest to v2
   1693 		p2[0] = v3[0];
   1694 		p2[1] = v2[1];
   1695 	} //end else
   1696 	//
   1697 	if (dir1[0])
   1698 	{
   1699 		//
   1700 		a1 = dir1[1] / dir1[0];
   1701 		b1 = v1[1] - a1 * v1[0];
   1702 		//point on the edge vector of area1 closest to v3
   1703 		p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
   1704 		p3[1] = a1 * p3[0] + b1;
   1705 		//point on the edge vector of area1 closest to v4
   1706 		p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
   1707 		p4[1] = a1 * p4[0] + b1;
   1708 	} //end if
   1709 	else
   1710 	{
   1711 		//point on the edge vector of area1 closest to v3
   1712 		p3[0] = v1[0];
   1713 		p3[1] = v3[1];
   1714 		//point on the edge vector of area1 closest to v4
   1715 		p4[0] = v1[0];
   1716 		p4[1] = v4[1];
   1717 	} //end else
   1718 	//start with zero z-coordinates
   1719 	p1[2] = 0;
   1720 	p2[2] = 0;
   1721 	p3[2] = 0;
   1722 	p4[2] = 0;
   1723 	//calculate the z-coordinates from the ground planes
   1724 	p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
   1725 	p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
   1726 	p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
   1727 	p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
   1728 	//
   1729 	founddist = qfalse;
   1730 	//
   1731 	if (VectorBetweenVectors(p1, v3, v4))
   1732 	{
   1733 		dist = VectorDistance(v1, p1);
   1734 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1735 		{
   1736 			VectorMiddle(beststart, v1, beststart);
   1737 			VectorMiddle(bestend, p1, bestend);
   1738 		} //end if
   1739 		else if (dist < bestdist)
   1740 		{
   1741 			bestdist = dist;
   1742 			VectorCopy(v1, beststart);
   1743 			VectorCopy(p1, bestend);
   1744 		} //end if
   1745 		founddist = qtrue;
   1746 	} //end if
   1747 	if (VectorBetweenVectors(p2, v3, v4))
   1748 	{
   1749 		dist = VectorDistance(v2, p2);
   1750 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1751 		{
   1752 			VectorMiddle(beststart, v2, beststart);
   1753 			VectorMiddle(bestend, p2, bestend);
   1754 		} //end if
   1755 		else if (dist < bestdist)
   1756 		{
   1757 			bestdist = dist;
   1758 			VectorCopy(v2, beststart);
   1759 			VectorCopy(p2, bestend);
   1760 		} //end if
   1761 		founddist = qtrue;
   1762 	} //end else if
   1763 	if (VectorBetweenVectors(p3, v1, v2))
   1764 	{
   1765 		dist = VectorDistance(v3, p3);
   1766 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1767 		{
   1768 			VectorMiddle(beststart, p3, beststart);
   1769 			VectorMiddle(bestend, v3, bestend);
   1770 		} //end if
   1771 		else if (dist < bestdist)
   1772 		{
   1773 			bestdist = dist;
   1774 			VectorCopy(p3, beststart);
   1775 			VectorCopy(v3, bestend);
   1776 		} //end if
   1777 		founddist = qtrue;
   1778 	} //end else if
   1779 	if (VectorBetweenVectors(p4, v1, v2))
   1780 	{
   1781 		dist = VectorDistance(v4, p4);
   1782 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1783 		{
   1784 			VectorMiddle(beststart, p4, beststart);
   1785 			VectorMiddle(bestend, v4, bestend);
   1786 		} //end if
   1787 		else if (dist < bestdist)
   1788 		{
   1789 			bestdist = dist;
   1790 			VectorCopy(p4, beststart);
   1791 			VectorCopy(v4, bestend);
   1792 		} //end if
   1793 		founddist = qtrue;
   1794 	} //end else if
   1795 	//if no shortest distance was found the shortest distance
   1796 	//is between one of the vertexes of edge1 and one of edge2
   1797 	if (!founddist)
   1798 	{
   1799 		dist = VectorDistance(v1, v3);
   1800 		if (dist < bestdist)
   1801 		{
   1802 			bestdist = dist;
   1803 			VectorCopy(v1, beststart);
   1804 			VectorCopy(v3, bestend);
   1805 		} //end if
   1806 		dist = VectorDistance(v1, v4);
   1807 		if (dist < bestdist)
   1808 		{
   1809 			bestdist = dist;
   1810 			VectorCopy(v1, beststart);
   1811 			VectorCopy(v4, bestend);
   1812 		} //end if
   1813 		dist = VectorDistance(v2, v3);
   1814 		if (dist < bestdist)
   1815 		{
   1816 			bestdist = dist;
   1817 			VectorCopy(v2, beststart);
   1818 			VectorCopy(v3, bestend);
   1819 		} //end if
   1820 		dist = VectorDistance(v2, v4);
   1821 		if (dist < bestdist)
   1822 		{
   1823 			bestdist = dist;
   1824 			VectorCopy(v2, beststart);
   1825 			VectorCopy(v4, bestend);
   1826 		} //end if
   1827 	} //end if
   1828 	return bestdist;
   1829 } //end of the function AAS_ClosestEdgePoints*/
   1830 
   1831 float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
   1832 							aas_plane_t *plane1, aas_plane_t *plane2,
   1833 							vec3_t beststart1, vec3_t bestend1,
   1834 							vec3_t beststart2, vec3_t bestend2, float bestdist)
   1835 {
   1836 	vec3_t dir1, dir2, p1, p2, p3, p4;
   1837 	float a1, a2, b1, b2, dist, dist1, dist2;
   1838 	int founddist;
   1839 
   1840 	//edge vectors
   1841 	VectorSubtract(v2, v1, dir1);
   1842 	VectorSubtract(v4, v3, dir2);
   1843 	//get the horizontal directions
   1844 	dir1[2] = 0;
   1845 	dir2[2] = 0;
   1846 	//
   1847 	// p1 = point on an edge vector of area2 closest to v1
   1848 	// p2 = point on an edge vector of area2 closest to v2
   1849 	// p3 = point on an edge vector of area1 closest to v3
   1850 	// p4 = point on an edge vector of area1 closest to v4
   1851 	//
   1852 	if (dir2[0])
   1853 	{
   1854 		a2 = dir2[1] / dir2[0];
   1855 		b2 = v3[1] - a2 * v3[0];
   1856 		//point on the edge vector of area2 closest to v1
   1857 		p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
   1858 		p1[1] = a2 * p1[0] + b2;
   1859 		//point on the edge vector of area2 closest to v2
   1860 		p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
   1861 		p2[1] = a2 * p2[0] + b2;
   1862 	} //end if
   1863 	else
   1864 	{
   1865 		//point on the edge vector of area2 closest to v1
   1866 		p1[0] = v3[0];
   1867 		p1[1] = v1[1];
   1868 		//point on the edge vector of area2 closest to v2
   1869 		p2[0] = v3[0];
   1870 		p2[1] = v2[1];
   1871 	} //end else
   1872 	//
   1873 	if (dir1[0])
   1874 	{
   1875 		//
   1876 		a1 = dir1[1] / dir1[0];
   1877 		b1 = v1[1] - a1 * v1[0];
   1878 		//point on the edge vector of area1 closest to v3
   1879 		p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
   1880 		p3[1] = a1 * p3[0] + b1;
   1881 		//point on the edge vector of area1 closest to v4
   1882 		p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
   1883 		p4[1] = a1 * p4[0] + b1;
   1884 	} //end if
   1885 	else
   1886 	{
   1887 		//point on the edge vector of area1 closest to v3
   1888 		p3[0] = v1[0];
   1889 		p3[1] = v3[1];
   1890 		//point on the edge vector of area1 closest to v4
   1891 		p4[0] = v1[0];
   1892 		p4[1] = v4[1];
   1893 	} //end else
   1894 	//start with zero z-coordinates
   1895 	p1[2] = 0;
   1896 	p2[2] = 0;
   1897 	p3[2] = 0;
   1898 	p4[2] = 0;
   1899 	//calculate the z-coordinates from the ground planes
   1900 	p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
   1901 	p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
   1902 	p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
   1903 	p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
   1904 	//
   1905 	founddist = qfalse;
   1906 	//
   1907 	if (VectorBetweenVectors(p1, v3, v4))
   1908 	{
   1909 		dist = VectorDistance(v1, p1);
   1910 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1911 		{
   1912 			dist1 = VectorDistance(beststart1, v1);
   1913 			dist2 = VectorDistance(beststart2, v1);
   1914 			if (dist1 > dist2)
   1915 			{
   1916 				if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2);
   1917 			} //end if
   1918 			else
   1919 			{
   1920 				if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1);
   1921 			} //end else
   1922 			dist1 = VectorDistance(bestend1, p1);
   1923 			dist2 = VectorDistance(bestend2, p1);
   1924 			if (dist1 > dist2)
   1925 			{
   1926 				if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2);
   1927 			} //end if
   1928 			else
   1929 			{
   1930 				if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1);
   1931 			} //end else
   1932 		} //end if
   1933 		else if (dist < bestdist)
   1934 		{
   1935 			bestdist = dist;
   1936 			VectorCopy(v1, beststart1);
   1937 			VectorCopy(v1, beststart2);
   1938 			VectorCopy(p1, bestend1);
   1939 			VectorCopy(p1, bestend2);
   1940 		} //end if
   1941 		founddist = qtrue;
   1942 	} //end if
   1943 	if (VectorBetweenVectors(p2, v3, v4))
   1944 	{
   1945 		dist = VectorDistance(v2, p2);
   1946 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1947 		{
   1948 			dist1 = VectorDistance(beststart1, v2);
   1949 			dist2 = VectorDistance(beststart2, v2);
   1950 			if (dist1 > dist2)
   1951 			{
   1952 				if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2);
   1953 			} //end if
   1954 			else
   1955 			{
   1956 				if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1);
   1957 			} //end else
   1958 			dist1 = VectorDistance(bestend1, p2);
   1959 			dist2 = VectorDistance(bestend2, p2);
   1960 			if (dist1 > dist2)
   1961 			{
   1962 				if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2);
   1963 			} //end if
   1964 			else
   1965 			{
   1966 				if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1);
   1967 			} //end else
   1968 		} //end if
   1969 		else if (dist < bestdist)
   1970 		{
   1971 			bestdist = dist;
   1972 			VectorCopy(v2, beststart1);
   1973 			VectorCopy(v2, beststart2);
   1974 			VectorCopy(p2, bestend1);
   1975 			VectorCopy(p2, bestend2);
   1976 		} //end if
   1977 		founddist = qtrue;
   1978 	} //end else if
   1979 	if (VectorBetweenVectors(p3, v1, v2))
   1980 	{
   1981 		dist = VectorDistance(v3, p3);
   1982 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   1983 		{
   1984 			dist1 = VectorDistance(beststart1, p3);
   1985 			dist2 = VectorDistance(beststart2, p3);
   1986 			if (dist1 > dist2)
   1987 			{
   1988 				if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2);
   1989 			} //end if
   1990 			else
   1991 			{
   1992 				if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1);
   1993 			} //end else
   1994 			dist1 = VectorDistance(bestend1, v3);
   1995 			dist2 = VectorDistance(bestend2, v3);
   1996 			if (dist1 > dist2)
   1997 			{
   1998 				if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2);
   1999 			} //end if
   2000 			else
   2001 			{
   2002 				if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1);
   2003 			} //end else
   2004 		} //end if
   2005 		else if (dist < bestdist)
   2006 		{
   2007 			bestdist = dist;
   2008 			VectorCopy(p3, beststart1);
   2009 			VectorCopy(p3, beststart2);
   2010 			VectorCopy(v3, bestend1);
   2011 			VectorCopy(v3, bestend2);
   2012 		} //end if
   2013 		founddist = qtrue;
   2014 	} //end else if
   2015 	if (VectorBetweenVectors(p4, v1, v2))
   2016 	{
   2017 		dist = VectorDistance(v4, p4);
   2018 		if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
   2019 		{
   2020 			dist1 = VectorDistance(beststart1, p4);
   2021 			dist2 = VectorDistance(beststart2, p4);
   2022 			if (dist1 > dist2)
   2023 			{
   2024 				if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2);
   2025 			} //end if
   2026 			else
   2027 			{
   2028 				if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1);
   2029 			} //end else
   2030 			dist1 = VectorDistance(bestend1, v4);
   2031 			dist2 = VectorDistance(bestend2, v4);
   2032 			if (dist1 > dist2)
   2033 			{
   2034 				if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2);
   2035 			} //end if
   2036 			else
   2037 			{
   2038 				if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1);
   2039 			} //end else
   2040 		} //end if
   2041 		else if (dist < bestdist)
   2042 		{
   2043 			bestdist = dist;
   2044 			VectorCopy(p4, beststart1);
   2045 			VectorCopy(p4, beststart2);
   2046 			VectorCopy(v4, bestend1);
   2047 			VectorCopy(v4, bestend2);
   2048 		} //end if
   2049 		founddist = qtrue;
   2050 	} //end else if
   2051 	//if no shortest distance was found the shortest distance
   2052 	//is between one of the vertexes of edge1 and one of edge2
   2053 	if (!founddist)
   2054 	{
   2055 		dist = VectorDistance(v1, v3);
   2056 		if (dist < bestdist)
   2057 		{
   2058 			bestdist = dist;
   2059 			VectorCopy(v1, beststart1);
   2060 			VectorCopy(v1, beststart2);
   2061 			VectorCopy(v3, bestend1);
   2062 			VectorCopy(v3, bestend2);
   2063 		} //end if
   2064 		dist = VectorDistance(v1, v4);
   2065 		if (dist < bestdist)
   2066 		{
   2067 			bestdist = dist;
   2068 			VectorCopy(v1, beststart1);
   2069 			VectorCopy(v1, beststart2);
   2070 			VectorCopy(v4, bestend1);
   2071 			VectorCopy(v4, bestend2);
   2072 		} //end if
   2073 		dist = VectorDistance(v2, v3);
   2074 		if (dist < bestdist)
   2075 		{
   2076 			bestdist = dist;
   2077 			VectorCopy(v2, beststart1);
   2078 			VectorCopy(v2, beststart2);
   2079 			VectorCopy(v3, bestend1);
   2080 			VectorCopy(v3, bestend2);
   2081 		} //end if
   2082 		dist = VectorDistance(v2, v4);
   2083 		if (dist < bestdist)
   2084 		{
   2085 			bestdist = dist;
   2086 			VectorCopy(v2, beststart1);
   2087 			VectorCopy(v2, beststart2);
   2088 			VectorCopy(v4, bestend1);
   2089 			VectorCopy(v4, bestend2);
   2090 		} //end if
   2091 	} //end if
   2092 	return bestdist;
   2093 } //end of the function AAS_ClosestEdgePoints
   2094 //===========================================================================
   2095 // creates possible jump reachabilities between the areas
   2096 //
   2097 // The two closest points on the ground of the areas are calculated
   2098 // One of the points will be on an edge of a ground face of area1 and
   2099 // one on an edge of a ground face of area2.
   2100 // If there is a range of closest points the point in the middle of this range
   2101 // is selected.
   2102 // Between these two points there must be one or more gaps.
   2103 // If the gaps exist a potential jump is predicted.
   2104 //
   2105 // Parameter:				-
   2106 // Returns:					-
   2107 // Changes Globals:		-
   2108 //===========================================================================
   2109 int AAS_Reachability_Jump(int area1num, int area2num)
   2110 {
   2111 	int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype;
   2112 	int stopevent, areas[10], numareas;
   2113 	float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed;
   2114 	vec_t *v1, *v2, *v3, *v4;
   2115 	vec3_t beststart, beststart2, bestend, bestend2;
   2116 	vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards;
   2117 	aas_area_t *area1, *area2;
   2118 	aas_face_t *face1, *face2;
   2119 	aas_edge_t *edge1, *edge2;
   2120 	aas_plane_t *plane1, *plane2, *plane;
   2121 	aas_trace_t trace;
   2122 	aas_clientmove_t move;
   2123 	aas_lreachability_t *lreach;
   2124 
   2125 	if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
   2126 	//cannot jump from or to a crouch area
   2127 	if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse;
   2128 	//
   2129 	area1 = &aasworld.areas[area1num];
   2130 	area2 = &aasworld.areas[area2num];
   2131 	//
   2132 	phys_jumpvel = aassettings.phys_jumpvel;
   2133 	//maximum distance a player can jump
   2134 	maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel);
   2135 	//maximum height a player can jump with the given initial z velocity
   2136 	maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
   2137 
   2138 	//if the areas are not near anough in the x-y direction
   2139 	for (i = 0; i < 2; i++)
   2140 	{
   2141 		if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse;
   2142 		if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse;
   2143 	} //end for
   2144 	//if area2 is way to high to jump up to
   2145 	if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse;
   2146 	//
   2147 	bestdist = 999999;
   2148 	//
   2149 	for (i = 0; i < area1->numfaces; i++)
   2150 	{
   2151 		face1num = aasworld.faceindex[area1->firstface + i];
   2152 		face1 = &aasworld.faces[abs(face1num)];
   2153 		//if not a ground face
   2154 		if (!(face1->faceflags & FACE_GROUND)) continue;
   2155 		//
   2156 		for (j = 0; j < area2->numfaces; j++)
   2157 		{
   2158 			face2num = aasworld.faceindex[area2->firstface + j];
   2159 			face2 = &aasworld.faces[abs(face2num)];
   2160 			//if not a ground face
   2161 			if (!(face2->faceflags & FACE_GROUND)) continue;
   2162 			//
   2163 			for (k = 0; k < face1->numedges; k++)
   2164 			{
   2165 				edge1num = abs(aasworld.edgeindex[face1->firstedge + k]);
   2166 				edge1 = &aasworld.edges[edge1num];
   2167 				for (l = 0; l < face2->numedges; l++)
   2168 				{
   2169 					edge2num = abs(aasworld.edgeindex[face2->firstedge + l]);
   2170 					edge2 = &aasworld.edges[edge2num];
   2171 					//calculate the minimum distance between the two edges
   2172 					v1 = aasworld.vertexes[edge1->v[0]];
   2173 					v2 = aasworld.vertexes[edge1->v[1]];
   2174 					v3 = aasworld.vertexes[edge2->v[0]];
   2175 					v4 = aasworld.vertexes[edge2->v[1]];
   2176 					//get the ground planes
   2177 					plane1 = &aasworld.planes[face1->planenum];
   2178 					plane2 = &aasworld.planes[face2->planenum];
   2179 					//
   2180 					bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2,
   2181 														beststart, bestend,
   2182 														beststart2, bestend2, bestdist);
   2183 				} //end for
   2184 			} //end for
   2185 		} //end for
   2186 	} //end for
   2187 	VectorMiddle(beststart, beststart2, beststart);
   2188 	VectorMiddle(bestend, bestend2, bestend);
   2189 	if (bestdist > 4 && bestdist < maxjumpdistance)
   2190 	{
   2191 //		Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist);
   2192 		// if very close and almost no height difference then the bot can walk
   2193 		if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8)
   2194 		{
   2195 			speed = 400;
   2196 			traveltype = TRAVEL_WALKOFFLEDGE;
   2197 		} //end if
   2198 		else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed))
   2199 		{
   2200 			//FIXME: why multiply with 1.2???
   2201 			speed *= 1.2f;
   2202 			traveltype = TRAVEL_WALKOFFLEDGE;
   2203 		} //end else if
   2204 		else
   2205 		{
   2206 			//get the horizontal speed for the jump, if it isn't possible to calculate this
   2207 			//speed (the jump is not possible) then there's no jump reachability created
   2208 			if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed))
   2209 				return qfalse;
   2210 			speed *= 1.05f;
   2211 			traveltype = TRAVEL_JUMP;
   2212 			//
   2213 			//NOTE: test if the horizontal distance isn't too small
   2214 			VectorSubtract(bestend, beststart, dir);
   2215 			dir[2] = 0;
   2216 			if (VectorLength(dir) < 10)
   2217 				return qfalse;
   2218 		} //end if
   2219 		//
   2220 		VectorSubtract(bestend, beststart, dir);
   2221 		VectorNormalize(dir);
   2222 		VectorMA(beststart, 1, dir, teststart);
   2223 		//
   2224 		VectorCopy(teststart, testend);
   2225 		testend[2] -= 100;
   2226 		trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
   2227 		//
   2228 		if (trace.startsolid)
   2229 			return qfalse;
   2230 		if (trace.fraction < 1)
   2231 		{
   2232 			plane = &aasworld.planes[trace.planenum];
   2233 			// if the bot can stand on the surface
   2234 			if (DotProduct(plane->normal, up) >= 0.7)
   2235 			{
   2236 				// if no lava or slime below
   2237 				if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
   2238 				{
   2239 					if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
   2240 						return qfalse;
   2241 				} //end if
   2242 			} //end if
   2243 		} //end if
   2244 		//
   2245 		VectorMA(bestend, -1, dir, teststart);
   2246 		//
   2247 		VectorCopy(teststart, testend);
   2248 		testend[2] -= 100;
   2249 		trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
   2250 		//
   2251 		if (trace.startsolid)
   2252 			return qfalse;
   2253 		if (trace.fraction < 1)
   2254 		{
   2255 			plane = &aasworld.planes[trace.planenum];
   2256 			// if the bot can stand on the surface
   2257 			if (DotProduct(plane->normal, up) >= 0.7)
   2258 			{
   2259 				// if no lava or slime below
   2260 				if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
   2261 				{
   2262 					if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
   2263 						return qfalse;
   2264 				} //end if
   2265 			} //end if
   2266 		} //end if
   2267 		//
   2268 		// get command movement
   2269 		VectorClear(cmdmove);
   2270 		if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
   2271 			cmdmove[2] = aassettings.phys_jumpvel;
   2272 		else
   2273 			cmdmove[2] = 0;
   2274 		//
   2275 		VectorSubtract(bestend, beststart, dir);
   2276 		dir[2] = 0;
   2277 		VectorNormalize(dir);
   2278 		CrossProduct(dir, up, sidewards);
   2279 		//
   2280 		stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE;
   2281 		if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num))
   2282 			stopevent |= SE_TOUCHCLUSTERPORTAL;
   2283 		//
   2284 		for (i = 0; i < 3; i++)
   2285 		{
   2286 			//
   2287 			if (i == 1)
   2288 				VectorAdd(testend, sidewards, testend);
   2289 			else if (i == 2)
   2290 				VectorSubtract(bestend, sidewards, testend);
   2291 			else
   2292 				VectorCopy(bestend, testend);
   2293 			VectorSubtract(testend, beststart, dir);
   2294 			dir[2] = 0;
   2295 			VectorNormalize(dir);
   2296 			VectorScale(dir, speed, velocity);
   2297 			//
   2298 			AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue,
   2299 										velocity, cmdmove, 3, 30, 0.1f,
   2300 										stopevent, 0, qfalse);
   2301 			// if prediction time wasn't enough to fully predict the movement
   2302 			if (move.frames >= 30)
   2303 				return qfalse;
   2304 			// don't enter slime or lava and don't fall from too high
   2305 			if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
   2306 				return qfalse;
   2307 			// never jump or fall through a cluster portal
   2308 			if (move.stopevent & SE_TOUCHCLUSTERPORTAL)
   2309 				return qfalse;
   2310 			//the end position should be in area2, also test a little bit back
   2311 			//because the predicted jump could have rushed through the area
   2312 			VectorMA(move.endpos, -64, dir, teststart);
   2313 			teststart[2] += 1;
   2314 			numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int));
   2315 			for (j = 0; j < numareas; j++)
   2316 			{
   2317 				if (areas[j] == area2num)
   2318 					break;
   2319 			} //end for
   2320 			if (j < numareas)
   2321 				break;
   2322 		}
   2323 		if (i >= 3)
   2324 			return qfalse;
   2325 		//
   2326 #ifdef REACH_DEBUG
   2327 		//create the reachability
   2328 		Log_Write("jump reachability between %d and %d\r\n", area1num, area2num);
   2329 #endif //REACH_DEBUG
   2330 		//create a new reachability link
   2331 		lreach = AAS_AllocReachability();
   2332 		if (!lreach) return qfalse;
   2333 		lreach->areanum = area2num;
   2334 		lreach->facenum = 0;
   2335 		lreach->edgenum = 0;
   2336 		VectorCopy(beststart, lreach->start);
   2337 		VectorCopy(bestend, lreach->end);
   2338 		lreach->traveltype = traveltype;
   2339 
   2340 		VectorSubtract(bestend, beststart, dir);
   2341 		height = dir[2];
   2342 		dir[2] = 0;
   2343 		if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir))
   2344 		{
   2345 			lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity;
   2346 		}
   2347 		else
   2348 		{
   2349 			lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity;
   2350 		} //end if
   2351 		//
   2352 		if (!AAS_AreaJumpPad(area2num))
   2353 		{
   2354 			if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5)
   2355 			{
   2356 				lreach->traveltime += aassettings.rs_falldamage5;
   2357 			} //end if
   2358 			else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10)
   2359 			{
   2360 				lreach->traveltime += aassettings.rs_falldamage10;
   2361 			} //end if
   2362 		} //end if
   2363 		lreach->next = areareachability[area1num];
   2364 		areareachability[area1num] = lreach;
   2365 		//
   2366 		if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
   2367 			reach_jump++;
   2368 		else
   2369 			reach_walkoffledge++;
   2370 	} //end if
   2371 	return qfalse;
   2372 } //end of the function AAS_Reachability_Jump
   2373 //===========================================================================
   2374 // create a possible ladder reachability from area1 to area2
   2375 //
   2376 // Parameter:				-
   2377 // Returns:					-
   2378 // Changes Globals:		-
   2379 //===========================================================================
   2380 int AAS_Reachability_Ladder(int area1num, int area2num)
   2381 {
   2382 	int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum;
   2383 	int face1num, face2num, ladderface1num, ladderface2num;
   2384 	int ladderface1vertical, ladderface2vertical, firstv;
   2385 	float face1area, face2area, bestface1area, bestface2area;
   2386 	float phys_jumpvel, maxjumpheight;
   2387 	vec3_t area1point, area2point, v1, v2, up = {0, 0, 1};
   2388 	vec3_t mid, lowestpoint, start, end, sharededgevec, dir;
   2389 	aas_area_t *area1, *area2;
   2390 	aas_face_t *face1, *face2, *ladderface1, *ladderface2;
   2391 	aas_plane_t *plane1, *plane2;
   2392 	aas_edge_t *sharededge, *edge1;
   2393 	aas_lreachability_t *lreach;
   2394 	aas_trace_t trace;
   2395 
   2396 	if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse;
   2397 	//
   2398 	phys_jumpvel = aassettings.phys_jumpvel;
   2399 	//maximum height a player can jump with the given initial z velocity
   2400 	maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
   2401 
   2402 	area1 = &aasworld.areas[area1num];
   2403 	area2 = &aasworld.areas[area2num];
   2404 	//
   2405 	ladderface1 = NULL;
   2406 	ladderface2 = NULL;
   2407 	ladderface1num = 0; //make compiler happy
   2408 	ladderface2num = 0; //make compiler happy
   2409 	bestface1area = -9999;
   2410 	bestface2area = -9999;
   2411 	sharededgenum = 0; //make compiler happy
   2412 	lowestedgenum = 0; //make compiler happy
   2413 	//
   2414 	for (i = 0; i < area1->numfaces; i++)
   2415 	{
   2416 		face1num = aasworld.faceindex[area1->firstface + i];
   2417 		face1 = &aasworld.faces[abs(face1num)];
   2418 		//if not a ladder face
   2419 		if (!(face1->faceflags & FACE_LADDER)) continue;
   2420 		//
   2421 		for (j = 0; j < area2->numfaces; j++)
   2422 		{
   2423 			face2num = aasworld.faceindex[area2->firstface + j];
   2424 			face2 = &aasworld.faces[abs(face2num)];
   2425 			//if not a ladder face
   2426 			if (!(face2->faceflags & FACE_LADDER)) continue;
   2427 			//check if the faces share an edge
   2428 			for (k = 0; k < face1->numedges; k++)
   2429 			{
   2430 				edge1num = aasworld.edgeindex[face1->firstedge + k];
   2431 				for (l = 0; l < face2->numedges; l++)
   2432 				{
   2433 					edge2num = aasworld.edgeindex[face2->firstedge + l];
   2434 					if (abs(edge1num) == abs(edge2num))
   2435 					{
   2436 						//get the face with the largest area
   2437 						face1area = AAS_FaceArea(face1);
   2438 						face2area = AAS_FaceArea(face2);
   2439 						if (face1area > bestface1area && face2area > bestface2area)
   2440 						{
   2441 							bestface1area = face1area;
   2442 							bestface2area = face2area;
   2443 							ladderface1 = face1;
   2444 							ladderface2 = face2;
   2445 							ladderface1num = face1num;
   2446 							ladderface2num = face2num;
   2447 							sharededgenum = edge1num;
   2448 						} //end if
   2449 						break;
   2450 					} //end if
   2451 				} //end for
   2452 				if (l != face2->numedges) break;
   2453 			} //end for
   2454 		} //end for
   2455 	} //end for
   2456 	//
   2457 	if (ladderface1 && ladderface2)
   2458 	{
   2459 		//get the middle of the shared edge
   2460 		sharededge = &aasworld.edges[abs(sharededgenum)];
   2461 		firstv = sharededgenum < 0;
   2462 		//
   2463 		VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1);
   2464 		VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2);
   2465 		VectorAdd(v1, v2, area1point);
   2466 		VectorScale(area1point, 0.5, area1point);
   2467 		VectorCopy(area1point, area2point);
   2468 		//
   2469 		//if the face plane in area 1 is pretty much vertical
   2470 		plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)];
   2471 		plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)];
   2472 		//
   2473 		//get the points really into the areas
   2474 		VectorSubtract(v2, v1, sharededgevec);
   2475 		CrossProduct(plane1->normal, sharededgevec, dir);
   2476 		VectorNormalize(dir);
   2477 		//NOTE: 32 because that's larger than 16 (bot bbox x,y)
   2478 		VectorMA(area1point, -32, dir, area1point);
   2479 		VectorMA(area2point, 32, dir, area2point);
   2480 		//
   2481 		ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1;
   2482 		ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1;
   2483 		//there's only reachability between vertical ladder faces
   2484 		if (!ladderface1vertical && !ladderface2vertical) return qfalse;
   2485 		//if both vertical ladder faces
   2486 		if (ladderface1vertical && ladderface2vertical
   2487 					//and the ladder faces do not make a sharp corner
   2488 					&& DotProduct(plane1->normal, plane2->normal) > 0.7
   2489 					//and the shared edge is not too vertical
   2490 					&& abs(DotProduct(sharededgevec, up)) < 0.7)
   2491 		{
   2492 			//create a new reachability link
   2493 			lreach = AAS_AllocReachability();
   2494 			if (!lreach) return qfalse;
   2495 			lreach->areanum = area2num;
   2496 			lreach->facenum = ladderface1num;
   2497 			lreach->edgenum = abs(sharededgenum);
   2498 			VectorCopy(area1point, lreach->start);
   2499 			//VectorCopy(area2point, lreach->end);
   2500 			VectorMA(area2point, -3, plane1->normal, lreach->end);
   2501 			lreach->traveltype = TRAVEL_LADDER;
   2502 			lreach->traveltime = 10;
   2503 			lreach->next = areareachability[area1num];
   2504 			areareachability[area1num] = lreach;
   2505 			//
   2506 			reach_ladder++;
   2507 			//create a new reachability link
   2508 			lreach = AAS_AllocReachability();
   2509 			if (!lreach) return qfalse;
   2510 			lreach->areanum = area1num;
   2511 			lreach->facenum = ladderface2num;
   2512 			lreach->edgenum = abs(sharededgenum);
   2513 			VectorCopy(area2point, lreach->start);
   2514 			//VectorCopy(area1point, lreach->end);
   2515 			VectorMA(area1point, -3, plane1->normal, lreach->end);
   2516 			lreach->traveltype = TRAVEL_LADDER;
   2517 			lreach->traveltime = 10;
   2518 			lreach->next = areareachability[area2num];
   2519 			areareachability[area2num] = lreach;
   2520 			//
   2521 			reach_ladder++;
   2522 			//
   2523 			return qtrue;
   2524 		} //end if
   2525 		//if the second ladder face is also a ground face
   2526 		//create ladder end (just ladder) reachability and
   2527 		//walk off a ladder (ledge) reachability
   2528 		if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND))
   2529 		{
   2530 			//create a new reachability link
   2531 			lreach = AAS_AllocReachability();
   2532 			if (!lreach) return qfalse;
   2533 			lreach->areanum = area2num;
   2534 			lreach->facenum = ladderface1num;
   2535 			lreach->edgenum = abs(sharededgenum);
   2536 			VectorCopy(area1point, lreach->start);
   2537 			VectorCopy(area2point, lreach->end);
   2538 			lreach->end[2] += 16;
   2539 			VectorMA(lreach->end, -15, plane1->normal, lreach->end);
   2540 			lreach->traveltype = TRAVEL_LADDER;
   2541 			lreach->traveltime = 10;
   2542 			lreach->next = areareachability[area1num];
   2543 			areareachability[area1num] = lreach;
   2544 			//
   2545 			reach_ladder++;
   2546 			//create a new reachability link
   2547 			lreach = AAS_AllocReachability();
   2548 			if (!lreach) return qfalse;
   2549 			lreach->areanum = area1num;
   2550 			lreach->facenum = ladderface2num;
   2551 			lreach->edgenum = abs(sharededgenum);
   2552 			VectorCopy(area2point, lreach->start);
   2553 			VectorCopy(area1point, lreach->end);
   2554 			lreach->traveltype = TRAVEL_WALKOFFLEDGE;
   2555 			lreach->traveltime = 10;
   2556 			lreach->next = areareachability[area2num];
   2557 			areareachability[area2num] = lreach;
   2558 			//
   2559 			reach_walkoffledge++;
   2560 			//
   2561 			return qtrue;
   2562 		} //end if
   2563 		//
   2564 		if (ladderface1vertical)
   2565 		{
   2566 			//find lowest edge of the ladder face
   2567 			lowestpoint[2] = 99999;
   2568 			for (i = 0; i < ladderface1->numedges; i++)
   2569 			{
   2570 				edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]);
   2571 				edge1 = &aasworld.edges[edge1num];
   2572 				//
   2573 				VectorCopy(aasworld.vertexes[edge1->v[0]], v1);
   2574 				VectorCopy(aasworld.vertexes[edge1->v[1]], v2);
   2575 				//
   2576 				VectorAdd(v1, v2, mid);
   2577 				VectorScale(mid, 0.5, mid);
   2578 				//
   2579 				if (mid[2] < lowestpoint[2])
   2580 				{
   2581 					VectorCopy(mid, lowestpoint);
   2582 					lowestedgenum = edge1num;
   2583 				} //end if
   2584 			} //end for
   2585 			//
   2586 			plane1 = &aasworld.planes[ladderface1->planenum];
   2587 			//trace down in the middle of this edge
   2588 			VectorMA(lowestpoint, 5, plane1->normal, start);
   2589 			VectorCopy(start, end);
   2590 			start[2] += 5;
   2591 			end[2] -= 100;
   2592 			//trace without entity collision
   2593 			trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
   2594 			//
   2595 			//
   2596 #ifdef REACH_DEBUG
   2597 			if (trace.startsolid)
   2598 			{
   2599 				Log_Write("trace from area %d started in solid\r\n", area1num);
   2600 			} //end if
   2601 #endif //REACH_DEBUG
   2602 			//
   2603 			trace.endpos[2] += 1;
   2604 			area2num = AAS_PointAreaNum(trace.endpos);
   2605 			//
   2606 			area2 = &aasworld.areas[area2num];
   2607 			for (i = 0; i < area2->numfaces; i++)
   2608 			{
   2609 				face2num = aasworld.faceindex[area2->firstface + i];
   2610 				face2 = &aasworld.faces[abs(face2num)];
   2611 				//
   2612 				if (face2->faceflags & FACE_LADDER)
   2613 				{
   2614 					plane2 = &aasworld.planes[face2->planenum];
   2615 					if (abs(DotProduct(plane2->normal, up)) < 0.1) break;
   2616 				} //end if
   2617 			} //end for
   2618 			//if from another area without vertical ladder faces
   2619 			if (i >= area2->numfaces && area2num != area1num &&
   2620 						//the reachabilities shouldn't exist already
   2621 						!AAS_ReachabilityExists(area1num, area2num) &&
   2622 						!AAS_ReachabilityExists(area2num, area1num))
   2623 			{
   2624 				//if the height is jumpable
   2625 				if (start[2] - trace.endpos[2] < maxjumpheight)
   2626 				{
   2627 					//create a new reachability link
   2628 					lreach = AAS_AllocReachability();
   2629 					if (!lreach) return qfalse;
   2630 					lreach->areanum = area2num;
   2631 					lreach->facenum = ladderface1num;
   2632 					lreach->edgenum = lowestedgenum;
   2633 					VectorCopy(lowestpoint, lreach->start);
   2634 					VectorCopy(trace.endpos, lreach->end);
   2635 					lreach->traveltype = TRAVEL_LADDER;
   2636 					lreach->traveltime = 10;
   2637 					lreach->next = areareachability[area1num];
   2638 					areareachability[area1num] = lreach;
   2639 					//
   2640 					reach_ladder++;
   2641 					//create a new reachability link
   2642 					lreach = AAS_AllocReachability();
   2643 					if (!lreach) return qfalse;
   2644 					lreach->areanum = area1num;
   2645 					lreach->facenum = ladderface1num;
   2646 					lreach->edgenum = lowestedgenum;
   2647 					VectorCopy(trace.endpos, lreach->start);
   2648 					//get the end point a little bit into the ladder
   2649 					VectorMA(lowestpoint, -5, plane1->normal, lreach->end);
   2650 					//get the end point a little higher
   2651 					lreach->end[2] += 10;
   2652 					lreach->traveltype = TRAVEL_JUMP;
   2653 					lreach->traveltime = 10;
   2654 					lreach->next = areareachability[area2num];
   2655 					areareachability[area2num] = lreach;
   2656 					//
   2657 					reach_jump++;	
   2658 					//
   2659 					return qtrue;
   2660 #ifdef REACH_DEBUG
   2661 					Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num);
   2662 #endif //REACH_DEBUG
   2663 				} //end if
   2664 #ifdef REACH_DEBUG
   2665 				else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num);
   2666 #endif //REACH_DEBUG
   2667 			} //end if
   2668 			/*//if slime or lava below the ladder
   2669 			//try jump reachability from far towards the ladder
   2670 			if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME
   2671 													| AREACONTENTS_LAVA))
   2672 			{
   2673 				for (i = 20; i <= 120; i += 20)
   2674 				{
   2675 					//trace down in the middle of this edge
   2676 					VectorMA(lowestpoint, i, plane1->normal, start);
   2677 					VectorCopy(start, end);
   2678 					start[2] += 5;
   2679 					end[2] -= 100;
   2680 					//trace without entity collision
   2681 					trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
   2682 					//
   2683 					if (trace.startsolid) break;
   2684 					trace.endpos[2] += 1;
   2685 					area2num = AAS_PointAreaNum(trace.endpos);
   2686 					if (area2num == area1num) continue;
   2687 					//
   2688 					if (start[2] - trace.endpos[2] > maxjumpheight) continue;
   2689 					if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME
   2690 												| AREACONTENTS_LAVA)) continue;
   2691 					//
   2692 					//create a new reachability link
   2693 					lreach = AAS_AllocReachability();
   2694 					if (!lreach) return qfalse;
   2695 					lreach->areanum = area1num;
   2696 					lreach->facenum = ladderface1num;
   2697 					lreach->edgenum = lowestedgenum;
   2698 					VectorCopy(trace.endpos, lreach->start);
   2699 					VectorCopy(lowestpoint, lreach->end);
   2700 					lreach->end[2] += 5;
   2701 					lreach->traveltype = TRAVEL_JUMP;
   2702 					lreach->traveltime = 10;
   2703 					lreach->next = areareachability[area2num];
   2704 					areareachability[area2num] = lreach;
   2705 					//
   2706 					reach_jump++;
   2707 					//
   2708 					Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num);
   2709 					//
   2710 					break;
   2711 				} //end for
   2712 			} //end if*/
   2713 		} //end if
   2714 	} //end if
   2715 	return qfalse;
   2716 } //end of the function AAS_Reachability_Ladder
   2717 //===========================================================================
   2718 //
   2719 // Parameter:			-
   2720 // Returns:				-
   2721 // Changes Globals:		-
   2722 //===========================================================================
   2723 int AAS_TravelFlagsForTeam(int ent)
   2724 {
   2725 	int notteam;
   2726 
   2727 	if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", &notteam))
   2728 		return 0;
   2729 	if (notteam == 1)
   2730 		return TRAVELFLAG_NOTTEAM1;
   2731 	if (notteam == 2)
   2732 		return TRAVELFLAG_NOTTEAM2;
   2733 	return 0;
   2734 } //end of the function AAS_TravelFlagsForTeam
   2735 //===========================================================================
   2736 // create possible teleporter reachabilities
   2737 // this is very game dependent.... :(
   2738 //
   2739 // classname = trigger_multiple or trigger_teleport
   2740 // target = "t1"
   2741 //
   2742 // classname = target_teleporter
   2743 // targetname = "t1"
   2744 // target = "t2"
   2745 //
   2746 // classname = misc_teleporter_dest
   2747 // targetname = "t2"
   2748 //
   2749 // Parameter:				-
   2750 // Returns:					-
   2751 // Changes Globals:		-
   2752 //===========================================================================
   2753 void AAS_Reachability_Teleport(void)
   2754 {
   2755 	int area1num, area2num;
   2756 	char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
   2757 	char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
   2758 	int ent, dest;
   2759 	float angle;
   2760 	vec3_t origin, destorigin, mins, maxs, end, angles;
   2761 	vec3_t mid, velocity, cmdmove;
   2762 	aas_lreachability_t *lreach;
   2763 	aas_clientmove_t move;
   2764 	aas_trace_t trace;
   2765 	aas_link_t *areas, *link;
   2766 
   2767 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
   2768 	{
   2769 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
   2770 		if (!strcmp(classname, "trigger_multiple"))
   2771 		{
   2772 			AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
   2773 //#ifdef REACH_DEBUG
   2774 			botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model);
   2775 //#endif REACH_DEBUG
   2776 			VectorClear(angles);
   2777 			AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
   2778 			//
   2779 			if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
   2780 			{
   2781 				botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n",
   2782 									origin[0], origin[1], origin[2]);
   2783 				continue;
   2784 			} //end if
   2785 			for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
   2786 			{
   2787 				if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue;
   2788 				if (!strcmp(classname, "target_teleporter"))
   2789 				{
   2790 					if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue;
   2791 					if (!strcmp(targetname, target))
   2792 					{
   2793 						break;
   2794 					} //end if
   2795 				} //end if
   2796 			} //end for
   2797 			if (!dest)
   2798 			{
   2799 				continue;
   2800 			} //end if
   2801 			if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY))
   2802 			{
   2803 				botimport.Print(PRT_ERROR, "target_teleporter without target\n");
   2804 				continue;
   2805 			} //end if
   2806 		} //end else
   2807 		else if (!strcmp(classname, "trigger_teleport"))
   2808 		{
   2809 			AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
   2810 //#ifdef REACH_DEBUG
   2811 			botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model);
   2812 //#endif REACH_DEBUG
   2813 			VectorClear(angles);
   2814 			AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
   2815 			//
   2816 			if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
   2817 			{
   2818 				botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n",
   2819 									origin[0], origin[1], origin[2]);
   2820 				continue;
   2821 			} //end if
   2822 		} //end if
   2823 		else
   2824 		{
   2825 			continue;
   2826 		} //end else
   2827 		//
   2828 		for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
   2829 		{
   2830 			//classname should be misc_teleporter_dest
   2831 			//but I've also seen target_position and actually any
   2832 			//entity could be used... burp
   2833 			if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY))
   2834 			{
   2835 				if (!strcmp(targetname, target))
   2836 				{
   2837 					break;
   2838 				} //end if
   2839 			} //end if
   2840 		} //end for
   2841 		if (!dest)
   2842 		{
   2843 			botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target);
   2844 			continue;
   2845 		} //end if
   2846 		if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin))
   2847 		{
   2848 			botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target);
   2849 			continue;
   2850 		} //end if
   2851 		//
   2852 		area2num = AAS_PointAreaNum(destorigin);
   2853 		//if not teleported into a teleporter or into a jumppad
   2854 		if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num))
   2855 		{
   2856 			VectorCopy(destorigin, end);
   2857 			end[2] -= 64;
   2858 			trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);
   2859 			if (trace.startsolid)
   2860 			{
   2861 				botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target);
   2862 				continue;
   2863 			} //end if
   2864 			area2num = AAS_PointAreaNum(trace.endpos);
   2865 			//
   2866 			/*
   2867 			if (!AAS_AreaTeleporter(area2num) &&
   2868 				!AAS_AreaJumpPad(area2num) &&
   2869 				!AAS_AreaGrounded(area2num))
   2870 			{
   2871 				VectorCopy(trace.endpos, destorigin);
   2872 			}
   2873 			else*/
   2874 			{
   2875 				//predict where you'll end up
   2876 				AAS_FloatForBSPEpairKey(dest, "angle", &angle);
   2877 				if (angle)
   2878 				{
   2879 					VectorSet(angles, 0, angle, 0);
   2880 					AngleVectors(angles, velocity, NULL, NULL);
   2881 					VectorScale(velocity, 400, velocity);
   2882 				} //end if
   2883 				else
   2884 				{
   2885 					VectorClear(velocity);
   2886 				} //end else
   2887 				VectorClear(cmdmove);
   2888 				AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse,
   2889 										velocity, cmdmove, 0, 30, 0.1f,
   2890 										SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
   2891 										SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue);
   2892 				area2num = AAS_PointAreaNum(move.endpos);
   2893 				if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
   2894 				{
   2895 					botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target);
   2896 				} //end if
   2897 				VectorCopy(move.endpos, destorigin);
   2898 			} //end else
   2899 		} //end if
   2900 		//
   2901 		//botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]);
   2902 		//botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]);
   2903 		//botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]);
   2904 		VectorAdd(origin, mins, mins);
   2905 		VectorAdd(origin, maxs, maxs);
   2906 		//
   2907 		VectorAdd(mins, maxs, mid);
   2908 		VectorScale(mid, 0.5, mid);
   2909 		//link an invalid (-1) entity
   2910 		areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH);
   2911 		if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n");
   2912 		//
   2913 		for (link = areas; link; link = link->next_area)
   2914 		{
   2915 			//if (!AAS_AreaGrounded(link->areanum)) continue;
   2916 			if (!AAS_AreaTeleporter(link->areanum)) continue;
   2917 			//
   2918 			area1num = link->areanum;
   2919 			//create a new reachability link
   2920 			lreach = AAS_AllocReachability();
   2921 			if (!lreach) break;
   2922 			lreach->areanum = area2num;
   2923 			lreach->facenum = 0;
   2924 			lreach->edgenum = 0;
   2925 			VectorCopy(mid, lreach->start);
   2926 			VectorCopy(destorigin, lreach->end);
   2927 			lreach->traveltype = TRAVEL_TELEPORT;
   2928 			lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
   2929 			lreach->traveltime = aassettings.rs_teleport;
   2930 			lreach->next = areareachability[area1num];
   2931 			areareachability[area1num] = lreach;
   2932 			//
   2933 			reach_teleport++;
   2934 		} //end for
   2935 		//unlink the invalid entity
   2936 		AAS_UnlinkFromAreas(areas);
   2937 	} //end for
   2938 } //end of the function AAS_Reachability_Teleport
   2939 //===========================================================================
   2940 // create possible elevator (func_plat) reachabilities
   2941 // this is very game dependent.... :(
   2942 //
   2943 // Parameter:			-
   2944 // Returns:				-
   2945 // Changes Globals:		-
   2946 //===========================================================================
   2947 void AAS_Reachability_Elevator(void)
   2948 {
   2949 	int area1num, area2num, modelnum, i, j, k, l, n, p;
   2950 	float lip, height, speed;
   2951 	char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY];
   2952 	int ent;
   2953 	vec3_t mins, maxs, origin, angles = {0, 0, 0};
   2954 	vec3_t pos1, pos2, mids, platbottom, plattop;
   2955 	vec3_t bottomorg, toporg, start, end, dir;
   2956 	vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8];
   2957 	aas_lreachability_t *lreach;
   2958 	aas_trace_t trace;
   2959 
   2960 #ifdef REACH_DEBUG
   2961 	Log_Write("AAS_Reachability_Elevator\r\n");
   2962 #endif //REACH_DEBUG
   2963 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
   2964 	{
   2965 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
   2966 		if (!strcmp(classname, "func_plat"))
   2967 		{
   2968 #ifdef REACH_DEBUG
   2969 			Log_Write("found func plat\r\n");
   2970 #endif //REACH_DEBUG
   2971 			if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
   2972 			{
   2973 				botimport.Print(PRT_ERROR, "func_plat without model\n");
   2974 				continue;
   2975 			} //end if
   2976 			//get the model number, and skip the leading *
   2977 			modelnum = atoi(model+1);
   2978 			if (modelnum <= 0)
   2979 			{
   2980 				botimport.Print(PRT_ERROR, "func_plat with invalid model number\n");
   2981 				continue;
   2982 			} //end if
   2983 			//get the mins, maxs and origin of the model
   2984 			//NOTE: the origin is usually (0,0,0) and the mins and maxs
   2985 			//      are the absolute mins and maxs
   2986 			AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
   2987 			//
   2988 			AAS_VectorForBSPEpairKey(ent, "origin", origin);
   2989 			//pos1 is the top position, pos2 is the bottom
   2990 			VectorCopy(origin, pos1);
   2991 			VectorCopy(origin, pos2);
   2992 			//get the lip of the plat
   2993 			AAS_FloatForBSPEpairKey(ent, "lip", &lip);
   2994 			if (!lip) lip = 8;
   2995 			//get the movement height of the plat
   2996 			AAS_FloatForBSPEpairKey(ent, "height", &height);
   2997 			if (!height) height = (maxs[2] - mins[2]) - lip;
   2998 			//get the speed of the plat
   2999 			AAS_FloatForBSPEpairKey(ent, "speed", &speed);
   3000 			if (!speed) speed = 200;
   3001 			//get bottom position below pos1
   3002 			pos2[2] -= height;
   3003 			//
   3004 			//get a point just above the plat in the bottom position
   3005 			VectorAdd(mins, maxs, mids);
   3006 			VectorMA(pos2, 0.5, mids, platbottom);
   3007 			platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2;
   3008 			//get a point just above the plat in the top position
   3009 			VectorAdd(mins, maxs, mids);
   3010 			VectorMA(pos2, 0.5, mids, plattop);
   3011 			plattop[2] = maxs[2] + 2;
   3012 			//
   3013 			/*if (!area1num)
   3014 			{
   3015 				Log_Write("no grounded area near plat bottom\r\n");
   3016 				continue;
   3017 			} //end if*/
   3018 			//get the mins and maxs a little larger
   3019 			for (i = 0; i < 3; i++)
   3020 			{
   3021 				mins[i] -= 1;
   3022 				maxs[i] += 1;
   3023 			} //end for
   3024 			//
   3025 			//botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]);
   3026 			//
   3027 			VectorAdd(mins, maxs, mids);
   3028 			VectorScale(mids, 0.5, mids);
   3029 			//
   3030 			xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0];
   3031 			yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1];
   3032 			//
   3033 			xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0];
   3034 			yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1];
   3035 			//find adjacent areas around the bottom of the plat
   3036 			for (i = 0; i < 9; i++)
   3037 			{
   3038 				if (i < 8) //check at the sides of the plat
   3039 				{
   3040 					bottomorg[0] = origin[0] + xvals[i];
   3041 					bottomorg[1] = origin[1] + yvals[i];
   3042 					bottomorg[2] = platbottom[2] + 16;
   3043 					//get a grounded or swim area near the plat in the bottom position
   3044 					area1num = AAS_PointAreaNum(bottomorg);
   3045 					for (k = 0; k < 16; k++)
   3046 					{
   3047 						if (area1num)
   3048 						{
   3049 							if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break;
   3050 						} //end if
   3051 						bottomorg[2] += 4;
   3052 						area1num = AAS_PointAreaNum(bottomorg);
   3053 					} //end if
   3054 					//if in solid
   3055 					if (k >= 16)
   3056 					{
   3057 						continue;
   3058 					} //end if
   3059 				} //end if
   3060 				else //at the middle of the plat
   3061 				{
   3062 					VectorCopy(plattop, bottomorg);
   3063 					bottomorg[2] += 24;
   3064 					area1num = AAS_PointAreaNum(bottomorg);
   3065 					if (!area1num) continue;
   3066 					VectorCopy(platbottom, bottomorg);
   3067 					bottomorg[2] += 24;
   3068 				} //end else
   3069 				//look at adjacent areas around the top of the plat
   3070 				//make larger steps to outside the plat everytime
   3071 				for (n = 0; n < 3; n++)
   3072 				{
   3073 					for (k = 0; k < 3; k++)
   3074 					{
   3075 						mins[k] -= 4;
   3076 						maxs[k] += 4;
   3077 					} //end for
   3078 					xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0];
   3079 					yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1];
   3080 					//
   3081 					xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0];
   3082 					yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1];
   3083 					//
   3084 					for (j = 0; j < 8; j++)
   3085 					{
   3086 						toporg[0] = origin[0] + xvals_top[j];
   3087 						toporg[1] = origin[1] + yvals_top[j];
   3088 						toporg[2] = plattop[2] + 16;
   3089 						//get a grounded or swim area near the plat in the top position
   3090 						area2num = AAS_PointAreaNum(toporg);
   3091 						for (l = 0; l < 16; l++)
   3092 						{
   3093 							if (area2num)
   3094 							{
   3095 								if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num))
   3096 								{
   3097 									VectorCopy(plattop, start);
   3098 									start[2] += 32;
   3099 									VectorCopy(toporg, end);
   3100 									end[2] += 1;
   3101 									trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
   3102 									if (trace.fraction >= 1) break;
   3103 								} //end if
   3104 							} //end if
   3105 							toporg[2] += 4;
   3106 							area2num = AAS_PointAreaNum(toporg);
   3107 						} //end if
   3108 						//if in solid
   3109 						if (l >= 16) continue;
   3110 						//never create a reachability in the same area
   3111 						if (area2num == area1num) continue;
   3112 						//if the area isn't grounded
   3113 						if (!AAS_AreaGrounded(area2num)) continue;
   3114 						//if there already exists reachability between the areas
   3115 						if (AAS_ReachabilityExists(area1num, area2num)) continue;
   3116 						//if the reachability start is within the elevator bounding box
   3117 						VectorSubtract(bottomorg, platbottom, dir);
   3118 						VectorNormalize(dir);
   3119 						dir[0] = bottomorg[0] + 24 * dir[0];
   3120 						dir[1] = bottomorg[1] + 24 * dir[1];
   3121 						dir[2] = bottomorg[2];
   3122 						//
   3123 						for (p = 0; p < 3; p++)
   3124 							if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break;
   3125 						if (p >= 3) continue;
   3126 						//create a new reachability link
   3127 						lreach = AAS_AllocReachability();
   3128 						if (!lreach) continue;
   3129 						lreach->areanum = area2num;
   3130 						//the facenum is the model number
   3131 						lreach->facenum = modelnum;
   3132 						//the edgenum is the height
   3133 						lreach->edgenum = (int) height;
   3134 						//
   3135 						VectorCopy(dir, lreach->start);
   3136 						VectorCopy(toporg, lreach->end);
   3137 						lreach->traveltype = TRAVEL_ELEVATOR;
   3138 						lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
   3139 						lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed;
   3140 						lreach->next = areareachability[area1num];
   3141 						areareachability[area1num] = lreach;
   3142 						//don't go any further to the outside
   3143 						n = 9999;
   3144 						//
   3145 #ifdef REACH_DEBUG
   3146 						Log_Write("elevator reach from %d to %d\r\n", area1num, area2num);
   3147 #endif //REACH_DEBUG
   3148 						//
   3149 						reach_elevator++;
   3150 					} //end for
   3151 				} //end for
   3152 			} //end for
   3153 		} //end if
   3154 	} //end for
   3155 } //end of the function AAS_Reachability_Elevator
   3156 //===========================================================================
   3157 //
   3158 // Parameter:			-
   3159 // Returns:				-
   3160 // Changes Globals:		-
   3161 //===========================================================================
   3162 aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface)
   3163 {
   3164 	int i, j, k, l;
   3165 	int facenum, edgenum, bestfacenum;
   3166 	float *v1, *v2, *v3, *v4;
   3167 	float bestdist, speed, hordist, dist;
   3168 	vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint;
   3169 	aas_lreachability_t *lreach, *lreachabilities;
   3170 	aas_area_t *area;
   3171 	aas_face_t *face;
   3172 	aas_edge_t *edge;
   3173 	aas_plane_t *faceplane, *bestfaceplane;
   3174 
   3175 	//
   3176 	lreachabilities = NULL;
   3177 	bestfacenum = 0;
   3178 	bestfaceplane = NULL;
   3179 	//
   3180 	for (i = 1; i < aasworld.numareas; i++)
   3181 	{
   3182 		area = &aasworld.areas[i];
   3183 		// get the shortest distance between one of the func_bob start edges and
   3184 		// one of the face edges of area1
   3185 		bestdist = 999999;
   3186 		for (j = 0; j < area->numfaces; j++)
   3187 		{
   3188 			facenum = aasworld.faceindex[area->firstface + j];
   3189 			face = &aasworld.faces[abs(facenum)];
   3190 			//if not a ground face
   3191 			if (!(face->faceflags & FACE_GROUND)) continue;
   3192 			//get the ground planes
   3193 			faceplane = &aasworld.planes[face->planenum];
   3194 			//
   3195 			for (k = 0; k < face->numedges; k++)
   3196 			{
   3197 				edgenum = abs(aasworld.edgeindex[face->firstedge + k]);
   3198 				edge = &aasworld.edges[edgenum];
   3199 				//calculate the minimum distance between the two edges
   3200 				v1 = aasworld.vertexes[edge->v[0]];
   3201 				v2 = aasworld.vertexes[edge->v[1]];
   3202 				//
   3203 				for (l = 0; l < numpoints; l++)
   3204 				{
   3205 					v3 = facepoints[l];
   3206 					v4 = facepoints[(l+1) % numpoints];
   3207 					dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane,
   3208 													beststart, bestend,
   3209 													beststart2, bestend2, bestdist);
   3210 					if (dist < bestdist)
   3211 					{
   3212 						bestfacenum = facenum;
   3213 						bestfaceplane = faceplane;
   3214 						bestdist = dist;
   3215 					} //end if
   3216 				} //end for
   3217 			} //end for
   3218 		} //end for
   3219 		//
   3220 		if (bestdist > 192) continue;
   3221 		//
   3222 		VectorMiddle(beststart, beststart2, beststart);
   3223 		VectorMiddle(bestend, bestend2, bestend);
   3224 		//
   3225 		if (!towardsface)
   3226 		{
   3227 			VectorCopy(beststart, tmp);
   3228 			VectorCopy(bestend, beststart);
   3229 			VectorCopy(tmp, bestend);
   3230 		} //end if
   3231 		//
   3232 		VectorSubtract(bestend, beststart, hordir);
   3233 		hordir[2] = 0;
   3234 		hordist = VectorLength(hordir);
   3235 		//
   3236 		if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue;
   3237 		//the end point should not be significantly higher than the start point
   3238 		if (bestend[2] - 32 > beststart[2]) continue;
   3239 		//don't fall down too far
   3240 		if (bestend[2] < beststart[2] - 128) continue;
   3241 		//the distance should not be too far
   3242 		if (hordist > 32)
   3243 		{
   3244 			//check for walk off ledge
   3245 			if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue;
   3246 		} //end if
   3247 		//
   3248 		beststart[2] += 1;
   3249 		bestend[2] += 1;
   3250 		//
   3251 		if (towardsface) VectorCopy(bestend, testpoint);
   3252 		else VectorCopy(beststart, testpoint);
   3253 		testpoint[2] = 0;
   3254 		testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2];
   3255 		//
   3256 		if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f))
   3257 		{
   3258 			//if the faces are not overlapping then only go down
   3259 			if (bestend[2] - 16 > beststart[2]) continue;
   3260 		} //end if
   3261 		lreach = AAS_AllocReachability();
   3262 		if (!lreach) return lreachabilities;
   3263 		lreach->areanum = i;
   3264 		lreach->facenum = 0;
   3265 		lreach->edgenum = 0;
   3266 		VectorCopy(beststart, lreach->start);
   3267 		VectorCopy(bestend, lreach->end);
   3268 		lreach->traveltype = 0;
   3269 		lreach->traveltime = 0;
   3270 		lreach->next = lreachabilities;
   3271 		lreachabilities = lreach;
   3272 #ifndef BSPC
   3273 		if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1);
   3274 		else AAS_PermanentLine(lreach->start, lreach->end, 2);
   3275 #endif
   3276 	} //end for
   3277 	return lreachabilities;
   3278 } //end of the function AAS_FindFaceReachabilities
   3279 //===========================================================================
   3280 //
   3281 // Parameter:			-
   3282 // Returns:				-
   3283 // Changes Globals:		-
   3284 //===========================================================================
   3285 void AAS_Reachability_FuncBobbing(void)
   3286 {
   3287 	int ent, spawnflags, modelnum, axis;
   3288 	int i, numareas, areas[10];
   3289 	char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
   3290 	vec3_t origin, move_end, move_start, move_start_top, move_end_top;
   3291 	vec3_t mins, maxs, angles = {0, 0, 0};
   3292 	vec3_t start_edgeverts[4], end_edgeverts[4], mid;
   3293 	vec3_t org, start, end, dir, points[10];
   3294 	float height;
   3295 	aas_plane_t start_plane, end_plane;
   3296 	aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach;
   3297 	aas_lreachability_t *firststartreach, *firstendreach;
   3298 
   3299 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
   3300 	{
   3301 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
   3302 		if (strcmp(classname, "func_bobbing")) continue;
   3303 		AAS_FloatForBSPEpairKey(ent, "height", &height);
   3304 		if (!height) height = 32;
   3305 		//
   3306 		if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
   3307 		{
   3308 			botimport.Print(PRT_ERROR, "func_bobbing without model\n");
   3309 			continue;
   3310 		} //end if
   3311 		//get the model number, and skip the leading *
   3312 		modelnum = atoi(model+1);
   3313 		if (modelnum <= 0)
   3314 		{
   3315 			botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n");
   3316 			continue;
   3317 		} //end if
   3318 		//if the entity has an origin set then use it
   3319 		if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
   3320 			VectorSet(origin, 0, 0, 0);
   3321 		//
   3322 		AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
   3323 		//
   3324 		VectorAdd(mins, origin, mins);
   3325 		VectorAdd(maxs, origin, maxs);
   3326 		//
   3327 		VectorAdd(mins, maxs, mid);
   3328 		VectorScale(mid, 0.5, mid);
   3329 		VectorCopy(mid, origin);
   3330 		//
   3331 		VectorCopy(origin, move_end);
   3332 		VectorCopy(origin, move_start);
   3333 		//
   3334 		AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
   3335 		// set the axis of bobbing
   3336 		if (spawnflags & 1) axis = 0;
   3337 		else if (spawnflags & 2) axis = 1;
   3338 		else axis = 2;
   3339 		//
   3340 		move_start[axis] -= height;
   3341 		move_end[axis] += height;
   3342 		//
   3343 		Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n",
   3344 					modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]);
   3345 		//
   3346 #ifndef BSPC
   3347 		/*
   3348 		AAS_DrawPermanentCross(move_start, 4, 1);
   3349 		AAS_DrawPermanentCross(move_end, 4, 2);
   3350 		*/
   3351 #endif
   3352 		//
   3353 		for (i = 0; i < 4; i++)
   3354 		{
   3355 			VectorCopy(move_start, start_edgeverts[i]);
   3356 			start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z
   3357 			start_edgeverts[i][2] += 24;	//+ player origin to ground dist
   3358 		} //end for
   3359 		start_edgeverts[0][0] += maxs[0] - mid[0];
   3360 		start_edgeverts[0][1] += maxs[1] - mid[1];
   3361 		start_edgeverts[1][0] += maxs[0] - mid[0];
   3362 		start_edgeverts[1][1] += mins[1] - mid[1];
   3363 		start_edgeverts[2][0] += mins[0] - mid[0];
   3364 		start_edgeverts[2][1] += mins[1] - mid[1];
   3365 		start_edgeverts[3][0] += mins[0] - mid[0];
   3366 		start_edgeverts[3][1] += maxs[1] - mid[1];
   3367 		//
   3368 		start_plane.dist = start_edgeverts[0][2];
   3369 		VectorSet(start_plane.normal, 0, 0, 1);
   3370 		//
   3371 		for (i = 0; i < 4; i++)
   3372 		{
   3373 			VectorCopy(move_end, end_edgeverts[i]);
   3374 			end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z
   3375 			end_edgeverts[i][2] += 24;	//+ player origin to ground dist
   3376 		} //end for
   3377 		end_edgeverts[0][0] += maxs[0] - mid[0];
   3378 		end_edgeverts[0][1] += maxs[1] - mid[1];
   3379 		end_edgeverts[1][0] += maxs[0] - mid[0];
   3380 		end_edgeverts[1][1] += mins[1] - mid[1];
   3381 		end_edgeverts[2][0] += mins[0] - mid[0];
   3382 		end_edgeverts[2][1] += mins[1] - mid[1];
   3383 		end_edgeverts[3][0] += mins[0] - mid[0];
   3384 		end_edgeverts[3][1] += maxs[1] - mid[1];
   3385 		//
   3386 		end_plane.dist = end_edgeverts[0][2];
   3387 		VectorSet(end_plane.normal, 0, 0, 1);
   3388 		//
   3389 #ifndef BSPC
   3390 #if 0
   3391 		for (i = 0; i < 4; i++)
   3392 		{
   3393 			AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1);
   3394 			AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1);
   3395 		} //end for
   3396 #endif
   3397 #endif
   3398 		VectorCopy(move_start, move_start_top);
   3399 		move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z
   3400 		VectorCopy(move_end, move_end_top);
   3401 		move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z
   3402 		//
   3403 		if (!AAS_PointAreaNum(move_start_top)) continue;
   3404 		if (!AAS_PointAreaNum(move_end_top)) continue;
   3405 		//
   3406 		for (i = 0; i < 2; i++)
   3407 		{
   3408 			firststartreach = firstendreach = NULL;
   3409 			//
   3410 			if (i == 0)
   3411 			{
   3412 				firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue);
   3413 				firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse);
   3414 			} //end if
   3415 			else
   3416 			{
   3417 				firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue);
   3418 				firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse);
   3419 			} //end else
   3420 			//
   3421 			//create reachabilities from start to end
   3422 			for (startreach = firststartreach; startreach; startreach = nextstartreach)
   3423 			{
   3424 				nextstartreach = startreach->next;
   3425 				//
   3426 				//trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1);
   3427 				//if (trace.fraction < 1) continue;
   3428 				//
   3429 				for (endreach = firstendreach; endreach; endreach = nextendreach)
   3430 				{
   3431 					nextendreach = endreach->next;
   3432 					//
   3433 					//trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1);
   3434 					//if (trace.fraction < 1) continue;
   3435 					//
   3436 					Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum);
   3437 					//
   3438 					//
   3439 					if (i == 0) VectorCopy(move_start_top, org);
   3440 					else VectorCopy(move_end_top, org);
   3441 					VectorSubtract(startreach->start, org, dir);
   3442 					dir[2] = 0;
   3443 					VectorNormalize(dir);
   3444 					VectorCopy(startreach->start, start);
   3445 					VectorMA(startreach->start, 1, dir, start);
   3446 					start[2] += 1;
   3447 					VectorMA(startreach->start, 16, dir, end);
   3448 					end[2] += 1;
   3449 					//
   3450 					numareas = AAS_TraceAreas(start, end, areas, points, 10);
   3451 					if (numareas <= 0) continue;
   3452 					if (numareas > 1) VectorCopy(points[1], startreach->start);
   3453 					else VectorCopy(end, startreach->start);
   3454 					//
   3455 					if (!AAS_PointAreaNum(startreach->start)) continue;
   3456 					if (!AAS_PointAreaNum(endreach->end)) continue;
   3457 					//
   3458 					lreach = AAS_AllocReachability();
   3459 					lreach->areanum = endreach->areanum;
   3460 					if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff);
   3461 					else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff);
   3462 					lreach->facenum = (spawnflags << 16) | modelnum;
   3463 					VectorCopy(startreach->start, lreach->start);
   3464 					VectorCopy(endreach->end, lreach->end);
   3465 #ifndef BSPC
   3466 //					AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW);
   3467 //					AAS_PermanentLine(lreach->start, lreach->end, 1);
   3468 #endif
   3469 					lreach->traveltype = TRAVEL_FUNCBOB;
   3470 					lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
   3471 					lreach->traveltime = aassettings.rs_funcbob;
   3472 					reach_funcbob++;
   3473 					lreach->next = areareachability[startreach->areanum];
   3474 					areareachability[startreach->areanum] = lreach;
   3475 					//
   3476 				} //end for
   3477 			} //end for
   3478 			for (startreach = firststartreach; startreach; startreach = nextstartreach)
   3479 			{
   3480 				nextstartreach = startreach->next;
   3481 				AAS_FreeReachability(startreach);
   3482 			} //end for
   3483 			for (endreach = firstendreach; endreach; endreach = nextendreach)
   3484 			{
   3485 				nextendreach = endreach->next;
   3486 				AAS_FreeReachability(endreach);
   3487 			} //end for
   3488 			//only go up with func_bobbing entities that go up and down
   3489 			if (!(spawnflags & 1) && !(spawnflags & 2)) break;
   3490 		} //end for
   3491 	} //end for
   3492 } //end of the function AAS_Reachability_FuncBobbing
   3493 //===========================================================================
   3494 //
   3495 // Parameter:			-
   3496 // Returns:				-
   3497 // Changes Globals:		-
   3498 //===========================================================================
   3499 void AAS_Reachability_JumpPad(void)
   3500 {
   3501 	int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads;
   3502 	//int modelnum, ent2;
   3503 	//float dist, time, height, gravity, forward;
   3504 	float speed, zvel, hordist;
   3505 	aas_face_t *face2;
   3506 	aas_area_t *area2;
   3507 	aas_lreachability_t *lreach;
   3508 	vec3_t areastart, facecenter, dir, cmdmove;
   3509 	vec3_t velocity, absmins, absmaxs;
   3510 	//vec3_t origin, ent2origin, angles, teststart;
   3511 	aas_clientmove_t move;
   3512 	//aas_trace_t trace;
   3513 	aas_link_t *areas, *link;
   3514 	//char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
   3515 	char classname[MAX_EPAIRKEY];
   3516 
   3517 #ifdef BSPC
   3518 	bot_visualizejumppads = 0;
   3519 #else
   3520 	bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
   3521 #endif
   3522 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
   3523 	{
   3524 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
   3525 		if (strcmp(classname, "trigger_push")) continue;
   3526 		//
   3527 		if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
   3528 		/*
   3529 		//
   3530 		AAS_FloatForBSPEpairKey(ent, "speed", &speed);
   3531 		if (!speed) speed = 1000;
   3532 //		AAS_VectorForBSPEpairKey(ent, "angles", angles);
   3533 //		AAS_SetMovedir(angles, velocity);
   3534 //		VectorScale(velocity, speed, velocity);
   3535 		VectorClear(angles);
   3536 		//get the mins, maxs and origin of the model
   3537 		AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
   3538 		if (model[0]) modelnum = atoi(model+1);
   3539 		else modelnum = 0;
   3540 		AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
   3541 		VectorAdd(origin, absmins, absmins);
   3542 		VectorAdd(origin, absmaxs, absmaxs);
   3543 		//
   3544 #ifdef REACH_DEBUG
   3545 		botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]);
   3546 		botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]);
   3547 #endif REACH_DEBUG
   3548 		VectorAdd(absmins, absmaxs, origin);
   3549 		VectorScale (origin, 0.5, origin);
   3550 
   3551 		//get the start areas
   3552 		VectorCopy(origin, teststart);
   3553 		teststart[2] += 64;
   3554 		trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
   3555 		if (trace.startsolid)
   3556 		{
   3557 			botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
   3558 			VectorCopy(origin, areastart);
   3559 		} //end if
   3560 		else
   3561 		{
   3562 			VectorCopy(trace.endpos, areastart);
   3563 		} //end else
   3564 		areastart[2] += 0.125;
   3565 		//
   3566 		//AAS_DrawPermanentCross(origin, 4, 4);
   3567 		//get the target entity
   3568 		AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
   3569 		for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
   3570 		{
   3571 			if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
   3572 			if (!strcmp(targetname, target)) break;
   3573 		} //end for
   3574 		if (!ent2)
   3575 		{
   3576 			botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
   3577 			continue;
   3578 		} //end if
   3579 		AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
   3580 		//
   3581 		height = ent2origin[2] - origin[2];
   3582 		gravity = aassettings.sv_gravity;
   3583 		time = sqrt( height / ( 0.5 * gravity ) );
   3584 		if (!time)
   3585 		{
   3586 			botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
   3587 			continue;
   3588 		} //end if
   3589 		// set s.origin2 to the push velocity
   3590 		VectorSubtract ( ent2origin, origin, velocity);
   3591 		dist = VectorNormalize( velocity);
   3592 		forward = dist / time;
   3593 		//FIXME: why multiply by 1.1
   3594 		forward *= 1.1;
   3595 		VectorScale(velocity, forward, velocity);
   3596 		velocity[2] = time * gravity;
   3597 		*/
   3598 		//get the areas the jump pad brush is in
   3599 		areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
   3600 		/*
   3601 		for (link = areas; link; link = link->next_area)
   3602 		{
   3603 			if (link->areanum == 563)
   3604 			{
   3605 				ret = qfalse;
   3606 			}
   3607 		}
   3608         */
   3609 		for (link = areas; link; link = link->next_area)
   3610 		{
   3611 			if (AAS_AreaJumpPad(link->areanum)) break;
   3612 		} //end for
   3613 		if (!link)
   3614 		{
   3615 			botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
   3616 			AAS_UnlinkFromAreas(areas);
   3617 			continue;
   3618 		} //end if
   3619 		//
   3620 		botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
   3621 		//if there is a horizontal velocity check for a reachability without air control
   3622 		if (velocity[0] || velocity[1])
   3623 		{
   3624 			VectorSet(cmdmove, 0, 0, 0);
   3625 			//VectorCopy(velocity, cmdmove);
   3626 			//cmdmove[2] = 0;
   3627 			Com_Memset(&move, 0, sizeof(aas_clientmove_t));
   3628 			area2num = 0;
   3629 			for (i = 0; i < 20; i++)
   3630 			{
   3631 				AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
   3632 										velocity, cmdmove, 0, 30, 0.1f,
   3633 										SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
   3634 										SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads);
   3635 				area2num = move.endarea;
   3636 				for (link = areas; link; link = link->next_area)
   3637 				{
   3638 					if (!AAS_AreaJumpPad(link->areanum)) continue;
   3639 					if (link->areanum == area2num) break;
   3640 				} //end if
   3641 				if (!link) break;
   3642 				VectorCopy(move.endpos, areastart);
   3643 				VectorCopy(move.velocity, velocity);
   3644 			} //end for
   3645 			if (area2num && i < 20)
   3646 			{
   3647 				for (link = areas; link; link = link->next_area)
   3648 				{
   3649 					if (!AAS_AreaJumpPad(link->areanum)) continue;
   3650 					if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
   3651 					//create a rocket or bfg jump reachability from area1 to area2
   3652 					lreach = AAS_AllocReachability();
   3653 					if (!lreach)
   3654 					{
   3655 						AAS_UnlinkFromAreas(areas);
   3656 						return;
   3657 					} //end if
   3658 					lreach->areanum = area2num;
   3659 					//NOTE: the facenum is the Z velocity
   3660 					lreach->facenum = velocity[2];
   3661 					//NOTE: the edgenum is the horizontal velocity
   3662 					lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]);
   3663 					VectorCopy(areastart, lreach->start);
   3664 					VectorCopy(move.endpos, lreach->end);
   3665 					lreach->traveltype = TRAVEL_JUMPPAD;
   3666 					lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
   3667 					lreach->traveltime = aassettings.rs_jumppad;
   3668 					lreach->next = areareachability[link->areanum];
   3669 					areareachability[link->areanum] = lreach;
   3670 					//
   3671 					reach_jumppad++;
   3672 				} //end for
   3673 			} //end if
   3674 		} //end if
   3675 		//
   3676 		if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue;
   3677 		//check for areas we can reach with air control
   3678 		for (area2num = 1; area2num < aasworld.numareas; area2num++)
   3679 		{
   3680 			visualize = qfalse;
   3681 			/*
   3682 			if (area2num == 3568)
   3683 			{
   3684 				for (link = areas; link; link = link->next_area)
   3685 				{
   3686 					if (link->areanum == 3380)
   3687 					{
   3688 						visualize = qtrue;
   3689 						botimport.Print(PRT_MESSAGE, "bah\n");
   3690 					} //end if
   3691 				} //end for
   3692 			} //end if*/
   3693 			//never try to go back to one of the original jumppad areas
   3694 			//and don't create reachabilities if they already exist
   3695 			for (link = areas; link; link = link->next_area)
   3696 			{
   3697 				if (AAS_ReachabilityExists(link->areanum, area2num)) break;
   3698 				if (AAS_AreaJumpPad(link->areanum))
   3699 				{
   3700 					if (link->areanum == area2num) break;
   3701 				} //end if
   3702 			} //end if
   3703 			if (link) continue;
   3704 			//
   3705 			area2 = &aasworld.areas[area2num];
   3706 			for (i = 0; i < area2->numfaces; i++)
   3707 			{
   3708 				face2num = aasworld.faceindex[area2->firstface + i];
   3709 				face2 = &aasworld.faces[abs(face2num)];
   3710 				//if it is not a ground face
   3711 				if (!(face2->faceflags & FACE_GROUND)) continue;
   3712 				//get the center of the face
   3713 				AAS_FaceCenter(face2num, facecenter);
   3714 				//only go higher up
   3715 				if (facecenter[2] < areastart[2]) continue;
   3716 				//get the jumppad jump z velocity
   3717 				zvel = velocity[2];
   3718 				//get the horizontal speed for the jump, if it isn't possible to calculate this
   3719 				//speed
   3720 				ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
   3721 				if (ret && speed < 150)
   3722 				{
   3723 					//direction towards the face center
   3724 					VectorSubtract(facecenter, areastart, dir);
   3725 					dir[2] = 0;
   3726 					hordist = VectorNormalize(dir);
   3727 					//if (hordist < 1.6 * facecenter[2] - areastart[2])
   3728 					{
   3729 						//get command movement
   3730 						VectorScale(dir, speed, cmdmove);
   3731 						//
   3732 						AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
   3733 													velocity, cmdmove, 30, 30, 0.1f,
   3734 													SE_ENTERWATER|SE_ENTERSLIME|
   3735 													SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
   3736 													SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize);
   3737 						//if prediction time wasn't enough to fully predict the movement
   3738 						//don't enter slime or lava and don't fall from too high
   3739 						if (move.frames < 30 && 
   3740 								!(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
   3741 								&& (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER)))
   3742 						{
   3743 							//never go back to the same jumppad
   3744 							for (link = areas; link; link = link->next_area)
   3745 							{
   3746 								if (link->areanum == move.endarea) break;
   3747 							}
   3748 							if (!link)
   3749 							{
   3750 								for (link = areas; link; link = link->next_area)
   3751 								{
   3752 									if (!AAS_AreaJumpPad(link->areanum)) continue;
   3753 									if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
   3754 									//create a jumppad reachability from area1 to area2
   3755 									lreach = AAS_AllocReachability();
   3756 									if (!lreach)
   3757 									{
   3758 										AAS_UnlinkFromAreas(areas);
   3759 										return;
   3760 									} //end if
   3761 									lreach->areanum = move.endarea;
   3762 									//NOTE: the facenum is the Z velocity
   3763 									lreach->facenum = velocity[2];
   3764 									//NOTE: the edgenum is the horizontal velocity
   3765 									lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]);
   3766 									VectorCopy(areastart, lreach->start);
   3767 									VectorCopy(facecenter, lreach->end);
   3768 									lreach->traveltype = TRAVEL_JUMPPAD;
   3769 									lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
   3770 									lreach->traveltime = aassettings.rs_aircontrolledjumppad;
   3771 									lreach->next = areareachability[link->areanum];
   3772 									areareachability[link->areanum] = lreach;
   3773 									//
   3774 									reach_jumppad++;
   3775 								} //end for
   3776 							}
   3777 						} //end if
   3778 					} //end if
   3779 				} //end for
   3780 			} //end for
   3781 		} //end for
   3782 		AAS_UnlinkFromAreas(areas);
   3783 	} //end for
   3784 } //end of the function AAS_Reachability_JumpPad
   3785 //===========================================================================
   3786 // never point at ground faces
   3787 // always a higher and pretty far area
   3788 //
   3789 // Parameter:				-
   3790 // Returns:					-
   3791 // Changes Globals:		-
   3792 //===========================================================================
   3793 int AAS_Reachability_Grapple(int area1num, int area2num)
   3794 {
   3795 	int face2num, i, j, areanum, numareas, areas[20];
   3796 	float mingrappleangle, z, hordist;
   3797 	bsp_trace_t bsptrace;
   3798 	aas_trace_t trace;
   3799 	aas_face_t *face2;
   3800 	aas_area_t *area1, *area2;
   3801 	aas_lreachability_t *lreach;
   3802 	vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1};
   3803 	vec_t *v;
   3804 
   3805 	//only grapple when on the ground or swimming
   3806 	if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
   3807 	//don't grapple from a crouch area
   3808 	if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse;
   3809 	//NOTE: disabled area swim it doesn't work right
   3810 	if (AAS_AreaSwim(area1num)) return qfalse;
   3811 	//
   3812 	area1 = &aasworld.areas[area1num];
   3813 	area2 = &aasworld.areas[area2num];
   3814 	//don't grapple towards way lower areas
   3815 	if (area2->maxs[2] < area1->mins[2]) return qfalse;
   3816 	//
   3817 	VectorCopy(aasworld.areas[area1num].center, start);
   3818 	//if not a swim area
   3819 	if (!AAS_AreaSwim(area1num))
   3820 	{
   3821 		if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
   3822 								start[0], start[1], start[2]);
   3823 		VectorCopy(start, end);
   3824 		end[2] -= 1000;
   3825 		trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
   3826 		if (trace.startsolid) return qfalse;
   3827 		VectorCopy(trace.endpos, areastart);
   3828 	} //end if
   3829 	else
   3830 	{
   3831 		if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse;
   3832 	} //end else
   3833 	//
   3834 	//start is now the start point
   3835 	//
   3836 	for (i = 0; i < area2->numfaces; i++)
   3837 	{
   3838 		face2num = aasworld.faceindex[area2->firstface + i];
   3839 		face2 = &aasworld.faces[abs(face2num)];
   3840 		//if it is not a solid face
   3841 		if (!(face2->faceflags & FACE_SOLID)) continue;
   3842 		//direction towards the first vertex of the face
   3843 		v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]];
   3844 		VectorSubtract(v, areastart, dir);
   3845 		//if the face plane is facing away
   3846 		if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue;
   3847 		//get the center of the face
   3848 		AAS_FaceCenter(face2num, facecenter);
   3849 		//only go higher up with the grapple
   3850 		if (facecenter[2] < areastart[2] + 64) continue;
   3851 		//only use vertical faces or downward facing faces
   3852 		if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue;
   3853 		//direction towards the face center
   3854 		VectorSubtract(facecenter, areastart, dir);
   3855 		//
   3856 		z = dir[2];
   3857 		dir[2] = 0;
   3858 		hordist = VectorLength(dir);
   3859 		if (!hordist) continue;
   3860 		//if too far
   3861 		if (hordist > 2000) continue;
   3862 		//check the minimal angle of the movement
   3863 		mingrappleangle = 15; //15 degrees
   3864 		if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue;
   3865 		//
   3866 		VectorCopy(facecenter, start);
   3867 		VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end);
   3868 		//
   3869 		bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID);
   3870 		//the grapple won't stick to the sky and the grapple point should be near the AAS wall
   3871 		if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue;
   3872 		//trace a full bounding box from the area center on the ground to
   3873 		//the center of the face
   3874 		VectorSubtract(facecenter, areastart, dir);
   3875 		VectorNormalize(dir);
   3876 		VectorMA(areastart, 4, dir, start);
   3877 		VectorCopy(bsptrace.endpos, end);
   3878 		trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
   3879 		VectorSubtract(trace.endpos, facecenter, dir);
   3880 		if (VectorLength(dir) > 24) continue;
   3881 		//
   3882 		VectorCopy(trace.endpos, start);
   3883 		VectorCopy(trace.endpos, end);
   3884 		end[2] -= AAS_FallDamageDistance();
   3885 		trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
   3886 		if (trace.fraction >= 1) continue;
   3887 		//area to end in
   3888 		areanum = AAS_PointAreaNum(trace.endpos);
   3889 		//if not in lava or slime
   3890 		if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA))
   3891 		{
   3892 			continue;
   3893 		} //end if
   3894 		//do not go the the source area
   3895 		if (areanum == area1num) continue;
   3896 		//don't create reachabilities if they already exist
   3897 		if (AAS_ReachabilityExists(area1num, areanum)) continue;
   3898 		//only end in areas we can stand
   3899 		if (!AAS_AreaGrounded(areanum)) continue;
   3900 		//never go through cluster portals!!
   3901 		numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20);
   3902 		if (numareas >= 20) continue;
   3903 		for (j = 0; j < numareas; j++)
   3904 		{
   3905 			if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break;
   3906 		} //end for
   3907 		if (j < numareas) continue;
   3908 		//create a new reachability link
   3909 		lreach = AAS_AllocReachability();
   3910 		if (!lreach) return qfalse;
   3911 		lreach->areanum = areanum;
   3912 		lreach->facenum = face2num;
   3913 		lreach->edgenum = 0;
   3914 		VectorCopy(areastart, lreach->start);
   3915 		//VectorCopy(facecenter, lreach->end);
   3916 		VectorCopy(bsptrace.endpos, lreach->end);
   3917 		lreach->traveltype = TRAVEL_GRAPPLEHOOK;
   3918 		VectorSubtract(lreach->end, lreach->start, dir);
   3919 		lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25;
   3920 		lreach->next = areareachability[area1num];
   3921 		areareachability[area1num] = lreach;
   3922 		//
   3923 		reach_grapple++;
   3924 	} //end for
   3925 	//
   3926 	return qfalse;
   3927 } //end of the function AAS_Reachability_Grapple
   3928 //===========================================================================
   3929 //
   3930 // Parameter:				-
   3931 // Returns:					-
   3932 // Changes Globals:		-
   3933 //===========================================================================
   3934 void AAS_SetWeaponJumpAreaFlags(void)
   3935 {
   3936 	int ent, i;
   3937 	vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15};
   3938 	vec3_t origin;
   3939 	int areanum, weaponjumpareas, spawnflags;
   3940 	char classname[MAX_EPAIRKEY];
   3941 
   3942 	weaponjumpareas = 0;
   3943 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
   3944 	{
   3945 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
   3946 		if (
   3947 			!strcmp(classname, "item_armor_body") ||
   3948 			!strcmp(classname, "item_armor_combat") ||
   3949 			!strcmp(classname, "item_health_mega") ||
   3950 			!strcmp(classname, "weapon_grenadelauncher") ||
   3951 			!strcmp(classname, "weapon_rocketlauncher") ||
   3952 			!strcmp(classname, "weapon_lightning") ||
   3953 			!strcmp(classname, "weapon_plasmagun") ||
   3954 			!strcmp(classname, "weapon_railgun") ||
   3955 			!strcmp(classname, "weapon_bfg") ||
   3956 			!strcmp(classname, "item_quad") ||
   3957 			!strcmp(classname, "item_regen") ||
   3958 			!strcmp(classname, "item_invulnerability"))
   3959 		{
   3960 			if (AAS_VectorForBSPEpairKey(ent, "origin", origin))
   3961 			{
   3962 				spawnflags = 0;
   3963 				AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
   3964 				//if not a stationary item
   3965 				if (!(spawnflags & 1))
   3966 				{
   3967 					if (!AAS_DropToFloor(origin, mins, maxs))
   3968 					{
   3969 						botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
   3970 														classname, origin[0], origin[1], origin[2]);
   3971 					} //end if
   3972 				} //end if
   3973 				//areanum = AAS_PointAreaNum(origin);
   3974 				areanum = AAS_BestReachableArea(origin, mins, maxs, origin);
   3975 				//the bot may rocket jump towards this area
   3976 				aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP;
   3977 				//
   3978 				//if (!AAS_AreaGrounded(areanum))
   3979 				//	botimport.Print(PRT_MESSAGE, "area not grounded\n");
   3980 				//
   3981 				weaponjumpareas++;
   3982 			} //end if
   3983 		} //end if
   3984 	} //end for
   3985 	for (i = 1; i < aasworld.numareas; i++)
   3986 	{
   3987 		if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
   3988 		{
   3989 			aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP;
   3990 			weaponjumpareas++;
   3991 		} //end if
   3992 	} //end for
   3993 	botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas);
   3994 } //end of the function AAS_SetWeaponJumpAreaFlags
   3995 //===========================================================================
   3996 // create a possible weapon jump reachability from area1 to area2
   3997 //
   3998 // check if there's a cool item in the second area
   3999 // check if area1 is lower than area2
   4000 // check if the bot can rocketjump from area1 to area2
   4001 //
   4002 // Parameter:				-
   4003 // Returns:					-
   4004 // Changes Globals:		-
   4005 //===========================================================================
   4006 int AAS_Reachability_WeaponJump(int area1num, int area2num)
   4007 {
   4008 	int face2num, i, n, ret, visualize;
   4009 	float speed, zvel, hordist;
   4010 	aas_face_t *face2;
   4011 	aas_area_t *area1, *area2;
   4012 	aas_lreachability_t *lreach;
   4013 	vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart;
   4014 	vec3_t velocity;
   4015 	aas_clientmove_t move;
   4016 	aas_trace_t trace;
   4017 
   4018 	visualize = qfalse;
   4019 //	if (area1num == 4436 && area2num == 4318)
   4020 //	{
   4021 //		visualize = qtrue;
   4022 //	}
   4023 	if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse;
   4024 	if (!AAS_AreaGrounded(area2num)) return qfalse;
   4025 	//NOTE: only weapon jump towards areas with an interesting item in it??
   4026 	if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse;
   4027 	//
   4028 	area1 = &aasworld.areas[area1num];
   4029 	area2 = &aasworld.areas[area2num];
   4030 	//don't weapon jump towards way lower areas
   4031 	if (area2->maxs[2] < area1->mins[2]) return qfalse;
   4032 	//
   4033 	VectorCopy(aasworld.areas[area1num].center, start);
   4034 	//if not a swim area
   4035 	if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
   4036 							start[0], start[1], start[2]);
   4037 	VectorCopy(start, end);
   4038 	end[2] -= 1000;
   4039 	trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
   4040 	if (trace.startsolid) return qfalse;
   4041 	VectorCopy(trace.endpos, areastart);
   4042 	//
   4043 	//areastart is now the start point
   4044 	//
   4045 	for (i = 0; i < area2->numfaces; i++)
   4046 	{
   4047 		face2num = aasworld.faceindex[area2->firstface + i];
   4048 		face2 = &aasworld.faces[abs(face2num)];
   4049 		//if it is not a solid face
   4050 		if (!(face2->faceflags & FACE_GROUND)) continue;
   4051 		//get the center of the face
   4052 		AAS_FaceCenter(face2num, facecenter);
   4053 		//only go higher up with weapon jumps
   4054 		if (facecenter[2] < areastart[2] + 64) continue;
   4055 		//NOTE: set to 2 to allow bfg jump reachabilities
   4056 		for (n = 0; n < 1/*2*/; n++)
   4057 		{
   4058 			//get the rocket jump z velocity
   4059 			if (n) zvel = AAS_BFGJumpZVelocity(areastart);
   4060 			else zvel = AAS_RocketJumpZVelocity(areastart);
   4061 			//get the horizontal speed for the jump, if it isn't possible to calculate this
   4062 			//speed (the jump is not possible) then there's no jump reachability created
   4063 			ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
   4064 			if (ret && speed < 300)
   4065 			{
   4066 				//direction towards the face center
   4067 				VectorSubtract(facecenter, areastart, dir);
   4068 				dir[2] = 0;
   4069 				hordist = VectorNormalize(dir);
   4070 				//if (hordist < 1.6 * (facecenter[2] - areastart[2]))
   4071 				{
   4072 					//get command movement
   4073 					VectorScale(dir, speed, cmdmove);
   4074 					VectorSet(velocity, 0, 0, zvel);
   4075 					/*
   4076 					//get command movement
   4077 					VectorScale(dir, speed, velocity);
   4078 					velocity[2] = zvel;
   4079 					VectorSet(cmdmove, 0, 0, 0);
   4080 					*/
   4081 					//
   4082 					AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue,
   4083 												velocity, cmdmove, 30, 30, 0.1f,
   4084 												SE_ENTERWATER|SE_ENTERSLIME|
   4085 												SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
   4086 												SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize);
   4087 					//if prediction time wasn't enough to fully predict the movement
   4088 					//don't enter slime or lava and don't fall from too high
   4089 					if (move.frames < 30 && 
   4090 							!(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
   4091 								&& (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD)))
   4092 					{
   4093 						//create a rocket or bfg jump reachability from area1 to area2
   4094 						lreach = AAS_AllocReachability();
   4095 						if (!lreach) return qfalse;
   4096 						lreach->areanum = area2num;
   4097 						lreach->facenum = 0;
   4098 						lreach->edgenum = 0;
   4099 						VectorCopy(areastart, lreach->start);
   4100 						VectorCopy(facecenter, lreach->end);
   4101 						if (n)
   4102 						{
   4103 							lreach->traveltype = TRAVEL_BFGJUMP;
   4104 							lreach->traveltime = aassettings.rs_bfgjump;
   4105 						} //end if
   4106 						else
   4107 						{
   4108 							lreach->traveltype = TRAVEL_ROCKETJUMP;
   4109 							lreach->traveltime = aassettings.rs_rocketjump;
   4110 						} //end else
   4111 						lreach->next = areareachability[area1num];
   4112 						areareachability[area1num] = lreach;
   4113 						//
   4114 						reach_rocketjump++;
   4115 						return qtrue;
   4116 					} //end if
   4117 				} //end if
   4118 			} //end if
   4119 		} //end for
   4120 	} //end for
   4121 	//
   4122 	return qfalse;
   4123 } //end of the function AAS_Reachability_WeaponJump
   4124 //===========================================================================
   4125 // calculates additional walk off ledge reachabilities for the given area
   4126 //
   4127 // Parameter:				-
   4128 // Returns:					-
   4129 // Changes Globals:		-
   4130 //===========================================================================
   4131 void AAS_Reachability_WalkOffLedge(int areanum)
   4132 {
   4133 	int i, j, k, l, m, n, p, areas[10], numareas;
   4134 	int face1num, face2num, face3num, edge1num, edge2num, edge3num;
   4135 	int otherareanum, gap, reachareanum, side;
   4136 	aas_area_t *area, *area2;
   4137 	aas_face_t *face1, *face2, *face3;
   4138 	aas_edge_t *edge;
   4139 	aas_plane_t *plane;
   4140 	vec_t *v1, *v2;
   4141 	vec3_t sharededgevec, mid, dir, testend;
   4142 	aas_lreachability_t *lreach;
   4143 	aas_trace_t trace;
   4144 
   4145 	if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return;
   4146 	//
   4147 	area = &aasworld.areas[areanum];
   4148 	//
   4149 	for (i = 0; i < area->numfaces; i++)
   4150 	{
   4151 		face1num = aasworld.faceindex[area->firstface + i];
   4152 		face1 = &aasworld.faces[abs(face1num)];
   4153 		//face 1 must be a ground face
   4154 		if (!(face1->faceflags & FACE_GROUND)) continue;
   4155 		//go through all the edges of this ground face
   4156 		for (k = 0; k < face1->numedges; k++)
   4157 		{
   4158 			edge1num = aasworld.edgeindex[face1->firstedge + k];
   4159 			//find another not ground face using this same edge
   4160 			for (j = 0; j < area->numfaces; j++)
   4161 			{
   4162 				face2num = aasworld.faceindex[area->firstface + j];
   4163 				face2 = &aasworld.faces[abs(face2num)];
   4164 				//face 2 may not be a ground face
   4165 				if (face2->faceflags & FACE_GROUND) continue;
   4166 				//compare all the edges
   4167 				for (l = 0; l < face2->numedges; l++)
   4168 				{
   4169 					edge2num = aasworld.edgeindex[face2->firstedge + l];
   4170 					if (abs(edge1num) == abs(edge2num))
   4171 					{
   4172 						//get the area at the other side of the face
   4173 						if (face2->frontarea == areanum) otherareanum = face2->backarea;
   4174 						else otherareanum = face2->frontarea;
   4175 						//
   4176 						area2 = &aasworld.areas[otherareanum];
   4177 						//if the other area is grounded!
   4178 						if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED)
   4179 						{
   4180 							//check for a possible gap
   4181 							gap = qfalse;
   4182 							for (n = 0; n < area2->numfaces; n++)
   4183 							{
   4184 								face3num = aasworld.faceindex[area2->firstface + n];
   4185 								//may not be the shared face of the two areas
   4186 								if (abs(face3num) == abs(face2num)) continue;
   4187 								//
   4188 								face3 = &aasworld.faces[abs(face3num)];
   4189 								//find an edge shared by all three faces
   4190 								for (m = 0; m < face3->numedges; m++)
   4191 								{
   4192 									edge3num = aasworld.edgeindex[face3->firstedge + m];
   4193 									//but the edge should be shared by all three faces
   4194 									if (abs(edge3num) == abs(edge1num))
   4195 									{
   4196 										if (!(face3->faceflags & FACE_SOLID))
   4197 										{
   4198 											gap = qtrue;
   4199 											break;
   4200 										} //end if
   4201 										//
   4202 										if (face3->faceflags & FACE_GROUND)
   4203 										{
   4204 											gap = qfalse;
   4205 											break;
   4206 										} //end if
   4207 										//FIXME: there are more situations to be handled
   4208 										gap = qtrue;
   4209 										break;
   4210 									} //end if
   4211 								} //end for
   4212 								if (m < face3->numedges) break;
   4213 							} //end for
   4214 							if (!gap) break;
   4215 						} //end if
   4216 						//check for a walk off ledge reachability
   4217 						edge = &aasworld.edges[abs(edge1num)];
   4218 						side = edge1num < 0;
   4219 						//
   4220 						v1 = aasworld.vertexes[edge->v[side]];
   4221 						v2 = aasworld.vertexes[edge->v[!side]];
   4222 						//
   4223 						plane = &aasworld.planes[face1->planenum];
   4224 						//get the points really into the areas
   4225 						VectorSubtract(v2, v1, sharededgevec);
   4226 						CrossProduct(plane->normal, sharededgevec, dir);
   4227 						VectorNormalize(dir);
   4228 						//
   4229 						VectorAdd(v1, v2, mid);
   4230 						VectorScale(mid, 0.5, mid);
   4231 						VectorMA(mid, 8, dir, mid);
   4232 						//
   4233 						VectorCopy(mid, testend);
   4234 						testend[2] -= 1000;
   4235 						trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1);
   4236 						//
   4237 						if (trace.startsolid)
   4238 						{
   4239 							//Log_Write("area %d: trace.startsolid\r\n", areanum);
   4240 							break;
   4241 						} //end if
   4242 						reachareanum = AAS_PointAreaNum(trace.endpos);
   4243 						if (reachareanum == areanum)
   4244 						{
   4245 							//Log_Write("area %d: same area\r\n", areanum);
   4246 							break;
   4247 						} //end if
   4248 						if (AAS_ReachabilityExists(areanum, reachareanum))
   4249 						{
   4250 							//Log_Write("area %d: reachability already exists\r\n", areanum);
   4251 							break;
   4252 						} //end if
   4253 						if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum))
   4254 						{
   4255 							//Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum);
   4256 							break;
   4257 						} //end if
   4258 						//
   4259 						if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME
   4260 																						| AREACONTENTS_LAVA))
   4261 						{
   4262 							//Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum);
   4263 							break;
   4264 						} //end if
   4265 						//if not going through a cluster portal
   4266 						numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int));
   4267 						for (p = 0; p < numareas; p++)
   4268 							if (AAS_AreaClusterPortal(areas[p]))
   4269 								break;
   4270 						if (p < numareas)
   4271 							break;
   4272 						// if a maximum fall height is set and the bot would fall down further
   4273 						if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight)
   4274 							break;
   4275 						//
   4276 						lreach = AAS_AllocReachability();
   4277 						if (!lreach) break;
   4278 						lreach->areanum = reachareanum;
   4279 						lreach->facenum = 0;
   4280 						lreach->edgenum = edge1num;
   4281 						VectorCopy(mid, lreach->start);
   4282 						VectorCopy(trace.endpos, lreach->end);
   4283 						lreach->traveltype = TRAVEL_WALKOFFLEDGE;
   4284 						lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity;
   4285 						if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum))
   4286 						{
   4287 							if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5)
   4288 							{
   4289 								lreach->traveltime += aassettings.rs_falldamage5;
   4290 							} //end if
   4291 							else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10)
   4292 							{
   4293 								lreach->traveltime += aassettings.rs_falldamage10;
   4294 							} //end if
   4295 						} //end if
   4296 						lreach->next = areareachability[areanum];
   4297 						areareachability[areanum] = lreach;
   4298 						//we've got another walk off ledge reachability
   4299 						reach_walkoffledge++;
   4300 					} //end if
   4301 				} //end for
   4302 			} //end for
   4303 		} //end for
   4304 	} //end for
   4305 } //end of the function AAS_Reachability_WalkOffLedge
   4306 //===========================================================================
   4307 //
   4308 // Parameter:				-
   4309 // Returns:					-
   4310 // Changes Globals:		-
   4311 //===========================================================================
   4312 void AAS_StoreReachability(void)
   4313 {
   4314 	int i;
   4315 	aas_areasettings_t *areasettings;
   4316 	aas_lreachability_t *lreach;
   4317 	aas_reachability_t *reach;
   4318 
   4319 	if (aasworld.reachability) FreeMemory(aasworld.reachability);
   4320 	aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t));
   4321 	aasworld.reachabilitysize = 1;
   4322 	for (i = 0; i < aasworld.numareas; i++)
   4323 	{
   4324 		areasettings = &aasworld.areasettings[i];
   4325 		areasettings->firstreachablearea = aasworld.reachabilitysize;
   4326 		areasettings->numreachableareas = 0;
   4327 		for (lreach = areareachability[i]; lreach; lreach = lreach->next)
   4328 		{
   4329 			reach = &aasworld.reachability[areasettings->firstreachablearea +
   4330 													areasettings->numreachableareas];
   4331 			reach->areanum = lreach->areanum;
   4332 			reach->facenum = lreach->facenum;
   4333 			reach->edgenum = lreach->edgenum;
   4334 			VectorCopy(lreach->start, reach->start);
   4335 			VectorCopy(lreach->end, reach->end);
   4336 			reach->traveltype = lreach->traveltype;
   4337 			reach->traveltime = lreach->traveltime;
   4338 			//
   4339 			areasettings->numreachableareas++;
   4340 		} //end for
   4341 		aasworld.reachabilitysize += areasettings->numreachableareas;
   4342 	} //end for
   4343 } //end of the function AAS_StoreReachability
   4344 //===========================================================================
   4345 //
   4346 // TRAVEL_WALK					100%	equal floor height + steps
   4347 // TRAVEL_CROUCH				100%
   4348 // TRAVEL_BARRIERJUMP			100%
   4349 // TRAVEL_JUMP					 80%
   4350 // TRAVEL_LADDER				100%	+ fall down from ladder + jump up to ladder
   4351 // TRAVEL_WALKOFFLEDGE			 90%	walk off very steep walls?
   4352 // TRAVEL_SWIM					100%
   4353 // TRAVEL_WATERJUMP				100%
   4354 // TRAVEL_TELEPORT				100%
   4355 // TRAVEL_ELEVATOR				100%
   4356 // TRAVEL_GRAPPLEHOOK			100%
   4357 // TRAVEL_DOUBLEJUMP			  0%
   4358 // TRAVEL_RAMPJUMP				  0%
   4359 // TRAVEL_STRAFEJUMP			  0%
   4360 // TRAVEL_ROCKETJUMP			100%	(currently limited towards areas with items)
   4361 // TRAVEL_BFGJUMP				  0%	(currently disabled)
   4362 // TRAVEL_JUMPPAD				100%
   4363 // TRAVEL_FUNCBOB				100%
   4364 //
   4365 // Parameter:			-
   4366 // Returns:				true if NOT finished
   4367 // Changes Globals:		-
   4368 //===========================================================================
   4369 int AAS_ContinueInitReachability(float time)
   4370 {
   4371 	int i, j, todo, start_time;
   4372 	static float framereachability, reachability_delay;
   4373 	static int lastpercentage;
   4374 
   4375 	if (!aasworld.loaded) return qfalse;
   4376 	//if reachability is calculated for all areas
   4377 	if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse;
   4378 	//if starting with area 1 (area 0 is a dummy)
   4379 	if (aasworld.numreachabilityareas == 1)
   4380 	{
   4381 		botimport.Print(PRT_MESSAGE, "calculating reachability...\n");
   4382 		lastpercentage = 0;
   4383 		framereachability = 2000;
   4384 		reachability_delay = 1000;
   4385 	} //end if
   4386 	//number of areas to calculate reachability for this cycle
   4387 	todo = aasworld.numreachabilityareas + (int) framereachability;
   4388 	start_time = Sys_MilliSeconds();
   4389 	//loop over the areas
   4390 	for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++)
   4391 	{
   4392 		aasworld.numreachabilityareas++;
   4393 		//only create jumppad reachabilities from jumppad areas
   4394 		if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
   4395 		{
   4396 			continue;
   4397 		} //end if
   4398 		//loop over the areas
   4399 		for (j = 1; j < aasworld.numareas; j++)
   4400 		{
   4401 			if (i == j) continue;
   4402 			//never create reachabilities from teleporter or jumppad areas to regular areas
   4403 			if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
   4404 			{
   4405 				if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)))
   4406 				{
   4407 					continue;
   4408 				} //end if
   4409 			} //end if
   4410 			//if there already is a reachability link from area i to j
   4411 			if (AAS_ReachabilityExists(i, j)) continue;
   4412 			//check for a swim reachability
   4413 			if (AAS_Reachability_Swim(i, j)) continue;
   4414 			//check for a simple walk on equal floor height reachability
   4415 			if (AAS_Reachability_EqualFloorHeight(i, j)) continue;
   4416 			//check for step, barrier, waterjump and walk off ledge reachabilities
   4417 			if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue;
   4418 			//check for ladder reachabilities
   4419 			if (AAS_Reachability_Ladder(i, j)) continue;
   4420 			//check for a jump reachability
   4421 			if (AAS_Reachability_Jump(i, j)) continue;
   4422 		} //end for
   4423 		//never create these reachabilities from teleporter or jumppad areas
   4424 		if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
   4425 		{
   4426 			continue;
   4427 		} //end if
   4428 		//loop over the areas
   4429 		for (j = 1; j < aasworld.numareas; j++)
   4430 		{
   4431 			if (i == j) continue;
   4432 			//
   4433 			if (AAS_ReachabilityExists(i, j)) continue;
   4434 			//check for a grapple hook reachability
   4435 			if (calcgrapplereach) AAS_Reachability_Grapple(i, j);
   4436 			//check for a weapon jump reachability
   4437 			AAS_Reachability_WeaponJump(i, j);
   4438 		} //end for
   4439 		//if the calculation took more time than the max reachability delay
   4440 		if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break;
   4441 		//
   4442 		if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break;
   4443 	} //end for
   4444 	//
   4445 	if (aasworld.numreachabilityareas == aasworld.numareas)
   4446 	{
   4447 		botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0);
   4448 		botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n");
   4449 		aasworld.numreachabilityareas++;
   4450 	} //end if
   4451 	//if this is the last step in the reachability calculations
   4452 	else if (aasworld.numreachabilityareas == aasworld.numareas + 1)
   4453 	{
   4454 		//create additional walk off ledge reachabilities for every area
   4455 		for (i = 1; i < aasworld.numareas; i++)
   4456 		{
   4457 			//only create jumppad reachabilities from jumppad areas
   4458 			if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
   4459 			{
   4460 				continue;
   4461 			} //end if
   4462 			AAS_Reachability_WalkOffLedge(i);
   4463 		} //end for
   4464 		//create jump pad reachabilities
   4465 		AAS_Reachability_JumpPad();
   4466 		//create teleporter reachabilities
   4467 		AAS_Reachability_Teleport();
   4468 		//create elevator (func_plat) reachabilities
   4469 		AAS_Reachability_Elevator();
   4470 		//create func_bobbing reachabilities
   4471 		AAS_Reachability_FuncBobbing();
   4472 		//
   4473 #ifdef DEBUG
   4474 		botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim);
   4475 		botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor);
   4476 		botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step);
   4477 		botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier);
   4478 		botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump);
   4479 		botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge);
   4480 		botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump);
   4481 		botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder);
   4482 		botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk);
   4483 		botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport);
   4484 		botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob);
   4485 		botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator);
   4486 		botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple);
   4487 		botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump);
   4488 		botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad);
   4489 #endif
   4490 		//*/
   4491 		//store all the reachabilities
   4492 		AAS_StoreReachability();
   4493 		//free the reachability link heap
   4494 		AAS_ShutDownReachabilityHeap();
   4495 		//
   4496 		FreeMemory(areareachability);
   4497 		//
   4498 		aasworld.numreachabilityareas++;
   4499 		//
   4500 		botimport.Print(PRT_MESSAGE, "calculating clusters...\n");
   4501 	} //end if
   4502 	else
   4503 	{
   4504 		lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas;
   4505 		botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10);
   4506 	} //end else
   4507 	//not yet finished
   4508 	return qtrue;
   4509 } //end of the function AAS_ContinueInitReachability
   4510 //===========================================================================
   4511 //
   4512 // Parameter:				-
   4513 // Returns:					-
   4514 // Changes Globals:		-
   4515 //===========================================================================
   4516 void AAS_InitReachability(void)
   4517 {
   4518 	if (!aasworld.loaded) return;
   4519 
   4520 	if (aasworld.reachabilitysize)
   4521 	{
   4522 #ifndef BSPC
   4523 		if (!((int)LibVarGetValue("forcereachability")))
   4524 		{
   4525 			aasworld.numreachabilityareas = aasworld.numareas + 2;
   4526 			return;
   4527 		} //end if
   4528 #else
   4529 		aasworld.numreachabilityareas = aasworld.numareas + 2;
   4530 		return;
   4531 #endif //BSPC
   4532 	} //end if
   4533 #ifndef BSPC
   4534 	calcgrapplereach = LibVarGetValue("grapplereach");
   4535 #endif
   4536 	aasworld.savefile = qtrue;
   4537 	//start with area 1 because area zero is a dummy
   4538 	aasworld.numreachabilityareas = 1;
   4539 	////aasworld.numreachabilityareas = aasworld.numareas + 1;		//only calculate entity reachabilities
   4540 	//setup the heap with reachability links
   4541 	AAS_SetupReachabilityHeap();
   4542 	//allocate area reachability link array
   4543 	areareachability = (aas_lreachability_t **) GetClearedMemory(
   4544 									aasworld.numareas * sizeof(aas_lreachability_t *));
   4545 	//
   4546 	AAS_SetWeaponJumpAreaFlags();
   4547 } //end of the function AAS_InitReachable