public static void GenerateLevel(string StarsPath)
        {
            N8Level level = new N8Level();
            N8BlockFactory LevelBlocks = level.blocks;
            List<Vector3D> points = new List<Vector3D>();

            using (StreamReader sr = new StreamReader(File.OpenRead(StarsPath)))
            {
                string input;

                Regex SplitWhitespace = new Regex(@"\s+");
                while ((input = sr.ReadLine()) != null)
                {
                    string[] parts = SplitWhitespace.Split(input);
                    points.Add(new Vector3D(double.Parse(parts[3]), double.Parse(parts[4]), double.Parse(parts[5])));
                }
            }

            //Take only the first 300 stars
            points = points.Take(300).ToList();

            List<N8Block> stars;

            //Useful options:
            //snowmantop
            //letter.period
            //letter.period.big
            stars = Utilities.PlotPointsSphere(points, new Quaternion(0, 0, 0, 1), 1000, "snowmantop", "Star", level);

            N8Block ControlPoint = LevelBlocks.GenerateBlock("minipixelblack", "Control Point");

            foreach (N8Block star in stars)
            {
                ControlPoint.AttachToMe(star);
            }

            //Make a roof
            for (int i = -800; i <= 800; i += 400)
            {
                for (int j = -800; j <= 800; j += 400)
                {
                    N8Block roof = LevelBlocks.GenerateBlock("simplelandblack", "Vault of the Heavens");
                    roof.position = new Vector3D(i, j, 1000);
                }
            }

            string save = level.GenerateSaveFile();
            using(StreamWriter sw = new StreamWriter(File.Open(@"C:\Program Files (x86)\N8\Saves\planetarium.ncd", FileMode.Truncate)))
            {
                sw.WriteLine(save);
            }

            Console.WriteLine(save);
            Console.Read();
        }
        public static void Mirror()
        {
            string filename = @"C:\Program Files (x86)\N8\Saves\ctf_blu_wallsonly_wip.ncd";

            N8Level level = new N8Level(filename);

            List<N8Block> blue_blocks = (from b in level.blocks.Blocks where b.type.Contains("blue") select b).ToList<N8Block>();

            foreach (N8Block b in blue_blocks)
            {
                Console.WriteLine("Looking at " + b);
                N8Block red_clone = level.blocks.CloneBlock(b);
                Console.WriteLine("Clone is: " + red_clone);

                //necessary to change the type
                red_clone.type = red_clone.type.Replace("blue", "red");

                //Not necessary, cosmetic only.
                red_clone.name = red_clone.name.Replace("Blue", "Red");

                Console.WriteLine("Changed name, now it's: " + red_clone);

                red_clone.position = Utilities.ReflectPlane(red_clone.position, new Vector3D(1, 0, 0));

                Console.WriteLine("Reflected it, now it's: " + red_clone);
            }

            string result = level.GenerateSaveFile();
            Console.WriteLine(result);
            Console.Read();

            string outFilename = @"C:\Program Files (x86)\N8\Saves\out-test.ncd";

            using (StreamWriter sw = new StreamWriter(File.OpenWrite(outFilename)))
            {
                sw.Write(result);
            }
        }
 public void MergeWithSafe(N8Level other)
 {
     this.blocks.CopyFromNonDestructive(other.blocks);
 }
        public static void GenerateProxyBimesh()
        {
            string SavePath = @"C:\Program Files (x86)\N8\Saves\proxybimesh.ncd";

            N8Level Level = new N8Level();

            N8BlockFactory LevelBlocks = Level.blocks;

            List<FlowTronic> proxies = new List<FlowTronic>();
            for (int i = -330; i <= 330; i += 60)
            {
                for (int j = -330; j <= 330; j += 60)
                {
                    int x = i;
                    int y = j;
                    if ((i/60) % 2 == 0)
                    {
                        y = j + 15;
                    }
                    else
                    {
                        y = j - 15;
                    }
                    if ((j / 60) % 2 == 0)
                    {
                        x = i + 15;
                    }
                    else
                    {
                        x = i - 15;
                    }

                    Vector3D pos = new Vector3D(x, y, 1000);

                    FlowTronic TopProxy = LevelBlocks.Proxy(x + "," + y);
                    pos.Z = 270;
                    TopProxy.position = pos;

                    FlowTronic BottomProxy = LevelBlocks.Proxy(x + "," + y);
                    pos.Z = 0;
                    BottomProxy.position = pos;

                    proxies.Add(TopProxy);
                    proxies.Add(BottomProxy);
                }
            }
            FleeTronics(LevelBlocks, proxies);

            Utilities.Save(SavePath, Level);
        }
        public static N8Level GetProxyBubble()
        {
            N8Level Level = new N8Level();
            Quaternion UpsideDown = new Quaternion(new Vector3D(1, 0, 0), 180);

            N8BlockFactory LevelBlocks = Level.blocks;

            List<Tuple<Vector3D, Quaternion>> proxies = new List<Tuple<Vector3D,Quaternion>>();
            //points.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, -1), 45, 45));
            //points.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 250), 45, 45));

            proxies.Add(Tuple.Create(new Vector3D(120, 0, 250), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-120, 0, 250), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(0, 120, 250), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(0, -120, 250), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(50, 50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-50, 50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(50, -50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-50, -50, 50), new Quaternion()));

            proxies.AddRange(Utilities.EvenSphere(new Vector3D(0, 0, 50), 85, 480, (double)8 / 16 * Math.PI));
            //points.AddRange(Utilities.EvenSphere(new Vector3D(0, 0, 50), 85, 175, (double)8 / 16 * Math.PI));
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 0), 90, 300));
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 0), 80, 250));
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 0), 70, 200));
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 0), 60, 150));
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 0), 50, 75));
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 0), 45, 20));
            //points.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, 50), 90, 700));
            //*/

            List<Tuple<Vector3D, Quaternion>> targets = new List<Tuple<Vector3D, Quaternion>>();
            targets.AddRange(Utilities.EvenSphere(new Vector3D(0, 0, 50), 45, 50, (double)10 / 16 * Math.PI));

            Quaternion ProxyRot = new Quaternion(new Vector3D(0, 0, 1), -90) * new Quaternion(new Vector3D(0,1,0), 90);
            Quaternion TargetRot = new Quaternion();//new Vector3D(0, 1, 0), -90);

            List<FlowTronic> alerts = new List<FlowTronic>();
            for(int i = 0; i < proxies.Count; i++)
            {
                Tuple<Vector3D, Quaternion> t = proxies[i];
                FlowTronic prox = LevelBlocks.Proxy("i=" + i);
                prox.position = t.Item1;

                alerts.Add(prox);
            }

            for (int i = 0; i < targets.Count; i++)
            {
                Tuple<Vector3D, Quaternion> t = targets[i];
                FlowTronic target = LevelBlocks.Target("i=" + i);
                target.position = t.Item1;
                target.rotation = TargetRot * t.Item2;

                alerts.Add(target);
            }

            FleeTronics(LevelBlocks, alerts);

            //Console.WriteLine("Total number of blocks used: " + (LevelBlocks.Tronics.Count + LevelBlocks.Blocks.Count));
            return Level;
        }
        public static N8Level GetHutProxies(string password=null)
        {
            N8Level Level = new N8Level(Utilities.GetDefaultSaveFolder() + "shrine_hut_final.ncd");
            int counter = 500;
            foreach (N8Block b in Level.blocks.Blocks)
            {
                b.ID = counter;
                counter++;
            }

            Level.blocks.SetNextID(counter);

            Quaternion UpsideDown = new Quaternion(new Vector3D(1, 0, 0), 180);

            N8BlockFactory LevelBlocks = Level.blocks;

            List<Tuple<Vector3D, Quaternion>> targets = new List<Tuple<Vector3D, Quaternion>>();
            List<Tuple<Vector3D, Quaternion>> proxies = new List<Tuple<Vector3D, Quaternion>>();
            proxies.AddRange(Utilities.EvenCircle(new Vector3D(0, 0, -1), 45, 45));
            proxies.Add(Tuple.Create(new Vector3D(50, 50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-50, 50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(50, -50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-50, -50, 50), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-80, -80, 90), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(80, -80, 90), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(-80, 80, 90), new Quaternion()));
            proxies.Add(Tuple.Create(new Vector3D(80, 80, 90), new Quaternion()));
            //proxies.AddRange(Utilities.EvenSphere(new Vector3D(0, 0, 50), 60, 250, (double)8 / 16 * Math.PI));

            for (int i = -160; i <= 160; i += 40)
            {
                for (int j = -40; j <= 40; j += 40)
                {
                    proxies.Add(Tuple.Create(new Vector3D(i, j, 270), new Quaternion()));
                }
            }

            for (int i = -130; i <= 140; i += 45)
            {
                for (int j = -90; j <= 90; j += 45)
                {
                    targets.Add(Tuple.Create(new Vector3D(i, j, 300), new Quaternion()));
                }
            }

            targets.AddRange(Utilities.EvenSphere(new Vector3D(0, 0, 50), 45, 50, (double)10 / 16 * Math.PI));

            Quaternion ProxyRot = new Quaternion(new Vector3D(0, 0, 1), -90) * new Quaternion(new Vector3D(0, 1, 0), 90);
            Quaternion TargetRot = new Quaternion();//new Vector3D(0, 1, 0), -90);

            List<FlowTronic> alerts = new List<FlowTronic>();
            for (int i = 0; i < proxies.Count; i++)
            {
                Tuple<Vector3D, Quaternion> t = proxies[i];
                FlowTronic prox = LevelBlocks.Proxy("i=" + i);
                prox.position = t.Item1;

                alerts.Add(prox);
            }

            for (int i = 0; i < targets.Count; i++)
            {
                Tuple<Vector3D, Quaternion> t = targets[i];
                FlowTronic target = LevelBlocks.Target("i=" + i);
                target.position = t.Item1;
                target.rotation = TargetRot * t.Item2;

                alerts.Add(target);
            }

            FleeTronics(LevelBlocks, alerts, false, true, password);

            //Console.WriteLine("Total number of blocks used: " + (LevelBlocks.Tronics.Count + LevelBlocks.Blocks.Count));
            return Level;
        }
        public static void GenerateLevel()
        {
            string SavePath = @"C:\Program Files (x86)\N8\Saves\maxprotecttest.ncd";
            string TronicsPath = @"C:\Program Files (x86)\N8\Saves\maxprotect_tronics_base_proxies.ncd";
            //I like black best, so make it more likely to show up
            //string[] colors = { "blue", "green", "orange", "purple", "red", "black", "black" };
            //Apparently nobody else likes the colors, so we're back to just black.
            string[] colors = { "black" };
            N8Level Level = new N8Level();
            N8Level tronics = new N8Level(TronicsPath);

            N8BlockFactory LevelBlocks = Level.blocks;

            Random rand = new Random();

            for (int i = -1000; i < 1380; i = i + 70)
            {
                for (int j = -1333; j <= 2000; j = j + 1333)
                {
                    for(int k = -1333; k <= 2000; k = k + 1333)
                    {
                        if (!(k == 0 && j == 0))
                        {
                            string color = colors[rand.Next(0, colors.Length)];
                            N8Block CurrentBlock = LevelBlocks.GenerateBlock("simpleland" + color, "Vault of the Heavens");
                            CurrentBlock.position.Z = i;
                            CurrentBlock.position.X = j;
                            CurrentBlock.position.Y = k;

                        }
                    }
                }
            }

            Level.MergeWithDestructive(tronics);

            //Non position dependent tronics - tronics whose position doesn't matter
            var NPDTronics = from N8Tronic t in LevelBlocks.Tronics
                                        where !(t.type == "rproximity" || t.type == "rkeyboard" || t.type == "tmover")
                                        select t;
            N8Block AttachmentPoint = (from N8Block b in LevelBlocks.Blocks
                                       where b.name == "Attach Point"
                                       select b).First();

            foreach (N8Tronic t in NPDTronics)
            {
                t.Detach();
                t.position.X = 0;
                t.position.Y = 0;
                t.position.Z = -250;
                AttachmentPoint.AttachToMe(t);
            }

            string ret = Level.GenerateSaveFile();
            Console.Read();

            if (!File.Exists(SavePath))
            {
                using (File.Create(SavePath)) { }
            }

            using (StreamWriter sw = new StreamWriter(File.Open(SavePath, FileMode.Truncate, FileAccess.Write, FileShare.None)))
            {
                sw.WriteLine(ret);
            }

            Console.WriteLine(ret);
        }
        public static void GenerateProxyMatrix()
        {
            string TronicsPath = @"C:\Program Files (x86)\N8\Saves\maxprotect_tronics_base.ncd";
            string SavePath = @"C:\Program Files (x86)\N8\Saves\maxprotect_tronics_base_proxies.ncd";

            N8Level tronics = new N8Level(TronicsPath);
            N8Level proxies = new N8Level();

            N8BlockFactory LevelBlocks = proxies.blocks;
            List<N8Tronic> ProxyBlocks = new List<N8Tronic>();

            int stepsize = 36;

            for (int i = -90 + stepsize; i <= 90 - stepsize; i += stepsize)
            {
                for (int j = -90 + stepsize; j <= 90 - stepsize; j += stepsize)
                {
                    N8Tronic LowerProxy = LevelBlocks.GenerateTronic("rproximity", "Detector Mesh");
                    LowerProxy.position.X = i;
                    LowerProxy.position.Y = j;
                    LowerProxy.position.Z = 45;

                    N8Tronic UpperProxy = LevelBlocks.GenerateTronic("rproximity", "Detector Mesh");
                    UpperProxy.position.X = i * 2.5;
                    UpperProxy.position.Y = j * 2.5;
                    UpperProxy.position.Z = 335;
                    UpperProxy.rotation = new Quaternion(new Vector3D(1, 0, 0), 90);

                    ProxyBlocks.Add(LowerProxy);
                    ProxyBlocks.Add(UpperProxy);
                }
            }

            proxies.MergeWithDestructive(tronics);

            N8Tronic MoverGateway = (from N8Tronic b in proxies.blocks.Tronics where b.type == "cifgreat" select b).First();

            N8Block ControlPoint = LevelBlocks.GenerateBlock("snowmancoal", "Control Point");
            ControlPoint.position.X = -100;

            foreach (N8Tronic prox in ProxyBlocks)
            {
                ControlPoint.AttachToMe(prox);
                //MoverGateway.WireTo(prox, Tronics.NodeType.FlowIn, Tronics.NodeType.FlowOutA);
            }

            Console.Read();
            if (!File.Exists(SavePath))
            {
                using (File.Create(SavePath)) { }
            }

            using (StreamWriter sw = new StreamWriter(File.Open(SavePath, FileMode.Truncate, FileAccess.Write, FileShare.None)))
            {
                sw.WriteLine(proxies.GenerateSaveFile());
            }
        }
        private void Materialize_Click(object sender, EventArgs e)
        {
            Materialize.Enabled = false;
            //Flip the heightmap's Y coordinates, because N8 uses y+ to mean up-screen and
            //everything else we've been doing so far uses Y+ to mean down-screen
            Terrain.Map.Map.ForEach((x) => x.Reverse());

            //Assume that the cell will be tiled with CellSquareSize x CellSquareSize blocks
            int MaxCellSquareSize = (int)LandsPerCell.Value;

            //And then the number of cells in the X and Y direction...
            int NumCellsX = Terrain.Map.sizeX / MaxCellSquareSize;
            int NumCellsY = Terrain.Map.sizeY / MaxCellSquareSize;

            //And then reduce this map to whatever size we really want
            //Generate every map from 0 to what we have, so we can pick them out later.
            //That didn't work out :( I need to generate like a polygon surface or something and then tile it, not interpolate the points...

            List<Tuple<Heightmap, int>> maps = new List<Tuple<Heightmap, int>>();
            Heightmap current = Terrain.Map;

            maps.Add(Tuple.Create(current, MaxCellSquareSize));

            /*
            for (int i = MaxCellSquareSize; i >= 5; i--)
            {
                int CellSquareSize = i;
                //Reduce the map so each cell is CellSquareSize x CellSquareSize
                int newXSize = NumCellsX * CellSquareSize;
                int newYSize = NumCellsY * CellSquareSize;

                Heightmap fullmap = current.reduceTo(newXSize, newYSize);
                List<List<Heightmap>> map = Terrain.ChopHeightmap(CellSquareSize, fullmap);
                fullmap.ToHeatmap(CellSquareSize, null).Save(Utilities.GetDefaultSaveFolder() + @"terrain\heatmap," + CellSquareSize + ".png", System.Drawing.Imaging.ImageFormat.Png);
                maps.Add(Tuple.Create(fullmap, CellSquareSize));
                current = fullmap;
            }
            */

            Random rand = new Random();

                    //Pick which map to use; ideally, cells that need more detail will use a more detailed map.
                    //Right now I'm just gonna generate them all and see how they vary by hand
            for (int CellSize = 0; CellSize < maps.Count; CellSize++)
            {

                Heightmap fullmap = maps[CellSize].Item1;
                int CellSquareSize = maps[CellSize].Item2;
                List<List<Heightmap>> CurrMap = Terrain.ChopHeightmap(CellSquareSize, fullmap);
                for (int k = 0; k < NumCellsX; k++)
                {
                    for (int l = 0; l < NumCellsY; l++)
                    {
                        #region land placing setup
                        //Given that square size, figure out how to (flat) tile a cell.
                        //Each block will be cell size / square size in (effective) width and breadth.
                        int BlockSize = 4000 / CellSquareSize;

                        //If we have an even square size, we'll want to offset the block's position.
                        int Offset = 0;
                        if (CellSquareSize % 2 == 0)
                        {
                            Offset = BlockSize / 2;
                        }

                        //And we'll also need to calculate the offset to the edges of blocks, for rotation generation
                        //But I'm not sure how to do that nicely, so I'll skip it for now.
                        int BlockEdgeOffset = 0;
                        if (CellSquareSize % 2 == 1)
                        {
                            BlockEdgeOffset = BlockSize / 2;
                        }
                        //Just set it to 0 in order to skip
                        BlockEdgeOffset = 0;

                        //And calculate the iterator offset, so we have everything in the middle
                        int IterOffset = (CellSquareSize - 1) / 2;

                        N8Level Level = new N8Level();
                        List<N8Block> lands = new List<N8Block>(CellSquareSize * CellSquareSize);
                        Vector3D LowestPoint = new Vector3D(0, 0, int.MaxValue);
                        string LandType = "land";
                        if (CellSquareSize < 10)
                        {
                            LandType = "landmega";
                        }
                        #endregion

                        #region placing lands
                        for (int i = 0; i < CellSquareSize; i++)
                        {
                            for (int j = 0; j < CellSquareSize; j++)
                            {

                                N8Block land = Level.blocks.GenerateBlock(LandType, "Terrain");
                                land.position.X = ((i - IterOffset) * BlockSize) - Offset;
                                land.position.Y = ((j - IterOffset) * BlockSize) - Offset;
                                land.position.Z = CurrMap[k][l][i, j];

                                //Find the lowest point in the cell, but restrict it to near the center
                                if (Math.Abs(land.position.X) < 1000 && Math.Abs(land.position.Y) < 1000)
                                {
                                    if (land.position.Z < LowestPoint.Z)
                                    {
                                        LowestPoint = land.position;
                                    }
                                }
                                //Calculate the rotation - what we do is figure out what the
                                //angle is between the south point and the  north point,
                                //then multiply that by the angle between the east point and west point.

                                //North/South is Y +- 1, East/West is X +- 1
                                //We pull height information directly from the original heightmap because on the edges we'll need to get data from a different square.
                                Vector3D North = new Vector3D(land.position.X, (((j + 1) - IterOffset) * BlockSize + BlockEdgeOffset), fullmap[k * CellSquareSize + i, l * CellSquareSize + (j + 1)]);
                                Vector3D South = new Vector3D(land.position.X, (((j - 1) - IterOffset) * BlockSize + BlockEdgeOffset), fullmap[k * CellSquareSize + i, l * CellSquareSize + (j - 1)]);
                                Vector3D East = new Vector3D((((i + 1) - IterOffset) * BlockSize + BlockEdgeOffset), land.position.Y, fullmap[k * CellSquareSize + (i + 1), l * CellSquareSize + j]);
                                Vector3D West = new Vector3D((((i - 1) - IterOffset) * BlockSize + BlockEdgeOffset), land.position.Y, fullmap[k * CellSquareSize + (i - 1), l * CellSquareSize + j]); ;
                                Vector3D LandPos = land.position;

                                Quaternion NSRot = new Quaternion(0, 0, 0, 1);
                                Quaternion EWRot = new Quaternion(0, 0, 0, 1);

                                //If I'm between two things that are both higher or both lower than me, I shouldn't have a tilt in that direction.
                                //E.G: if I'm like this : _-_ or -_-, I shouldn't have a tilt.
                                if ((North.Z < LandPos.Z && LandPos.Z < South.Z) || (North.Z > LandPos.Z && LandPos.Z > South.Z))
                                {
                                    if ((North - South).Length > 400)
                                    {
                                        //                       land.type = "landmega";
                                    }
                                    Vector3D higher = North;
                                    Vector3D lower = South;
                                    int dirMul = -1;
                                    if (South.Z > North.Z)
                                    {
                                        higher = South;
                                        lower = North;
                                        dirMul = 1;
                                    }
                                    Vector3D A = lower - new Vector3D(higher.X, higher.Y, lower.Z);
                                    Vector3D B = lower - new Vector3D(higher.X, higher.Y, higher.Z);
                                    A.Normalize();
                                    B.Normalize();

                                    NSRot = new Quaternion(new Vector3D(1, 0, 0), dirMul * Math.Acos(A.Z * B.Z + A.Y * B.Y) * Utilities.RadToDeg);

                                }

                                if ((East.Z < LandPos.Z && LandPos.Z < West.Z) || (West.Z < LandPos.Z && LandPos.Z < East.Z))
                                {
                                    if ((East - West).Length > 400)
                                    {
                                        //                     land.type = "landmega";
                                    }
                                    Vector3D higher = West;
                                    Vector3D lower = East;
                                    int dirMul = -1;
                                    if (East.Z > West.Z)
                                    {
                                        higher = East;
                                        lower = West;
                                        dirMul = 1;
                                    }
                                    Vector3D A = lower - new Vector3D(higher.X, higher.Y, lower.Z);
                                    Vector3D B = lower - new Vector3D(higher.X, higher.Y, higher.Z);
                                    A.Normalize();
                                    B.Normalize();

                                    EWRot = new Quaternion(new Vector3D(0, 1, 0), dirMul * Math.Acos(A.Z * B.Z + A.X * B.X) * Utilities.RadToDeg);
                                }

                                land.rotation = EWRot * NSRot;

                                lands.Add(land);
                            }
                        }
            #endregion

                        //Utilities.Save(Utilities.GetDefaultSaveFolder() + @"terrain\plain\" + (k - BitmapOffset.X) + "," + (l - BitmapOffset.Y) + ".ncd", Level);
                        Utilities.Save(Utilities.GetDefaultSaveFolder() + @"terrain\testing\" + (k - BitmapOffset.X) + "," + (l - BitmapOffset.Y) + ".ncd", Level);
                        #region Extra nice things
                        /*
                        //Now add some nice things like a stack and random trees

                        for (int b = -1000; b < LowestPoint.Z; b += 70)
                        {
                            N8Block stack = Level.blocks.GenerateBlock("floor", "Stack");
                            stack.position = LowestPoint;
                            stack.position.Z = b;
                        }

                        Utilities.Save(Utilities.GetDefaultSaveFolder() + @"terrain\stack\" + (k - BitmapOffset.X) + "," + (l - BitmapOffset.Y) + ".ncd", Level);

                        for (int b = 0; b < 75; b++)
                        {
                            //Pick a random piece of land
                            N8Block currentLand = lands[rand.Next(lands.Count)];

                            //Put the tree's position in the middle of it
                            Vector3D TreePos = new Vector3D();
                            TreePos = rand.NextVector(new Vector3D(-200, -200, 60), new Vector3D(200, 200, 70));

                            //Give it a random spin
                            Quaternion rot = new Quaternion(new Vector3D(0, 0, 1), rand.Next(360));

                            //And finally materialize the tree
                            N8Block tree = Level.blocks.GenerateBlock(Forest.TreeTypes[rand.Next() % Forest.TreeTypes.Length], Forest.TreeNames[rand.Next() % Forest.TreeNames.Length]);
                            tree.position = TreePos;
                            tree.rotation = rot;

                            currentLand.AttachToMe(tree);
                        }

                        Utilities.Save(Utilities.GetDefaultSaveFolder() + @"terrain\tree\" + (k - BitmapOffset.X) + "," + (l - BitmapOffset.Y) + ".ncd", Level);

                        // */
                        #endregion

                    }
                }
            }
            //Unflip it, so we're back where we started
            Terrain.Map.Map.ForEach((x) => x.Reverse());
            Terrain.Map.ToHeatmap(MaxCellSquareSize, null).Save(Utilities.GetDefaultSaveFolder() + @"terrain\heatmap.png", System.Drawing.Imaging.ImageFormat.Png);
            try
            {
                Terrain.Map.ToBitmap().Save(Utilities.GetDefaultSaveFolder() + @"terrain\heightmap.png", System.Drawing.Imaging.ImageFormat.Png);
            }
            catch (Exception)
            {
                MessageBox.Show("Warning, couldn't save heightmap");
            }
            MessageBox.Show("Done!");
            Materialize.Enabled = true;
        }