AAS_routing.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "AAS_local.h"
  23. #include "../Game_local.h" // for print and error
  24. #define CACHETYPE_AREA 1
  25. #define CACHETYPE_PORTAL 2
  26. #define MAX_ROUTING_CACHE_MEMORY (2*1024*1024)
  27. #define LEDGE_TRAVELTIME_PANALTY 250
  28. /*
  29. ============
  30. idRoutingCache::idRoutingCache
  31. ============
  32. */
  33. idRoutingCache::idRoutingCache( int size ) {
  34. areaNum = 0;
  35. cluster = 0;
  36. next = prev = NULL;
  37. time_next = time_prev = NULL;
  38. travelFlags = 0;
  39. startTravelTime = 0;
  40. type = 0;
  41. this->size = size;
  42. reachabilities = new byte[size];
  43. memset( reachabilities, 0, size * sizeof( reachabilities[0] ) );
  44. travelTimes = new unsigned short[size];
  45. memset( travelTimes, 0, size * sizeof( travelTimes[0] ) );
  46. }
  47. /*
  48. ============
  49. idRoutingCache::~idRoutingCache
  50. ============
  51. */
  52. idRoutingCache::~idRoutingCache( void ) {
  53. delete [] reachabilities;
  54. delete [] travelTimes;
  55. }
  56. /*
  57. ============
  58. idRoutingCache::Size
  59. ============
  60. */
  61. int idRoutingCache::Size( void ) const {
  62. return sizeof( idRoutingCache ) + size * sizeof( reachabilities[0] ) + size * sizeof( travelTimes[0] );
  63. }
  64. /*
  65. ============
  66. idAASLocal::AreaTravelTime
  67. ============
  68. */
  69. unsigned short idAASLocal::AreaTravelTime( int areaNum, const idVec3 &start, const idVec3 &end ) const {
  70. float dist;
  71. dist = ( end - start ).Length();
  72. if ( file->GetArea( areaNum ).travelFlags & TFL_CROUCH ) {
  73. dist *= 100.0f / 100.0f;
  74. } else if ( file->GetArea( areaNum ).travelFlags & TFL_WATER ) {
  75. dist *= 100.0f / 150.0f;
  76. } else {
  77. dist *= 100.0f / 300.0f;
  78. }
  79. if ( dist < 1.0f ) {
  80. return 1;
  81. }
  82. return (unsigned short) idMath::FtoiFast( dist );
  83. }
  84. /*
  85. ============
  86. idAASLocal::CalculateAreaTravelTimes
  87. ============
  88. */
  89. void idAASLocal::CalculateAreaTravelTimes(void) {
  90. int n, i, j, numReach, numRevReach, t, maxt;
  91. byte *bytePtr;
  92. idReachability *reach, *rev_reach;
  93. // get total memory for all area travel times
  94. numAreaTravelTimes = 0;
  95. for ( n = 0; n < file->GetNumAreas(); n++ ) {
  96. if ( !(file->GetArea( n ).flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
  97. continue;
  98. }
  99. numReach = 0;
  100. for ( reach = file->GetArea( n ).reach; reach; reach = reach->next ) {
  101. numReach++;
  102. }
  103. numRevReach = 0;
  104. for ( rev_reach = file->GetArea( n ).rev_reach; rev_reach; rev_reach = rev_reach->rev_next ) {
  105. numRevReach++;
  106. }
  107. numAreaTravelTimes += numReach * numRevReach;
  108. }
  109. areaTravelTimes = (unsigned short *) Mem_Alloc( numAreaTravelTimes * sizeof( unsigned short ) );
  110. bytePtr = (byte *) areaTravelTimes;
  111. for ( n = 0; n < file->GetNumAreas(); n++ ) {
  112. if ( !(file->GetArea( n ).flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
  113. continue;
  114. }
  115. // for each reachability that starts in this area calculate the travel time
  116. // towards all the reachabilities that lead towards this area
  117. for ( maxt = i = 0, reach = file->GetArea( n ).reach; reach; reach = reach->next, i++ ) {
  118. assert( i < MAX_REACH_PER_AREA );
  119. if ( i >= MAX_REACH_PER_AREA ) {
  120. gameLocal.Error( "i >= MAX_REACH_PER_AREA" );
  121. }
  122. reach->number = i;
  123. reach->disableCount = 0;
  124. reach->areaTravelTimes = (unsigned short *) bytePtr;
  125. for ( j = 0, rev_reach = file->GetArea( n ).rev_reach; rev_reach; rev_reach = rev_reach->rev_next, j++ ) {
  126. t = AreaTravelTime( n, reach->start, rev_reach->end );
  127. reach->areaTravelTimes[j] = t;
  128. if ( t > maxt ) {
  129. maxt = t;
  130. }
  131. }
  132. bytePtr += j * sizeof( unsigned short );
  133. }
  134. // if this area is a portal
  135. if ( file->GetArea( n ).cluster < 0 ) {
  136. // set the maximum travel time through this portal
  137. file->SetPortalMaxTravelTime( -file->GetArea( n ).cluster, maxt );
  138. }
  139. }
  140. assert( ( (unsigned int) bytePtr - (unsigned int) areaTravelTimes ) <= numAreaTravelTimes * sizeof( unsigned short ) );
  141. }
  142. /*
  143. ============
  144. idAASLocal::DeleteAreaTravelTimes
  145. ============
  146. */
  147. void idAASLocal::DeleteAreaTravelTimes( void ) {
  148. Mem_Free( areaTravelTimes );
  149. areaTravelTimes = NULL;
  150. numAreaTravelTimes = 0;
  151. }
  152. /*
  153. ============
  154. idAASLocal::SetupRoutingCache
  155. ============
  156. */
  157. void idAASLocal::SetupRoutingCache( void ) {
  158. int i;
  159. byte *bytePtr;
  160. areaCacheIndexSize = 0;
  161. for ( i = 0; i < file->GetNumClusters(); i++ ) {
  162. areaCacheIndexSize += file->GetCluster( i ).numReachableAreas;
  163. }
  164. areaCacheIndex = (idRoutingCache ***) Mem_ClearedAlloc( file->GetNumClusters() * sizeof( idRoutingCache ** ) +
  165. areaCacheIndexSize * sizeof( idRoutingCache *) );
  166. bytePtr = ((byte *)areaCacheIndex) + file->GetNumClusters() * sizeof( idRoutingCache ** );
  167. for ( i = 0; i < file->GetNumClusters(); i++ ) {
  168. areaCacheIndex[i] = ( idRoutingCache ** ) bytePtr;
  169. bytePtr += file->GetCluster( i ).numReachableAreas * sizeof( idRoutingCache * );
  170. }
  171. portalCacheIndexSize = file->GetNumAreas();
  172. portalCacheIndex = (idRoutingCache **) Mem_ClearedAlloc( portalCacheIndexSize * sizeof( idRoutingCache * ) );
  173. areaUpdate = (idRoutingUpdate *) Mem_ClearedAlloc( file->GetNumAreas() * sizeof( idRoutingUpdate ) );
  174. portalUpdate = (idRoutingUpdate *) Mem_ClearedAlloc( (file->GetNumPortals()+1) * sizeof( idRoutingUpdate ) );
  175. goalAreaTravelTimes = (unsigned short *) Mem_ClearedAlloc( file->GetNumAreas() * sizeof( unsigned short ) );
  176. cacheListStart = cacheListEnd = NULL;
  177. totalCacheMemory = 0;
  178. }
  179. /*
  180. ============
  181. idAASLocal::DeleteClusterCache
  182. ============
  183. */
  184. void idAASLocal::DeleteClusterCache( int clusterNum ) {
  185. int i;
  186. idRoutingCache *cache;
  187. for ( i = 0; i < file->GetCluster( clusterNum ).numReachableAreas; i++ ) {
  188. for ( cache = areaCacheIndex[clusterNum][i]; cache; cache = areaCacheIndex[clusterNum][i] ) {
  189. areaCacheIndex[clusterNum][i] = cache->next;
  190. UnlinkCache( cache );
  191. delete cache;
  192. }
  193. }
  194. }
  195. /*
  196. ============
  197. idAASLocal::DeletePortalCache
  198. ============
  199. */
  200. void idAASLocal::DeletePortalCache( void ) {
  201. int i;
  202. idRoutingCache *cache;
  203. for ( i = 0; i < file->GetNumAreas(); i++ ) {
  204. for ( cache = portalCacheIndex[i]; cache; cache = portalCacheIndex[i] ) {
  205. portalCacheIndex[i] = cache->next;
  206. UnlinkCache( cache );
  207. delete cache;
  208. }
  209. }
  210. }
  211. /*
  212. ============
  213. idAASLocal::ShutdownRoutingCache
  214. ============
  215. */
  216. void idAASLocal::ShutdownRoutingCache( void ) {
  217. int i;
  218. for ( i = 0; i < file->GetNumClusters(); i++ ) {
  219. DeleteClusterCache( i );
  220. }
  221. DeletePortalCache();
  222. Mem_Free( areaCacheIndex );
  223. areaCacheIndex = NULL;
  224. areaCacheIndexSize = 0;
  225. Mem_Free( portalCacheIndex );
  226. portalCacheIndex = NULL;
  227. portalCacheIndexSize = 0;
  228. Mem_Free( areaUpdate );
  229. areaUpdate = NULL;
  230. Mem_Free( portalUpdate );
  231. portalUpdate = NULL;
  232. Mem_Free( goalAreaTravelTimes );
  233. goalAreaTravelTimes = NULL;
  234. cacheListStart = cacheListEnd = NULL;
  235. totalCacheMemory = 0;
  236. }
  237. /*
  238. ============
  239. idAASLocal::SetupRouting
  240. ============
  241. */
  242. bool idAASLocal::SetupRouting( void ) {
  243. CalculateAreaTravelTimes();
  244. SetupRoutingCache();
  245. return true;
  246. }
  247. /*
  248. ============
  249. idAASLocal::ShutdownRouting
  250. ============
  251. */
  252. void idAASLocal::ShutdownRouting( void ) {
  253. DeleteAreaTravelTimes();
  254. ShutdownRoutingCache();
  255. }
  256. /*
  257. ============
  258. idAASLocal::RoutingStats
  259. ============
  260. */
  261. void idAASLocal::RoutingStats( void ) const {
  262. idRoutingCache *cache;
  263. int numAreaCache, numPortalCache;
  264. int totalAreaCacheMemory, totalPortalCacheMemory;
  265. numAreaCache = numPortalCache = 0;
  266. totalAreaCacheMemory = totalPortalCacheMemory = 0;
  267. for ( cache = cacheListStart; cache; cache = cache->time_next ) {
  268. if ( cache->type == CACHETYPE_AREA ) {
  269. numAreaCache++;
  270. totalAreaCacheMemory += sizeof( idRoutingCache ) + cache->size * (sizeof( unsigned short ) + sizeof( byte ));
  271. } else {
  272. numPortalCache++;
  273. totalPortalCacheMemory += sizeof( idRoutingCache ) + cache->size * (sizeof( unsigned short ) + sizeof( byte ));
  274. }
  275. }
  276. gameLocal.Printf( "%6d area cache (%d KB)\n", numAreaCache, totalAreaCacheMemory >> 10 );
  277. gameLocal.Printf( "%6d portal cache (%d KB)\n", numPortalCache, totalPortalCacheMemory >> 10 );
  278. gameLocal.Printf( "%6d total cache (%d KB)\n", numAreaCache + numPortalCache, totalCacheMemory >> 10 );
  279. gameLocal.Printf( "%6d area travel times (%d KB)\n", numAreaTravelTimes, ( numAreaTravelTimes * sizeof( unsigned short ) ) >> 10 );
  280. gameLocal.Printf( "%6d area cache entries (%d KB)\n", areaCacheIndexSize, ( areaCacheIndexSize * sizeof( idRoutingCache * ) ) >> 10 );
  281. gameLocal.Printf( "%6d portal cache entries (%d KB)\n", portalCacheIndexSize, ( portalCacheIndexSize * sizeof( idRoutingCache * ) ) >> 10 );
  282. }
  283. /*
  284. ============
  285. idAASLocal::RemoveRoutingCacheUsingArea
  286. ============
  287. */
  288. void idAASLocal::RemoveRoutingCacheUsingArea( int areaNum ) {
  289. int clusterNum;
  290. clusterNum = file->GetArea( areaNum ).cluster;
  291. if ( clusterNum > 0 ) {
  292. // remove all the cache in the cluster the area is in
  293. DeleteClusterCache( clusterNum );
  294. }
  295. else {
  296. // if this is a portal remove all cache in both the front and back cluster
  297. DeleteClusterCache( file->GetPortal( -clusterNum ).clusters[0] );
  298. DeleteClusterCache( file->GetPortal( -clusterNum ).clusters[1] );
  299. }
  300. DeletePortalCache();
  301. }
  302. /*
  303. ============
  304. idAASLocal::DisableArea
  305. ============
  306. */
  307. void idAASLocal::DisableArea( int areaNum ) {
  308. assert( areaNum > 0 && areaNum < file->GetNumAreas() );
  309. if ( file->GetArea( areaNum ).travelFlags & TFL_INVALID ) {
  310. return;
  311. }
  312. file->SetAreaTravelFlag( areaNum, TFL_INVALID );
  313. RemoveRoutingCacheUsingArea( areaNum );
  314. }
  315. /*
  316. ============
  317. idAASLocal::EnableArea
  318. ============
  319. */
  320. void idAASLocal::EnableArea( int areaNum ) {
  321. assert( areaNum > 0 && areaNum < file->GetNumAreas() );
  322. if ( !( file->GetArea( areaNum ).travelFlags & TFL_INVALID ) ) {
  323. return;
  324. }
  325. file->RemoveAreaTravelFlag( areaNum, TFL_INVALID );
  326. RemoveRoutingCacheUsingArea( areaNum );
  327. }
  328. /*
  329. ============
  330. idAASLocal::SetAreaState_r
  331. ============
  332. */
  333. bool idAASLocal::SetAreaState_r( int nodeNum, const idBounds &bounds, const int areaContents, bool disabled ) {
  334. int res;
  335. const aasNode_t *node;
  336. bool foundClusterPortal = false;
  337. while( nodeNum != 0 ) {
  338. if ( nodeNum < 0 ) {
  339. // if this area is a cluster portal
  340. if ( file->GetArea( -nodeNum ).contents & areaContents ) {
  341. if ( disabled ) {
  342. DisableArea( -nodeNum );
  343. } else {
  344. EnableArea( -nodeNum );
  345. }
  346. foundClusterPortal |= true;
  347. }
  348. break;
  349. }
  350. node = &file->GetNode( nodeNum );
  351. res = bounds.PlaneSide( file->GetPlane( node->planeNum ) );
  352. if ( res == PLANESIDE_BACK ) {
  353. nodeNum = node->children[1];
  354. }
  355. else if ( res == PLANESIDE_FRONT ) {
  356. nodeNum = node->children[0];
  357. }
  358. else {
  359. foundClusterPortal |= SetAreaState_r( node->children[1], bounds, areaContents, disabled );
  360. nodeNum = node->children[0];
  361. }
  362. }
  363. return foundClusterPortal;
  364. }
  365. /*
  366. ============
  367. idAASLocal::SetAreaState
  368. ============
  369. */
  370. bool idAASLocal::SetAreaState( const idBounds &bounds, const int areaContents, bool disabled ) {
  371. idBounds expBounds;
  372. if ( !file ) {
  373. return false;
  374. }
  375. expBounds[0] = bounds[0] - file->GetSettings().boundingBoxes[0][1];
  376. expBounds[1] = bounds[1] - file->GetSettings().boundingBoxes[0][0];
  377. // find all areas within or touching the bounds with the given contents and disable/enable them for routing
  378. return SetAreaState_r( 1, expBounds, areaContents, disabled );
  379. }
  380. /*
  381. ============
  382. idAASLocal::GetBoundsAreas_r
  383. ============
  384. */
  385. void idAASLocal::GetBoundsAreas_r( int nodeNum, const idBounds &bounds, idList<int> &areas ) const {
  386. int res;
  387. const aasNode_t *node;
  388. while( nodeNum != 0 ) {
  389. if ( nodeNum < 0 ) {
  390. areas.Append( -nodeNum );
  391. break;
  392. }
  393. node = &file->GetNode( nodeNum );
  394. res = bounds.PlaneSide( file->GetPlane( node->planeNum ) );
  395. if ( res == PLANESIDE_BACK ) {
  396. nodeNum = node->children[1];
  397. }
  398. else if ( res == PLANESIDE_FRONT ) {
  399. nodeNum = node->children[0];
  400. }
  401. else {
  402. GetBoundsAreas_r( node->children[1], bounds, areas );
  403. nodeNum = node->children[0];
  404. }
  405. }
  406. }
  407. /*
  408. ============
  409. idAASLocal::SetObstacleState
  410. ============
  411. */
  412. void idAASLocal::SetObstacleState( const idRoutingObstacle *obstacle, bool enable ) {
  413. int i;
  414. const aasArea_t *area;
  415. idReachability *reach, *rev_reach;
  416. bool inside;
  417. for ( i = 0; i < obstacle->areas.Num(); i++ ) {
  418. RemoveRoutingCacheUsingArea( obstacle->areas[i] );
  419. area = &file->GetArea( obstacle->areas[i] );
  420. for ( rev_reach = area->rev_reach; rev_reach; rev_reach = rev_reach->rev_next ) {
  421. if ( rev_reach->travelType & TFL_INVALID ) {
  422. continue;
  423. }
  424. inside = false;
  425. if ( obstacle->bounds.ContainsPoint( rev_reach->end ) ) {
  426. inside = true;
  427. }
  428. else {
  429. for ( reach = area->reach; reach; reach = reach->next ) {
  430. if ( obstacle->bounds.LineIntersection( rev_reach->end, reach->start ) ) {
  431. inside = true;
  432. break;
  433. }
  434. }
  435. }
  436. if ( inside ) {
  437. if ( enable ) {
  438. rev_reach->disableCount--;
  439. if ( rev_reach->disableCount <= 0 ) {
  440. rev_reach->travelType &= ~TFL_INVALID;
  441. rev_reach->disableCount = 0;
  442. }
  443. }
  444. else {
  445. rev_reach->travelType |= TFL_INVALID;
  446. rev_reach->disableCount++;
  447. }
  448. }
  449. }
  450. }
  451. }
  452. /*
  453. ============
  454. idAASLocal::AddObstacle
  455. ============
  456. */
  457. aasHandle_t idAASLocal::AddObstacle( const idBounds &bounds ) {
  458. idRoutingObstacle *obstacle;
  459. if ( !file ) {
  460. return -1;
  461. }
  462. obstacle = new idRoutingObstacle;
  463. obstacle->bounds[0] = bounds[0] - file->GetSettings().boundingBoxes[0][1];
  464. obstacle->bounds[1] = bounds[1] - file->GetSettings().boundingBoxes[0][0];
  465. GetBoundsAreas_r( 1, obstacle->bounds, obstacle->areas );
  466. SetObstacleState( obstacle, true );
  467. obstacleList.Append( obstacle );
  468. return obstacleList.Num() - 1;
  469. }
  470. /*
  471. ============
  472. idAASLocal::RemoveObstacle
  473. ============
  474. */
  475. void idAASLocal::RemoveObstacle( const aasHandle_t handle ) {
  476. if ( !file ) {
  477. return;
  478. }
  479. if ( ( handle >= 0 ) && ( handle < obstacleList.Num() ) ) {
  480. SetObstacleState( obstacleList[handle], false );
  481. delete obstacleList[handle];
  482. obstacleList.RemoveIndex( handle );
  483. }
  484. }
  485. /*
  486. ============
  487. idAASLocal::RemoveAllObstacles
  488. ============
  489. */
  490. void idAASLocal::RemoveAllObstacles( void ) {
  491. int i;
  492. if ( !file ) {
  493. return;
  494. }
  495. for ( i = 0; i < obstacleList.Num(); i++ ) {
  496. SetObstacleState( obstacleList[i], false );
  497. delete obstacleList[i];
  498. }
  499. obstacleList.Clear();
  500. }
  501. /*
  502. ============
  503. idAASLocal::LinkCache
  504. link the cache in the cache list sorted from oldest to newest cache
  505. ============
  506. */
  507. void idAASLocal::LinkCache( idRoutingCache *cache ) const {
  508. // if the cache is already linked
  509. if ( cache->time_next || cache->time_prev || cacheListStart == cache ) {
  510. UnlinkCache( cache );
  511. }
  512. totalCacheMemory += cache->Size();
  513. // add cache to the end of the list
  514. cache->time_next = NULL;
  515. cache->time_prev = cacheListEnd;
  516. if ( cacheListEnd ) {
  517. cacheListEnd->time_next = cache;
  518. }
  519. cacheListEnd = cache;
  520. if ( !cacheListStart ) {
  521. cacheListStart = cache;
  522. }
  523. }
  524. /*
  525. ============
  526. idAASLocal::UnlinkCache
  527. ============
  528. */
  529. void idAASLocal::UnlinkCache( idRoutingCache *cache ) const {
  530. totalCacheMemory -= cache->Size();
  531. // unlink the cache
  532. if ( cache->time_next ) {
  533. cache->time_next->time_prev = cache->time_prev;
  534. } else {
  535. cacheListEnd = cache->time_prev;
  536. }
  537. if ( cache->time_prev ) {
  538. cache->time_prev->time_next = cache->time_next;
  539. } else {
  540. cacheListStart = cache->time_next;
  541. }
  542. cache->time_next = cache->time_prev = NULL;
  543. }
  544. /*
  545. ============
  546. idAASLocal::DeleteOldestCache
  547. ============
  548. */
  549. void idAASLocal::DeleteOldestCache( void ) const {
  550. idRoutingCache *cache;
  551. assert( cacheListStart );
  552. // unlink the oldest cache
  553. cache = cacheListStart;
  554. UnlinkCache( cache );
  555. // unlink the oldest cache from the area or portal cache index
  556. if ( cache->next ) {
  557. cache->next->prev = cache->prev;
  558. }
  559. if ( cache->prev ) {
  560. cache->prev->next = cache->next;
  561. }
  562. else if ( cache->type == CACHETYPE_AREA ) {
  563. areaCacheIndex[cache->cluster][ClusterAreaNum( cache->cluster, cache->areaNum )] = cache->next;
  564. }
  565. else if ( cache->type == CACHETYPE_PORTAL ) {
  566. portalCacheIndex[cache->areaNum] = cache->next;
  567. }
  568. delete cache;
  569. }
  570. /*
  571. ============
  572. idAASLocal::GetAreaReachability
  573. ============
  574. */
  575. idReachability *idAASLocal::GetAreaReachability( int areaNum, int reachabilityNum ) const {
  576. idReachability *reach;
  577. for ( reach = file->GetArea( areaNum ).reach; reach; reach = reach->next ) {
  578. if ( --reachabilityNum < 0 ) {
  579. return reach;
  580. }
  581. }
  582. return NULL;
  583. }
  584. /*
  585. ============
  586. idAASLocal::ClusterAreaNum
  587. ============
  588. */
  589. ID_INLINE int idAASLocal::ClusterAreaNum( int clusterNum, int areaNum ) const {
  590. int side, areaCluster;
  591. areaCluster = file->GetArea( areaNum ).cluster;
  592. if ( areaCluster > 0 ) {
  593. return file->GetArea( areaNum ).clusterAreaNum;
  594. }
  595. else {
  596. side = file->GetPortal( -areaCluster ).clusters[0] != clusterNum;
  597. return file->GetPortal( -areaCluster ).clusterAreaNum[side];
  598. }
  599. }
  600. /*
  601. ============
  602. idAASLocal::UpdateAreaRoutingCache
  603. ============
  604. */
  605. void idAASLocal::UpdateAreaRoutingCache( idRoutingCache *areaCache ) const {
  606. int i, nextAreaNum, cluster, badTravelFlags, clusterAreaNum, numReachableAreas;
  607. unsigned short t, startAreaTravelTimes[MAX_REACH_PER_AREA];
  608. idRoutingUpdate *updateListStart, *updateListEnd, *curUpdate, *nextUpdate;
  609. idReachability *reach;
  610. const aasArea_t *nextArea;
  611. // number of reachability areas within this cluster
  612. numReachableAreas = file->GetCluster( areaCache->cluster ).numReachableAreas;
  613. // number of the start area within the cluster
  614. clusterAreaNum = ClusterAreaNum( areaCache->cluster, areaCache->areaNum );
  615. if ( clusterAreaNum >= numReachableAreas ) {
  616. return;
  617. }
  618. areaCache->travelTimes[clusterAreaNum] = areaCache->startTravelTime;
  619. badTravelFlags = ~areaCache->travelFlags;
  620. memset( startAreaTravelTimes, 0, sizeof( startAreaTravelTimes ) );
  621. // initialize first update
  622. curUpdate = &areaUpdate[clusterAreaNum];
  623. curUpdate->areaNum = areaCache->areaNum;
  624. curUpdate->areaTravelTimes = startAreaTravelTimes;
  625. curUpdate->tmpTravelTime = areaCache->startTravelTime;
  626. curUpdate->next = NULL;
  627. curUpdate->prev = NULL;
  628. updateListStart = curUpdate;
  629. updateListEnd = curUpdate;
  630. // while there are updates in the list
  631. while( updateListStart ) {
  632. curUpdate = updateListStart;
  633. if ( curUpdate->next ) {
  634. curUpdate->next->prev = NULL;
  635. }
  636. else {
  637. updateListEnd = NULL;
  638. }
  639. updateListStart = curUpdate->next;
  640. curUpdate->isInList = false;
  641. for ( i = 0, reach = file->GetArea( curUpdate->areaNum ).rev_reach; reach; reach = reach->rev_next, i++ ) {
  642. // if the reachability uses an undesired travel type
  643. if ( reach->travelType & badTravelFlags ) {
  644. continue;
  645. }
  646. // next area the reversed reachability leads to
  647. nextAreaNum = reach->fromAreaNum;
  648. nextArea = &file->GetArea( nextAreaNum );
  649. // if traveling through the next area requires an undesired travel flag
  650. if ( nextArea->travelFlags & badTravelFlags ) {
  651. continue;
  652. }
  653. // get the cluster number of the area
  654. cluster = nextArea->cluster;
  655. // don't leave the cluster, however do flood into cluster portals
  656. if ( cluster > 0 && cluster != areaCache->cluster ) {
  657. continue;
  658. }
  659. // get the number of the area in the cluster
  660. clusterAreaNum = ClusterAreaNum( areaCache->cluster, nextAreaNum );
  661. if ( clusterAreaNum >= numReachableAreas ) {
  662. continue; // should never happen
  663. }
  664. assert( clusterAreaNum < areaCache->size );
  665. // time already travelled plus the traveltime through the current area
  666. // plus the travel time of the reachability towards the next area
  667. t = curUpdate->tmpTravelTime + curUpdate->areaTravelTimes[i] + reach->travelTime;
  668. if ( !areaCache->travelTimes[clusterAreaNum] || t < areaCache->travelTimes[clusterAreaNum] ) {
  669. areaCache->travelTimes[clusterAreaNum] = t;
  670. areaCache->reachabilities[clusterAreaNum] = reach->number; // reversed reachability used to get into this area
  671. nextUpdate = &areaUpdate[clusterAreaNum];
  672. nextUpdate->areaNum = nextAreaNum;
  673. nextUpdate->tmpTravelTime = t;
  674. nextUpdate->areaTravelTimes = reach->areaTravelTimes;
  675. // if we are not allowed to fly
  676. if ( badTravelFlags & TFL_FLY ) {
  677. // avoid areas near ledges
  678. if ( file->GetArea( nextAreaNum ).flags & AREA_LEDGE ) {
  679. nextUpdate->tmpTravelTime += LEDGE_TRAVELTIME_PANALTY;
  680. }
  681. }
  682. if ( !nextUpdate->isInList ) {
  683. nextUpdate->next = NULL;
  684. nextUpdate->prev = updateListEnd;
  685. if ( updateListEnd ) {
  686. updateListEnd->next = nextUpdate;
  687. }
  688. else {
  689. updateListStart = nextUpdate;
  690. }
  691. updateListEnd = nextUpdate;
  692. nextUpdate->isInList = true;
  693. }
  694. }
  695. }
  696. }
  697. }
  698. /*
  699. ============
  700. idAASLocal::GetAreaRoutingCache
  701. ============
  702. */
  703. idRoutingCache *idAASLocal::GetAreaRoutingCache( int clusterNum, int areaNum, int travelFlags ) const {
  704. int clusterAreaNum;
  705. idRoutingCache *cache, *clusterCache;
  706. // number of the area in the cluster
  707. clusterAreaNum = ClusterAreaNum( clusterNum, areaNum );
  708. // pointer to the cache for the area in the cluster
  709. clusterCache = areaCacheIndex[clusterNum][clusterAreaNum];
  710. // check if cache without undesired travel flags already exists
  711. for ( cache = clusterCache; cache; cache = cache->next ) {
  712. if ( cache->travelFlags == travelFlags ) {
  713. break;
  714. }
  715. }
  716. // if no cache found
  717. if ( !cache ) {
  718. cache = new idRoutingCache( file->GetCluster( clusterNum ).numReachableAreas );
  719. cache->type = CACHETYPE_AREA;
  720. cache->cluster = clusterNum;
  721. cache->areaNum = areaNum;
  722. cache->startTravelTime = 1;
  723. cache->travelFlags = travelFlags;
  724. cache->prev = NULL;
  725. cache->next = clusterCache;
  726. if ( clusterCache ) {
  727. clusterCache->prev = cache;
  728. }
  729. areaCacheIndex[clusterNum][clusterAreaNum] = cache;
  730. UpdateAreaRoutingCache( cache );
  731. }
  732. LinkCache( cache );
  733. return cache;
  734. }
  735. /*
  736. ============
  737. idAASLocal::UpdatePortalRoutingCache
  738. ============
  739. */
  740. void idAASLocal::UpdatePortalRoutingCache( idRoutingCache *portalCache ) const {
  741. int i, portalNum, clusterAreaNum;
  742. unsigned short t;
  743. const aasPortal_t *portal;
  744. const aasCluster_t *cluster;
  745. idRoutingCache *cache;
  746. idRoutingUpdate *updateListStart, *updateListEnd, *curUpdate, *nextUpdate;
  747. curUpdate = &portalUpdate[ file->GetNumPortals() ];
  748. curUpdate->cluster = portalCache->cluster;
  749. curUpdate->areaNum = portalCache->areaNum;
  750. curUpdate->tmpTravelTime = portalCache->startTravelTime;
  751. //put the area to start with in the current read list
  752. curUpdate->next = NULL;
  753. curUpdate->prev = NULL;
  754. updateListStart = curUpdate;
  755. updateListEnd = curUpdate;
  756. // while there are updates in the current list
  757. while( updateListStart ) {
  758. curUpdate = updateListStart;
  759. // remove the current update from the list
  760. if ( curUpdate->next ) {
  761. curUpdate->next->prev = NULL;
  762. }
  763. else {
  764. updateListEnd = NULL;
  765. }
  766. updateListStart = curUpdate->next;
  767. // current update is removed from the list
  768. curUpdate->isInList = false;
  769. cluster = &file->GetCluster( curUpdate->cluster );
  770. cache = GetAreaRoutingCache( curUpdate->cluster, curUpdate->areaNum, portalCache->travelFlags );
  771. // take all portals of the cluster
  772. for ( i = 0; i < cluster->numPortals; i++ ) {
  773. portalNum = file->GetPortalIndex( cluster->firstPortal + i );
  774. assert( portalNum < portalCache->size );
  775. portal = &file->GetPortal( portalNum );
  776. clusterAreaNum = ClusterAreaNum( curUpdate->cluster, portal->areaNum );
  777. if ( clusterAreaNum >= cluster->numReachableAreas ) {
  778. continue;
  779. }
  780. t = cache->travelTimes[clusterAreaNum];
  781. if ( t == 0 ) {
  782. continue;
  783. }
  784. t += curUpdate->tmpTravelTime;
  785. if ( !portalCache->travelTimes[portalNum] || t < portalCache->travelTimes[portalNum] ) {
  786. portalCache->travelTimes[portalNum] = t;
  787. portalCache->reachabilities[portalNum] = cache->reachabilities[clusterAreaNum];
  788. nextUpdate = &portalUpdate[portalNum];
  789. if ( portal->clusters[0] == curUpdate->cluster ) {
  790. nextUpdate->cluster = portal->clusters[1];
  791. }
  792. else {
  793. nextUpdate->cluster = portal->clusters[0];
  794. }
  795. nextUpdate->areaNum = portal->areaNum;
  796. // add travel time through the actual portal area for the next update
  797. nextUpdate->tmpTravelTime = t + portal->maxAreaTravelTime;
  798. if ( !nextUpdate->isInList ) {
  799. nextUpdate->next = NULL;
  800. nextUpdate->prev = updateListEnd;
  801. if ( updateListEnd ) {
  802. updateListEnd->next = nextUpdate;
  803. }
  804. else {
  805. updateListStart = nextUpdate;
  806. }
  807. updateListEnd = nextUpdate;
  808. nextUpdate->isInList = true;
  809. }
  810. }
  811. }
  812. }
  813. }
  814. /*
  815. ============
  816. idAASLocal::GetPortalRoutingCache
  817. ============
  818. */
  819. idRoutingCache *idAASLocal::GetPortalRoutingCache( int clusterNum, int areaNum, int travelFlags ) const {
  820. idRoutingCache *cache;
  821. // check if cache without undesired travel flags already exists
  822. for ( cache = portalCacheIndex[areaNum]; cache; cache = cache->next ) {
  823. if ( cache->travelFlags == travelFlags ) {
  824. break;
  825. }
  826. }
  827. // if no cache found
  828. if ( !cache ) {
  829. cache = new idRoutingCache( file->GetNumPortals() );
  830. cache->type = CACHETYPE_PORTAL;
  831. cache->cluster = clusterNum;
  832. cache->areaNum = areaNum;
  833. cache->startTravelTime = 1;
  834. cache->travelFlags = travelFlags;
  835. cache->prev = NULL;
  836. cache->next = portalCacheIndex[areaNum];
  837. if ( portalCacheIndex[areaNum] ) {
  838. portalCacheIndex[areaNum]->prev = cache;
  839. }
  840. portalCacheIndex[areaNum] = cache;
  841. UpdatePortalRoutingCache( cache );
  842. }
  843. LinkCache( cache );
  844. return cache;
  845. }
  846. /*
  847. ============
  848. idAASLocal::RouteToGoalArea
  849. ============
  850. */
  851. bool idAASLocal::RouteToGoalArea( int areaNum, const idVec3 origin, int goalAreaNum, int travelFlags, int &travelTime, idReachability **reach ) const {
  852. int clusterNum, goalClusterNum, portalNum, i, clusterAreaNum;
  853. unsigned short int t, bestTime;
  854. const aasPortal_t *portal;
  855. const aasCluster_t *cluster;
  856. idRoutingCache *areaCache, *portalCache, *clusterCache;
  857. idReachability *bestReach, *r, *nextr;
  858. travelTime = 0;
  859. *reach = NULL;
  860. if ( !file ) {
  861. return false;
  862. }
  863. if ( areaNum == goalAreaNum ) {
  864. return true;
  865. }
  866. if ( areaNum <= 0 || areaNum >= file->GetNumAreas() ) {
  867. gameLocal.Printf( "RouteToGoalArea: areaNum %d out of range\n", areaNum );
  868. return false;
  869. }
  870. if ( goalAreaNum <= 0 || goalAreaNum >= file->GetNumAreas() ) {
  871. gameLocal.Printf( "RouteToGoalArea: goalAreaNum %d out of range\n", goalAreaNum );
  872. return false;
  873. }
  874. while( totalCacheMemory > MAX_ROUTING_CACHE_MEMORY ) {
  875. DeleteOldestCache();
  876. }
  877. clusterNum = file->GetArea( areaNum ).cluster;
  878. goalClusterNum = file->GetArea( goalAreaNum ).cluster;
  879. // if the source area is a cluster portal, read directly from the portal cache
  880. if ( clusterNum < 0 ) {
  881. // if the goal area is a portal
  882. if ( goalClusterNum < 0 ) {
  883. // just assume the goal area is part of the front cluster
  884. portal = &file->GetPortal( -goalClusterNum );
  885. goalClusterNum = portal->clusters[0];
  886. }
  887. // get the portal routing cache
  888. portalCache = GetPortalRoutingCache( goalClusterNum, goalAreaNum, travelFlags );
  889. *reach = GetAreaReachability( areaNum, portalCache->reachabilities[-clusterNum] );
  890. travelTime = portalCache->travelTimes[-clusterNum] + AreaTravelTime( areaNum, origin, (*reach)->start );
  891. return true;
  892. }
  893. bestTime = 0;
  894. bestReach = NULL;
  895. // check if the goal area is a portal of the source area cluster
  896. if ( goalClusterNum < 0 ) {
  897. portal = &file->GetPortal( -goalClusterNum );
  898. if ( portal->clusters[0] == clusterNum || portal->clusters[1] == clusterNum) {
  899. goalClusterNum = clusterNum;
  900. }
  901. }
  902. // if both areas are in the same cluster
  903. if ( clusterNum > 0 && goalClusterNum > 0 && clusterNum == goalClusterNum ) {
  904. clusterCache = GetAreaRoutingCache( clusterNum, goalAreaNum, travelFlags );
  905. clusterAreaNum = ClusterAreaNum( clusterNum, areaNum );
  906. if ( clusterCache->travelTimes[clusterAreaNum] ) {
  907. bestReach = GetAreaReachability( areaNum, clusterCache->reachabilities[clusterAreaNum] );
  908. bestTime = clusterCache->travelTimes[clusterAreaNum] + AreaTravelTime( areaNum, origin, bestReach->start );
  909. }
  910. else {
  911. clusterCache = NULL;
  912. }
  913. }
  914. else {
  915. clusterCache = NULL;
  916. }
  917. clusterNum = file->GetArea( areaNum ).cluster;
  918. goalClusterNum = file->GetArea( goalAreaNum ).cluster;
  919. // if the goal area is a portal
  920. if ( goalClusterNum < 0 ) {
  921. // just assume the goal area is part of the front cluster
  922. portal = &file->GetPortal( -goalClusterNum );
  923. goalClusterNum = portal->clusters[0];
  924. }
  925. // get the portal routing cache
  926. portalCache = GetPortalRoutingCache( goalClusterNum, goalAreaNum, travelFlags );
  927. // the cluster the area is in
  928. cluster = &file->GetCluster( clusterNum );
  929. // current area inside the current cluster
  930. clusterAreaNum = ClusterAreaNum( clusterNum, areaNum );
  931. // if the area is not a reachable area
  932. if ( clusterAreaNum >= cluster->numReachableAreas) {
  933. return false;
  934. }
  935. // find the portal of the source area cluster leading towards the goal area
  936. for ( i = 0; i < cluster->numPortals; i++ ) {
  937. portalNum = file->GetPortalIndex( cluster->firstPortal + i );
  938. // if the goal area isn't reachable from the portal
  939. if ( !portalCache->travelTimes[portalNum] ) {
  940. continue;
  941. }
  942. portal = &file->GetPortal( portalNum );
  943. // get the cache of the portal area
  944. areaCache = GetAreaRoutingCache( clusterNum, portal->areaNum, travelFlags );
  945. // if the portal is not reachable from this area
  946. if ( !areaCache->travelTimes[clusterAreaNum] ) {
  947. continue;
  948. }
  949. r = GetAreaReachability( areaNum, areaCache->reachabilities[clusterAreaNum] );
  950. if ( clusterCache ) {
  951. // if the next reachability from the portal leads back into the cluster
  952. nextr = GetAreaReachability( portal->areaNum, portalCache->reachabilities[portalNum] );
  953. if ( file->GetArea( nextr->toAreaNum ).cluster < 0 || file->GetArea( nextr->toAreaNum ).cluster == clusterNum ) {
  954. continue;
  955. }
  956. }
  957. // the total travel time is the travel time from the portal area to the goal area
  958. // plus the travel time from the source area towards the portal area
  959. t = portalCache->travelTimes[portalNum] + areaCache->travelTimes[clusterAreaNum];
  960. // NOTE: Should add the exact travel time through the portal area.
  961. // However we add the largest travel time through the portal area.
  962. // We cannot directly calculate the exact travel time through the portal area
  963. // because the reachability used to travel into the portal area is not known.
  964. t += portal->maxAreaTravelTime;
  965. // if the time is better than the one already found
  966. if ( !bestTime || t < bestTime ) {
  967. bestReach = r;
  968. bestTime = t;
  969. }
  970. }
  971. if ( !bestReach ) {
  972. return false;
  973. }
  974. *reach = bestReach;
  975. travelTime = bestTime;
  976. return true;
  977. }
  978. /*
  979. ============
  980. idAASLocal::TravelTimeToGoalArea
  981. ============
  982. */
  983. int idAASLocal::TravelTimeToGoalArea( int areaNum, const idVec3 &origin, int goalAreaNum, int travelFlags ) const {
  984. int travelTime;
  985. idReachability *reach;
  986. if ( !file ) {
  987. return 0;
  988. }
  989. if ( !RouteToGoalArea( areaNum, origin, goalAreaNum, travelFlags, travelTime, &reach ) ) {
  990. return 0;
  991. }
  992. return travelTime;
  993. }
  994. /*
  995. ============
  996. idAASLocal::FindNearestGoal
  997. ============
  998. */
  999. bool idAASLocal::FindNearestGoal( aasGoal_t &goal, int areaNum, const idVec3 origin, const idVec3 &target, int travelFlags, aasObstacle_t *obstacles, int numObstacles, idAASCallback &callback ) const {
  1000. int i, j, k, badTravelFlags, nextAreaNum, bestAreaNum;
  1001. unsigned short t, bestTravelTime;
  1002. idRoutingUpdate *updateListStart, *updateListEnd, *curUpdate, *nextUpdate;
  1003. idReachability *reach;
  1004. const aasArea_t *nextArea;
  1005. idVec3 v1, v2, p;
  1006. float targetDist, dist;
  1007. if ( file == NULL || areaNum <= 0 ) {
  1008. goal.areaNum = areaNum;
  1009. goal.origin = origin;
  1010. return false;
  1011. }
  1012. // if the first area is valid goal, just return the origin
  1013. if ( callback.TestArea( this, areaNum ) ) {
  1014. goal.areaNum = areaNum;
  1015. goal.origin = origin;
  1016. return true;
  1017. }
  1018. // setup obstacles
  1019. for ( k = 0; k < numObstacles; k++ ) {
  1020. obstacles[k].expAbsBounds[0] = obstacles[k].absBounds[0] - file->GetSettings().boundingBoxes[0][1];
  1021. obstacles[k].expAbsBounds[1] = obstacles[k].absBounds[1] - file->GetSettings().boundingBoxes[0][0];
  1022. }
  1023. badTravelFlags = ~travelFlags;
  1024. SIMDProcessor->Memset( goalAreaTravelTimes, 0, file->GetNumAreas() * sizeof( unsigned short ) );
  1025. targetDist = (target - origin).Length();
  1026. // initialize first update
  1027. curUpdate = &areaUpdate[areaNum];
  1028. curUpdate->areaNum = areaNum;
  1029. curUpdate->tmpTravelTime = 0;
  1030. curUpdate->start = origin;
  1031. curUpdate->next = NULL;
  1032. curUpdate->prev = NULL;
  1033. updateListStart = curUpdate;
  1034. updateListEnd = curUpdate;
  1035. bestTravelTime = 0;
  1036. bestAreaNum = 0;
  1037. // while there are updates in the list
  1038. while ( updateListStart ) {
  1039. curUpdate = updateListStart;
  1040. if ( curUpdate->next ) {
  1041. curUpdate->next->prev = NULL;
  1042. }
  1043. else {
  1044. updateListEnd = NULL;
  1045. }
  1046. updateListStart = curUpdate->next;
  1047. curUpdate->isInList = false;
  1048. // if we already found a closer location
  1049. if ( bestTravelTime && curUpdate->tmpTravelTime >= bestTravelTime ) {
  1050. continue;
  1051. }
  1052. for ( i = 0, reach = file->GetArea( curUpdate->areaNum ).reach; reach; reach = reach->next, i++ ) {
  1053. // if the reachability uses an undesired travel type
  1054. if ( reach->travelType & badTravelFlags ) {
  1055. continue;
  1056. }
  1057. // next area the reversed reachability leads to
  1058. nextAreaNum = reach->toAreaNum;
  1059. nextArea = &file->GetArea( nextAreaNum );
  1060. // if traveling through the next area requires an undesired travel flag
  1061. if ( nextArea->travelFlags & badTravelFlags ) {
  1062. continue;
  1063. }
  1064. t = curUpdate->tmpTravelTime +
  1065. AreaTravelTime( curUpdate->areaNum, curUpdate->start, reach->start ) +
  1066. reach->travelTime;
  1067. // project target origin onto movement vector through the area
  1068. v1 = reach->end - curUpdate->start;
  1069. v1.Normalize();
  1070. v2 = target - curUpdate->start;
  1071. p = curUpdate->start + (v2 * v1) * v1;
  1072. // get the point on the path closest to the target
  1073. for ( j = 0; j < 3; j++ ) {
  1074. if ( (p[j] > curUpdate->start[j] + 0.1f && p[j] > reach->end[j] + 0.1f) ||
  1075. (p[j] < curUpdate->start[j] - 0.1f && p[j] < reach->end[j] - 0.1f) ) {
  1076. break;
  1077. }
  1078. }
  1079. if ( j >= 3 ) {
  1080. dist = (target - p).Length();
  1081. } else {
  1082. dist = (target - reach->end).Length();
  1083. }
  1084. // avoid moving closer to the target
  1085. if ( dist < targetDist ) {
  1086. t += ( targetDist - dist ) * 10;
  1087. }
  1088. // if we already found a closer location
  1089. if ( bestTravelTime && t >= bestTravelTime ) {
  1090. continue;
  1091. }
  1092. // if this is not the best path towards the next area
  1093. if ( goalAreaTravelTimes[nextAreaNum] && t >= goalAreaTravelTimes[nextAreaNum] ) {
  1094. continue;
  1095. }
  1096. // path may not go through any obstacles
  1097. for ( k = 0; k < numObstacles; k++ ) {
  1098. // if the movement vector intersects the expanded obstacle bounds
  1099. if ( obstacles[k].expAbsBounds.LineIntersection( curUpdate->start, reach->end ) ) {
  1100. break;
  1101. }
  1102. }
  1103. if ( k < numObstacles ) {
  1104. continue;
  1105. }
  1106. goalAreaTravelTimes[nextAreaNum] = t;
  1107. nextUpdate = &areaUpdate[nextAreaNum];
  1108. nextUpdate->areaNum = nextAreaNum;
  1109. nextUpdate->tmpTravelTime = t;
  1110. nextUpdate->start = reach->end;
  1111. // if we are not allowed to fly
  1112. if ( badTravelFlags & TFL_FLY ) {
  1113. // avoid areas near ledges
  1114. if ( file->GetArea( nextAreaNum ).flags & AREA_LEDGE ) {
  1115. nextUpdate->tmpTravelTime += LEDGE_TRAVELTIME_PANALTY;
  1116. }
  1117. }
  1118. if ( !nextUpdate->isInList ) {
  1119. nextUpdate->next = NULL;
  1120. nextUpdate->prev = updateListEnd;
  1121. if ( updateListEnd ) {
  1122. updateListEnd->next = nextUpdate;
  1123. } else {
  1124. updateListStart = nextUpdate;
  1125. }
  1126. updateListEnd = nextUpdate;
  1127. nextUpdate->isInList = true;
  1128. }
  1129. // don't put goal near a ledge
  1130. if ( !( nextArea->flags & AREA_LEDGE ) ) {
  1131. // add travel time through the area
  1132. t += AreaTravelTime( reach->toAreaNum, reach->end, nextArea->center );
  1133. if ( !bestTravelTime || t < bestTravelTime ) {
  1134. // if the area is not visible to the target
  1135. if ( callback.TestArea( this, reach->toAreaNum ) ) {
  1136. bestTravelTime = t;
  1137. bestAreaNum = reach->toAreaNum;
  1138. }
  1139. }
  1140. }
  1141. }
  1142. }
  1143. if ( bestAreaNum ) {
  1144. goal.areaNum = bestAreaNum;
  1145. goal.origin = AreaCenter( bestAreaNum );
  1146. return true;
  1147. }
  1148. return false;
  1149. }