DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

AASFile.cpp (33890B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 
     33 #include "AASFile.h"
     34 #include "AASFile_local.h"
     35 
     36 
     37 /*
     38 ===============================================================================
     39 
     40 	idReachability
     41 
     42 ===============================================================================
     43 */
     44 
     45 /*
     46 ================
     47 Reachability_Write
     48 ================
     49 */
     50 bool Reachability_Write( idFile *fp, idReachability *reach ) {
     51 	fp->WriteFloatString( "\t\t%d %d (%f %f %f) (%f %f %f) %d %d",
     52 				(int) reach->travelType, (int) reach->toAreaNum, reach->start.x, reach->start.y, reach->start.z,
     53 				reach->end.x, reach->end.y, reach->end.z, reach->edgeNum, (int) reach->travelTime );
     54 	return true;
     55 }
     56 
     57 /*
     58 ================
     59 Reachability_Read
     60 ================
     61 */
     62 bool Reachability_Read( idLexer &src, idReachability *reach ) {
     63 	reach->travelType = src.ParseInt();
     64 	reach->toAreaNum = src.ParseInt();
     65 	src.Parse1DMatrix( 3, reach->start.ToFloatPtr() );
     66 	src.Parse1DMatrix( 3, reach->end.ToFloatPtr() );
     67 	reach->edgeNum = src.ParseInt();
     68 	reach->travelTime = src.ParseInt();
     69 	return true;
     70 }
     71 
     72 /*
     73 ================
     74 idReachability::CopyBase
     75 ================
     76 */
     77 void idReachability::CopyBase( idReachability &reach ) {
     78 	travelType = reach.travelType;
     79 	toAreaNum = reach.toAreaNum;
     80 	start = reach.start;
     81 	end = reach.end;
     82 	edgeNum = reach.edgeNum;
     83 	travelTime = reach.travelTime;
     84 }
     85 
     86 
     87 /*
     88 ===============================================================================
     89 
     90 	idReachability_Special
     91 
     92 ===============================================================================
     93 */
     94 
     95 /*
     96 ================
     97 Reachability_Special_Write
     98 ================
     99 */
    100 bool Reachability_Special_Write( idFile *fp, idReachability_Special *reach ) {
    101 	int i;
    102 	const idKeyValue *keyValue;
    103 
    104 	fp->WriteFloatString( "\n\t\t{\n" );
    105 	for ( i = 0; i < reach->dict.GetNumKeyVals(); i++ ) {
    106 		keyValue = reach->dict.GetKeyVal( i );
    107 		fp->WriteFloatString( "\t\t\t\"%s\" \"%s\"\n", keyValue->GetKey().c_str(), keyValue->GetValue().c_str() );
    108 	}
    109 	fp->WriteFloatString( "\t\t}\n" );
    110 
    111 	return true;
    112 }
    113 
    114 /*
    115 ================
    116 Reachability_Special_Read
    117 ================
    118 */
    119 bool Reachability_Special_Read( idLexer &src, idReachability_Special *reach ) {
    120 	idToken key, value;
    121 
    122 	src.ExpectTokenString( "{" );
    123 	while( src.ReadToken( &key ) ) {
    124 		if ( key == "}" ) {
    125 			return true;
    126 		}
    127 		src.ExpectTokenType( TT_STRING, 0, &value );
    128 		reach->dict.Set( key, value );
    129 	}
    130 	return false;
    131 }
    132 
    133 /*
    134 ===============================================================================
    135 
    136 	idAASSettings
    137 
    138 ===============================================================================
    139 */
    140 
    141 /*
    142 ============
    143 idAASSettings::idAASSettings
    144 ============
    145 */
    146 idAASSettings::idAASSettings() {
    147 	numBoundingBoxes = 1;
    148 	boundingBoxes[0] = idBounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 72 ) );
    149 	usePatches = false;
    150 	writeBrushMap = false;
    151 	playerFlood = false;
    152 	noOptimize = false;
    153 	allowSwimReachabilities = false;
    154 	allowFlyReachabilities = false;
    155 	fileExtension = "aas48";
    156 	// physics settings
    157 	gravity = idVec3( 0, 0, -1066 );
    158 	gravityDir = gravity;
    159 	gravityValue = gravityDir.Normalize();
    160 	invGravityDir = -gravityDir;
    161 	maxStepHeight = 14.0f;
    162 	maxBarrierHeight = 32.0f;
    163 	maxWaterJumpHeight = 20.0f;
    164 	maxFallHeight = 64.0f;
    165 	minFloorCos = 0.7f;
    166 	// fixed travel times
    167 	tt_barrierJump = 100;
    168 	tt_startCrouching = 100;
    169 	tt_waterJump = 100;
    170 	tt_startWalkOffLedge = 100;
    171 }
    172 
    173 /*
    174 ============
    175 idAASSettings::ParseBool
    176 ============
    177 */
    178 bool idAASSettings::ParseBool( idLexer &src, bool &b ) {
    179 	if ( !src.ExpectTokenString( "=" ) ) {
    180 		return false;
    181 	}
    182 	b = src.ParseBool();
    183 	return true;
    184 }
    185 
    186 /*
    187 ============
    188 idAASSettings::ParseInt
    189 ============
    190 */
    191 bool idAASSettings::ParseInt( idLexer &src, int &i ) {
    192 	if ( !src.ExpectTokenString( "=" ) ) {
    193 		return false;
    194 	}
    195 	i = src.ParseInt();
    196 	return true;
    197 }
    198 
    199 /*
    200 ============
    201 idAASSettings::ParseFloat
    202 ============
    203 */
    204 bool idAASSettings::ParseFloat( idLexer &src, float &f ) {
    205 	if ( !src.ExpectTokenString( "=" ) ) {
    206 		return false;
    207 	}
    208 	f = src.ParseFloat();
    209 	return true;
    210 }
    211 
    212 /*
    213 ============
    214 idAASSettings::ParseVector
    215 ============
    216 */
    217 bool idAASSettings::ParseVector( idLexer &src, idVec3 &vec ) {
    218 	if ( !src.ExpectTokenString( "=" ) ) {
    219 		return false;
    220 	}
    221 	return ( src.Parse1DMatrix( 3, vec.ToFloatPtr() ) != 0 );
    222 }
    223 
    224 /*
    225 ============
    226 idAASSettings::ParseBBoxes
    227 ============
    228 */
    229 bool idAASSettings::ParseBBoxes( idLexer &src ) {
    230 	idToken token;
    231 	idBounds bounds;
    232 
    233 	numBoundingBoxes = 0;
    234 
    235 	if ( !src.ExpectTokenString( "{" ) ) {
    236 		return false;
    237 	}
    238 	while( src.ReadToken( &token ) ) {
    239 		if ( token == "}" ) {
    240 			return true;
    241 		}
    242 		src.UnreadToken( &token );
    243 		src.Parse1DMatrix( 3, bounds[0].ToFloatPtr() );
    244 		if ( !src.ExpectTokenString( "-" ) ) {
    245 			return false;
    246 		}
    247 		src.Parse1DMatrix( 3, bounds[1].ToFloatPtr() );
    248 
    249 		boundingBoxes[numBoundingBoxes++] = bounds;
    250 	}
    251 	return false;
    252 }
    253 
    254 /*
    255 ============
    256 idAASSettings::FromParser
    257 ============
    258 */
    259 bool idAASSettings::FromParser( idLexer &src ) {
    260 	idToken token;
    261 
    262 	if ( !src.ExpectTokenString( "{" ) ) {
    263 		return false;
    264 	}
    265 
    266 	// parse the file
    267 	while ( 1 ) {
    268 		if ( !src.ReadToken( &token ) ) {
    269 			break;
    270 		}
    271 
    272 		if ( token == "}" ) {
    273 			break;
    274 		}
    275 
    276 		if ( token == "bboxes" ) {
    277 			if ( !ParseBBoxes( src ) ) { return false; }
    278 		}
    279 		else if ( token == "usePatches" ) {
    280 			if ( !ParseBool( src, usePatches ) ) { return false; }
    281 		}
    282 		else if ( token == "writeBrushMap" ) {
    283 			if ( !ParseBool( src, writeBrushMap ) ) { return false; }
    284 		}
    285 		else if ( token == "playerFlood" ) {
    286 			if ( !ParseBool( src, playerFlood ) ) { return false; }
    287 		}
    288 		else if ( token == "allowSwimReachabilities" ) {
    289 			if ( !ParseBool( src, allowSwimReachabilities ) ) { return false; }
    290 		}
    291 		else if ( token == "allowFlyReachabilities" ) {
    292 			if ( !ParseBool( src, allowFlyReachabilities ) ) { return false; }
    293 		}
    294 		else if ( token == "fileExtension" ) {
    295 			src.ExpectTokenString( "=" );
    296 			src.ExpectTokenType( TT_STRING, 0, &token );
    297 			fileExtension = token;
    298 		}
    299 		else if ( token == "gravity" ) {
    300 			ParseVector( src, gravity );
    301 			gravityDir = gravity;
    302 			gravityValue = gravityDir.Normalize();
    303 			invGravityDir = -gravityDir;
    304 		}
    305 		else if ( token == "maxStepHeight" ) {
    306 			if ( !ParseFloat( src, maxStepHeight ) ) { return false; }
    307 		}
    308 		else if ( token == "maxBarrierHeight" ) {
    309 			if ( !ParseFloat( src, maxBarrierHeight ) ) { return false; }
    310 		}
    311 		else if ( token == "maxWaterJumpHeight" ) {
    312 			if ( !ParseFloat( src, maxWaterJumpHeight ) ) { return false; }
    313 		}
    314 		else if ( token == "maxFallHeight" ) {
    315 			if ( !ParseFloat( src, maxFallHeight ) ) { return false; }
    316 		}
    317 		else if ( token == "minFloorCos" ) {
    318 			if ( !ParseFloat( src, minFloorCos ) ) { return false; }
    319 		}
    320 		else if ( token == "tt_barrierJump" ) {
    321 			if ( !ParseInt( src, tt_barrierJump ) ) { return false; }
    322 		}
    323 		else if ( token == "tt_startCrouching" ) {
    324 			if ( !ParseInt( src, tt_startCrouching ) ) { return false; }
    325 		}
    326 		else if ( token == "tt_waterJump" ) {
    327 			if ( !ParseInt( src, tt_waterJump ) ) { return false; }
    328 		}
    329 		else if ( token == "tt_startWalkOffLedge" ) {
    330 			if ( !ParseInt( src, tt_startWalkOffLedge ) ) { return false; }
    331 		}
    332 		else {
    333 			src.Error( "invalid token '%s'", token.c_str() );
    334 		}
    335 	}
    336 
    337 	if ( numBoundingBoxes <= 0 ) {
    338 		src.Error( "no valid bounding box" );
    339 	}
    340 
    341 	return true;
    342 }
    343 
    344 /*
    345 ============
    346 idAASSettings::FromFile
    347 ============
    348 */
    349 bool idAASSettings::FromFile( const idStr &fileName ) {
    350 	idLexer src( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT );
    351 	idStr name;
    352 
    353 	name = fileName;
    354 
    355 	common->Printf( "loading %s\n", name.c_str() );
    356 
    357 	if ( !src.LoadFile( name ) ) {
    358 		common->Error( "WARNING: couldn't load %s\n", name.c_str() );
    359 		return false;
    360 	}
    361 
    362 	if ( !src.ExpectTokenString( "settings" ) ) {
    363 		common->Error( "%s is not a settings file", name.c_str() );
    364 		return false;
    365 	}
    366 
    367 	if ( !FromParser( src ) ) {
    368 		common->Error( "failed to parse %s", name.c_str() );
    369 		return false;
    370 	}
    371 
    372 	return true;
    373 }
    374 
    375 /*
    376 ============
    377 idAASSettings::FromDict
    378 ============
    379 */
    380 bool idAASSettings::FromDict( const char *name, const idDict *dict ) {
    381 	idBounds bounds;
    382 
    383 	if ( !dict->GetVector( "mins", "0 0 0", bounds[ 0 ] ) ) {
    384 		common->Error( "Missing 'mins' in entityDef '%s'", name );
    385 	}
    386 	if ( !dict->GetVector( "maxs", "0 0 0", bounds[ 1 ] ) ) {
    387 		common->Error( "Missing 'maxs' in entityDef '%s'", name );
    388 	}
    389 
    390 	numBoundingBoxes = 1;
    391 	boundingBoxes[0] = bounds;
    392 
    393 	if ( !dict->GetBool( "usePatches", "0", usePatches ) ) {
    394 		common->Error( "Missing 'usePatches' in entityDef '%s'", name );
    395 	}
    396 
    397 	if ( !dict->GetBool( "writeBrushMap", "0", writeBrushMap ) ) {
    398 		common->Error( "Missing 'writeBrushMap' in entityDef '%s'", name );
    399 	}
    400 
    401 	if ( !dict->GetBool( "playerFlood", "0", playerFlood ) ) {
    402 		common->Error( "Missing 'playerFlood' in entityDef '%s'", name );
    403 	}
    404 
    405 	if ( !dict->GetBool( "allowSwimReachabilities", "0", allowSwimReachabilities ) ) {
    406 		common->Error( "Missing 'allowSwimReachabilities' in entityDef '%s'", name );
    407 	}
    408 
    409 	if ( !dict->GetBool( "allowFlyReachabilities", "0", allowFlyReachabilities ) ) {
    410 		common->Error( "Missing 'allowFlyReachabilities' in entityDef '%s'", name );
    411 	}
    412 
    413 	if ( !dict->GetString( "fileExtension", "", fileExtension ) ) {
    414 		common->Error( "Missing 'fileExtension' in entityDef '%s'", name );
    415 	}
    416 
    417 	if ( !dict->GetVector( "gravity", "0 0 -1066", gravity ) ) {
    418 		common->Error( "Missing 'gravity' in entityDef '%s'", name );
    419 	}
    420 	gravityDir = gravity;
    421 	gravityValue = gravityDir.Normalize();
    422 	invGravityDir = -gravityDir;
    423 
    424 	if ( !dict->GetFloat( "maxStepHeight", "0", maxStepHeight ) ) {
    425 		common->Error( "Missing 'maxStepHeight' in entityDef '%s'", name );
    426 	}
    427 
    428 	if ( !dict->GetFloat( "maxBarrierHeight", "0", maxBarrierHeight ) ) {
    429 		common->Error( "Missing 'maxBarrierHeight' in entityDef '%s'", name );
    430 	}
    431 
    432 	if ( !dict->GetFloat( "maxWaterJumpHeight", "0", maxWaterJumpHeight ) ) {
    433 		common->Error( "Missing 'maxWaterJumpHeight' in entityDef '%s'", name );
    434 	}
    435 
    436 	if ( !dict->GetFloat( "maxFallHeight", "0", maxFallHeight ) ) {
    437 		common->Error( "Missing 'maxFallHeight' in entityDef '%s'", name );
    438 	}
    439 
    440 	if ( !dict->GetFloat( "minFloorCos", "0", minFloorCos ) ) {
    441 		common->Error( "Missing 'minFloorCos' in entityDef '%s'", name );
    442 	}
    443 
    444 	if ( !dict->GetInt( "tt_barrierJump", "0", tt_barrierJump ) ) {
    445 		common->Error( "Missing 'tt_barrierJump' in entityDef '%s'", name );
    446 	}
    447 
    448 	if ( !dict->GetInt( "tt_startCrouching", "0", tt_startCrouching ) ) {
    449 		common->Error( "Missing 'tt_startCrouching' in entityDef '%s'", name );
    450 	}
    451 
    452 	if ( !dict->GetInt( "tt_waterJump", "0", tt_waterJump ) ) {
    453 		common->Error( "Missing 'tt_waterJump' in entityDef '%s'", name );
    454 	}
    455 
    456 	if ( !dict->GetInt( "tt_startWalkOffLedge", "0", tt_startWalkOffLedge ) ) {
    457 		common->Error( "Missing 'tt_startWalkOffLedge' in entityDef '%s'", name );
    458 	}
    459 
    460 	return true;
    461 }
    462 
    463 
    464 /*
    465 ============
    466 idAASSettings::WriteToFile
    467 ============
    468 */
    469 bool idAASSettings::WriteToFile( idFile *fp ) const {
    470 	int i;
    471 
    472 	fp->WriteFloatString( "{\n" );
    473 	fp->WriteFloatString( "\tbboxes\n\t{\n" );
    474 	for ( i = 0; i < numBoundingBoxes; i++ ) {
    475 		fp->WriteFloatString( "\t\t(%f %f %f)-(%f %f %f)\n", boundingBoxes[i][0].x, boundingBoxes[i][0].y,
    476 						boundingBoxes[i][0].z, boundingBoxes[i][1].x, boundingBoxes[i][1].y, boundingBoxes[i][1].z );
    477 	}
    478 	fp->WriteFloatString( "\t}\n" );
    479 	fp->WriteFloatString( "\tusePatches = %d\n", usePatches );
    480 	fp->WriteFloatString( "\twriteBrushMap = %d\n", writeBrushMap );
    481 	fp->WriteFloatString( "\tplayerFlood = %d\n", playerFlood );
    482 	fp->WriteFloatString( "\tallowSwimReachabilities = %d\n", allowSwimReachabilities );
    483 	fp->WriteFloatString( "\tallowFlyReachabilities = %d\n", allowFlyReachabilities );
    484 	fp->WriteFloatString( "\tfileExtension = \"%s\"\n", fileExtension.c_str() );
    485 	fp->WriteFloatString( "\tgravity = (%f %f %f)\n", gravity.x, gravity.y, gravity.z );
    486 	fp->WriteFloatString( "\tmaxStepHeight = %f\n", maxStepHeight );
    487 	fp->WriteFloatString( "\tmaxBarrierHeight = %f\n", maxBarrierHeight );
    488 	fp->WriteFloatString( "\tmaxWaterJumpHeight = %f\n", maxWaterJumpHeight );
    489 	fp->WriteFloatString( "\tmaxFallHeight = %f\n", maxFallHeight );
    490 	fp->WriteFloatString( "\tminFloorCos = %f\n", minFloorCos );
    491 	fp->WriteFloatString( "\ttt_barrierJump = %d\n", tt_barrierJump );
    492 	fp->WriteFloatString( "\ttt_startCrouching = %d\n", tt_startCrouching );
    493 	fp->WriteFloatString( "\ttt_waterJump = %d\n", tt_waterJump );
    494 	fp->WriteFloatString( "\ttt_startWalkOffLedge = %d\n", tt_startWalkOffLedge );
    495 	fp->WriteFloatString( "}\n" );
    496 	return true;
    497 }
    498 
    499 /*
    500 ============
    501 idAASSettings::ValidForBounds
    502 ============
    503 */
    504 bool idAASSettings::ValidForBounds( const idBounds &bounds ) const {
    505 	int i;
    506 
    507 	for ( i = 0; i < 3; i++ ) {
    508 		if ( bounds[0][i] < boundingBoxes[0][0][i] ) {
    509 			return false;
    510 		}
    511 		if ( bounds[1][i] > boundingBoxes[0][1][i] ) {
    512 			return false;
    513 		}
    514 	}
    515 	return true;
    516 }
    517 
    518 /*
    519 ============
    520 idAASSettings::ValidEntity
    521 ============
    522 */
    523 bool idAASSettings::ValidEntity( const char *classname ) const {
    524 	idStr			use_aas;
    525 	idVec3			size;
    526 	idBounds		bounds;
    527 
    528 	if ( playerFlood ) {
    529 		if ( !strcmp( classname, "info_player_start" ) || !strcmp( classname , "info_player_deathmatch" ) || !strcmp( classname, "func_teleporter" ) ) {
    530 			return true;
    531 		}
    532 	}
    533 
    534 	const idDeclEntityDef *decl = static_cast<const idDeclEntityDef *>( declManager->FindType( DECL_ENTITYDEF, classname, false ) );
    535 	if ( ( decl != NULL ) && decl->dict.GetString( "use_aas", NULL, use_aas ) && !fileExtension.Icmp( use_aas ) ) {
    536 		if ( decl->dict.GetVector( "mins", NULL, bounds[0] ) ) {
    537 			decl->dict.GetVector( "maxs", NULL, bounds[1] );
    538 		} else if ( decl->dict.GetVector( "size", NULL, size ) ) {
    539 			bounds[ 0 ].Set( size.x * -0.5f, size.y * -0.5f, 0.0f );
    540 			bounds[ 1 ].Set( size.x * 0.5f, size.y * 0.5f, size.z );
    541 		}
    542 
    543 		if ( !ValidForBounds( bounds ) ) {
    544 			common->Error( "%s cannot use %s\n", classname, fileExtension.c_str() );
    545 		}
    546 
    547 		return true;
    548 	}
    549 
    550 	return false;
    551 }
    552 
    553 
    554 /*
    555 ===============================================================================
    556 
    557 	idAASFileLocal
    558 
    559 ===============================================================================
    560 */
    561 
    562 #define AAS_LIST_GRANULARITY	1024
    563 #define AAS_INDEX_GRANULARITY	4096
    564 #define AAS_PLANE_GRANULARITY	4096
    565 #define AAS_VERTEX_GRANULARITY	4096
    566 #define AAS_EDGE_GRANULARITY	4096
    567 
    568 /*
    569 ================
    570 idAASFileLocal::idAASFileLocal
    571 ================
    572 */
    573 idAASFileLocal::idAASFileLocal() {
    574 	planeList.SetGranularity( AAS_PLANE_GRANULARITY );
    575 	vertices.SetGranularity( AAS_VERTEX_GRANULARITY );
    576 	edges.SetGranularity( AAS_EDGE_GRANULARITY );
    577 	edgeIndex.SetGranularity( AAS_INDEX_GRANULARITY );
    578 	faces.SetGranularity( AAS_LIST_GRANULARITY );
    579 	faceIndex.SetGranularity( AAS_INDEX_GRANULARITY );
    580 	areas.SetGranularity( AAS_LIST_GRANULARITY );
    581 	nodes.SetGranularity( AAS_LIST_GRANULARITY );
    582 	portals.SetGranularity( AAS_LIST_GRANULARITY );
    583 	portalIndex.SetGranularity( AAS_INDEX_GRANULARITY );
    584 	clusters.SetGranularity( AAS_LIST_GRANULARITY );
    585 }
    586 
    587 /*
    588 ================
    589 idAASFileLocal::~idAASFileLocal
    590 ================
    591 */
    592 idAASFileLocal::~idAASFileLocal() {
    593 	int i;
    594 	idReachability *reach, *next;
    595 
    596 	for ( i = 0; i < areas.Num(); i++ ) {
    597 		for ( reach = areas[i].reach; reach; reach = next ) {
    598 			next = reach->next;
    599 			delete reach;
    600 		}
    601 	}
    602 }
    603 
    604 /*
    605 ================
    606 idAASFileLocal::Clear
    607 ================
    608 */
    609 void idAASFileLocal::Clear() {
    610 	planeList.Clear();
    611 	vertices.Clear();
    612 	edges.Clear();
    613 	edgeIndex.Clear();
    614 	faces.Clear();
    615 	faceIndex.Clear();
    616 	areas.Clear();
    617 	nodes.Clear();
    618 	portals.Clear();
    619 	portalIndex.Clear();
    620 	clusters.Clear();
    621 }
    622 
    623 /*
    624 ================
    625 idAASFileLocal::Write
    626 ================
    627 */
    628 bool idAASFileLocal::Write( const idStr &fileName, unsigned int mapFileCRC ) {
    629 	int i, num;
    630 	idFile *aasFile;
    631 	idReachability *reach;
    632 
    633 	common->Printf( "[Write AAS]\n" );
    634 	common->Printf( "writing %s\n", fileName.c_str() );
    635 
    636 	name = fileName;
    637 	crc = mapFileCRC;
    638 
    639 	aasFile = fileSystem->OpenFileWrite( fileName, "fs_basepath" );
    640 	if ( !aasFile ) {
    641 		common->Error( "Error opening %s", fileName.c_str() );
    642 		return false;
    643 	}
    644 
    645 	aasFile->WriteFloatString( "%s \"%s\"\n\n", AAS_FILEID, AAS_FILEVERSION );
    646 	aasFile->WriteFloatString( "%u\n\n", mapFileCRC );
    647 
    648 	// write out the settings
    649 	aasFile->WriteFloatString( "settings\n" );
    650 	settings.WriteToFile( aasFile );
    651 
    652 	// write out planes
    653 	aasFile->WriteFloatString( "planes %d {\n", planeList.Num() );
    654 	for ( i = 0; i < planeList.Num(); i++ ) {
    655 		aasFile->WriteFloatString( "\t%d ( %f %f %f %f )\n", i,
    656 				planeList[i].Normal().x, planeList[i].Normal().y, planeList[i].Normal().z, planeList[i].Dist() );
    657 	}
    658 	aasFile->WriteFloatString( "}\n" );
    659 
    660 	// write out vertices
    661 	aasFile->WriteFloatString( "vertices %d {\n", vertices.Num() );
    662 	for ( i = 0; i < vertices.Num(); i++ ) {
    663 		aasFile->WriteFloatString( "\t%d ( %f %f %f )\n", i, vertices[i].x, vertices[i].y, vertices[i].z );
    664 	}
    665 	aasFile->WriteFloatString( "}\n" );
    666 
    667 	// write out edges
    668 	aasFile->WriteFloatString( "edges %d {\n", edges.Num() );
    669 	for ( i = 0; i < edges.Num(); i++ ) {
    670 		aasFile->WriteFloatString( "\t%d ( %d %d )\n", i, edges[i].vertexNum[0], edges[i].vertexNum[1] );
    671 	}
    672 	aasFile->WriteFloatString( "}\n" );
    673 
    674 	// write out edgeIndex
    675 	aasFile->WriteFloatString( "edgeIndex %d {\n", edgeIndex.Num() );
    676 	for ( i = 0; i < edgeIndex.Num(); i++ ) {
    677 		aasFile->WriteFloatString( "\t%d ( %d )\n", i, edgeIndex[i] );
    678 	}
    679 	aasFile->WriteFloatString( "}\n" );
    680 
    681 	// write out faces
    682 	aasFile->WriteFloatString( "faces %d {\n", faces.Num() );
    683 	for ( i = 0; i < faces.Num(); i++ ) {
    684 		aasFile->WriteFloatString( "\t%d ( %d %d %d %d %d %d )\n", i, faces[i].planeNum, faces[i].flags,
    685 						faces[i].areas[0], faces[i].areas[1], faces[i].firstEdge, faces[i].numEdges );
    686 	}
    687 	aasFile->WriteFloatString( "}\n" );
    688 
    689 	// write out faceIndex
    690 	aasFile->WriteFloatString( "faceIndex %d {\n", faceIndex.Num() );
    691 	for ( i = 0; i < faceIndex.Num(); i++ ) {
    692 		aasFile->WriteFloatString( "\t%d ( %d )\n", i, faceIndex[i] );
    693 	}
    694 	aasFile->WriteFloatString( "}\n" );
    695 
    696 	// write out areas
    697 	aasFile->WriteFloatString( "areas %d {\n", areas.Num() );
    698 	for ( i = 0; i < areas.Num(); i++ ) {
    699 		for ( num = 0, reach = areas[i].reach; reach; reach = reach->next ) {
    700 			num++;
    701 		}
    702 		aasFile->WriteFloatString( "\t%d ( %d %d %d %d %d %d ) %d {\n", i, areas[i].flags, areas[i].contents,
    703 						areas[i].firstFace, areas[i].numFaces, areas[i].cluster, areas[i].clusterAreaNum, num );
    704 		for ( reach = areas[i].reach; reach; reach = reach->next ) {
    705 			Reachability_Write( aasFile, reach );
    706 			switch( reach->travelType ) {
    707 				case TFL_SPECIAL:
    708 					Reachability_Special_Write( aasFile, static_cast<idReachability_Special *>(reach) );
    709 					break;
    710 			}
    711 			aasFile->WriteFloatString( "\n" );
    712 		}
    713 		aasFile->WriteFloatString( "\t}\n" );
    714 	}
    715 	aasFile->WriteFloatString( "}\n" );
    716 
    717 	// write out nodes
    718 	aasFile->WriteFloatString( "nodes %d {\n", nodes.Num() );
    719 	for ( i = 0; i < nodes.Num(); i++ ) {
    720 		aasFile->WriteFloatString( "\t%d ( %d %d %d )\n", i, nodes[i].planeNum, nodes[i].children[0], nodes[i].children[1] );
    721 	}
    722 	aasFile->WriteFloatString( "}\n" );
    723 
    724 	// write out portals
    725 	aasFile->WriteFloatString( "portals %d {\n", portals.Num() );
    726 	for ( i = 0; i < portals.Num(); i++ ) {
    727 		aasFile->WriteFloatString( "\t%d ( %d %d %d %d %d )\n", i, portals[i].areaNum, portals[i].clusters[0],
    728 						portals[i].clusters[1], portals[i].clusterAreaNum[0], portals[i].clusterAreaNum[1] );
    729 	}
    730 	aasFile->WriteFloatString( "}\n" );
    731 
    732 	// write out portalIndex
    733 	aasFile->WriteFloatString( "portalIndex %d {\n", portalIndex.Num() );
    734 	for ( i = 0; i < portalIndex.Num(); i++ ) {
    735 		aasFile->WriteFloatString( "\t%d ( %d )\n", i, portalIndex[i] );
    736 	}
    737 	aasFile->WriteFloatString( "}\n" );
    738 
    739 	// write out clusters
    740 	aasFile->WriteFloatString( "clusters %d {\n", clusters.Num() );
    741 	for ( i = 0; i < clusters.Num(); i++ ) {
    742 		aasFile->WriteFloatString( "\t%d ( %d %d %d %d )\n", i, clusters[i].numAreas, clusters[i].numReachableAreas,
    743 							clusters[i].firstPortal, clusters[i].numPortals );
    744 	}
    745 	aasFile->WriteFloatString( "}\n" );
    746 
    747 	// close file
    748 	fileSystem->CloseFile( aasFile );
    749 
    750 	common->Printf( "done.\n" );
    751 
    752 	return true;
    753 }
    754 
    755 /*
    756 ================
    757 idAASFileLocal::ParseIndex
    758 ================
    759 */
    760 bool idAASFileLocal::ParseIndex( idLexer &src, idList<aasIndex_t> &indexes ) {
    761 	int numIndexes, i;
    762 	aasIndex_t index;
    763 
    764 	numIndexes = src.ParseInt();
    765 	indexes.Resize( numIndexes );
    766 	if ( !src.ExpectTokenString( "{" ) ) {
    767 		return false;
    768 	}
    769 	for ( i = 0; i < numIndexes; i++ ) {
    770 		src.ParseInt();
    771 		src.ExpectTokenString( "(" );
    772 		index = src.ParseInt();
    773 		src.ExpectTokenString( ")" );
    774 		indexes.Append( index );
    775 	}
    776 	if ( !src.ExpectTokenString( "}" ) ) {
    777 		return false;
    778 	}
    779 	return true;
    780 }
    781 
    782 /*
    783 ================
    784 idAASFileLocal::ParsePlanes
    785 ================
    786 */
    787 bool idAASFileLocal::ParsePlanes( idLexer &src ) {
    788 	int numPlanes, i;
    789 	idPlane plane;
    790 	idVec4 vec;
    791 
    792 	numPlanes = src.ParseInt();
    793 	planeList.Resize( numPlanes );
    794 	if ( !src.ExpectTokenString( "{" ) ) {
    795 		return false;
    796 	}
    797 	for ( i = 0; i < numPlanes; i++ ) {
    798 		src.ParseInt();
    799 		if ( !src.Parse1DMatrix( 4, vec.ToFloatPtr() ) ) {
    800 			return false;
    801 		}
    802 		plane.SetNormal( vec.ToVec3() );
    803 		plane.SetDist( vec[3] );
    804 		planeList.Append( plane );
    805 	}
    806 	if ( !src.ExpectTokenString( "}" ) ) {
    807 		return false;
    808 	}
    809 	return true;
    810 }
    811 
    812 /*
    813 ================
    814 idAASFileLocal::ParseVertices
    815 ================
    816 */
    817 bool idAASFileLocal::ParseVertices( idLexer &src ) {
    818 	int numVertices, i;
    819 	idVec3 vec;
    820 
    821 	numVertices = src.ParseInt();
    822 	vertices.Resize( numVertices );
    823 	if ( !src.ExpectTokenString( "{" ) ) {
    824 		return false;
    825 	}
    826 	for ( i = 0; i < numVertices; i++ ) {
    827 		src.ParseInt();
    828 		if ( !src.Parse1DMatrix( 3, vec.ToFloatPtr() ) ) {
    829 			return false;
    830 		}
    831 		vertices.Append( vec );
    832 	}
    833 	if ( !src.ExpectTokenString( "}" ) ) {
    834 		return false;
    835 	}
    836 	return true;
    837 }
    838 
    839 /*
    840 ================
    841 idAASFileLocal::ParseEdges
    842 ================
    843 */
    844 bool idAASFileLocal::ParseEdges( idLexer &src ) {
    845 	int numEdges, i;
    846 	aasEdge_t edge;
    847 
    848 	numEdges = src.ParseInt();
    849 	edges.Resize( numEdges );
    850 	if ( !src.ExpectTokenString( "{" ) ) {
    851 		return false;
    852 	}
    853 	for ( i = 0; i < numEdges; i++ ) {
    854 		src.ParseInt();
    855 		src.ExpectTokenString( "(" );
    856 		edge.vertexNum[0] = src.ParseInt();
    857 		edge.vertexNum[1] = src.ParseInt();
    858 		src.ExpectTokenString( ")" );
    859 		edges.Append( edge );
    860 	}
    861 	if ( !src.ExpectTokenString( "}" ) ) {
    862 		return false;
    863 	}
    864 	return true;
    865 }
    866 
    867 /*
    868 ================
    869 idAASFileLocal::ParseFaces
    870 ================
    871 */
    872 bool idAASFileLocal::ParseFaces( idLexer &src ) {
    873 	int numFaces, i;
    874 	aasFace_t face;
    875 
    876 	numFaces = src.ParseInt();
    877 	faces.Resize( numFaces );
    878 	if ( !src.ExpectTokenString( "{" ) ) {
    879 		return false;
    880 	}
    881 	for ( i = 0; i < numFaces; i++ ) {
    882 		src.ParseInt();
    883 		src.ExpectTokenString( "(" );
    884 		face.planeNum = src.ParseInt();
    885 		face.flags = src.ParseInt();
    886 		face.areas[0] = src.ParseInt();
    887 		face.areas[1] = src.ParseInt();
    888 		face.firstEdge = src.ParseInt();
    889 		face.numEdges = src.ParseInt();
    890 		src.ExpectTokenString( ")" );
    891 		faces.Append( face );
    892 	}
    893 	if ( !src.ExpectTokenString( "}" ) ) {
    894 		return false;
    895 	}
    896 	return true;
    897 }
    898 
    899 /*
    900 ================
    901 idAASFileLocal::ParseReachabilities
    902 ================
    903 */
    904 bool idAASFileLocal::ParseReachabilities( idLexer &src, int areaNum ) {
    905 	int num, j;
    906 	aasArea_t *area;
    907 	idReachability reach, *newReach;
    908 	idReachability_Special *special;
    909 
    910 	area = &areas[areaNum];
    911 
    912 	num = src.ParseInt();
    913 	src.ExpectTokenString( "{" );
    914 	area->reach = NULL;
    915 	area->rev_reach = NULL;
    916 	area->travelFlags = AreaContentsTravelFlags( areaNum );
    917 	for ( j = 0; j < num; j++ ) {
    918 		Reachability_Read( src, &reach );
    919 		switch( reach.travelType ) {
    920 			case TFL_SPECIAL:
    921 				newReach = special = new (TAG_AAS) idReachability_Special();
    922 				Reachability_Special_Read( src, special );
    923 				break;
    924 			default:
    925 				newReach = new (TAG_AAS) idReachability();
    926 				break;
    927 		}
    928 		newReach->CopyBase( reach );
    929 		newReach->fromAreaNum = areaNum;
    930 		newReach->next = area->reach;
    931 		area->reach = newReach;
    932 	}
    933 	src.ExpectTokenString( "}" );
    934 	return true;
    935 }
    936 
    937 /*
    938 ================
    939 idAASFileLocal::LinkReversedReachability
    940 ================
    941 */
    942 void idAASFileLocal::LinkReversedReachability() {
    943 	int i;
    944 	idReachability *reach;
    945 
    946 	// link reversed reachabilities
    947 	for ( i = 0; i < areas.Num(); i++ ) {
    948 		for ( reach = areas[i].reach; reach; reach = reach->next ) {
    949 			reach->rev_next = areas[reach->toAreaNum].rev_reach;
    950 			areas[reach->toAreaNum].rev_reach = reach;
    951 		}
    952 	}
    953 }
    954 
    955 /*
    956 ================
    957 idAASFileLocal::ParseAreas
    958 ================
    959 */
    960 bool idAASFileLocal::ParseAreas( idLexer &src ) {
    961 	int numAreas, i;
    962 	aasArea_t area;
    963 
    964 	numAreas = src.ParseInt();
    965 	areas.Resize( numAreas );
    966 	if ( !src.ExpectTokenString( "{" ) ) {
    967 		return false;
    968 	}
    969 	for ( i = 0; i < numAreas; i++ ) {
    970 		src.ParseInt();
    971 		src.ExpectTokenString( "(" );
    972 		area.flags = src.ParseInt();
    973 		area.contents = src.ParseInt();
    974 		area.firstFace = src.ParseInt();
    975 		area.numFaces = src.ParseInt();
    976 		area.cluster = src.ParseInt();
    977 		area.clusterAreaNum = src.ParseInt();
    978 		src.ExpectTokenString( ")" );
    979 		areas.Append( area );
    980 		ParseReachabilities( src, i );
    981 	}
    982 	if ( !src.ExpectTokenString( "}" ) ) {
    983 		return false;
    984 	}
    985 
    986 	LinkReversedReachability();
    987 
    988 	return true;
    989 }
    990 
    991 /*
    992 ================
    993 idAASFileLocal::ParseNodes
    994 ================
    995 */
    996 bool idAASFileLocal::ParseNodes( idLexer &src ) {
    997 	int numNodes, i;
    998 	aasNode_t node;
    999 
   1000 	numNodes = src.ParseInt();
   1001 	nodes.Resize( numNodes );
   1002 	if ( !src.ExpectTokenString( "{" ) ) {
   1003 		return false;
   1004 	}
   1005 	for ( i = 0; i < numNodes; i++ ) {
   1006 		src.ParseInt();
   1007 		src.ExpectTokenString( "(" );
   1008 		node.planeNum = src.ParseInt();
   1009 		node.children[0] = src.ParseInt();
   1010 		node.children[1] = src.ParseInt();
   1011 		src.ExpectTokenString( ")" );
   1012 		nodes.Append( node );
   1013 	}
   1014 	if ( !src.ExpectTokenString( "}" ) ) {
   1015 		return false;
   1016 	}
   1017 	return true;
   1018 }
   1019 
   1020 /*
   1021 ================
   1022 idAASFileLocal::ParsePortals
   1023 ================
   1024 */
   1025 bool idAASFileLocal::ParsePortals( idLexer &src ) {
   1026 	int numPortals, i;
   1027 	aasPortal_t portal;
   1028 
   1029 	numPortals = src.ParseInt();
   1030 	portals.Resize( numPortals );
   1031 	if ( !src.ExpectTokenString( "{" ) ) {
   1032 		return false;
   1033 	}
   1034 	for ( i = 0; i < numPortals; i++ ) {
   1035 		src.ParseInt();
   1036 		src.ExpectTokenString( "(" );
   1037 		portal.areaNum = src.ParseInt();
   1038 		portal.clusters[0] = src.ParseInt();
   1039 		portal.clusters[1] = src.ParseInt();
   1040 		portal.clusterAreaNum[0] = src.ParseInt();
   1041 		portal.clusterAreaNum[1] = src.ParseInt();
   1042 		src.ExpectTokenString( ")" );
   1043 		portals.Append( portal );
   1044 	}
   1045 	if ( !src.ExpectTokenString( "}" ) ) {
   1046 		return false;
   1047 	}
   1048 	return true;
   1049 }
   1050 
   1051 /*
   1052 ================
   1053 idAASFileLocal::ParseClusters
   1054 ================
   1055 */
   1056 bool idAASFileLocal::ParseClusters( idLexer &src ) {
   1057 	int numClusters, i;
   1058 	aasCluster_t cluster;
   1059 
   1060 	numClusters = src.ParseInt();
   1061 	clusters.Resize( numClusters );
   1062 	if ( !src.ExpectTokenString( "{" ) ) {
   1063 		return false;
   1064 	}
   1065 	for ( i = 0; i < numClusters; i++ ) {
   1066 		src.ParseInt();
   1067 		src.ExpectTokenString( "(" );
   1068 		cluster.numAreas = src.ParseInt();
   1069 		cluster.numReachableAreas = src.ParseInt();
   1070 		cluster.firstPortal = src.ParseInt();
   1071 		cluster.numPortals = src.ParseInt();
   1072 		src.ExpectTokenString( ")" );
   1073 		clusters.Append( cluster );
   1074 	}
   1075 	if ( !src.ExpectTokenString( "}" ) ) {
   1076 		return false;
   1077 	}
   1078 	return true;
   1079 }
   1080 
   1081 /*
   1082 ================
   1083 idAASFileLocal::FinishAreas
   1084 ================
   1085 */
   1086 void idAASFileLocal::FinishAreas() {
   1087 	int i;
   1088 
   1089 	for ( i = 0; i < areas.Num(); i++ ) {
   1090 		areas[i].center = AreaReachableGoal( i );
   1091 		areas[i].bounds = AreaBounds( i );
   1092 	}
   1093 }
   1094 
   1095 /*
   1096 ================
   1097 idAASFileLocal::Load
   1098 ================
   1099 */
   1100 bool idAASFileLocal::Load( const idStr &fileName, unsigned int mapFileCRC ) {
   1101 	idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWPATHNAMES );
   1102 	idToken token;
   1103 	int depth;
   1104 	unsigned int c;
   1105 
   1106 	name = fileName;
   1107 	crc = mapFileCRC;
   1108 
   1109 	common->Printf( "[Load AAS]\n" );
   1110 	common->Printf( "loading %s\n", name.c_str() );
   1111 
   1112 	if ( !src.LoadFile( name ) ) {
   1113 		return false;
   1114 	}
   1115 
   1116 	if ( !src.ExpectTokenString( AAS_FILEID ) ) {
   1117 		common->Warning( "Not an AAS file: '%s'", name.c_str() );
   1118 		return false;
   1119 	}
   1120 
   1121 	if ( !src.ReadToken( &token ) || token != AAS_FILEVERSION ) {
   1122 		common->Warning( "AAS file '%s' has version %s instead of %s", name.c_str(), token.c_str(), AAS_FILEVERSION );
   1123 		return false;
   1124 	}
   1125 
   1126 	if ( !src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
   1127 		common->Warning( "AAS file '%s' has no map file CRC", name.c_str() );
   1128 		return false;
   1129 	}
   1130 
   1131 	c = token.GetUnsignedLongValue();
   1132 	if ( mapFileCRC && c != mapFileCRC ) {
   1133 		common->Warning( "AAS file '%s' is out of date", name.c_str() );
   1134 		return false;
   1135 	}
   1136 
   1137 	// clear the file in memory
   1138 	Clear();
   1139 
   1140 	// parse the file
   1141 	while ( 1 ) {
   1142 		if ( !src.ReadToken( &token ) ) {
   1143 			break;
   1144 		}
   1145 
   1146 		if ( token == "settings" ) {
   1147 			if ( !settings.FromParser( src ) ) { return false; }
   1148 		}
   1149 		else if ( token == "planes" ) {
   1150 			if ( !ParsePlanes( src ) ) { return false; }
   1151 		}
   1152 		else if ( token == "vertices" ) {
   1153 			if ( !ParseVertices( src ) ) { return false; }
   1154 		}
   1155 		else if ( token == "edges" ) {
   1156 			if ( !ParseEdges( src ) ) { return false; }
   1157 		}
   1158 		else if ( token == "edgeIndex" ) {
   1159 			if ( !ParseIndex( src, edgeIndex ) ) { return false; }
   1160 		}
   1161 		else if ( token == "faces" ) {
   1162 			if ( !ParseFaces( src ) ) { return false; }
   1163 		}
   1164 		else if ( token == "faceIndex" ) {
   1165 			if ( !ParseIndex( src, faceIndex ) ) { return false; }
   1166 		}
   1167 		else if ( token == "areas" ) {
   1168 			if ( !ParseAreas( src ) ) { return false; }
   1169 		}
   1170 		else if ( token == "nodes" ) {
   1171 			if ( !ParseNodes( src ) ) { return false; }
   1172 		}
   1173 		else if ( token == "portals" ) {
   1174 			if ( !ParsePortals( src ) ) { return false; }
   1175 		}
   1176 		else if ( token == "portalIndex" ) {
   1177 			if ( !ParseIndex( src, portalIndex ) ) { return false; }
   1178 		}
   1179 		else if ( token == "clusters" ) {
   1180 			if ( !ParseClusters( src ) ) { return false; }
   1181 		}
   1182 		else {
   1183 			src.Error( "idAASFileLocal::Load: bad token \"%s\"", token.c_str() );
   1184 			return false;
   1185 		}
   1186 	}
   1187 
   1188 	FinishAreas();
   1189 
   1190 	depth = MaxTreeDepth();
   1191 	if ( depth > MAX_AAS_TREE_DEPTH ) {
   1192 		src.Error( "idAASFileLocal::Load: tree depth = %d", depth );
   1193 	}
   1194 
   1195 	common->UpdateLevelLoadPacifier();
   1196 
   1197 	common->Printf( "done.\n" );
   1198 
   1199 	return true;
   1200 }
   1201 
   1202 /*
   1203 ================
   1204 idAASFileLocal::MemorySize
   1205 ================
   1206 */
   1207 int idAASFileLocal::MemorySize() const {
   1208 	int size;
   1209 
   1210 	size = planeList.Size();
   1211 	size += vertices.Size();
   1212 	size += edges.Size();
   1213 	size += edgeIndex.Size();
   1214 	size += faces.Size();
   1215 	size += faceIndex.Size();
   1216 	size += areas.Size();
   1217 	size += nodes.Size();
   1218 	size += portals.Size();
   1219 	size += portalIndex.Size();
   1220 	size += clusters.Size();
   1221 	size += sizeof( idReachability_Walk ) * NumReachabilities();
   1222 
   1223 	return size;
   1224 }
   1225 
   1226 /*
   1227 ================
   1228 idAASFileLocal::PrintInfo
   1229 ================
   1230 */
   1231 void idAASFileLocal::PrintInfo() const {
   1232 	common->Printf( "%6d KB file size\n", MemorySize() >> 10 );
   1233 	common->Printf( "%6d areas\n", areas.Num() );
   1234 	common->Printf( "%6d max tree depth\n", MaxTreeDepth() );
   1235 	ReportRoutingEfficiency();
   1236 }
   1237 
   1238 /*
   1239 ================
   1240 idAASFileLocal::NumReachabilities
   1241 ================
   1242 */
   1243 int idAASFileLocal::NumReachabilities() const {
   1244 	int i, num;
   1245 	idReachability *reach;
   1246 
   1247 	num = 0;
   1248 	for ( i = 0; i < areas.Num(); i++ ) {
   1249 		for ( reach = areas[i].reach; reach; reach = reach->next ) {
   1250 			num++;
   1251 		}
   1252 	}
   1253 	return num;
   1254 }
   1255 
   1256 /*
   1257 ================
   1258 idAASFileLocal::ReportRoutingEfficiency
   1259 ================
   1260 */
   1261 void idAASFileLocal::ReportRoutingEfficiency() const {
   1262 	int numReachableAreas, total, i, n;
   1263 
   1264 	numReachableAreas = 0;
   1265 	total = 0;
   1266 	for ( i = 0; i < clusters.Num(); i++ ) {
   1267 		n = clusters[i].numReachableAreas;
   1268 		numReachableAreas += n;
   1269 		total += n * n;
   1270 	}
   1271 	total += numReachableAreas * portals.Num();
   1272 
   1273 	common->Printf( "%6d reachable areas\n", numReachableAreas );
   1274 	common->Printf( "%6d reachabilities\n", NumReachabilities() );
   1275 	common->Printf( "%6d KB max routing cache\n", ( total * 3 ) >> 10 );
   1276 }
   1277 
   1278 /*
   1279 ================
   1280 idAASFileLocal::DeleteReachabilities
   1281 ================
   1282 */
   1283 void idAASFileLocal::DeleteReachabilities() {
   1284 	int i;
   1285 	idReachability *reach, *nextReach;
   1286 
   1287 	for ( i = 0; i < areas.Num(); i++ ) {
   1288 		for ( reach = areas[i].reach; reach; reach = nextReach ) {
   1289 			nextReach = reach->next;
   1290 			delete reach;
   1291 		}
   1292 		areas[i].reach = NULL;
   1293 		areas[i].rev_reach = NULL;
   1294 	}
   1295 }
   1296 
   1297 /*
   1298 ================
   1299 idAASFileLocal::DeleteClusters
   1300 ================
   1301 */
   1302 void idAASFileLocal::DeleteClusters() {
   1303 	aasPortal_t portal;
   1304 	aasCluster_t cluster;
   1305 
   1306 	portals.Clear();
   1307 	portalIndex.Clear();
   1308 	clusters.Clear();
   1309 
   1310 	// first portal is a dummy
   1311 	memset( &portal, 0, sizeof( portal ) );
   1312 	portals.Append( portal );
   1313 
   1314 	// first cluster is a dummy
   1315 	memset( &cluster, 0, sizeof( cluster ) );
   1316 	clusters.Append( cluster );
   1317 }