public static void RingRandomizer(ref LevelFile level)
        {
            int ringsPerLevel = 10;
            int index         = 0;
            int lw            = level.header.width;
            int lh            = level.header.height;

            Bounds bounds = GetCameraBounds(level);

            TumorRemover(ref level);

            // place new rings(s)
            for (int i = 0; i < ringsPerLevel; i++)
            {
                bool placed = false;
                do
                {
                    int row = RNG.random.Next(bounds.Top, bounds.Bottom);
                    int col = RNG.random.Next(bounds.Left, bounds.Right);

                    index = row * lw + col;
                    if (level.data.active[index] == TileID.Empty)
                    {
                        level.data.active[index] = TileID.Ring;
                        placed = true;
                    }
                } while (!placed);
            }
        }
        public static void PlaceTile(ref LevelFile level, TileID toPlace, int numPerLevel, TileID toReplace = TileID.Empty, bool ignoreTags = false, int cushion = 3)
        {
            //int numPerLevel = 10;
            int index = 0;
            int lw    = level.header.width;
            int lh    = level.header.height;

            Bounds bounds = GetCameraBounds(level);

            //TumorRemover(ref level);

            // place new tumor(s)
            for (int i = 0; i < numPerLevel; i++)
            {
                bool placed = false;
                do
                {
                    int row = RNG.random.Next(bounds.Top, bounds.Bottom);
                    int col = RNG.random.Next(bounds.Left + cushion, bounds.Right - cushion);

                    index = row * lw + col;
                    if (level.data.active[index] == toReplace)
                    {
                        if (level.data.tag[index] == TileID.Empty || ignoreTags)
                        {
                            level.data.active[index] = toPlace;
                        }
                        placed = true;
                    }
                } while (!placed);
            }
        }
