/* =========================================================================== Wolfenstein: Enemy Territory GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). Wolf ET 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 3 of the License, or (at your option) any later version. Wolf ET 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 Wolf ET Source Code. If not, see . In addition, the Wolf: ET 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 Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below. 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. =========================================================================== */ /***************************************************************************** * name: be_aas_route.c * * desc: AAS * * *****************************************************************************/ #include "../game/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 "../game/botlib.h" #include "../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" #define ROUTING_DEBUG //travel time in hundreths of a second = distance * 100 / speed #define DISTANCEFACTOR_CROUCH 1.3 //crouch speed = 100 #define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 #define DISTANCEFACTOR_WALK 0.33 //walk speed = 300 // Ridah, scale traveltimes with ground steepness of area #define GROUNDSTEEPNESS_TIMESCALE 1 // this is the maximum scale, 1 being the usual for a flat ground //cache refresh time #define CACHE_REFRESHTIME 15.0 //15 seconds refresh time #define DEFAULT_MAX_ROUTINGCACHESIZE "16384" extern aas_t aasworlds[MAX_AAS_WORLDS]; /* 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; int max_frameroutingupdates; // Ridah, routing memory calls go here, so we can change between Hunk/Zone easily void *AAS_RoutingGetMemory( int size ) { return GetClearedMemory( size ); } void AAS_RoutingFreeMemory( void *ptr ) { FreeMemory( ptr ); } // done. //=========================================================================== // // 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: - //=========================================================================== __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: - //=========================================================================== 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: - //=========================================================================== int AAS_TravelFlagForType( int traveltype ) { if ( traveltype < 0 || traveltype >= MAX_TRAVELTYPES ) { return TFL_INVALID; } return aasworld->travelflagfortype[traveltype]; } //end of the function AAS_TravelFlagForType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== __inline float AAS_RoutingTime( void ) { return AAS_Time(); } //end of the function AAS_RoutingTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeRoutingCache( aas_routingcache_t *cache ) { routingcachesize -= cache->size; AAS_RoutingFreeMemory( cache ); } //end of the function AAS_FreeRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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: - //=========================================================================== 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 if ( aasworld->portalcache ) { 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_TeamTravelFlagsForAreaFlags( int areaflags ) { int travelflags = 0; // if ( areaflags & AREA_TEAM_FLAGS ) { if ( areaflags & AREA_TEAM_AXIS ) { travelflags |= TFL_TEAM_AXIS; } if ( areaflags & AREA_TEAM_ALLIES ) { travelflags |= TFL_TEAM_ALLIES; } if ( areaflags & AREA_TEAM_AXIS_DISGUISED ) { travelflags |= TFL_TEAM_AXIS_DISGUISED; } if ( areaflags & AREA_TEAM_ALLIES_DISGUISED ) { travelflags |= TFL_TEAM_AXIS_DISGUISED; } if ( areaflags & AREA_AVOID_AXIS ) { travelflags |= TFL_TEAM_AXIS; } if ( areaflags & AREA_AVOID_ALLIES ) { travelflags |= TFL_TEAM_ALLIES; } } // return travelflags; } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ClearClusterTeamFlags( int areanum ) { int clusternum; // clusternum = aasworld->areasettings[areanum].cluster; if ( clusternum > 0 ) { aasworld->clusterTeamTravelFlags[clusternum] = -1; // recalculate } } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CalculateClusterTeamFlags( int clusternum ) { int i; // if ( clusternum < 0 ) { return; } // aasworld->clusterTeamTravelFlags[clusternum] = 0; for ( i = 1; i < aasworld->numareas; i++ ) { if ( aasworld->areasettings[i].cluster == clusternum ) { aasworld->clusterTeamTravelFlags[clusternum] |= AAS_TeamTravelFlagsForAreaFlags( aasworld->areasettings[i].areaflags ); } } } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_EnableRoutingArea( int areanum, int enable ) { int flags; int bitflag; // flag to set or clear if ( areanum <= 0 || areanum >= aasworld->numareas ) { if ( bot_developer ) { botimport.Print( PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum ); } //end if return 0; } //end if if ( ( enable & 1 ) || ( enable < 0 ) ) { // clear all flags bitflag = AREA_AVOID | AREA_DISABLED | AREA_TEAM_AXIS | AREA_TEAM_ALLIES | AREA_TEAM_AXIS_DISGUISED | AREA_TEAM_ALLIES_DISGUISED; } else if ( enable & 0x10 ) { bitflag = AREA_AVOID; } else if ( enable & 0x20 ) { bitflag = AREA_TEAM_AXIS; } else if ( enable & 0x40 ) { bitflag = AREA_TEAM_ALLIES; } else if ( enable & 0x80 ) { bitflag = AREA_TEAM_AXIS_DISGUISED; } else if ( enable & 0x100 ) { bitflag = AREA_TEAM_ALLIES_DISGUISED; } else { bitflag = AREA_DISABLED; } // remove avoidance flag enable &= 1; flags = aasworld->areasettings[areanum].areaflags & bitflag; if ( enable < 0 ) { return !flags; } if ( enable ) { aasworld->areasettings[areanum].areaflags &= ~bitflag; } else { aasworld->areasettings[areanum].areaflags |= bitflag; } // if the status of the area changed if ( ( flags & bitflag ) != ( aasworld->areasettings[areanum].areaflags & bitflag ) ) { //remove all routing cache involving this area AAS_RemoveRoutingCacheUsingArea( areanum ); // recalculate the team flags that are used in this cluster AAS_ClearClusterTeamFlags( areanum ); } //end if return !flags; } //end of the function AAS_EnableRoutingArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_EnableAllAreas( void ) { int i; for ( i = 0; i < ( *aasworld ).numareas; i++ ) { if ( ( *aasworld ).areasettings[i].areaflags & AREA_DISABLED ) { AAS_EnableRoutingArea( i, qtrue ); } } } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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 ) { AAS_RoutingFreeMemory( aasworld->reversedreachability ); } //allocate memory for the reversed reachability links ptr = (char *) AAS_RoutingGetMemory( 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 other areas for reachability links to the area for ( i = 1; i < aasworld->numareas; i++ ) { //settings of the area settings = &( aasworld->areasettings[i] ); //check the reachability links for ( n = 0; n < settings->numreachableareas; n++ ) { // Gordon: Temp hack for b0rked last area in if ( settings->firstreachablearea < 0 || settings->firstreachablearea >= ( *aasworld ).reachabilitysize ) { Com_Printf( "^1WARNING: settings->firstreachablearea out of range\n" ); continue; } //reachability link reach = &aasworld->reachability[settings->firstreachablearea + n]; if ( ( reach->areanum < 0 || ( reach->areanum >= aasworld->reachabilitysize ) ) ) { Com_Printf( "^1WARNING: reach->areanum out of range\n" ); continue; } 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 //DEBUG } //end of the function AAS_CreateReversedReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== // Gordon: always returns 1, so er?... float AAS_AreaGroundSteepnessScale( int areanum ) { return ( 1.0 + aasworld->areasettings[areanum].groundsteepness * (float)( GROUNDSTEEPNESS_TIMESCALE - 1 ) ); } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short int AAS_AreaTravelTime( int areanum, vec3_t start, vec3_t end ) { int intdist; float dist; // Ridah, factor in the groundsteepness now dist = VectorDistance( start, end ); // * AAS_AreaGroundSteepnessScale(areanum); // Gordon: useless as it returns 1 all the time... if ( AAS_AreaCrouch( areanum ) ) { dist *= DISTANCEFACTOR_CROUCH; //if crouch only area /* } else if( AAS_AreaSwim(areanum)) { // Gordon: again, uselss as it's a multiply by 1 dist *= DISTANCEFACTOR_SWIM; //if swim area */ } else { dist *= DISTANCEFACTOR_WALK; //normal walk area } intdist = myftol( dist ); //make sure the distance isn't zero if ( intdist <= 0 ) { return 1; } return intdist; } //end of the function AAS_AreaTravelTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CalculateAreaTravelTimes( void ) { int i, l, n, size; char *ptr; vec3_t end; aas_reversedreachability_t *revreach; aas_reversedlink_t *revlink; aas_reachability_t *reach; aas_areasettings_t *settings; int starttime; starttime = Sys_MilliSeconds(); //if there are still area travel times, free the memory if ( aasworld->areatraveltimes ) { AAS_RoutingFreeMemory( 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 * revreach->numlinks * sizeof( unsigned short ); } //end for //allocate memory for the area travel times ptr = (char *) AAS_RoutingGetMemory( 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]; // if ( settings->numreachableareas ) { aasworld->areatraveltimes[i] = (unsigned short **) ptr; ptr += settings->numreachableareas * sizeof( unsigned short * ); // reach = &aasworld->reachability[settings->firstreachablearea]; for ( l = 0; l < settings->numreachableareas; l++, reach++ ) { aasworld->areatraveltimes[i][l] = (unsigned short *) ptr; ptr += revreach->numlinks * sizeof( unsigned short ); //reachability link // 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 //DEBUG } //end of the function AAS_CalculateAreaTravelTimes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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: - //=========================================================================== void AAS_InitPortalMaxTravelTimes( void ) { int i; if ( aasworld->portalmaxtraveltimes ) { AAS_RoutingFreeMemory( aasworld->portalmaxtraveltimes ); } aasworld->portalmaxtraveltimes = (int *) AAS_RoutingGetMemory( 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: - //=========================================================================== void AAS_UnlinkCache(aas_routingcache_t *cache) { if (cache->time_next) cache->time_next->time_prev = cache->time_prev; else newestcache = cache->time_prev; if (cache->time_prev) cache->time_prev->time_next = cache->time_next; else oldestcache = cache->time_next; cache->time_next = NULL; cache->time_prev = NULL; } //end of the function AAS_UnlinkCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_LinkCache(aas_routingcache_t *cache) { if (newestcache) { newestcache->time_next = cache; cache->time_prev = cache; } //end if else { oldestcache = cache; cache->time_prev = NULL; } //end else cache->time_next = NULL; newestcache = cache; } //end of the function AAS_LinkCache*/ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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: - //=========================================================================== 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 *) AAS_RoutingGetMemory( 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: - //=========================================================================== 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 AAS_RoutingFreeMemory( aasworld->clusterareacache ); aasworld->clusterareacache = NULL; } //end of the function AAS_FreeAllClusterAreaCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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 *) AAS_RoutingGetMemory( 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: - //=========================================================================== 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 AAS_RoutingFreeMemory( aasworld->portalcache ); aasworld->portalcache = NULL; } //end of the function AAS_FreeAllPortalCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitPortalCache( void ) { // aasworld->portalcache = (aas_routingcache_t **) AAS_RoutingGetMemory( aasworld->numareas * sizeof( aas_routingcache_t * ) ); } //end of the function AAS_InitPortalCache // //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeAreaVisibility( void ) { int i; if ( aasworld->areavisibility ) { for ( i = 0; i < aasworld->numareas; i++ ) { if ( aasworld->areavisibility[i] ) { FreeMemory( aasworld->areavisibility[i] ); } } } if ( aasworld->areavisibility ) { FreeMemory( aasworld->areavisibility ); } aasworld->areavisibility = NULL; if ( aasworld->decompressedvis ) { FreeMemory( aasworld->decompressedvis ); } aasworld->decompressedvis = NULL; } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitRoutingUpdate( void ) { // int i, maxreachabilityareas; //free routing update fields if already existing if ( aasworld->areaupdate ) { AAS_RoutingFreeMemory( aasworld->areaupdate ); } aasworld->areaupdate = (aas_routingupdate_t*)AAS_RoutingGetMemory( aasworld->numareas * sizeof( aas_routingupdate_t ) ); if ( aasworld->portalupdate ) { AAS_RoutingFreeMemory( aasworld->portalupdate ); } //allocate memory for the portal update fields aasworld->portalupdate = (aas_routingupdate_t *) AAS_RoutingGetMemory( ( aasworld->numportals + 1 ) * sizeof( aas_routingupdate_t ) ); } //end of the function AAS_InitRoutingUpdate //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateAllRoutingCache( void ) { int i, j, k, t, tfl, numroutingareas; aas_areasettings_t *areasettings; aas_reachability_t *reach; numroutingareas = 0; 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 // tfl = TFL_DEFAULT & ~(TFL_JUMPPAD|TFL_ROCKETJUMP|TFL_BFGJUMP|TFL_GRAPPLEHOOK|TFL_DOUBLEJUMP|TFL_RAMPJUMP|TFL_STRAFEJUMP|TFL_SLIME|TFL_LAVA); botimport.Print( PRT_MESSAGE, "AAS_CreateAllRoutingCache\n" ); // for ( i = 1; i < aasworld->numareas; i++ ) { if ( !AAS_AreaReachability( i ) ) { continue; } areasettings = &aasworld->areasettings[i]; for ( k = 0; k < areasettings->numreachableareas; k++ ) { reach = &aasworld->reachability[areasettings->firstreachablearea + k]; if ( aasworld->travelflagfortype[reach->traveltype] & tfl ) { break; } } if ( k >= areasettings->numreachableareas ) { continue; } aasworld->areasettings[i].areaflags |= AREA_USEFORROUTING; numroutingareas++; } for ( i = 1; i < aasworld->numareas; i++ ) { if ( !( aasworld->areasettings[i].areaflags & AREA_USEFORROUTING ) ) { continue; } for ( j = 1; j < aasworld->numareas; j++ ) { if ( i == j ) { continue; } if ( !( aasworld->areasettings[j].areaflags & AREA_USEFORROUTING ) ) { continue; } t = AAS_AreaTravelTimeToGoalArea( j, aasworld->areawaypoints[j], i, tfl ); aasworld->frameroutingupdates = 0; //if (t) break; //Log_Write("traveltime from %d to %d is %d", i, j, t); } //end for } //end for } //end of the function AAS_CreateAllRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short CRC_ProcessString( unsigned char *data, int length ); //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 reachcrc; int numportalcache; int numareacache; } routecacheheader_t; #define RCID ( ( 'C' << 24 ) + ( 'R' << 16 ) + ( 'E' << 8 ) + 'M' ) #define RCVERSION 16 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, size; aas_routingcache_t *cache; aas_cluster_t *cluster; fileHandle_t fp; char filename[MAX_QPATH]; routecacheheader_t routecacheheader; byte *buf; buf = (byte *) GetClearedMemory( aasworld->numareas * 2 * sizeof( byte ) ); // in case it ends up bigger than the decompressedvis, which is rare but possible 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.reachcrc = CRC_ProcessString( (unsigned char *)aasworld->reachability, sizeof( aas_reachability_t ) * aasworld->reachabilitysize ); routecacheheader.numportalcache = numportalcache; routecacheheader.numareacache = numareacache; //write the header botimport.FS_Write( &routecacheheader, sizeof( routecacheheader_t ), fp ); //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 ); } //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 ); } //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, buf ); botimport.FS_Write( &size, sizeof( int ), fp ); botimport.FS_Write( buf, size, fp ); } // write the waypoints botimport.FS_Write( aasworld->areawaypoints, sizeof( vec3_t ) * aasworld->numareas, fp ); // botimport.FS_FCloseFile( fp ); botimport.Print( PRT_MESSAGE, "\nroute cache written to %s\n", filename ); } //end of the function AAS_WriteRouteCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_routingcache_t *AAS_ReadCache( fileHandle_t fp ) { int size, i; aas_routingcache_t *cache; botimport.FS_Read( &size, sizeof( size ), fp ); size = LittleLong( size ); cache = (aas_routingcache_t *) AAS_RoutingGetMemory( size ); cache->size = size; botimport.FS_Read( (unsigned char *)cache + sizeof( size ), size - sizeof( size ), fp ); if ( 1 != LittleLong( 1 ) ) { cache->time = LittleFloat( cache->time ); cache->cluster = LittleLong( cache->cluster ); cache->areanum = LittleLong( cache->areanum ); cache->origin[0] = LittleFloat( cache->origin[0] ); cache->origin[1] = LittleFloat( cache->origin[1] ); cache->origin[2] = LittleFloat( cache->origin[2] ); cache->starttraveltime = LittleFloat( cache->starttraveltime ); cache->travelflags = LittleLong( cache->travelflags ); } // cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + // (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; cache->reachabilities = (unsigned char *) cache + sizeof( aas_routingcache_t ) + ( ( size - sizeof( aas_routingcache_t ) ) / 3 ) * 2; //DAJ BUGFIX for missing byteswaps for traveltimes size = ( size - sizeof( aas_routingcache_t ) ) / 3 + 1; for ( i = 0; i < size; i++ ) { cache->traveltimes[i] = LittleShort( cache->traveltimes[i] ); } return cache; } //end of the function AAS_ReadCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_ReadRouteCache( void ) { int i, clusterareanum, size; fileHandle_t fp = 0; 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 ) { botimport.FS_FCloseFile( fp ); AAS_Error( "%s is not a route cache dump\n" ); return qfalse; } //end if if ( routecacheheader.version != RCVERSION ) { botimport.FS_FCloseFile( fp ); AAS_Error( "route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION ); return qfalse; } //end if if ( routecacheheader.numareas != aasworld->numareas ) { botimport.FS_FCloseFile( fp ); //AAS_Error("route cache dump has wrong number of areas\n"); return qfalse; } //end if if ( routecacheheader.numclusters != aasworld->numclusters ) { botimport.FS_FCloseFile( fp ); //AAS_Error("route cache dump has wrong number of clusters\n"); return qfalse; } //end if #if defined( MACOSX ) // the crc table stuff is endian orientated.... #else if ( routecacheheader.areacrc != CRC_ProcessString( (unsigned char *)aasworld->areas, sizeof( aas_area_t ) * aasworld->numareas ) ) { botimport.FS_FCloseFile( fp ); //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 ) ) { botimport.FS_FCloseFile( fp ); //AAS_Error("route cache dump cluster CRC incorrect\n"); return qfalse; } //end if if ( routecacheheader.reachcrc != CRC_ProcessString( (unsigned char *)aasworld->reachability, sizeof( aas_reachability_t ) * aasworld->reachabilitysize ) ) { botimport.FS_FCloseFile( fp ); //AAS_Error("route cache dump reachability CRC incorrect\n"); return qfalse; } //end if #endif //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 ); } } // read the area waypoints aasworld->areawaypoints = (vec3_t *) GetClearedMemory( aasworld->numareas * sizeof( vec3_t ) ); botimport.FS_Read( aasworld->areawaypoints, aasworld->numareas * sizeof( vec3_t ), fp ); // botimport.FS_FCloseFile( fp ); return qtrue; } //end of the function AAS_ReadRouteCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateVisibility( qboolean waypointsOnly ); void AAS_InitRouting( void ) { AAS_InitTravelFlagFromType(); //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(); // #ifdef ROUTING_DEBUG numareacacheupdates = 0; numportalcacheupdates = 0; #endif //ROUTING_DEBUG // routingcachesize = 0; max_routingcachesize = 1024 * (int) LibVarValue( "max_routingcache", DEFAULT_MAX_ROUTINGCACHESIZE ); max_frameroutingupdates = (int) LibVarGetValue( "bot_frameroutingupdates" ); // // enable this for quick testing of maps without enemies if ( LibVarGetValue( "bot_norcd" ) ) { // RF, create the waypoints for each area AAS_CreateVisibility( qtrue ); } else { // Ridah, load or create the routing cache if ( !AAS_ReadRouteCache() ) { aasworld->initialized = qtrue; // Hack, so routing can compute traveltimes AAS_CreateVisibility( qfalse ); // RF, removed, going back to dynamic routes //AAS_CreateAllRoutingCache(); aasworld->initialized = qfalse; AAS_WriteRouteCache(); // save it so we don't have to create it again } // done. } } //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 all the existing area visibility data AAS_FreeAreaVisibility(); // free cached travel times within areas if ( aasworld->areatraveltimes ) { AAS_RoutingFreeMemory( aasworld->areatraveltimes ); } aasworld->areatraveltimes = NULL; // free cached maximum travel time through cluster portals if ( aasworld->portalmaxtraveltimes ) { AAS_RoutingFreeMemory( aasworld->portalmaxtraveltimes ); } aasworld->portalmaxtraveltimes = NULL; // free reversed reachability links if ( aasworld->reversedreachability ) { AAS_RoutingFreeMemory( aasworld->reversedreachability ); } aasworld->reversedreachability = NULL; // free routing algorithm memory if ( aasworld->areaupdate ) { AAS_RoutingFreeMemory( aasworld->areaupdate ); } aasworld->areaupdate = NULL; if ( aasworld->portalupdate ) { AAS_RoutingFreeMemory( aasworld->portalupdate ); } aasworld->portalupdate = NULL; // free area waypoints if ( aasworld->areawaypoints ) { FreeMemory( aasworld->areawaypoints ); } aasworld->areawaypoints = NULL; } //end of the function AAS_FreeRoutingCaches //=========================================================================== // this function could be replaced by a bubble sort or for even faster // routing by a B+ tree // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== __inline void AAS_AddUpdateToList( aas_routingupdate_t **updateliststart, aas_routingupdate_t **updatelistend, aas_routingupdate_t *update ) { if ( !update->inlist ) { if ( *updatelistend ) { ( *updatelistend )->next = update; } else { *updateliststart = update;} update->prev = *updatelistend; update->next = NULL; *updatelistend = update; update->inlist = qtrue; } //end if } //end of the function AAS_AddUpdateToList //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaContentsTravelFlag( int areanum ) { int contents, tfl; contents = aasworld->areasettings[areanum].contents; tfl = 0; if ( contents & AREACONTENTS_WATER ) { return tfl |= TFL_WATER; } else if ( contents & AREACONTENTS_SLIME ) { return tfl |= TFL_SLIME; } else if ( contents & AREACONTENTS_LAVA ) { return tfl |= TFL_LAVA; } else { tfl |= TFL_AIR;} if ( contents & AREACONTENTS_DONOTENTER_LARGE ) { tfl |= TFL_DONOTENTER_LARGE; } if ( contents & AREACONTENTS_DONOTENTER ) { return tfl |= TFL_DONOTENTER; } return tfl; } //end of the function AAS_AreaContentsTravelFlag //=========================================================================== // update the given routing cache // // Parameter: areacache : routing cache to update // Returns: - // Changes Globals: - //=========================================================================== void AAS_UpdateAreaRoutingCache( aas_routingcache_t *areacache ) { int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum; int numreachabilityareas; unsigned short int t, startareatraveltimes[128]; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; aas_reversedreachability_t *revreach; aas_reversedlink_t *revlink; #ifdef ROUTING_DEBUG numareacacheupdates++; #endif //ROUTING_DEBUG //number of reachability areas within this cluster numreachabilityareas = aasworld->clusters[areacache->cluster].numreachabilityareas; // //clear the routing update fields // memset(aasworld->areaupdate, 0, aasworld->numareas * sizeof(aas_routingupdate_t)); // badtravelflags = ~areacache->travelflags; // clusterareanum = AAS_ClusterAreaNum( areacache->cluster, areacache->areanum ); if ( clusterareanum >= numreachabilityareas ) { return; } // memset( startareatraveltimes, 0, sizeof( startareatraveltimes ) ); // curupdate = &aasworld->areaupdate[clusterareanum]; curupdate->areanum = areacache->areanum; //VectorCopy(areacache->origin, curupdate->start); curupdate->areatraveltimes = aasworld->areatraveltimes[areacache->areanum][0]; 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, flip the lists 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 ( aasworld->travelflagfortype[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_AreaContentsTravelFlag( 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 trying to avoid this area if ( aasworld->areasettings[reach->areanum].areaflags & AREA_AVOID ) { t += 1000; } else if ( ( aasworld->areasettings[reach->areanum].areaflags & AREA_AVOID_AXIS ) && ( areacache->travelflags & TFL_TEAM_AXIS ) ) { t += 200; // + (curupdate->areatraveltimes[i] + reach->traveltime) * 30; } else if ( ( aasworld->areasettings[reach->areanum].areaflags & AREA_AVOID_ALLIES ) && ( areacache->travelflags & TFL_TEAM_ALLIES ) ) { t += 200; // + (curupdate->areatraveltimes[i] + reach->traveltime) * 30; } // aasworld->frameroutingupdates++; // if ( aasworld->areatraveltimes[nextareanum] && ( !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 ) { 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: - //=========================================================================== aas_routingcache_t *AAS_GetAreaRoutingCache( int clusternum, int areanum, int travelflags, qboolean forceUpdate ) { 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]; // RF, remove team-specific flags which don't exist in this cluster travelflags &= ~TFL_TEAM_FLAGS | aasworld->clusterTeamTravelFlags[clusternum]; //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 ) { //NOTE: the number of routing updates is limited per frame if ( !forceUpdate && ( aasworld->frameroutingupdates > max_frameroutingupdates ) ) { return NULL; } //end if 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 //the cache has been accessed cache->time = AAS_RoutingTime(); return cache; } //end of the function AAS_GetAreaRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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 // 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; //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, flip the lists 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, qtrue ); //take all portals of the cluster for ( i = 0; i < cluster->numportals; i++ ) { portalnum = aasworld->portalindex[cluster->firstportal + i]; portal = &aasworld->portals[portalnum]; // 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; portalcache->reachabilities[portalnum] = cache->reachabilities[clusterareanum]; 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 actual portal area for the next update nextupdate->tmptraveltime = t + aasworld->portalmaxtraveltimes[portalnum]; if ( !nextupdate->inlist ) { 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: - //=========================================================================== 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 //the cache has been accessed cache->time = AAS_RoutingTime(); return cache; } //end of the function AAS_GetPortalRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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; aas_portalindex_t *pPortalnum; if ( !aasworld->initialized ) { return qfalse; } if ( areanum == goalareanum ) { *traveltime = 1; *reachnum = 0; return qtrue; } //end if // if ( areanum <= 0 || areanum >= aasworld->numareas ) { if ( bot_developer ) { 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 ( bot_developer ) { botimport.Print( PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum ); } //end if return qfalse; } //end if //make sure the routing cache doesn't grow to large while ( routingcachesize > max_routingcachesize ) { if ( !AAS_FreeOldestCache() ) { break; } } // if ( AAS_AreaDoNotEnter( areanum ) || AAS_AreaDoNotEnter( goalareanum ) ) { travelflags |= TFL_DONOTENTER; } //end if if ( AAS_AreaDoNotEnterLarge( areanum ) || AAS_AreaDoNotEnterLarge( goalareanum ) ) { travelflags |= TFL_DONOTENTER_LARGE; } //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; } } else if ( clusternum > 0 && goalclusternum < 0 ) { //check if the goalarea is a portal of the area cluster portal = &aasworld->portals[-goalclusternum]; if ( portal->frontcluster == clusternum || portal->backcluster == clusternum ) { goalclusternum = clusternum; } } //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, qfalse ); // RF, note that the routing cache might be NULL now since we are restricting // the updates per frame, hopefully rejected cache's will be requested again // when things have settled down if ( !areacache ) { return qfalse; } //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 qfalse; } //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; } // besttime = 0; bestreachnum = -1; //the cluster the area is in cluster = &aasworld->clusters[clusternum]; //current area inside the current cluster clusterareanum = AAS_ClusterAreaNum( clusternum, areanum ); //if the area is NOT a reachability area if ( clusterareanum >= cluster->numreachabilityareas ) { return qfalse; } // pPortalnum = aasworld->portalindex + cluster->firstportal; //find the portal of the area cluster leading towards the goal area for ( i = 0; i < cluster->numportals; i++, pPortalnum++ ) { portalnum = *pPortalnum; //if the goal area isn't reachable from the portal if ( !portalcache->traveltimes[portalnum] ) { continue; } // portal = aasworld->portals + portalnum; // if the area in disabled if ( aasworld->areasettings[portal->areanum].areaflags & AREA_DISABLED ) { continue; } // if there is no reachability out of the area if ( !aasworld->areasettings[portal->areanum].numreachableareas ) { continue; } //get the cache of the portal area areacache = AAS_GetAreaRoutingCache( clusternum, portal->areanum, travelflags, qfalse ); // RF, this may be NULL if we were unable to calculate the cache this frame if ( !areacache ) { return qfalse; } //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 area portal // because we can't directly calculate the exact travel time // to be more specific we don't know which reachability is used to travel // into the portal area when coming from the current area t += aasworld->portalmaxtraveltimes[portalnum]; // // Ridah, needs to be up here *reachnum = aasworld->areasettings[areanum].firstreachablearea + areacache->reachabilities[clusterareanum]; //botimport.Print(PRT_MESSAGE, "portal reachability: %i\n", (int)areacache->reachabilities[clusterareanum] ); if ( origin ) { 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 // Ridah, check a route was found 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; if ( AAS_AreaRouteToGoalArea( areanum, origin, goalareanum, travelflags, &traveltime, &reachnum ) ) { return traveltime; } return 0; } //end of the function AAS_AreaTravelTimeToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaTravelTimeToGoalAreaCheckLoop( int areanum, vec3_t origin, int goalareanum, int travelflags, int loopareanum ) { int traveltime, reachnum; aas_reachability_t *reach; if ( AAS_AreaRouteToGoalArea( areanum, origin, goalareanum, travelflags, &traveltime, &reachnum ) ) { reach = &aasworld->reachability[reachnum]; if ( loopareanum && reach->areanum == loopareanum ) { return 0; // going here will cause a looped route } return traveltime; } return 0; } //end of the function AAS_AreaTravelTimeToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaReachabilityToGoalArea( int areanum, vec3_t origin, int goalareanum, int travelflags ) { int traveltime, reachnum; if ( AAS_AreaRouteToGoalArea( areanum, origin, goalareanum, travelflags, &traveltime, &reachnum ) ) { return reachnum; } return 0; } //end of the function AAS_AreaReachabilityToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ReachabilityFromNum( int num, struct aas_reachability_s *reach ) { if ( !aasworld->initialized ) { memset( reach, 0, sizeof( aas_reachability_t ) ); return; } //end if if ( num < 0 || num >= aasworld->reachabilitysize ) { memset( reach, 0, sizeof( aas_reachability_t ) ); return; } //end if 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 == TRAVEL_ELEVATOR ) { if ( aasworld->reachability[i].facenum == modelnum ) { return i; } } //end if else if ( aasworld->reachability[i].traveltype == 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 && 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 //=========================================================================== // run-length compression on zeros // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_CompressVis( byte *vis, int numareas, byte *dest ) { int j; int rep; //int visrow; byte *dest_p; byte check; // dest_p = dest; //visrow = (numareas + 7)>>3; for ( j = 0 ; j < numareas /*visrow*/ ; j++ ) { *dest_p++ = vis[j]; check = vis[j]; //if (vis[j]) // continue; rep = 1; for ( j++; j < numareas /*visrow*/ ; j++ ) if ( vis[j] != check || rep == 255 ) { break; } else { rep++; } *dest_p++ = rep; j--; } return dest_p - dest; } //end of the function AAS_CompressVis //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DecompressVis( byte *in, int numareas, byte *decompressed ) { byte c; byte *out; //int row; byte *end; // initialize the vis data, only set those that are visible memset( decompressed, 0, numareas ); //row = (numareas+7)>>3; out = decompressed; end = ( byte * )( (int)decompressed + numareas ); do { /* if (*in) { *out++ = *in++; continue; } */ c = in[1]; if ( !c ) { AAS_Error( "DecompressVis: 0 repeat" ); } if ( *in ) { // we need to set these bits memset( out, 1, c ); } in += 2; /* while (c) { *out++ = 0; c--; } */ out += c; } while ( out < end ); } //end of the function AAS_DecompressVis //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaVisible( int srcarea, int destarea ) { if ( !aasworld->areavisibility ) { // botimport.Print(PRT_MESSAGE, "AAS_AreaVisible: no vis data available, returning qtrue\n" ); return qtrue; } if ( srcarea != aasworld->decompressedvisarea ) { if ( !aasworld->areavisibility[srcarea] ) { return qfalse; } AAS_DecompressVis( aasworld->areavisibility[srcarea], aasworld->numareas, aasworld->decompressedvis ); aasworld->decompressedvisarea = srcarea; } return aasworld->decompressedvis[destarea]; } //end of the function AAS_AreaVisible //=========================================================================== // just center to center visibility checking... // FIXME: implement a correct full vis // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateVisibility( qboolean waypointsOnly ) { int i, j, size, totalsize; vec3_t endpos, mins, maxs; bsp_trace_t trace; byte *buf; byte *validareas = NULL; int numvalid = 0; byte *areaTable = NULL; int numAreas, numAreaBits; numAreas = aasworld->numareas; numAreaBits = ( ( numAreas + 8 ) >> 3 ); if ( !waypointsOnly ) { validareas = (byte *) GetClearedMemory( numAreas * sizeof( byte ) ); } aasworld->areawaypoints = (vec3_t *) GetClearedMemory( numAreas * sizeof( vec3_t ) ); totalsize = numAreas * sizeof( byte * ); for ( i = 1; i < numAreas; i++ ) { if ( !AAS_AreaReachability( i ) ) { continue; } // find the waypoint VectorCopy( aasworld->areas[i].center, endpos ); endpos[2] -= 256; AAS_PresenceTypeBoundingBox( PRESENCE_NORMAL, mins, maxs ); trace = AAS_Trace( aasworld->areas[i].center, mins, maxs, endpos, -1, CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ); if ( trace.startsolid && trace.ent < ENTITYNUM_WORLD ) { trace = AAS_Trace( aasworld->areas[i].center, mins, maxs, endpos, trace.ent, CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ); } if ( !trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum( trace.endpos ) == i ) { VectorCopy( trace.endpos, aasworld->areawaypoints[i] ); if ( !waypointsOnly ) { validareas[i] = 1; } numvalid++; } else { VectorClear( aasworld->areawaypoints[i] ); } } if ( waypointsOnly ) { return; } aasworld->areavisibility = (byte **) GetClearedMemory( numAreas * sizeof( byte * ) ); aasworld->decompressedvis = (byte *) GetClearedMemory( numAreas * sizeof( byte ) ); areaTable = (byte *) GetClearedMemory( numAreas * numAreaBits * sizeof( byte ) ); buf = (byte *) GetClearedMemory( numAreas * 2 * sizeof( byte ) ); // in case it ends up bigger than the decompressedvis, which is rare but possible for ( i = 1; i < numAreas; i++ ) { if ( !validareas[i] ) { continue; } for ( j = 1; j < numAreas; j++ ) { aasworld->decompressedvis[j] = 0; if ( i == j ) { aasworld->decompressedvis[j] = 1; if ( areaTable ) { areaTable[ ( i * numAreaBits ) + ( j >> 3 ) ] |= ( 1 << ( j & 7 ) ); } continue; } if ( !validareas[j] ) { continue; } // if we have already checked this combination, copy the result if ( areaTable && ( i > j ) ) { // use the reverse result stored in the table if ( areaTable[ ( j * numAreaBits ) + ( i >> 3 ) ] & ( 1 << ( i & 7 ) ) ) { aasworld->decompressedvis[j] = 1; } // done, move to the next area continue; } // RF, check PVS first, since it's much faster if ( !AAS_inPVS( aasworld->areawaypoints[i], aasworld->areawaypoints[j] ) ) { continue; } trace = AAS_Trace( aasworld->areawaypoints[i], NULL, NULL, aasworld->areawaypoints[j], -1, CONTENTS_SOLID ); if ( trace.startsolid && trace.ent < ENTITYNUM_WORLD ) { trace = AAS_Trace( aasworld->areas[i].center, mins, maxs, endpos, trace.ent, CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ); } if ( trace.fraction >= 1 ) { if ( areaTable ) { areaTable[ ( i * numAreaBits ) + ( j >> 3 ) ] |= ( 1 << ( j & 7 ) ); } aasworld->decompressedvis[j] = 1; } } size = AAS_CompressVis( aasworld->decompressedvis, numAreas, buf ); aasworld->areavisibility[i] = (byte *) GetMemory( size ); memcpy( aasworld->areavisibility[i], buf, size ); totalsize += size; } if ( areaTable ) { FreeMemory( areaTable ); } botimport.Print( PRT_MESSAGE, "AAS_CreateVisibility: compressed vis size = %i\n", totalsize ); } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float VectorDistance( vec3_t v1, vec3_t v2 ); extern void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ) ; int AAS_NearestHideArea( int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags, float maxdist, vec3_t distpos ) { int i, j, nextareanum, badtravelflags, numreach, bestarea; unsigned short int t, besttraveltime, enemytraveltime; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; float dist1, dist2; float enemytraveldist; vec3_t enemyVec; qboolean startVisible; vec3_t v1, v2, p; #define MAX_HIDEAREA_LOOPS 3000 static float lastTime; static int loopCount; // if ( !aasworld->areavisibility ) { return 0; } // if ( srcnum < 0 ) { // hack to force run this call srcnum = -srcnum - 1; lastTime = 0; } // don't run this more than once per frame if ( lastTime == AAS_Time() && loopCount >= MAX_HIDEAREA_LOOPS ) { return 0; } if ( lastTime != AAS_Time() ) { loopCount = 0; } lastTime = AAS_Time(); // if ( !aasworld->hidetraveltimes ) { aasworld->hidetraveltimes = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } else { memset( aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof( unsigned short int ) ); } //end else // if ( !aasworld->visCache ) { aasworld->visCache = (byte *) GetClearedMemory( aasworld->numareas * sizeof( byte ) ); } else { memset( aasworld->visCache, 0, aasworld->numareas * sizeof( byte ) ); } //end else besttraveltime = 0; bestarea = 0; if ( enemyareanum ) { enemytraveltime = AAS_AreaTravelTimeToGoalArea( areanum, origin, enemyareanum, travelflags ); } VectorSubtract( enemyorigin, origin, enemyVec ); enemytraveldist = VectorNormalize( enemyVec ); startVisible = botimport.BotVisibleFromPos( enemyorigin, enemynum, origin, srcnum, qfalse ); // badtravelflags = ~travelflags; // curupdate = &aasworld->areaupdate[areanum]; curupdate->areanum = areanum; VectorCopy( origin, curupdate->start ); // GORDON: TEMP: FIXME: just avoiding a crash for the moment if ( areanum == 0 ) { return 0; } 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 current list, flip the lists 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 ( aasworld->travelflagfortype[reach->traveltype] & badtravelflags ) { continue; } // if ( AAS_AreaContentsTravelFlag( reach->areanum ) & badtravelflags ) { continue; } // dont pass through ladder areas if ( aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER ) { continue; } // if ( aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED ) { 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; } // if this moves us outside maxdist if ( distpos && ( VectorDistance( reach->end, distpos ) > maxdist ) ) { 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; // inc the loopCount, we are starting to use a bit of cpu time loopCount++; // if this isn't the fastest route to this area, ignore if ( aasworld->hidetraveltimes[nextareanum] && aasworld->hidetraveltimes[nextareanum] < t ) { continue; } aasworld->hidetraveltimes[nextareanum] = t; // if the bestarea is this area, then it must be a longer route, so ignore it if ( bestarea == nextareanum ) { bestarea = 0; besttraveltime = 0; } // do this test now, so we can reject the route if it starts out too long if ( besttraveltime && t >= besttraveltime ) { continue; } // //avoid going near the enemy ProjectPointOntoVector( enemyorigin, curupdate->start, reach->end, p ); for ( j = 0; j < 3; j++ ) { if ( ( p[j] > curupdate->start[j] + 0.1 && p[j] > reach->end[j] + 0.1 ) || ( p[j] < curupdate->start[j] - 0.1 && p[j] < reach->end[j] - 0.1 ) ) { 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 ( enemytraveldist > 32 && dist2 < enemytraveldist && dist2 < 256 ) { continue; } // VectorSubtract( reach->end, origin, v2 ); if ( enemytraveldist > 32 && DotProduct( v2, enemyVec ) > enemytraveldist / 2 ) { continue; } // VectorSubtract( enemyorigin, curupdate->start, v1 ); dist1 = VectorLength( v1 ); // if ( enemytraveldist > 32 && dist2 < dist1 ) { t += ( dist1 - dist2 ) * 10; // test it again after modifying it if ( besttraveltime && t >= besttraveltime ) { continue; } } // make sure the hide area doesn't have anyone else in it if ( AAS_IsEntityInArea( srcnum, -1, nextareanum ) ) { t += 1000; // avoid this path/area //continue; } // // if we weren't visible when starting, make sure we don't move into their view if ( enemyareanum && !startVisible && AAS_AreaVisible( enemyareanum, nextareanum ) ) { continue; //t += 1000; } // if ( !besttraveltime || besttraveltime > t ) { // // if this area doesn't have a vis list, ignore it if ( aasworld->areavisibility[nextareanum] ) { //if the nextarea is not visible from the enemy area if ( !AAS_AreaVisible( enemyareanum, nextareanum ) ) { // now last of all, check that this area is a safe hiding spot if ( ( aasworld->visCache[nextareanum] == 2 ) || ( !aasworld->visCache[nextareanum] && !botimport.BotVisibleFromPos( enemyorigin, enemynum, aasworld->areawaypoints[nextareanum], srcnum, qfalse ) ) ) { aasworld->visCache[nextareanum] = 2; besttraveltime = t; bestarea = nextareanum; } else { aasworld->visCache[nextareanum] = 1; } } //end if } // // getting down to here is bad for cpu usage if ( loopCount++ > MAX_HIDEAREA_LOOPS ) { //botimport.Print(PRT_MESSAGE, "AAS_NearestHideArea: exceeded max loops, aborting\n" ); continue; } // // otherwise, add this to the list so we check is reachables // disabled, this should only store the raw traveltime, not the adjusted time //aasworld->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 //botimport.Print(PRT_MESSAGE, "AAS_NearestHideArea: hidearea: %i, %i loops\n", bestarea, count ); return bestarea; } //end of the function AAS_NearestHideArea int BotFuzzyPointReachabilityArea( vec3_t origin ); //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FindAttackSpotWithinRange( int srcnum, int rangenum, int enemynum, float rangedist, int travelflags, float *outpos ) { int i, nextareanum, badtravelflags, numreach, bestarea; unsigned short int t, besttraveltime, enemytraveltime; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; vec3_t srcorg, rangeorg, enemyorg; int srcarea, rangearea, enemyarea; unsigned short int srctraveltime; int count = 0; #define MAX_ATTACKAREA_LOOPS 200 static float lastTime; // // RF, currently doesn't work with multiple AAS worlds, so only enable for the default world //if (aasworld != aasworlds) return 0; // // don't run this more than once per frame if ( lastTime == AAS_Time() ) { return 0; } lastTime = AAS_Time(); // if ( !aasworld->hidetraveltimes ) { aasworld->hidetraveltimes = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } else { memset( aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof( unsigned short int ) ); } //end else // if ( !aasworld->visCache ) { aasworld->visCache = (byte *) GetClearedMemory( aasworld->numareas * sizeof( byte ) ); } else { memset( aasworld->visCache, 0, aasworld->numareas * sizeof( byte ) ); } //end else // AAS_EntityOrigin( srcnum, srcorg ); AAS_EntityOrigin( rangenum, rangeorg ); AAS_EntityOrigin( enemynum, enemyorg ); // srcarea = BotFuzzyPointReachabilityArea( srcorg ); rangearea = BotFuzzyPointReachabilityArea( rangeorg ); enemyarea = BotFuzzyPointReachabilityArea( enemyorg ); // besttraveltime = 0; bestarea = 0; enemytraveltime = AAS_AreaTravelTimeToGoalArea( srcarea, srcorg, enemyarea, travelflags ); // badtravelflags = ~travelflags; // curupdate = &aasworld->areaupdate[rangearea]; curupdate->areanum = rangearea; VectorCopy( rangeorg, curupdate->start ); curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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 current list, flip the lists 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 ( aasworld->travelflagfortype[reach->traveltype] & badtravelflags ) { continue; } // if ( AAS_AreaContentsTravelFlag( reach->areanum ) & badtravelflags ) { continue; } // dont pass through ladder areas if ( aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER ) { continue; } // if ( aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED ) { continue; } //number of the area the reachability leads to nextareanum = reach->areanum; // if this moves us into the enemies area, skip it if ( nextareanum == enemyarea ) { continue; } // if we've already been to this area if ( aasworld->hidetraveltimes[nextareanum] ) { continue; } //time already travelled plus the traveltime through //the current area plus the travel time from the reachability if ( count++ > MAX_ATTACKAREA_LOOPS ) { //botimport.Print(PRT_MESSAGE, "AAS_FindAttackSpotWithinRange: exceeded max loops, aborting\n" ); if ( bestarea ) { VectorCopy( aasworld->areawaypoints[bestarea], outpos ); } return bestarea; } t = curupdate->tmptraveltime + AAS_AreaTravelTime( curupdate->areanum, curupdate->start, reach->start ) + reach->traveltime; // // if it's too far from rangenum, ignore if ( Distance( rangeorg, aasworld->areawaypoints[nextareanum] ) > rangedist ) { continue; } // // find the traveltime from srcnum srctraveltime = AAS_AreaTravelTimeToGoalArea( srcarea, srcorg, nextareanum, travelflags ); // do this test now, so we can reject the route if it starts out too long if ( besttraveltime && srctraveltime >= besttraveltime ) { continue; } // // if this area doesn't have a vis list, ignore it if ( aasworld->areavisibility[nextareanum] ) { //if the nextarea can see the enemy area if ( AAS_AreaVisible( enemyarea, nextareanum ) ) { // now last of all, check that this area is a good attacking spot if ( ( aasworld->visCache[nextareanum] == 2 ) || ( !aasworld->visCache[nextareanum] && ( count += 10 ) && // we are about to use lots of CPU time botimport.BotCheckAttackAtPos( srcnum, enemynum, aasworld->areawaypoints[nextareanum], qfalse, qfalse ) ) ) { aasworld->visCache[nextareanum] = 2; besttraveltime = srctraveltime; bestarea = nextareanum; } else { aasworld->visCache[nextareanum] = 1; } } //end if } aasworld->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 for } //end while //botimport.Print(PRT_MESSAGE, "AAS_NearestHideArea: hidearea: %i, %i loops\n", bestarea, count ); if ( bestarea ) { VectorCopy( aasworld->areawaypoints[bestarea], outpos ); } return bestarea; } //end of the function AAS_NearestHideArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_GetRouteFirstVisPos( vec3_t srcpos, vec3_t destpos, int travelflags, vec3_t retpos ) { int srcarea, destarea, travarea; vec3_t travpos; int ftraveltime, freachnum, lasttraveltime; aas_reachability_t reach; int loops = 0; #define MAX_GETROUTE_VISPOS_LOOPS 200 // // SRCPOS: enemy // DESTPOS: self // RETPOS: first area that is visible from destpos, in route from srcpos to destpos srcarea = BotFuzzyPointReachabilityArea( srcpos ); if ( !srcarea ) { return qfalse; } destarea = BotFuzzyPointReachabilityArea( destpos ); if ( !destarea ) { return qfalse; } if ( destarea == srcarea ) { VectorCopy( srcpos, retpos ); return qtrue; } // //if the srcarea can see the destarea if ( AAS_AreaVisible( srcarea, destarea ) ) { VectorCopy( srcpos, retpos ); return qtrue; } // if this area doesn't have a vis list, ignore it if ( !aasworld->areavisibility[destarea] ) { return qfalse; } // travarea = srcarea; VectorCopy( srcpos, travpos ); lasttraveltime = -1; while ( ( loops++ < MAX_GETROUTE_VISPOS_LOOPS ) && AAS_AreaRouteToGoalArea( travarea, travpos, destarea, travelflags, &ftraveltime, &freachnum ) ) { if ( lasttraveltime >= 0 && ftraveltime >= lasttraveltime ) { return qfalse; // we may be in a loop } lasttraveltime = ftraveltime; // AAS_ReachabilityFromNum( freachnum, &reach ); if ( reach.areanum == destarea ) { VectorCopy( travpos, retpos ); return qtrue; } //if the reach area can see the destarea if ( AAS_AreaVisible( reach.areanum, destarea ) ) { VectorCopy( reach.end, retpos ); return qtrue; } // travarea = reach.areanum; VectorCopy( reach.end, travpos ); } // // unsuccessful return qfalse; } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_ListAreasInRange( vec3_t srcpos, int srcarea, float range, int travelflags, vec3_t *outareas, int maxareas ) { int i, nextareanum, badtravelflags, numreach; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; int count = 0; // if ( !aasworld->hidetraveltimes ) { aasworld->hidetraveltimes = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } else { memset( aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof( unsigned short int ) ); } //end else // badtravelflags = ~travelflags; // curupdate = &aasworld->areaupdate[srcarea]; curupdate->areanum = srcarea; VectorCopy( srcpos, curupdate->start ); // GORDON: TEMP: FIXME: temp to stop crash if ( srcarea == 0 ) { return 0; } curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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 current list, flip the lists 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 ( aasworld->travelflagfortype[reach->traveltype] & badtravelflags ) { continue; } // if ( AAS_AreaContentsTravelFlag( reach->areanum ) & badtravelflags ) { continue; } // dont pass through ladder areas //if (aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER) continue; // //if (aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; //number of the area the reachability leads to nextareanum = reach->areanum; // if we've already been to this area if ( aasworld->hidetraveltimes[nextareanum] ) { continue; } aasworld->hidetraveltimes[nextareanum] = 1; // if it's too far from srcpos, ignore if ( Distance( srcpos, aasworld->areawaypoints[nextareanum] ) > range ) { continue; } // // if visible from srcarea if ( !( aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER ) && !( aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED ) && ( AAS_AreaVisible( srcarea, nextareanum ) ) ) { VectorCopy( aasworld->areawaypoints[nextareanum], outareas[count] ); count++; if ( count >= maxareas ) { break; } } // nextupdate = &aasworld->areaupdate[nextareanum]; nextupdate->areanum = nextareanum; //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 for } //end while return count; } //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AvoidDangerArea( vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, float range, int travelflags ) { int i, nextareanum, badtravelflags, numreach, bestarea = 0; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; int bestTravel = 999999, t; const int maxTime = 5000; const int goodTime = 1000; vec_t dangerDistance = 0; // if ( !aasworld->areavisibility ) { return 0; } if ( !srcarea ) { return 0; } // if ( !aasworld->hidetraveltimes ) { aasworld->hidetraveltimes = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } else { memset( aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof( unsigned short int ) ); } //end else // badtravelflags = ~travelflags; // curupdate = &aasworld->areaupdate[srcarea]; curupdate->areanum = srcarea; VectorCopy( srcpos, curupdate->start ); curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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; // Mad Doctor I, 11/3/2002. The source area never needs to be expanded // again, so mark it as cut off aasworld->hidetraveltimes[srcarea] = 1; //while there are updates in the current list, flip the lists 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 ( aasworld->travelflagfortype[reach->traveltype] & badtravelflags ) { continue; } // if ( AAS_AreaContentsTravelFlag( reach->areanum ) & badtravelflags ) { continue; } // dont pass through ladder areas if ( aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER ) { continue; } // if ( aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED ) { continue; } //number of the area the reachability leads to nextareanum = reach->areanum; // if we've already been to this area if ( aasworld->hidetraveltimes[nextareanum] ) { continue; } aasworld->hidetraveltimes[nextareanum] = 1; // calc traveltime from srcpos t = curupdate->tmptraveltime + AAS_AreaTravelTime( curupdate->areanum, curupdate->start, reach->start ) + reach->traveltime; if ( t > maxTime ) { continue; } if ( t > bestTravel ) { continue; } // How far is it dangerDistance = Distance( dangerpos, aasworld->areawaypoints[nextareanum] ); // if it's safe from dangerpos if ( aasworld->areavisibility[nextareanum] && ( dangerDistance > range ) ) { if ( t < goodTime ) { return nextareanum; } if ( t < bestTravel ) { bestTravel = t; bestarea = nextareanum; } } // nextupdate = &aasworld->areaupdate[nextareanum]; nextupdate->areanum = nextareanum; //remember where we entered this area VectorCopy( reach->end, nextupdate->start ); //if this update is not in the list yet // Mad Doctor I, 11/3/2002. The inlist field seems to not be inited properly for this function. // It causes certain routes to be excluded unnecessarily, so I'm trying to do without it. // Note that the hidetraveltimes array seems to cut off duplicates already. // 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 for } //end while return bestarea; } // // AAS_DangerPQInit() // // Description: Init the priority queue // Written: 12/12/2002 // void AAS_DangerPQInit ( ) { // Local Variables //////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// //@TEST. To test that the retreat algorithm is going to work, I've implemented // a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. // Init the distanceFromDanger array if needed if ( !aasworld->PQ_accumulator ) { // Get memory for this array the safe way. aasworld->PQ_accumulator = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } // if (!aasworld->distanceFromDanger) ... // There are no items in the PQ right now aasworld->PQ_size = 0; } // // AAS_DangerPQInit() // // // AAS_DangerPQInsert // // Description: Put an area into the PQ. ASSUMES the dangerdistance for the // area is set ahead of time. // Written: 12/11/2002 // void AAS_DangerPQInsert ( // The area to insert int areaNum ) { // Local Variables //////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// //@TEST. To test that the retreat algorithm is going to work, I've implemented // a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. // Increment the count in the accum aasworld->PQ_size++; // Put this one at the end aasworld->PQ_accumulator[aasworld->PQ_size] = areaNum; } // // AAS_DangerPQInsert // // // AAS_DangerPQEmpty // // Description: Is the Danger Priority Queue empty? // Written: 12/11/2002 // qboolean AAS_DangerPQEmpty ( ) { // Local Variables //////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// //@TEST. To test that the retreat algorithm is going to work, I've implemented // a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. // It's not empty if anything is in the accumulator if ( aasworld->PQ_size > 0 ) { return qfalse; } return qtrue; } // // AAS_DangerPQEmpty // // // AAS_DangerPQRemove // // Description: Pull the smallest distance area off of the danger Priority // Queue. // Written: 12/11/2002 // int AAS_DangerPQRemove ( ) { // Local Variables //////////////////////////////////////////////////////// int j; int nearest = 1; int nearestArea = aasworld->PQ_accumulator[nearest]; int nearestDistance = aasworld->distanceFromDanger[nearestArea]; int distance; int temp; int currentArea; /////////////////////////////////////////////////////////////////////////// //@TEST. To test that the retreat algorithm is going to work, I've implemented // a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. // Just loop through the items in the PQ for ( j = 2; j <= aasworld->PQ_size; j++ ) { // What's the next area? currentArea = aasworld->PQ_accumulator[j]; // What's the danerg distance of it distance = aasworld->distanceFromDanger[currentArea]; // Is this element the best one? Top of the heap, so to speak if ( distance < nearestDistance ) { // Save this one nearest = j; // This has the best distance nearestDistance = distance; // This one is the nearest region so far nearestArea = currentArea; } // if (distance < nearestDistance)... } // for (j = 2; j <= aasworld->PQ_size; j++)... // Save out the old end of list temp = aasworld->PQ_accumulator[aasworld->PQ_size]; // Put this where the old one was aasworld->PQ_accumulator[nearest] = temp; // Decrement the count aasworld->PQ_size--; return nearestArea; } // // AAS_DangerPQRemove // // // AAS_DangerPQChange // // Description: We've changed the danger distance for this node, so change // its place in the PQ if needed. // Written: 12/11/2002 // void AAS_DangerPQChange ( // The area to change in the PQ int areaNum ) { // Local Variables //////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// //@TEST. To test that the retreat algorithm is going to work, I've implemented // a quicky, slow-ass PQ. This needs to be replaced with a heap implementation. // NOTHING NEEDS TO BE DONE HERE FOR THE SLOW-ASS PQ. } // // AAS_DangerPQChange // //=========================================================================== // // AAS_CalculateDangerZones // // Description: // Written: 12/11/2002 // //=========================================================================== void AAS_CalculateDangerZones ( // Locations of the danger spots int *dangerSpots, // The number of danger spots int dangerSpotCount, // What is the furthest danger range we care about? (Everything further is safe) float dangerRange, // A safe distance to init distanceFromDanger to unsigned short int definitelySafe ) { // Local Variables //////////////////////////////////////////////////////// // Generic index used to loop through danger zones (and later, the reachabilities) int i; // The area number of a danger spot int dangerAreaNum; // What's the current AAS area we're measuring distance from danger to int currentArea; // How many links come out of the current AAS area? int numreach; // Number of the area the reachability leads to int nextareanum; // Distance from current node to next node float distanceFromCurrentNode; // Total distance from danger to next node int dangerDistance; // The previous distance for this node int oldDistance; // A link from the current node aas_reachability_t *reach = NULL; /////////////////////////////////////////////////////////////////////////// // Initialize all of the starting danger zones. for ( i = 0; i < dangerSpotCount; i++ ) { // Get the area number of this danger spot dangerAreaNum = dangerSpots[i]; // Set it's distance to 0, meaning it's 0 units to danger aasworld->distanceFromDanger[dangerAreaNum] = 0; // Add the zone to the PQ. AAS_DangerPQInsert( dangerAreaNum ); } // for (i = 0; i < dangerSpotCount; i++)... // Go through the Priority Queue, pop off the smallest distance, and expand // to the neighboring nodes. Stop when the PQ is empty. while ( !AAS_DangerPQEmpty() ) { // Get the smallest distance in the PQ. currentArea = AAS_DangerPQRemove(); // Check all reversed reachability links numreach = aasworld->areasettings[currentArea].numreachableareas; reach = &aasworld->reachability[aasworld->areasettings[currentArea].firstreachablearea]; // Loop through the neighbors to this node. for ( i = 0; i < numreach; i++, reach++ ) { // Number of the area the reachability leads to nextareanum = reach->areanum; // How far was it from the last node to this one? distanceFromCurrentNode = Distance( aasworld->areawaypoints[currentArea], aasworld->areawaypoints[nextareanum] ); // Calculate the distance from danger to this neighbor along the // current route. dangerDistance = aasworld->distanceFromDanger[currentArea] + (int) distanceFromCurrentNode; // Skip this neighbor if the distance is bigger than we care about. if ( dangerDistance > dangerRange ) { continue; } // Store the distance from danger if it's smaller than any previous // distance to this node (note that unvisited nodes are inited with // a big distance, so this check will be satisfied). if ( dangerDistance < aasworld->distanceFromDanger[nextareanum] ) { // How far was this node from danger before this visit? oldDistance = aasworld->distanceFromDanger[nextareanum]; // Store the new value aasworld->distanceFromDanger[nextareanum] = dangerDistance; // If the neighbor has been calculated already, see if we need to // update the priority. if ( oldDistance != definitelySafe ) { // We need to update the priority queue's position for this node AAS_DangerPQChange( nextareanum ); } // if (aasworld->distanceFromDanger[nextareanum] == definitelySafe)... // Otherwise, insert the neighbor into the PQ. else { // Insert this node into the PQ AAS_DangerPQInsert( nextareanum ); } // else ... } // if (dangerDistance < aasworld->distanceFromDanger[nextareanum])... } // for (i = 0; i < numreach; i++, reach++)... } // while (!AAS_DangerPQEmpty())... // At this point, all of the nodes within our danger range have their // distance from danger calculated. } // // AAS_CalculateDangerZones // //=========================================================================== // // AAS_Retreat // // Use this to find a safe spot away from a dangerous situation/enemy. // This differs from AAS_AvoidDangerArea in the following ways: // * AAS_Retreat will return the farthest location found even if it does not // exceed the desired minimum distance. // * AAS_Retreat will give preference to nodes on the "safe" side of the danger // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Retreat ( // Locations of the danger spots (AAS area numbers) int *dangerSpots, // The number of danger spots int dangerSpotCount, vec3_t srcpos, int srcarea, vec3_t dangerpos, int dangerarea, // Min range from startpos float range, // Min range from danger float dangerRange, int travelflags ) { int i, nextareanum, badtravelflags, numreach, bestarea = 0; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; int t; const int maxTime = 5000; // const int goodTime = 1000; vec_t dangerDistance = 0; vec_t sourceDistance = 0; float bestDistanceSoFar = 0; // Choose a safe distance to init distanceFromDanger to // int definitelySafe = 1 + (int) range; unsigned short int definitelySafe = 0xFFFF; // if ( !aasworld->areavisibility ) { return 0; } // Init the hide travel time if needed if ( !aasworld->hidetraveltimes ) { aasworld->hidetraveltimes = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } // Otherwise, it exists already, so just reset the values else { memset( aasworld->hidetraveltimes, 0, aasworld->numareas * sizeof( unsigned short int ) ); } //end else // Init the distanceFromDanger array if needed if ( !aasworld->distanceFromDanger ) { // Get memory for this array the safe way. aasworld->distanceFromDanger = (unsigned short int *) GetClearedMemory( aasworld->numareas * sizeof( unsigned short int ) ); } // if (!aasworld->distanceFromDanger) ... // Set all the values in the distanceFromDanger array to a safe value memset( aasworld->distanceFromDanger, 0xFF, aasworld->numareas * sizeof( unsigned short int ) ); // Init the priority queue AAS_DangerPQInit(); // Set up the distanceFromDanger array AAS_CalculateDangerZones( dangerSpots, dangerSpotCount, dangerRange, definitelySafe ); // badtravelflags = ~travelflags; // curupdate = &aasworld->areaupdate[srcarea]; curupdate->areanum = srcarea; VectorCopy( srcpos, curupdate->start ); curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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; // Mad Doctor I, 11/3/2002. The source area never needs to be expanded // again, so mark it as cut off aasworld->hidetraveltimes[srcarea] = 1; //while there are updates in the current list, flip the lists 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 ( aasworld->travelflagfortype[reach->traveltype] & badtravelflags ) { continue; } // if ( AAS_AreaContentsTravelFlag( reach->areanum ) & badtravelflags ) { continue; } // dont pass through ladder areas if ( aasworld->areasettings[reach->areanum].areaflags & AREA_LADDER ) { continue; } // if ( aasworld->areasettings[reach->areanum].areaflags & AREA_DISABLED ) { continue; } //number of the area the reachability leads to nextareanum = reach->areanum; // if we've already been to this area if ( aasworld->hidetraveltimes[nextareanum] ) { continue; } aasworld->hidetraveltimes[nextareanum] = 1; // calc traveltime from srcpos t = curupdate->tmptraveltime + AAS_AreaTravelTime( curupdate->areanum, curupdate->start, reach->start ) + reach->traveltime; if ( t > maxTime ) { continue; } // How far is it from a danger area? dangerDistance = aasworld->distanceFromDanger[nextareanum]; // How far is it from our starting position? sourceDistance = Distance( srcpos, aasworld->areawaypoints[nextareanum] ); // If it's safe from dangerpos if ( aasworld->areavisibility[nextareanum] && ( sourceDistance > range ) && ( ( dangerDistance > dangerRange ) || ( dangerDistance == definitelySafe ) ) ) { // Just use this area return nextareanum; } // In case we don't find a perfect one, save the best if ( dangerDistance > bestDistanceSoFar ) { bestarea = nextareanum; bestDistanceSoFar = dangerDistance; } // if (dangerDistance > bestDistanceSoFar)... // nextupdate = &aasworld->areaupdate[nextareanum]; nextupdate->areanum = nextareanum; //remember where we entered this area VectorCopy( reach->end, nextupdate->start ); //if this update is not in the list yet //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 for } //end while return bestarea; } /* =================== AAS_InitTeamDeath =================== */ void AAS_InitTeamDeath( void ) { if ( aasworld->teamDeathTime ) { FreeMemory( aasworld->teamDeathTime ); } aasworld->teamDeathTime = GetClearedMemory( sizeof( int ) * aasworld->numareas * 2 ); if ( aasworld->teamDeathCount ) { FreeMemory( aasworld->teamDeathCount ); } aasworld->teamDeathCount = GetClearedMemory( sizeof( byte ) * aasworld->numareas * 2 ); if ( aasworld->teamDeathAvoid ) { FreeMemory( aasworld->teamDeathAvoid ); } aasworld->teamDeathAvoid = GetClearedMemory( sizeof( byte ) * aasworld->numareas * 2 ); } #define TEAM_DEATH_TIMEOUT 120.0 #define TEAM_DEATH_AVOID_COUNT_SCALE 0.5 // so if there are 12 players, then if count reaches (12 * scale) then AVOID #define TEAM_DEATH_RANGE 512.0 /* =================== AAS_UpdateTeamDeath =================== */ void AAS_UpdateTeamDeath( void ) { int i, j, k; // check for areas which have timed out, so we can stop avoiding them // for each area for ( i = 0; i < aasworld->numareas; i++ ) { // for each team for ( j = 0; j < 2; j++ ) { k = ( aasworld->numareas * j ) + i; if ( aasworld->teamDeathTime[k] ) { if ( aasworld->teamDeathTime[k] < AAS_Time() - TEAM_DEATH_TIMEOUT ) { // this area has timed out aasworld->teamDeathAvoid[k] = 0; aasworld->teamDeathTime[k] = 0; if ( aasworld->teamDeathAvoid[k] ) { // unmark this area if ( j == 0 ) { aasworld->areasettings[i].areaflags &= ~AREA_AVOID_AXIS; } else { aasworld->areasettings[i].areaflags &= ~AREA_AVOID_ALLIES; } //remove all routing cache involving this area AAS_RemoveRoutingCacheUsingArea( i ); // recalculate the team flags that are used in this cluster AAS_ClearClusterTeamFlags( i ); } } } } } } /* =================== AAS_RecordTeamDeathArea =================== */ void AAS_RecordTeamDeathArea( vec3_t srcpos, int srcarea, int team, int teamCount, int travelflags ) { int i, nextareanum, badtravelflags, numreach, k; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; // int count=0; // return; // badtravelflags = ~travelflags; k = ( aasworld->numareas * team ) + srcarea; // curupdate = &aasworld->areaupdate[srcarea]; curupdate->areanum = srcarea; VectorCopy( srcpos, curupdate->start ); // if ( srcarea == 0 ) { return; } curupdate->areatraveltimes = aasworld->areatraveltimes[srcarea][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 current list, flip the lists 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 tihs area has already been done if ( aasworld->teamDeathTime[reach->areanum] >= AAS_Time() ) { continue; } aasworld->teamDeathTime[reach->areanum] = AAS_Time(); // //if an undesired travel type is used if ( aasworld->travelflagfortype[reach->traveltype] & badtravelflags ) { continue; } // if ( AAS_AreaContentsTravelFlag( reach->areanum ) & badtravelflags ) { continue; } //number of the area the reachability leads to nextareanum = reach->areanum; // // if it's too far from srcpos, ignore if ( Distance( curupdate->start, reach->end ) + (float)curupdate->tmptraveltime > TEAM_DEATH_RANGE ) { continue; } // k = reach->areanum; // mark this area aasworld->teamDeathCount[k]++; if ( aasworld->teamDeathCount[k] > 100 ) { aasworld->teamDeathCount[k] = 100; // make sure it doesnt loop around } // // see if this area is now to be avoided if ( !aasworld->teamDeathAvoid[k] ) { if ( aasworld->teamDeathCount[k] > (int)( TEAM_DEATH_AVOID_COUNT_SCALE * teamCount ) ) { // avoid this area aasworld->teamDeathAvoid[k] = 1; // mark this area if ( team == 0 ) { aasworld->areasettings[k].areaflags |= AREA_AVOID_AXIS; } else { aasworld->areasettings[k].areaflags |= AREA_AVOID_ALLIES; } //remove all routing cache involving this area AAS_RemoveRoutingCacheUsingArea( k ); // recalculate the team flags that are used in this cluster AAS_ClearClusterTeamFlags( k ); } } // nextupdate = &aasworld->areaupdate[nextareanum]; nextupdate->areanum = nextareanum; //remember where we entered this area VectorCopy( reach->end, nextupdate->start ); // calc the distance nextupdate->tmptraveltime = (float)curupdate->tmptraveltime + Distance( curupdate->start, reach->end ); //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 for } //end while // return; }