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