Beispiel #3
0
        public static void PrintLevelToConsole(LevelFile level)
        {
            var lh = level.header.height;
            var lw = level.header.width;

            Console.WriteLine("============================================");
            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    int index = i * lw + j;
                    if (level.data.active[index] == TileID.Solid)
                    {
                        Console.Write("▓▓");
                    }
                    else if (level.data.back1[index] == TileID.WholePiece)
                    {
                        Console.Write("▒▒");
                    }
                    else if (level.data.back2[index] == TileID.WholePiece2)
                    {
                        Console.Write("░░");
                    }
                    else
                    {
                        Console.Write("  ");
                    }
                }
                Console.Write("\n");
            }
            Console.WriteLine("============================================");
        }
        public static bool AppendPieceH(LevelPiece left, LevelPiece right, bool noExcept = false)
        {
            Pair L1ExitCoord  = GetExitCoord(ref left.File);   // Get entry and exit coords
            Pair L2EntryCoord = GetEntryCoord(ref right.File);

            // check for mismatch in level exit/entrance
            //if (left.CeilingEx != right.CeilingEn) FixCeiling(ref left, ref right);
            //if (left.FloorEx   != right.FloorEn)   FixFloor(ref left, ref right);

            // establish level boundaries and new level size
            Pair L1Origin = new Pair(), L2Origin = new Pair();                      // initialize

            GetLevelOrigins(L1ExitCoord, L2EntryCoord, ref L1Origin, ref L2Origin); // set the values in this function

            // calculate dimensions for new level
            int width  = L1ExitCoord.Second + right.File.header.width - L2EntryCoord.Second + 1;
            int height = Math.Max(L1Origin.First + left.File.header.height, L2Origin.First + right.File.header.height);

            // check for and correct ceiling and floor compatibility
            LevelPiece transition = new LevelPiece(new LevelFile(0, 0)); Pair TOrigin = new Pair();

            if (left.CeilingEx != right.CeilingEn || left.FloorEx != right.FloorEn)
            {
                transition     = CheckCeilingFloor(ref left, ref right);
                TOrigin.First  = L1Origin.First + L1ExitCoord.First - transition.File.header.height + 2;
                TOrigin.Second = L2Origin.Second;
                width++; L2Origin.Second++;
            }

            // noExcept is used to make sure that the entrances and exits
            // can be added without triggering the size limitation
            if (!noExcept && (height > UsableHeight || width > UsableWidth))
            {
                return(false);
            }

            TempLevel = GetNewLevelFile(width, height);             // create new level
            CopyToCoords(ref left.File, ref TempLevel, L1Origin);   // copy left  level into new level
            if (transition.File.header.height != 0)
            {
                CopyToCoords(ref transition.File, ref TempLevel, TOrigin);
            }
            CopyToCoords(ref right.File, ref TempLevel, L2Origin);  // copy right level into new level

            // get rid of old entrances
            int index = (L1Origin.First + L1ExitCoord.First) * TempLevel.header.width + (L1Origin.Second + L1ExitCoord.Second);

            TempLevel.data.tag[index] = TileID.Empty;
            index = (L2Origin.First + L2EntryCoord.First) * TempLevel.header.width + (L2Origin.Second + L2EntryCoord.Second);
            TempLevel.data.tag[index] = TileID.Empty;

            // set canvas piece info
            Canvas.File      = TempLevel;
            Canvas.FloorEn   = left.FloorEn;
            Canvas.FloorEx   = right.FloorEx;
            Canvas.CeilingEn = left.CeilingEn;
            Canvas.CeilingEx = right.CeilingEx;

            return(true);
        }
        public static void CopyToCoords(ref LevelFile copyLevel, ref LevelFile pasteLevel, Pair coords)    // pass in the origin coords of the level to be copied from
        {
            int copyIndex  = 0;
            int pasteIndex = 0;
            int copylw     = copyLevel.header.width;
            int copylh     = copyLevel.header.height;
            int pastelw    = pasteLevel.header.width;
            int pastelh    = pasteLevel.header.height;

            for (int i = 0; i < copylh; i++)
            {
                for (int j = 0; j < copylw; j++)
                {
                    copyIndex  = i * copylw + j;
                    pasteIndex = (i + coords.First) * pastelw + (j + coords.Second);

                    if (pasteLevel.data.tag[pasteIndex] != TileID.OOBMarker)
                    {
                        throw new LevelCollisionException();
                    }

                    pasteLevel.data.active[pasteIndex]  = copyLevel.data.active[copyIndex];
                    pasteLevel.data.back1[pasteIndex]   = copyLevel.data.back1[copyIndex];
                    pasteLevel.data.back2[pasteIndex]   = copyLevel.data.back2[copyIndex];
                    pasteLevel.data.tag[pasteIndex]     = copyLevel.data.tag[copyIndex];
                    pasteLevel.data.overlay[pasteIndex] = copyLevel.data.overlay[copyIndex];
                }
            }
        }
Beispiel #6
0
        // This is the offset of the exit as compared to the entrance
        //public Pair Shift;

        public LevelPiece(LevelFile file)
        {
            File = file;

            Name   = null;
            Folder = null;

            Margin.Left   = 0;
            Margin.Right  = 0;
            Margin.Top    = 0;
            Margin.Bottom = 0;

            CeilingEn = false;
            CeilingEx = false;

            FloorEn = false;
            FloorEx = false;

            FullHeight = false;

            // Determines whether pieces can be placed above or below a certain piece.
            AllowBuildingAbove = true;
            AllowBuildingBelow = true;

            // The direction the entrance and exit tiles are facing.
            // Later on these should be replaced by a list of all entrances and exits to a piece.
            Entrance = Direction.None;
            Exit     = Direction.None;
        }
Beispiel #7
0
        public static LevelFile copyLevel(LevelFile level)
        {
            var levelNew = new LevelFile();

            levelNew = level;

            return(levelNew);
        }
