/*
===========================================================================

Return to Castle Wolfenstein single player GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).  

RTCW SP Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

RTCW SP Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.

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.

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.

===========================================================================
*/

//===========================================================================
//
// Name:				aas_map.c
// Function:
// Programmer:		Mr Elusive (MrElusive@demigod.demon.nl)
// Last update:	1997-12-03
// Tab Size:		3
//===========================================================================

#include "qbsp.h"
#include "l_mem.h"
#include "../botlib/aasfile.h"           //aas_bbox_t
#include "aas_store.h"       //AAS_MAX_BBOXES
#include "aas_cfg.h"
#include "../game/surfaceflags.h"

#define SPAWNFLAG_NOT_EASY          0x00000100
#define SPAWNFLAG_NOT_MEDIUM        0x00000200
#define SPAWNFLAG_NOT_HARD          0x00000400
#define SPAWNFLAG_NOT_DEATHMATCH    0x00000800
#define SPAWNFLAG_NOT_COOP          0x00001000

#define STATE_TOP               0
#define STATE_BOTTOM            1
#define STATE_UP                2
#define STATE_DOWN          3

#define DOOR_START_OPEN     1
#define DOOR_REVERSE            2
#define DOOR_CRUSHER            4
#define DOOR_NOMONSTER      8
#define DOOR_TOGGLE         32
#define DOOR_X_AXIS         64
#define DOOR_Y_AXIS         128

#define BBOX_NORMAL_EPSILON         0.00001

