/* =========================================================================== 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 . 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