Beispiel #8
0
        public static void FlipLevelH(ref LevelFile level)
        {
            int lw = level.header.width;
            int lh = level.header.height;

            FlipLayerH(ref level.data.back1, lw, lh);
            FlipLayerH(ref level.data.active, lw, lh);
            FlipLayerH(ref level.data.tag, lw, lh);
            FlipLayerH(ref level.data.overlay, lw, lh);
            FlipLayerH(ref level.data.back2, lw, lh);
        }
 public static void OverlayStuff(ref LevelFile level)
 {
     for (int i = 0; i < 60; i++)
     {
         int index = RNG.random.Next(0, level.data.active.Length);
         //if (level.data.tag[index] == 0)
         //{
         level.data.overlay[index] = (TileID)OverlayTiles[RNG.random.Next(0, OverlayTiles.Length)];
         //}
     }
 }
 public static void RandomCrumbles(ref LevelFile level)
 {
     for (int i = 0; i < 100; i++)
     {
         int index = RNG.random.Next(0, level.data.active.Length);
         if (level.data.active[index] == TileID.Solid)
         {
             level.data.active[index] = TileID.Crumble;
         }
     }
 }
 public static void AddTiles(ref LevelFile level, int num)
 {
     for (int i = 0; i < num; i++)
     {
         int index = RNG.random.Next(0, level.data.active.Length);
         if (level.data.active[index] != TileID.Solid && level.data.tag[index] == TileID.Empty)
         {
             level.data.active[index] = (TileID)ActiveTiles[RNG.random.Next(0, ActiveTiles.Length)];
         }
     }
 }
        public static void WaterLevel(ref LevelFile level)
        {
            int lw = level.header.width;
            int lh = level.header.height;

            Bounds bounds = GetCameraBounds(level);

            for (int j = 0; j < lw; j++)
            {
                level.data.overlay[j] = TileID.WaterUB;
            }
        }
        static int GetCeilingHeight(LevelFile level, Pair coord)
        {
            int ceilingHeight = 0;
            int lw            = level.header.width;

            for (ceilingHeight = 1; ceilingHeight < coord.First; ceilingHeight++)
            {
                int index = (coord.First - ceilingHeight) * lw + coord.Second;
                if (level.data.active[index] == TileID.Solid)
                {
                    break;
                }
            }
            return(ceilingHeight);
        }
