/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_route.c * * desc: AAS * * $Archive: /MissionPack/code/botlib/be_aas_route.c $ * *****************************************************************************/ #include "../qcommon/q_shared.h" #include "l_utils.h" #include "l_memory.h" #include "l_log.h" #include "l_crc.h" #include "l_libvar.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "botlib.h" #include "be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" #define ROUTING_DEBUG //travel time in hundredths of a second = distance * 100 / speed #define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100 #define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 #define DISTANCEFACTOR_WALK 0.33f //walk speed = 300 //cache refresh time #define CACHE_REFRESHTIME 15.0f //15 seconds refresh time //maximum number of routing updates each frame #define MAX_FRAMEROUTINGUPDATES 10 /* area routing cache: stores the distances within one cluster to a specific goal area this goal area is in this same cluster and could be a cluster portal for every cluster there's a list with routing cache for every area in that cluster (including the portals of that cluster) area cache stores aasworld.clusters[?].numreachabilityareas travel times portal routing cache: stores the distances of all portals to a specific goal area this goal area could be in any cluster and could also be a cluster portal for every area (aasworld.numareas) the portal cache stores aasworld.numportals travel times */ #ifdef ROUTING_DEBUG int numareacacheupdates; int numportalcacheupdates; #endif //ROUTING_DEBUG int routingcachesize; int max_routingcachesize; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef ROUTING_DEBUG void AAS_RoutingInfo(void) { botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates); botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates); botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize); } //end of the function AAS_RoutingInfo #endif //ROUTING_DEBUG //=========================================================================== // returns the number of the area in the cluster // assumes the given area is in the given cluster or a portal of the cluster // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static ID_INLINE int AAS_ClusterAreaNum(int cluster, int areanum) { int side, areacluster; areacluster = aasworld.areasettings[areanum].cluster; if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum; else { /*#ifdef ROUTING_DEBUG if (aasworld.portals[-areacluster].frontcluster != cluster && aasworld.portals[-areacluster].backcluster != cluster) { botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n" , -areacluster, cluster); } //end if #endif //ROUTING_DEBUG*/ side = aasworld.portals[-areacluster].frontcluster != cluster; return aasworld.portals[-areacluster].clusterareanum[side]; } //end else } //end of the function AAS_ClusterAreaNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_InitTravelFlagFromType(void) { int i; for (i = 0; i < MAX_TRAVELTYPES; i++) { aasworld.travelflagfortype[i] = TFL_INVALID; } //end for aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID; aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK; aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH; aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP; aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP; aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER; aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE; aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM; aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP; aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT; aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR; aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP; aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP; aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK; aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP; aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP; aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP; aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD; aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB; } //end of the function AAS_InitTravelFlagFromType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static ID_INLINE int AAS_TravelFlagForType_inline(unsigned int traveltype) { int tfl; tfl = 0; if (traveltype & TRAVELFLAG_NOTTEAM1) tfl |= TFL_NOTTEAM1; if (traveltype & TRAVELFLAG_NOTTEAM2) tfl |= TFL_NOTTEAM2; traveltype &= TRAVELTYPE_MASK; if (traveltype >= MAX_TRAVELTYPES) return TFL_INVALID; tfl |= aasworld.travelflagfortype[traveltype]; return tfl; } //end of the function AAS_TravelFlagForType_inline //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_TravelFlagForType(int traveltype) { return AAS_TravelFlagForType_inline(traveltype); } //end of the function AAS_TravelFlagForType_inline //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_UnlinkCache(aas_routingcache_t *cache) { if (cache->time_next) cache->time_next->time_prev = cache->time_prev; else aasworld.newestcache = cache->time_prev; if (cache->time_prev) cache->time_prev->time_next = cache->time_next; else aasworld.oldestcache = cache->time_next; cache->time_next = NULL; cache->time_prev = NULL; } //end of the function AAS_UnlinkCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_LinkCache(aas_routingcache_t *cache) { if (aasworld.newestcache) { aasworld.newestcache->time_next = cache; cache->time_prev = aasworld.newestcache; } //end if else { aasworld.oldestcache = cache; cache->time_prev = NULL; } //end else cache->time_next = NULL; aasworld.newestcache = cache; } //end of the function AAS_LinkCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeRoutingCache(aas_routingcache_t *cache) { AAS_UnlinkCache(cache); routingcachesize -= cache->size; FreeMemory(cache); } //end of the function AAS_FreeRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_RemoveRoutingCacheInCluster( int clusternum ) { int i; aas_routingcache_t *cache, *nextcache; aas_cluster_t *cluster; if (!aasworld.clusterareacache) return; cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numareas; i++) { for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.clusterareacache[clusternum][i] = NULL; } //end for } //end of the function AAS_RemoveRoutingCacheInCluster //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_RemoveRoutingCacheUsingArea( int areanum ) { int i, clusternum; aas_routingcache_t *cache, *nextcache; clusternum = aasworld.areasettings[areanum].cluster; if (clusternum > 0) { //remove all the cache in the cluster the area is in AAS_RemoveRoutingCacheInCluster( clusternum ); } //end if else { // if this is a portal remove all cache in both the front and back cluster AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster ); AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster ); } //end else // remove all portal cache for (i = 0; i < aasworld.numareas; i++) { //refresh portal cache for (cache = aasworld.portalcache[i]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.portalcache[i] = NULL; } //end for } //end of the function AAS_RemoveRoutingCacheUsingArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_EnableRoutingArea(int areanum, int enable) { int flags; if (areanum <= 0 || areanum >= aasworld.numareas) { if (botDeveloper) { botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum); } //end if return 0; } //end if flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED; if (enable < 0) return !flags; if (enable) aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED; else aasworld.areasettings[areanum].areaflags |= AREA_DISABLED; // if the status of the area changed if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) ) { //remove all routing cache involving this area AAS_RemoveRoutingCacheUsingArea( areanum ); } //end if return !flags; } //end of the function AAS_EnableRoutingArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static ID_INLINE float AAS_RoutingTime(void) { return AAS_Time(); } //end of the function AAS_RoutingTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_GetAreaContentsTravelFlags(int areanum) { int contents, tfl; contents = aasworld.areasettings[areanum].contents; tfl = 0; if (contents & AREACONTENTS_WATER) tfl |= TFL_WATER; else if (contents & AREACONTENTS_SLIME) tfl |= TFL_SLIME; else if (contents & AREACONTENTS_LAVA) tfl |= TFL_LAVA; else tfl |= TFL_AIR; if (contents & AREACONTENTS_DONOTENTER) tfl |= TFL_DONOTENTER; if (contents & AREACONTENTS_NOTTEAM1) tfl |= TFL_NOTTEAM1; if (contents & AREACONTENTS_NOTTEAM2) tfl |= TFL_NOTTEAM2; if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE) tfl |= TFL_BRIDGE; return tfl; } //end of the function AAS_GetAreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static ID_INLINE int AAS_AreaContentsTravelFlags_inline(int areanum) { return aasworld.areacontentstravelflags[areanum]; } //end of the function AAS_AreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaContentsTravelFlags(int areanum) { return aasworld.areacontentstravelflags[areanum]; } //end of the function AAS_AreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_InitAreaContentsTravelFlags(void) { int i; if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); // for (i = 0; i < aasworld.numareas; i++) { aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i); } } //end of the function AAS_InitAreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_CreateReversedReachability(void) { int i, n; aas_reversedlink_t *revlink; aas_reachability_t *reach; aas_areasettings_t *settings; char *ptr; #ifdef DEBUG int starttime; starttime = Sys_MilliSeconds(); #endif //free reversed links that have already been created if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); //allocate memory for the reversed reachability links ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) + aasworld.reachabilitysize * sizeof(aas_reversedlink_t)); // aasworld.reversedreachability = (aas_reversedreachability_t *) ptr; //pointer to the memory for the reversed links ptr += aasworld.numareas * sizeof(aas_reversedreachability_t); //check all reachabilities of all areas for (i = 1; i < aasworld.numareas; i++) { //settings of the area settings = &aasworld.areasettings[i]; // if (settings->numreachableareas > 128) botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i); //create reversed links for the reachabilities for (n = 0; n < settings->numreachableareas && n < 128; n++) { //reachability link reach = &aasworld.reachability[settings->firstreachablearea + n]; // revlink = (aas_reversedlink_t *) ptr; ptr += sizeof(aas_reversedlink_t); // revlink->areanum = i; revlink->linknum = settings->firstreachablearea + n; revlink->next = aasworld.reversedreachability[reach->areanum].first; aasworld.reversedreachability[reach->areanum].first = revlink; aasworld.reversedreachability[reach->areanum].numlinks++; } //end for } //end for #ifdef DEBUG botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime); #endif } //end of the function AAS_CreateReversedReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end) { int intdist; float dist; vec3_t dir; VectorSubtract(start, end, dir); dist = VectorLength(dir); //if crouch only area if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH; //if swim area else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM; //normal walk area else dist *= DISTANCEFACTOR_WALK; // intdist = (int) dist; //make sure the distance isn't zero if (intdist <= 0) intdist = 1; return intdist; } //end of the function AAS_AreaTravelTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_CalculateAreaTravelTimes(void) { int i, l, n, size; char *ptr; vec3_t end; const aas_reversedreachability_t *revreach; const aas_reversedlink_t *revlink; aas_reachability_t *reach; aas_areasettings_t *settings; #ifdef DEBUG int starttime; starttime = Sys_MilliSeconds(); #endif //if there are still area travel times, free the memory if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); //get the total size of all the area travel times size = aasworld.numareas * sizeof(unsigned short **); for (i = 0; i < aasworld.numareas; i++) { revreach = &aasworld.reversedreachability[i]; //settings of the area settings = &aasworld.areasettings[i]; // size += settings->numreachableareas * sizeof(unsigned short *); // size += settings->numreachableareas * PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short); } //end for //allocate memory for the area travel times ptr = (char *) GetClearedMemory(size); aasworld.areatraveltimes = (unsigned short ***) ptr; ptr += aasworld.numareas * sizeof(unsigned short **); //calcluate the travel times for all the areas for (i = 0; i < aasworld.numareas; i++) { //reversed reachabilities of this area revreach = &aasworld.reversedreachability[i]; //settings of the area settings = &aasworld.areasettings[i]; // aasworld.areatraveltimes[i] = (unsigned short **) ptr; ptr += settings->numreachableareas * sizeof(unsigned short *); // for (l = 0; l < settings->numreachableareas; l++) { aasworld.areatraveltimes[i][l] = (unsigned short *) ptr; ptr += PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short); //reachability link reach = &aasworld.reachability[settings->firstreachablearea + l]; // for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) { VectorCopy(aasworld.reachability[revlink->linknum].end, end); // aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start); } //end for } //end for } //end for #ifdef DEBUG botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime); #endif } //end of the function AAS_CalculateAreaTravelTimes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_PortalMaxTravelTime(int portalnum) { int l, n, t, maxt; aas_portal_t *portal; aas_reversedreachability_t *revreach; aas_reversedlink_t *revlink; aas_areasettings_t *settings; portal = &aasworld.portals[portalnum]; //reversed reachabilities of this portal area revreach = &aasworld.reversedreachability[portal->areanum]; //settings of the portal area settings = &aasworld.areasettings[portal->areanum]; // maxt = 0; for (l = 0; l < settings->numreachableareas; l++) { for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) { t = aasworld.areatraveltimes[portal->areanum][l][n]; if (t > maxt) { maxt = t; } //end if } //end for } //end for return maxt; } //end of the function AAS_PortalMaxTravelTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_InitPortalMaxTravelTimes(void) { int i; if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int)); for (i = 0; i < aasworld.numportals; i++) { aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i); //botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]); } //end for } //end of the function AAS_InitPortalMaxTravelTimes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== /* static int AAS_FreeOldestCache(void) { int i, j, bestcluster, bestarea, freed; float besttime; aas_routingcache_t *cache, *bestcache; freed = qfalse; besttime = 999999999; bestcache = NULL; bestcluster = 0; bestarea = 0; //refresh cluster cache for (i = 0; i < aasworld.numclusters; i++) { for (j = 0; j < aasworld.clusters[i].numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) { //never remove cache leading towards a portal if (aasworld.areasettings[cache->areanum].cluster < 0) continue; //if this cache is older than the cache we found so far if (cache->time < besttime) { bestcache = cache; bestcluster = i; bestarea = j; besttime = cache->time; } //end if } //end for } //end for } //end for if (bestcache) { cache = bestcache; if (cache->prev) cache->prev->next = cache->next; else aasworld.clusterareacache[bestcluster][bestarea] = cache->next; if (cache->next) cache->next->prev = cache->prev; AAS_FreeRoutingCache(cache); freed = qtrue; } //end if besttime = 999999999; bestcache = NULL; bestarea = 0; for (i = 0; i < aasworld.numareas; i++) { //refresh portal cache for (cache = aasworld.portalcache[i]; cache; cache = cache->next) { if (cache->time < besttime) { bestcache = cache; bestarea = i; besttime = cache->time; } //end if } //end for } //end for if (bestcache) { cache = bestcache; if (cache->prev) cache->prev->next = cache->next; else aasworld.portalcache[bestarea] = cache->next; if (cache->next) cache->next->prev = cache->prev; AAS_FreeRoutingCache(cache); freed = qtrue; } //end if return freed; } //end of the function AAS_FreeOldestCache */ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_FreeOldestCache(void) { int clusterareanum; aas_routingcache_t *cache; for (cache = aasworld.oldestcache; cache; cache = cache->time_next) { // never free area cache leading towards a portal if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) { continue; } break; } if (cache) { // unlink the cache if (cache->type == CACHETYPE_AREA) { //number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); // unlink from cluster area cache if (cache->prev) cache->prev->next = cache->next; else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next; if (cache->next) cache->next->prev = cache->prev; } else { // unlink from portal cache if (cache->prev) cache->prev->next = cache->next; else aasworld.portalcache[cache->areanum] = cache->next; if (cache->next) cache->next->prev = cache->prev; } AAS_FreeRoutingCache(cache); return qtrue; } return qfalse; } //end of the function AAS_FreeOldestCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes) { aas_routingcache_t *cache; int size; // size = sizeof(aas_routingcache_t) + numtraveltimes * sizeof(unsigned short int) + numtraveltimes * sizeof(unsigned char); // routingcachesize += size; // cache = (aas_routingcache_t *) GetClearedMemory(size); cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) + numtraveltimes * sizeof(unsigned short int); cache->size = size; return cache; } //end of the function AAS_AllocRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_FreeAllClusterAreaCache(void) { int i, j; aas_routingcache_t *cache, *nextcache; aas_cluster_t *cluster; //free all cluster cache if existing if (!aasworld.clusterareacache) return; //free caches for (i = 0; i < aasworld.numclusters; i++) { cluster = &aasworld.clusters[i]; for (j = 0; j < cluster->numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.clusterareacache[i][j] = NULL; } //end for } //end for //free the cluster cache array FreeMemory(aasworld.clusterareacache); aasworld.clusterareacache = NULL; } //end of the function AAS_FreeAllClusterAreaCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_InitClusterAreaCache(void) { int i, size; char *ptr; // for (size = 0, i = 0; i < aasworld.numclusters; i++) { size += aasworld.clusters[i].numareas; } //end for //two dimensional array with pointers for every cluster to routing cache //for every area in that cluster ptr = (char *) GetClearedMemory( aasworld.numclusters * sizeof(aas_routingcache_t **) + size * sizeof(aas_routingcache_t *)); aasworld.clusterareacache = (aas_routingcache_t ***) ptr; ptr += aasworld.numclusters * sizeof(aas_routingcache_t **); for (i = 0; i < aasworld.numclusters; i++) { aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr; ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *); } //end for } //end of the function AAS_InitClusterAreaCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_FreeAllPortalCache(void) { int i; aas_routingcache_t *cache, *nextcache; //free all portal cache if existing if (!aasworld.portalcache) return; //free portal caches for (i = 0; i < aasworld.numareas; i++) { for (cache = aasworld.portalcache[i]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.portalcache[i] = NULL; } //end for FreeMemory(aasworld.portalcache); aasworld.portalcache = NULL; } //end of the function AAS_FreeAllPortalCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_InitPortalCache(void) { // aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory( aasworld.numareas * sizeof(aas_routingcache_t *)); } //end of the function AAS_InitPortalCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_InitRoutingUpdate(void) { int i, maxreachabilityareas; //free routing update fields if already existing if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); // maxreachabilityareas = 0; for (i = 0; i < aasworld.numclusters; i++) { if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas) { maxreachabilityareas = aasworld.clusters[i].numreachabilityareas; } //end if } //end for //allocate memory for the routing update fields aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory( maxreachabilityareas * sizeof(aas_routingupdate_t)); // if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); //allocate memory for the portal update fields aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory( (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); } //end of the function AAS_InitRoutingUpdate //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateAllRoutingCache(void) { int i, j; //int t; aasworld.initialized = qtrue; botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n"); for (i = 1; i < aasworld.numareas; i++) { if (!AAS_AreaReachability(i)) continue; for (j = 1; j < aasworld.numareas; j++) { if (i == j) continue; if (!AAS_AreaReachability(j)) continue; AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT); //t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT); //Log_Write("traveltime from %d to %d is %d", i, j, t); } //end for } //end for aasworld.initialized = qfalse; } //end of the function AAS_CreateAllRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== //the route cache header //this header is followed by numportalcache + numareacache aas_routingcache_t //structures that store routing cache typedef struct routecacheheader_s { int ident; int version; int numareas; int numclusters; int areacrc; int clustercrc; int numportalcache; int numareacache; } routecacheheader_t; #define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M') #define RCVERSION 2 //void AAS_DecompressVis(byte *in, int numareas, byte *decompressed); //int AAS_CompressVis(byte *vis, int numareas, byte *dest); void AAS_WriteRouteCache(void) { int i, j, numportalcache, numareacache, totalsize; aas_routingcache_t *cache; aas_cluster_t *cluster; fileHandle_t fp; char filename[MAX_QPATH]; routecacheheader_t routecacheheader; numportalcache = 0; for (i = 0; i < aasworld.numareas; i++) { for (cache = aasworld.portalcache[i]; cache; cache = cache->next) { numportalcache++; } //end for } //end for numareacache = 0; for (i = 0; i < aasworld.numclusters; i++) { cluster = &aasworld.clusters[i]; for (j = 0; j < cluster->numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) { numareacache++; } //end for } //end for } //end for // open the file for writing Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); if (!fp) { AAS_Error("Unable to open file: %s\n", filename); return; } //end if //create the header routecacheheader.ident = RCID; routecacheheader.version = RCVERSION; routecacheheader.numareas = aasworld.numareas; routecacheheader.numclusters = aasworld.numclusters; routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ); routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ); routecacheheader.numportalcache = numportalcache; routecacheheader.numareacache = numareacache; //write the header botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp); // totalsize = 0; //write all the cache for (i = 0; i < aasworld.numareas; i++) { for (cache = aasworld.portalcache[i]; cache; cache = cache->next) { botimport.FS_Write(cache, cache->size, fp); totalsize += cache->size; } //end for } //end for for (i = 0; i < aasworld.numclusters; i++) { cluster = &aasworld.clusters[i]; for (j = 0; j < cluster->numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) { botimport.FS_Write(cache, cache->size, fp); totalsize += cache->size; } //end for } //end for } //end for // write the visareas /* for (i = 0; i < aasworld.numareas; i++) { if (!aasworld.areavisibility[i]) { size = 0; botimport.FS_Write(&size, sizeof(int), fp); continue; } AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis ); size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis ); botimport.FS_Write(&size, sizeof(int), fp); botimport.FS_Write(aasworld.decompressedvis, size, fp); } */ // botimport.FS_FCloseFile(fp); botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename); botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize); } //end of the function AAS_WriteRouteCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static aas_routingcache_t *AAS_ReadCache(fileHandle_t fp) { int size; aas_routingcache_t *cache; botimport.FS_Read(&size, sizeof(size), fp); cache = (aas_routingcache_t *) GetMemory(size); cache->size = size; botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp); cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; return cache; } //end of the function AAS_ReadCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_ReadRouteCache(void) { int i, clusterareanum;//, size; fileHandle_t fp; char filename[MAX_QPATH]; routecacheheader_t routecacheheader; aas_routingcache_t *cache; Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); botimport.FS_FOpenFile( filename, &fp, FS_READ ); if (!fp) { return qfalse; } //end if botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp ); if (routecacheheader.ident != RCID) { AAS_Error("%s is not a route cache dump\n", filename); return qfalse; } //end if if (routecacheheader.version != RCVERSION) { AAS_Error("route cache dump has wrong version %d, should be %d\n", routecacheheader.version, RCVERSION); return qfalse; } //end if if (routecacheheader.numareas != aasworld.numareas) { //AAS_Error("route cache dump has wrong number of areas\n"); return qfalse; } //end if if (routecacheheader.numclusters != aasworld.numclusters) { //AAS_Error("route cache dump has wrong number of clusters\n"); return qfalse; } //end if if (routecacheheader.areacrc != CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas )) { //AAS_Error("route cache dump area CRC incorrect\n"); return qfalse; } //end if if (routecacheheader.clustercrc != CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters )) { //AAS_Error("route cache dump cluster CRC incorrect\n"); return qfalse; } //end if //read all the portal cache for (i = 0; i < routecacheheader.numportalcache; i++) { cache = AAS_ReadCache(fp); cache->next = aasworld.portalcache[cache->areanum]; cache->prev = NULL; if (aasworld.portalcache[cache->areanum]) aasworld.portalcache[cache->areanum]->prev = cache; aasworld.portalcache[cache->areanum] = cache; } //end for //read all the cluster area cache for (i = 0; i < routecacheheader.numareacache; i++) { cache = AAS_ReadCache(fp); clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum]; cache->prev = NULL; if (aasworld.clusterareacache[cache->cluster][clusterareanum]) aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache; aasworld.clusterareacache[cache->cluster][clusterareanum] = cache; } //end for // read the visareas /* aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *)); aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte)); for (i = 0; i < aasworld.numareas; i++) { botimport.FS_Read(&size, sizeof(size), fp ); if (size) { aasworld.areavisibility[i] = (byte *) GetMemory(size); botimport.FS_Read(aasworld.areavisibility[i], size, fp ); } } */ // botimport.FS_FCloseFile(fp); return qtrue; } //end of the function AAS_ReadRouteCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #define MAX_REACHABILITYPASSAREAS 32 static void AAS_InitReachabilityAreas(void) { int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS]; int numreachareas; aas_reachability_t *reach; vec3_t start, end; if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); aasworld.reachabilityareas = (aas_reachabilityareas_t *) GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t)); aasworld.reachabilityareaindex = (int *) GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int)); numreachareas = 0; for (i = 0; i < aasworld.reachabilitysize; i++) { reach = &aasworld.reachability[i]; numareas = 0; switch(reach->traveltype & TRAVELTYPE_MASK) { //trace areas from start to end case TRAVEL_BARRIERJUMP: case TRAVEL_WATERJUMP: VectorCopy(reach->start, end); end[2] = reach->end[2]; numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS); break; case TRAVEL_WALKOFFLEDGE: VectorCopy(reach->end, start); start[2] = reach->start[2]; numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); break; case TRAVEL_GRAPPLEHOOK: numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); break; //trace arch case TRAVEL_JUMP: break; case TRAVEL_ROCKETJUMP: break; case TRAVEL_BFGJUMP: break; case TRAVEL_JUMPPAD: break; //trace from reach->start to entity center, along entity movement //and from entity center to reach->end case TRAVEL_ELEVATOR: break; case TRAVEL_FUNCBOB: break; //no areas in between case TRAVEL_WALK: break; case TRAVEL_CROUCH: break; case TRAVEL_LADDER: break; case TRAVEL_SWIM: break; case TRAVEL_TELEPORT: break; default: break; } //end switch aasworld.reachabilityareas[i].firstarea = numreachareas; aasworld.reachabilityareas[i].numareas = numareas; for (j = 0; j < numareas; j++) { aasworld.reachabilityareaindex[numreachareas++] = areas[j]; } //end for } //end for } //end of the function AAS_InitReachabilityAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitRouting(void) { AAS_InitTravelFlagFromType(); // AAS_InitAreaContentsTravelFlags(); //initialize the routing update fields AAS_InitRoutingUpdate(); //create reversed reachability links used by the routing update algorithm AAS_CreateReversedReachability(); //initialize the cluster cache AAS_InitClusterAreaCache(); //initialize portal cache AAS_InitPortalCache(); //initialize the area travel times AAS_CalculateAreaTravelTimes(); //calculate the maximum travel times through portals AAS_InitPortalMaxTravelTimes(); //get the areas reachabilities go through AAS_InitReachabilityAreas(); // #ifdef ROUTING_DEBUG numareacacheupdates = 0; numportalcacheupdates = 0; #endif //ROUTING_DEBUG // routingcachesize = 0; max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096"); // read any routing cache if available AAS_ReadRouteCache(); } //end of the function AAS_InitRouting //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeRoutingCaches(void) { // free all the existing cluster area cache AAS_FreeAllClusterAreaCache(); // free all the existing portal cache AAS_FreeAllPortalCache(); // free cached travel times within areas if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); aasworld.areatraveltimes = NULL; // free cached maximum travel time through cluster portals if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); aasworld.portalmaxtraveltimes = NULL; // free reversed reachability links if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); aasworld.reversedreachability = NULL; // free routing algorithm memory if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); aasworld.areaupdate = NULL; if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); aasworld.portalupdate = NULL; // free lists with areas the reachabilities go through if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); aasworld.reachabilityareas = NULL; // free the reachability area index if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); aasworld.reachabilityareaindex = NULL; // free area contents travel flags look up table if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); aasworld.areacontentstravelflags = NULL; } //end of the function AAS_FreeRoutingCaches //=========================================================================== // update the given routing cache // // Parameter: areacache : routing cache to update // Returns: - // Changes Globals: - //=========================================================================== static void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache) { int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum; int numreachabilityareas; unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; const aas_reversedreachability_t *revreach; const aas_reversedlink_t *revlink; #ifdef ROUTING_DEBUG numareacacheupdates++; #endif //ROUTING_DEBUG //number of reachability areas within this cluster numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas; // aasworld.frameroutingupdates++; //clear the routing update fields // Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t)); // badtravelflags = ~areacache->travelflags; // clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum); if (clusterareanum >= numreachabilityareas) return; // Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes)); // curupdate = &aasworld.areaupdate[clusterareanum]; curupdate->areanum = areacache->areanum; //VectorCopy(areacache->origin, curupdate->start); curupdate->areatraveltimes = startareatraveltimes; curupdate->tmptraveltime = areacache->starttraveltime; // areacache->traveltimes[clusterareanum] = areacache->starttraveltime; //put the area to start with in the current read list curupdate->next = NULL; curupdate->prev = NULL; updateliststart = curupdate; updatelistend = curupdate; //while there are updates in the current list while (updateliststart) { curupdate = updateliststart; // if (curupdate->next) curupdate->next->prev = NULL; else updatelistend = NULL; updateliststart = curupdate->next; // curupdate->inlist = qfalse; //check all reversed reachability links revreach = &aasworld.reversedreachability[curupdate->areanum]; // for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++) { linknum = revlink->linknum; reach = &aasworld.reachability[linknum]; //if there is used an undesired travel type if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; //if not allowed to enter the next area if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; //if the next area has a not allowed travel flag if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; //number of the area the reversed reachability leads to nextareanum = revlink->areanum; //get the cluster number of the area cluster = aasworld.areasettings[nextareanum].cluster; //don't leave the cluster if (cluster > 0 && cluster != areacache->cluster) continue; //get the number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum); if (clusterareanum >= numreachabilityareas) continue; //time already travelled plus the traveltime through //the current area plus the travel time from the reachability t = curupdate->tmptraveltime + //AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) + curupdate->areatraveltimes[i] + reach->traveltime; // if (!areacache->traveltimes[clusterareanum] || areacache->traveltimes[clusterareanum] > t) { areacache->traveltimes[clusterareanum] = t; areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea; nextupdate = &aasworld.areaupdate[clusterareanum]; nextupdate->areanum = nextareanum; nextupdate->tmptraveltime = t; //VectorCopy(reach->start, nextupdate->start); nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum - aasworld.areasettings[nextareanum].firstreachablearea]; if (!nextupdate->inlist) { // we add the update to the end of the list // we could also use a B+ tree to have a real sorted list // on travel time which makes for faster routing updates nextupdate->next = NULL; nextupdate->prev = updatelistend; if (updatelistend) updatelistend->next = nextupdate; else updateliststart = nextupdate; updatelistend = nextupdate; nextupdate->inlist = qtrue; } //end if } //end if } //end for } //end while } //end of the function AAS_UpdateAreaRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags) { int clusterareanum; aas_routingcache_t *cache, *clustercache; //number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); //pointer to the cache for the area in the cluster clustercache = aasworld.clusterareacache[clusternum][clusterareanum]; //find the cache without undesired travel flags for (cache = clustercache; cache; cache = cache->next) { //if there aren't used any undesired travel types for the cache if (cache->travelflags == travelflags) break; } //end for //if there was no cache if (!cache) { cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas); cache->cluster = clusternum; cache->areanum = areanum; VectorCopy(aasworld.areas[areanum].center, cache->origin); cache->starttraveltime = 1; cache->travelflags = travelflags; cache->prev = NULL; cache->next = clustercache; if (clustercache) clustercache->prev = cache; aasworld.clusterareacache[clusternum][clusterareanum] = cache; AAS_UpdateAreaRoutingCache(cache); } //end if else { AAS_UnlinkCache(cache); } //end else //the cache has been accessed cache->time = AAS_RoutingTime(); cache->type = CACHETYPE_AREA; AAS_LinkCache(cache); return cache; } //end of the function AAS_GetAreaRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache) { int i, portalnum, clusterareanum, clusternum; unsigned short int t; aas_portal_t *portal; aas_cluster_t *cluster; aas_routingcache_t *cache; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; #ifdef ROUTING_DEBUG numportalcacheupdates++; #endif //ROUTING_DEBUG //clear the routing update fields // Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); // curupdate = &aasworld.portalupdate[aasworld.numportals]; curupdate->cluster = portalcache->cluster; curupdate->areanum = portalcache->areanum; curupdate->tmptraveltime = portalcache->starttraveltime; //if the start area is a cluster portal, store the travel time for that portal clusternum = aasworld.areasettings[portalcache->areanum].cluster; if (clusternum < 0) { portalcache->traveltimes[-clusternum] = portalcache->starttraveltime; } //end if //put the area to start with in the current read list curupdate->next = NULL; curupdate->prev = NULL; updateliststart = curupdate; updatelistend = curupdate; //while there are updates in the current list while (updateliststart) { curupdate = updateliststart; //remove the current update from the list if (curupdate->next) curupdate->next->prev = NULL; else updatelistend = NULL; updateliststart = curupdate->next; //current update is removed from the list curupdate->inlist = qfalse; // cluster = &aasworld.clusters[curupdate->cluster]; // cache = AAS_GetAreaRoutingCache(curupdate->cluster, curupdate->areanum, portalcache->travelflags); //take all portals of the cluster for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; //if this is the portal of the current update continue if (portal->areanum == curupdate->areanum) continue; // clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum); if (clusterareanum >= cluster->numreachabilityareas) continue; // t = cache->traveltimes[clusterareanum]; if (!t) continue; t += curupdate->tmptraveltime; // if (!portalcache->traveltimes[portalnum] || portalcache->traveltimes[portalnum] > t) { portalcache->traveltimes[portalnum] = t; nextupdate = &aasworld.portalupdate[portalnum]; if (portal->frontcluster == curupdate->cluster) { nextupdate->cluster = portal->backcluster; } //end if else { nextupdate->cluster = portal->frontcluster; } //end else nextupdate->areanum = portal->areanum; //add travel time through the actual portal area for the next update nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum]; if (!nextupdate->inlist) { // we add the update to the end of the list // we could also use a B+ tree to have a real sorted list // on travel time which makes for faster routing updates nextupdate->next = NULL; nextupdate->prev = updatelistend; if (updatelistend) updatelistend->next = nextupdate; else updateliststart = nextupdate; updatelistend = nextupdate; nextupdate->inlist = qtrue; } //end if } //end if } //end for } //end while } //end of the function AAS_UpdatePortalRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags) { aas_routingcache_t *cache; //find the cached portal routing if existing for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next) { if (cache->travelflags == travelflags) break; } //end for //if the portal routing isn't cached if (!cache) { cache = AAS_AllocRoutingCache(aasworld.numportals); cache->cluster = clusternum; cache->areanum = areanum; VectorCopy(aasworld.areas[areanum].center, cache->origin); cache->starttraveltime = 1; cache->travelflags = travelflags; //add the cache to the cache list cache->prev = NULL; cache->next = aasworld.portalcache[areanum]; if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache; aasworld.portalcache[areanum] = cache; //update the cache AAS_UpdatePortalRoutingCache(cache); } //end if else { AAS_UnlinkCache(cache); } //end else //the cache has been accessed cache->time = AAS_RoutingTime(); cache->type = CACHETYPE_PORTAL; AAS_LinkCache(cache); return cache; } //end of the function AAS_GetPortalRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum) { int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum; unsigned short int t, besttime; aas_portal_t *portal; aas_cluster_t *cluster; aas_routingcache_t *areacache, *portalcache; aas_reachability_t *reach; if (!aasworld.initialized) return qfalse; if (areanum == goalareanum) { *traveltime = 1; *reachnum = 0; return qtrue; } //check !AAS_AreaReachability(areanum) with custom developer-only debug message if (areanum <= 0 || areanum >= aasworld.numareas) { if (botDeveloper) { botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum); } //end if return qfalse; } //end if if (goalareanum <= 0 || goalareanum >= aasworld.numareas) { if (botDeveloper) { botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum); } //end if return qfalse; } //end if if (!aasworld.areasettings[areanum].numreachableareas || !aasworld.areasettings[goalareanum].numreachableareas) { return qfalse; } //end if // make sure the routing cache doesn't grow to large while ( routingcachesize > 12 * 1024 * 1024 ) { if ( !AAS_FreeOldestCache() ) { break; } } // if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum)) { travelflags |= TFL_DONOTENTER; } //end if //NOTE: the number of routing updates is limited per frame /* if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES) { #ifdef DEBUG //Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed"); #endif return 0; } //end if */ // clusternum = aasworld.areasettings[areanum].cluster; goalclusternum = aasworld.areasettings[goalareanum].cluster; //check if the area is a portal of the goal area cluster if (clusternum < 0 && goalclusternum > 0) { portal = &aasworld.portals[-clusternum]; if (portal->frontcluster == goalclusternum || portal->backcluster == goalclusternum) { clusternum = goalclusternum; } //end if } //end if //check if the goalarea is a portal of the area cluster else if (clusternum > 0 && goalclusternum < 0) { portal = &aasworld.portals[-goalclusternum]; if (portal->frontcluster == clusternum || portal->backcluster == clusternum) { goalclusternum = clusternum; } //end if } //end if //if both areas are in the same cluster //NOTE: there might be a shorter route via another cluster!!! but we don't care if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum) { // areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags); //the number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); //the cluster the area is in cluster = &aasworld.clusters[clusternum]; //if the area is NOT a reachability area if (clusterareanum >= cluster->numreachabilityareas) return 0; //if it is possible to travel to the goal area through this cluster if (areacache->traveltimes[clusterareanum] != 0) { *reachnum = aasworld.areasettings[areanum].firstreachablearea + areacache->reachabilities[clusterareanum]; if (!origin) { *traveltime = areacache->traveltimes[clusterareanum]; return qtrue; } reach = &aasworld.reachability[*reachnum]; *traveltime = areacache->traveltimes[clusterareanum] + AAS_AreaTravelTime(areanum, origin, reach->start); // return qtrue; } //end if } //end if // clusternum = aasworld.areasettings[areanum].cluster; goalclusternum = aasworld.areasettings[goalareanum].cluster; //if the goal area is a portal if (goalclusternum < 0) { //just assume the goal area is part of the front cluster portal = &aasworld.portals[-goalclusternum]; goalclusternum = portal->frontcluster; } //end if //get the portal routing cache portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags); //if the area is a cluster portal, read directly from the portal cache if (clusternum < 0) { *traveltime = portalcache->traveltimes[-clusternum]; *reachnum = aasworld.areasettings[areanum].firstreachablearea + portalcache->reachabilities[-clusternum]; return qtrue; } //end if // besttime = 0; bestreachnum = -1; //the cluster the area is in cluster = &aasworld.clusters[clusternum]; //find the portal of the area cluster leading towards the goal area for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; //if the goal area isn't reachable from the portal if (!portalcache->traveltimes[portalnum]) continue; // portal = &aasworld.portals[portalnum]; //get the cache of the portal area areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags); //current area inside the current cluster clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); //if the area is NOT a reachability area if (clusterareanum >= cluster->numreachabilityareas) continue; //if the portal is NOT reachable from this area if (!areacache->traveltimes[clusterareanum]) continue; //total travel time is the travel time the portal area is from //the goal area plus the travel time towards the portal area t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum]; //FIXME: add the exact travel time through the actual portal area //NOTE: for now we just add the largest travel time through the portal area // because we can't directly calculate the exact travel time // to be more specific we don't know which reachability was used to travel // into the portal area t += aasworld.portalmaxtraveltimes[portalnum]; // if (origin) { *reachnum = aasworld.areasettings[areanum].firstreachablearea + areacache->reachabilities[clusterareanum]; reach = aasworld.reachability + *reachnum; t += AAS_AreaTravelTime(areanum, origin, reach->start); } //end if //if the time is better than the one already found if (!besttime || t < besttime) { bestreachnum = *reachnum; besttime = t; } //end if } //end for if (bestreachnum < 0) { return qfalse; } *reachnum = bestreachnum; *traveltime = besttime; return qtrue; } //end of the function AAS_AreaRouteToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) { int traveltime, reachnum = 0; if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) { return traveltime; } return 0; } //end of the function AAS_AreaTravelTimeToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) { int traveltime, reachnum = 0; if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) { return reachnum; } return 0; } //end of the function AAS_AreaReachabilityToGoalArea //=========================================================================== // predict the route and stop on one of the stop events // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum) { int curareanum, reachnum, i, j, testareanum; vec3_t curorigin; aas_reachability_t *reach; aas_reachabilityareas_t *reachareas; //init output route->stopevent = RSE_NONE; route->endarea = goalareanum; route->endcontents = 0; route->endtravelflags = 0; VectorCopy(origin, route->endpos); route->time = 0; curareanum = areanum; VectorCopy(origin, curorigin); for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++) { reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags); if (!reachnum) { route->stopevent = RSE_NOROUTE; return qfalse; } //end if reach = &aasworld.reachability[reachnum]; // if (stopevent & RSE_USETRAVELTYPE) { if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl) { route->stopevent = RSE_USETRAVELTYPE; route->endarea = curareanum; route->endcontents = aasworld.areasettings[curareanum].contents; route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); VectorCopy(reach->start, route->endpos); return qtrue; } //end if if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl) { route->stopevent = RSE_USETRAVELTYPE; route->endarea = reach->areanum; route->endcontents = aasworld.areasettings[reach->areanum].contents; route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum); VectorCopy(reach->end, route->endpos); route->time += AAS_AreaTravelTime(areanum, origin, reach->start); route->time += reach->traveltime; return qtrue; } //end if } //end if reachareas = &aasworld.reachabilityareas[reachnum]; for (j = 0; j < reachareas->numareas + 1; j++) { if (j >= reachareas->numareas) testareanum = reach->areanum; else testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j]; if (stopevent & RSE_ENTERCONTENTS) { if (aasworld.areasettings[testareanum].contents & stopcontents) { route->stopevent = RSE_ENTERCONTENTS; route->endarea = testareanum; route->endcontents = aasworld.areasettings[testareanum].contents; VectorCopy(reach->end, route->endpos); route->time += AAS_AreaTravelTime(areanum, origin, reach->start); route->time += reach->traveltime; return qtrue; } //end if } //end if if (stopevent & RSE_ENTERAREA) { if (testareanum == stopareanum) { route->stopevent = RSE_ENTERAREA; route->endarea = testareanum; route->endcontents = aasworld.areasettings[testareanum].contents; VectorCopy(reach->start, route->endpos); return qtrue; } //end if } //end if } //end for route->time += AAS_AreaTravelTime(areanum, origin, reach->start); route->time += reach->traveltime; route->endarea = reach->areanum; route->endcontents = aasworld.areasettings[reach->areanum].contents; route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); VectorCopy(reach->end, route->endpos); // curareanum = reach->areanum; VectorCopy(reach->end, curorigin); // if (maxtime && route->time > maxtime) break; } //end while if (curareanum != goalareanum) return qfalse; return qtrue; } //end of the function AAS_PredictRoute #if 0 //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BridgeWalkable(int areanum) { return qfalse; } //end of the function AAS_BridgeWalkable #endif //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach) { if (!aasworld.initialized) { Com_Memset(reach, 0, sizeof(aas_reachability_t)); return; } //end if if (num < 0 || num >= aasworld.reachabilitysize) { Com_Memset(reach, 0, sizeof(aas_reachability_t)); return; } //end if Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));; } //end of the function AAS_ReachabilityFromNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NextAreaReachability(int areanum, int reachnum) { aas_areasettings_t *settings; if (!aasworld.initialized) return 0; if (areanum <= 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum); return 0; } //end if settings = &aasworld.areasettings[areanum]; if (!reachnum) { return settings->firstreachablearea; } //end if if (reachnum < settings->firstreachablearea) { botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara"); return 0; } //end if reachnum++; if (reachnum >= settings->firstreachablearea + settings->numreachableareas) { return 0; } //end if return reachnum; } //end of the function AAS_NextAreaReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NextModelReachability(int num, int modelnum) { int i; if (num <= 0) num = 1; else if (num >= aasworld.reachabilitysize) return 0; else num++; // for (i = num; i < aasworld.reachabilitysize; i++) { if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) { if (aasworld.reachability[i].facenum == modelnum) return i; } //end if else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) { if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i; } //end if } //end for return 0; } //end of the function AAS_NextModelReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin) { int i, n, t; vec3_t start, end; aas_trace_t trace; //if the area has no reachabilities if (!AAS_AreaReachability(areanum)) return qfalse; // n = aasworld.numareas * random(); for (i = 0; i < aasworld.numareas; i++) { if (n <= 0) n = 1; if (n >= aasworld.numareas) n = 1; if (AAS_AreaReachability(n)) { t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags); //if the goal is reachable if (t > 0) { if (AAS_AreaSwim(n)) { *goalareanum = n; VectorCopy(aasworld.areas[n].center, goalorigin); //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); return qtrue; } //end if VectorCopy(aasworld.areas[n].center, start); if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]); VectorCopy(start, end); end[2] -= 300; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n) { if (AAS_AreaGroundFaceArea(n) > 300) { *goalareanum = n; VectorCopy(trace.endpos, goalorigin); //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); return qtrue; } //end if } //end if } //end if } //end if n++; } //end for return qfalse; } //end of the function AAS_RandomGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_AreaVisible(int srcarea, int destarea) { return qfalse; } //end of the function AAS_AreaVisible #if 0 //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point) { vec3_t vec, p2; AAS_ProjectPointOntoVector(point, v1, v2, p2); VectorSubtract(point, p2, vec); return VectorLength(vec); } //end of the function DistancePointToLine #endif //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags) { int i, j, nextareanum, badtravelflags, numreach, bestarea; unsigned short int t, besttraveltime; static unsigned short int *hidetraveltimes; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; float dist1, dist2; vec3_t v1, v2, p; qboolean startVisible; // if (!hidetraveltimes) { hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int)); } //end if else { Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int)); } //end else besttraveltime = 0; bestarea = 0; //assume visible startVisible = qtrue; // badtravelflags = ~travelflags; // curupdate = &aasworld.areaupdate[areanum]; curupdate->areanum = areanum; VectorCopy(origin, curupdate->start); curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0]; curupdate->tmptraveltime = 0; //put the area to start with in the current read list curupdate->next = NULL; curupdate->prev = NULL; updateliststart = curupdate; updatelistend = curupdate; //while there are updates in the list while (updateliststart) { curupdate = updateliststart; // if (curupdate->next) curupdate->next->prev = NULL; else updatelistend = NULL; updateliststart = curupdate->next; // curupdate->inlist = qfalse; //check all reversed reachability links numreach = aasworld.areasettings[curupdate->areanum].numreachableareas; reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea]; // for (i = 0; i < numreach; i++, reach++) { //if an undesired travel type is used if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; // if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; //number of the area the reachability leads to nextareanum = reach->areanum; // if this moves us into the enemies area, skip it if (nextareanum == enemyareanum) continue; //time already travelled plus the traveltime through //the current area plus the travel time from the reachability t = curupdate->tmptraveltime + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + reach->traveltime; //avoid going near the enemy AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p); for (j = 0; j < 3; j++) if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) || (p[j] < curupdate->start[j] && p[j] < reach->end[j])) break; if (j < 3) { VectorSubtract(enemyorigin, reach->end, v2); } //end if else { VectorSubtract(enemyorigin, p, v2); } //end else dist2 = VectorLength(v2); //never go through the enemy if (dist2 < 40) continue; // VectorSubtract(enemyorigin, curupdate->start, v1); dist1 = VectorLength(v1); // if (dist2 < dist1) { t += (dist1 - dist2) * 10; } // if we weren't visible when starting, make sure we don't move into their view if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) { continue; } // if (besttraveltime && t >= besttraveltime) continue; // if (!hidetraveltimes[nextareanum] || hidetraveltimes[nextareanum] > t) { //if the nextarea is not visible from the enemy area if (!AAS_AreaVisible(enemyareanum, nextareanum)) { besttraveltime = t; bestarea = nextareanum; } //end if hidetraveltimes[nextareanum] = t; nextupdate = &aasworld.areaupdate[nextareanum]; nextupdate->areanum = nextareanum; nextupdate->tmptraveltime = t; //remember where we entered this area VectorCopy(reach->end, nextupdate->start); //if this update is not in the list yet if (!nextupdate->inlist) { //add the new update to the end of the list nextupdate->next = NULL; nextupdate->prev = updatelistend; if (updatelistend) updatelistend->next = nextupdate; else updateliststart = nextupdate; updatelistend = nextupdate; nextupdate->inlist = qtrue; } //end if } //end if } //end for } //end while return bestarea; } //end of the function AAS_NearestHideArea