//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
vec_t BoxOriginDistanceFromPlane( vec3_t normal, vec3_t mins, vec3_t maxs, int side ) {
	vec3_t v1, v2;
	int i;

	if ( side ) {
		for ( i = 0; i < 3; i++ )
		{
			if ( normal[i] > BBOX_NORMAL_EPSILON ) {
				v1[i] = maxs[i];
			} else if ( normal[i] < -BBOX_NORMAL_EPSILON ) {
				v1[i] = mins[i];
			} else { v1[i] = 0;}
		} //end for
	} //end if
	else
	{
		for ( i = 0; i < 3; i++ )
		{
			if ( normal[i] > BBOX_NORMAL_EPSILON ) {
				v1[i] = mins[i];
			} else if ( normal[i] < -BBOX_NORMAL_EPSILON ) {
				v1[i] = maxs[i];
			} else { v1[i] = 0;}
		} //end for
	} //end else
	VectorCopy( normal, v2 );
	VectorInverse( v2 );
	return DotProduct( v1, v2 );
} //end of the function BoxOriginDistanceFromPlane
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
vec_t CapsuleOriginDistanceFromPlane( vec3_t normal, vec3_t mins, vec3_t maxs ) {
	float offset_up, offset_down, width, radius;

	width = maxs[0] - mins[0];
	// if the box is less high then it is wide
	if ( maxs[2] - mins[2] < width ) {
		width = maxs[2] - mins[2];
	}
	radius = width * 0.5;
	// offset to upper and lower sphere
	offset_up = maxs[2] - radius;
	offset_down = -mins[2] - radius;

	// if normal points upward
	if ( normal[2] > 0 ) {
		// touches lower sphere first
		return normal[2] * offset_down + radius;
	} else {
		// touched upper sphere first
		return -normal[2] * offset_up + radius;
	}
}
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void AAS_ExpandMapBrush( mapbrush_t *brush, vec3_t mins, vec3_t maxs ) {
	int sn;
	float dist;
	side_t *s;
	plane_t *plane;

	for ( sn = 0; sn < brush->numsides; sn++ )
	{
		s = brush->original_sides + sn;
		plane = &mapplanes[s->planenum];
		dist = plane->dist;
		if ( capsule_collision ) {
			dist += CapsuleOriginDistanceFromPlane( plane->normal, mins, maxs );
		} else {
			dist += BoxOriginDistanceFromPlane( plane->normal, mins, maxs, 0 );
		}
		s->planenum = FindFloatPlane( plane->normal, dist );
		//the side isn't a bevel after expanding
		s->flags &= ~SFL_BEVEL;
		//don't skip the surface
		s->surf &= ~SURF_SKIP;
		//make sure the texinfo is not TEXINFO_NODE
		//when player clip contents brushes are read from the bsp tree
		//they have the texinfo field set to TEXINFO_NODE
		//s->texinfo = 0;
	} //end for
} //end of the function AAS_ExpandMapBrush
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_SetTexinfo( mapbrush_t *brush ) {
	int n;
	side_t *side;

	if ( brush->contents & ( CONTENTS_LADDER
							 | CONTENTS_AREAPORTAL
							 | CONTENTS_CLUSTERPORTAL
							 | CONTENTS_TELEPORTER
							 | CONTENTS_JUMPPAD
							 | CONTENTS_DONOTENTER
							 | CONTENTS_DONOTENTER_LARGE
							 | CONTENTS_WATER
							 | CONTENTS_LAVA
							 | CONTENTS_SLIME
							 | CONTENTS_WINDOW
							 | CONTENTS_PLAYERCLIP ) ) {
		//we just set texinfo to 0 because these brush sides MUST be used as
		//bsp splitters textured or not textured
		for ( n = 0; n < brush->numsides; n++ )
		{
			side = brush->original_sides + n;
			//side->flags |= SFL_TEXTURED|SFL_VISIBLE;
			side->texinfo = 0;
		} //end for
	} //end if
	else
	{
		//only use brush sides as splitters if they are textured
		//texinfo of non-textured sides will be set to TEXINFO_NODE
		for ( n = 0; n < brush->numsides; n++ )
		{
			side = brush->original_sides + n;
			//don't use side as splitter (set texinfo to TEXINFO_NODE) if not textured
			if ( side->flags & ( SFL_TEXTURED | SFL_BEVEL ) ) {
				side->texinfo = 0;
			} else { side->texinfo = TEXINFO_NODE;}
		} //end for
	} //end else
} //end of the function AAS_SetTexinfo
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void FreeBrushWindings( mapbrush_t *brush ) {
	int n;
	side_t *side;
	//
	for ( n = 0; n < brush->numsides; n++ )
	{
		side = brush->original_sides + n;
		//
		if ( side->winding ) {
			FreeWinding( side->winding );
		}
	} //end for
} //end of the function FreeBrushWindings
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_AddMapBrushSide( mapbrush_t *brush, int planenum ) {
	side_t *side;
	//
	if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) {
		Error( "MAX_MAPFILE_BRUSHSIDES" );
	}
	//
	side = brush->original_sides + brush->numsides;
	side->original = NULL;
	side->winding = NULL;
	side->contents = brush->contents;
	side->flags &= ~( SFL_BEVEL | SFL_VISIBLE );
	side->surf = 0;
	side->planenum = planenum;
	side->texinfo = 0;
	//
	nummapbrushsides++;
	brush->numsides++;
} //end of the function AAS_AddMapBrushSide
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FixMapBrush( mapbrush_t *brush ) {
	int i, j, planenum;
	float dist;
	winding_t *w;
	plane_t *plane, *plane1, *plane2;
	side_t *side;
	vec3_t normal;

	//calculate the brush bounds
	ClearBounds( brush->mins, brush->maxs );
	for ( i = 0; i < brush->numsides; i++ )
	{
		plane = &mapplanes[brush->original_sides[i].planenum];
		w = BaseWindingForPlane( plane->normal, plane->dist );
		for ( j = 0; j < brush->numsides && w; j++ )
		{
			if ( i == j ) {
				continue;
			}
			//there are no brush bevels marked but who cares :)
			if ( brush->original_sides[j].flags & SFL_BEVEL ) {
				continue;
			}
			plane = &mapplanes[brush->original_sides[j].planenum ^ 1];
			ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON);
		} //end for

		side = &brush->original_sides[i];
		side->winding = w;
		if ( w ) {
			for ( j = 0; j < w->numpoints; j++ )
			{
				AddPointToBounds( w->p[j], brush->mins, brush->maxs );
			} //end for
		} //end if
	} //end for
	  //
	for ( i = 0; i < brush->numsides; i++ )
	{
		for ( j = 0; j < brush->numsides; j++ )
		{
			if ( i == j ) {
				continue;
			}
			plane1 = &mapplanes[brush->original_sides[i].planenum];
			plane2 = &mapplanes[brush->original_sides[j].planenum];
			if ( WindingsNonConvex( brush->original_sides[i].winding,
									brush->original_sides[j].winding,
									plane1->normal, plane2->normal,
									plane1->dist, plane2->dist ) ) {
				Log_Print( "non convex brush" );
			} //end if
		} //end for
	} //end for

	//NOW close the fucking brush!!
	for ( i = 0; i < 3; i++ )
	{
		if ( brush->mins[i] < -MAX_MAP_BOUNDS ) {
			VectorClear( normal );
			normal[i] = -1;
			dist = MAX_MAP_BOUNDS - 10;
			planenum = FindFloatPlane( normal, dist );
			//
			Log_Print( "mins out of range: added extra brush side\n" );
			AAS_AddMapBrushSide( brush, planenum );
		} //end if
		if ( brush->maxs[i] > MAX_MAP_BOUNDS ) {
			VectorClear( normal );
			normal[i] = 1;
			dist = MAX_MAP_BOUNDS - 10;
			planenum = FindFloatPlane( normal, dist );
			//
			Log_Print( "maxs out of range: added extra brush side\n" );
			AAS_AddMapBrushSide( brush, planenum );
		} //end if
		if ( brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS ) {
			Log_Print( "entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum );
		} //end if
	} //end for
	  //free all the windings
	FreeBrushWindings( brush );
} //end of the function AAS_FixMapBrush
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
qboolean AAS_MakeBrushWindings( mapbrush_t *ob ) {
	int i, j;
	winding_t   *w;
	side_t      *side;
	plane_t     *plane, *plane1, *plane2;

	ClearBounds( ob->mins, ob->maxs );

	for ( i = 0; i < ob->numsides; i++ )
	{
		plane = &mapplanes[ob->original_sides[i].planenum];
		w = BaseWindingForPlane( plane->normal, plane->dist );
		for ( j = 0; j < ob->numsides && w; j++ )
		{
			if ( i == j ) {
				continue;
			}
			if ( ob->original_sides[j].flags & SFL_BEVEL ) {
				continue;
			}
			plane = &mapplanes[ob->original_sides[j].planenum ^ 1];
			ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON);
		}

		side = &ob->original_sides[i];
		side->winding = w;
		if ( w ) {
			side->flags |= SFL_VISIBLE;
			for ( j = 0; j < w->numpoints; j++ )
				AddPointToBounds( w->p[j], ob->mins, ob->maxs );
		}
	}
	//check if the brush is convex
	for ( i = 0; i < ob->numsides; i++ )
	{
		for ( j = 0; j < ob->numsides; j++ )
		{
			if ( i == j ) {
				continue;
			}
			plane1 = &mapplanes[ob->original_sides[i].planenum];
			plane2 = &mapplanes[ob->original_sides[j].planenum];
			if ( WindingsNonConvex( ob->original_sides[i].winding,
									ob->original_sides[j].winding,
									plane1->normal, plane2->normal,
									plane1->dist, plane2->dist ) ) {
				Log_Print( "non convex brush" );
			} //end if
		} //end for
	} //end for
	  //check for out of bound brushes
	for ( i = 0; i < 3; i++ )
	{
		//IDBUG: all the indexes into the mins and maxs were zero (not using i)
		if ( ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS ) {
			Log_Print( "entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum );
			Log_Print( "ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i] );
			ob->numsides = 0; //remove the brush
			break;
		} //end if
		if ( ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS ) {
			Log_Print( "entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum );
			Log_Print( "ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i] );
			ob->numsides = 0; //remove the brush
			break;
		} //end if
	} //end for
	return true;
} //end of the function AAS_MakeBrushWindings
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
mapbrush_t *AAS_CopyMapBrush( mapbrush_t *brush, entity_t *mapent ) {
	int n;
	mapbrush_t *newbrush;
	side_t *side, *newside;

	if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) {
		Error( "MAX_MAPFILE_BRUSHES" );
	}

	newbrush = &mapbrushes[nummapbrushes];
	newbrush->original_sides = &brushsides[nummapbrushsides];
	newbrush->entitynum = brush->entitynum;
	newbrush->brushnum = nummapbrushes - mapent->firstbrush;
	newbrush->numsides = brush->numsides;
	newbrush->contents = brush->contents;

	//copy the sides
	for ( n = 0; n < brush->numsides; n++ )
	{
		if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) {
			Error( "MAX_MAPFILE_BRUSHSIDES" );
		}
		side = brush->original_sides + n;

		newside = newbrush->original_sides + n;
		newside->original = NULL;
		newside->winding = NULL;
		newside->contents = side->contents;
		newside->flags = side->flags;
		newside->surf = side->surf;
		newside->planenum = side->planenum;
		newside->texinfo = side->texinfo;
		nummapbrushsides++;
	} //end for
	  //
	nummapbrushes++;
	mapent->numbrushes++;
	return newbrush;
} //end of the function AAS_CopyMapBrush
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_AlwaysTriggered( char *targetname ) {
	int i;

	if ( !strlen( targetname ) ) {
		return false;
	}
	//
	for ( i = 0; i < num_entities; i++ )
	{
		//if the entity will activate the given targetname
		if ( !strcmp( targetname, ValueForKey( &entities[i], "target" ) ) ) {
			//if this activator is present in deathmatch
			if ( !( atoi( ValueForKey( &entities[i], "spawnflags" ) ) & SPAWNFLAG_NOT_DEATHMATCH ) ) {
				//if it is a trigger_always entity
				if ( !strcmp( "trigger_always", ValueForKey( &entities[i], "classname" ) ) ) {
					return true;
				} //end if
				  //check for possible trigger_always entities activating this entity
				if ( AAS_AlwaysTriggered( ValueForKey( &entities[i], "targetname" ) ) ) {
					return true;
				} //end if
			} //end if
		} //end if
	} //end for
	return false;
} //end of the function AAS_AlwaysTriggered
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_ValidEntity( entity_t *mapent ) {
	int i;
	char target[1024];

	//all world brushes are used for AAS
	if ( mapent == &entities[0] ) {
		return true;
	} //end if
	  //some of the func_wall brushes are also used for AAS
	else if ( !strcmp( "func_wall", ValueForKey( mapent, "classname" ) ) ) {
		//Log_Print("found func_wall entity %d\n", mapent - entities);
		//if the func wall is used in deathmatch
		//if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH))
		{
			//Log_Print("func_wall USED in deathmatch mode %d\n", atoi(ValueForKey(mapent, "spawnflags")));
			return true;
		} //end if
	} //end else if
	else if ( !strcmp( "func_door_rotating", ValueForKey( mapent, "classname" ) ) ||
			  !strcmp( "func_door", ValueForKey( mapent, "classname" ) ) ||
			  !strcmp( "func_invisible_user", ValueForKey( mapent, "classname" ) ) ) {
		//if the func_door_rotating is present in deathmatch
		//if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH))
		{
			//if the func_door_rotating is always activated in deathmatch
			if ( AAS_AlwaysTriggered( ValueForKey( mapent, "targetname" ) ) ) {
				//Log_Print("found func_door_rotating in deathmatch\ntargetname %s\n", ValueForKey(mapent, "targetname"));
				return true;
			} //end if
		} //end if
	} //end else if
	else if ( !strcmp( "trigger_hurt", ValueForKey( mapent, "classname" ) ) ) {
		// RF, spawnflag & 1 is for delayed spawn, so ignore it
		if ( atoi( ValueForKey( mapent, "spawnflags" ) ) & 1 ) {
			return false;
		}

		//"dmg" is the damage, for instance: "dmg" "666"
		return true;
	} //end else if
	else if ( !strcmp( "trigger_push", ValueForKey( mapent, "classname" ) ) ) {
		return true;
	} //end else if
	else if ( !strcmp( "trigger_multiple", ValueForKey( mapent, "classname" ) ) ) {
		//find out if the trigger_multiple is pointing to a target_teleporter
		strcpy( target, ValueForKey( mapent, "target" ) );
		for ( i = 0; i < num_entities; i++ )
		{
			//if the entity will activate the given targetname
			if ( !strcmp( target, ValueForKey( &entities[i], "targetname" ) ) ) {
				if ( !strcmp( "target_teleporter", ValueForKey( &entities[i], "classname" ) ) ) {
					return true;
				} //end if
			} //end if
		} //end for
	} //end else if
	else if ( !strcmp( "trigger_teleport", ValueForKey( mapent, "classname" ) ) ) {
		return true;
	} //end else if
	else if ( !strcmp( "func_tramcar", ValueForKey( mapent, "classname" ) ) ) {
		return true;
	} //end else if
	else if ( !strcmp( "func_invisible_user", ValueForKey( mapent, "classname" ) ) ) {
		return true;
	}
	/*
	else if (!strcmp("func_static", ValueForKey(mapent, "classname")))
	{
		//FIXME: easy/medium/hard/deathmatch specific?
		return true;
	} //end else if
	*/
	return false;
} //end of the function AAS_ValidEntity
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_TransformPlane( int planenum, vec3_t origin, vec3_t angles ) {
	float newdist, matrix[3][3];
	vec3_t normal;

	//rotate the node plane
	VectorCopy( mapplanes[planenum].normal, normal );
	CreateRotationMatrix( angles, matrix );
	RotatePoint( normal, matrix );
	newdist = mapplanes[planenum].dist + DotProduct( normal, origin );
	return FindFloatPlane( normal, newdist );
} //end of the function AAS_TransformPlane
//===========================================================================
// this function sets the func_rotating_door in it's final position
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_PositionFuncRotatingBrush( entity_t *mapent, mapbrush_t *brush ) {
	int spawnflags, i;
	float distance;
	vec3_t movedir, angles, pos1, pos2;
	side_t *s;

	spawnflags = FloatForKey( mapent, "spawnflags" );
	VectorClear( movedir );
	if ( spawnflags & DOOR_X_AXIS ) {
		movedir[2] = 1.0;       //roll
	} else if ( spawnflags & DOOR_Y_AXIS )                                   {
		movedir[0] = 1.0;       //pitch
	} else { // Z_AXIS
		movedir[1] = 1.0;       //yaw

	}
	// check for reverse rotation
	if ( spawnflags & DOOR_REVERSE ) {
		VectorInverse( movedir );
	}

	distance = FloatForKey( mapent, "distance" );
	if ( !distance ) {
		distance = 90;
	}

	GetVectorForKey( mapent, "angles", angles );
	VectorCopy( angles, pos1 );
	VectorMA( angles, -distance, movedir, pos2 );
	// if it starts open, switch the positions
	if ( spawnflags & DOOR_START_OPEN ) {
		VectorCopy( pos2, angles );
		VectorCopy( pos1, pos2 );
		VectorCopy( angles, pos1 );
		VectorInverse( movedir );
	} //end if
	  //
	for ( i = 0; i < brush->numsides; i++ )
	{
		s = &brush->original_sides[i];
		s->planenum = AAS_TransformPlane( s->planenum, mapent->origin, pos2 );
	} //end for
	  //
	FreeBrushWindings( brush );
	AAS_MakeBrushWindings( brush );
	AddBrushBevels( brush );
	FreeBrushWindings( brush );
} //end of the function AAS_PositionFuncRotatingBrush
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_PositionBrush( entity_t *mapent, mapbrush_t *brush ) {
	side_t *s;
	float newdist;
	int i;
	char *model;

	if ( !strcmp( ValueForKey( mapent, "classname" ), "func_door_rotating" ) ) {
		AAS_PositionFuncRotatingBrush( mapent, brush );
	} //end if
	else
	{
		if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) {
			for ( i = 0; i < brush->numsides; i++ )
			{
				s = &brush->original_sides[i];
				newdist = mapplanes[s->planenum].dist +
						  DotProduct( mapplanes[s->planenum].normal, mapent->origin );
				s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist );
			} //end for
		} //end if
		  // RF, disabled for Wolf, we dont use trigger_hurt for lava
		  //if it's a trigger hurt
		  //if (!strcmp("trigger_hurt", ValueForKey(mapent, "classname")))
		  //{
		  //	//set the lava contents
		  //	brush->contents |= CONTENTS_LAVA;
		  //	//Log_Print("found trigger_hurt brush\n");
		  //} //end if
		  //
		else if ( !strcmp( "trigger_push", ValueForKey( mapent, "classname" ) ) ) {
			//set the jumppad contents
			brush->contents = CONTENTS_JUMPPAD;
			//Log_Print("found trigger_push brush\n");
		} //end if
		  //
		else if ( !strcmp( "trigger_multiple", ValueForKey( mapent, "classname" ) ) ) {
			//set teleporter contents
			brush->contents = CONTENTS_TELEPORTER;
			//Log_Print("found trigger_multiple teleporter brush\n");
		} //end if
		  //
		else if ( !strcmp( "trigger_teleport", ValueForKey( mapent, "classname" ) ) ) {
			//set teleporter contents
			brush->contents = CONTENTS_TELEPORTER;
			//Log_Print("found trigger_teleport teleporter brush\n");
		} //end if
		else if ( !strcmp( "func_door", ValueForKey( mapent, "classname" ) ) ) {
			//set mover contents
			brush->contents = CONTENTS_MOVER;
			//get the model number
			model = ValueForKey( mapent, "model" );
			brush->modelnum = atoi( model + 1 );
		} //end if
		else if ( !strcmp( "func_invisible_user", ValueForKey( mapent, "classname" ) ) ) {
			//set mover contents
			brush->contents = CONTENTS_TRIGGER;
		} //end if

	} //end else
} //end of the function AAS_PositionBrush
//===========================================================================
// uses the global cfg_t cfg
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CreateMapBrushes( mapbrush_t *brush, entity_t *mapent, int addbevels ) {
	int i;
	//side_t *s;
	mapbrush_t *bboxbrushes[16];

	//if the brushes are not from an entity used for AAS
	if ( !AAS_ValidEntity( mapent ) ) {
		nummapbrushsides -= brush->numsides;
		brush->numsides = 0;
		return;
	} //end if
	  //
	AAS_PositionBrush( mapent, brush );
	//from all normal solid brushes only the textured brush sides will
	//be used as bsp splitters, so set the right texinfo reference here
	AAS_SetTexinfo( brush );
	//remove contents detail flag, otherwise player clip contents won't be
	//bsped correctly for AAS!
	brush->contents &= ~CONTENTS_DETAIL;
	//if the brush has contents area portal it should be the only contents
	if ( brush->contents & ( CONTENTS_AREAPORTAL | CONTENTS_CLUSTERPORTAL ) ) {
		brush->contents = CONTENTS_CLUSTERPORTAL;
		brush->leafnum = -1;
	} //end if
	  //window and playerclip are used for player clipping, make them solid
	if ( brush->contents & ( CONTENTS_WINDOW | CONTENTS_PLAYERCLIP ) ) {
		//
		brush->contents &= ~( CONTENTS_WINDOW | CONTENTS_PLAYERCLIP );
		brush->contents |= CONTENTS_SOLID;
		brush->leafnum = -1;
	} //end if
	  //
// Rafael TBD: no flag to support CONTENTS_BOTCLIP
/*
	if (brush->contents & CONTENTS_BOTCLIP)
	{
		brush->contents = CONTENTS_SOLID;
		brush->leafnum = -1;
	} // end if
*/
	//
	//Log_Write("brush %d contents = ", brush->brushnum);
	//PrintContents(brush->contents);
	//Log_Write("\r\n");
	//if not one of the following brushes then the brush is NOT used for AAS
	if ( !( brush->contents & ( CONTENTS_SOLID
								| CONTENTS_LADDER
								| CONTENTS_CLUSTERPORTAL
								| CONTENTS_DONOTENTER
								| CONTENTS_DONOTENTER_LARGE
								| CONTENTS_TELEPORTER
								| CONTENTS_JUMPPAD
								| CONTENTS_WATER
								| CONTENTS_LAVA
								| CONTENTS_SLIME
								| CONTENTS_MOVER
								) ) ) {
		nummapbrushsides -= brush->numsides;
		brush->numsides = 0;
		return;
	} //end if
	  //fix the map brush
	  //AAS_FixMapBrush(brush);
	  //if brush bevels should be added (for real map brushes, not bsp map brushes)
	if ( addbevels ) {
		//NOTE: we first have to get the mins and maxs of the brush before
		//			creating the brush bevels... the mins and maxs are used to
		//			create them. so we call MakeBrushWindings to get the mins
		//			and maxs and then after creating the bevels we free the
		//			windings because they are created for all sides (including
		//			bevels) a little later
		AAS_MakeBrushWindings( brush );
		AddBrushBevels( brush );
		FreeBrushWindings( brush );
	} //end if
	  //NOTE: add the brush to the WORLD entity!!!
	mapent = &entities[0];
	//there's at least one new brush for now
	nummapbrushes++;
	mapent->numbrushes++;
	//liquid brushes are expanded for the maximum possible bounding box
	if ( brush->contents & ( CONTENTS_WATER
							 | CONTENTS_LAVA
							 | CONTENTS_SLIME
							 | CONTENTS_TELEPORTER
							 | CONTENTS_JUMPPAD
							 | CONTENTS_DONOTENTER
							 | CONTENTS_DONOTENTER_LARGE
							 | CONTENTS_MOVER
							 ) ) {
		brush->expansionbbox = 0;
		//NOTE: the first bounding box is the max
		//FIXME: use max bounding box created from all bboxes
		AAS_ExpandMapBrush( brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs );
		AAS_MakeBrushWindings( brush );
	} //end if
	  //area portal brushes are NOT expanded
	else if ( brush->contents & CONTENTS_CLUSTERPORTAL ) {
		brush->expansionbbox = 0;
		//NOTE: the first bounding box is the max
		//FIXME: use max bounding box created from all bboxes
		AAS_ExpandMapBrush( brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs );
		AAS_MakeBrushWindings( brush );
	} //end if
	  //all solid brushes are expanded for all bounding boxes
	else if ( brush->contents & ( CONTENTS_SOLID
								  | CONTENTS_LADDER
								  ) ) {
		//brush for the first bounding box
		bboxbrushes[0] = brush;
		//make a copy for the other bounding boxes
		for ( i = 1; i < cfg.numbboxes; i++ )
		{
			bboxbrushes[i] = AAS_CopyMapBrush( brush, mapent );
		} //end for
		  //expand every brush for it's bounding box and create windings
		for ( i = 0; i < cfg.numbboxes; i++ )
		{
			AAS_ExpandMapBrush( bboxbrushes[i], cfg.bboxes[i].mins, cfg.bboxes[i].maxs );
			bboxbrushes[i]->expansionbbox = cfg.bboxes[i].presencetype;
			AAS_MakeBrushWindings( bboxbrushes[i] );
		} //end for
	} //end else
} //end of the function AAS_CreateMapBrushes