Beispiel #14
0
        public static LevelFile Load(string path)
        {
            LevelFile level = new LevelFile()
            {
            };

            byte[] filedata = File.ReadAllBytes(path);

            // load level header
            byte[] tempBytes = new byte[4];

            Buffer.BlockCopy(filedata, 0, tempBytes, 0, 4);
            Array.Reverse(tempBytes);
            level.header.version = BitConverter.ToInt32(tempBytes, 0);
            Buffer.BlockCopy(filedata, 4, tempBytes, 0, 4);
            Array.Reverse(tempBytes);
            level.header.width = BitConverter.ToInt32(tempBytes, 0);
            Buffer.BlockCopy(filedata, 8, tempBytes, 0, 4);
            Array.Reverse(tempBytes);
            level.header.height = BitConverter.ToInt32(tempBytes, 0);
            Buffer.BlockCopy(filedata, 12, tempBytes, 0, 4);
            Array.Reverse(tempBytes);
            level.header.layers = BitConverter.ToInt32(tempBytes, 0);

            // get data layer length
            int layerLength = level.header.width * level.header.height;

            //Console.WriteLine("file length  (bytes): " + filedata.Length);
            //Console.WriteLine("layer length (bytes): " + layerLength);

            // initialize data layers
            level.data.back1   = new TileID[layerLength];
            level.data.active  = new TileID[layerLength];
            level.data.tag     = new TileID[layerLength];
            level.data.overlay = new TileID[layerLength];
            level.data.back2   = new TileID[layerLength];

            // load data layers
            int offset = 16;

            LoadLayer(ref filedata, ref level.data.back1, ref offset);
            LoadLayer(ref filedata, ref level.data.active, ref offset);
            LoadLayer(ref filedata, ref level.data.tag, ref offset);
            LoadLayer(ref filedata, ref level.data.overlay, ref offset);
            LoadLayer(ref filedata, ref level.data.back2, ref offset);

            return(level);
        }
        public static bool SmartCorruptActive(ref LevelFile level)
        {
            int lw = level.header.width;
            int lh = level.header.height;

            var  options      = new string[] { };
            bool hasGas       = false;
            int  corruptLevel = 3;

            // loop over entire level
            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    int index = i * lw + j;

                    if (SmartTiles.TryGetValue((int)level.data.active[index], out int[] alts) && alts.Length > 0)
        public static void SpikeStrips(ref LevelFile level)
        {
            int lw = level.header.width;
            int lh = level.header.height;

            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    int index = i * lw + j;
                    if (level.data.active[index] == TileID.Solid && j % 5 == i % 5)
                    {
                        level.data.active[index] = TileID.SpikeU;
                    }
                }
            }
        }
        public static LevelFile GetNewLevelFile(int width = 54, int height = 32)
        {
            LevelFile level = new LevelFile(width, height);

            int index = 0;
            int lw    = level.header.width;
            int lh    = level.header.height;

            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    index = i * lw + j;
                    level.data.tag[index] = TileID.OOBMarker;
                }
            }

            return(level);
        }
        public static bool AddEnemies(ref LevelFile level, int num)
        {
            bool hasGas = false;

            for (int i = 0; i < num; i++)
            {
                int index = RNG.random.Next(0, level.data.active.Length);
                if (level.data.active[index] == 0 && level.data.tag[index] == 0)
                {
                    var tile = (TileID)EntityTiles[RNG.random.Next(0, EntityTiles.Length)];
                    level.data.active[index] = tile;
                    if (tile == TileID.Gasper || tile == TileID.GasCloud)
                    {
                        hasGas = true;
                    }
                }
            }
            return(hasGas);
        }
        public static void TumorRemover(ref LevelFile level)
        {
            int index = 0;
            int lw    = level.header.width;
            int lh    = level.header.height;

            // delete original tumor
            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    index = i * lw + j;
                    if (level.data.active[index] == TileID.Tumor)
                    {
                        level.data.active[index] = TileID.Empty;
                    }
                }
            }
        }
        public static void Crushers(ref LevelFile level)
        {
            for (int i = 0; i < 30; i++)
            {
                int index = RNG.random.Next(0, level.data.active.Length);
                if (level.data.active[index] == TileID.Solid && level.data.tag[index] == TileID.Empty)
                {
                    if (RNG.CoinFlip())
                    {
                        level.data.active[index] = TileID.CrusherEye;
                    }
                    else
                    {
                        level.data.active[index] = TileID.CrusherGear;
                    }

                    level.data.tag[index] = TileID.Crusher;
                }
            }
        }
        public static Pair GetEntryCoord(ref LevelFile level)
        {
            // Find new piece's entrance coordinates
            int index = 0;
            int lw    = level.header.width;
            int lh    = level.header.height;

            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    index = i * lw + j;
                    if (level.data.tag[index] == TileID.GreenTransitionL)
                    {
                        return(new Pair(i, j));
                    }
                }
            }
            return(new Pair());
        }
        public static Pair GetExitCoord(ref LevelFile level)
        {
            // Find Canvas exit coordinates
            int index = 0;
            int lw    = level.header.width;
            int lh    = level.header.height;

            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    index = i * lw + j;
                    if (level.data.tag[index] == TileID.YellowTransitionR)
                    {
                        return(new Pair(i, j));
                    }
                }
            }
            return(new Pair());
        }
Beispiel #23
0
        public static void Save(LevelFile level, string path)
        {
            // get data layer length
            int layerLength = level.header.width * level.header.height;
            int fileLength  = layerLength * 5 * 4 + 16;

            //Console.WriteLine("file length  (bytes): " + fileLength);
            //Console.WriteLine("layer length (bytes): " + layerLength);

            byte[] filedata  = new byte[fileLength];
            byte[] tempBytes = new byte[4];
            tempBytes = BitConverter.GetBytes(level.header.version);
            Array.Reverse(tempBytes);
            Buffer.BlockCopy(tempBytes, 0, filedata, 0, 4);
            tempBytes = BitConverter.GetBytes(level.header.width);
            Array.Reverse(tempBytes);
            Buffer.BlockCopy(tempBytes, 0, filedata, 4, 4);
            tempBytes = BitConverter.GetBytes(level.header.height);
            Array.Reverse(tempBytes);
            Buffer.BlockCopy(tempBytes, 0, filedata, 8, 4);
            tempBytes = BitConverter.GetBytes(level.header.layers);
            Array.Reverse(tempBytes);
            Buffer.BlockCopy(tempBytes, 0, filedata, 12, 4);

            // save data layers
            int offset = 16;

            SaveLayer(ref filedata, ref level.data.back1, ref offset);
            SaveLayer(ref filedata, ref level.data.active, ref offset);
            SaveLayer(ref filedata, ref level.data.tag, ref offset);
            SaveLayer(ref filedata, ref level.data.overlay, ref offset);
            SaveLayer(ref filedata, ref level.data.back2, ref offset);

            string folder = Path.GetDirectoryName(path);

            if (!File.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }
            File.WriteAllBytes(path, filedata);     // output copied file
        }
