1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 /*****************************************************************************
24  * name:		be_aas_main.c
25  *
26  * desc:		AAS
27  *
28  * $Archive: /MissionPack/code/botlib/be_aas_main.c $
29  *
30  *****************************************************************************/
31 
32 #include "../qcommon/q_shared.h"
33 #include "l_memory.h"
34 #include "l_libvar.h"
35 #include "l_utils.h"
36 #include "l_script.h"
37 #include "l_precomp.h"
38 #include "l_struct.h"
39 #include "l_log.h"
40 #include "aasfile.h"
41 #include "botlib.h"
42 #include "be_aas.h"
43 #include "be_aas_funcs.h"
44 #include "be_interface.h"
45 #include "be_aas_def.h"
46 
47 aas_t aasworld;
48 
49 libvar_t *saveroutingcache;
50 
51 //===========================================================================
52 //
53 // Parameter:				-
54 // Returns:					-
55 // Changes Globals:		-
56 //===========================================================================
AAS_Error(char * fmt,...)57 void QDECL AAS_Error(char *fmt, ...)
58 {
59 	char str[1024];
60 	va_list arglist;
61 
62 	va_start(arglist, fmt);
63 	Q_vsnprintf(str, sizeof(str), fmt, arglist);
64 	va_end(arglist);
65 	botimport.Print(PRT_FATAL, "%s", str);
66 } //end of the function AAS_Error
67 //===========================================================================
68 //
69 // Parameter:				-
70 // Returns:					-
71 // Changes Globals:		-
72 //===========================================================================
AAS_StringFromIndex(char * indexname,char * stringindex[],int numindexes,int index)73 char *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index)
74 {
75 	if (!aasworld.indexessetup)
76 	{
77 		botimport.Print(PRT_ERROR, "%s: index %d not setup\n", indexname, index);
78 		return "";
79 	} //end if
80 	if (index < 0 || index >= numindexes)
81 	{
82 		botimport.Print(PRT_ERROR, "%s: index %d out of range\n", indexname, index);
83 		return "";
84 	} //end if
85 	if (!stringindex[index])
86 	{
87 		if (index)
88 		{
89 			botimport.Print(PRT_ERROR, "%s: reference to unused index %d\n", indexname, index);
90 		} //end if
91 		return "";
92 	} //end if
93 	return stringindex[index];
94 } //end of the function AAS_StringFromIndex
95 //===========================================================================
96 //
97 // Parameter:				-
98 // Returns:					-
99 // Changes Globals:		-
100 //===========================================================================
AAS_IndexFromString(char * indexname,char * stringindex[],int numindexes,char * string)101 int AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string)
102 {
103 	int i;
104 	if (!aasworld.indexessetup)
105 	{
106 		botimport.Print(PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string);
107 		return 0;
108 	} //end if
109 	for (i = 0; i < numindexes; i++)
110 	{
111 		if (!stringindex[i]) continue;
112 		if (!Q_stricmp(stringindex[i], string)) return i;
113 	} //end for
114 	return 0;
115 } //end of the function AAS_IndexFromString
116 //===========================================================================
117 //
118 // Parameter:				-
119 // Returns:					-
120 // Changes Globals:		-
121 //===========================================================================
AAS_ModelFromIndex(int index)122 char *AAS_ModelFromIndex(int index)
123 {
124 	return AAS_StringFromIndex("ModelFromIndex", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index);
125 } //end of the function AAS_ModelFromIndex
126 //===========================================================================
127 //
128 // Parameter:				-
129 // Returns:					-
130 // Changes Globals:		-
131 //===========================================================================
AAS_IndexFromModel(char * modelname)132 int AAS_IndexFromModel(char *modelname)
133 {
134 	return AAS_IndexFromString("IndexFromModel", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname);
135 } //end of the function AAS_IndexFromModel
136 //===========================================================================
137 //
138 // Parameter:				-
139 // Returns:					-
140 // Changes Globals:		-
141 //===========================================================================
AAS_UpdateStringIndexes(int numconfigstrings,char * configstrings[])142 void AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[])
143 {
144 	int i;
145 	//set string pointers and copy the strings
146 	for (i = 0; i < numconfigstrings; i++)
147 	{
148 		if (configstrings[i])
149 		{
150 			//if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]);
151 			aasworld.configstrings[i] = (char *) GetMemory(strlen(configstrings[i]) + 1);
152 			strcpy(aasworld.configstrings[i], configstrings[i]);
153 		} //end if
154 	} //end for
155 	aasworld.indexessetup = qtrue;
156 } //end of the function AAS_UpdateStringIndexes
157 //===========================================================================
158 //
159 // Parameter:				-
160 // Returns:					-
161 // Changes Globals:		-
162 //===========================================================================
AAS_Loaded(void)163 int AAS_Loaded(void)
164 {
165 	return aasworld.loaded;
166 } //end of the function AAS_Loaded
167 //===========================================================================
168 //
169 // Parameter:				-
170 // Returns:					-
171 // Changes Globals:		-
172 //===========================================================================
AAS_Initialized(void)173 int AAS_Initialized(void)
174 {
175 	return aasworld.initialized;
176 } //end of the function AAS_Initialized
177 //===========================================================================
178 //
179 // Parameter:				-
180 // Returns:					-
181 // Changes Globals:		-
182 //===========================================================================
AAS_SetInitialized(void)183 void AAS_SetInitialized(void)
184 {
185 	aasworld.initialized = qtrue;
186 	botimport.Print(PRT_MESSAGE, "AAS initialized.\n");
187 #ifdef DEBUG
188 	//create all the routing cache
189 	//AAS_CreateAllRoutingCache();
190 	//
191 	//AAS_RoutingInfo();
192 #endif
193 } //end of the function AAS_SetInitialized
194 //===========================================================================
195 //
196 // Parameter:				-
197 // Returns:					-
198 // Changes Globals:		-
199 //===========================================================================
AAS_ContinueInit(float time)200 void AAS_ContinueInit(float time)
201 {
202 	//if no AAS file loaded
203 	if (!aasworld.loaded) return;
204 	//if AAS is already initialized
205 	if (aasworld.initialized) return;
206 	//calculate reachability, if not finished return
207 	if (AAS_ContinueInitReachability(time)) return;
208 	//initialize clustering for the new map
209 	AAS_InitClustering();
210 	//if reachability has been calculated and an AAS file should be written
211 	//or there is a forced data optimization
212 	if (aasworld.savefile || ((int)LibVarGetValue("forcewrite")))
213 	{
214 		//optimize the AAS data
215 		if ((int)LibVarValue("aasoptimize", "0")) AAS_Optimize();
216 		//save the AAS file
217 		if (AAS_WriteAASFile(aasworld.filename))
218 		{
219 			botimport.Print(PRT_MESSAGE, "%s written succesfully\n", aasworld.filename);
220 		} //end if
221 		else
222 		{
223 			botimport.Print(PRT_ERROR, "couldn't write %s\n", aasworld.filename);
224 		} //end else
225 	} //end if
226 	//initialize the routing
227 	AAS_InitRouting();
228 	//at this point AAS is initialized
229 	AAS_SetInitialized();
230 } //end of the function AAS_ContinueInit
231 //===========================================================================
232 // called at the start of every frame
233 //
234 // Parameter:				-
235 // Returns:					-
236 // Changes Globals:		-
237 //===========================================================================
AAS_StartFrame(float time)238 int AAS_StartFrame(float time)
239 {
240 	aasworld.time = time;
241 	//unlink all entities that were not updated last frame
242 	AAS_UnlinkInvalidEntities();
243 	//invalidate the entities
244 	AAS_InvalidateEntities();
245 	//initialize AAS
246 	AAS_ContinueInit(time);
247 	//
248 	aasworld.frameroutingupdates = 0;
249 	//
250 	if (bot_developer)
251 	{
252 		if (LibVarGetValue("showcacheupdates"))
253 		{
254 			AAS_RoutingInfo();
255 			LibVarSet("showcacheupdates", "0");
256 		} //end if
257 		if (LibVarGetValue("showmemoryusage"))
258 		{
259 			PrintUsedMemorySize();
260 			LibVarSet("showmemoryusage", "0");
261 		} //end if
262 		if (LibVarGetValue("memorydump"))
263 		{
264 			PrintMemoryLabels();
265 			LibVarSet("memorydump", "0");
266 		} //end if
267 	} //end if
268 	//
269 	if (saveroutingcache->value)
270 	{
271 		AAS_WriteRouteCache();
272 		LibVarSet("saveroutingcache", "0");
273 	} //end if
274 	//
275 	aasworld.numframes++;
276 	return BLERR_NOERROR;
277 } //end of the function AAS_StartFrame
278 //===========================================================================
279 //
280 // Parameter:				-
281 // Returns:					-
282 // Changes Globals:		-
283 //===========================================================================
AAS_Time(void)284 float AAS_Time(void)
285 {
286 	return aasworld.time;
287 } //end of the function AAS_Time
288 //===========================================================================
289 //
290 // Parameter:			-
291 // Returns:				-
292 // Changes Globals:		-
293 //===========================================================================
AAS_ProjectPointOntoVector(vec3_t point,vec3_t vStart,vec3_t vEnd,vec3_t vProj)294 void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
295 {
296 	vec3_t pVec, vec;
297 
298 	VectorSubtract( point, vStart, pVec );
299 	VectorSubtract( vEnd, vStart, vec );
300 	VectorNormalize( vec );
301 	// project onto the directional vector for this segment
302 	VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
303 } //end of the function AAS_ProjectPointOntoVector
304 //===========================================================================
305 //
306 // Parameter:				-
307 // Returns:					-
308 // Changes Globals:		-
309 //===========================================================================
AAS_LoadFiles(const char * mapname)310 int AAS_LoadFiles(const char *mapname)
311 {
312 	int errnum;
313 	char aasfile[MAX_PATH];
314 //	char bspfile[MAX_PATH];
315 
316 	strcpy(aasworld.mapname, mapname);
317 	//NOTE: first reset the entity links into the AAS areas and BSP leaves
318 	// the AAS link heap and BSP link heap are reset after respectively the
319 	// AAS file and BSP file are loaded
320 	AAS_ResetEntityLinks();
321 	// load bsp info
322 	AAS_LoadBSPFile();
323 
324 	//load the aas file
325 	Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname);
326 	errnum = AAS_LoadAASFile(aasfile);
327 	if (errnum != BLERR_NOERROR)
328 		return errnum;
329 
330 	botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile);
331 	strncpy(aasworld.filename, aasfile, MAX_PATH);
332 	return BLERR_NOERROR;
333 } //end of the function AAS_LoadFiles
334 //===========================================================================
335 // called everytime a map changes
336 //
337 // Parameter:				-
338 // Returns:					-
339 // Changes Globals:		-
340 //===========================================================================
AAS_LoadMap(const char * mapname)341 int AAS_LoadMap(const char *mapname)
342 {
343 	int	errnum;
344 
345 	//if no mapname is provided then the string indexes are updated
346 	if (!mapname)
347 	{
348 		return 0;
349 	} //end if
350 	//
351 	aasworld.initialized = qfalse;
352 	//NOTE: free the routing caches before loading a new map because
353 	// to free the caches the old number of areas, number of clusters
354 	// and number of areas in a clusters must be available
355 	AAS_FreeRoutingCaches();
356 	//load the map
357 	errnum = AAS_LoadFiles(mapname);
358 	if (errnum != BLERR_NOERROR)
359 	{
360 		aasworld.loaded = qfalse;
361 		return errnum;
362 	} //end if
363 	//
364 	AAS_InitSettings();
365 	//initialize the AAS link heap for the new map
366 	AAS_InitAASLinkHeap();
367 	//initialize the AAS linked entities for the new map
368 	AAS_InitAASLinkedEntities();
369 	//initialize reachability for the new map
370 	AAS_InitReachability();
371 	//initialize the alternative routing
372 	AAS_InitAlternativeRouting();
373 	//everything went ok
374 	return 0;
375 } //end of the function AAS_LoadMap
376 //===========================================================================
377 // called when the library is first loaded
378 //
379 // Parameter:				-
380 // Returns:					-
381 // Changes Globals:		-
382 //===========================================================================
AAS_Setup(void)383 int AAS_Setup(void)
384 {
385 	aasworld.maxclients = (int) LibVarValue("maxclients", "128");
386 	aasworld.maxentities = (int) LibVarValue("maxentities", "1024");
387 	// as soon as it's set to 1 the routing cache will be saved
388 	saveroutingcache = LibVar("saveroutingcache", "0");
389 	//allocate memory for the entities
390 	if (aasworld.entities) FreeMemory(aasworld.entities);
391 	aasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t));
392 	//invalidate all the entities
393 	AAS_InvalidateEntities();
394 	//force some recalculations
395 	//LibVarSet("forceclustering", "1");			//force clustering calculation
396 	//LibVarSet("forcereachability", "1");		//force reachability calculation
397 	aasworld.numframes = 0;
398 	return BLERR_NOERROR;
399 } //end of the function AAS_Setup
400 //===========================================================================
401 //
402 // Parameter:				-
403 // Returns:					-
404 // Changes Globals:		-
405 //===========================================================================
AAS_Shutdown(void)406 void AAS_Shutdown(void)
407 {
408 	AAS_ShutdownAlternativeRouting();
409 	//
410 	AAS_DumpBSPData();
411 	//free routing caches
412 	AAS_FreeRoutingCaches();
413 	//free aas link heap
414 	AAS_FreeAASLinkHeap();
415 	//free aas linked entities
416 	AAS_FreeAASLinkedEntities();
417 	//free the aas data
418 	AAS_DumpAASData();
419 	//free the entities
420 	if (aasworld.entities) FreeMemory(aasworld.entities);
421 	//clear the aasworld structure
422 	Com_Memset(&aasworld, 0, sizeof(aas_t));
423 	//aas has not been initialized
424 	aasworld.initialized = qfalse;
425 	//NOTE: as soon as a new .bsp file is loaded the .bsp file memory is
426 	// freed an reallocated, so there's no need to free that memory here
427 	//print shutdown
428 	botimport.Print(PRT_MESSAGE, "AAS shutdown.\n");
429 } //end of the function AAS_Shutdown
430