mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-11-25 13:21:08 +00:00
0d5fb492cd
Fix GCC 6 misleading-indentation warning add SECURITY.md OpenGL2: Restore adding fixed ambient light when HDR is enabled Few LCC memory fixes. fix a few potential buffer overwrite in Game VM Enable compiler optimization on all macOS architectures Don't allow qagame module to create "botlib.log" at ANY filesystem location Make FS_BuildOSPath for botlib.log consistent with typical usage tiny readme thing Remove extra plus sign from Huff_Compress() Fix VMs being able to change CVAR_PROTECTED cvars Don't register fs_game cvar everywhere just to get the value Don't let VMs change engine latch cvars immediately Fix fs_game '..' reading outside of home and base path Fix VMs forcing engine latch cvar to update to latched value Revert my recent cvar latch changes Revert "Don't let VMs change engine latch cvars immediately" Partially revert "Fix fs_game '..' reading outside of home and base path" Revert "Fix VMs forcing engine latch cvar to update to latched value" Fix exploit to bypass filename restrictions on Windows Changes to systemd q3a.service Fix Q_vsnprintf for mingw-w64 Fix timelimit causing an infinite map ending loop Fix invalid access to cluster 0 in AAS_AreaRouteToGoalArea() Fix negative frag/capturelimit causing an infinite map end loop OpenGL2: Fix dark lightmap on shader in mpteam6 Make FS_InvalidGameDir() consider subdirectories invalid [qcommon] Remove dead serialization code [qcommon] Make several zone variables and functions static. Fix MAC_OS_X_VERSION_MIN_REQUIRED for macOS 10.10 and later Increase q3_ui .arena filename list buffer size to 4096 bytes OpenGL2: Fix crash when BSP has deluxe maps and vertex lit surfaces Support Unicode characters greater than 0xFF in cl_consoleKeys Fix macOS app bundle with space in name OpenGL1: Use glGenTextures instead of hardcoded values Remove CON_FlushIn function and where STDIN needs flushing, use tcflush POSIX function Update libogg from 1.3.2 to 1.3.3 Rename (already updated) libogg-1.3.2 to libogg-1.3.3 Update libvorbis from 1.3.5 to 1.3.6 * Fix CVE-2018-5146 - out-of-bounds write on codebook decoding. * Fix CVE-2017-14632 - free() on unitialized data * Fix CVE-2017-14633 - out-of-bounds read Rename (already updated) libvorbis-1.3.5 to libvorbis-1.3.6 Update opus from 1.1.4 to 1.2.1 Rename (already updated) opus-1.1.4 to opus-1.2.1 Update opusfile from 0.8 to 0.9 Rename (already updated) opusfile-0.8 to opusfile-0.9 First swing at a CONTRIBUTING.md Allow loading system OpenAL library on macOS again Remove duplicate setting of FREETYPE_CFLAGS in Makefile Fix exploit to reset player by sending wrong serverId Fix "Going to CS_ZOMBIE for [clientname]" developer message Fix MSG_Read*String*() functions not being able to read last byte from message
2217 lines
73 KiB
C
2217 lines
73 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
* name: be_aas_route.c
|
|
*
|
|
* desc: AAS
|
|
*
|
|
* $Archive: /MissionPack/code/botlib/be_aas_route.c $
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "../qcommon/q_shared.h"
|
|
#include "l_utils.h"
|
|
#include "l_memory.h"
|
|
#include "l_log.h"
|
|
#include "l_crc.h"
|
|
#include "l_libvar.h"
|
|
#include "l_script.h"
|
|
#include "l_precomp.h"
|
|
#include "l_struct.h"
|
|
#include "aasfile.h"
|
|
#include "botlib.h"
|
|
#include "be_aas.h"
|
|
#include "be_aas_funcs.h"
|
|
#include "be_interface.h"
|
|
#include "be_aas_def.h"
|
|
|
|
#define ROUTING_DEBUG
|
|
|
|
//travel time in hundreths of a second = distance * 100 / speed
|
|
#define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100
|
|
#define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150
|
|
#define DISTANCEFACTOR_WALK 0.33f //walk speed = 300
|
|
|
|
//cache refresh time
|
|
#define CACHE_REFRESHTIME 15.0f //15 seconds refresh time
|
|
|
|
//maximum number of routing updates each frame
|
|
#define MAX_FRAMEROUTINGUPDATES 10
|
|
|
|
|
|
/*
|
|
|
|
area routing cache:
|
|
stores the distances within one cluster to a specific goal area
|
|
this goal area is in this same cluster and could be a cluster portal
|
|
for every cluster there's a list with routing cache for every area
|
|
in that cluster (including the portals of that cluster)
|
|
area cache stores aasworld.clusters[?].numreachabilityareas travel times
|
|
|
|
portal routing cache:
|
|
stores the distances of all portals to a specific goal area
|
|
this goal area could be in any cluster and could also be a cluster portal
|
|
for every area (aasworld.numareas) the portal cache stores
|
|
aasworld.numportals travel times
|
|
|
|
*/
|
|
|
|
#ifdef ROUTING_DEBUG
|
|
int numareacacheupdates;
|
|
int numportalcacheupdates;
|
|
#endif //ROUTING_DEBUG
|
|
|
|
int routingcachesize;
|
|
int max_routingcachesize;
|
|
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
#ifdef ROUTING_DEBUG
|
|
void AAS_RoutingInfo(void)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates);
|
|
botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates);
|
|
botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize);
|
|
} //end of the function AAS_RoutingInfo
|
|
#endif //ROUTING_DEBUG
|
|
//===========================================================================
|
|
// returns the number of the area in the cluster
|
|
// assumes the given area is in the given cluster or a portal of the cluster
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
static ID_INLINE int AAS_ClusterAreaNum(int cluster, int areanum)
|
|
{
|
|
int side, areacluster;
|
|
|
|
areacluster = aasworld.areasettings[areanum].cluster;
|
|
if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum;
|
|
else
|
|
{
|
|
/*#ifdef ROUTING_DEBUG
|
|
if (aasworld.portals[-areacluster].frontcluster != cluster &&
|
|
aasworld.portals[-areacluster].backcluster != cluster)
|
|
{
|
|
botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n"
|
|
, -areacluster, cluster);
|
|
} //end if
|
|
#endif //ROUTING_DEBUG*/
|
|
side = aasworld.portals[-areacluster].frontcluster != cluster;
|
|
return aasworld.portals[-areacluster].clusterareanum[side];
|
|
} //end else
|
|
} //end of the function AAS_ClusterAreaNum
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_InitTravelFlagFromType(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_TRAVELTYPES; i++)
|
|
{
|
|
aasworld.travelflagfortype[i] = TFL_INVALID;
|
|
} //end for
|
|
aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID;
|
|
aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK;
|
|
aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH;
|
|
aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP;
|
|
aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER;
|
|
aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE;
|
|
aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM;
|
|
aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT;
|
|
aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR;
|
|
aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK;
|
|
aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP;
|
|
aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD;
|
|
aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB;
|
|
} //end of the function AAS_InitTravelFlagFromType
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
static ID_INLINE int AAS_TravelFlagForType_inline(int traveltype)
|
|
{
|
|
int tfl = 0;
|
|
|
|
if (traveltype & TRAVELFLAG_NOTTEAM1)
|
|
tfl |= TFL_NOTTEAM1;
|
|
if (traveltype & TRAVELFLAG_NOTTEAM2)
|
|
tfl |= TFL_NOTTEAM2;
|
|
traveltype &= TRAVELTYPE_MASK;
|
|
if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES)
|
|
return TFL_INVALID;
|
|
tfl |= aasworld.travelflagfortype[traveltype];
|
|
return tfl;
|
|
} //end of the function AAS_TravelFlagForType_inline
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_TravelFlagForType(int traveltype)
|
|
{
|
|
return AAS_TravelFlagForType_inline(traveltype);
|
|
} //end of the function AAS_TravelFlagForType_inline
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_UnlinkCache(aas_routingcache_t *cache)
|
|
{
|
|
if (cache->time_next) cache->time_next->time_prev = cache->time_prev;
|
|
else aasworld.newestcache = cache->time_prev;
|
|
if (cache->time_prev) cache->time_prev->time_next = cache->time_next;
|
|
else aasworld.oldestcache = cache->time_next;
|
|
cache->time_next = NULL;
|
|
cache->time_prev = NULL;
|
|
} //end of the function AAS_UnlinkCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_LinkCache(aas_routingcache_t *cache)
|
|
{
|
|
if (aasworld.newestcache)
|
|
{
|
|
aasworld.newestcache->time_next = cache;
|
|
cache->time_prev = aasworld.newestcache;
|
|
} //end if
|
|
else
|
|
{
|
|
aasworld.oldestcache = cache;
|
|
cache->time_prev = NULL;
|
|
} //end else
|
|
cache->time_next = NULL;
|
|
aasworld.newestcache = cache;
|
|
} //end of the function AAS_LinkCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_FreeRoutingCache(aas_routingcache_t *cache)
|
|
{
|
|
AAS_UnlinkCache(cache);
|
|
routingcachesize -= cache->size;
|
|
FreeMemory(cache);
|
|
} //end of the function AAS_FreeRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
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
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
//refresh portal cache
|
|
for (cache = aasworld.portalcache[i]; cache; cache = nextcache)
|
|
{
|
|
nextcache = cache->next;
|
|
AAS_FreeRoutingCache(cache);
|
|
} //end for
|
|
aasworld.portalcache[i] = NULL;
|
|
} //end for
|
|
} //end of the function AAS_RemoveRoutingCacheUsingArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_EnableRoutingArea(int areanum, int enable)
|
|
{
|
|
int flags;
|
|
|
|
if (areanum <= 0 || areanum >= aasworld.numareas)
|
|
{
|
|
if (botDeveloper)
|
|
{
|
|
botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum);
|
|
} //end if
|
|
return 0;
|
|
} //end if
|
|
flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED;
|
|
if (enable < 0)
|
|
return !flags;
|
|
|
|
if (enable)
|
|
aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED;
|
|
else
|
|
aasworld.areasettings[areanum].areaflags |= AREA_DISABLED;
|
|
// if the status of the area changed
|
|
if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) )
|
|
{
|
|
//remove all routing cache involving this area
|
|
AAS_RemoveRoutingCacheUsingArea( areanum );
|
|
} //end if
|
|
return !flags;
|
|
} //end of the function AAS_EnableRoutingArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
static ID_INLINE float AAS_RoutingTime(void)
|
|
{
|
|
return AAS_Time();
|
|
} //end of the function AAS_RoutingTime
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_GetAreaContentsTravelFlags(int areanum)
|
|
{
|
|
int contents, tfl;
|
|
|
|
contents = aasworld.areasettings[areanum].contents;
|
|
tfl = 0;
|
|
if (contents & AREACONTENTS_WATER)
|
|
tfl |= TFL_WATER;
|
|
else if (contents & AREACONTENTS_SLIME)
|
|
tfl |= TFL_SLIME;
|
|
else if (contents & AREACONTENTS_LAVA)
|
|
tfl |= TFL_LAVA;
|
|
else
|
|
tfl |= TFL_AIR;
|
|
if (contents & AREACONTENTS_DONOTENTER)
|
|
tfl |= TFL_DONOTENTER;
|
|
if (contents & AREACONTENTS_NOTTEAM1)
|
|
tfl |= TFL_NOTTEAM1;
|
|
if (contents & AREACONTENTS_NOTTEAM2)
|
|
tfl |= TFL_NOTTEAM2;
|
|
if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE)
|
|
tfl |= TFL_BRIDGE;
|
|
return tfl;
|
|
} //end of the function AAS_GetAreaContentsTravelFlags
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
static ID_INLINE int AAS_AreaContentsTravelFlags_inline(int areanum)
|
|
{
|
|
return aasworld.areacontentstravelflags[areanum];
|
|
} //end of the function AAS_AreaContentsTravelFlags
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_AreaContentsTravelFlags(int areanum)
|
|
{
|
|
return aasworld.areacontentstravelflags[areanum];
|
|
} //end of the function AAS_AreaContentsTravelFlags
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_InitAreaContentsTravelFlags(void)
|
|
{
|
|
int i;
|
|
|
|
if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);
|
|
aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int));
|
|
//
|
|
for (i = 0; i < aasworld.numareas; i++) {
|
|
aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i);
|
|
}
|
|
} //end of the function AAS_InitAreaContentsTravelFlags
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_CreateReversedReachability(void)
|
|
{
|
|
int i, n;
|
|
aas_reversedlink_t *revlink;
|
|
aas_reachability_t *reach;
|
|
aas_areasettings_t *settings;
|
|
char *ptr;
|
|
#ifdef DEBUG
|
|
int starttime;
|
|
|
|
starttime = Sys_MilliSeconds();
|
|
#endif
|
|
//free reversed links that have already been created
|
|
if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);
|
|
//allocate memory for the reversed reachability links
|
|
ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) +
|
|
aasworld.reachabilitysize * sizeof(aas_reversedlink_t));
|
|
//
|
|
aasworld.reversedreachability = (aas_reversedreachability_t *) ptr;
|
|
//pointer to the memory for the reversed links
|
|
ptr += aasworld.numareas * sizeof(aas_reversedreachability_t);
|
|
//check all reachabilities of all areas
|
|
for (i = 1; i < aasworld.numareas; i++)
|
|
{
|
|
//settings of the area
|
|
settings = &aasworld.areasettings[i];
|
|
//
|
|
if (settings->numreachableareas >= 128)
|
|
botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i);
|
|
//create reversed links for the reachabilities
|
|
for (n = 0; n < settings->numreachableareas && n < 128; n++)
|
|
{
|
|
//reachability link
|
|
reach = &aasworld.reachability[settings->firstreachablearea + n];
|
|
//
|
|
revlink = (aas_reversedlink_t *) ptr;
|
|
ptr += sizeof(aas_reversedlink_t);
|
|
//
|
|
revlink->areanum = i;
|
|
revlink->linknum = settings->firstreachablearea + n;
|
|
revlink->next = aasworld.reversedreachability[reach->areanum].first;
|
|
aasworld.reversedreachability[reach->areanum].first = revlink;
|
|
aasworld.reversedreachability[reach->areanum].numlinks++;
|
|
} //end for
|
|
} //end for
|
|
#ifdef DEBUG
|
|
botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime);
|
|
#endif
|
|
} //end of the function AAS_CreateReversedReachability
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end)
|
|
{
|
|
int intdist;
|
|
float dist;
|
|
vec3_t dir;
|
|
|
|
VectorSubtract(start, end, dir);
|
|
dist = VectorLength(dir);
|
|
//if crouch only area
|
|
if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH;
|
|
//if swim area
|
|
else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM;
|
|
//normal walk area
|
|
else dist *= DISTANCEFACTOR_WALK;
|
|
//
|
|
intdist = (int) dist;
|
|
//make sure the distance isn't zero
|
|
if (intdist <= 0) intdist = 1;
|
|
return intdist;
|
|
} //end of the function AAS_AreaTravelTime
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
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;
|
|
#ifdef DEBUG
|
|
int starttime;
|
|
|
|
starttime = Sys_MilliSeconds();
|
|
#endif
|
|
//if there are still area travel times, free the memory
|
|
if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);
|
|
//get the total size of all the area travel times
|
|
size = aasworld.numareas * sizeof(unsigned short **);
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
revreach = &aasworld.reversedreachability[i];
|
|
//settings of the area
|
|
settings = &aasworld.areasettings[i];
|
|
//
|
|
size += settings->numreachableareas * sizeof(unsigned short *);
|
|
//
|
|
size += settings->numreachableareas *
|
|
PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short);
|
|
} //end for
|
|
//allocate memory for the area travel times
|
|
ptr = (char *) GetClearedMemory(size);
|
|
aasworld.areatraveltimes = (unsigned short ***) ptr;
|
|
ptr += aasworld.numareas * sizeof(unsigned short **);
|
|
//calcluate the travel times for all the areas
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
//reversed reachabilities of this area
|
|
revreach = &aasworld.reversedreachability[i];
|
|
//settings of the area
|
|
settings = &aasworld.areasettings[i];
|
|
//
|
|
aasworld.areatraveltimes[i] = (unsigned short **) ptr;
|
|
ptr += settings->numreachableareas * sizeof(unsigned short *);
|
|
//
|
|
for (l = 0; l < settings->numreachableareas; l++)
|
|
{
|
|
aasworld.areatraveltimes[i][l] = (unsigned short *) ptr;
|
|
ptr += PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short);
|
|
//reachability link
|
|
reach = &aasworld.reachability[settings->firstreachablearea + l];
|
|
//
|
|
for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)
|
|
{
|
|
VectorCopy(aasworld.reachability[revlink->linknum].end, end);
|
|
//
|
|
aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start);
|
|
} //end for
|
|
} //end for
|
|
} //end for
|
|
#ifdef DEBUG
|
|
botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime);
|
|
#endif
|
|
} //end of the function AAS_CalculateAreaTravelTimes
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
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) FreeMemory(aasworld.portalmaxtraveltimes);
|
|
|
|
aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int));
|
|
|
|
for (i = 0; i < aasworld.numportals; i++)
|
|
{
|
|
aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i);
|
|
//botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]);
|
|
} //end for
|
|
} //end of the function AAS_InitPortalMaxTravelTimes
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
/*
|
|
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: -
|
|
//===========================================================================
|
|
int AAS_FreeOldestCache(void)
|
|
{
|
|
int clusterareanum;
|
|
aas_routingcache_t *cache;
|
|
|
|
for (cache = aasworld.oldestcache; cache; cache = cache->time_next) {
|
|
// never free area cache leading towards a portal
|
|
if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (cache) {
|
|
// unlink the cache
|
|
if (cache->type == CACHETYPE_AREA) {
|
|
//number of the area in the cluster
|
|
clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);
|
|
// unlink from cluster area cache
|
|
if (cache->prev) cache->prev->next = cache->next;
|
|
else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next;
|
|
if (cache->next) cache->next->prev = cache->prev;
|
|
}
|
|
else {
|
|
// unlink from portal cache
|
|
if (cache->prev) cache->prev->next = cache->next;
|
|
else aasworld.portalcache[cache->areanum] = cache->next;
|
|
if (cache->next) cache->next->prev = cache->prev;
|
|
}
|
|
AAS_FreeRoutingCache(cache);
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
} //end of the function AAS_FreeOldestCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes)
|
|
{
|
|
aas_routingcache_t *cache;
|
|
int size;
|
|
|
|
//
|
|
size = sizeof(aas_routingcache_t)
|
|
+ numtraveltimes * sizeof(unsigned short int)
|
|
+ numtraveltimes * sizeof(unsigned char);
|
|
//
|
|
routingcachesize += size;
|
|
//
|
|
cache = (aas_routingcache_t *) GetClearedMemory(size);
|
|
cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t)
|
|
+ numtraveltimes * sizeof(unsigned short int);
|
|
cache->size = size;
|
|
return cache;
|
|
} //end of the function AAS_AllocRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_FreeAllClusterAreaCache(void)
|
|
{
|
|
int i, j;
|
|
aas_routingcache_t *cache, *nextcache;
|
|
aas_cluster_t *cluster;
|
|
|
|
//free all cluster cache if existing
|
|
if (!aasworld.clusterareacache) return;
|
|
//free caches
|
|
for (i = 0; i < aasworld.numclusters; i++)
|
|
{
|
|
cluster = &aasworld.clusters[i];
|
|
for (j = 0; j < cluster->numareas; j++)
|
|
{
|
|
for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache)
|
|
{
|
|
nextcache = cache->next;
|
|
AAS_FreeRoutingCache(cache);
|
|
} //end for
|
|
aasworld.clusterareacache[i][j] = NULL;
|
|
} //end for
|
|
} //end for
|
|
//free the cluster cache array
|
|
FreeMemory(aasworld.clusterareacache);
|
|
aasworld.clusterareacache = NULL;
|
|
} //end of the function AAS_FreeAllClusterAreaCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_InitClusterAreaCache(void)
|
|
{
|
|
int i, size;
|
|
char *ptr;
|
|
|
|
//
|
|
for (size = 0, i = 0; i < aasworld.numclusters; i++)
|
|
{
|
|
size += aasworld.clusters[i].numareas;
|
|
} //end for
|
|
//two dimensional array with pointers for every cluster to routing cache
|
|
//for every area in that cluster
|
|
ptr = (char *) GetClearedMemory(
|
|
aasworld.numclusters * sizeof(aas_routingcache_t **) +
|
|
size * sizeof(aas_routingcache_t *));
|
|
aasworld.clusterareacache = (aas_routingcache_t ***) ptr;
|
|
ptr += aasworld.numclusters * sizeof(aas_routingcache_t **);
|
|
for (i = 0; i < aasworld.numclusters; i++)
|
|
{
|
|
aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr;
|
|
ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *);
|
|
} //end for
|
|
} //end of the function AAS_InitClusterAreaCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_FreeAllPortalCache(void)
|
|
{
|
|
int i;
|
|
aas_routingcache_t *cache, *nextcache;
|
|
|
|
//free all portal cache if existing
|
|
if (!aasworld.portalcache) return;
|
|
//free portal caches
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
for (cache = aasworld.portalcache[i]; cache; cache = nextcache)
|
|
{
|
|
nextcache = cache->next;
|
|
AAS_FreeRoutingCache(cache);
|
|
} //end for
|
|
aasworld.portalcache[i] = NULL;
|
|
} //end for
|
|
FreeMemory(aasworld.portalcache);
|
|
aasworld.portalcache = NULL;
|
|
} //end of the function AAS_FreeAllPortalCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_InitPortalCache(void)
|
|
{
|
|
//
|
|
aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory(
|
|
aasworld.numareas * sizeof(aas_routingcache_t *));
|
|
} //end of the function AAS_InitPortalCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_InitRoutingUpdate(void)
|
|
{
|
|
int i, maxreachabilityareas;
|
|
|
|
//free routing update fields if already existing
|
|
if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);
|
|
//
|
|
maxreachabilityareas = 0;
|
|
for (i = 0; i < aasworld.numclusters; i++)
|
|
{
|
|
if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas)
|
|
{
|
|
maxreachabilityareas = aasworld.clusters[i].numreachabilityareas;
|
|
} //end if
|
|
} //end for
|
|
//allocate memory for the routing update fields
|
|
aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory(
|
|
maxreachabilityareas * sizeof(aas_routingupdate_t));
|
|
//
|
|
if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);
|
|
//allocate memory for the portal update fields
|
|
aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory(
|
|
(aasworld.numportals+1) * sizeof(aas_routingupdate_t));
|
|
} //end of the function AAS_InitRoutingUpdate
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_CreateAllRoutingCache(void)
|
|
{
|
|
int i, j;
|
|
//int t;
|
|
|
|
aasworld.initialized = qtrue;
|
|
botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n");
|
|
for (i = 1; i < aasworld.numareas; i++)
|
|
{
|
|
if (!AAS_AreaReachability(i)) continue;
|
|
for (j = 1; j < aasworld.numareas; j++)
|
|
{
|
|
if (i == j) continue;
|
|
if (!AAS_AreaReachability(j)) continue;
|
|
AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT);
|
|
//t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT);
|
|
//Log_Write("traveltime from %d to %d is %d", i, j, t);
|
|
} //end for
|
|
} //end for
|
|
aasworld.initialized = qfalse;
|
|
} //end of the function AAS_CreateAllRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
|
|
//the route cache header
|
|
//this header is followed by numportalcache + numareacache aas_routingcache_t
|
|
//structures that store routing cache
|
|
typedef struct routecacheheader_s
|
|
{
|
|
int ident;
|
|
int version;
|
|
int numareas;
|
|
int numclusters;
|
|
int areacrc;
|
|
int clustercrc;
|
|
int numportalcache;
|
|
int numareacache;
|
|
} routecacheheader_t;
|
|
|
|
#define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M')
|
|
#define RCVERSION 2
|
|
|
|
//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed);
|
|
//int AAS_CompressVis(byte *vis, int numareas, byte *dest);
|
|
|
|
void AAS_WriteRouteCache(void)
|
|
{
|
|
int i, j, numportalcache, numareacache, totalsize;
|
|
aas_routingcache_t *cache;
|
|
aas_cluster_t *cluster;
|
|
fileHandle_t fp;
|
|
char filename[MAX_QPATH];
|
|
routecacheheader_t routecacheheader;
|
|
|
|
numportalcache = 0;
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
|
|
{
|
|
numportalcache++;
|
|
} //end for
|
|
} //end for
|
|
numareacache = 0;
|
|
for (i = 0; i < aasworld.numclusters; i++)
|
|
{
|
|
cluster = &aasworld.clusters[i];
|
|
for (j = 0; j < cluster->numareas; j++)
|
|
{
|
|
for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
|
|
{
|
|
numareacache++;
|
|
} //end for
|
|
} //end for
|
|
} //end for
|
|
// open the file for writing
|
|
Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname);
|
|
botimport.FS_FOpenFile( filename, &fp, FS_WRITE );
|
|
if (!fp)
|
|
{
|
|
AAS_Error("Unable to open file: %s\n", filename);
|
|
return;
|
|
} //end if
|
|
//create the header
|
|
routecacheheader.ident = RCID;
|
|
routecacheheader.version = RCVERSION;
|
|
routecacheheader.numareas = aasworld.numareas;
|
|
routecacheheader.numclusters = aasworld.numclusters;
|
|
routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas );
|
|
routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters );
|
|
routecacheheader.numportalcache = numportalcache;
|
|
routecacheheader.numareacache = numareacache;
|
|
//write the header
|
|
botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp);
|
|
//
|
|
totalsize = 0;
|
|
//write all the cache
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
|
|
{
|
|
botimport.FS_Write(cache, cache->size, fp);
|
|
totalsize += cache->size;
|
|
} //end for
|
|
} //end for
|
|
for (i = 0; i < aasworld.numclusters; i++)
|
|
{
|
|
cluster = &aasworld.clusters[i];
|
|
for (j = 0; j < cluster->numareas; j++)
|
|
{
|
|
for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
|
|
{
|
|
botimport.FS_Write(cache, cache->size, fp);
|
|
totalsize += cache->size;
|
|
} //end for
|
|
} //end for
|
|
} //end for
|
|
// write the visareas
|
|
/*
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
if (!aasworld.areavisibility[i]) {
|
|
size = 0;
|
|
botimport.FS_Write(&size, sizeof(int), fp);
|
|
continue;
|
|
}
|
|
AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis );
|
|
size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis );
|
|
botimport.FS_Write(&size, sizeof(int), fp);
|
|
botimport.FS_Write(aasworld.decompressedvis, size, fp);
|
|
}
|
|
*/
|
|
//
|
|
botimport.FS_FCloseFile(fp);
|
|
botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename);
|
|
botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize);
|
|
} //end of the function AAS_WriteRouteCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
aas_routingcache_t *AAS_ReadCache(fileHandle_t fp)
|
|
{
|
|
int size;
|
|
aas_routingcache_t *cache;
|
|
|
|
botimport.FS_Read(&size, sizeof(size), fp);
|
|
cache = (aas_routingcache_t *) GetMemory(size);
|
|
cache->size = size;
|
|
botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp);
|
|
cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) +
|
|
(size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2;
|
|
return cache;
|
|
} //end of the function AAS_ReadCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_ReadRouteCache(void)
|
|
{
|
|
int i, clusterareanum;//, size;
|
|
fileHandle_t fp;
|
|
char filename[MAX_QPATH];
|
|
routecacheheader_t routecacheheader;
|
|
aas_routingcache_t *cache;
|
|
|
|
Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname);
|
|
botimport.FS_FOpenFile( filename, &fp, FS_READ );
|
|
if (!fp)
|
|
{
|
|
return qfalse;
|
|
} //end if
|
|
botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp );
|
|
if (routecacheheader.ident != RCID)
|
|
{
|
|
AAS_Error("%s is not a route cache dump\n", filename);
|
|
return qfalse;
|
|
} //end if
|
|
if (routecacheheader.version != RCVERSION)
|
|
{
|
|
AAS_Error("route cache dump has wrong version %d, should be %d\n", routecacheheader.version, RCVERSION);
|
|
return qfalse;
|
|
} //end if
|
|
if (routecacheheader.numareas != aasworld.numareas)
|
|
{
|
|
//AAS_Error("route cache dump has wrong number of areas\n");
|
|
return qfalse;
|
|
} //end if
|
|
if (routecacheheader.numclusters != aasworld.numclusters)
|
|
{
|
|
//AAS_Error("route cache dump has wrong number of clusters\n");
|
|
return qfalse;
|
|
} //end if
|
|
if (routecacheheader.areacrc !=
|
|
CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ))
|
|
{
|
|
//AAS_Error("route cache dump area CRC incorrect\n");
|
|
return qfalse;
|
|
} //end if
|
|
if (routecacheheader.clustercrc !=
|
|
CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ))
|
|
{
|
|
//AAS_Error("route cache dump cluster CRC incorrect\n");
|
|
return qfalse;
|
|
} //end if
|
|
//read all the portal cache
|
|
for (i = 0; i < routecacheheader.numportalcache; i++)
|
|
{
|
|
cache = AAS_ReadCache(fp);
|
|
cache->next = aasworld.portalcache[cache->areanum];
|
|
cache->prev = NULL;
|
|
if (aasworld.portalcache[cache->areanum])
|
|
aasworld.portalcache[cache->areanum]->prev = cache;
|
|
aasworld.portalcache[cache->areanum] = cache;
|
|
} //end for
|
|
//read all the cluster area cache
|
|
for (i = 0; i < routecacheheader.numareacache; i++)
|
|
{
|
|
cache = AAS_ReadCache(fp);
|
|
clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);
|
|
cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum];
|
|
cache->prev = NULL;
|
|
if (aasworld.clusterareacache[cache->cluster][clusterareanum])
|
|
aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache;
|
|
aasworld.clusterareacache[cache->cluster][clusterareanum] = cache;
|
|
} //end for
|
|
// read the visareas
|
|
/*
|
|
aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *));
|
|
aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte));
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
botimport.FS_Read(&size, sizeof(size), fp );
|
|
if (size) {
|
|
aasworld.areavisibility[i] = (byte *) GetMemory(size);
|
|
botimport.FS_Read(aasworld.areavisibility[i], size, fp );
|
|
}
|
|
}
|
|
*/
|
|
//
|
|
botimport.FS_FCloseFile(fp);
|
|
return qtrue;
|
|
} //end of the function AAS_ReadRouteCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
#define MAX_REACHABILITYPASSAREAS 32
|
|
|
|
void AAS_InitReachabilityAreas(void)
|
|
{
|
|
int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS];
|
|
int numreachareas;
|
|
aas_reachability_t *reach;
|
|
vec3_t start, end;
|
|
|
|
if (aasworld.reachabilityareas)
|
|
FreeMemory(aasworld.reachabilityareas);
|
|
if (aasworld.reachabilityareaindex)
|
|
FreeMemory(aasworld.reachabilityareaindex);
|
|
|
|
aasworld.reachabilityareas = (aas_reachabilityareas_t *)
|
|
GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t));
|
|
aasworld.reachabilityareaindex = (int *)
|
|
GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int));
|
|
numreachareas = 0;
|
|
for (i = 0; i < aasworld.reachabilitysize; i++)
|
|
{
|
|
reach = &aasworld.reachability[i];
|
|
numareas = 0;
|
|
switch(reach->traveltype & TRAVELTYPE_MASK)
|
|
{
|
|
//trace areas from start to end
|
|
case TRAVEL_BARRIERJUMP:
|
|
case TRAVEL_WATERJUMP:
|
|
VectorCopy(reach->start, end);
|
|
end[2] = reach->end[2];
|
|
numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS);
|
|
break;
|
|
case TRAVEL_WALKOFFLEDGE:
|
|
VectorCopy(reach->end, start);
|
|
start[2] = reach->start[2];
|
|
numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);
|
|
break;
|
|
case TRAVEL_GRAPPLEHOOK:
|
|
numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);
|
|
break;
|
|
|
|
//trace arch
|
|
case TRAVEL_JUMP: break;
|
|
case TRAVEL_ROCKETJUMP: break;
|
|
case TRAVEL_BFGJUMP: break;
|
|
case TRAVEL_JUMPPAD: break;
|
|
|
|
//trace from reach->start to entity center, along entity movement
|
|
//and from entity center to reach->end
|
|
case TRAVEL_ELEVATOR: break;
|
|
case TRAVEL_FUNCBOB: break;
|
|
|
|
//no areas in between
|
|
case TRAVEL_WALK: break;
|
|
case TRAVEL_CROUCH: break;
|
|
case TRAVEL_LADDER: break;
|
|
case TRAVEL_SWIM: break;
|
|
case TRAVEL_TELEPORT: break;
|
|
default: break;
|
|
} //end switch
|
|
aasworld.reachabilityareas[i].firstarea = numreachareas;
|
|
aasworld.reachabilityareas[i].numareas = numareas;
|
|
for (j = 0; j < numareas; j++)
|
|
{
|
|
aasworld.reachabilityareaindex[numreachareas++] = areas[j];
|
|
} //end for
|
|
} //end for
|
|
} //end of the function AAS_InitReachabilityAreas
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_InitRouting(void)
|
|
{
|
|
AAS_InitTravelFlagFromType();
|
|
//
|
|
AAS_InitAreaContentsTravelFlags();
|
|
//initialize the routing update fields
|
|
AAS_InitRoutingUpdate();
|
|
//create reversed reachability links used by the routing update algorithm
|
|
AAS_CreateReversedReachability();
|
|
//initialize the cluster cache
|
|
AAS_InitClusterAreaCache();
|
|
//initialize portal cache
|
|
AAS_InitPortalCache();
|
|
//initialize the area travel times
|
|
AAS_CalculateAreaTravelTimes();
|
|
//calculate the maximum travel times through portals
|
|
AAS_InitPortalMaxTravelTimes();
|
|
//get the areas reachabilities go through
|
|
AAS_InitReachabilityAreas();
|
|
//
|
|
#ifdef ROUTING_DEBUG
|
|
numareacacheupdates = 0;
|
|
numportalcacheupdates = 0;
|
|
#endif //ROUTING_DEBUG
|
|
//
|
|
routingcachesize = 0;
|
|
max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096");
|
|
// read any routing cache if available
|
|
AAS_ReadRouteCache();
|
|
} //end of the function AAS_InitRouting
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_FreeRoutingCaches(void)
|
|
{
|
|
// free all the existing cluster area cache
|
|
AAS_FreeAllClusterAreaCache();
|
|
// free all the existing portal cache
|
|
AAS_FreeAllPortalCache();
|
|
// free cached travel times within areas
|
|
if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);
|
|
aasworld.areatraveltimes = NULL;
|
|
// free cached maximum travel time through cluster portals
|
|
if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);
|
|
aasworld.portalmaxtraveltimes = NULL;
|
|
// free reversed reachability links
|
|
if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);
|
|
aasworld.reversedreachability = NULL;
|
|
// free routing algorithm memory
|
|
if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);
|
|
aasworld.areaupdate = NULL;
|
|
if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);
|
|
aasworld.portalupdate = NULL;
|
|
// free lists with areas the reachabilities go through
|
|
if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas);
|
|
aasworld.reachabilityareas = NULL;
|
|
// free the reachability area index
|
|
if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex);
|
|
aasworld.reachabilityareaindex = NULL;
|
|
// free area contents travel flags look up table
|
|
if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);
|
|
aasworld.areacontentstravelflags = NULL;
|
|
} //end of the function AAS_FreeRoutingCaches
|
|
//===========================================================================
|
|
// update the given routing cache
|
|
//
|
|
// Parameter: areacache : routing cache to update
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache)
|
|
{
|
|
int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum;
|
|
int numreachabilityareas;
|
|
unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed
|
|
aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
|
|
aas_reachability_t *reach;
|
|
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;
|
|
//
|
|
aasworld.frameroutingupdates++;
|
|
//clear the routing update fields
|
|
// Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t));
|
|
//
|
|
badtravelflags = ~areacache->travelflags;
|
|
//
|
|
clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum);
|
|
if (clusterareanum >= numreachabilityareas) return;
|
|
//
|
|
Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes));
|
|
//
|
|
curupdate = &aasworld.areaupdate[clusterareanum];
|
|
curupdate->areanum = areacache->areanum;
|
|
//VectorCopy(areacache->origin, curupdate->start);
|
|
curupdate->areatraveltimes = startareatraveltimes;
|
|
curupdate->tmptraveltime = areacache->starttraveltime;
|
|
//
|
|
areacache->traveltimes[clusterareanum] = areacache->starttraveltime;
|
|
//put the area to start with in the current read list
|
|
curupdate->next = NULL;
|
|
curupdate->prev = NULL;
|
|
updateliststart = curupdate;
|
|
updatelistend = curupdate;
|
|
//while there are updates in the current list
|
|
while (updateliststart)
|
|
{
|
|
curupdate = updateliststart;
|
|
//
|
|
if (curupdate->next) curupdate->next->prev = NULL;
|
|
else updatelistend = NULL;
|
|
updateliststart = curupdate->next;
|
|
//
|
|
curupdate->inlist = qfalse;
|
|
//check all reversed reachability links
|
|
revreach = &aasworld.reversedreachability[curupdate->areanum];
|
|
//
|
|
for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++)
|
|
{
|
|
linknum = revlink->linknum;
|
|
reach = &aasworld.reachability[linknum];
|
|
//if there is used an undesired travel type
|
|
if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;
|
|
//if not allowed to enter the next area
|
|
if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue;
|
|
//if the next area has a not allowed travel flag
|
|
if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;
|
|
//number of the area the reversed reachability leads to
|
|
nextareanum = revlink->areanum;
|
|
//get the cluster number of the area
|
|
cluster = aasworld.areasettings[nextareanum].cluster;
|
|
//don't leave the cluster
|
|
if (cluster > 0 && cluster != areacache->cluster) continue;
|
|
//get the number of the area in the cluster
|
|
clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum);
|
|
if (clusterareanum >= numreachabilityareas) continue;
|
|
//time already travelled plus the traveltime through
|
|
//the current area plus the travel time from the reachability
|
|
t = curupdate->tmptraveltime +
|
|
//AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) +
|
|
curupdate->areatraveltimes[i] +
|
|
reach->traveltime;
|
|
//
|
|
if (!areacache->traveltimes[clusterareanum] ||
|
|
areacache->traveltimes[clusterareanum] > t)
|
|
{
|
|
areacache->traveltimes[clusterareanum] = t;
|
|
areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea;
|
|
nextupdate = &aasworld.areaupdate[clusterareanum];
|
|
nextupdate->areanum = nextareanum;
|
|
nextupdate->tmptraveltime = t;
|
|
//VectorCopy(reach->start, nextupdate->start);
|
|
nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum -
|
|
aasworld.areasettings[nextareanum].firstreachablearea];
|
|
if (!nextupdate->inlist)
|
|
{
|
|
// we add the update to the end of the list
|
|
// we could also use a B+ tree to have a real sorted list
|
|
// on travel time which makes for faster routing updates
|
|
nextupdate->next = NULL;
|
|
nextupdate->prev = updatelistend;
|
|
if (updatelistend) updatelistend->next = nextupdate;
|
|
else updateliststart = nextupdate;
|
|
updatelistend = nextupdate;
|
|
nextupdate->inlist = qtrue;
|
|
} //end if
|
|
} //end if
|
|
} //end for
|
|
} //end while
|
|
} //end of the function AAS_UpdateAreaRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags)
|
|
{
|
|
int clusterareanum;
|
|
aas_routingcache_t *cache, *clustercache;
|
|
|
|
//number of the area in the cluster
|
|
clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
|
|
//pointer to the cache for the area in the cluster
|
|
clustercache = aasworld.clusterareacache[clusternum][clusterareanum];
|
|
//find the cache without undesired travel flags
|
|
for (cache = clustercache; cache; cache = cache->next)
|
|
{
|
|
//if there aren't used any undesired travel types for the cache
|
|
if (cache->travelflags == travelflags) break;
|
|
} //end for
|
|
//if there was no cache
|
|
if (!cache)
|
|
{
|
|
cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas);
|
|
cache->cluster = clusternum;
|
|
cache->areanum = areanum;
|
|
VectorCopy(aasworld.areas[areanum].center, cache->origin);
|
|
cache->starttraveltime = 1;
|
|
cache->travelflags = travelflags;
|
|
cache->prev = NULL;
|
|
cache->next = clustercache;
|
|
if (clustercache) clustercache->prev = cache;
|
|
aasworld.clusterareacache[clusternum][clusterareanum] = cache;
|
|
AAS_UpdateAreaRoutingCache(cache);
|
|
} //end if
|
|
else
|
|
{
|
|
AAS_UnlinkCache(cache);
|
|
} //end else
|
|
//the cache has been accessed
|
|
cache->time = AAS_RoutingTime();
|
|
cache->type = CACHETYPE_AREA;
|
|
AAS_LinkCache(cache);
|
|
return cache;
|
|
} //end of the function AAS_GetAreaRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache)
|
|
{
|
|
int i, portalnum, clusterareanum, clusternum;
|
|
unsigned short int t;
|
|
aas_portal_t *portal;
|
|
aas_cluster_t *cluster;
|
|
aas_routingcache_t *cache;
|
|
aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
|
|
|
|
#ifdef ROUTING_DEBUG
|
|
numportalcacheupdates++;
|
|
#endif //ROUTING_DEBUG
|
|
//clear the routing update fields
|
|
// Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t));
|
|
//
|
|
curupdate = &aasworld.portalupdate[aasworld.numportals];
|
|
curupdate->cluster = portalcache->cluster;
|
|
curupdate->areanum = portalcache->areanum;
|
|
curupdate->tmptraveltime = portalcache->starttraveltime;
|
|
//if the start area is a cluster portal, store the travel time for that portal
|
|
clusternum = aasworld.areasettings[portalcache->areanum].cluster;
|
|
if (clusternum < 0)
|
|
{
|
|
portalcache->traveltimes[-clusternum] = portalcache->starttraveltime;
|
|
} //end if
|
|
//put the area to start with in the current read list
|
|
curupdate->next = NULL;
|
|
curupdate->prev = NULL;
|
|
updateliststart = curupdate;
|
|
updatelistend = curupdate;
|
|
//while there are updates in the current list
|
|
while (updateliststart)
|
|
{
|
|
curupdate = updateliststart;
|
|
//remove the current update from the list
|
|
if (curupdate->next) curupdate->next->prev = NULL;
|
|
else updatelistend = NULL;
|
|
updateliststart = curupdate->next;
|
|
//current update is removed from the list
|
|
curupdate->inlist = qfalse;
|
|
//
|
|
cluster = &aasworld.clusters[curupdate->cluster];
|
|
//
|
|
cache = AAS_GetAreaRoutingCache(curupdate->cluster,
|
|
curupdate->areanum, portalcache->travelflags);
|
|
//take all portals of the cluster
|
|
for (i = 0; i < cluster->numportals; i++)
|
|
{
|
|
portalnum = aasworld.portalindex[cluster->firstportal + i];
|
|
portal = &aasworld.portals[portalnum];
|
|
//if this is the portal of the current update continue
|
|
if (portal->areanum == curupdate->areanum) continue;
|
|
//
|
|
clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum);
|
|
if (clusterareanum >= cluster->numreachabilityareas) continue;
|
|
//
|
|
t = cache->traveltimes[clusterareanum];
|
|
if (!t) continue;
|
|
t += curupdate->tmptraveltime;
|
|
//
|
|
if (!portalcache->traveltimes[portalnum] ||
|
|
portalcache->traveltimes[portalnum] > t)
|
|
{
|
|
portalcache->traveltimes[portalnum] = t;
|
|
nextupdate = &aasworld.portalupdate[portalnum];
|
|
if (portal->frontcluster == curupdate->cluster)
|
|
{
|
|
nextupdate->cluster = portal->backcluster;
|
|
} //end if
|
|
else
|
|
{
|
|
nextupdate->cluster = portal->frontcluster;
|
|
} //end else
|
|
nextupdate->areanum = portal->areanum;
|
|
//add travel time through the actual portal area for the next update
|
|
nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum];
|
|
if (!nextupdate->inlist)
|
|
{
|
|
// we add the update to the end of the list
|
|
// we could also use a B+ tree to have a real sorted list
|
|
// on travel time which makes for faster routing updates
|
|
nextupdate->next = NULL;
|
|
nextupdate->prev = updatelistend;
|
|
if (updatelistend) updatelistend->next = nextupdate;
|
|
else updateliststart = nextupdate;
|
|
updatelistend = nextupdate;
|
|
nextupdate->inlist = qtrue;
|
|
} //end if
|
|
} //end if
|
|
} //end for
|
|
} //end while
|
|
} //end of the function AAS_UpdatePortalRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags)
|
|
{
|
|
aas_routingcache_t *cache;
|
|
|
|
//find the cached portal routing if existing
|
|
for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next)
|
|
{
|
|
if (cache->travelflags == travelflags) break;
|
|
} //end for
|
|
//if the portal routing isn't cached
|
|
if (!cache)
|
|
{
|
|
cache = AAS_AllocRoutingCache(aasworld.numportals);
|
|
cache->cluster = clusternum;
|
|
cache->areanum = areanum;
|
|
VectorCopy(aasworld.areas[areanum].center, cache->origin);
|
|
cache->starttraveltime = 1;
|
|
cache->travelflags = travelflags;
|
|
//add the cache to the cache list
|
|
cache->prev = NULL;
|
|
cache->next = aasworld.portalcache[areanum];
|
|
if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache;
|
|
aasworld.portalcache[areanum] = cache;
|
|
//update the cache
|
|
AAS_UpdatePortalRoutingCache(cache);
|
|
} //end if
|
|
else
|
|
{
|
|
AAS_UnlinkCache(cache);
|
|
} //end else
|
|
//the cache has been accessed
|
|
cache->time = AAS_RoutingTime();
|
|
cache->type = CACHETYPE_PORTAL;
|
|
AAS_LinkCache(cache);
|
|
return cache;
|
|
} //end of the function AAS_GetPortalRoutingCache
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum)
|
|
{
|
|
int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum;
|
|
unsigned short int t, besttime;
|
|
aas_portal_t *portal;
|
|
aas_cluster_t *cluster;
|
|
aas_routingcache_t *areacache, *portalcache;
|
|
aas_reachability_t *reach;
|
|
|
|
if (!aasworld.initialized) return qfalse;
|
|
|
|
if (areanum == goalareanum)
|
|
{
|
|
*traveltime = 1;
|
|
*reachnum = 0;
|
|
return qtrue;
|
|
}
|
|
//check !AAS_AreaReachability(areanum) with custom developer-only debug message
|
|
if (areanum <= 0 || areanum >= aasworld.numareas)
|
|
{
|
|
if (botDeveloper)
|
|
{
|
|
botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum);
|
|
} //end if
|
|
return qfalse;
|
|
} //end if
|
|
if (goalareanum <= 0 || goalareanum >= aasworld.numareas)
|
|
{
|
|
if (botDeveloper)
|
|
{
|
|
botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum);
|
|
} //end if
|
|
return qfalse;
|
|
} //end if
|
|
if (!aasworld.areasettings[areanum].numreachableareas || !aasworld.areasettings[goalareanum].numreachableareas)
|
|
{
|
|
return qfalse;
|
|
} //end if
|
|
// make sure the routing cache doesn't grow to large
|
|
while(AvailableMemory() < 1 * 1024 * 1024) {
|
|
if (!AAS_FreeOldestCache()) break;
|
|
}
|
|
//
|
|
if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum))
|
|
{
|
|
travelflags |= TFL_DONOTENTER;
|
|
} //end if
|
|
//NOTE: the number of routing updates is limited per frame
|
|
/*
|
|
if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES)
|
|
{
|
|
#ifdef DEBUG
|
|
//Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed");
|
|
#endif
|
|
return 0;
|
|
} //end if
|
|
*/
|
|
//
|
|
clusternum = aasworld.areasettings[areanum].cluster;
|
|
goalclusternum = aasworld.areasettings[goalareanum].cluster;
|
|
//check if the area is a portal of the goal area cluster
|
|
if (clusternum < 0 && goalclusternum > 0)
|
|
{
|
|
portal = &aasworld.portals[-clusternum];
|
|
if (portal->frontcluster == goalclusternum ||
|
|
portal->backcluster == goalclusternum)
|
|
{
|
|
clusternum = goalclusternum;
|
|
} //end if
|
|
} //end if
|
|
//check if the goalarea is a portal of the area cluster
|
|
else if (clusternum > 0 && goalclusternum < 0)
|
|
{
|
|
portal = &aasworld.portals[-goalclusternum];
|
|
if (portal->frontcluster == clusternum ||
|
|
portal->backcluster == clusternum)
|
|
{
|
|
goalclusternum = clusternum;
|
|
} //end if
|
|
} //end if
|
|
//if both areas are in the same cluster
|
|
//NOTE: there might be a shorter route via another cluster!!! but we don't care
|
|
if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum)
|
|
{
|
|
//
|
|
areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags);
|
|
//the number of the area in the cluster
|
|
clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
|
|
//the cluster the area is in
|
|
cluster = &aasworld.clusters[clusternum];
|
|
//if the area is NOT a reachability area
|
|
if (clusterareanum >= cluster->numreachabilityareas) return 0;
|
|
//if it is possible to travel to the goal area through this cluster
|
|
if (areacache->traveltimes[clusterareanum] != 0)
|
|
{
|
|
*reachnum = aasworld.areasettings[areanum].firstreachablearea +
|
|
areacache->reachabilities[clusterareanum];
|
|
if (!origin) {
|
|
*traveltime = areacache->traveltimes[clusterareanum];
|
|
return qtrue;
|
|
}
|
|
reach = &aasworld.reachability[*reachnum];
|
|
*traveltime = areacache->traveltimes[clusterareanum] +
|
|
AAS_AreaTravelTime(areanum, origin, reach->start);
|
|
//
|
|
return qtrue;
|
|
} //end if
|
|
} //end if
|
|
//
|
|
clusternum = aasworld.areasettings[areanum].cluster;
|
|
goalclusternum = aasworld.areasettings[goalareanum].cluster;
|
|
//if the goal area is a portal
|
|
if (goalclusternum < 0)
|
|
{
|
|
//just assume the goal area is part of the front cluster
|
|
portal = &aasworld.portals[-goalclusternum];
|
|
goalclusternum = portal->frontcluster;
|
|
} //end if
|
|
//get the portal routing cache
|
|
portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags);
|
|
//if the area is a cluster portal, read directly from the portal cache
|
|
if (clusternum < 0)
|
|
{
|
|
*traveltime = portalcache->traveltimes[-clusternum];
|
|
*reachnum = aasworld.areasettings[areanum].firstreachablearea +
|
|
portalcache->reachabilities[-clusternum];
|
|
return qtrue;
|
|
} //end if
|
|
//
|
|
besttime = 0;
|
|
bestreachnum = -1;
|
|
//the cluster the area is in
|
|
cluster = &aasworld.clusters[clusternum];
|
|
//find the portal of the area cluster leading towards the goal area
|
|
for (i = 0; i < cluster->numportals; i++)
|
|
{
|
|
portalnum = aasworld.portalindex[cluster->firstportal + i];
|
|
//if the goal area isn't reachable from the portal
|
|
if (!portalcache->traveltimes[portalnum]) continue;
|
|
//
|
|
portal = &aasworld.portals[portalnum];
|
|
//get the cache of the portal area
|
|
areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags);
|
|
//current area inside the current cluster
|
|
clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
|
|
//if the area is NOT a reachability area
|
|
if (clusterareanum >= cluster->numreachabilityareas) continue;
|
|
//if the portal is NOT reachable from this area
|
|
if (!areacache->traveltimes[clusterareanum]) continue;
|
|
//total travel time is the travel time the portal area is from
|
|
//the goal area plus the travel time towards the portal area
|
|
t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum];
|
|
//FIXME: add the exact travel time through the actual portal area
|
|
//NOTE: for now we just add the largest travel time through the portal area
|
|
// because we can't directly calculate the exact travel time
|
|
// to be more specific we don't know which reachability was used to travel
|
|
// into the portal area
|
|
t += aasworld.portalmaxtraveltimes[portalnum];
|
|
//
|
|
if (origin)
|
|
{
|
|
*reachnum = aasworld.areasettings[areanum].firstreachablearea +
|
|
areacache->reachabilities[clusterareanum];
|
|
reach = aasworld.reachability + *reachnum;
|
|
t += AAS_AreaTravelTime(areanum, origin, reach->start);
|
|
} //end if
|
|
//if the time is better than the one already found
|
|
if (!besttime || t < besttime)
|
|
{
|
|
bestreachnum = *reachnum;
|
|
besttime = t;
|
|
} //end if
|
|
} //end for
|
|
if (bestreachnum < 0) {
|
|
return qfalse;
|
|
}
|
|
*reachnum = bestreachnum;
|
|
*traveltime = besttime;
|
|
return qtrue;
|
|
} //end of the function AAS_AreaRouteToGoalArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)
|
|
{
|
|
int traveltime, reachnum = 0;
|
|
|
|
if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))
|
|
{
|
|
return traveltime;
|
|
}
|
|
return 0;
|
|
} //end of the function AAS_AreaTravelTimeToGoalArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)
|
|
{
|
|
int traveltime, reachnum = 0;
|
|
|
|
if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))
|
|
{
|
|
return reachnum;
|
|
}
|
|
return 0;
|
|
} //end of the function AAS_AreaReachabilityToGoalArea
|
|
//===========================================================================
|
|
// predict the route and stop on one of the stop events
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin,
|
|
int goalareanum, int travelflags, int maxareas, int maxtime,
|
|
int stopevent, int stopcontents, int stoptfl, int stopareanum)
|
|
{
|
|
int curareanum, reachnum, i, j, testareanum;
|
|
vec3_t curorigin;
|
|
aas_reachability_t *reach;
|
|
aas_reachabilityareas_t *reachareas;
|
|
|
|
//init output
|
|
route->stopevent = RSE_NONE;
|
|
route->endarea = goalareanum;
|
|
route->endcontents = 0;
|
|
route->endtravelflags = 0;
|
|
VectorCopy(origin, route->endpos);
|
|
route->time = 0;
|
|
|
|
curareanum = areanum;
|
|
VectorCopy(origin, curorigin);
|
|
|
|
for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++)
|
|
{
|
|
reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags);
|
|
if (!reachnum)
|
|
{
|
|
route->stopevent = RSE_NOROUTE;
|
|
return qfalse;
|
|
} //end if
|
|
reach = &aasworld.reachability[reachnum];
|
|
//
|
|
if (stopevent & RSE_USETRAVELTYPE)
|
|
{
|
|
if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl)
|
|
{
|
|
route->stopevent = RSE_USETRAVELTYPE;
|
|
route->endarea = curareanum;
|
|
route->endcontents = aasworld.areasettings[curareanum].contents;
|
|
route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);
|
|
VectorCopy(reach->start, route->endpos);
|
|
return qtrue;
|
|
} //end if
|
|
if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl)
|
|
{
|
|
route->stopevent = RSE_USETRAVELTYPE;
|
|
route->endarea = reach->areanum;
|
|
route->endcontents = aasworld.areasettings[reach->areanum].contents;
|
|
route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum);
|
|
VectorCopy(reach->end, route->endpos);
|
|
route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
|
|
route->time += reach->traveltime;
|
|
return qtrue;
|
|
} //end if
|
|
} //end if
|
|
reachareas = &aasworld.reachabilityareas[reachnum];
|
|
for (j = 0; j < reachareas->numareas + 1; j++)
|
|
{
|
|
if (j >= reachareas->numareas)
|
|
testareanum = reach->areanum;
|
|
else
|
|
testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j];
|
|
if (stopevent & RSE_ENTERCONTENTS)
|
|
{
|
|
if (aasworld.areasettings[testareanum].contents & stopcontents)
|
|
{
|
|
route->stopevent = RSE_ENTERCONTENTS;
|
|
route->endarea = testareanum;
|
|
route->endcontents = aasworld.areasettings[testareanum].contents;
|
|
VectorCopy(reach->end, route->endpos);
|
|
route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
|
|
route->time += reach->traveltime;
|
|
return qtrue;
|
|
} //end if
|
|
} //end if
|
|
if (stopevent & RSE_ENTERAREA)
|
|
{
|
|
if (testareanum == stopareanum)
|
|
{
|
|
route->stopevent = RSE_ENTERAREA;
|
|
route->endarea = testareanum;
|
|
route->endcontents = aasworld.areasettings[testareanum].contents;
|
|
VectorCopy(reach->start, route->endpos);
|
|
return qtrue;
|
|
} //end if
|
|
} //end if
|
|
} //end for
|
|
|
|
route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
|
|
route->time += reach->traveltime;
|
|
route->endarea = reach->areanum;
|
|
route->endcontents = aasworld.areasettings[reach->areanum].contents;
|
|
route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);
|
|
VectorCopy(reach->end, route->endpos);
|
|
//
|
|
curareanum = reach->areanum;
|
|
VectorCopy(reach->end, curorigin);
|
|
//
|
|
if (maxtime && route->time > maxtime)
|
|
break;
|
|
} //end while
|
|
if (curareanum != goalareanum)
|
|
return qfalse;
|
|
return qtrue;
|
|
} //end of the function AAS_PredictRoute
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_BridgeWalkable(int areanum)
|
|
{
|
|
return qfalse;
|
|
} //end of the function AAS_BridgeWalkable
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach)
|
|
{
|
|
if (!aasworld.initialized)
|
|
{
|
|
Com_Memset(reach, 0, sizeof(aas_reachability_t));
|
|
return;
|
|
} //end if
|
|
if (num < 0 || num >= aasworld.reachabilitysize)
|
|
{
|
|
Com_Memset(reach, 0, sizeof(aas_reachability_t));
|
|
return;
|
|
} //end if
|
|
Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));;
|
|
} //end of the function AAS_ReachabilityFromNum
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_NextAreaReachability(int areanum, int reachnum)
|
|
{
|
|
aas_areasettings_t *settings;
|
|
|
|
if (!aasworld.initialized) return 0;
|
|
|
|
if (areanum <= 0 || areanum >= aasworld.numareas)
|
|
{
|
|
botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum);
|
|
return 0;
|
|
} //end if
|
|
|
|
settings = &aasworld.areasettings[areanum];
|
|
if (!reachnum)
|
|
{
|
|
return settings->firstreachablearea;
|
|
} //end if
|
|
if (reachnum < settings->firstreachablearea)
|
|
{
|
|
botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara");
|
|
return 0;
|
|
} //end if
|
|
reachnum++;
|
|
if (reachnum >= settings->firstreachablearea + settings->numreachableareas)
|
|
{
|
|
return 0;
|
|
} //end if
|
|
return reachnum;
|
|
} //end of the function AAS_NextAreaReachability
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_NextModelReachability(int num, int modelnum)
|
|
{
|
|
int i;
|
|
|
|
if (num <= 0) num = 1;
|
|
else if (num >= aasworld.reachabilitysize) return 0;
|
|
else num++;
|
|
//
|
|
for (i = num; i < aasworld.reachabilitysize; i++)
|
|
{
|
|
if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR)
|
|
{
|
|
if (aasworld.reachability[i].facenum == modelnum) return i;
|
|
} //end if
|
|
else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
|
|
{
|
|
if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i;
|
|
} //end if
|
|
} //end for
|
|
return 0;
|
|
} //end of the function AAS_NextModelReachability
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin)
|
|
{
|
|
int i, n, t;
|
|
vec3_t start, end;
|
|
aas_trace_t trace;
|
|
|
|
//if the area has no reachabilities
|
|
if (!AAS_AreaReachability(areanum)) return qfalse;
|
|
//
|
|
n = aasworld.numareas * random();
|
|
for (i = 0; i < aasworld.numareas; i++)
|
|
{
|
|
if (n <= 0) n = 1;
|
|
if (n >= aasworld.numareas) n = 1;
|
|
if (AAS_AreaReachability(n))
|
|
{
|
|
t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags);
|
|
//if the goal is reachable
|
|
if (t > 0)
|
|
{
|
|
if (AAS_AreaSwim(n))
|
|
{
|
|
*goalareanum = n;
|
|
VectorCopy(aasworld.areas[n].center, goalorigin);
|
|
//botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum);
|
|
return qtrue;
|
|
} //end if
|
|
VectorCopy(aasworld.areas[n].center, start);
|
|
if (!AAS_PointAreaNum(start))
|
|
Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]);
|
|
VectorCopy(start, end);
|
|
end[2] -= 300;
|
|
trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
|
|
if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n)
|
|
{
|
|
if (AAS_AreaGroundFaceArea(n) > 300)
|
|
{
|
|
*goalareanum = n;
|
|
VectorCopy(trace.endpos, goalorigin);
|
|
//botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum);
|
|
return qtrue;
|
|
} //end if
|
|
} //end if
|
|
} //end if
|
|
} //end if
|
|
n++;
|
|
} //end for
|
|
return qfalse;
|
|
} //end of the function AAS_RandomGoalArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_AreaVisible(int srcarea, int destarea)
|
|
{
|
|
return qfalse;
|
|
} //end of the function AAS_AreaVisible
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point)
|
|
{
|
|
vec3_t vec, p2;
|
|
|
|
AAS_ProjectPointOntoVector(point, v1, v2, p2);
|
|
VectorSubtract(point, p2, vec);
|
|
return VectorLength(vec);
|
|
} //end of the function DistancePointToLine
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags)
|
|
{
|
|
int i, j, nextareanum, badtravelflags, numreach, bestarea;
|
|
unsigned short int t, besttraveltime;
|
|
static unsigned short int *hidetraveltimes;
|
|
aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
|
|
aas_reachability_t *reach;
|
|
float dist1, dist2;
|
|
vec3_t v1, v2, p;
|
|
qboolean startVisible;
|
|
|
|
//
|
|
if (!hidetraveltimes)
|
|
{
|
|
hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int));
|
|
} //end if
|
|
else
|
|
{
|
|
Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int));
|
|
} //end else
|
|
besttraveltime = 0;
|
|
bestarea = 0;
|
|
//assume visible
|
|
startVisible = qtrue;
|
|
//
|
|
badtravelflags = ~travelflags;
|
|
//
|
|
curupdate = &aasworld.areaupdate[areanum];
|
|
curupdate->areanum = areanum;
|
|
VectorCopy(origin, curupdate->start);
|
|
curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0];
|
|
curupdate->tmptraveltime = 0;
|
|
//put the area to start with in the current read list
|
|
curupdate->next = NULL;
|
|
curupdate->prev = NULL;
|
|
updateliststart = curupdate;
|
|
updatelistend = curupdate;
|
|
//while there are updates in the list
|
|
while (updateliststart)
|
|
{
|
|
curupdate = updateliststart;
|
|
//
|
|
if (curupdate->next) curupdate->next->prev = NULL;
|
|
else updatelistend = NULL;
|
|
updateliststart = curupdate->next;
|
|
//
|
|
curupdate->inlist = qfalse;
|
|
//check all reversed reachability links
|
|
numreach = aasworld.areasettings[curupdate->areanum].numreachableareas;
|
|
reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea];
|
|
//
|
|
for (i = 0; i < numreach; i++, reach++)
|
|
{
|
|
//if an undesired travel type is used
|
|
if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;
|
|
//
|
|
if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;
|
|
//number of the area the reachability leads to
|
|
nextareanum = reach->areanum;
|
|
// if this moves us into the enemies area, skip it
|
|
if (nextareanum == enemyareanum) continue;
|
|
//time already travelled plus the traveltime through
|
|
//the current area plus the travel time from the reachability
|
|
t = curupdate->tmptraveltime +
|
|
AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) +
|
|
reach->traveltime;
|
|
|
|
//avoid going near the enemy
|
|
AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p);
|
|
for (j = 0; j < 3; j++)
|
|
if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) ||
|
|
(p[j] < curupdate->start[j] && p[j] < reach->end[j]))
|
|
break;
|
|
if (j < 3)
|
|
{
|
|
VectorSubtract(enemyorigin, reach->end, v2);
|
|
} //end if
|
|
else
|
|
{
|
|
VectorSubtract(enemyorigin, p, v2);
|
|
} //end else
|
|
dist2 = VectorLength(v2);
|
|
//never go through the enemy
|
|
if (dist2 < 40) continue;
|
|
//
|
|
VectorSubtract(enemyorigin, curupdate->start, v1);
|
|
dist1 = VectorLength(v1);
|
|
//
|
|
if (dist2 < dist1)
|
|
{
|
|
t += (dist1 - dist2) * 10;
|
|
}
|
|
// if we weren't visible when starting, make sure we don't move into their view
|
|
if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) {
|
|
continue;
|
|
}
|
|
//
|
|
if (besttraveltime && t >= besttraveltime) continue;
|
|
//
|
|
if (!hidetraveltimes[nextareanum] ||
|
|
hidetraveltimes[nextareanum] > t)
|
|
{
|
|
//if the nextarea is not visible from the enemy area
|
|
if (!AAS_AreaVisible(enemyareanum, nextareanum))
|
|
{
|
|
besttraveltime = t;
|
|
bestarea = nextareanum;
|
|
} //end if
|
|
hidetraveltimes[nextareanum] = t;
|
|
nextupdate = &aasworld.areaupdate[nextareanum];
|
|
nextupdate->areanum = nextareanum;
|
|
nextupdate->tmptraveltime = t;
|
|
//remember where we entered this area
|
|
VectorCopy(reach->end, nextupdate->start);
|
|
//if this update is not in the list yet
|
|
if (!nextupdate->inlist)
|
|
{
|
|
//add the new update to the end of the list
|
|
nextupdate->next = NULL;
|
|
nextupdate->prev = updatelistend;
|
|
if (updatelistend) updatelistend->next = nextupdate;
|
|
else updateliststart = nextupdate;
|
|
updatelistend = nextupdate;
|
|
nextupdate->inlist = qtrue;
|
|
} //end if
|
|
} //end if
|
|
} //end for
|
|
} //end while
|
|
return bestarea;
|
|
} //end of the function AAS_NearestHideArea
|