1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 /*****************************************************************************
24  * name:		be_aas_route.c
25  *
26  * desc:		AAS
27  *
28  * $Archive: /MissionPack/code/botlib/be_aas_route.c $
29  *
30  *****************************************************************************/
31 
32 #include "../qcommon/q_shared.h"
33 #include "l_utils.h"
34 #include "l_memory.h"
35 #include "l_log.h"
36 #include "l_crc.h"
37 #include "l_libvar.h"
38 #include "l_script.h"
39 #include "l_precomp.h"
40 #include "l_struct.h"
41 #include "aasfile.h"
42 #include "botlib.h"
43 #include "be_aas.h"
44 #include "be_aas_funcs.h"
45 #include "be_interface.h"
46 #include "be_aas_def.h"
47 
48 #define ROUTING_DEBUG
49 
50 //travel time in hundreths of a second = distance * 100 / speed
51 #define DISTANCEFACTOR_CROUCH		1.3f		//crouch speed = 100
52 #define DISTANCEFACTOR_SWIM			1		//should be 0.66, swim speed = 150
53 #define DISTANCEFACTOR_WALK			0.33f	//walk speed = 300
54 
55 //cache refresh time
56 #define CACHE_REFRESHTIME		15.0f	//15 seconds refresh time
57 
58 //maximum number of routing updates each frame
59 #define MAX_FRAMEROUTINGUPDATES		10
60 
61 
62 /*
63 
64   area routing cache:
65   stores the distances within one cluster to a specific goal area
66   this goal area is in this same cluster and could be a cluster portal
67   for every cluster there's a list with routing cache for every area
68   in that cluster (including the portals of that cluster)
69   area cache stores aasworld.clusters[?].numreachabilityareas travel times
70 
71   portal routing cache:
72   stores the distances of all portals to a specific goal area
73   this goal area could be in any cluster and could also be a cluster portal
74   for every area (aasworld.numareas) the portal cache stores
75   aasworld.numportals travel times
76 
77 */
78 
79 #ifdef ROUTING_DEBUG
80 int numareacacheupdates;
81 int numportalcacheupdates;
82 #endif //ROUTING_DEBUG
83 
84 int routingcachesize;
85 int max_routingcachesize;
86 
87 //===========================================================================
88 //
89 // Parameter:			-
90 // Returns:				-
91 // Changes Globals:		-
92 //===========================================================================
93 #ifdef ROUTING_DEBUG
AAS_RoutingInfo(void)94 void AAS_RoutingInfo(void)
95 {
96 	botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates);
97 	botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates);
98 	botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize);
99 } //end of the function AAS_RoutingInfo
100 #endif //ROUTING_DEBUG
101 //===========================================================================
102 // returns the number of the area in the cluster
103 // assumes the given area is in the given cluster or a portal of the cluster
104 //
105 // Parameter:			-
106 // Returns:				-
107 // Changes Globals:		-
108 //===========================================================================
AAS_ClusterAreaNum(int cluster,int areanum)109 ID_INLINE int AAS_ClusterAreaNum(int cluster, int areanum)
110 {
111 	int side, areacluster;
112 
113 	areacluster = aasworld.areasettings[areanum].cluster;
114 	if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum;
115 	else
116 	{
117 /*#ifdef ROUTING_DEBUG
118 		if (aasworld.portals[-areacluster].frontcluster != cluster &&
119 				aasworld.portals[-areacluster].backcluster != cluster)
120 		{
121 			botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n"
122 											, -areacluster, cluster);
123 		} //end if
124 #endif //ROUTING_DEBUG*/
125 		side = aasworld.portals[-areacluster].frontcluster != cluster;
126 		return aasworld.portals[-areacluster].clusterareanum[side];
127 	} //end else
128 } //end of the function AAS_ClusterAreaNum
129 //===========================================================================
130 //
131 // Parameter:			-
132 // Returns:				-
133 // Changes Globals:		-
134 //===========================================================================
AAS_InitTravelFlagFromType(void)135 void AAS_InitTravelFlagFromType(void)
136 {
137 	int i;
138 
139 	for (i = 0; i < MAX_TRAVELTYPES; i++)
140 	{
141 		aasworld.travelflagfortype[i] = TFL_INVALID;
142 	} //end for
143 	aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID;
144 	aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK;
145 	aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH;
146 	aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP;
147 	aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP;
148 	aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER;
149 	aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE;
150 	aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM;
151 	aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP;
152 	aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT;
153 	aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR;
154 	aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP;
155 	aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP;
156 	aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK;
157 	aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP;
158 	aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP;
159 	aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP;
160 	aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD;
161 	aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB;
162 } //end of the function AAS_InitTravelFlagFromType
163 //===========================================================================
164 //
165 // Parameter:			-
166 // Returns:				-
167 // Changes Globals:		-
168 //===========================================================================
AAS_TravelFlagForType_inline(int traveltype)169 ID_INLINE int AAS_TravelFlagForType_inline(int traveltype)
170 {
171 	int tfl;
172 
173 	tfl = 0;
174 	if (tfl & TRAVELFLAG_NOTTEAM1)
175 		tfl |= TFL_NOTTEAM1;
176 	if (tfl & TRAVELFLAG_NOTTEAM2)
177 		tfl |= TFL_NOTTEAM2;
178 	traveltype &= TRAVELTYPE_MASK;
179 	if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES)
180 		return TFL_INVALID;
181 	tfl |= aasworld.travelflagfortype[traveltype];
182 	return tfl;
183 } //end of the function AAS_TravelFlagForType_inline
184 //===========================================================================
185 //
186 // Parameter:			-
187 // Returns:				-
188 // Changes Globals:		-
189 //===========================================================================
AAS_TravelFlagForType(int traveltype)190 int AAS_TravelFlagForType(int traveltype)
191 {
192 	return AAS_TravelFlagForType_inline(traveltype);
193 } //end of the function AAS_TravelFlagForType_inline
194 //===========================================================================
195 //
196 // Parameter:			-
197 // Returns:				-
198 // Changes Globals:		-
199 //===========================================================================
AAS_UnlinkCache(aas_routingcache_t * cache)200 void AAS_UnlinkCache(aas_routingcache_t *cache)
201 {
202 	if (cache->time_next) cache->time_next->time_prev = cache->time_prev;
203 	else aasworld.newestcache = cache->time_prev;
204 	if (cache->time_prev) cache->time_prev->time_next = cache->time_next;
205 	else aasworld.oldestcache = cache->time_next;
206 	cache->time_next = NULL;
207 	cache->time_prev = NULL;
208 } //end of the function AAS_UnlinkCache
209 //===========================================================================
210 //
211 // Parameter:			-
212 // Returns:				-
213 // Changes Globals:		-
214 //===========================================================================
AAS_LinkCache(aas_routingcache_t * cache)215 void AAS_LinkCache(aas_routingcache_t *cache)
216 {
217 	if (aasworld.newestcache)
218 	{
219 		aasworld.newestcache->time_next = cache;
220 		cache->time_prev = aasworld.newestcache;
221 	} //end if
222 	else
223 	{
224 		aasworld.oldestcache = cache;
225 		cache->time_prev = NULL;
226 	} //end else
227 	cache->time_next = NULL;
228 	aasworld.newestcache = cache;
229 } //end of the function AAS_LinkCache
230 //===========================================================================
231 //
232 // Parameter:			-
233 // Returns:				-
234 // Changes Globals:		-
235 //===========================================================================
AAS_FreeRoutingCache(aas_routingcache_t * cache)236 void AAS_FreeRoutingCache(aas_routingcache_t *cache)
237 {
238 	AAS_UnlinkCache(cache);
239 	routingcachesize -= cache->size;
240 	FreeMemory(cache);
241 } //end of the function AAS_FreeRoutingCache
242 //===========================================================================
243 //
244 // Parameter:			-
245 // Returns:				-
246 // Changes Globals:		-
247 //===========================================================================
AAS_RemoveRoutingCacheInCluster(int clusternum)248 void AAS_RemoveRoutingCacheInCluster( int clusternum )
249 {
250 	int i;
251 	aas_routingcache_t *cache, *nextcache;
252 	aas_cluster_t *cluster;
253 
254 	if (!aasworld.clusterareacache)
255 		return;
256 	cluster = &aasworld.clusters[clusternum];
257 	for (i = 0; i < cluster->numareas; i++)
258 	{
259 		for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache)
260 		{
261 			nextcache = cache->next;
262 			AAS_FreeRoutingCache(cache);
263 		} //end for
264 		aasworld.clusterareacache[clusternum][i] = NULL;
265 	} //end for
266 } //end of the function AAS_RemoveRoutingCacheInCluster
267 //===========================================================================
268 //
269 // Parameter:			-
270 // Returns:				-
271 // Changes Globals:		-
272 //===========================================================================
AAS_RemoveRoutingCacheUsingArea(int areanum)273 void AAS_RemoveRoutingCacheUsingArea( int areanum )
274 {
275 	int i, clusternum;
276 	aas_routingcache_t *cache, *nextcache;
277 
278 	clusternum = aasworld.areasettings[areanum].cluster;
279 	if (clusternum > 0)
280 	{
281 		//remove all the cache in the cluster the area is in
282 		AAS_RemoveRoutingCacheInCluster( clusternum );
283 	} //end if
284 	else
285 	{
286 		// if this is a portal remove all cache in both the front and back cluster
287 		AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster );
288 		AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster );
289 	} //end else
290 	// remove all portal cache
291 	for (i = 0; i < aasworld.numareas; i++)
292 	{
293 		//refresh portal cache
294 		for (cache = aasworld.portalcache[i]; cache; cache = nextcache)
295 		{
296 			nextcache = cache->next;
297 			AAS_FreeRoutingCache(cache);
298 		} //end for
299 		aasworld.portalcache[i] = NULL;
300 	} //end for
301 } //end of the function AAS_RemoveRoutingCacheUsingArea
302 //===========================================================================
303 //
304 // Parameter:			-
305 // Returns:				-
306 // Changes Globals:		-
307 //===========================================================================
AAS_EnableRoutingArea(int areanum,int enable)308 int AAS_EnableRoutingArea(int areanum, int enable)
309 {
310 	int flags;
311 
312 	if (areanum <= 0 || areanum >= aasworld.numareas)
313 	{
314 		if (bot_developer)
315 		{
316 			botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum);
317 		} //end if
318 		return 0;
319 	} //end if
320 	flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED;
321 	if (enable < 0)
322 		return !flags;
323 
324 	if (enable)
325 		aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED;
326 	else
327 		aasworld.areasettings[areanum].areaflags |= AREA_DISABLED;
328 	// if the status of the area changed
329 	if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) )
330 	{
331 		//remove all routing cache involving this area
332 		AAS_RemoveRoutingCacheUsingArea( areanum );
333 	} //end if
334 	return !flags;
335 } //end of the function AAS_EnableRoutingArea
336 //===========================================================================
337 //
338 // Parameter:			-
339 // Returns:				-
340 // Changes Globals:		-
341 //===========================================================================
AAS_RoutingTime(void)342 ID_INLINE float AAS_RoutingTime(void)
343 {
344 	return AAS_Time();
345 } //end of the function AAS_RoutingTime
346 //===========================================================================
347 //
348 // Parameter:				-
349 // Returns:					-
350 // Changes Globals:		-
351 //===========================================================================
AAS_GetAreaContentsTravelFlags(int areanum)352 int AAS_GetAreaContentsTravelFlags(int areanum)
353 {
354 	int contents, tfl;
355 
356 	contents = aasworld.areasettings[areanum].contents;
357 	tfl = 0;
358 	if (contents & AREACONTENTS_WATER)
359 		tfl |= TFL_WATER;
360 	else if (contents & AREACONTENTS_SLIME)
361 		tfl |= TFL_SLIME;
362 	else if (contents & AREACONTENTS_LAVA)
363 		tfl |= TFL_LAVA;
364 	else
365 		tfl |= TFL_AIR;
366 	if (contents & AREACONTENTS_DONOTENTER)
367 		tfl |= TFL_DONOTENTER;
368 	if (contents & AREACONTENTS_NOTTEAM1)
369 		tfl |= TFL_NOTTEAM1;
370 	if (contents & AREACONTENTS_NOTTEAM2)
371 		tfl |= TFL_NOTTEAM2;
372 	if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE)
373 		tfl |= TFL_BRIDGE;
374 	return tfl;
375 } //end of the function AAS_GetAreaContentsTravelFlags
376 //===========================================================================
377 //
378 // Parameter:			-
379 // Returns:				-
380 // Changes Globals:		-
381 //===========================================================================
AAS_AreaContentsTravelFlags_inline(int areanum)382 ID_INLINE int AAS_AreaContentsTravelFlags_inline(int areanum)
383 {
384 	return aasworld.areacontentstravelflags[areanum];
385 } //end of the function AAS_AreaContentsTravelFlags
386 //===========================================================================
387 //
388 // Parameter:			-
389 // Returns:				-
390 // Changes Globals:		-
391 //===========================================================================
AAS_AreaContentsTravelFlags(int areanum)392 int AAS_AreaContentsTravelFlags(int areanum)
393 {
394 	return aasworld.areacontentstravelflags[areanum];
395 } //end of the function AAS_AreaContentsTravelFlags
396 //===========================================================================
397 //
398 // Parameter:			-
399 // Returns:				-
400 // Changes Globals:		-
401 //===========================================================================
AAS_InitAreaContentsTravelFlags(void)402 void AAS_InitAreaContentsTravelFlags(void)
403 {
404 	int i;
405 
406 	if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);
407 	aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int));
408 	//
409 	for (i = 0; i < aasworld.numareas; i++) {
410 		aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i);
411 	}
412 } //end of the function AAS_InitAreaContentsTravelFlags
413 //===========================================================================
414 //
415 // Parameter:			-
416 // Returns:				-
417 // Changes Globals:		-
418 //===========================================================================
AAS_CreateReversedReachability(void)419 void AAS_CreateReversedReachability(void)
420 {
421 	int i, n;
422 	aas_reversedlink_t *revlink;
423 	aas_reachability_t *reach;
424 	aas_areasettings_t *settings;
425 	char *ptr;
426 #ifdef DEBUG
427 	int starttime;
428 
429 	starttime = Sys_MilliSeconds();
430 #endif
431 	//free reversed links that have already been created
432 	if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);
433 	//allocate memory for the reversed reachability links
434 	ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) +
435 							aasworld.reachabilitysize * sizeof(aas_reversedlink_t));
436 	//
437 	aasworld.reversedreachability = (aas_reversedreachability_t *) ptr;
438 	//pointer to the memory for the reversed links
439 	ptr += aasworld.numareas * sizeof(aas_reversedreachability_t);
440 	//check all reachabilities of all areas
441 	for (i = 1; i < aasworld.numareas; i++)
442 	{
443 		//settings of the area
444 		settings = &aasworld.areasettings[i];
445 		//
446 		if (settings->numreachableareas >= 128)
447 			botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i);
448 		//create reversed links for the reachabilities
449 		for (n = 0; n < settings->numreachableareas && n < 128; n++)
450 		{
451 			//reachability link
452 			reach = &aasworld.reachability[settings->firstreachablearea + n];
453 			//
454 			revlink = (aas_reversedlink_t *) ptr;
455 			ptr += sizeof(aas_reversedlink_t);
456 			//
457 			revlink->areanum = i;
458 			revlink->linknum = settings->firstreachablearea + n;
459 			revlink->next = aasworld.reversedreachability[reach->areanum].first;
460 			aasworld.reversedreachability[reach->areanum].first = revlink;
461 			aasworld.reversedreachability[reach->areanum].numlinks++;
462 		} //end for
463 	} //end for
464 #ifdef DEBUG
465 	botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime);
466 #endif
467 } //end of the function AAS_CreateReversedReachability
468 //===========================================================================
469 //
470 // Parameter:			-
471 // Returns:				-
472 // Changes Globals:		-
473 //===========================================================================
AAS_AreaTravelTime(int areanum,vec3_t start,vec3_t end)474 unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end)
475 {
476 	int intdist;
477 	float dist;
478 	vec3_t dir;
479 
480 	VectorSubtract(start, end, dir);
481 	dist = VectorLength(dir);
482 	//if crouch only area
483 	if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH;
484 	//if swim area
485 	else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM;
486 	//normal walk area
487 	else dist *= DISTANCEFACTOR_WALK;
488 	//
489 	intdist = (int) dist;
490 	//make sure the distance isn't zero
491 	if (intdist <= 0) intdist = 1;
492 	return intdist;
493 } //end of the function AAS_AreaTravelTime
494 //===========================================================================
495 //
496 // Parameter:			-
497 // Returns:				-
498 // Changes Globals:		-
499 //===========================================================================
AAS_CalculateAreaTravelTimes(void)500 void AAS_CalculateAreaTravelTimes(void)
501 {
502 	int i, l, n, size;
503 	char *ptr;
504 	vec3_t end;
505 	aas_reversedreachability_t *revreach;
506 	aas_reversedlink_t *revlink;
507 	aas_reachability_t *reach;
508 	aas_areasettings_t *settings;
509 	int starttime;
510 
511 	starttime = Sys_MilliSeconds();
512 	//if there are still area travel times, free the memory
513 	if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);
514 	//get the total size of all the area travel times
515 	size = aasworld.numareas * sizeof(unsigned short **);
516 	for (i = 0; i < aasworld.numareas; i++)
517 	{
518 		revreach = &aasworld.reversedreachability[i];
519 		//settings of the area
520 		settings = &aasworld.areasettings[i];
521 		//
522 		size += settings->numreachableareas * sizeof(unsigned short *);
523 		//
524 		size += settings->numreachableareas *
525 			PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short);
526 	} //end for
527 	//allocate memory for the area travel times
528 	ptr = (char *) GetClearedMemory(size);
529 	aasworld.areatraveltimes = (unsigned short ***) ptr;
530 	ptr += aasworld.numareas * sizeof(unsigned short **);
531 	//calcluate the travel times for all the areas
532 	for (i = 0; i < aasworld.numareas; i++)
533 	{
534 		//reversed reachabilities of this area
535 		revreach = &aasworld.reversedreachability[i];
536 		//settings of the area
537 		settings = &aasworld.areasettings[i];
538 		//
539 		aasworld.areatraveltimes[i] = (unsigned short **) ptr;
540 		ptr += settings->numreachableareas * sizeof(unsigned short *);
541 		//
542 		for (l = 0; l < settings->numreachableareas; l++)
543 		{
544 			aasworld.areatraveltimes[i][l] = (unsigned short *) ptr;
545 			ptr += PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short);
546 			//reachability link
547 			reach = &aasworld.reachability[settings->firstreachablearea + l];
548 			//
549 			for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)
550 			{
551 				VectorCopy(aasworld.reachability[revlink->linknum].end, end);
552 				//
553 				aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start);
554 			} //end for
555 		} //end for
556 	} //end for
557 #ifdef DEBUG
558 	botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime);
559 #endif
560 } //end of the function AAS_CalculateAreaTravelTimes
561 //===========================================================================
562 //
563 // Parameter:			-
564 // Returns:				-
565 // Changes Globals:		-
566 //===========================================================================
AAS_PortalMaxTravelTime(int portalnum)567 int AAS_PortalMaxTravelTime(int portalnum)
568 {
569 	int l, n, t, maxt;
570 	aas_portal_t *portal;
571 	aas_reversedreachability_t *revreach;
572 	aas_reversedlink_t *revlink;
573 	aas_areasettings_t *settings;
574 
575 	portal = &aasworld.portals[portalnum];
576 	//reversed reachabilities of this portal area
577 	revreach = &aasworld.reversedreachability[portal->areanum];
578 	//settings of the portal area
579 	settings = &aasworld.areasettings[portal->areanum];
580 	//
581 	maxt = 0;
582 	for (l = 0; l < settings->numreachableareas; l++)
583 	{
584 		for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)
585 		{
586 			t = aasworld.areatraveltimes[portal->areanum][l][n];
587 			if (t > maxt)
588 			{
589 				maxt = t;
590 			} //end if
591 		} //end for
592 	} //end for
593 	return maxt;
594 } //end of the function AAS_PortalMaxTravelTime
595 //===========================================================================
596 //
597 // Parameter:			-
598 // Returns:				-
599 // Changes Globals:		-
600 //===========================================================================
AAS_InitPortalMaxTravelTimes(void)601 void AAS_InitPortalMaxTravelTimes(void)
602 {
603 	int i;
604 
605 	if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);
606 
607 	aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int));
608 
609 	for (i = 0; i < aasworld.numportals; i++)
610 	{
611 		aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i);
612 		//botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]);
613 	} //end for
614 } //end of the function AAS_InitPortalMaxTravelTimes
615 //===========================================================================
616 //
617 // Parameter:			-
618 // Returns:				-
619 // Changes Globals:		-
620 //===========================================================================
621 /*
622 int AAS_FreeOldestCache(void)
623 {
624 	int i, j, bestcluster, bestarea, freed;
625 	float besttime;
626 	aas_routingcache_t *cache, *bestcache;
627 
628 	freed = qfalse;
629 	besttime = 999999999;
630 	bestcache = NULL;
631 	bestcluster = 0;
632 	bestarea = 0;
633 	//refresh cluster cache
634 	for (i = 0; i < aasworld.numclusters; i++)
635 	{
636 		for (j = 0; j < aasworld.clusters[i].numareas; j++)
637 		{
638 			for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
639 			{
640 				//never remove cache leading towards a portal
641 				if (aasworld.areasettings[cache->areanum].cluster < 0) continue;
642 				//if this cache is older than the cache we found so far
643 				if (cache->time < besttime)
644 				{
645 					bestcache = cache;
646 					bestcluster = i;
647 					bestarea = j;
648 					besttime = cache->time;
649 				} //end if
650 			} //end for
651 		} //end for
652 	} //end for
653 	if (bestcache)
654 	{
655 		cache = bestcache;
656 		if (cache->prev) cache->prev->next = cache->next;
657 		else aasworld.clusterareacache[bestcluster][bestarea] = cache->next;
658 		if (cache->next) cache->next->prev = cache->prev;
659 		AAS_FreeRoutingCache(cache);
660 		freed = qtrue;
661 	} //end if
662 	besttime = 999999999;
663 	bestcache = NULL;
664 	bestarea = 0;
665 	for (i = 0; i < aasworld.numareas; i++)
666 	{
667 		//refresh portal cache
668 		for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
669 		{
670 			if (cache->time < besttime)
671 			{
672 				bestcache = cache;
673 				bestarea = i;
674 				besttime = cache->time;
675 			} //end if
676 		} //end for
677 	} //end for
678 	if (bestcache)
679 	{
680 		cache = bestcache;
681 		if (cache->prev) cache->prev->next = cache->next;
682 		else aasworld.portalcache[bestarea] = cache->next;
683 		if (cache->next) cache->next->prev = cache->prev;
684 		AAS_FreeRoutingCache(cache);
685 		freed = qtrue;
686 	} //end if
687 	return freed;
688 } //end of the function AAS_FreeOldestCache
689 */
690 //===========================================================================
691 //
692 // Parameter:			-
693 // Returns:				-
694 // Changes Globals:		-
695 //===========================================================================
AAS_FreeOldestCache(void)696 int AAS_FreeOldestCache(void)
697 {
698 	int clusterareanum;
699 	aas_routingcache_t *cache;
700 
701 	for (cache = aasworld.oldestcache; cache; cache = cache->time_next) {
702 		// never free area cache leading towards a portal
703 		if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) {
704 			continue;
705 		}
706 		break;
707 	}
708 	if (cache) {
709 		// unlink the cache
710 		if (cache->type == CACHETYPE_AREA) {
711 			//number of the area in the cluster
712 			clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);
713 			// unlink from cluster area cache
714 			if (cache->prev) cache->prev->next = cache->next;
715 			else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next;
716 			if (cache->next) cache->next->prev = cache->prev;
717 		}
718 		else {
719 			// unlink from portal cache
720 			if (cache->prev) cache->prev->next = cache->next;
721 			else aasworld.portalcache[cache->areanum] = cache->next;
722 			if (cache->next) cache->next->prev = cache->prev;
723 		}
724 		AAS_FreeRoutingCache(cache);
725 		return qtrue;
726 	}
727 	return qfalse;
728 } //end of the function AAS_FreeOldestCache
729 //===========================================================================
730 //
731 // Parameter:			-
732 // Returns:				-
733 // Changes Globals:		-
734 //===========================================================================
AAS_AllocRoutingCache(int numtraveltimes)735 aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes)
736 {
737 	aas_routingcache_t *cache;
738 	int size;
739 
740 	//
741 	size = sizeof(aas_routingcache_t)
742 						+ numtraveltimes * sizeof(unsigned short int)
743 						+ numtraveltimes * sizeof(unsigned char);
744 	//
745 	routingcachesize += size;
746 	//
747 	cache = (aas_routingcache_t *) GetClearedMemory(size);
748 	cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t)
749 								+ numtraveltimes * sizeof(unsigned short int);
750 	cache->size = size;
751 	return cache;
752 } //end of the function AAS_AllocRoutingCache
753 //===========================================================================
754 //
755 // Parameter:			-
756 // Returns:				-
757 // Changes Globals:		-
758 //===========================================================================
AAS_FreeAllClusterAreaCache(void)759 void AAS_FreeAllClusterAreaCache(void)
760 {
761 	int i, j;
762 	aas_routingcache_t *cache, *nextcache;
763 	aas_cluster_t *cluster;
764 
765 	//free all cluster cache if existing
766 	if (!aasworld.clusterareacache) return;
767 	//free caches
768 	for (i = 0; i < aasworld.numclusters; i++)
769 	{
770 		cluster = &aasworld.clusters[i];
771 		for (j = 0; j < cluster->numareas; j++)
772 		{
773 			for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache)
774 			{
775 				nextcache = cache->next;
776 				AAS_FreeRoutingCache(cache);
777 			} //end for
778 			aasworld.clusterareacache[i][j] = NULL;
779 		} //end for
780 	} //end for
781 	//free the cluster cache array
782 	FreeMemory(aasworld.clusterareacache);
783 	aasworld.clusterareacache = NULL;
784 } //end of the function AAS_FreeAllClusterAreaCache
785 //===========================================================================
786 //
787 // Parameter:			-
788 // Returns:				-
789 // Changes Globals:		-
790 //===========================================================================
AAS_InitClusterAreaCache(void)791 void AAS_InitClusterAreaCache(void)
792 {
793 	int i, size;
794 	char *ptr;
795 
796 	//
797 	for (size = 0, i = 0; i < aasworld.numclusters; i++)
798 	{
799 		size += aasworld.clusters[i].numareas;
800 	} //end for
801 	//two dimensional array with pointers for every cluster to routing cache
802 	//for every area in that cluster
803 	ptr = (char *) GetClearedMemory(
804 				aasworld.numclusters * sizeof(aas_routingcache_t **) +
805 				size * sizeof(aas_routingcache_t *));
806 	aasworld.clusterareacache = (aas_routingcache_t ***) ptr;
807 	ptr += aasworld.numclusters * sizeof(aas_routingcache_t **);
808 	for (i = 0; i < aasworld.numclusters; i++)
809 	{
810 		aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr;
811 		ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *);
812 	} //end for
813 } //end of the function AAS_InitClusterAreaCache
814 //===========================================================================
815 //
816 // Parameter:			-
817 // Returns:				-
818 // Changes Globals:		-
819 //===========================================================================
AAS_FreeAllPortalCache(void)820 void AAS_FreeAllPortalCache(void)
821 {
822 	int i;
823 	aas_routingcache_t *cache, *nextcache;
824 
825 	//free all portal cache if existing
826 	if (!aasworld.portalcache) return;
827 	//free portal caches
828 	for (i = 0; i < aasworld.numareas; i++)
829 	{
830 		for (cache = aasworld.portalcache[i]; cache; cache = nextcache)
831 		{
832 			nextcache = cache->next;
833 			AAS_FreeRoutingCache(cache);
834 		} //end for
835 		aasworld.portalcache[i] = NULL;
836 	} //end for
837 	FreeMemory(aasworld.portalcache);
838 	aasworld.portalcache = NULL;
839 } //end of the function AAS_FreeAllPortalCache
840 //===========================================================================
841 //
842 // Parameter:				-
843 // Returns:					-
844 // Changes Globals:		-
845 //===========================================================================
AAS_InitPortalCache(void)846 void AAS_InitPortalCache(void)
847 {
848 	//
849 	aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory(
850 								aasworld.numareas * sizeof(aas_routingcache_t *));
851 } //end of the function AAS_InitPortalCache
852 //===========================================================================
853 //
854 // Parameter:				-
855 // Returns:					-
856 // Changes Globals:		-
857 //===========================================================================
AAS_InitRoutingUpdate(void)858 void AAS_InitRoutingUpdate(void)
859 {
860 	int i, maxreachabilityareas;
861 
862 	//free routing update fields if already existing
863 	if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);
864 	//
865 	maxreachabilityareas = 0;
866 	for (i = 0; i < aasworld.numclusters; i++)
867 	{
868 		if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas)
869 		{
870 			maxreachabilityareas = aasworld.clusters[i].numreachabilityareas;
871 		} //end if
872 	} //end for
873 	//allocate memory for the routing update fields
874 	aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory(
875 									maxreachabilityareas * sizeof(aas_routingupdate_t));
876 	//
877 	if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);
878 	//allocate memory for the portal update fields
879 	aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory(
880 									(aasworld.numportals+1) * sizeof(aas_routingupdate_t));
881 } //end of the function AAS_InitRoutingUpdate
882 //===========================================================================
883 //
884 // Parameter:			-
885 // Returns:				-
886 // Changes Globals:		-
887 //===========================================================================
AAS_CreateAllRoutingCache(void)888 void AAS_CreateAllRoutingCache(void)
889 {
890 	int i, j, t;
891 
892 	aasworld.initialized = qtrue;
893 	botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n");
894 	for (i = 1; i < aasworld.numareas; i++)
895 	{
896 		if (!AAS_AreaReachability(i)) continue;
897 		for (j = 1; j < aasworld.numareas; j++)
898 		{
899 			if (i == j) continue;
900 			if (!AAS_AreaReachability(j)) continue;
901 			t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT);
902 			//Log_Write("traveltime from %d to %d is %d", i, j, t);
903 		} //end for
904 	} //end for
905 	aasworld.initialized = qfalse;
906 } //end of the function AAS_CreateAllRoutingCache
907 //===========================================================================
908 //
909 // Parameter:			-
910 // Returns:				-
911 // Changes Globals:		-
912 //===========================================================================
913 
914 //the route cache header
915 //this header is followed by numportalcache + numareacache aas_routingcache_t
916 //structures that store routing cache
917 typedef struct routecacheheader_s
918 {
919 	int ident;
920 	int version;
921 	int numareas;
922 	int numclusters;
923 	int areacrc;
924 	int clustercrc;
925 	int numportalcache;
926 	int numareacache;
927 } routecacheheader_t;
928 
929 #define RCID						(('C'<<24)+('R'<<16)+('E'<<8)+'M')
930 #define RCVERSION					2
931 
932 //void AAS_DecompressVis(byte *in, int numareas, byte *decompressed);
933 //int AAS_CompressVis(byte *vis, int numareas, byte *dest);
934 
AAS_WriteRouteCache(void)935 void AAS_WriteRouteCache(void)
936 {
937 	int i, j, numportalcache, numareacache, totalsize;
938 	aas_routingcache_t *cache;
939 	aas_cluster_t *cluster;
940 	fileHandle_t fp;
941 	char filename[MAX_QPATH];
942 	routecacheheader_t routecacheheader;
943 
944 	numportalcache = 0;
945 	for (i = 0; i < aasworld.numareas; i++)
946 	{
947 		for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
948 		{
949 			numportalcache++;
950 		} //end for
951 	} //end for
952 	numareacache = 0;
953 	for (i = 0; i < aasworld.numclusters; i++)
954 	{
955 		cluster = &aasworld.clusters[i];
956 		for (j = 0; j < cluster->numareas; j++)
957 		{
958 			for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
959 			{
960 				numareacache++;
961 			} //end for
962 		} //end for
963 	} //end for
964 	// open the file for writing
965 	Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname);
966 	botimport.FS_FOpenFile( filename, &fp, FS_WRITE );
967 	if (!fp)
968 	{
969 		AAS_Error("Unable to open file: %s\n", filename);
970 		return;
971 	} //end if
972 	//create the header
973 	routecacheheader.ident = RCID;
974 	routecacheheader.version = RCVERSION;
975 	routecacheheader.numareas = aasworld.numareas;
976 	routecacheheader.numclusters = aasworld.numclusters;
977 	routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas );
978 	routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters );
979 	routecacheheader.numportalcache = numportalcache;
980 	routecacheheader.numareacache = numareacache;
981 	//write the header
982 	botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp);
983 	//
984 	totalsize = 0;
985 	//write all the cache
986 	for (i = 0; i < aasworld.numareas; i++)
987 	{
988 		for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
989 		{
990 			botimport.FS_Write(cache, cache->size, fp);
991 			totalsize += cache->size;
992 		} //end for
993 	} //end for
994 	for (i = 0; i < aasworld.numclusters; i++)
995 	{
996 		cluster = &aasworld.clusters[i];
997 		for (j = 0; j < cluster->numareas; j++)
998 		{
999 			for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
1000 			{
1001 				botimport.FS_Write(cache, cache->size, fp);
1002 				totalsize += cache->size;
1003 			} //end for
1004 		} //end for
1005 	} //end for
1006 	// write the visareas
1007 	/*
1008 	for (i = 0; i < aasworld.numareas; i++)
1009 	{
1010 		if (!aasworld.areavisibility[i]) {
1011 			size = 0;
1012 			botimport.FS_Write(&size, sizeof(int), fp);
1013 			continue;
1014 		}
1015 		AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis );
1016 		size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis );
1017 		botimport.FS_Write(&size, sizeof(int), fp);
1018 		botimport.FS_Write(aasworld.decompressedvis, size, fp);
1019 	}
1020 	*/
1021 	//
1022 	botimport.FS_FCloseFile(fp);
1023 	botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename);
1024 	botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize);
1025 } //end of the function AAS_WriteRouteCache
1026 //===========================================================================
1027 //
1028 // Parameter:			-
1029 // Returns:				-
1030 // Changes Globals:		-
1031 //===========================================================================
AAS_ReadCache(fileHandle_t fp)1032 aas_routingcache_t *AAS_ReadCache(fileHandle_t fp)
1033 {
1034 	int size;
1035 	aas_routingcache_t *cache;
1036 
1037 	botimport.FS_Read(&size, sizeof(size), fp);
1038 	cache = (aas_routingcache_t *) GetMemory(size);
1039 	cache->size = size;
1040 	botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp);
1041 	cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) +
1042 		(size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2;
1043 	return cache;
1044 } //end of the function AAS_ReadCache
1045 //===========================================================================
1046 //
1047 // Parameter:			-
1048 // Returns:				-
1049 // Changes Globals:		-
1050 //===========================================================================
AAS_ReadRouteCache(void)1051 int AAS_ReadRouteCache(void)
1052 {
1053 	int i, clusterareanum;//, size;
1054 	fileHandle_t fp;
1055 	char filename[MAX_QPATH];
1056 	routecacheheader_t routecacheheader;
1057 	aas_routingcache_t *cache;
1058 
1059 	Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname);
1060 	botimport.FS_FOpenFile( filename, &fp, FS_READ );
1061 	if (!fp)
1062 	{
1063 		return qfalse;
1064 	} //end if
1065 	botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp );
1066 	if (routecacheheader.ident != RCID)
1067 	{
1068 		AAS_Error("%s is not a route cache dump\n");
1069 		return qfalse;
1070 	} //end if
1071 	if (routecacheheader.version != RCVERSION)
1072 	{
1073 		AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION);
1074 		return qfalse;
1075 	} //end if
1076 	if (routecacheheader.numareas != aasworld.numareas)
1077 	{
1078 		//AAS_Error("route cache dump has wrong number of areas\n");
1079 		return qfalse;
1080 	} //end if
1081 	if (routecacheheader.numclusters != aasworld.numclusters)
1082 	{
1083 		//AAS_Error("route cache dump has wrong number of clusters\n");
1084 		return qfalse;
1085 	} //end if
1086 	if (routecacheheader.areacrc !=
1087 		CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ))
1088 	{
1089 		//AAS_Error("route cache dump area CRC incorrect\n");
1090 		return qfalse;
1091 	} //end if
1092 	if (routecacheheader.clustercrc !=
1093 		CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ))
1094 	{
1095 		//AAS_Error("route cache dump cluster CRC incorrect\n");
1096 		return qfalse;
1097 	} //end if
1098 	//read all the portal cache
1099 	for (i = 0; i < routecacheheader.numportalcache; i++)
1100 	{
1101 		cache = AAS_ReadCache(fp);
1102 		cache->next = aasworld.portalcache[cache->areanum];
1103 		cache->prev = NULL;
1104 		if (aasworld.portalcache[cache->areanum])
1105 			aasworld.portalcache[cache->areanum]->prev = cache;
1106 		aasworld.portalcache[cache->areanum] = cache;
1107 	} //end for
1108 	//read all the cluster area cache
1109 	for (i = 0; i < routecacheheader.numareacache; i++)
1110 	{
1111 		cache = AAS_ReadCache(fp);
1112 		clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);
1113 		cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum];
1114 		cache->prev = NULL;
1115 		if (aasworld.clusterareacache[cache->cluster][clusterareanum])
1116 			aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache;
1117 		aasworld.clusterareacache[cache->cluster][clusterareanum] = cache;
1118 	} //end for
1119 	// read the visareas
1120 	/*
1121 	aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *));
1122 	aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte));
1123 	for (i = 0; i < aasworld.numareas; i++)
1124 	{
1125 		botimport.FS_Read(&size, sizeof(size), fp );
1126 		if (size) {
1127 			aasworld.areavisibility[i] = (byte *) GetMemory(size);
1128 			botimport.FS_Read(aasworld.areavisibility[i], size, fp );
1129 		}
1130 	}
1131 	*/
1132 	//
1133 	botimport.FS_FCloseFile(fp);
1134 	return qtrue;
1135 } //end of the function AAS_ReadRouteCache
1136 //===========================================================================
1137 //
1138 // Parameter:			-
1139 // Returns:				-
1140 // Changes Globals:		-
1141 //===========================================================================
1142 #define MAX_REACHABILITYPASSAREAS		32
1143 
AAS_InitReachabilityAreas(void)1144 void AAS_InitReachabilityAreas(void)
1145 {
1146 	int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS];
1147 	int numreachareas;
1148 	aas_reachability_t *reach;
1149 	vec3_t start, end;
1150 
1151 	if (aasworld.reachabilityareas)
1152 		FreeMemory(aasworld.reachabilityareas);
1153 	if (aasworld.reachabilityareaindex)
1154 		FreeMemory(aasworld.reachabilityareaindex);
1155 
1156 	aasworld.reachabilityareas = (aas_reachabilityareas_t *)
1157 				GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t));
1158 	aasworld.reachabilityareaindex = (int *)
1159 				GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int));
1160 	numreachareas = 0;
1161 	for (i = 0; i < aasworld.reachabilitysize; i++)
1162 	{
1163 		reach = &aasworld.reachability[i];
1164 		numareas = 0;
1165 		switch(reach->traveltype & TRAVELTYPE_MASK)
1166 		{
1167 			//trace areas from start to end
1168 			case TRAVEL_BARRIERJUMP:
1169 			case TRAVEL_WATERJUMP:
1170 				VectorCopy(reach->start, end);
1171 				end[2] = reach->end[2];
1172 				numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS);
1173 				break;
1174 			case TRAVEL_WALKOFFLEDGE:
1175 				VectorCopy(reach->end, start);
1176 				start[2] = reach->start[2];
1177 				numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);
1178 				break;
1179 			case TRAVEL_GRAPPLEHOOK:
1180 				numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);
1181 				break;
1182 
1183 			//trace arch
1184 			case TRAVEL_JUMP: break;
1185 			case TRAVEL_ROCKETJUMP: break;
1186 			case TRAVEL_BFGJUMP: break;
1187 			case TRAVEL_JUMPPAD: break;
1188 
1189 			//trace from reach->start to entity center, along entity movement
1190 			//and from entity center to reach->end
1191 			case TRAVEL_ELEVATOR: break;
1192 			case TRAVEL_FUNCBOB: break;
1193 
1194 			//no areas in between
1195 			case TRAVEL_WALK: break;
1196 			case TRAVEL_CROUCH: break;
1197 			case TRAVEL_LADDER: break;
1198 			case TRAVEL_SWIM: break;
1199 			case TRAVEL_TELEPORT: break;
1200 			default: break;
1201 		} //end switch
1202 		aasworld.reachabilityareas[i].firstarea = numreachareas;
1203 		aasworld.reachabilityareas[i].numareas = numareas;
1204 		for (j = 0; j < numareas; j++)
1205 		{
1206 			aasworld.reachabilityareaindex[numreachareas++] = areas[j];
1207 		} //end for
1208 	} //end for
1209 } //end of the function AAS_InitReachabilityAreas
1210 //===========================================================================
1211 //
1212 // Parameter:			-
1213 // Returns:				-
1214 // Changes Globals:		-
1215 //===========================================================================
AAS_InitRouting(void)1216 void AAS_InitRouting(void)
1217 {
1218 	AAS_InitTravelFlagFromType();
1219 	//
1220 	AAS_InitAreaContentsTravelFlags();
1221 	//initialize the routing update fields
1222 	AAS_InitRoutingUpdate();
1223 	//create reversed reachability links used by the routing update algorithm
1224 	AAS_CreateReversedReachability();
1225 	//initialize the cluster cache
1226 	AAS_InitClusterAreaCache();
1227 	//initialize portal cache
1228 	AAS_InitPortalCache();
1229 	//initialize the area travel times
1230 	AAS_CalculateAreaTravelTimes();
1231 	//calculate the maximum travel times through portals
1232 	AAS_InitPortalMaxTravelTimes();
1233 	//get the areas reachabilities go through
1234 	AAS_InitReachabilityAreas();
1235 	//
1236 #ifdef ROUTING_DEBUG
1237 	numareacacheupdates = 0;
1238 	numportalcacheupdates = 0;
1239 #endif //ROUTING_DEBUG
1240 	//
1241 	routingcachesize = 0;
1242 	max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096");
1243 	// read any routing cache if available
1244 	AAS_ReadRouteCache();
1245 } //end of the function AAS_InitRouting
1246 //===========================================================================
1247 //
1248 // Parameter:			-
1249 // Returns:				-
1250 // Changes Globals:		-
1251 //===========================================================================
AAS_FreeRoutingCaches(void)1252 void AAS_FreeRoutingCaches(void)
1253 {
1254 	// free all the existing cluster area cache
1255 	AAS_FreeAllClusterAreaCache();
1256 	// free all the existing portal cache
1257 	AAS_FreeAllPortalCache();
1258 	// free cached travel times within areas
1259 	if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);
1260 	aasworld.areatraveltimes = NULL;
1261 	// free cached maximum travel time through cluster portals
1262 	if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);
1263 	aasworld.portalmaxtraveltimes = NULL;
1264 	// free reversed reachability links
1265 	if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);
1266 	aasworld.reversedreachability = NULL;
1267 	// free routing algorithm memory
1268 	if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);
1269 	aasworld.areaupdate = NULL;
1270 	if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);
1271 	aasworld.portalupdate = NULL;
1272 	// free lists with areas the reachabilities go through
1273 	if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas);
1274 	aasworld.reachabilityareas = NULL;
1275 	// free the reachability area index
1276 	if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex);
1277 	aasworld.reachabilityareaindex = NULL;
1278 	// free area contents travel flags look up table
1279 	if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);
1280 	aasworld.areacontentstravelflags = NULL;
1281 } //end of the function AAS_FreeRoutingCaches
1282 //===========================================================================
1283 // update the given routing cache
1284 //
1285 // Parameter:			areacache		: routing cache to update
1286 // Returns:				-
1287 // Changes Globals:		-
1288 //===========================================================================
AAS_UpdateAreaRoutingCache(aas_routingcache_t * areacache)1289 void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache)
1290 {
1291 	int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum;
1292 	int numreachabilityareas;
1293 	unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed
1294 	aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
1295 	aas_reachability_t *reach;
1296 	aas_reversedreachability_t *revreach;
1297 	aas_reversedlink_t *revlink;
1298 
1299 #ifdef ROUTING_DEBUG
1300 	numareacacheupdates++;
1301 #endif //ROUTING_DEBUG
1302 	//number of reachability areas within this cluster
1303 	numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas;
1304 	//
1305 	aasworld.frameroutingupdates++;
1306 	//clear the routing update fields
1307 //	Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t));
1308 	//
1309 	badtravelflags = ~areacache->travelflags;
1310 	//
1311 	clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum);
1312 	if (clusterareanum >= numreachabilityareas) return;
1313 	//
1314 	Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes));
1315 	//
1316 	curupdate = &aasworld.areaupdate[clusterareanum];
1317 	curupdate->areanum = areacache->areanum;
1318 	//VectorCopy(areacache->origin, curupdate->start);
1319 	curupdate->areatraveltimes = startareatraveltimes;
1320 	curupdate->tmptraveltime = areacache->starttraveltime;
1321 	//
1322 	areacache->traveltimes[clusterareanum] = areacache->starttraveltime;
1323 	//put the area to start with in the current read list
1324 	curupdate->next = NULL;
1325 	curupdate->prev = NULL;
1326 	updateliststart = curupdate;
1327 	updatelistend = curupdate;
1328 	//while there are updates in the current list
1329 	while (updateliststart)
1330 	{
1331 		curupdate = updateliststart;
1332 		//
1333 		if (curupdate->next) curupdate->next->prev = NULL;
1334 		else updatelistend = NULL;
1335 		updateliststart = curupdate->next;
1336 		//
1337 		curupdate->inlist = qfalse;
1338 		//check all reversed reachability links
1339 		revreach = &aasworld.reversedreachability[curupdate->areanum];
1340 		//
1341 		for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++)
1342 		{
1343 			linknum = revlink->linknum;
1344 			reach = &aasworld.reachability[linknum];
1345 			//if there is used an undesired travel type
1346 			if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;
1347 			//if not allowed to enter the next area
1348 			if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue;
1349 			//if the next area has a not allowed travel flag
1350 			if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;
1351 			//number of the area the reversed reachability leads to
1352 			nextareanum = revlink->areanum;
1353 			//get the cluster number of the area
1354 			cluster = aasworld.areasettings[nextareanum].cluster;
1355 			//don't leave the cluster
1356 			if (cluster > 0 && cluster != areacache->cluster) continue;
1357 			//get the number of the area in the cluster
1358 			clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum);
1359 			if (clusterareanum >= numreachabilityareas) continue;
1360 			//time already travelled plus the traveltime through
1361 			//the current area plus the travel time from the reachability
1362 			t = curupdate->tmptraveltime +
1363 						//AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) +
1364 						curupdate->areatraveltimes[i] +
1365 							reach->traveltime;
1366 			//
1367 			if (!areacache->traveltimes[clusterareanum] ||
1368 					areacache->traveltimes[clusterareanum] > t)
1369 			{
1370 				areacache->traveltimes[clusterareanum] = t;
1371 				areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea;
1372 				nextupdate = &aasworld.areaupdate[clusterareanum];
1373 				nextupdate->areanum = nextareanum;
1374 				nextupdate->tmptraveltime = t;
1375 				//VectorCopy(reach->start, nextupdate->start);
1376 				nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum -
1377 													aasworld.areasettings[nextareanum].firstreachablearea];
1378 				if (!nextupdate->inlist)
1379 				{
1380 					// we add the update to the end of the list
1381 					// we could also use a B+ tree to have a real sorted list
1382 					// on travel time which makes for faster routing updates
1383 					nextupdate->next = NULL;
1384 					nextupdate->prev = updatelistend;
1385 					if (updatelistend) updatelistend->next = nextupdate;
1386 					else updateliststart = nextupdate;
1387 					updatelistend = nextupdate;
1388 					nextupdate->inlist = qtrue;
1389 				} //end if
1390 			} //end if
1391 		} //end for
1392 	} //end while
1393 } //end of the function AAS_UpdateAreaRoutingCache
1394 //===========================================================================
1395 //
1396 // Parameter:			-
1397 // Returns:				-
1398 // Changes Globals:		-
1399 //===========================================================================
AAS_GetAreaRoutingCache(int clusternum,int areanum,int travelflags)1400 aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags)
1401 {
1402 	int clusterareanum;
1403 	aas_routingcache_t *cache, *clustercache;
1404 
1405 	//number of the area in the cluster
1406 	clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
1407 	//pointer to the cache for the area in the cluster
1408 	clustercache = aasworld.clusterareacache[clusternum][clusterareanum];
1409 	//find the cache without undesired travel flags
1410 	for (cache = clustercache; cache; cache = cache->next)
1411 	{
1412 		//if there aren't used any undesired travel types for the cache
1413 		if (cache->travelflags == travelflags) break;
1414 	} //end for
1415 	//if there was no cache
1416 	if (!cache)
1417 	{
1418 		cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas);
1419 		cache->cluster = clusternum;
1420 		cache->areanum = areanum;
1421 		VectorCopy(aasworld.areas[areanum].center, cache->origin);
1422 		cache->starttraveltime = 1;
1423 		cache->travelflags = travelflags;
1424 		cache->prev = NULL;
1425 		cache->next = clustercache;
1426 		if (clustercache) clustercache->prev = cache;
1427 		aasworld.clusterareacache[clusternum][clusterareanum] = cache;
1428 		AAS_UpdateAreaRoutingCache(cache);
1429 	} //end if
1430 	else
1431 	{
1432 		AAS_UnlinkCache(cache);
1433 	} //end else
1434 	//the cache has been accessed
1435 	cache->time = AAS_RoutingTime();
1436 	cache->type = CACHETYPE_AREA;
1437 	AAS_LinkCache(cache);
1438 	return cache;
1439 } //end of the function AAS_GetAreaRoutingCache
1440 //===========================================================================
1441 //
1442 // Parameter:			-
1443 // Returns:				-
1444 // Changes Globals:		-
1445 //===========================================================================
AAS_UpdatePortalRoutingCache(aas_routingcache_t * portalcache)1446 void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache)
1447 {
1448 	int i, portalnum, clusterareanum, clusternum;
1449 	unsigned short int t;
1450 	aas_portal_t *portal;
1451 	aas_cluster_t *cluster;
1452 	aas_routingcache_t *cache;
1453 	aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
1454 
1455 #ifdef ROUTING_DEBUG
1456 	numportalcacheupdates++;
1457 #endif //ROUTING_DEBUG
1458 	//clear the routing update fields
1459 //	Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t));
1460 	//
1461 	curupdate = &aasworld.portalupdate[aasworld.numportals];
1462 	curupdate->cluster = portalcache->cluster;
1463 	curupdate->areanum = portalcache->areanum;
1464 	curupdate->tmptraveltime = portalcache->starttraveltime;
1465 	//if the start area is a cluster portal, store the travel time for that portal
1466 	clusternum = aasworld.areasettings[portalcache->areanum].cluster;
1467 	if (clusternum < 0)
1468 	{
1469 		portalcache->traveltimes[-clusternum] = portalcache->starttraveltime;
1470 	} //end if
1471 	//put the area to start with in the current read list
1472 	curupdate->next = NULL;
1473 	curupdate->prev = NULL;
1474 	updateliststart = curupdate;
1475 	updatelistend = curupdate;
1476 	//while there are updates in the current list
1477 	while (updateliststart)
1478 	{
1479 		curupdate = updateliststart;
1480 		//remove the current update from the list
1481 		if (curupdate->next) curupdate->next->prev = NULL;
1482 		else updatelistend = NULL;
1483 		updateliststart = curupdate->next;
1484 		//current update is removed from the list
1485 		curupdate->inlist = qfalse;
1486 		//
1487 		cluster = &aasworld.clusters[curupdate->cluster];
1488 		//
1489 		cache = AAS_GetAreaRoutingCache(curupdate->cluster,
1490 								curupdate->areanum, portalcache->travelflags);
1491 		//take all portals of the cluster
1492 		for (i = 0; i < cluster->numportals; i++)
1493 		{
1494 			portalnum = aasworld.portalindex[cluster->firstportal + i];
1495 			portal = &aasworld.portals[portalnum];
1496 			//if this is the portal of the current update continue
1497 			if (portal->areanum == curupdate->areanum) continue;
1498 			//
1499 			clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum);
1500 			if (clusterareanum >= cluster->numreachabilityareas) continue;
1501 			//
1502 			t = cache->traveltimes[clusterareanum];
1503 			if (!t) continue;
1504 			t += curupdate->tmptraveltime;
1505 			//
1506 			if (!portalcache->traveltimes[portalnum] ||
1507 					portalcache->traveltimes[portalnum] > t)
1508 			{
1509 				portalcache->traveltimes[portalnum] = t;
1510 				nextupdate = &aasworld.portalupdate[portalnum];
1511 				if (portal->frontcluster == curupdate->cluster)
1512 				{
1513 					nextupdate->cluster = portal->backcluster;
1514 				} //end if
1515 				else
1516 				{
1517 					nextupdate->cluster = portal->frontcluster;
1518 				} //end else
1519 				nextupdate->areanum = portal->areanum;
1520 				//add travel time through the actual portal area for the next update
1521 				nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum];
1522 				if (!nextupdate->inlist)
1523 				{
1524 					// we add the update to the end of the list
1525 					// we could also use a B+ tree to have a real sorted list
1526 					// on travel time which makes for faster routing updates
1527 					nextupdate->next = NULL;
1528 					nextupdate->prev = updatelistend;
1529 					if (updatelistend) updatelistend->next = nextupdate;
1530 					else updateliststart = nextupdate;
1531 					updatelistend = nextupdate;
1532 					nextupdate->inlist = qtrue;
1533 				} //end if
1534 			} //end if
1535 		} //end for
1536 	} //end while
1537 } //end of the function AAS_UpdatePortalRoutingCache
1538 //===========================================================================
1539 //
1540 // Parameter:			-
1541 // Returns:				-
1542 // Changes Globals:		-
1543 //===========================================================================
AAS_GetPortalRoutingCache(int clusternum,int areanum,int travelflags)1544 aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags)
1545 {
1546 	aas_routingcache_t *cache;
1547 
1548 	//find the cached portal routing if existing
1549 	for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next)
1550 	{
1551 		if (cache->travelflags == travelflags) break;
1552 	} //end for
1553 	//if the portal routing isn't cached
1554 	if (!cache)
1555 	{
1556 		cache = AAS_AllocRoutingCache(aasworld.numportals);
1557 		cache->cluster = clusternum;
1558 		cache->areanum = areanum;
1559 		VectorCopy(aasworld.areas[areanum].center, cache->origin);
1560 		cache->starttraveltime = 1;
1561 		cache->travelflags = travelflags;
1562 		//add the cache to the cache list
1563 		cache->prev = NULL;
1564 		cache->next = aasworld.portalcache[areanum];
1565 		if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache;
1566 		aasworld.portalcache[areanum] = cache;
1567 		//update the cache
1568 		AAS_UpdatePortalRoutingCache(cache);
1569 	} //end if
1570 	else
1571 	{
1572 		AAS_UnlinkCache(cache);
1573 	} //end else
1574 	//the cache has been accessed
1575 	cache->time = AAS_RoutingTime();
1576 	cache->type = CACHETYPE_PORTAL;
1577 	AAS_LinkCache(cache);
1578 	return cache;
1579 } //end of the function AAS_GetPortalRoutingCache
1580 //===========================================================================
1581 //
1582 // Parameter:			-
1583 // Returns:				-
1584 // Changes Globals:		-
1585 //===========================================================================
AAS_AreaRouteToGoalArea(int areanum,vec3_t origin,int goalareanum,int travelflags,int * traveltime,int * reachnum)1586 int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum)
1587 {
1588 	int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum;
1589 	unsigned short int t, besttime;
1590 	aas_portal_t *portal;
1591 	aas_cluster_t *cluster;
1592 	aas_routingcache_t *areacache, *portalcache;
1593 	aas_reachability_t *reach;
1594 
1595 	if (!aasworld.initialized) return qfalse;
1596 
1597 	if (areanum == goalareanum)
1598 	{
1599 		*traveltime = 1;
1600 		*reachnum = 0;
1601 		return qtrue;
1602 	}
1603 	//
1604 	if (areanum <= 0 || areanum >= aasworld.numareas)
1605 	{
1606 		if (bot_developer)
1607 		{
1608 			botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum);
1609 		} //end if
1610 		return qfalse;
1611 	} //end if
1612 	if (goalareanum <= 0 || goalareanum >= aasworld.numareas)
1613 	{
1614 		if (bot_developer)
1615 		{
1616 			botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum);
1617 		} //end if
1618 		return qfalse;
1619 	} //end if
1620 	// make sure the routing cache doesn't grow to large
1621 	while(AvailableMemory() < 1 * 1024 * 1024) {
1622 		if (!AAS_FreeOldestCache()) break;
1623 	}
1624 	//
1625 	if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum))
1626 	{
1627 		travelflags |= TFL_DONOTENTER;
1628 	} //end if
1629 	//NOTE: the number of routing updates is limited per frame
1630 	/*
1631 	if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES)
1632 	{
1633 #ifdef DEBUG
1634 		//Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed");
1635 #endif
1636 		return 0;
1637 	} //end if
1638 	*/
1639 	//
1640 	clusternum = aasworld.areasettings[areanum].cluster;
1641 	goalclusternum = aasworld.areasettings[goalareanum].cluster;
1642 	//check if the area is a portal of the goal area cluster
1643 	if (clusternum < 0 && goalclusternum > 0)
1644 	{
1645 		portal = &aasworld.portals[-clusternum];
1646 		if (portal->frontcluster == goalclusternum ||
1647 				portal->backcluster == goalclusternum)
1648 		{
1649 			clusternum = goalclusternum;
1650 		} //end if
1651 	} //end if
1652 	//check if the goalarea is a portal of the area cluster
1653 	else if (clusternum > 0 && goalclusternum < 0)
1654 	{
1655 		portal = &aasworld.portals[-goalclusternum];
1656 		if (portal->frontcluster == clusternum ||
1657 				portal->backcluster == clusternum)
1658 		{
1659 			goalclusternum = clusternum;
1660 		} //end if
1661 	} //end if
1662 	//if both areas are in the same cluster
1663 	//NOTE: there might be a shorter route via another cluster!!! but we don't care
1664 	if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum)
1665 	{
1666 		//
1667 		areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags);
1668 		//the number of the area in the cluster
1669 		clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
1670 		//the cluster the area is in
1671 		cluster = &aasworld.clusters[clusternum];
1672 		//if the area is NOT a reachability area
1673 		if (clusterareanum >= cluster->numreachabilityareas) return 0;
1674 		//if it is possible to travel to the goal area through this cluster
1675 		if (areacache->traveltimes[clusterareanum] != 0)
1676 		{
1677 			*reachnum = aasworld.areasettings[areanum].firstreachablearea +
1678 							areacache->reachabilities[clusterareanum];
1679 			if (!origin) {
1680 				*traveltime = areacache->traveltimes[clusterareanum];
1681 				return qtrue;
1682 			}
1683 			reach = &aasworld.reachability[*reachnum];
1684 			*traveltime = areacache->traveltimes[clusterareanum] +
1685 							AAS_AreaTravelTime(areanum, origin, reach->start);
1686 			//
1687 			return qtrue;
1688 		} //end if
1689 	} //end if
1690 	//
1691 	clusternum = aasworld.areasettings[areanum].cluster;
1692 	goalclusternum = aasworld.areasettings[goalareanum].cluster;
1693 	//if the goal area is a portal
1694 	if (goalclusternum < 0)
1695 	{
1696 		//just assume the goal area is part of the front cluster
1697 		portal = &aasworld.portals[-goalclusternum];
1698 		goalclusternum = portal->frontcluster;
1699 	} //end if
1700 	//get the portal routing cache
1701 	portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags);
1702 	//if the area is a cluster portal, read directly from the portal cache
1703 	if (clusternum < 0)
1704 	{
1705 		*traveltime = portalcache->traveltimes[-clusternum];
1706 		*reachnum = aasworld.areasettings[areanum].firstreachablearea +
1707 						portalcache->reachabilities[-clusternum];
1708 		return qtrue;
1709 	} //end if
1710 	//
1711 	besttime = 0;
1712 	bestreachnum = -1;
1713 	//the cluster the area is in
1714 	cluster = &aasworld.clusters[clusternum];
1715 	//find the portal of the area cluster leading towards the goal area
1716 	for (i = 0; i < cluster->numportals; i++)
1717 	{
1718 		portalnum = aasworld.portalindex[cluster->firstportal + i];
1719 		//if the goal area isn't reachable from the portal
1720 		if (!portalcache->traveltimes[portalnum]) continue;
1721 		//
1722 		portal = &aasworld.portals[portalnum];
1723 		//get the cache of the portal area
1724 		areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags);
1725 		//current area inside the current cluster
1726 		clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
1727 		//if the area is NOT a reachability area
1728 		if (clusterareanum >= cluster->numreachabilityareas) continue;
1729 		//if the portal is NOT reachable from this area
1730 		if (!areacache->traveltimes[clusterareanum]) continue;
1731 		//total travel time is the travel time the portal area is from
1732 		//the goal area plus the travel time towards the portal area
1733 		t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum];
1734 		//FIXME: add the exact travel time through the actual portal area
1735 		//NOTE: for now we just add the largest travel time through the portal area
1736 		//		because we can't directly calculate the exact travel time
1737 		//		to be more specific we don't know which reachability was used to travel
1738 		//		into the portal area
1739 		t += aasworld.portalmaxtraveltimes[portalnum];
1740 		//
1741 		if (origin)
1742 		{
1743 			*reachnum = aasworld.areasettings[areanum].firstreachablearea +
1744 							areacache->reachabilities[clusterareanum];
1745 			reach = aasworld.reachability + *reachnum;
1746 			t += AAS_AreaTravelTime(areanum, origin, reach->start);
1747 		} //end if
1748 		//if the time is better than the one already found
1749 		if (!besttime || t < besttime)
1750 		{
1751 			bestreachnum = *reachnum;
1752 			besttime = t;
1753 		} //end if
1754 	} //end for
1755 	if (bestreachnum < 0) {
1756 		return qfalse;
1757 	}
1758 	*reachnum = bestreachnum;
1759 	*traveltime = besttime;
1760 	return qtrue;
1761 } //end of the function AAS_AreaRouteToGoalArea
1762 //===========================================================================
1763 //
1764 // Parameter:			-
1765 // Returns:				-
1766 // Changes Globals:		-
1767 //===========================================================================
AAS_AreaTravelTimeToGoalArea(int areanum,vec3_t origin,int goalareanum,int travelflags)1768 int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)
1769 {
1770 	int traveltime, reachnum;
1771 
1772 	if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))
1773 	{
1774 		return traveltime;
1775 	}
1776 	return 0;
1777 } //end of the function AAS_AreaTravelTimeToGoalArea
1778 //===========================================================================
1779 //
1780 // Parameter:			-
1781 // Returns:				-
1782 // Changes Globals:		-
1783 //===========================================================================
AAS_AreaReachabilityToGoalArea(int areanum,vec3_t origin,int goalareanum,int travelflags)1784 int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)
1785 {
1786 	int traveltime, reachnum;
1787 
1788 	if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))
1789 	{
1790 		return reachnum;
1791 	}
1792 	return 0;
1793 } //end of the function AAS_AreaReachabilityToGoalArea
1794 //===========================================================================
1795 // predict the route and stop on one of the stop events
1796 //
1797 // Parameter:			-
1798 // Returns:				-
1799 // Changes Globals:		-
1800 //===========================================================================
AAS_PredictRoute(struct aas_predictroute_s * route,int areanum,vec3_t origin,int goalareanum,int travelflags,int maxareas,int maxtime,int stopevent,int stopcontents,int stoptfl,int stopareanum)1801 int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin,
1802 							int goalareanum, int travelflags, int maxareas, int maxtime,
1803 							int stopevent, int stopcontents, int stoptfl, int stopareanum)
1804 {
1805 	int curareanum, reachnum, i, j, testareanum;
1806 	vec3_t curorigin;
1807 	aas_reachability_t *reach;
1808 	aas_reachabilityareas_t *reachareas;
1809 
1810 	//init output
1811 	route->stopevent = RSE_NONE;
1812 	route->endarea = goalareanum;
1813 	route->endcontents = 0;
1814 	route->endtravelflags = 0;
1815 	VectorCopy(origin, route->endpos);
1816 	route->time = 0;
1817 
1818 	curareanum = areanum;
1819 	VectorCopy(origin, curorigin);
1820 
1821 	for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++)
1822 	{
1823 		reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags);
1824 		if (!reachnum)
1825 		{
1826 			route->stopevent = RSE_NOROUTE;
1827 			return qfalse;
1828 		} //end if
1829 		reach = &aasworld.reachability[reachnum];
1830 		//
1831 		if (stopevent & RSE_USETRAVELTYPE)
1832 		{
1833 			if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl)
1834 			{
1835 				route->stopevent = RSE_USETRAVELTYPE;
1836 				route->endarea = curareanum;
1837 				route->endcontents = aasworld.areasettings[curareanum].contents;
1838 				route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);
1839 				VectorCopy(reach->start, route->endpos);
1840 				return qtrue;
1841 			} //end if
1842 			if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl)
1843 			{
1844 				route->stopevent = RSE_USETRAVELTYPE;
1845 				route->endarea = reach->areanum;
1846 				route->endcontents = aasworld.areasettings[reach->areanum].contents;
1847 				route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum);
1848 				VectorCopy(reach->end, route->endpos);
1849 				route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
1850 				route->time += reach->traveltime;
1851 				return qtrue;
1852 			} //end if
1853 		} //end if
1854 		reachareas = &aasworld.reachabilityareas[reachnum];
1855 		for (j = 0; j < reachareas->numareas + 1; j++)
1856 		{
1857 			if (j >= reachareas->numareas)
1858 				testareanum = reach->areanum;
1859 			else
1860 				testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j];
1861 			if (stopevent & RSE_ENTERCONTENTS)
1862 			{
1863 				if (aasworld.areasettings[testareanum].contents & stopcontents)
1864 				{
1865 					route->stopevent = RSE_ENTERCONTENTS;
1866 					route->endarea = testareanum;
1867 					route->endcontents = aasworld.areasettings[testareanum].contents;
1868 					VectorCopy(reach->end, route->endpos);
1869 					route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
1870 					route->time += reach->traveltime;
1871 					return qtrue;
1872 				} //end if
1873 			} //end if
1874 			if (stopevent & RSE_ENTERAREA)
1875 			{
1876 				if (testareanum == stopareanum)
1877 				{
1878 					route->stopevent = RSE_ENTERAREA;
1879 					route->endarea = testareanum;
1880 					route->endcontents = aasworld.areasettings[testareanum].contents;
1881 					VectorCopy(reach->start, route->endpos);
1882 					return qtrue;
1883 				} //end if
1884 			} //end if
1885 		} //end for
1886 
1887 		route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
1888 		route->time += reach->traveltime;
1889 		route->endarea = reach->areanum;
1890 		route->endcontents = aasworld.areasettings[reach->areanum].contents;
1891 		route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);
1892 		VectorCopy(reach->end, route->endpos);
1893 		//
1894 		curareanum = reach->areanum;
1895 		VectorCopy(reach->end, curorigin);
1896 		//
1897 		if (maxtime && route->time > maxtime)
1898 			break;
1899 	} //end while
1900 	if (curareanum != goalareanum)
1901 		return qfalse;
1902 	return qtrue;
1903 } //end of the function AAS_PredictRoute
1904 //===========================================================================
1905 //
1906 // Parameter:			-
1907 // Returns:				-
1908 // Changes Globals:		-
1909 //===========================================================================
AAS_BridgeWalkable(int areanum)1910 int AAS_BridgeWalkable(int areanum)
1911 {
1912 	return qfalse;
1913 } //end of the function AAS_BridgeWalkable
1914 //===========================================================================
1915 //
1916 // Parameter:			-
1917 // Returns:				-
1918 // Changes Globals:		-
1919 //===========================================================================
AAS_ReachabilityFromNum(int num,struct aas_reachability_s * reach)1920 void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach)
1921 {
1922 	if (!aasworld.initialized)
1923 	{
1924 		Com_Memset(reach, 0, sizeof(aas_reachability_t));
1925 		return;
1926 	} //end if
1927 	if (num < 0 || num >= aasworld.reachabilitysize)
1928 	{
1929 		Com_Memset(reach, 0, sizeof(aas_reachability_t));
1930 		return;
1931 	} //end if
1932 	Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));;
1933 } //end of the function AAS_ReachabilityFromNum
1934 //===========================================================================
1935 //
1936 // Parameter:			-
1937 // Returns:				-
1938 // Changes Globals:		-
1939 //===========================================================================
AAS_NextAreaReachability(int areanum,int reachnum)1940 int AAS_NextAreaReachability(int areanum, int reachnum)
1941 {
1942 	aas_areasettings_t *settings;
1943 
1944 	if (!aasworld.initialized) return 0;
1945 
1946 	if (areanum <= 0 || areanum >= aasworld.numareas)
1947 	{
1948 		botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum);
1949 		return 0;
1950 	} //end if
1951 
1952 	settings = &aasworld.areasettings[areanum];
1953 	if (!reachnum)
1954 	{
1955 		return settings->firstreachablearea;
1956 	} //end if
1957 	if (reachnum < settings->firstreachablearea)
1958 	{
1959 		botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara");
1960 		return 0;
1961 	} //end if
1962 	reachnum++;
1963 	if (reachnum >= settings->firstreachablearea + settings->numreachableareas)
1964 	{
1965 		return 0;
1966 	} //end if
1967 	return reachnum;
1968 } //end of the function AAS_NextAreaReachability
1969 //===========================================================================
1970 //
1971 // Parameter:			-
1972 // Returns:				-
1973 // Changes Globals:		-
1974 //===========================================================================
AAS_NextModelReachability(int num,int modelnum)1975 int AAS_NextModelReachability(int num, int modelnum)
1976 {
1977 	int i;
1978 
1979 	if (num <= 0) num = 1;
1980 	else if (num >= aasworld.reachabilitysize) return 0;
1981 	else num++;
1982 	//
1983 	for (i = num; i < aasworld.reachabilitysize; i++)
1984 	{
1985 		if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR)
1986 		{
1987 			if (aasworld.reachability[i].facenum == modelnum) return i;
1988 		} //end if
1989 		else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
1990 		{
1991 			if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i;
1992 		} //end if
1993 	} //end for
1994 	return 0;
1995 } //end of the function AAS_NextModelReachability
1996 //===========================================================================
1997 //
1998 // Parameter:			-
1999 // Returns:				-
2000 // Changes Globals:		-
2001 //===========================================================================
AAS_RandomGoalArea(int areanum,int travelflags,int * goalareanum,vec3_t goalorigin)2002 int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin)
2003 {
2004 	int i, n, t;
2005 	vec3_t start, end;
2006 	aas_trace_t trace;
2007 
2008 	//if the area has no reachabilities
2009 	if (!AAS_AreaReachability(areanum)) return qfalse;
2010 	//
2011 	n = aasworld.numareas * random();
2012 	for (i = 0; i < aasworld.numareas; i++)
2013 	{
2014 		if (n <= 0) n = 1;
2015 		if (n >= aasworld.numareas) n = 1;
2016 		if (AAS_AreaReachability(n))
2017 		{
2018 			t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags);
2019 			//if the goal is reachable
2020 			if (t > 0)
2021 			{
2022 				if (AAS_AreaSwim(n))
2023 				{
2024 					*goalareanum = n;
2025 					VectorCopy(aasworld.areas[n].center, goalorigin);
2026 					//botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum);
2027 					return qtrue;
2028 				} //end if
2029 				VectorCopy(aasworld.areas[n].center, start);
2030 				if (!AAS_PointAreaNum(start))
2031 					Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]);
2032 				VectorCopy(start, end);
2033 				end[2] -= 300;
2034 				trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
2035 				if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n)
2036 				{
2037 					if (AAS_AreaGroundFaceArea(n) > 300)
2038 					{
2039 						*goalareanum = n;
2040 						VectorCopy(trace.endpos, goalorigin);
2041 						//botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum);
2042 						return qtrue;
2043 					} //end if
2044 				} //end if
2045 			} //end if
2046 		} //end if
2047 		n++;
2048 	} //end for
2049 	return qfalse;
2050 } //end of the function AAS_RandomGoalArea
2051 //===========================================================================
2052 //
2053 // Parameter:			-
2054 // Returns:				-
2055 // Changes Globals:		-
2056 //===========================================================================
AAS_AreaVisible(int srcarea,int destarea)2057 int AAS_AreaVisible(int srcarea, int destarea)
2058 {
2059 	return qfalse;
2060 } //end of the function AAS_AreaVisible
2061 //===========================================================================
2062 //
2063 // Parameter:			-
2064 // Returns:				-
2065 // Changes Globals:		-
2066 //===========================================================================
DistancePointToLine(vec3_t v1,vec3_t v2,vec3_t point)2067 float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point)
2068 {
2069 	vec3_t vec, p2;
2070 
2071 	AAS_ProjectPointOntoVector(point, v1, v2, p2);
2072 	VectorSubtract(point, p2, vec);
2073 	return VectorLength(vec);
2074 } //end of the function DistancePointToLine
2075 //===========================================================================
2076 //
2077 // Parameter:			-
2078 // Returns:				-
2079 // Changes Globals:		-
2080 //===========================================================================
AAS_NearestHideArea(int srcnum,vec3_t origin,int areanum,int enemynum,vec3_t enemyorigin,int enemyareanum,int travelflags)2081 int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags)
2082 {
2083 	int i, j, nextareanum, badtravelflags, numreach, bestarea;
2084 	unsigned short int t, besttraveltime;
2085 	static unsigned short int *hidetraveltimes;
2086 	aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
2087 	aas_reachability_t *reach;
2088 	float dist1, dist2;
2089 	vec3_t v1, v2, p;
2090 	qboolean startVisible;
2091 
2092 	//
2093 	if (!hidetraveltimes)
2094 	{
2095 		hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int));
2096 	} //end if
2097 	else
2098 	{
2099 		Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int));
2100 	} //end else
2101 	besttraveltime = 0;
2102 	bestarea = 0;
2103 	//assume visible
2104 	startVisible = qtrue;
2105 	//
2106 	badtravelflags = ~travelflags;
2107 	//
2108 	curupdate = &aasworld.areaupdate[areanum];
2109 	curupdate->areanum = areanum;
2110 	VectorCopy(origin, curupdate->start);
2111 	curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0];
2112 	curupdate->tmptraveltime = 0;
2113 	//put the area to start with in the current read list
2114 	curupdate->next = NULL;
2115 	curupdate->prev = NULL;
2116 	updateliststart = curupdate;
2117 	updatelistend = curupdate;
2118 	//while there are updates in the list
2119 	while (updateliststart)
2120 	{
2121 		curupdate = updateliststart;
2122 		//
2123 		if (curupdate->next) curupdate->next->prev = NULL;
2124 		else updatelistend = NULL;
2125 		updateliststart = curupdate->next;
2126 		//
2127 		curupdate->inlist = qfalse;
2128 		//check all reversed reachability links
2129 		numreach = aasworld.areasettings[curupdate->areanum].numreachableareas;
2130 		reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea];
2131 		//
2132 		for (i = 0; i < numreach; i++, reach++)
2133 		{
2134 			//if an undesired travel type is used
2135 			if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;
2136 			//
2137 			if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;
2138 			//number of the area the reachability leads to
2139 			nextareanum = reach->areanum;
2140 			// if this moves us into the enemies area, skip it
2141 			if (nextareanum == enemyareanum) continue;
2142 			//time already travelled plus the traveltime through
2143 			//the current area plus the travel time from the reachability
2144 			t = curupdate->tmptraveltime +
2145 						AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) +
2146 							reach->traveltime;
2147 
2148 			//avoid going near the enemy
2149 			AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p);
2150 			for (j = 0; j < 3; j++)
2151 				if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) ||
2152 					(p[j] < curupdate->start[j] && p[j] < reach->end[j]))
2153 					break;
2154 			if (j < 3)
2155 			{
2156 				VectorSubtract(enemyorigin, reach->end, v2);
2157 			} //end if
2158 			else
2159 			{
2160 				VectorSubtract(enemyorigin, p, v2);
2161 			} //end else
2162 			dist2 = VectorLength(v2);
2163 			//never go through the enemy
2164 			if (dist2 < 40) continue;
2165 			//
2166 			VectorSubtract(enemyorigin, curupdate->start, v1);
2167 			dist1 = VectorLength(v1);
2168 			//
2169 			if (dist2 < dist1)
2170 			{
2171 				t += (dist1 - dist2) * 10;
2172 			}
2173 			// if we weren't visible when starting, make sure we don't move into their view
2174 			if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) {
2175 				continue;
2176 			}
2177 			//
2178 			if (besttraveltime && t >= besttraveltime) continue;
2179 			//
2180 			if (!hidetraveltimes[nextareanum] ||
2181 					hidetraveltimes[nextareanum] > t)
2182 			{
2183 				//if the nextarea is not visible from the enemy area
2184 				if (!AAS_AreaVisible(enemyareanum, nextareanum))
2185 				{
2186 					besttraveltime = t;
2187 					bestarea = nextareanum;
2188 				} //end if
2189 				hidetraveltimes[nextareanum] = t;
2190 				nextupdate = &aasworld.areaupdate[nextareanum];
2191 				nextupdate->areanum = nextareanum;
2192 				nextupdate->tmptraveltime = t;
2193 				//remember where we entered this area
2194 				VectorCopy(reach->end, nextupdate->start);
2195 				//if this update is not in the list yet
2196 				if (!nextupdate->inlist)
2197 				{
2198 					//add the new update to the end of the list
2199 					nextupdate->next = NULL;
2200 					nextupdate->prev = updatelistend;
2201 					if (updatelistend) updatelistend->next = nextupdate;
2202 					else updateliststart = nextupdate;
2203 					updatelistend = nextupdate;
2204 					nextupdate->inlist = qtrue;
2205 				} //end if
2206 			} //end if
2207 		} //end for
2208 	} //end while
2209 	return bestarea;
2210 } //end of the function AAS_NearestHideArea
2211