1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP 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 RTCW SP 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP 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 RTCW SP 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 
30 /*****************************************************************************
31  * name:		be_aas_main.c
32  *
33  * desc:		AAS
34  *
35  *
36  *****************************************************************************/
37 
38 #include "../qcommon/q_shared.h"
39 #include "l_memory.h"
40 #include "l_libvar.h"
41 #include "l_utils.h"
42 #include "l_script.h"
43 #include "l_precomp.h"
44 #include "l_struct.h"
45 #include "l_log.h"
46 #include "aasfile.h"
47 #include "botlib.h"
48 #include "be_aas.h"
49 #include "be_aas_funcs.h"
50 #include "be_interface.h"
51 #include "be_aas_def.h"
52 
53 aas_t aasworlds[MAX_AAS_WORLDS];
54 
55 aas_t *aasworld;
56 
57 //===========================================================================
58 //
59 // Parameter:				-
60 // Returns:					-
61 // Changes Globals:		-
62 //===========================================================================
AAS_Error(char * fmt,...)63 void QDECL AAS_Error( char *fmt, ... ) {
64 	char str[1024];
65 	va_list arglist;
66 
67 	va_start( arglist, fmt );
68 	Q_vsnprintf(str, sizeof(str), fmt, arglist);
69 	va_end( arglist );
70 	botimport.Print(PRT_FATAL, "%s", str);
71 } //end of the function AAS_Error
72 
73 // Ridah, multiple AAS worlds
74 //===========================================================================
75 //
76 // Parameter:				-
77 // Returns:					-
78 // Changes Globals:		-
79 //===========================================================================
AAS_SetCurrentWorld(int index)80 void AAS_SetCurrentWorld( int index ) {
81 	if ( index >= MAX_AAS_WORLDS || index < 0 ) {
82 		AAS_Error( "AAS_SetCurrentWorld: index out of range\n" );
83 		return;
84 	}
85 
86 	// set the current world pointer
87 	aasworld = &aasworlds[index];
88 }
89 // done.
90 
91 //===========================================================================
92 //
93 // Parameter:				-
94 // Returns:					-
95 // Changes Globals:		-
96 //===========================================================================
AAS_StringFromIndex(char * indexname,char * stringindex[],int numindexes,int index)97 char *AAS_StringFromIndex( char *indexname, char *stringindex[], int numindexes, int index ) {
98 	if ( !( *aasworld ).indexessetup ) {
99 		botimport.Print( PRT_ERROR, "%s: index %d not setup\n", indexname, index );
100 		return "";
101 	} //end if
102 	if ( index < 0 || index >= numindexes ) {
103 		botimport.Print( PRT_ERROR, "%s: index %d out of range\n", indexname, index );
104 		return "";
105 	} //end if
106 	if ( !stringindex[index] ) {
107 		if ( index ) {
108 			botimport.Print( PRT_ERROR, "%s: reference to unused index %d\n", indexname, index );
109 		} //end if
110 		return "";
111 	} //end if
112 	return stringindex[index];
113 } //end of the function AAS_StringFromIndex
114 //===========================================================================
115 //
116 // Parameter:				-
117 // Returns:					-
118 // Changes Globals:		-
119 //===========================================================================
AAS_IndexFromString(char * indexname,char * stringindex[],int numindexes,char * string)120 int AAS_IndexFromString( char *indexname, char *stringindex[], int numindexes, char *string ) {
121 	int i;
122 	if ( !( *aasworld ).indexessetup ) {
123 		botimport.Print( PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string );
124 		return 0;
125 	} //end if
126 	for ( i = 0; i < numindexes; i++ )
127 	{
128 		if ( !stringindex[i] ) {
129 			continue;
130 		}
131 		if ( !Q_stricmp( stringindex[i], string ) ) {
132 			return i;
133 		}
134 	} //end for
135 	return 0;
136 } //end of the function AAS_IndexFromString
137 //===========================================================================
138 //
139 // Parameter:				-
140 // Returns:					-
141 // Changes Globals:		-
142 //===========================================================================
AAS_ModelFromIndex(int index)143 char *AAS_ModelFromIndex( int index ) {
144 //	return AAS_StringFromIndex("ModelFromIndex", &(*aasworld).configstrings[CS_MODELS], MAX_MODELS, index);
145 	return 0;   // removed so the CS_ defines could be removed from be_aas_def.h
146 } //end of the function AAS_ModelFromIndex
147 //===========================================================================
148 //
149 // Parameter:				-
150 // Returns:					-
151 // Changes Globals:		-
152 //===========================================================================
AAS_IndexFromModel(char * modelname)153 int AAS_IndexFromModel( char *modelname ) {
154 //	return AAS_IndexFromString("IndexFromModel", &(*aasworld).configstrings[CS_MODELS], MAX_MODELS, modelname);
155 	return 0;   // removed so the CS_ defines could be removed from be_aas_def.h
156 } //end of the function AAS_IndexFromModel
157 //===========================================================================
158 //
159 // Parameter:				-
160 // Returns:					-
161 // Changes Globals:		-
162 //===========================================================================
AAS_UpdateStringIndexes(int numconfigstrings,char * configstrings[])163 void AAS_UpdateStringIndexes( int numconfigstrings, char *configstrings[] ) {
164 	int i;
165 	//set string pointers and copy the strings
166 	for ( i = 0; i < numconfigstrings; i++ )
167 	{
168 		if ( configstrings[i] ) {
169 			//if ((*aasworld).configstrings[i]) FreeMemory((*aasworld).configstrings[i]);
170 			( *aasworld ).configstrings[i] = (char *) GetMemory( strlen( configstrings[i] ) + 1 );
171 			strcpy( ( *aasworld ).configstrings[i], configstrings[i] );
172 		} //end if
173 	} //end for
174 	( *aasworld ).indexessetup = qtrue;
175 } //end of the function AAS_UpdateStringIndexes
176 //===========================================================================
177 //
178 // Parameter:				-
179 // Returns:					-
180 // Changes Globals:		-
181 //===========================================================================
AAS_Loaded(void)182 int AAS_Loaded( void ) {
183 	return ( *aasworld ).loaded;
184 } //end of the function AAS_Loaded
185 //===========================================================================
186 //
187 // Parameter:				-
188 // Returns:					-
189 // Changes Globals:		-
190 //===========================================================================
AAS_Initialized(void)191 int AAS_Initialized( void ) {
192 	return ( *aasworld ).initialized;
193 } //end of the function AAS_Initialized
194 //===========================================================================
195 //
196 // Parameter:				-
197 // Returns:					-
198 // Changes Globals:		-
199 //===========================================================================
AAS_SetInitialized(void)200 void AAS_SetInitialized( void ) {
201 	( *aasworld ).initialized = qtrue;
202 	botimport.Print( PRT_MESSAGE, "AAS initialized.\n" );
203 #ifdef DEBUG
204 	//create all the routing cache
205 	//AAS_CreateAllRoutingCache();
206 	//
207 	//AAS_RoutingInfo();
208 #endif
209 
210 	// Ridah, build/load the route-table
211 	AAS_RT_BuildRouteTable();
212 	// done.
213 
214 } //end of the function AAS_SetInitialized
215 //===========================================================================
216 //
217 // Parameter:				-
218 // Returns:					-
219 // Changes Globals:		-
220 //===========================================================================
AAS_ContinueInit(float time)221 void AAS_ContinueInit( float time ) {
222 	//if no AAS file loaded
223 	if ( !( *aasworld ).loaded ) {
224 		return;
225 	}
226 	//if AAS is already initialized
227 	if ( ( *aasworld ).initialized ) {
228 		return;
229 	}
230 	//calculate reachability, if not finished return
231 	if ( AAS_ContinueInitReachability( time ) ) {
232 		return;
233 	}
234 	//initialize clustering for the new map
235 	AAS_InitClustering();
236 	//if reachability has been calculated and an AAS file should be written
237 	//or there is a forced data optimization
238 	if ( ( *aasworld ).savefile || ( (int)LibVarGetValue( "forcewrite" ) ) ) {
239 		//optimize the AAS data
240 		if ( !( (int)LibVarValue( "nooptimize", "1" ) ) ) {
241 			AAS_Optimize();
242 		}
243 		//save the AAS file
244 		if ( AAS_WriteAASFile( ( *aasworld ).filename ) ) {
245 			botimport.Print(PRT_MESSAGE, "%s written successfully\n", ( *aasworld ).filename);
246 		} //end if
247 		else
248 		{
249 			botimport.Print( PRT_ERROR, "couldn't write %s\n", ( *aasworld ).filename );
250 		} //end else
251 	} //end if
252 	  //initialize the routing
253 	AAS_InitRouting();
254 	//at this point AAS is initialized
255 	AAS_SetInitialized();
256 } //end of the function AAS_ContinueInit
257 //===========================================================================
258 // called at the start of every frame
259 //
260 // Parameter:				-
261 // Returns:					-
262 // Changes Globals:		-
263 //===========================================================================
AAS_StartFrame(float time)264 int AAS_StartFrame( float time ) {
265 	// Ridah, do each of the aasworlds
266 	int i;
267 
268 	for ( i = 0; i < MAX_AAS_WORLDS; i++ )
269 	{
270 		AAS_SetCurrentWorld( i );
271 
272 		( *aasworld ).time = time;
273 		//invalidate the entities
274 		AAS_InvalidateEntities();
275 		//initialize AAS
276 		AAS_ContinueInit( time );
277 		//
278 		( *aasworld ).frameroutingupdates = 0;
279 		//
280 		/* Ridah, disabled for speed
281 		if (LibVarGetValue("showcacheupdates"))
282 		{
283 			AAS_RoutingInfo();
284 			LibVarSet("showcacheupdates", "0");
285 		} //end if
286 		if (LibVarGetValue("showmemoryusage"))
287 		{
288 			PrintUsedMemorySize();
289 			LibVarSet("showmemoryusage", "0");
290 		} //end if
291 		if (LibVarGetValue("memorydump"))
292 		{
293 			PrintMemoryLabels();
294 			LibVarSet("memorydump", "0");
295 		} //end if
296 		*/
297 	} //end if
298 	( *aasworld ).numframes++;
299 	return BLERR_NOERROR;
300 } //end of the function AAS_StartFrame
301 //===========================================================================
302 //
303 // Parameter:				-
304 // Returns:					-
305 // Changes Globals:		-
306 //===========================================================================
AAS_Time(void)307 float AAS_Time( void ) {
308 	return ( *aasworld ).time;
309 } //end of the function AAS_Time
310 //===========================================================================
311 // basedir	= Quake2 console basedir
312 // gamedir	= Quake2 console gamedir
313 //	mapname	= name of the map without extension (.bsp)
314 //
315 // Parameter:				-
316 // Returns:					-
317 // Changes Globals:		-
318 //===========================================================================
AAS_LoadFiles(const char * mapname)319 int AAS_LoadFiles( const char *mapname ) {
320 	int errnum;
321 	char aasfile[MAX_QPATH];
322 
323 	Q_strncpyz( ( *aasworld ).mapname, mapname, sizeof( ( *aasworld ).mapname ) );
324 	//NOTE: first reset the entity links into the AAS areas and BSP leaves
325 	// the AAS link heap and BSP link heap are reset after respectively the
326 	// AAS file and BSP file are loaded
327 	AAS_ResetEntityLinks();
328 	//
329 
330 	// load bsp info
331 	AAS_LoadBSPFile();
332 
333 	//load the aas file
334 	Com_sprintf( aasfile, sizeof( aasfile ), "maps/%s.aas", mapname );
335 	errnum = AAS_LoadAASFile( aasfile );
336 	if ( errnum != BLERR_NOERROR ) {
337 		return errnum;
338 	}
339 
340 	botimport.Print( PRT_MESSAGE, "loaded %s\n", aasfile );
341 	Q_strncpyz( ( *aasworld ).filename, aasfile, sizeof( ( *aasworld ).filename ) );
342 	return BLERR_NOERROR;
343 } //end of the function AAS_LoadFiles
344 //===========================================================================
345 // called everytime a map changes
346 //
347 // Parameter:				-
348 // Returns:					-
349 // Changes Globals:		-
350 //===========================================================================
351 
352 // Ridah, modified this for multiple AAS files
353 
AAS_LoadMap(const char * mapname)354 int AAS_LoadMap( const char *mapname ) {
355 #define MAPNAME_LEN 256
356 	int errnum;
357 	int i;
358 	char this_mapname[MAPNAME_LEN], intstr[4];
359 	qboolean loaded = qfalse;
360 	int missingErrNum = 0;
361 
362 	for ( i = 0; i < MAX_AAS_WORLDS; i++ )
363 	{
364 		AAS_SetCurrentWorld( i );
365 
366 		Q_strncpyz( this_mapname, mapname, sizeof( this_mapname ) );
367 		Q_strcat( this_mapname, sizeof( this_mapname ) - strlen( this_mapname ) - 1, "_b" );
368 		Com_sprintf( intstr, sizeof( intstr ), "%i", i);
369 		Q_strcat( this_mapname, sizeof( this_mapname ) - strlen( this_mapname ) - 1, intstr );
370 
371 		//if no mapname is provided then the string indexes are updated
372 		if ( !mapname ) {
373 			return 0;
374 		} //end if
375 		  //
376 		( *aasworld ).initialized = qfalse;
377 		//NOTE: free the routing caches before loading a new map because
378 		// to free the caches the old number of areas, number of clusters
379 		// and number of areas in a clusters must be available
380 		AAS_FreeRoutingCaches();
381 		//load the map
382 		errnum = AAS_LoadFiles( this_mapname );
383 		if ( errnum != BLERR_NOERROR ) {
384 			( *aasworld ).loaded = qfalse;
385 			// RF, we are allowed to skip one of the files, but not both
386 			//return errnum;
387 			missingErrNum = errnum;
388 			continue;
389 		} //end if
390 		  //
391 		loaded = qtrue;
392 		//
393 		AAS_InitSettings();
394 		//initialize the AAS link heap for the new map
395 		AAS_InitAASLinkHeap();
396 		//initialize the AAS linked entities for the new map
397 		AAS_InitAASLinkedEntities();
398 		//initialize reachability for the new map
399 		AAS_InitReachability();
400 		//initialize the alternative routing
401 		AAS_InitAlternativeRouting();
402 	}
403 
404 	if ( !loaded ) {
405 		return missingErrNum;
406 	}
407 
408 	//everything went ok
409 	return 0;
410 } //end of the function AAS_LoadMap
411 
412 // done.
413 
414 //===========================================================================
415 // called when the library is first loaded
416 //
417 // Parameter:				-
418 // Returns:					-
419 // Changes Globals:		-
420 //===========================================================================
AAS_Setup(void)421 int AAS_Setup( void ) {
422 	// Ridah, just use the default world for entities
423 	AAS_SetCurrentWorld( 0 );
424 
425 	( *aasworlds ).maxclients = (int) LibVarValue( "maxclients", "128" );
426 	( *aasworlds ).maxentities = (int) LibVarValue( "maxentities", "2048" );
427 	//allocate memory for the entities
428 	if ( ( *aasworld ).entities ) {
429 		FreeMemory( ( *aasworld ).entities );
430 	}
431 	( *aasworld ).entities = (aas_entity_t *) GetClearedHunkMemory( ( *aasworld ).maxentities * sizeof( aas_entity_t ) );
432 	//invalidate all the entities
433 	AAS_InvalidateEntities();
434 
435 	//force some recalculations
436 	//LibVarSet("forceclustering", "1");			//force clustering calculation
437 	//LibVarSet("forcereachability", "1");		//force reachability calculation
438 	( *aasworld ).numframes = 0;
439 	return BLERR_NOERROR;
440 } //end of the function AAS_Setup
441 //===========================================================================
442 //
443 // Parameter:				-
444 // Returns:					-
445 // Changes Globals:		-
446 //===========================================================================
AAS_Shutdown(void)447 void AAS_Shutdown( void ) {
448 	// Ridah, do each of the worlds
449 	int i;
450 
451 	for ( i = 0; i < MAX_AAS_WORLDS; i++ )
452 	{
453 		AAS_SetCurrentWorld( i );
454 
455 		// Ridah, kill the route-table data
456 		AAS_RT_ShutdownRouteTable();
457 
458 		AAS_ShutdownAlternativeRouting();
459 		AAS_DumpBSPData();
460 		//free routing caches
461 		AAS_FreeRoutingCaches();
462 		//free aas link heap
463 		AAS_FreeAASLinkHeap();
464 		//free aas linked entities
465 		AAS_FreeAASLinkedEntities();
466 		//free the aas data
467 		AAS_DumpAASData();
468 
469 		if ( i == 0 ) {
470 			//free the entities
471 			if ( ( *aasworld ).entities ) {
472 				FreeMemory( ( *aasworld ).entities );
473 			}
474 		}
475 
476 		//clear the (*aasworld) structure
477 		memset( &( *aasworld ), 0, sizeof( aas_t ) );
478 		//aas has not been initialized
479 		( *aasworld ).initialized = qfalse;
480 	}
481 
482 	//NOTE: as soon as a new .bsp file is loaded the .bsp file memory is
483 	// freed and reallocated, so there's no need to free that memory here
484 	//print shutdown
485 	botimport.Print( PRT_MESSAGE, "AAS shutdown.\n" );
486 } //end of the function AAS_Shutdown
487