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