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