Beispiel #24
0
        public static LevelFile FixAspect(ref LevelFile levelIn)
        {
            int lh = levelIn.header.height;
            int lw = levelIn.header.width;

            if (lh * 16 > lw * 9)
            {
                lw = lh * 16 / 9;
            }
            else
            {
                lh = lw * 9 / 16;
            }

            LevelFile levelOut = LevelGenerator.GetNewLevelFile(lw, lh);
            int       hOffset  = lh - levelIn.header.width;

            LevelGenerator.CopyToCoords(ref levelIn, ref levelOut, new Pair(0, hOffset));

            return(levelOut);
        }
Beispiel #25
0
        public static LevelFile RotateLevel(ref LevelFile level)
        {
            int lw = level.header.width;
            int lh = level.header.height;

            LevelFile levelNew = new LevelFile(lh, lw);

            for (int row = 0; row < lh; row++)
            {
                for (int col = 0; col < lw; col++)
                {
                    int copyIndex  = row * lw + col;
                    int pasteIndex = col * lh + (lh - row - 1);

                    levelNew.data.active[pasteIndex]  = GetRotation(level.data.active[copyIndex]);
                    levelNew.data.back1[pasteIndex]   = GetRotation(level.data.back1[copyIndex]);
                    levelNew.data.back2[pasteIndex]   = GetRotation(level.data.back2[copyIndex]);
                    levelNew.data.tag[pasteIndex]     = GetRotation(level.data.tag[copyIndex]);
                    levelNew.data.overlay[pasteIndex] = GetRotation(level.data.overlay[copyIndex]);
                }
            }

            return(levelNew);
        }
        public static Bounds GetCameraBounds(LevelFile level)
        {
            int lw     = level.header.width;
            int lh     = level.header.height;
            var bounds = new Bounds {
                Left = lw, Top = lh, Bottom = 0, Right = 0
            };
            bool CameraFound = false;

            int index = 0;

            for (int row = 0; row < lh; row++)
            {
                for (int col = 0; col < lw; col++)
                {
                    index = row * lw + col;
                    if (level.data.tag[index] == TileID.CameraBounds)
                    {
                        bounds.Top    = Math.Min(row, bounds.Top);
                        bounds.Bottom = Math.Max(row, bounds.Bottom);
                        bounds.Left   = Math.Min(col, bounds.Left);
                        bounds.Right  = Math.Max(col, bounds.Right);
                        CameraFound   = true;
                    }
                }
            }

            // correct for aspect ratio
            double correctAspect = 16 / 9;
            double width         = bounds.Right - bounds.Left;
            double height        = bounds.Bottom - bounds.Top;
            double aspect        = width / height;
            double hCenter       = bounds.Left + (width / 2);
            double vCenter       = bounds.Top + (height / 2);

            if (aspect < correctAspect) // aspect ratio too tall
            {
                width        = height * 16 / 9;
                bounds.Right = (int)(hCenter - width / 2);
                bounds.Left  = (int)(hCenter + width / 2);
            }
            if (aspect > correctAspect) // aspect ratio too wide
            {
                height        = width * 9 / 16;
                bounds.Top    = (int)(vCenter - height / 2);
                bounds.Bottom = (int)(vCenter + height / 2);
            }

            if (bounds.Top < 0)
            {
                bounds.Top = 0;
            }
            if (bounds.Left < 0)
            {
                bounds.Left = 0;
            }
            if (bounds.Right > lw)
            {
                bounds.Top = lw;
            }
            if (bounds.Bottom > lh)
            {
                bounds.Bottom = lh;
            }

            if (bounds.Top > bounds.Bottom || bounds.Left > bounds.Right || !CameraFound)
            {
                Console.WriteLine("Broken Bounds");
                bounds.Top    = 0;
                bounds.Left   = 0;
                bounds.Bottom = lh;
                bounds.Right  = lw;
            }

            return(bounds);
        }
        public static string CorruptLevel(ref LevelFile level)
        {
            string TSAppend = "";

            // smart corruptions done first
            if (Randomizer.settings.CRSmart)
            {
                if (SmartCorruptActive(ref level))
                {
                    TSAppend += "\n#added by level corruptor\nfx_shader_mid cloudripples\nmidfx_graphics None\nmidfx_layer 2\n";
                }
            }
            if (Randomizer.settings.CROverlays)
            {
                SmartCorruptOverlay(ref level);
            }

            // tumor remover second
            if (Randomizer.settings.CRTumors)
            {
                if (Randomizer.settings.AreaType == "normal")
                {
                    TumorRandomizer(ref level);
                }
                else if (Randomizer.settings.AreaType == "cart")
                {
                    RingRandomizer(ref level);
                }
                else if (Randomizer.settings.AreaType == "dark")
                {
                    TumorRemover(ref level);
                }
                else if (Randomizer.settings.AreaType == "glitch")
                {
                    TumorRemover(ref level);
                }
                else if (Randomizer.settings.AreaType == "ironcart")
                {
                    TumorRemover(ref level);
                }
            }

            // add enemies and add tiles is next
            AddTiles(ref level, Randomizer.settings.CRAddTiles);
            if (AddEnemies(ref level, Randomizer.settings.CRAddEnemies))
            {
                TSAppend += "\nfx_shader_mid cloudripples\nmidfx_graphics None\nmidfx_layer 2\n";
            }
            //PlaceTile(ref level, TileID.Feral, 5);

            // last priority is the various ones below
            //if (Randomizer.settings.CRChaos) TotalChaos(ref level);
            if (Randomizer.settings.CRCrumbles)
            {
                RandomCrumbles(ref level);
            }
            if (Randomizer.settings.CRSpikeStrips)
            {
                SpikeStrips(ref level);
            }
            if (Randomizer.settings.CRCrushers)
            {
                Crushers(ref level);
            }
            if (Randomizer.settings.CRWaterLevels && RNG.CoinFlip())
            {
                WaterLevel(ref level);
            }

            return(TSAppend);
        }
