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_cluster.c
32  *
33  * desc:		area clustering
34  *
35  *
36  *****************************************************************************/
37 
38 #include "../qcommon/q_shared.h"
39 #include "l_memory.h"
40 #include "l_script.h"
41 #include "l_precomp.h"
42 #include "l_struct.h"
43 #include "l_log.h"
44 #include "l_memory.h"
45 #include "l_libvar.h"
46 #include "aasfile.h"
47 #include "botlib.h"
48 #include "be_aas.h"
49 #include "be_aas_funcs.h"
50 #include "be_aas_def.h"
51 #include "be_aas_cluster.h"
52 
53 extern botlib_import_t botimport;
54 
55 #define AAS_MAX_PORTALS                 65536
56 #define AAS_MAX_PORTALINDEXSIZE         65536
57 #define AAS_MAX_CLUSTERS                65536
58 //
59 #define MAX_PORTALAREAS         1024
60 
61 // do not flood through area faces, only use reachabilities
62 int nofaceflood = qtrue;
63 
64 //===========================================================================
65 //
66 // Parameter:				-
67 // Returns:					-
68 // Changes Globals:		-
69 //===========================================================================
AAS_RemoveClusterAreas(void)70 void AAS_RemoveClusterAreas( void ) {
71 	int i;
72 
73 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
74 	{
75 		( *aasworld ).areasettings[i].cluster = 0;
76 	} //end for
77 } //end of the function AAS_RemoveClusterAreas
78 //===========================================================================
79 //
80 // Parameter:				-
81 // Returns:					-
82 // Changes Globals:		-
83 //===========================================================================
AAS_ClearCluster(int clusternum)84 void AAS_ClearCluster( int clusternum ) {
85 	int i;
86 
87 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
88 	{
89 		if ( ( *aasworld ).areasettings[i].cluster == clusternum ) {
90 			( *aasworld ).areasettings[i].cluster = 0;
91 		} //end if
92 	} //end for
93 } //end of the function AAS_ClearCluster
94 //===========================================================================
95 //
96 // Parameter:				-
97 // Returns:					-
98 // Changes Globals:		-
99 //===========================================================================
AAS_RemovePortalsClusterReference(int clusternum)100 void AAS_RemovePortalsClusterReference( int clusternum ) {
101 	int portalnum;
102 
103 	for ( portalnum = 1; portalnum < ( *aasworld ).numportals; portalnum++ )
104 	{
105 		if ( ( *aasworld ).portals[portalnum].frontcluster == clusternum ) {
106 			( *aasworld ).portals[portalnum].frontcluster = 0;
107 		} //end if
108 		if ( ( *aasworld ).portals[portalnum].backcluster == clusternum ) {
109 			( *aasworld ).portals[portalnum].backcluster = 0;
110 		} //end if
111 	} //end for
112 } //end of the function AAS_RemovePortalsClusterReference
113 //===========================================================================
114 //
115 // Parameter:				-
116 // Returns:					-
117 // Changes Globals:		-
118 //===========================================================================
AAS_UpdatePortal(int areanum,int clusternum)119 int AAS_UpdatePortal( int areanum, int clusternum ) {
120 	int portalnum;
121 	aas_portal_t *portal;
122 	aas_cluster_t *cluster;
123 
124 	//find the portal of the area
125 	for ( portalnum = 1; portalnum < ( *aasworld ).numportals; portalnum++ )
126 	{
127 		if ( ( *aasworld ).portals[portalnum].areanum == areanum ) {
128 			break;
129 		}
130 	} //end for
131 	  //
132 	if ( portalnum == ( *aasworld ).numportals ) {
133 		AAS_Error( "no portal of area %d\n", areanum );
134 		return qtrue;
135 	} //end if
136 	  //
137 	portal = &( *aasworld ).portals[portalnum];
138 	//if the portal is already fully updated
139 	if ( portal->frontcluster == clusternum ) {
140 		return qtrue;
141 	}
142 	if ( portal->backcluster == clusternum ) {
143 		return qtrue;
144 	}
145 	//if the portal has no front cluster yet
146 	if ( !portal->frontcluster ) {
147 		portal->frontcluster = clusternum;
148 	} //end if
149 	  //if the portal has no back cluster yet
150 	else if ( !portal->backcluster ) {
151 		portal->backcluster = clusternum;
152 	} //end else if
153 	else
154 	{
155 		Log_Write( "portal using area %d is separating more than two clusters\r\n", areanum );
156 		//remove the cluster portal flag contents
157 		( *aasworld ).areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
158 		return qfalse;
159 	} //end else
160 	if ( ( *aasworld ).portalindexsize >= AAS_MAX_PORTALINDEXSIZE ) {
161 		AAS_Error( "AAS_MAX_PORTALINDEXSIZE\n" );
162 		return qtrue;
163 	} //end if
164 	  //set the area cluster number to the negative portal number
165 	( *aasworld ).areasettings[areanum].cluster = -portalnum;
166 	//add the portal to the cluster using the portal index
167 	cluster = &( *aasworld ).clusters[clusternum];
168 	( *aasworld ).portalindex[cluster->firstportal + cluster->numportals] = portalnum;
169 	( *aasworld ).portalindexsize++;
170 	cluster->numportals++;
171 	return qtrue;
172 } //end of the function AAS_UpdatePortal
173 //===========================================================================
174 //
175 // Parameter:				-
176 // Returns:					-
177 // Changes Globals:		-
178 //===========================================================================
AAS_FloodClusterAreas_r(int areanum,int clusternum)179 int AAS_FloodClusterAreas_r( int areanum, int clusternum ) {
180 	aas_area_t *area;
181 	aas_face_t *face;
182 	int facenum, i;
183 
184 	//
185 	if ( areanum <= 0 || areanum >= ( *aasworld ).numareas ) {
186 		AAS_Error( "AAS_FloodClusterAreas_r: areanum out of range\n" );
187 		return qfalse;
188 	} //end if
189 	  //if the area is already part of a cluster
190 	if ( ( *aasworld ).areasettings[areanum].cluster > 0 ) {
191 		if ( ( *aasworld ).areasettings[areanum].cluster == clusternum ) {
192 			return qtrue;
193 		}
194 		//
195 		//there's a reachability going from one cluster to another only in one direction
196 		//
197 		AAS_Error( "cluster %d touched cluster %d at area %d\n",
198 				   clusternum, ( *aasworld ).areasettings[areanum].cluster, areanum );
199 		return qfalse;
200 	} //end if
201 	  //don't add the cluster portal areas to the clusters
202 	if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
203 		return AAS_UpdatePortal( areanum, clusternum );
204 	} //end if
205 	  //set the area cluster number
206 	( *aasworld ).areasettings[areanum].cluster = clusternum;
207 	( *aasworld ).areasettings[areanum].clusterareanum =
208 		( *aasworld ).clusters[clusternum].numareas;
209 	//the cluster has an extra area
210 	( *aasworld ).clusters[clusternum].numareas++;
211 
212 	area = &( *aasworld ).areas[areanum];
213 	//use area faces to flood into adjacent areas
214 	if ( !nofaceflood ) {
215 		for ( i = 0; i < area->numfaces; i++ )
216 		{
217 			facenum = abs( ( *aasworld ).faceindex[area->firstface + i] );
218 			face = &( *aasworld ).faces[facenum];
219 			if ( face->frontarea == areanum ) {
220 				if ( face->backarea ) {
221 					if ( !AAS_FloodClusterAreas_r( face->backarea, clusternum ) ) {
222 						return qfalse;
223 					}
224 				}
225 			} //end if
226 			else
227 			{
228 				if ( face->frontarea ) {
229 					if ( !AAS_FloodClusterAreas_r( face->frontarea, clusternum ) ) {
230 						return qfalse;
231 					}
232 				}
233 			} //end else
234 		} //end for
235 	}
236 	//use the reachabilities to flood into other areas
237 	for ( i = 0; i < ( *aasworld ).areasettings[areanum].numreachableareas; i++ )
238 	{
239 		if ( !( *aasworld ).reachability[
240 				 ( *aasworld ).areasettings[areanum].firstreachablearea + i].areanum ) {
241 			continue;
242 		} //end if
243 		if ( !AAS_FloodClusterAreas_r( ( *aasworld ).reachability[
244 										   ( *aasworld ).areasettings[areanum].firstreachablearea + i].areanum, clusternum ) ) {
245 			return qfalse;
246 		}
247 	} //end for
248 	return qtrue;
249 } //end of the function AAS_FloodClusterAreas_r
250 //===========================================================================
251 // try to flood from all areas without cluster into areas with a cluster set
252 //
253 // Parameter:				-
254 // Returns:					-
255 // Changes Globals:		-
256 //===========================================================================
AAS_FloodClusterAreasUsingReachabilities(int clusternum)257 int AAS_FloodClusterAreasUsingReachabilities( int clusternum ) {
258 	int i, j, areanum;
259 
260 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
261 	{
262 		//if this area already has a cluster set
263 		if ( ( *aasworld ).areasettings[i].cluster ) {
264 			continue;
265 		}
266 		//if this area is a cluster portal
267 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
268 			continue;
269 		}
270 		//loop over the reachable areas from this area
271 		for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
272 		{
273 			//the reachable area
274 			areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
275 			//if this area is a cluster portal
276 			if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
277 				continue;
278 			}
279 			//if this area has a cluster set
280 			if ( ( *aasworld ).areasettings[areanum].cluster ) {
281 				if ( !AAS_FloodClusterAreas_r( i, clusternum ) ) {
282 					return qfalse;
283 				}
284 				i = 0;
285 				break;
286 			} //end if
287 		} //end for
288 	} //end for
289 	return qtrue;
290 } //end of the function AAS_FloodClusterAreasUsingReachabilities
291 //===========================================================================
292 //
293 // Parameter:			-
294 // Returns:				-
295 // Changes Globals:		-
296 //===========================================================================
AAS_NumberClusterPortals(int clusternum)297 void AAS_NumberClusterPortals( int clusternum ) {
298 	int i, portalnum;
299 	aas_cluster_t *cluster;
300 	aas_portal_t *portal;
301 
302 	cluster = &( *aasworld ).clusters[clusternum];
303 	for ( i = 0; i < cluster->numportals; i++ )
304 	{
305 		portalnum = ( *aasworld ).portalindex[cluster->firstportal + i];
306 		portal = &( *aasworld ).portals[portalnum];
307 		if ( portal->frontcluster == clusternum ) {
308 			portal->clusterareanum[0] = cluster->numareas++;
309 		} //end if
310 		else
311 		{
312 			portal->clusterareanum[1] = cluster->numareas++;
313 		} //end else
314 	} //end for
315 } //end of the function AAS_NumberClusterPortals
316 //===========================================================================
317 //
318 // Parameter:			-
319 // Returns:				-
320 // Changes Globals:		-
321 //===========================================================================
AAS_NumberClusterAreas(int clusternum)322 void AAS_NumberClusterAreas( int clusternum ) {
323 	int i, portalnum;
324 	aas_cluster_t *cluster;
325 	aas_portal_t *portal;
326 
327 	( *aasworld ).clusters[clusternum].numareas = 0;
328 	( *aasworld ).clusters[clusternum].numreachabilityareas = 0;
329 	//number all areas in this cluster WITH reachabilities
330 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
331 	{
332 		//
333 		if ( ( *aasworld ).areasettings[i].cluster != clusternum ) {
334 			continue;
335 		}
336 		//
337 		if ( !AAS_AreaReachability( i ) ) {
338 			continue;
339 		}
340 		//
341 		( *aasworld ).areasettings[i].clusterareanum = ( *aasworld ).clusters[clusternum].numareas;
342 		//the cluster has an extra area
343 		( *aasworld ).clusters[clusternum].numareas++;
344 		( *aasworld ).clusters[clusternum].numreachabilityareas++;
345 	} //end for
346 	  //number all portals in this cluster WITH reachabilities
347 	cluster = &( *aasworld ).clusters[clusternum];
348 	for ( i = 0; i < cluster->numportals; i++ )
349 	{
350 		portalnum = ( *aasworld ).portalindex[cluster->firstportal + i];
351 		portal = &( *aasworld ).portals[portalnum];
352 		if ( !AAS_AreaReachability( portal->areanum ) ) {
353 			continue;
354 		}
355 		if ( portal->frontcluster == clusternum ) {
356 			portal->clusterareanum[0] = cluster->numareas++;
357 			( *aasworld ).clusters[clusternum].numreachabilityareas++;
358 		} //end if
359 		else
360 		{
361 			portal->clusterareanum[1] = cluster->numareas++;
362 			( *aasworld ).clusters[clusternum].numreachabilityareas++;
363 		} //end else
364 	} //end for
365 	  //number all areas in this cluster WITHOUT reachabilities
366 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
367 	{
368 		//
369 		if ( ( *aasworld ).areasettings[i].cluster != clusternum ) {
370 			continue;
371 		}
372 		//
373 		if ( AAS_AreaReachability( i ) ) {
374 			continue;
375 		}
376 		//
377 		( *aasworld ).areasettings[i].clusterareanum = ( *aasworld ).clusters[clusternum].numareas;
378 		//the cluster has an extra area
379 		( *aasworld ).clusters[clusternum].numareas++;
380 	} //end for
381 	  //number all portals in this cluster WITHOUT reachabilities
382 	cluster = &( *aasworld ).clusters[clusternum];
383 	for ( i = 0; i < cluster->numportals; i++ )
384 	{
385 		portalnum = ( *aasworld ).portalindex[cluster->firstportal + i];
386 		portal = &( *aasworld ).portals[portalnum];
387 		if ( AAS_AreaReachability( portal->areanum ) ) {
388 			continue;
389 		}
390 		if ( portal->frontcluster == clusternum ) {
391 			portal->clusterareanum[0] = cluster->numareas++;
392 		} //end if
393 		else
394 		{
395 			portal->clusterareanum[1] = cluster->numareas++;
396 		} //end else
397 	} //end for
398 } //end of the function AAS_NumberClusterAreas
399 //===========================================================================
400 //
401 // Parameter:			-
402 // Returns:				-
403 // Changes Globals:		-
404 //===========================================================================
AAS_FindClusters(void)405 int AAS_FindClusters( void ) {
406 	int i;
407 	aas_cluster_t *cluster;
408 
409 	AAS_RemoveClusterAreas();
410 	//
411 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
412 	{
413 		//if the area is already part of a cluster
414 		if ( ( *aasworld ).areasettings[i].cluster ) {
415 			continue;
416 		}
417 		// if not flooding through faces only use areas that have reachabilities
418 		if ( nofaceflood ) {
419 			if ( !( *aasworld ).areasettings[i].numreachableareas ) {
420 				continue;
421 			}
422 		} //end if
423 		  //if the area is a cluster portal
424 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
425 			continue;
426 		}
427 		if ( ( *aasworld ).numclusters >= AAS_MAX_CLUSTERS ) {
428 			AAS_Error( "AAS_MAX_CLUSTERS\n" );
429 			return qfalse;
430 		} //end if
431 		cluster = &( *aasworld ).clusters[( *aasworld ).numclusters];
432 		cluster->numareas = 0;
433 		cluster->numreachabilityareas = 0;
434 		cluster->firstportal = ( *aasworld ).portalindexsize;
435 		cluster->numportals = 0;
436 		//flood the areas in this cluster
437 		if ( !AAS_FloodClusterAreas_r( i, ( *aasworld ).numclusters ) ) {
438 			return qfalse;
439 		}
440 		if ( !AAS_FloodClusterAreasUsingReachabilities( ( *aasworld ).numclusters ) ) {
441 			return qfalse;
442 		}
443 		//number the cluster areas
444 		//AAS_NumberClusterPortals((*aasworld).numclusters);
445 		AAS_NumberClusterAreas( ( *aasworld ).numclusters );
446 		//Log_Write("cluster %d has %d areas\r\n", (*aasworld).numclusters, cluster->numareas);
447 		( *aasworld ).numclusters++;
448 	} //end for
449 	return qtrue;
450 } //end of the function AAS_FindClusters
451 //===========================================================================
452 //
453 // Parameter:				-
454 // Returns:					-
455 // Changes Globals:		-
456 //===========================================================================
AAS_CreatePortals(void)457 void AAS_CreatePortals( void ) {
458 	int i;
459 	aas_portal_t *portal;
460 
461 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
462 	{
463 		//if the area is a cluster portal
464 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
465 			if ( ( *aasworld ).numportals >= AAS_MAX_PORTALS ) {
466 				AAS_Error( "AAS_MAX_PORTALS\n" );
467 				return;
468 			} //end if
469 			portal = &( *aasworld ).portals[( *aasworld ).numportals];
470 			portal->areanum = i;
471 			portal->frontcluster = 0;
472 			portal->backcluster = 0;
473 			Log_Write( "portal %d: area %d\r\n", ( *aasworld ).numportals, portal->areanum );
474 			( *aasworld ).numportals++;
475 		} //end if
476 	} //end for
477 } //end of the function AAS_CreatePortals
478 /*
479 //===========================================================================
480 //
481 // Parameter:				-
482 // Returns:					-
483 // Changes Globals:		-
484 //===========================================================================
485 int AAS_MapContainsTeleporters(void)
486 {
487 	bsp_entity_t *entities, *ent;
488 	char *classname;
489 
490 	entities = AAS_ParseBSPEntities();
491 
492 	for (ent = entities; ent; ent = ent->next)
493 	{
494 		classname = AAS_ValueForBSPEpairKey(ent, "classname");
495 		if (classname && !strcmp(classname, "misc_teleporter"))
496 		{
497 			AAS_FreeBSPEntities(entities);
498 			return qtrue;
499 		} //end if
500 	} //end for
501 	return qfalse;
502 } //end of the function AAS_MapContainsTeleporters
503 //===========================================================================
504 //
505 // Parameter:				-
506 // Returns:					-
507 // Changes Globals:		-
508 //===========================================================================
509 int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2)
510 {
511 	int i, j, edgenum;
512 	aas_plane_t *plane1, *plane2;
513 	aas_edge_t *edge;
514 
515 
516 	plane1 = &(*aasworld).planes[face1->planenum ^ side1];
517 	plane2 = &(*aasworld).planes[face2->planenum ^ side2];
518 
519 	//check if one of the points of face1 is at the back of the plane of face2
520 	for (i = 0; i < face1->numedges; i++)
521 	{
522 		edgenum = abs((*aasworld).edgeindex[face1->firstedge + i]);
523 		edge = &(*aasworld).edges[edgenum];
524 		for (j = 0; j < 2; j++)
525 		{
526 			if (DotProduct(plane2->normal, (*aasworld).vertexes[edge->v[j]]) -
527 							plane2->dist < -0.01) return qtrue;
528 		} //end for
529 	} //end for
530 	for (i = 0; i < face2->numedges; i++)
531 	{
532 		edgenum = abs((*aasworld).edgeindex[face2->firstedge + i]);
533 		edge = &(*aasworld).edges[edgenum];
534 		for (j = 0; j < 2; j++)
535 		{
536 			if (DotProduct(plane1->normal, (*aasworld).vertexes[edge->v[j]]) -
537 							plane1->dist < -0.01) return qtrue;
538 		} //end for
539 	} //end for
540 
541 	return qfalse;
542 } //end of the function AAS_NonConvexFaces
543 //===========================================================================
544 //
545 // Parameter:				-
546 // Returns:					-
547 // Changes Globals:		-
548 //===========================================================================
549 qboolean AAS_CanMergeAreas(int *areanums, int numareas)
550 {
551 	int i, j, s, face1num, face2num, side1, side2, fn1, fn2;
552 	aas_face_t *face1, *face2;
553 	aas_area_t *area1, *area2;
554 
555 	for (i = 0; i < numareas; i++)
556 	{
557 		area1 = &(*aasworld).areas[areanums[i]];
558 		for (fn1 = 0; fn1 < area1->numfaces; fn1++)
559 		{
560 			face1num = abs((*aasworld).faceindex[area1->firstface + fn1]);
561 			face1 = &(*aasworld).faces[face1num];
562 			side1 = face1->frontarea != areanums[i];
563 			//check if the face isn't a shared one with one of the other areas
564 			for (s = 0; s < numareas; s++)
565 			{
566 				if (s == i) continue;
567 				if (face1->frontarea == s || face1->backarea == s) break;
568 			} //end for
569 			//if the face was a shared one
570 			if (s != numareas) continue;
571 			//
572 			for (j = 0; j < numareas; j++)
573 			{
574 				if (j == i) continue;
575 				area2 = &(*aasworld).areas[areanums[j]];
576 				for (fn2 = 0; fn2 < area2->numfaces; fn2++)
577 				{
578 					face2num = abs((*aasworld).faceindex[area2->firstface + fn2]);
579 					face2 = &(*aasworld).faces[face2num];
580 					side2 = face2->frontarea != areanums[j];
581 					//check if the face isn't a shared one with one of the other areas
582 					for (s = 0; s < numareas; s++)
583 					{
584 						if (s == j) continue;
585 						if (face2->frontarea == s || face2->backarea == s) break;
586 					} //end for
587 					//if the face was a shared one
588 					if (s != numareas) continue;
589 					//
590 					if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse;
591 				} //end for
592 			} //end for
593 		} //end for
594 	} //end for
595 	return qtrue;
596 } //end of the function AAS_CanMergeAreas
597 //===========================================================================
598 //
599 // Parameter:				-
600 // Returns:					-
601 // Changes Globals:		-
602 //===========================================================================
603 qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum)
604 {
605 	int i;
606 	vec3_t edgevec1, edgevec2, normal1, normal2;
607 	float dist1, dist2;
608 	aas_plane_t *plane;
609 
610 	plane = &(*aasworld).planes[planenum];
611 	VectorSubtract((*aasworld).vertexes[edge1->v[1]], (*aasworld).vertexes[edge1->v[0]], edgevec1);
612 	VectorSubtract((*aasworld).vertexes[edge2->v[1]], (*aasworld).vertexes[edge2->v[0]], edgevec2);
613 	if (side1) VectorInverse(edgevec1);
614 	if (side2) VectorInverse(edgevec2);
615 	//
616 	CrossProduct(edgevec1, plane->normal, normal1);
617 	dist1 = DotProduct(normal1, (*aasworld).vertexes[edge1->v[0]]);
618 	CrossProduct(edgevec2, plane->normal, normal2);
619 	dist2 = DotProduct(normal2, (*aasworld).vertexes[edge2->v[0]]);
620 
621 	for (i = 0; i < 2; i++)
622 	{
623 		if (DotProduct((*aasworld).vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse;
624 	} //end for
625 	for (i = 0; i < 2; i++)
626 	{
627 		if (DotProduct((*aasworld).vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse;
628 	} //end for
629 	return qtrue;
630 } //end of the function AAS_NonConvexEdges
631 //===========================================================================
632 //
633 // Parameter:				-
634 // Returns:					-
635 // Changes Globals:		-
636 //===========================================================================
637 qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum)
638 {
639 	int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens;
640 	aas_face_t *face1, *face2, *otherface;
641 	aas_edge_t *edge1, *edge2;
642 
643 	for (i = 0; i < numfaces; i++)
644 	{
645 		face1 = &(*aasworld).faces[facenums[i]];
646 		for (en1 = 0; en1 < face1->numedges; en1++)
647 		{
648 			edgenum1 = (*aasworld).edgeindex[face1->firstedge + en1];
649 			side1 = (edgenum1 < 0) ^ (face1->planenum != planenum);
650 			edgenum1 = abs(edgenum1);
651 			edge1 = &(*aasworld).edges[edgenum1];
652 			//check if the edge is shared with another face
653 			for (s = 0; s < numfaces; s++)
654 			{
655 				if (s == i) continue;
656 				otherface = &(*aasworld).faces[facenums[s]];
657 				for (ens = 0; ens < otherface->numedges; ens++)
658 				{
659 					if (edgenum1 == abs((*aasworld).edgeindex[otherface->firstedge + ens])) break;
660 				} //end for
661 				if (ens != otherface->numedges) break;
662 			} //end for
663 			//if the edge was shared
664 			if (s != numfaces) continue;
665 			//
666 			for (j = 0; j < numfaces; j++)
667 			{
668 				if (j == i) continue;
669 				face2 = &(*aasworld).faces[facenums[j]];
670 				for (en2 = 0; en2 < face2->numedges; en2++)
671 				{
672 					edgenum2 = (*aasworld).edgeindex[face2->firstedge + en2];
673 					side2 = (edgenum2 < 0) ^ (face2->planenum != planenum);
674 					edgenum2 = abs(edgenum2);
675 					edge2 = &(*aasworld).edges[edgenum2];
676 					//check if the edge is shared with another face
677 					for (s = 0; s < numfaces; s++)
678 					{
679 						if (s == i) continue;
680 						otherface = &(*aasworld).faces[facenums[s]];
681 						for (ens = 0; ens < otherface->numedges; ens++)
682 						{
683 							if (edgenum2 == abs((*aasworld).edgeindex[otherface->firstedge + ens])) break;
684 						} //end for
685 						if (ens != otherface->numedges) break;
686 					} //end for
687 					//if the edge was shared
688 					if (s != numfaces) continue;
689 					//
690 					if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse;
691 				} //end for
692 			} //end for
693 		} //end for
694 	} //end for
695 	return qtrue;
696 } //end of the function AAS_CanMergeFaces*/
697 //===========================================================================
698 //
699 // Parameter:				-
700 // Returns:					-
701 // Changes Globals:		-
702 //===========================================================================
AAS_ConnectedAreas_r(int * areanums,int numareas,int * connectedareas,int curarea)703 void AAS_ConnectedAreas_r( int *areanums, int numareas, int *connectedareas, int curarea ) {
704 	int i, j, otherareanum, facenum;
705 	aas_area_t *area;
706 	aas_face_t *face;
707 
708 	connectedareas[curarea] = qtrue;
709 	area = &( *aasworld ).areas[areanums[curarea]];
710 	for ( i = 0; i < area->numfaces; i++ )
711 	{
712 		facenum = abs( ( *aasworld ).faceindex[area->firstface + i] );
713 		face = &( *aasworld ).faces[facenum];
714 		//if the face is solid
715 		if ( face->faceflags & FACE_SOLID ) {
716 			continue;
717 		}
718 		//get the area at the other side of the face
719 		if ( face->frontarea != areanums[curarea] ) {
720 			otherareanum = face->frontarea;
721 		} else { otherareanum = face->backarea;}
722 		//check if the face is leading to one of the other areas
723 		for ( j = 0; j < numareas; j++ )
724 		{
725 			if ( areanums[j] == otherareanum ) {
726 				break;
727 			}
728 		} //end for
729 		  //if the face isn't leading to one of the other areas
730 		if ( j == numareas ) {
731 			continue;
732 		}
733 		//if the other area is already connected
734 		if ( connectedareas[j] ) {
735 			continue;
736 		}
737 		//recursively proceed with the other area
738 		AAS_ConnectedAreas_r( areanums, numareas, connectedareas, j );
739 	} //end for
740 } //end of the function AAS_ConnectedAreas_r
741 //===========================================================================
742 //
743 // Parameter:				-
744 // Returns:					-
745 // Changes Globals:		-
746 //===========================================================================
AAS_ConnectedAreas(int * areanums,int numareas)747 qboolean AAS_ConnectedAreas( int *areanums, int numareas ) {
748 	int connectedareas[MAX_PORTALAREAS], i;
749 
750 	memset( connectedareas, 0, sizeof( connectedareas ) );
751 	if ( numareas < 1 ) {
752 		return qfalse;
753 	}
754 	if ( numareas == 1 ) {
755 		return qtrue;
756 	}
757 	AAS_ConnectedAreas_r( areanums, numareas, connectedareas, 0 );
758 	for ( i = 0; i < numareas; i++ )
759 	{
760 		if ( !connectedareas[i] ) {
761 			return qfalse;
762 		}
763 	} //end for
764 	return qtrue;
765 } //end of the function AAS_ConnectedAreas
766 //===========================================================================
767 // gets adjacent areas with less presence types recursively
768 //
769 // Parameter:				-
770 // Returns:					-
771 // Changes Globals:		-
772 //===========================================================================
AAS_GetAdjacentAreasWithLessPresenceTypes_r(int * areanums,int numareas,int curareanum)773 int AAS_GetAdjacentAreasWithLessPresenceTypes_r( int *areanums, int numareas, int curareanum ) {
774 	int i, j, presencetype, otherpresencetype, otherareanum, facenum;
775 	aas_area_t *area;
776 	aas_face_t *face;
777 
778 	areanums[numareas++] = curareanum;
779 	area = &( *aasworld ).areas[curareanum];
780 	presencetype = ( *aasworld ).areasettings[curareanum].presencetype;
781 	for ( i = 0; i < area->numfaces; i++ )
782 	{
783 		facenum = abs( ( *aasworld ).faceindex[area->firstface + i] );
784 		face = &( *aasworld ).faces[facenum];
785 		//if the face is solid
786 		if ( face->faceflags & FACE_SOLID ) {
787 			continue;
788 		}
789 		//the area at the other side of the face
790 		if ( face->frontarea != curareanum ) {
791 			otherareanum = face->frontarea;
792 		} else { otherareanum = face->backarea;}
793 		//
794 		otherpresencetype = ( *aasworld ).areasettings[otherareanum].presencetype;
795 		//if the other area has less presence types
796 		if ( ( presencetype & ~otherpresencetype ) &&
797 			 !( otherpresencetype & ~presencetype ) ) {
798 			//check if the other area isn't already in the list
799 			for ( j = 0; j < numareas; j++ )
800 			{
801 				if ( otherareanum == areanums[j] ) {
802 					break;
803 				}
804 			} //end for
805 			  //if the other area isn't already in the list
806 			if ( j == numareas ) {
807 				if ( numareas >= MAX_PORTALAREAS ) {
808 					AAS_Error( "MAX_PORTALAREAS\n" );
809 					return numareas;
810 				} //end if
811 				numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r( areanums, numareas, otherareanum );
812 			} //end if
813 		} //end if
814 	} //end for
815 	return numareas;
816 } //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r
817 //===========================================================================
818 //
819 // Parameter:				-
820 // Returns:					-
821 // Changes Globals:		-
822 //===========================================================================
AAS_CheckAreaForPossiblePortals(int areanum)823 int AAS_CheckAreaForPossiblePortals( int areanum ) {
824 	int i, j, k, fen, ben, frontedgenum, backedgenum, facenum;
825 	int areanums[MAX_PORTALAREAS], numareas, otherareanum;
826 	int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS];
827 	int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS];
828 	int numfrontfaces, numbackfaces;
829 	int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS];
830 	int numfrontareas, numbackareas;
831 	int frontplanenum, backplanenum, faceplanenum;
832 	aas_area_t *area;
833 	aas_face_t *frontface, *backface, *face;
834 
835 	//if it isn't already a portal
836 	if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
837 		return 0;
838 	}
839 	//it must be a grounded area
840 	if ( !( ( *aasworld ).areasettings[areanum].areaflags & AREA_GROUNDED ) ) {
841 		return 0;
842 	}
843 	//
844 	memset( numareafrontfaces, 0, sizeof( numareafrontfaces ) );
845 	memset( numareabackfaces, 0, sizeof( numareabackfaces ) );
846 	numfrontfaces = numbackfaces = 0;
847 	numfrontareas = numbackareas = 0;
848 	frontplanenum = backplanenum = -1;
849 	//add any adjacent areas with less presence types
850 	numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r( areanums, 0, areanum );
851 	//
852 	for ( i = 0; i < numareas; i++ )
853 	{
854 		area = &( *aasworld ).areas[areanums[i]];
855 		for ( j = 0; j < area->numfaces; j++ )
856 		{
857 			facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
858 			face = &( *aasworld ).faces[facenum];
859 			//if the face is solid
860 			if ( face->faceflags & FACE_SOLID ) {
861 				continue;
862 			}
863 			//check if the face is shared with one of the other areas
864 			for ( k = 0; k < numareas; k++ )
865 			{
866 				if ( k == i ) {
867 					continue;
868 				}
869 				if ( face->frontarea == areanums[k] || face->backarea == areanums[k] ) {
870 					break;
871 				}
872 			} //end for
873 			  //if the face is shared
874 			if ( k != numareas ) {
875 				continue;
876 			}
877 			//the number of the area at the other side of the face
878 			if ( face->frontarea == areanums[i] ) {
879 				otherareanum = face->backarea;
880 			} else { otherareanum = face->frontarea;}
881 			//if the other area already is a cluter portal
882 			if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
883 				return 0;
884 			}
885 			//number of the plane of the area
886 			faceplanenum = face->planenum & ~1;
887 			//
888 			if ( frontplanenum < 0 || faceplanenum == frontplanenum ) {
889 				frontplanenum = faceplanenum;
890 				frontfacenums[numfrontfaces++] = facenum;
891 				for ( k = 0; k < numfrontareas; k++ )
892 				{
893 					if ( frontareanums[k] == otherareanum ) {
894 						break;
895 					}
896 				} //end for
897 				if ( k == numfrontareas ) {
898 					frontareanums[numfrontareas++] = otherareanum;
899 				}
900 				numareafrontfaces[i]++;
901 			} //end if
902 			else if ( backplanenum < 0 || faceplanenum == backplanenum ) {
903 				backplanenum = faceplanenum;
904 				backfacenums[numbackfaces++] = facenum;
905 				for ( k = 0; k < numbackareas; k++ )
906 				{
907 					if ( backareanums[k] == otherareanum ) {
908 						break;
909 					}
910 				} //end for
911 				if ( k == numbackareas ) {
912 					backareanums[numbackareas++] = otherareanum;
913 				}
914 				numareabackfaces[i]++;
915 			} //end else
916 			else
917 			{
918 				return 0;
919 			} //end else
920 		} //end for
921 	} //end for
922 	  //every area should have at least one front face and one back face
923 	for ( i = 0; i < numareas; i++ )
924 	{
925 		if ( !numareafrontfaces[i] || !numareabackfaces[i] ) {
926 			return 0;
927 		}
928 	} //end for
929 	  //the front areas should all be connected
930 	if ( !AAS_ConnectedAreas( frontareanums, numfrontareas ) ) {
931 		return 0;
932 	}
933 	//the back areas should all be connected
934 	if ( !AAS_ConnectedAreas( backareanums, numbackareas ) ) {
935 		return 0;
936 	}
937 	//none of the front faces should have a shared edge with a back face
938 	for ( i = 0; i < numfrontfaces; i++ )
939 	{
940 		frontface = &( *aasworld ).faces[frontfacenums[i]];
941 		for ( fen = 0; fen < frontface->numedges; fen++ )
942 		{
943 			frontedgenum = abs( ( *aasworld ).edgeindex[frontface->firstedge + fen] );
944 			for ( j = 0; j < numbackfaces; j++ )
945 			{
946 				backface = &( *aasworld ).faces[backfacenums[j]];
947 				for ( ben = 0; ben < backface->numedges; ben++ )
948 				{
949 					backedgenum = abs( ( *aasworld ).edgeindex[backface->firstedge + ben] );
950 					if ( frontedgenum == backedgenum ) {
951 						break;
952 					}
953 				} //end for
954 				if ( ben != backface->numedges ) {
955 					break;
956 				}
957 			} //end for
958 			if ( j != numbackfaces ) {
959 				break;
960 			}
961 		} //end for
962 		if ( fen != frontface->numedges ) {
963 			break;
964 		}
965 	} //end for
966 	if ( i != numfrontfaces ) {
967 		return 0;
968 	}
969 	//set the cluster portal contents
970 	for ( i = 0; i < numareas; i++ )
971 	{
972 		( *aasworld ).areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL;
973 		//this area can be used as a route portal
974 		( *aasworld ).areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL;
975 		Log_Write( "possible portal: %d\r\n", areanums[i] );
976 	} //end for
977 	  //
978 	return numareas;
979 } //end of the function AAS_CheckAreaForPossiblePortals
980 //===========================================================================
981 //
982 // Parameter:				-
983 // Returns:					-
984 // Changes Globals:		-
985 //===========================================================================
AAS_FindPossiblePortals(void)986 void AAS_FindPossiblePortals( void ) {
987 	int i, numpossibleportals;
988 
989 	numpossibleportals = 0;
990 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
991 	{
992 		numpossibleportals += AAS_CheckAreaForPossiblePortals( i );
993 	} //end for
994 	botimport.Print( PRT_MESSAGE, "\r%6d possible portals\n", numpossibleportals );
995 } //end of the function AAS_FindPossiblePortals
996 //===========================================================================
997 //
998 // Parameter:				-
999 // Returns:					-
1000 // Changes Globals:		-
1001 //===========================================================================
AAS_RemoveAllPortals(void)1002 void AAS_RemoveAllPortals( void ) {
1003 	int i;
1004 
1005 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1006 	{
1007 		( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1008 	} //end for
1009 } //end of the function AAS_RemoveAllPortals
1010 
1011 #if 0
1012 //===========================================================================
1013 //
1014 // Parameter:				-
1015 // Returns:					-
1016 // Changes Globals:		-
1017 //===========================================================================
1018 void AAS_FloodCluster_r( int areanum, int clusternum ) {
1019 	int i, otherareanum;
1020 	aas_face_t *face;
1021 	aas_area_t *area;
1022 
1023 	//set cluster mark
1024 	( *aasworld ).areasettings[areanum].cluster = clusternum;
1025 	//if the area is a portal
1026 	//if ((*aasworld).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return;
1027 	//
1028 	area = &( *aasworld ).areas[areanum];
1029 	//use area faces to flood into adjacent areas
1030 	for ( i = 0; i < area->numfaces; i++ )
1031 	{
1032 		face = &( *aasworld ).faces[abs( ( *aasworld ).faceindex[area->firstface + i] )];
1033 		//
1034 		if ( face->frontarea != areanum ) {
1035 			otherareanum = face->frontarea;
1036 		} else { otherareanum = face->backarea;}
1037 		//if there's no area at the other side
1038 		if ( !otherareanum ) {
1039 			continue;
1040 		}
1041 		//if the area is a portal
1042 		if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1043 			continue;
1044 		}
1045 		//if the area is already marked
1046 		if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1047 			continue;
1048 		}
1049 		//
1050 		AAS_FloodCluster_r( otherareanum, clusternum );
1051 	} //end for
1052 	  //use the reachabilities to flood into other areas
1053 	for ( i = 0; i < ( *aasworld ).areasettings[areanum].numreachableareas; i++ )
1054 	{
1055 		otherareanum = ( *aasworld ).reachability[
1056 			( *aasworld ).areasettings[areanum].firstreachablearea + i].areanum;
1057 		if ( !otherareanum ) {
1058 			continue;
1059 			AAS_Error( "reachability %d has zero area\n", ( *aasworld ).areasettings[areanum].firstreachablearea + i );
1060 		} //end if
1061 		  //if the area is a portal
1062 		if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1063 			continue;
1064 		}
1065 		//if the area is already marked
1066 		if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1067 			continue;
1068 		}
1069 		//
1070 		AAS_FloodCluster_r( otherareanum, clusternum );
1071 	} //end for
1072 } //end of the function AAS_FloodCluster_r
1073 //===========================================================================
1074 //
1075 // Parameter:				-
1076 // Returns:					-
1077 // Changes Globals:		-
1078 //===========================================================================
1079 void AAS_RemoveTeleporterPortals( void ) {
1080 	int i, j, areanum;
1081 
1082 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1083 	{
1084 		for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1085 		{
1086 			areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1087 			if ( ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT ) {
1088 				( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1089 				( *aasworld ).areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1090 				break;
1091 			} //end if
1092 		} //end for
1093 	} //end for
1094 } //end of the function AAS_RemoveTeleporterPortals
1095 //===========================================================================
1096 //
1097 // Parameter:				-
1098 // Returns:					-
1099 // Changes Globals:		-
1100 //===========================================================================
1101 void AAS_FloodClusterReachabilities( int clusternum ) {
1102 	int i, j, areanum;
1103 
1104 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1105 	{
1106 		//if this area already has a cluster set
1107 		if ( ( *aasworld ).areasettings[i].cluster ) {
1108 			continue;
1109 		}
1110 		//if this area is a cluster portal
1111 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
1112 			continue;
1113 		}
1114 		//loop over the reachable areas from this area
1115 		for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1116 		{
1117 			//the reachable area
1118 			areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1119 			//if this area is a cluster portal
1120 			if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1121 				continue;
1122 			}
1123 			//if this area has a cluster set
1124 			if ( ( *aasworld ).areasettings[areanum].cluster == clusternum ) {
1125 				AAS_FloodCluster_r( i, clusternum );
1126 				i = 0;
1127 				break;
1128 			} //end if
1129 		} //end for
1130 	} //end for
1131 } //end of the function AAS_FloodClusterReachabilities
1132 //===========================================================================
1133 //
1134 // Parameter:				-
1135 // Returns:					-
1136 // Changes Globals:		-
1137 //===========================================================================
1138 
1139 void AAS_RemoveNotClusterClosingPortals( void ) {
1140 	int i, j, k, facenum, otherareanum, nonclosingportals;
1141 	aas_area_t *area;
1142 	aas_face_t *face;
1143 
1144 	AAS_RemoveTeleporterPortals();
1145 	//
1146 	nonclosingportals = 0;
1147 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1148 	{
1149 		if ( !( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
1150 			continue;
1151 		}
1152 		//find a non-portal area adjacent to the portal area and flood
1153 		//the cluster from there
1154 		area = &( *aasworld ).areas[i];
1155 		for ( j = 0; j < area->numfaces; j++ )
1156 		{
1157 			facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
1158 			face = &( *aasworld ).faces[facenum];
1159 			//
1160 			if ( face->frontarea != i ) {
1161 				otherareanum = face->frontarea;
1162 			} else { otherareanum = face->backarea;}
1163 			//
1164 			if ( !otherareanum ) {
1165 				continue;
1166 			}
1167 			//
1168 			if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1169 				continue;
1170 			} //end if
1171 			  //reset all cluster fields
1172 			AAS_RemoveClusterAreas();
1173 			//
1174 			AAS_FloodCluster_r( otherareanum, 1 );
1175 			AAS_FloodClusterReachabilities( 1 );
1176 			//check if all adjacent non-portal areas have a cluster set
1177 			for ( k = 0; k < area->numfaces; k++ )
1178 			{
1179 				facenum = abs( ( *aasworld ).faceindex[area->firstface + k] );
1180 				face = &( *aasworld ).faces[facenum];
1181 				//
1182 				if ( face->frontarea != i ) {
1183 					otherareanum = face->frontarea;
1184 				} else { otherareanum = face->backarea;}
1185 				//
1186 				if ( !otherareanum ) {
1187 					continue;
1188 				}
1189 				//
1190 				if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1191 					continue;
1192 				} //end if
1193 				  //
1194 				if ( !( *aasworld ).areasettings[otherareanum].cluster ) {
1195 					break;
1196 				}
1197 			} //end for
1198 			  //if all adjacent non-portal areas have a cluster set then the portal
1199 			  //didn't seal a cluster
1200 			if ( k >= area->numfaces ) {
1201 				( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1202 				nonclosingportals++;
1203 				//recheck all the other portals again
1204 				i = 0;
1205 				break;
1206 			} //end if
1207 		} //end for
1208 	} //end for
1209 	botimport.Print( PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals );
1210 } //end of the function AAS_RemoveNotClusterClosingPortals*/
1211 //===========================================================================
1212 //
1213 // Parameter:				-
1214 // Returns:					-
1215 // Changes Globals:		-
1216 //===========================================================================
1217 
1218 void AAS_RemoveNotClusterClosingPortals( void ) {
1219 	int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters;
1220 	aas_area_t *area;
1221 	aas_face_t *face;
1222 
1223 	AAS_RemoveTeleporterPortals();
1224 	//
1225 	nonclosingportals = 0;
1226 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1227 	{
1228 		if ( !( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
1229 			continue;
1230 		}
1231 		//
1232 		numseperatedclusters = 0;
1233 		//reset all cluster fields
1234 		AAS_RemoveClusterAreas();
1235 		//find a non-portal area adjacent to the portal area and flood
1236 		//the cluster from there
1237 		area = &( *aasworld ).areas[i];
1238 		for ( j = 0; j < area->numfaces; j++ )
1239 		{
1240 			facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
1241 			face = &( *aasworld ).faces[facenum];
1242 			//
1243 			if ( face->frontarea != i ) {
1244 				otherareanum = face->frontarea;
1245 			} else { otherareanum = face->backarea;}
1246 			//if not solid at the other side of the face
1247 			if ( !otherareanum ) {
1248 				continue;
1249 			}
1250 			//don't flood into other portals
1251 			if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1252 				continue;
1253 			}
1254 			//if the area already has a cluster set
1255 			if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1256 				continue;
1257 			}
1258 			//another cluster is seperated by this portal
1259 			numseperatedclusters++;
1260 			//flood the cluster
1261 			AAS_FloodCluster_r( otherareanum, numseperatedclusters );
1262 			AAS_FloodClusterReachabilities( numseperatedclusters );
1263 		} //end for
1264 		  //use the reachabilities to flood into other areas
1265 		for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1266 		{
1267 			otherareanum = ( *aasworld ).reachability[
1268 				( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1269 			//this should never be qtrue but we check anyway
1270 			if ( !otherareanum ) {
1271 				continue;
1272 			}
1273 			//don't flood into other portals
1274 			if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1275 				continue;
1276 			}
1277 			//if the area already has a cluster set
1278 			if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1279 				continue;
1280 			}
1281 			//another cluster is seperated by this portal
1282 			numseperatedclusters++;
1283 			//flood the cluster
1284 			AAS_FloodCluster_r( otherareanum, numseperatedclusters );
1285 			AAS_FloodClusterReachabilities( numseperatedclusters );
1286 		} //end for
1287 		  //a portal must seperate no more and no less than 2 clusters
1288 		if ( numseperatedclusters != 2 ) {
1289 			( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1290 			nonclosingportals++;
1291 			//recheck all the other portals again
1292 			i = 0;
1293 		} //end if
1294 	} //end for
1295 	botimport.Print( PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals );
1296 } //end of the function AAS_RemoveNotClusterClosingPortals
1297 
1298 //===========================================================================
1299 //
1300 // Parameter:				-
1301 // Returns:					-
1302 // Changes Globals:		-
1303 //===========================================================================
1304 
1305 void AAS_AddTeleporterPortals( void ) {
1306 	int j, area2num, facenum, otherareanum;
1307 	char *target, *targetname, *classname;
1308 	bsp_entity_t *entities, *ent, *dest;
1309 	vec3_t origin, destorigin, mins, maxs, end;
1310 	vec3_t bbmins, bbmaxs;
1311 	aas_area_t *area;
1312 	aas_face_t *face;
1313 	aas_trace_t trace;
1314 	aas_link_t *areas, *link;
1315 
1316 	entities = AAS_ParseBSPEntities();
1317 
1318 	for ( ent = entities; ent; ent = ent->next )
1319 	{
1320 		classname = AAS_ValueForBSPEpairKey( ent, "classname" );
1321 		if ( classname && !strcmp( classname, "misc_teleporter" ) ) {
1322 			if ( !AAS_VectorForBSPEpairKey( ent, "origin", origin ) ) {
1323 				botimport.Print( PRT_ERROR, "teleporter (%s) without origin\n", target );
1324 				continue;
1325 			} //end if
1326 			  //
1327 			target = AAS_ValueForBSPEpairKey( ent, "target" );
1328 			if ( !target ) {
1329 				botimport.Print( PRT_ERROR, "teleporter (%s) without target\n", target );
1330 				continue;
1331 			} //end if
1332 			for ( dest = entities; dest; dest = dest->next )
1333 			{
1334 				classname = AAS_ValueForBSPEpairKey( dest, "classname" );
1335 				if ( classname && !strcmp( classname, "misc_teleporter_dest" ) ) {
1336 					targetname = AAS_ValueForBSPEpairKey( dest, "targetname" );
1337 					if ( targetname && !strcmp( targetname, target ) ) {
1338 						break;
1339 					} //end if
1340 				} //end if
1341 			} //end for
1342 			if ( !dest ) {
1343 				botimport.Print( PRT_ERROR, "teleporter without destination (%s)\n", target );
1344 				continue;
1345 			} //end if
1346 			if ( !AAS_VectorForBSPEpairKey( dest, "origin", destorigin ) ) {
1347 				botimport.Print( PRT_ERROR, "teleporter destination (%s) without origin\n", target );
1348 				continue;
1349 			} //end if
1350 			destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground
1351 			VectorCopy( destorigin, end );
1352 			end[2] -= 100;
1353 			trace = AAS_TraceClientBBox( destorigin, end, PRESENCE_CROUCH, -1 );
1354 			if ( trace.startsolid ) {
1355 				botimport.Print( PRT_ERROR, "teleporter destination (%s) in solid\n", target );
1356 				continue;
1357 			} //end if
1358 			VectorCopy( trace.endpos, destorigin );
1359 			area2num = AAS_PointAreaNum( destorigin );
1360 			//reset all cluster fields
1361 			for ( j = 0; j < ( *aasworld ).numareas; j++ )
1362 			{
1363 				( *aasworld ).areasettings[j].cluster = 0;
1364 			} //end for
1365 			  //
1366 			VectorSet( mins, -8, -8, 8 );
1367 			VectorSet( maxs, 8, 8, 24 );
1368 			//
1369 			AAS_PresenceTypeBoundingBox( PRESENCE_CROUCH, bbmins, bbmaxs );
1370 			//
1371 			VectorAdd( origin, mins, mins );
1372 			VectorAdd( origin, maxs, maxs );
1373 			//add bounding box size
1374 			VectorSubtract( mins, bbmaxs, mins );
1375 			VectorSubtract( maxs, bbmins, maxs );
1376 			//link an invalid (-1) entity
1377 			areas = AAS_AASLinkEntity( mins, maxs, -1 );
1378 			//
1379 			for ( link = areas; link; link = link->next_area )
1380 			{
1381 				if ( !AAS_AreaGrounded( link->areanum ) ) {
1382 					continue;
1383 				}
1384 				//add the teleporter portal mark
1385 				( *aasworld ).areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL |
1386 																	  AREACONTENTS_TELEPORTAL;
1387 			} //end for
1388 			  //
1389 			for ( link = areas; link; link = link->next_area )
1390 			{
1391 				if ( !AAS_AreaGrounded( link->areanum ) ) {
1392 					continue;
1393 				}
1394 				//find a non-portal area adjacent to the portal area and flood
1395 				//the cluster from there
1396 				area = &( *aasworld ).areas[link->areanum];
1397 				for ( j = 0; j < area->numfaces; j++ )
1398 				{
1399 					facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
1400 					face = &( *aasworld ).faces[facenum];
1401 					//
1402 					if ( face->frontarea != link->areanum ) {
1403 						otherareanum = face->frontarea;
1404 					} else { otherareanum = face->backarea;}
1405 					//
1406 					if ( !otherareanum ) {
1407 						continue;
1408 					}
1409 					//
1410 					if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1411 						continue;
1412 					} //end if
1413 					  //
1414 					AAS_FloodCluster_r( otherareanum, 1 );
1415 				} //end for
1416 			} //end for
1417 			  //if the teleport destination IS in the same cluster
1418 			if ( ( *aasworld ).areasettings[area2num].cluster ) {
1419 				for ( link = areas; link; link = link->next_area )
1420 				{
1421 					if ( !AAS_AreaGrounded( link->areanum ) ) {
1422 						continue;
1423 					}
1424 					//add the teleporter portal mark
1425 					( *aasworld ).areasettings[link->areanum].contents &= ~( AREACONTENTS_CLUSTERPORTAL |
1426 																			 AREACONTENTS_TELEPORTAL );
1427 				} //end for
1428 			} //end if
1429 		} //end if
1430 	} //end for
1431 	AAS_FreeBSPEntities( entities );
1432 } //end of the function AAS_AddTeleporterPortals
1433 
1434 //===========================================================================
1435 //
1436 // Parameter:				-
1437 // Returns:					-
1438 // Changes Globals:		-
1439 //===========================================================================
1440 void AAS_AddTeleporterPortals( void ) {
1441 	int i, j, areanum;
1442 
1443 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1444 	{
1445 		for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1446 		{
1447 			if ( ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT ) {
1448 				continue;
1449 			}
1450 			areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1451 			( *aasworld ).areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL;
1452 		} //end for
1453 	} //end for
1454 } //end of the function AAS_AddTeleporterPortals*/
1455 #endif
1456 //===========================================================================
1457 //
1458 // Parameter:				-
1459 // Returns:					-
1460 // Changes Globals:		-
1461 //===========================================================================
AAS_TestPortals(void)1462 int AAS_TestPortals( void ) {
1463 	int i;
1464 	aas_portal_t *portal;
1465 
1466 	for ( i = 1; i < ( *aasworld ).numportals; i++ )
1467 	{
1468 		portal = &( *aasworld ).portals[i];
1469 		if ( !portal->frontcluster ) {
1470 			( *aasworld ).areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1471 			Log_Write( "portal area %d has no front cluster\r\n", portal->areanum );
1472 			return qfalse;
1473 		} //end if
1474 		if ( !portal->backcluster ) {
1475 			( *aasworld ).areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1476 			Log_Write( "portal area %d has no back cluster\r\n", portal->areanum );
1477 			return qfalse;
1478 		} //end if
1479 	} //end for
1480 	return qtrue;
1481 } //end of the function
1482 //===========================================================================
1483 //
1484 // Parameter:				-
1485 // Returns:					-
1486 // Changes Globals:		-
1487 //===========================================================================
AAS_CountForcedClusterPortals(void)1488 void AAS_CountForcedClusterPortals( void ) {
1489 	int num, i;
1490 
1491 	num = 0;
1492 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1493 	{
1494 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
1495 			num++;
1496 		} //end if
1497 	} //end for
1498 	botimport.Print( PRT_MESSAGE, "%6d forced portals\n", num );
1499 } //end of the function AAS_CountForcedClusterPortals
1500 //===========================================================================
1501 //
1502 // Parameter:				-
1503 // Returns:					-
1504 // Changes Globals:		-
1505 //===========================================================================
AAS_CreateViewPortals(void)1506 void AAS_CreateViewPortals( void ) {
1507 	int i;
1508 
1509 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1510 	{
1511 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
1512 			( *aasworld ).areasettings[i].contents |= AREACONTENTS_VIEWPORTAL;
1513 		} //end if
1514 	} //end for
1515 } //end of the function AAS_CreateViewPortals
1516 //===========================================================================
1517 //
1518 // Parameter:				-
1519 // Returns:					-
1520 // Changes Globals:		-
1521 //===========================================================================
AAS_SetViewPortalsAsClusterPortals(void)1522 void AAS_SetViewPortalsAsClusterPortals( void ) {
1523 	int i;
1524 
1525 	for ( i = 1; i < ( *aasworld ).numareas; i++ )
1526 	{
1527 		if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_VIEWPORTAL ) {
1528 			( *aasworld ).areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL;
1529 		} //end if
1530 	} //end for
1531 } //end of the function AAS_SetViewPortalsAsClusterPortals
1532 //===========================================================================
1533 //
1534 // Parameter:				-
1535 // Returns:					-
1536 // Changes Globals:		-
1537 //===========================================================================
AAS_InitClustering(void)1538 void AAS_InitClustering( void ) {
1539 	int i, removedPortalAreas;
1540 	int n, total, numreachabilityareas;
1541 
1542 	if ( !( *aasworld ).loaded ) {
1543 		return;
1544 	}
1545 	//if there are clusters
1546 	if ( ( *aasworld ).numclusters >= 1 ) {
1547 #ifndef BSPC
1548 		//if clustering isn't forced
1549 		if ( !( (int)LibVarGetValue( "forceclustering" ) ) &&
1550 			 !( (int)LibVarGetValue( "forcereachability" ) ) ) {
1551 			return;
1552 		}
1553 #else
1554 		return;
1555 #endif
1556 	} //end if
1557 	  //
1558 	AAS_CountForcedClusterPortals();
1559 	//remove all the existing portals
1560 	//AAS_RemoveAllPortals();
1561 	//remove all area cluster marks
1562 	AAS_RemoveClusterAreas();
1563 	//find possible cluster portals
1564 	AAS_FindPossiblePortals();
1565 	//craete portals to for the bot view
1566 	AAS_CreateViewPortals();
1567 	//remove all portals that are not closing a cluster
1568 	//AAS_RemoveNotClusterClosingPortals();
1569 	//initialize portal memory
1570 	if ( ( *aasworld ).portals ) {
1571 		FreeMemory( ( *aasworld ).portals );
1572 	}
1573 	( *aasworld ).portals = (aas_portal_t *) GetClearedMemory( AAS_MAX_PORTALS * sizeof( aas_portal_t ) );
1574 	//initialize portal index memory
1575 	if ( ( *aasworld ).portalindex ) {
1576 		FreeMemory( ( *aasworld ).portalindex );
1577 	}
1578 	( *aasworld ).portalindex = (aas_portalindex_t *) GetClearedMemory( AAS_MAX_PORTALINDEXSIZE * sizeof( aas_portalindex_t ) );
1579 	//initialize cluster memory
1580 	if ( ( *aasworld ).clusters ) {
1581 		FreeMemory( ( *aasworld ).clusters );
1582 	}
1583 	( *aasworld ).clusters = (aas_cluster_t *) GetClearedMemory( AAS_MAX_CLUSTERS * sizeof( aas_cluster_t ) );
1584 	//
1585 	removedPortalAreas = 0;
1586 	botimport.Print( PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas );
1587 	while ( 1 )
1588 	{
1589 		botimport.Print( PRT_MESSAGE, "\r%6d", removedPortalAreas );
1590 		//initialize the number of portals and clusters
1591 		( *aasworld ).numportals = 1;     //portal 0 is a dummy
1592 		( *aasworld ).portalindexsize = 0;
1593 		( *aasworld ).numclusters = 1;        //cluster 0 is a dummy
1594 		//create the portals from the portal areas
1595 		AAS_CreatePortals();
1596 		//
1597 		removedPortalAreas++;
1598 		//find the clusters
1599 		if ( !AAS_FindClusters() ) {
1600 			continue;
1601 		}
1602 		//test the portals
1603 		if ( !AAS_TestPortals() ) {
1604 			continue;
1605 		}
1606 		//
1607 		break;
1608 	} //end while
1609 	botimport.Print( PRT_MESSAGE, "\n" );
1610 	//the AAS file should be saved
1611 	( *aasworld ).savefile = qtrue;
1612 	// report cluster info
1613 	botimport.Print( PRT_MESSAGE, "%6d portals created\n", ( *aasworld ).numportals );
1614 	botimport.Print( PRT_MESSAGE, "%6d clusters created\n", ( *aasworld ).numclusters );
1615 	for ( i = 1; i < ( *aasworld ).numclusters; i++ )
1616 	{
1617 		botimport.Print( PRT_MESSAGE, "cluster %d has %d reachability areas\n", i,
1618 						 ( *aasworld ).clusters[i].numreachabilityareas );
1619 	} //end for
1620 	  // report AAS file efficiency
1621 	numreachabilityareas = 0;
1622 	total = 0;
1623 	for ( i = 0; i < ( *aasworld ).numclusters; i++ ) {
1624 		n = ( *aasworld ).clusters[i].numreachabilityareas;
1625 		numreachabilityareas += n;
1626 		total += n * n;
1627 	}
1628 	total += numreachabilityareas * ( *aasworld ).numportals;
1629 	//
1630 	botimport.Print( PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas );
1631 	botimport.Print( PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3 );
1632 } //end of the function AAS_InitClustering
1633