Exemplo n.º 1
0
	// METHODS
	
	// +decompile()
	// Attempts to convert a map in a Doom WAD into a usable .MAP file. This has many
	// challenges, not the least of which is the fact that the Doom engine didn't use
	// brushes (at least, not in any sane way).
	public virtual Entities decompile()
	{
		DecompilerThread.OnMessage(this, "Decompiling...");
		DecompilerThread.OnMessage(this, doomMap.MapName);
		
		mapFile = new Entities();
		Entity world = new Entity("worldspawn");
		mapFile.Add(world);
		
		string[] lowerWallTextures = new string[doomMap.Sidedefs.Count];
		string[] midWallTextures = new string[doomMap.Sidedefs.Count];
		string[] higherWallTextures = new string[doomMap.Sidedefs.Count];
		
		short[] sectorTag = new short[doomMap.Sectors.Count];
		string playerStartOrigin = "";
		
		// Since Doom relied on sectors to define a cieling and floor height, and nothing else,
		// need to find the minimum and maximum used Z values. This is because the Doom engine
		// is only a pseudo-3D engine. For all it cares, the cieling and floor extend to their
		// respective infinities. For a GC/Hammer map, however, this cannot be the case.
		int ZMin = 32767; // Even though the values in the map will never exceed these, use ints here to avoid
		int ZMax = - 32768; // overflows, in case the map DOES go within 32 units of these values.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			DSector currentSector = doomMap.Sectors[i];
			sectorTag[i] = currentSector.Tag;
			if (currentSector.FloorHeight < ZMin + 32)
			{
				ZMin = currentSector.FloorHeight - 32; // Can't use the actual value, because that IS the floor
			}
			else
			{
				if (currentSector.CielingHeight > ZMax - 32)
				{
					ZMax = currentSector.CielingHeight + 32; // or the cieling. Subtract or add a sane value to it.
				}
			}
		}
		
		// I need to analyze the binary tree and get more information, particularly the
		// parent nodes of each subsector and node, as well as whether it's the right or
		// left child of that node. These are extremely important, as the parent defines
		// boundaries for the children, as well as inheriting further boundaries from its
		// parents. These boundaries are invaluable for forming brushes.
		int[] nodeparents = new int[doomMap.Nodes.Count];
		bool[] nodeIsLeft = new bool[doomMap.Nodes.Count];
		
		for (int i = 0; i < doomMap.Nodes.Count; i++)
		{
			nodeparents[i] = - 1; // There should only be one node left with -1 as a parent. This SHOULD be the root.
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				if (doomMap.Nodes[j].Child1 == i)
				{
					nodeparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 == i)
					{
						nodeparents[i] = j;
						nodeIsLeft[i] = true;
						break;
					}
				}
			}
		}
		
		// Keep a list of what subsectors belong to which sector
		int[] subsectorSectors = new int[doomMap.SubSectors.Count];
		// Keep a list of what sidedefs belong to what subsector as well
		int[][] subsectorSidedefs = new int[doomMap.SubSectors.Count][];
		
		short[][] sideDefShifts = new short[2][];
		for (int i2 = 0; i2 < 2; i2++)
		{
			sideDefShifts[i2] = new short[doomMap.Sidedefs.Count];
		}
		
		// Figure out what sector each subsector belongs to, and what node is its parent.
		// Depending on sector "tags" this will help greatly in creation of brushbased entities,
		// and also helps in finding subsector floor and cieling heights.
		int[] ssparents = new int[doomMap.SubSectors.Count];
		bool[] ssIsLeft = new bool[doomMap.SubSectors.Count];
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Populating texture lists for subsector " + i);
			// First, find the subsector's parent and whether it is the left or right child.
			ssparents[i] = - 1; // No subsector should have a -1 in here
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				// When a node references a subsector, it is not referenced by negative
				// index, as future BSP versions do. The bits 0-14 ARE the index, and
				// bit 15 (which is the sign bit in two's compliment math) determines
				// whether or not it is a node or subsector. Therefore, we need to add
				// 2^15 to the number to produce the actual index.
				if (doomMap.Nodes[j].Child1 + 32768 == i)
				{
					ssparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 + 32768 == i)
					{
						ssparents[i] = j;
						ssIsLeft[i] = true;
						break;
					}
				}
			}
			
			// Second, figure out what sector a subsector belongs to, and the type of sector it is.
			subsectorSectors[i] = - 1;
			Edge currentsubsector = doomMap.SubSectors[i];
			subsectorSidedefs[i] = new int[currentsubsector.NumSegs];
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// For each segment the subsector references
				DSegment currentsegment = doomMap.Segments[currentsubsector.FirstSeg + j];
				DLinedef currentlinedef = doomMap.Linedefs[currentsegment.Linedef];
				int currentsidedefIndex;
				int othersideIndex;
				if (currentsegment.Direction == 0)
				{
					currentsidedefIndex = currentlinedef.Right;
					othersideIndex = currentlinedef.Left;
				}
				else
				{
					currentsidedefIndex = currentlinedef.Left;
					othersideIndex = currentlinedef.Right;
				}
				subsectorSidedefs[i][j] = currentsidedefIndex;
				DSidedef currentSidedef = doomMap.Sidedefs[currentsidedefIndex];
				if (currentlinedef.OneSided)
				{
					// A one-sided linedef should always be like this
					midWallTextures[currentsidedefIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					higherWallTextures[currentsidedefIndex] = "special/nodraw";
					lowerWallTextures[currentsidedefIndex] = "special/nodraw";
					sideDefShifts[X][currentsidedefIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][currentsidedefIndex] = currentSidedef.OffsetY;
				}
				else
				{
					// I don't really get why I need to apply these textures to the other side. But if it works I won't argue...
					if (!currentSidedef.MidTexture.Equals("-"))
					{
						midWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					}
					else
					{
						midWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.HighTexture.Equals("-"))
					{
						higherWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.HighTexture;
					}
					else
					{
						higherWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.LowTexture.Equals("-"))
					{
						lowerWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.LowTexture;
					}
					else
					{
						lowerWallTextures[othersideIndex] = "special/nodraw";
					}
					sideDefShifts[X][othersideIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][othersideIndex] = currentSidedef.OffsetY;
				}
				// Sometimes a subsector seems to belong to more than one sector. Only the reference in the first seg is true.
				if (j == 0)
				{
					subsectorSectors[i] = currentSidedef.Sector;
				}
			}
		}
		bool[] linedefFlagsDealtWith = new bool[doomMap.Linedefs.Count];
		bool[] linedefSpecialsDealtWith = new bool[doomMap.Linedefs.Count];
		
		MAPBrush[][] sectorFloorBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i3 = 0; i3 < doomMap.Sectors.Count; i3++)
		{
			sectorFloorBrushes[i3] = new MAPBrush[0];
		}
		MAPBrush[][] sectorCielingBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i4 = 0; i4 < doomMap.Sectors.Count; i4++)
		{
			sectorCielingBrushes[i4] = new MAPBrush[0];
		}
		
		// For one-sided linedefs referenced by more than one subsector
		bool[] outsideBrushAlreadyCreated = new bool[doomMap.Linedefs.Count];
		
		int onePercent = (int)((doomMap.SubSectors.Count)/100);
		if(onePercent < 1) {
			onePercent = 1;
		}
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Creating brushes for subsector " + i);
			
			Edge currentsubsector = doomMap.SubSectors[i];
			
			// Third, create a few brushes out of the geometry.
			MAPBrush cielingBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush floorBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush midBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush sectorTriggerBrush = new MAPBrush(numBrshs++, 0, false);
			DSector currentSector = doomMap.Sectors[subsectorSectors[i]];
			
			Vector3D[] roofPlane = new Vector3D[3];
			double[] roofTexS = new double[3];
			double[] roofTexT = new double[3];
			roofPlane[0] = new Vector3D(0, Settings.planePointCoef, ZMax);
			roofPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMax);
			roofPlane[2] = new Vector3D(Settings.planePointCoef, 0, ZMax);
			roofTexS[0] = 1;
			roofTexT[1] = - 1;
			
			Vector3D[] cileingPlane = new Vector3D[3];
			double[] cileingTexS = new double[3];
			double[] cileingTexT = new double[3];
			cileingPlane[0] = new Vector3D(0, 0, currentSector.CielingHeight);
			cileingPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			cileingPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			cileingTexS[0] = 1;
			cileingTexT[1] = - 1;
			
			Vector3D[] floorPlane = new Vector3D[3];
			double[] floorTexS = new double[3];
			double[] floorTexT = new double[3];
			floorPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			floorTexS[0] = 1;
			floorTexT[1] = - 1;
			
			Vector3D[] foundationPlane = new Vector3D[3];
			double[] foundationTexS = new double[3];
			double[] foundationTexT = new double[3];
			foundationPlane[0] = new Vector3D(0, 0, ZMin);
			foundationPlane[1] = new Vector3D(Settings.planePointCoef, 0, ZMin);
			foundationPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMin);
			foundationTexS[0] = 1;
			foundationTexT[1] = - 1;
			
			Vector3D[] topPlane = new Vector3D[3];
			double[] topTexS = new double[3];
			double[] topTexT = new double[3];
			topPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			topTexS[0] = 1;
			topTexT[1] = - 1;
			
			Vector3D[] bottomPlane = new Vector3D[3];
			double[] bottomTexS = new double[3];
			double[] bottomTexT = new double[3];
			bottomPlane[0] = new Vector3D(0, 0, currentSector.FloorHeight);
			bottomPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			bottomPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			bottomTexS[0] = 1;
			bottomTexT[1] = - 1;
			
			int nextNode = ssparents[i];
			bool leftSide = ssIsLeft[i];
			
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// Iterate through the sidedefs defined by segments of this subsector
				DSegment currentseg = doomMap.Segments[currentsubsector.FirstSeg + j];
				Vector3D start = doomMap.Vertices[currentseg.StartVertex].Vector;
				Vector3D end = doomMap.Vertices[currentseg.EndVertex].Vector;
				DLinedef currentLinedef = doomMap.Linedefs[(int) currentseg.Linedef];
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(end.X, end.Y, ZMax);
				
				Vector3D linestart = new Vector3D(doomMap.Vertices[currentLinedef.Start].Vector.X, doomMap.Vertices[currentLinedef.Start].Vector.Y, ZMin);
				Vector3D lineend = new Vector3D(doomMap.Vertices[currentLinedef.End].Vector.X, doomMap.Vertices[currentLinedef.End].Vector.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				bool upperUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 3)) == 0);
				bool lowerUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 4)) == 0);
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = - 1;
				
				double SShift = sideDefShifts[X][subsectorSidedefs[i][j]] - (texS[0] * end.X) - (texS[1] * end.Y);
				double lowTShift = 0;
				double highTShift = 0;
				if (!currentLinedef.OneSided)
				{
					DSector otherSideSector;
					if (doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector] == currentSector)
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Right].Sector];
					}
					else
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector];
					}
					if (lowerUnpegged)
					{
						lowTShift = otherSideSector.CielingHeight;
					}
					else
					{
						lowTShift = currentSector.FloorHeight;
					}
					if (upperUnpegged)
					{
						highTShift = otherSideSector.CielingHeight;
					}
					else
					{
						highTShift = currentSector.CielingHeight;
					}
					lowTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
					highTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
				}
				MAPBrushSide low = new MAPBrushSide(plane, lowerWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, lowTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, higherWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, highTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid;
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				if (currentLinedef.OneSided)
				{
					if (!outsideBrushAlreadyCreated[currentseg.Linedef])
					{
						outsideBrushAlreadyCreated[currentseg.Linedef] = true;
						double highestCieling = currentSector.CielingHeight;
						double lowestFloor = currentSector.FloorHeight;
						if (currentSector.Tag != 0)
						{
							double temp = getHighestNeighborCielingHeight(subsectorSectors[i]);
							if (temp > highestCieling)
							{
								highestCieling = temp;
							}
							temp = getLowestNeighborFloorHeight(subsectorSectors[i]);
							if (temp < lowestFloor)
							{
								lowestFloor = temp;
							}
						}
						MAPBrush outsideBrush = null;
						if (lowestFloor <= highestCieling)
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, ZMin), new Vector3D(lineend.X, lineend.Y, ZMax), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						else
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, lowestFloor), new Vector3D(lineend.X, lineend.Y, highestCieling), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						world.Brushes.Add(outsideBrush);
					}
					mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				}
				else
				{
					double midTShift = sideDefShifts[Y][subsectorSidedefs[i][j]];
					if (lowerUnpegged)
					{
						midTShift += currentSector.FloorHeight;
					}
					else
					{
						midTShift += currentSector.CielingHeight;
					}
					mid = new MAPBrushSide(plane, midWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, midTShift, 0, 1, 1, 0, "wld_masked", 16, 0);
					if (!linedefFlagsDealtWith[currentseg.Linedef])
					{
						linedefFlagsDealtWith[currentseg.Linedef] = true;
						if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 0)) == 0))
						{
							// Flag 0x0001 indicates "solid" but doesn't block bullets. It is assumed for all one-sided.
							MAPBrush solidBrush = MAPBrush.createFaceBrush("special/clip", "special/clip", linestart, lineend, 0, 0, false, 0, 0);
							world.Brushes.Add(solidBrush);
						}
						else
						{
							if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 1)) == 0))
							{
								// Flag 0x0002 indicates "monster clip".
								MAPBrush solidBrush = MAPBrush.createFaceBrush("special/enemyclip", "special/enemyclip", linestart, lineend, 0, 0, false, 0, 0);
								world.Brushes.Add(solidBrush);
							}
						}
					}
					DSidedef otherside = doomMap.Sidedefs[currentLinedef.Left];
					if (currentLinedef.Action != 0 && !linedefSpecialsDealtWith[currentseg.Linedef])
					{
						linedefSpecialsDealtWith[currentseg.Linedef] = true;
						Entity trigger = null;
						MAPBrush triggerBrush = MAPBrush.createFaceBrush("special/trigger", "special/trigger", linestart, lineend, 0, 0, false, 0, 0);
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							bool[] bitset = new bool[16];
							for (int k = 0; k < 8; k++)
							{
								bitset[k] = !((currentLinedef.Flags[0] & ((sbyte) k << 1)) == 0);
							}
							for (int k = 0; k < 8; k++)
							{
								bitset[k + 8] = !((currentLinedef.Flags[1] & ((sbyte) k << 1)) == 0);
							}
							if (bitset[10] && bitset[11] && !bitset[12])
							{
								// Triggered when "Used" by player
								trigger = new Entity("func_button");
								trigger["spawnflags"] = "1";
								if (bitset[9])
								{
									trigger["wait"] = "1";
								}
								else
								{
									trigger["wait"] = "-1";
								}
							}
							else
							{
								if (bitset[9])
								{
									// Can be activated more than once
									trigger = new Entity("trigger_multiple");
									trigger["wait"] = "1";
								}
								else
								{
									trigger = new Entity("trigger_once");
								}
							}
							switch (currentLinedef.Action)
							{
								case 21: 
								// Floor lower to lowest surrounding floor
								case 22:  // Floor lower to next lowest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "lowerfloor";
									}
									break;
								case 24: 
								// Floor raise to highest surrounding floor
								case 25:  // Floor raise to next highest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "raisefloor";
									}
									break;
								case 70:  // Teleport
									trigger = new Entity("trigger_teleport");
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "teledest" + currentLinedef.Arguments[0];
									}
									else
									{
										trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									}
									break;
								case 80:  // Exec script
									// This is a toughie. I can't write a script-to-entity converter.
									trigger["target"] = "script" + currentLinedef.Arguments[0];
									trigger["arg0"] = "" + currentLinedef.Arguments[2];
									trigger["arg1"] = "" + currentLinedef.Arguments[3];
									trigger["arg2"] = "" + currentLinedef.Arguments[4];
									break;
								case 181:  // PLANE_ALIGN
									trigger = null;
									if (!leftSide)
									{
										DSidedef getsector = doomMap.Sidedefs[currentLinedef.Left];
										DSector copyheight = doomMap.Sectors[getsector.Sector];
										short newHeight = copyheight.FloorHeight;
										//floorPlane[0]=new Vector3D(0, Settings.getPlanePointCoef(), 2000);
										//floorPlane[1]=new Vector3D(Settings.getPlanePointCoef(), Settings.getPlanePointCoef(), currentSector.getFloorHeight());
										//floorPlane[2]=new Vector3D(Settings.getPlanePointCoef(), 0, currentSector.getFloorHeight());
									}
									else
									{
										linedefSpecialsDealtWith[currentseg.Linedef] = false;
									}
									break;
								default: 
									trigger = null;
									break;
							}
						}
						else
						{
							switch (currentLinedef.Action)
							{
								
								case 1: 
								// Use Door. open, wait, close
								case 31:  // Use Door. Open, stay.
									trigger = new Entity("func_button");
									trigger["wait"] = "1";
									if (currentLinedef.Action == 31)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									if (doomMap.Sectors[otherside.Sector].Tag == 0)
									{
										trigger["target"] = "sectornum" + otherside.Sector + "door";
										if (currentLinedef.Action == 1)
										{
											sectorTag[otherside.Sector] = - 1;
										}
										if (currentLinedef.Action == 31)
										{
											sectorTag[otherside.Sector] = - 2;
										}
									}
									else
									{
										trigger["target"] = "sector" + doomMap.Sectors[otherside.Sector].Tag + "door";
									}
									break;
								
								case 36: 
								// Floor lower to 8 above next lowest neighboring sector
								case 38:  // Floor lower to next lowest neighboring sector
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "lowerfloor";
									break;
								
								case 62:  // Floor lower to next lowest neighboring sector, wait 4s, goes back up
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									trigger["wait"] = "1";
									trigger["spawnflags"] = "1";
									break;
								
								case 63: 
								// Door with button, retriggerable
								case 103:  // Push button, one-time door open stay open
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									trigger["wait"] = "1";
									if (currentLinedef.Action == 103)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									break;
								
								case 88:  // Walkover retriggerable elevator trigger
									trigger = new Entity("trigger_multiple");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									break;
								
								case 97:  // Walkover retriggerable Teleport
									trigger = new Entity("trigger_teleport");
									trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									break;
								
								case 109:  // Walkover one-time door open stay open
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									break;
							}
						}
						if (trigger != null)
						{
							trigger.Brushes.Add(triggerBrush);
							mapFile.Add(trigger);
						}
					}
				}
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
			}
			
			MAPBrushSide roof = new MAPBrushSide(roofPlane, "special/nodraw", roofTexS, 0, roofTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide cieling = new MAPBrushSide(cileingPlane, doomMap.WadName + "/" + currentSector.CielingTexture, cileingTexS, 0, cileingTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide floor = new MAPBrushSide(floorPlane, doomMap.WadName + "/" + currentSector.FloorTexture, floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide foundation = new MAPBrushSide(foundationPlane, "special/nodraw", foundationTexS, 0, foundationTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide top = new MAPBrushSide(topPlane, "special/nodraw", topTexS, 0, topTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide bottom = new MAPBrushSide(bottomPlane, "special/nodraw", bottomTexS, 0, bottomTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide invertedFloor = new MAPBrushSide(Plane.flip(floorPlane), "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide damageTop = new MAPBrushSide(new Vector3D[]{floorPlane[0]+new Vector3D(0, 0, 1), floorPlane[1]+new Vector3D(0, 0, 1), floorPlane[2]+new Vector3D(0, 0, 1)}, "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			sectorTriggerBrush.add(damageTop);
			sectorTriggerBrush.add(invertedFloor);
			
			midBrush.add(top);
			midBrush.add(bottom);
			
			cielingBrush.add(cieling);
			cielingBrush.add(roof);
			
			floorBrush.add(floor);
			floorBrush.add(foundation);
			
			// Now need to add the data from node subdivisions. Neither segments nor nodes
			// will completely define a usable brush, but both of them together will.
			do 
			{
				DNode currentNode = doomMap.Nodes[nextNode];
				Vector3D start;
				Vector3D end;
				if (leftSide)
				{
					start = currentNode.VecHead+currentNode.VecTail;
					end = currentNode.VecHead;
				}
				else
				{
					start = currentNode.VecHead;
					end = currentNode.VecHead+currentNode.VecTail;
				}
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				// This is somehow always correct. And I'm okay with that.
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(start.X, start.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = 1;
				MAPBrushSide low = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
				
				leftSide = nodeIsLeft[nextNode];
				nextNode = nodeparents[nextNode];
			}
			while (nextNode != - 1);
			// Now we need to get rid of all the sides that aren't used. Get a list of
			// the useless sides from one brush, and delete those sides from all of them,
			// since they all have the same sides.
			int[] badSides = new int[0];
			if (!Settings.dontCull)
			{
				badSides = MAPBrush.findUnusedPlanes(cielingBrush);
				// Need to iterate backward, since these lists go from low indices to high, and
				// the index of all subsequent items changes when something before it is removed.
				if (cielingBrush.NumSides - badSides.Length < 4)
				{
					DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for subsector " + i);
					badSides = new int[0];
				}
				else
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						cielingBrush.delete(badSides[j]);
						floorBrush.delete(badSides[j]);
					}
				}
			}
			
			MAPBrush[] newFloorList = new MAPBrush[sectorFloorBrushes[subsectorSectors[i]].Length + 1];
			MAPBrush[] newCielingList = new MAPBrush[sectorCielingBrushes[subsectorSectors[i]].Length + 1];
			for (int j = 0; j < sectorFloorBrushes[subsectorSectors[i]].Length; j++)
			{
				newFloorList[j] = sectorFloorBrushes[subsectorSectors[i]][j];
				newCielingList[j] = sectorCielingBrushes[subsectorSectors[i]][j];
			}
			newFloorList[newFloorList.Length - 1] = floorBrush;
			newCielingList[newCielingList.Length - 1] = cielingBrush;
			sectorFloorBrushes[subsectorSectors[i]] = newFloorList;
			sectorCielingBrushes[subsectorSectors[i]] = newCielingList;
			
			bool containsMiddle = false;
			for (int j = 0; j < midBrush.NumSides; j++)
			{
				if (!midBrush[j].Texture.ToUpper().Equals("special/nodraw".ToUpper()))
				{
					containsMiddle = true;
					break;
				}
			}
			if (containsMiddle && currentSector.CielingHeight > currentSector.FloorHeight)
			{
				Entity middleEnt = new Entity("func_illusionary");
				if (midBrush.NumSides - badSides.Length >= 4)
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						midBrush.delete(badSides[j]);
					}
				}
				
				middleEnt.Brushes.Add(midBrush);
				mapFile.Add(middleEnt);
			}
			Entity hurtMe = new Entity("trigger_hurt");
			Entity triggerSecret = new Entity("trigger_bondsecret");
			switch (currentSector.Type)
			{
				case 4: 
				// 20% damage/s with lighting properties
				case 11: 
				// 20% damage/s
				case 16:  // 20% damage/s plus end level when player is almost dead
					hurtMe["dmg"] = "40";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 5:  // 10% damage/s
					hurtMe["dmg"] = "20";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 7:  // 5% damage/s
					hurtMe["dmg"] = "10";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 9:  // "secret"
					triggerSecret["Sound"] = "common/mission_success.wav";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					triggerSecret.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(triggerSecret);
					break;
				case 10:  // 30 seconds after level start, "close" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 3;
					}
					break;
				case 14:  // 300 seconds after level start, "open" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 4;
					}
					break;
				default: 
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					if ((currentSector.Type & 96) != 0)
					{
						// "Generalized" pain sectors
						if ((currentSector.Type & 96) == 32)
						{
							hurtMe["dmg"] = "10";
						}
						else
						{
							if ((currentSector.Type & 96) == 64)
							{
								hurtMe["dmg"] = "20";
							}
							else
							{
								if ((currentSector.Type & 96) == 96)
								{
									hurtMe["dmg"] = "40";
								}
							}
						}
						hurtMe.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(hurtMe);
					}
					if ((currentSector.Type & 128) == 128)
					{
						// "Generalized" secret trigger
						triggerSecret["Sound"] = "common/mission_success.wav";
						triggerSecret.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(triggerSecret);
					}
					break;
			}
			if((i+1)%onePercent == 0) {
				parent.OnProgress(this, (i+1)/(double)(doomMap.SubSectors.Count));
			}
			//Settings.setProgress(jobnum, i + 1, doomMap.SubSectors.Count, "Decompiling...");
		}
		
		// Add the brushes to the map, as world by default, or entities if they are supported.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			bool[] floorsUsed = new bool[sectorFloorBrushes[i].Length];
			bool[] cielingsUsed = new bool[sectorCielingBrushes[i].Length];
			if (sectorTag[i] == 0)
			{
				for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
					floorsUsed[j] = true;
					world.Brushes.Add(sectorCielingBrushes[i][j]);
					cielingsUsed[j] = true;
				}
			}
			else
			{
				if (sectorTag[i] == - 1 || sectorTag[i] == - 2 || sectorTag[i] == - 3 || sectorTag[i] == - 4)
				{
					// I'm using this to mean a door with no tag number
					Entity newDoor = new Entity("func_door");
					newDoor["speed"] = "60";
					newDoor["angles"] = "270 0 0";
					newDoor["spawnflags"] = "256";
					newDoor["targetname"] = "sectornum" + i + "door";
					if (sectorTag[i] == - 1)
					{
						newDoor["wait"] = "4";
					}
					else
					{
						if (sectorTag[i] == - 2)
						{
							newDoor["wait"] = "-1";
						}
					}
					if (sectorTag[i] == - 3)
					{
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "30";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					if (sectorTag[i] == - 4)
					{
						newDoor["Spawnflags"] = "1";
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "300";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
					int lip = ZMax - lowestNeighborCielingHeight + 4;
					newDoor["lip"] = "" + lip;
					for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
					{
						cielingsUsed[j] = true;
						newDoor.Brushes.Add(sectorCielingBrushes[i][j]);
					}
					mapFile.Add(newDoor);
				}
				else
				{
					for (int j = 0; j < doomMap.Linedefs.Count; j++)
					{
						DLinedef currentLinedef = doomMap.Linedefs[j];
						int linedefTriggerType = currentLinedef.Action;
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							switch (linedefTriggerType)
							{
								
								case 21: 
								// Floor lower to lowest neighbor
								case 22:  // Floor lower to nearest lower neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "90 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "lowerfloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
										}
										int lowestNeighborFloorHeight;
										if (linedefTriggerType == 21)
										{
											lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										}
										else
										{
											lowestNeighborFloorHeight = getNextLowestNeighborFloorHeight(i);
										}
										if (lowestNeighborFloorHeight == 32768)
										{
											lowestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - lowestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								case 24: 
								// Floor raise to highest neighbor
								case 25:  // Floor raise to nearest higher neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "270 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "raisefloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
										}
										int highestNeighborFloorHeight;
										if (linedefTriggerType == 24)
										{
											highestNeighborFloorHeight = getHighestNeighborFloorHeight(i);
										}
										else
										{
											highestNeighborFloorHeight = getNextHighestNeighborFloorHeight(i);
										}
										if (highestNeighborFloorHeight == - 32768)
										{
											highestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - highestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								}
						}
						else
						{
							if (currentLinedef.Tag == sectorTag[i])
							{
								switch (linedefTriggerType)
								{
									
									case 36: 
									// Line crossed, floor lowers, stays 8 above next lowest
									case 38:  // Line crossed, floor lowers, stays at next lowest
										Entity newFloor = new Entity("func_door");
										newFloor["speed"] = "120";
										newFloor["angles"] = "90 0 0";
										newFloor["targetname"] = "sector" + sectorTag[i] + "lowerfloor";
										newFloor["wait"] = "-1";
										int lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										int lip = ZMin - lowestNeighborFloorHeight;
										if (linedefTriggerType == 36)
										{
											lip -= 8;
										}
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
										break;
									case 63: 
									// Push button, door opens, waits 4s, closes
									case 103: 
									// Push button, door opens, stays
									case 109:  // Cross line, door opens, stays
										Entity newDoor = new Entity("func_door");
										newDoor["speed"] = "60";
										newDoor["angles"] = "270 0 0";
										newDoor["targetname"] = "sector" + sectorTag[i] + "door";
										newDoor["wait"] = "-1";
										if (sectorTag[i] == 63)
										{
											newDoor["wait"] = "4";
										}
										int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
										lip = ZMax - lowestNeighborCielingHeight + 4;
										newDoor["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!cielingsUsed[k])
											{
												cielingsUsed[k] = true;
												newDoor.Brushes.Add(sectorCielingBrushes[i][k]);
											}
										}
										mapFile.Add(newDoor);
										break;
									case 62: 
									// Push button, Elevator goes down to lowest, wait 4s, goes up
									case 88:  // Elevator goes down to lowest, wait 4s, goes up
										Entity newVator = new Entity("func_door");
										newVator["speed"] = "120";
										newVator["angles"] = "90 0 0";
										newVator["targetname"] = "sector" + sectorTag[i] + "vator";
										newVator["wait"] = "4";
										lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										lip = System.Math.Abs(ZMin - lowestNeighborFloorHeight);
										newVator["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												newVator.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
										}
										mapFile.Add(newVator);
										break;
									default:  // I'd like to not use this evenutally, all the trigger types ought to be handled
										DecompilerThread.OnMessage(this, "WARNING: Unimplemented linedef trigger type " + linedefTriggerType + " for sector " + i + " tagged " + sectorTag[i]);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												world.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
											if (!cielingsUsed[k])
											{
												world.Brushes.Add(sectorCielingBrushes[i][k]);
												cielingsUsed[k] = true;
											}
										}
										break;
								}
							}
						}
					}
				}
			}
			for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
			{
				if (!cielingsUsed[j])
				{
					world.Brushes.Add(sectorCielingBrushes[i][j]);
				}
				if (!floorsUsed[j])
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
				}
			}
		}
		
		// Convert THINGS
		for (int i = 0; i < doomMap.Things.Count; i++)
		{
			DThing currentThing = doomMap.Things[i];
			// To find the true height of a thing, I need to iterate through nodes until I come to a subsector
			// definition. Then I need to use the floor height of the sector that subsector belongs to.
			Vector3D origin = currentThing.Origin;
			int subsectorIndex = doomMap.Nodes.Count - 1;
			while (subsectorIndex >= 0)
			{
				// Once child is negative, subsector is found
				DNode currentNode = doomMap.Nodes[subsectorIndex];
				Vector3D start = currentNode.VecHead;
				Vector3D end = currentNode.VecHead+currentNode.VecTail;
				Plane currentPlane = new Plane(start, end, new Vector3D(start.X, start.Y, 1));
				if (currentPlane.distance(origin) < 0)
				{
					subsectorIndex = currentNode.Child1;
				}
				else
				{
					subsectorIndex = currentNode.Child2;
				}
			}
			subsectorIndex += 32768;
			int sectorIndex = subsectorSectors[subsectorIndex];
			DSector thingSector = doomMap.Sectors[sectorIndex];
			if (origin.Z == 0)
			{
				origin.Z=thingSector.FloorHeight;
			}
			
			Entity thing = null;
			// Things from both Doom. Currently converting to appropriate Doom 3 entities.
			switch (currentThing.ClassNum)
			{
				case 1: 
				// Single player spawn
				case 2: 
				// coop
				case 3: 
				// coop
				case 4:  // coop
					thing = new Entity("info_player_start");
					if (currentThing.ClassNum > 1)
					{
						thing["targetname"] = "coopspawn" + currentThing.ClassNum;
					}
					playerStartOrigin = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["origin"] = playerStartOrigin;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 11:  // Deathmatch spawn
					thing = new Entity("info_player_deathmatch");
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 14:  // Teleport destination
					thing = new Entity("info_teleport_destination");
					if (currentThing.ID != 0)
					{
						thing["targetname"] = "teledest" + currentThing.ID;
					}
					else
					{
						thing["targetname"] = "sector" + thingSector.Tag + "teledest";
					}
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 17:  // Big cell pack
					thing = new Entity("ammo_cells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 82:  // Super shotgun
					thing = new Entity("weapon_shotgun_double");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2001:  // Shotgun
					thing = new Entity("weapon_shotgun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2002:  // Chaingun
					thing = new Entity("weapon_chaingun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2003:  // Rocket launcher
					thing = new Entity("weapon_rocketlauncher");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2004:  // Plasma gun
					thing = new Entity("weapon_plasmagun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2005:  // Chainsaw
					thing = new Entity("weapon_chainsaw");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2006:  // BFG9000
					thing = new Entity("weapon_bfg");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2007:  // Ammo clip
					thing = new Entity("ammo_clip_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2008:  // Shotgun shells
					thing = new Entity("ammo_shells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2010:  // Rocket
					thing = new Entity("ammo_rockets_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2046:  // Box of Rockets
					thing = new Entity("ammo_rockets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2047:  // Cell pack
					thing = new Entity("ammo_cells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2048:  // Box of ammo
					thing = new Entity("ammo_bullets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2049:  // Box of shells
					thing = new Entity("ammo_shells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				}
			
			if (thing != null)
			{
				mapFile.Add(thing);
			}
		}
		
		Entity playerequip = new Entity("game_player_equip");
		playerequip["weapon_pistol"] = "1";
		playerequip["origin"] = playerStartOrigin;
		mapFile.Add(playerequip);
		parent.OnProgress(this, 1.0);
		return mapFile;
	}
    // -decompileBrush(Brush, int)
    // Decompiles the Brush and adds it to entitiy #currentEntity as MAPBrush classes.
    private void decompileBrush(Brush brush, int currentEntity)
    {
        Vector3D origin    = mapFile[currentEntity].Origin;
        int      firstSide = brush.FirstSide;
        int      numSides  = brush.NumSides;

        if (firstSide < 0)
        {
            isCoD             = true;
            firstSide         = currentSideIndex;
            currentSideIndex += numSides;
        }
        MAPBrushSide[] brushSides        = new MAPBrushSide[0];
        bool           isDetail          = false;
        int            brushTextureIndex = brush.Texture;

        byte[] contents = new byte[4];
        if (brushTextureIndex >= 0)
        {
            contents = BSPObject.Textures[brushTextureIndex].Contents;
        }
        if (!Settings.noDetail && (contents[3] & ((byte)1 << 3)) != 0)
        {
            // This is the flag according to q3 source
            isDetail = true;             // it's the same as Q2 (and Source), but I haven't found any Q3 maps that use it, so far
        }
        MAPBrush mapBrush     = new MAPBrush(numBrshs, currentEntity, isDetail);
        int      numRealFaces = 0;

        Plane[] brushPlanes = new Plane[0];
        //DecompilerThread.OnMessage(this, ": " + numSides + " sides");
        if (!Settings.noWater && (contents[0] & ((byte)1 << 5)) != 0)
        {
            mapBrush.Water = true;
        }
        bool isVisBrush = false;

        for (int i = 0; i < numSides; i++)
        {
            // For each side of the brush
            BrushSide currentSide      = BSPObject.BrushSides[firstSide + i];
            int       currentFaceIndex = currentSide.Face;
            Plane     currentPlane;
            if (isCoD)
            {
                switch (i)
                {
                case 0:                         // XMin
                    currentPlane = new Plane((double)(-1), (double)0, (double)0, (double)(-currentSide.Dist));
                    break;

                case 1:                         // XMax
                    currentPlane = new Plane((double)1, (double)0, (double)0, (double)currentSide.Dist);
                    break;

                case 2:                         // YMin
                    currentPlane = new Plane((double)0, (double)(-1), (double)0, (double)(-currentSide.Dist));
                    break;

                case 3:                         // YMax
                    currentPlane = new Plane((double)0, (double)1, (double)0, (double)currentSide.Dist);
                    break;

                case 4:                         // ZMin
                    currentPlane = new Plane((double)0, (double)0, (double)(-1), (double)(-currentSide.Dist));
                    break;

                case 5:                         // ZMax
                    currentPlane = new Plane((double)0, (double)0, (double)1, (double)currentSide.Dist);
                    break;

                default:
                    currentPlane = BSPObject.Planes[currentSide.Plane];
                    break;
                }
            }
            else
            {
                currentPlane = BSPObject.Planes[currentSide.Plane];
            }
            Vector3D[] triangle     = new Vector3D[0];         // Three points define a plane. All I have to do is find three points on that plane.
            bool       pointsWorked = false;
            int        firstVertex  = -1;
            int        numVertices  = 0;
            string     texture      = "noshader";
            bool       masked       = false;
            if (currentFaceIndex > -1)
            {
                Face currentFace         = BSPObject.Faces[currentFaceIndex];
                int  currentTextureIndex = currentFace.Texture;
                firstVertex = currentFace.FirstVertex;
                numVertices = currentFace.NumVertices;
                string mask = BSPObject.Textures[currentTextureIndex].Mask;
                if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0)
                {
                    texture = BSPObject.Textures[currentTextureIndex].Name;
                }
                else
                {
                    texture = mask.Substring(0, (mask.Length - 4) - (0));                     // Because mask includes file extensions
                    masked  = true;
                }
                if (numVertices != 0 && !Settings.planarDecomp)
                {
                    // If the face actually references a set of vertices
                    triangle = new Vector3D[3];
                    double currentHighest = 0.0;
                    // Find the combination of three vertices which gives the greatest area
                    for (int p1 = 0; p1 < numVertices - 2; p1++)
                    {
                        for (int p2 = p1 + 1; p2 < numVertices - 1; p2++)
                        {
                            for (int p3 = p2 + 1; p3 < numVertices; p3++)
                            {
                                double currentArea = Vector3D.SqrTriangleArea(BSPObject.Vertices[firstVertex + p1].Vector, BSPObject.Vertices[firstVertex + p2].Vector, BSPObject.Vertices[firstVertex + p3].Vector);
                                if (currentArea > Settings.precision * Settings.precision * 4.0)                                  // Three collinear points will generate an area of 0 or almost 0
                                {
                                    pointsWorked = true;
                                    if (currentArea > currentHighest)
                                    {
                                        currentHighest = currentArea;
                                        triangle[0]    = BSPObject.Vertices[firstVertex + p1].Vector;
                                        triangle[1]    = BSPObject.Vertices[firstVertex + p2].Vector;
                                        triangle[2]    = BSPObject.Vertices[firstVertex + p3].Vector;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // If face information is not available, use the brush side's info instead
                int currentTextureIndex = currentSide.Texture;
                if (currentTextureIndex >= 0)
                {
                    string mask = BSPObject.Textures[currentTextureIndex].Mask;
                    if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0)
                    {
                        texture = BSPObject.Textures[currentTextureIndex].Name;
                    }
                    else
                    {
                        texture = mask.Substring(0, (mask.Length - 4) - (0));                         // Because mask includes file extensions
                        masked  = true;
                    }
                }
                else
                {
                    // If neither face or brush side has texture info, fall all the way back to brush. I don't know if this ever happens.
                    if (brushTextureIndex >= 0)
                    {
                        // If none of them have any info, noshader
                        string mask = BSPObject.Textures[brushTextureIndex].Mask;
                        if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0)
                        {
                            texture = BSPObject.Textures[brushTextureIndex].Name;
                        }
                        else
                        {
                            texture = mask.Substring(0, (mask.Length - 4) - (0));                             // Because mask includes file extensions
                            masked  = true;
                        }
                    }
                }
            }
            if (texture.ToUpper().Equals("textures/common/vis".ToUpper()))
            {
                isVisBrush = true;
                return;                 // TODO: Try to recreate the vis entity? It's impossible to recreate the links...
            }
            // Get the lengths of the axis vectors.
            // TODO: This information seems to be contained in Q3's vertex structure. But there doesn't seem
            // to be a way to directly link faces to brush sides.
            double     UAxisLength  = 1;
            double     VAxisLength  = 1;
            double     texScaleS    = 1;
            double     texScaleT    = 1;
            Vector3D[] textureAxes  = TexInfo.textureAxisFromPlane(currentPlane);
            double     originShiftS = (textureAxes[0].X * origin[X]) + (textureAxes[0].Y * origin[Y]) + (textureAxes[0].Z * origin[Z]);
            double     originShiftT = (textureAxes[1].X * origin[X]) + (textureAxes[1].Y * origin[Y]) + (textureAxes[1].Z * origin[Z]);
            double     textureShiftS;
            double     textureShiftT;
            if (firstVertex >= 0)
            {
                textureShiftS = (double)BSPObject.Vertices[firstVertex].TexCoordX - originShiftS;
                textureShiftT = (double)BSPObject.Vertices[firstVertex].TexCoordY - originShiftT;
            }
            else
            {
                textureShiftS = 0 - originShiftS;
                textureShiftT = 0 - originShiftT;
            }
            float  texRot = 0;
            string material;
            if (masked)
            {
                material = "wld_masked";
            }
            else
            {
                material = "wld_lightmap";
            }
            double         lgtScale = 16;
            double         lgtRot   = 0;
            MAPBrushSide[] newList  = new MAPBrushSide[brushSides.Length + 1];
            for (int j = 0; j < brushSides.Length; j++)
            {
                newList[j] = brushSides[j];
            }
            int flags;
            //if(Settings.noFaceFlags) {
            flags = 0;
            //}
            if (pointsWorked)
            {
                newList[brushSides.Length] = new MAPBrushSide(currentPlane, triangle, texture, textureAxes[0].Point, textureShiftS, textureAxes[1].Point, textureShiftT, texRot, texScaleS, texScaleT, flags, material, lgtScale, lgtRot);
            }
            else
            {
                newList[brushSides.Length] = new MAPBrushSide(currentPlane, texture, textureAxes[0].Point, textureShiftS, textureAxes[1].Point, textureShiftT, texRot, texScaleS, texScaleT, flags, material, lgtScale, lgtRot);
            }
            brushSides = newList;
            numRealFaces++;
        }

        for (int i = 0; i < brushSides.Length; i++)
        {
            mapBrush.add(brushSides[i]);
        }

        brushPlanes = new Plane[mapBrush.NumSides];
        for (int i = 0; i < brushPlanes.Length; i++)
        {
            brushPlanes[i] = mapBrush[i].Plane;
        }

        if (isCoD && mapBrush.NumSides > 6)
        {
            // Now we need to get rid of all the sides that aren't used. Get a list of
            // the useless sides from one brush, and delete those sides from all of them,
            // since they all have the same sides.
            if (!Settings.dontCull && numSides > 6)
            {
                int[] badSides = MAPBrush.findUnusedPlanes(mapBrush);
                // Need to iterate backward, since these lists go from low indices to high, and
                // the index of all subsequent items changes when something before it is removed.
                if (mapBrush.NumSides - badSides.Length < 4)
                {
                    DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for entity " + currentEntity + " brush " + numBrshs);
                }
                else
                {
                    for (int i = badSides.Length - 1; i > -1; i--)
                    {
                        mapBrush.delete(badSides[i]);
                    }
                }
            }
        }

        if (!Settings.skipPlaneFlip)
        {
            if (mapBrush.hasBadSide())
            {
                // If there's a side that might be backward
                if (mapBrush.hasGoodSide())
                {
                    // If there's a side that is forward
                    mapBrush = MAPBrush.SimpleCorrectPlanes(mapBrush);
                    numSimpleCorrects++;
                    if (Settings.calcVerts)
                    {
                        // This is performed in advancedcorrect, so don't use it if that's happening
                        try {
                            mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                        } catch (System.NullReferenceException) {
                            DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                        }
                    }
                }
                else
                {
                    // If no forward side exists
                    try {
                        mapBrush = MAPBrush.AdvancedCorrectPlanes(mapBrush);
                        numAdvancedCorrects++;
                    } catch (System.ArithmeticException) {
                        DecompilerThread.OnMessage(this, "WARNING: Plane correct returned 0 triangles for entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                    }
                }
            }
            else
            {
                numGoodBrushes++;
            }
        }
        else
        {
            if (Settings.calcVerts)
            {
                // This is performed in advancedcorrect, so don't use it if that's happening
                try {
                    mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                } catch (System.NullReferenceException) {
                    DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                }
            }
        }

        // This adds the brush we've been finding and creating to
        // the current entity as an attribute. The way I've coded
        // this whole program and the entities parser, this shouldn't
        // cause any issues at all.
        if (Settings.brushesToWorld)
        {
            mapBrush.Water = false;
            worldspawn.Brushes.Add(mapBrush);
        }
        else
        {
            mapFile[currentEntity].Brushes.Add(mapBrush);
        }
    }
	// -decompileBrush(Brush, int)
	// Decompiles the Brush and adds it to entitiy #currentEntity as MAPBrush classes.
	private void decompileBrush(Brush brush, int currentEntity) {
		Vector3D origin = mapFile[currentEntity].Origin;
		int firstSide = brush.FirstSide;
		int numSides = brush.NumSides;
		if (firstSide < 0) {
			isCoD = true;
			firstSide = currentSideIndex;
			currentSideIndex += numSides;
		}
		MAPBrushSide[] brushSides = new MAPBrushSide[0];
		bool isDetail = false;
		int brushTextureIndex = brush.Texture;
		byte[] contents = new byte[4];
		if (brushTextureIndex >= 0) {
			contents = BSPObject.Textures[brushTextureIndex].Contents;
		}
		if (!Settings.noDetail && (contents[3] & ((byte) 1 << 3)) != 0) {
			// This is the flag according to q3 source
			isDetail = true; // it's the same as Q2 (and Source), but I haven't found any Q3 maps that use it, so far
		}
		MAPBrush mapBrush = new MAPBrush(numBrshs, currentEntity, isDetail);
		int numRealFaces = 0;
		Plane[] brushPlanes = new Plane[0];
		//DecompilerThread.OnMessage(this, ": " + numSides + " sides");
		if (!Settings.noWater && (contents[0] & ((byte) 1 << 5)) != 0) {
			mapBrush.Water = true;
		}
		bool isVisBrush = false;
		for (int i = 0; i < numSides; i++) {
			// For each side of the brush
			BrushSide currentSide = BSPObject.BrushSides[firstSide + i];
			int currentFaceIndex = currentSide.Face;
			Plane currentPlane;
			if (isCoD) {
				switch(i) {
					case 0: // XMin
						currentPlane = new Plane((double) (-1), (double) 0, (double) 0, (double) (-currentSide.Dist));
						break;
					case 1: // XMax
						currentPlane = new Plane((double) 1, (double) 0, (double) 0, (double) currentSide.Dist);
						break;
					case 2: // YMin
						currentPlane = new Plane((double) 0, (double) (-1), (double) 0, (double) (-currentSide.Dist));
						break;
					case 3: // YMax
						currentPlane = new Plane((double) 0, (double) 1, (double) 0, (double) currentSide.Dist);
						break;
					case 4: // ZMin
						currentPlane = new Plane((double) 0, (double) 0, (double) (-1), (double) (-currentSide.Dist));
						break;
					case 5: // ZMax
						currentPlane = new Plane((double) 0, (double) 0, (double) 1, (double) currentSide.Dist);
						break;
					default:
						currentPlane = BSPObject.Planes[currentSide.Plane];
						break;
				}
			} else {
				currentPlane = BSPObject.Planes[currentSide.Plane];
			}
			Vector3D[] triangle = new Vector3D[0]; // Three points define a plane. All I have to do is find three points on that plane.
			bool pointsWorked = false;
			int firstVertex = - 1;
			int numVertices = 0;
			string texture = "noshader";
			bool masked = false;
			if (currentFaceIndex > - 1) {
				Face currentFace = BSPObject.Faces[currentFaceIndex];
				int currentTextureIndex = currentFace.Texture;
				firstVertex = currentFace.FirstVertex;
				numVertices = currentFace.NumVertices;
				string mask = BSPObject.Textures[currentTextureIndex].Mask;
				if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0) {
					texture = BSPObject.Textures[currentTextureIndex].Name;
				} else {
					texture = mask.Substring(0, (mask.Length - 4) - (0)); // Because mask includes file extensions
					masked = true;
				}
				if (numVertices != 0 && !Settings.planarDecomp) {
					// If the face actually references a set of vertices
					triangle = new Vector3D[3];
					double currentHighest = 0.0;
					// Find the combination of three vertices which gives the greatest area
					for(int p1 = 0; p1 < numVertices-2; p1++) {
						for(int p2 = p1+1; p2 < numVertices-1; p2++) {
							for(int p3 = p2+1; p3 < numVertices; p3++) {
								double currentArea = Vector3D.SqrTriangleArea(BSPObject.Vertices[firstVertex + p1].Vector, BSPObject.Vertices[firstVertex + p2].Vector, BSPObject.Vertices[firstVertex + p3].Vector);
								if(currentArea > Settings.precision * Settings.precision * 4.0) { // Three collinear points will generate an area of 0 or almost 0
									pointsWorked = true;
									if(currentArea > currentHighest) {
										currentHighest = currentArea;
										triangle[0] = BSPObject.Vertices[firstVertex + p1].Vector;
										triangle[1] = BSPObject.Vertices[firstVertex + p2].Vector;
										triangle[2] = BSPObject.Vertices[firstVertex + p3].Vector;
									}
								}
							}
						}
					}
				}
			} else {
				// If face information is not available, use the brush side's info instead
				int currentTextureIndex = currentSide.Texture;
				if (currentTextureIndex >= 0) {
					string mask = BSPObject.Textures[currentTextureIndex].Mask;
					if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0) {
						texture = BSPObject.Textures[currentTextureIndex].Name;
					} else {
						texture = mask.Substring(0, (mask.Length - 4) - (0)); // Because mask includes file extensions
						masked = true;
					}
				} else {
					// If neither face or brush side has texture info, fall all the way back to brush. I don't know if this ever happens.
					if (brushTextureIndex >= 0) {
						// If none of them have any info, noshader
						string mask = BSPObject.Textures[brushTextureIndex].Mask;
						if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0) {
							texture = BSPObject.Textures[brushTextureIndex].Name;
						} else {
							texture = mask.Substring(0, (mask.Length - 4) - (0)); // Because mask includes file extensions
							masked = true;
						}
					}
				}
			}
			if (texture.ToUpper().Equals("textures/common/vis".ToUpper())) {
				isVisBrush = true;
				return; // TODO: Try to recreate the vis entity? It's impossible to recreate the links...
			}
			// Get the lengths of the axis vectors.
			// TODO: This information seems to be contained in Q3's vertex structure. But there doesn't seem
			// to be a way to directly link faces to brush sides.
			double UAxisLength = 1;
			double VAxisLength = 1;
			double texScaleS = 1;
			double texScaleT = 1;
			Vector3D[] textureAxes = TexInfo.textureAxisFromPlane(currentPlane);
			double originShiftS = (textureAxes[0].X * origin[X]) + (textureAxes[0].Y * origin[Y]) + (textureAxes[0].Z * origin[Z]);
			double originShiftT = (textureAxes[1].X * origin[X]) + (textureAxes[1].Y * origin[Y]) + (textureAxes[1].Z * origin[Z]);
			double textureShiftS;
			double textureShiftT;
			if (firstVertex >= 0) {
				textureShiftS = (double) BSPObject.Vertices[firstVertex].TexCoordX - originShiftS;
				textureShiftT = (double) BSPObject.Vertices[firstVertex].TexCoordY - originShiftT;
			} else {
				textureShiftS = 0 - originShiftS;
				textureShiftT = 0 - originShiftT;
			}
			float texRot = 0;
			string material;
			if (masked) {
				material = "wld_masked";
			} else {
				material = "wld_lightmap";
			}
			double lgtScale = 16;
			double lgtRot = 0;
			MAPBrushSide[] newList = new MAPBrushSide[brushSides.Length + 1];
			for (int j = 0; j < brushSides.Length; j++) {
				newList[j] = brushSides[j];
			}
			int flags;
			//if(Settings.noFaceFlags) {
			flags = 0;
			//}
			if (pointsWorked) {
				newList[brushSides.Length] = new MAPBrushSide(currentPlane, triangle, texture, textureAxes[0].Point, textureShiftS, textureAxes[1].Point, textureShiftT, texRot, texScaleS, texScaleT, flags, material, lgtScale, lgtRot);
			} else {
				newList[brushSides.Length] = new MAPBrushSide(currentPlane, texture, textureAxes[0].Point, textureShiftS, textureAxes[1].Point, textureShiftT, texRot, texScaleS, texScaleT, flags, material, lgtScale, lgtRot);
			}
			brushSides = newList;
			numRealFaces++;
		}
		
		for (int i = 0; i < brushSides.Length; i++) {
			mapBrush.add(brushSides[i]);
		}
		
		brushPlanes = new Plane[mapBrush.NumSides];
		for (int i = 0; i < brushPlanes.Length; i++) {
			brushPlanes[i] = mapBrush[i].Plane;
		}
		
		if (isCoD && mapBrush.NumSides > 6) {
			// Now we need to get rid of all the sides that aren't used. Get a list of
			// the useless sides from one brush, and delete those sides from all of them,
			// since they all have the same sides.
			if (!Settings.dontCull && numSides > 6) {
				int[] badSides = MAPBrush.findUnusedPlanes(mapBrush);
				// Need to iterate backward, since these lists go from low indices to high, and
				// the index of all subsequent items changes when something before it is removed.
				if (mapBrush.NumSides - badSides.Length < 4) {
					DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for entity " + currentEntity + " brush " + numBrshs);
				} else {
					for (int i = badSides.Length - 1; i > - 1; i--) {
						mapBrush.delete(badSides[i]);
					}
				}
			}
		}
		
		if (!Settings.skipPlaneFlip) {
			if (mapBrush.hasBadSide()) {
				// If there's a side that might be backward
				if (mapBrush.hasGoodSide()) {
					// If there's a side that is forward
					mapBrush = MAPBrush.SimpleCorrectPlanes(mapBrush);
					numSimpleCorrects++;
					if (Settings.calcVerts) {
						// This is performed in advancedcorrect, so don't use it if that's happening
						try {
							mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
						} catch (System.NullReferenceException) {
							DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
						}
					}
				} else {
					// If no forward side exists
					try {
						mapBrush = MAPBrush.AdvancedCorrectPlanes(mapBrush);
						numAdvancedCorrects++;
					} catch (System.ArithmeticException) {
						DecompilerThread.OnMessage(this, "WARNING: Plane correct returned 0 triangles for entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
					}
				}
			} else {
				numGoodBrushes++;
			}
		} else {
			if (Settings.calcVerts) {
				// This is performed in advancedcorrect, so don't use it if that's happening
				try {
					mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
				} catch (System.NullReferenceException) {
					DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
				}
			}
		}
		
		// This adds the brush we've been finding and creating to
		// the current entity as an attribute. The way I've coded
		// this whole program and the entities parser, this shouldn't
		// cause any issues at all.
		if (Settings.brushesToWorld) {
			mapBrush.Water = false;
			worldspawn.Brushes.Add(mapBrush);
		} else {
			mapFile[currentEntity].Brushes.Add(mapBrush);
		}
	}