Beispiel #28
0
        public static void RandomizeMod(MainWindow mw)
        {
            //ShadersList = mw.ShadersList;

            saveDir = settings.GameDirectory;

            PrepModFolders();

            // level corruptions
            string dir = $"{saveDir}tilemaps";

            if (Directory.Exists(dir))
            {
                string[] paths = Directory.GetFiles(dir);
                foreach (var file in paths)
                {
                    LevelFile level = LevelManip.Load(file);
                    //if(settings.MirrorMode)
                    //{
                    //    LevelManip.FlipLevelH(ref level);
                    //    FlipCSV(saveDir + "data/map.csv");
                    //}
                    if (settings.DoCorruptions)
                    {
                        LevelCorruptors.CorruptLevel(ref level);
                    }
                    LevelManip.Save(level, file);
                }
            }

            // data folder
            dir = $"{saveDir}data";
            if (Directory.Exists(dir))
            {
                // tilesets.txt
                var file = $"{dir}/tilesets.txt";
                if (File.Exists(file))
                {
                    string[] text = File.ReadAllLines(file);
                    for (int i = 0; i < text.Length; i++)
                    {
                        if (text[i].Contains("palette"))
                        {
                            text[i] = TilesetManip.GetPalette();
                        }
                        if (text[i].Contains("tile_graphics"))
                        {
                            text[i] = TilesetManip.GetTile();
                        }
                        if (text[i].Contains("overlay_graphics"))
                        {
                            text[i] = TilesetManip.GetOverlay();
                        }
                        if (text[i].Contains("global_particle"))
                        {
                            var split = text[i].Trim().Split(Convert.ToChar(" "));
                            text[i] = split[0] + " " + ParticleGenerator.GetParticle(settings);
                        }
                    }
                    File.Delete(file);
                    File.WriteAllLines(file, text);
                }
            }
        }
        public static void DecorateMachine(ref LevelFile level)
        {
            int lw = level.header.width;
            int lh = level.header.height;

            //TileID[] brokenWindows  = { TileID.DiagonalBL, TileID.DiagonalBR, TileID.SmallSideB, TileID.LargeSideB };
            //TileID[] brokenWindows2 = {  };

            // loop over entire level
            for (int i = 0; i < lh; i++)
            {
                for (int j = 0; j < lw; j++)
                {
                    int index  = i * lw + j;
                    int choice = 0;

                    // BACKGROUND 1 TILES
                    {
                        TileID tile = level.data.back1[index];
                        // decorate windows
                        if (tile == TileID.Back1Deco2 && (level.data.back1[index - lw] == TileID.WholePiece || level.data.back1[index - lw] == TileID.Back1Deco2)) // this if statement could cause a vector error if you place a window on the first row. So just don't do that!
                        {
                            choice = RNG.random.Next(0, 10);                                                                                                       // make a random number selection to decide how to modify the window
                            switch (choice)
                            {
                            case 0:
                                level.data.back1[index] = TileID.Back1Deco4;
                                break;

                            case 1:
                                level.data.back1[index] = (TileID)RNG.random.Next(50001, 50021);
                                break;

                            case 2:
                            case 3:
                                level.data.back2[index] = (TileID)RNG.random.Next(50034, 50054);
                                break;
                            }
                        }
                        // decorate remaining solid tiles
                        if (tile == TileID.WholePiece)
                        {
                            choice = RNG.random.Next(0, 20);
                            if (choice == 0)
                            {
                                level.data.back1[index] = TileID.Back1Deco1;
                            }
                            else if (choice < 4)
                            {
                                level.data.back1[index] = TileID.Back1Deco3;
                            }
                        }
                        // decorate side pieces
                        if (tile == TileID.LargeSideL)
                        {
                            choice = RNG.random.Next(0, 6); // make a random number selection to decide how to modify the window
                            switch (choice)
                            {
                            case 0:
                                break;

                            case 1:
                            case 2:
                                level.data.back1[index] = TileID.SmallSideL;
                                break;

                            case 3:
                                level.data.back1[index - 1] = (TileID)RNG.random.Next(50013, 50017);
                                goto default;

                            default:
                                level.data.back1[index] = TileID.Empty;
                                break;
                            }
                        }
                        if (tile == TileID.LargeSideR)
                        {
                            choice = RNG.random.Next(0, 6); // make a random number selection to decide how to modify the window
                            switch (choice)
                            {
                            case 0:
                                break;

                            case 1:
                            case 2:
                                level.data.back1[index] = TileID.SmallSideR;
                                break;

                            /*case 3:
                             *  level.data.back1[index + 1] = (TileID)RNG.random.Next(50013, 50017);
                             *  goto default;*/
                            default:
                                level.data.back1[index] = TileID.Empty;
                                break;
                            }
                        }
                        if (tile == TileID.LargeSideB)
                        {
                            choice = RNG.random.Next(0, 6); // make a random number selection to decide how to modify the window
                            switch (choice)
                            {
                            case 0:
                                break;

                            case 1:
                            case 2:
                                level.data.back1[index] = TileID.SmallSideB;
                                break;

                            /*case 3:
                             *  level.data.back1[index + lw] = (TileID)RNG.random.Next(50013, 50017);
                             *  goto default;*/
                            default:
                                level.data.back1[index] = TileID.Empty;
                                break;
                            }
                        }
                        if (tile == TileID.LargeSideT)
                        {
                            choice = RNG.random.Next(0, 6); // make a random number selection to decide how to modify the window
                            switch (choice)
                            {
                            case 0:
                                break;

                            case 1:
                            case 2:
                                level.data.back1[index] = TileID.SmallSideT;
                                break;

                            case 3:
                                level.data.back1[index] = TileID.Back1Deco4;
                                break;

                            /*case 4:
                             *  level.data.back1[index - lw] = (TileID)RNG.random.Next(50013, 50017);
                             *  goto default;*/
                            default:
                                level.data.back1[index] = TileID.Empty;
                                break;
                            }
                        }
                    }

                    // BACKGROUND 2 TILES
                    {
                        TileID tile = level.data.back2[index];
                        // decorate windows
                        if (tile == TileID.Back2Deco2 && (level.data.back2[index - lw] == TileID.WholePiece2 || level.data.back2[index - lw] == TileID.Back2Deco2)) // this if statement could cause a vector error if you place a window on the first row. So just don't do that!
                        {
                            choice = RNG.random.Next(0, 10);
                            switch (choice)
                            {
                            case 0:
                                level.data.back2[index] = TileID.Back2Deco4;
                                break;

                            case 1:
                                level.data.back2[index] = (TileID)RNG.random.Next(50034, 50054);
                                break;
                            }
                        }
                        // decorate remaining solid tiles
                        if (tile == TileID.WholePiece2)
                        {
                            choice = RNG.random.Next(0, 20);
                            if (choice == 0)
                            {
                                level.data.back2[index] = TileID.Back2Deco1;
                            }
                            else if (choice < 4)
                            {
                                level.data.back2[index] = TileID.Back2Deco3;
                            }
                        }
                    }
                }
            }
        }
        private void CreatePiecePools(object sender, RoutedEventArgs e)
        {
            foreach (var folder in Directory.GetDirectories("data/levelpieces"))
            {
                string folderName = Path.GetFileNameWithoutExtension(folder); // get folder name
                if (folderName == "GEN")
                {
                    continue;                      // ignore GEN folder
                }
                XDocument doc  = new XDocument();
                XElement  pool = new XElement("pool");
                pool.SetAttributeValue("enabled", "True");
                pool.SetAttributeValue("source", folderName);
                pool.SetAttributeValue("author", "Uzerro");
                pool.SetAttributeValue("order", "0");

                foreach (var file in Directory.GetFiles(folder, "*.lvl", SearchOption.TopDirectoryOnly))
                {
                    string   fileName = Path.GetFileNameWithoutExtension(file); // get file name
                    XElement piece    = new XElement("piece");
                    piece.SetAttributeValue("name", fileName);
                    // create XElement for piece
                    LevelFile level = LevelManip.Load(file);                // load the associated level file

                    Pair enCoord = LevelGenerator.GetEntryCoord(ref level); // get entry coord
                    Pair exCoord = LevelGenerator.GetExitCoord(ref level);  // get exit coord

                    // check for ceilings and floors
                    piece.SetAttributeValue("ceilingEn", "False");
                    piece.SetAttributeValue("ceilingEx", "False");
                    piece.SetAttributeValue("floorEn", "False");
                    piece.SetAttributeValue("floorEx", "False");

                    int index, lw = level.header.width, lh = level.header.height;

                    if (level.data.active[0] == TileID.Solid)               // checks top left tile for solid block
                    {
                        piece.SetAttributeValue("ceilingEn", "True");
                    }
                    if (level.data.active[lw - 1] == TileID.Solid)          // checks top right tile for solid block
                    {
                        piece.SetAttributeValue("ceilingEx", "True");
                    }

                    index = (enCoord.First + 1) * lw + enCoord.Second;
                    if (index < lh * lw)
                    {
                        if (level.data.active[index] == TileID.Solid)       // checks tile underneath the entrance for solid block
                        {
                            piece.SetAttributeValue("floorEn", "True");
                        }
                    }
                    index = (exCoord.First + 1) * lw + exCoord.Second;
                    if (index < lh * lw)
                    {
                        if (level.data.active[index] == TileID.Solid)       // checks tile underneath the exit for solid block
                        {
                            piece.SetAttributeValue("floorEx", "True");
                        }
                    }

                    // FullHeight, AllowBuildingAbove/Below, Margin will need to be set manually.

                    pool.Add(piece);
                }

                doc.Add(pool);
                doc.Save($"data/piecepools/{folderName}.xml");
            }

            MessageBox.Show($"creating piece pools complete", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
        }