public void GuitarSolo()
        {
            // This is the triangle behavior for the guitar solo portion.
            // The idea is to colorize the entire triangle area as motherf'in evil, and then the ones that aren't go through some cool flip effects.
            var bigPoints = new Vector2[16] {
                new Vector2(8, 0),
                new Vector2(9, 0),
                new Vector2(10, 0),
                new Vector2(7, 1),
                new Vector2(11, 1),
                new Vector2(4, 2),
                new Vector2(5, 2),
                new Vector2(6, 2),
                new Vector2(12, 2),
                new Vector2(13, 2),
                new Vector2(14, 2),
                new Vector2(5, 3),
                new Vector2(9, 3),
                new Vector2(13, 3),
                new Vector2(4, 4),
                new Vector2(14, 4)
            };

            // Now let's create a topography color based off that.
            CommandColor hotColor  = new CommandColor(0.8, 0.1, 0.1);
            CommandColor coldColor = new CommandColor(0.1, 0.1, 0.1);

            ColorTopography(StartTime, 0, hotColor, coldColor, 3, bigPoints);

            var beatDuration = 4 * (60000) / Beatmap.GetTimingPointAt((int)StartTime).Bpm;

            // The goal is to have the central non-hotspot flip on and off with backgrounds.
            executeSprite flipOff = delegate(OsbSprite s, float startTime, float duration) { s.StartLoopGroup(startTime, 4);
                                                                                             s.ScaleVec(OsbEasing.OutCubic, 0, duration, new CommandScale(1, 1), new CommandScale(1, 0));
                                                                                             s.ScaleVec(OsbEasing.InCubic, beatDuration - duration, beatDuration, new CommandScale(1, 0), new CommandScale(1, 1));
                                                                                             s.Fade(beatDuration * 2, 1);
                                                                                             s.EndGroup(); };

            executeSprite shockColor = delegate(OsbSprite s, float st, float step) { s.StartLoopGroup(st, 8);
                                                                                     s.Color(OsbEasing.OutQuint, 0, step, new CommandColor(1, 0, 0), s.ColorAt(st));
                                                                                     s.Fade(beatDuration, 1);
                                                                                     s.EndGroup(); };

            // Query for the wall being the hotColor
            querySprite colorWall = delegate(OsbSprite s, float queryTime) { return(s.ColorAt(queryTime) == hotColor); };

            // Wonder if this works. Let's try having a loop with the shockwaves.
            ShockwaveFill(StartTime, 600, 150, bigPoints[12] - new Vector2(0, 1), bigPoints[12], new bool[GridWidth, GridHeight], flipOff, colorWall);

            // Left/right shockwave flashes?
            ShockwaveFill(StartTime, 300, 75 / 2, new Vector2(0, 0), bigPoints[12], new bool[GridWidth, GridHeight], shockColor, colorWall);
            ShockwaveFill(StartTime, 300, 75 / 2, new Vector2(GridWidth - 1, 0), bigPoints[12], new bool[GridWidth, GridHeight], shockColor, colorWall);


            // HARD VALUES BAD
            ScaleXYFlip(120645 - 600, 600, true, false);
        }
        public void ShockwaveColor(float startTime, float stepTime, float delayTime, Vector2 target, CommandColor newColor, CommandColor ignoreColor, bool executeAsLoop)
        {
            // Creates a shockwave color point beginning at startTime.
            // The triangle closest to target becomes the first-point to flash from newColor -> baseColor.
            // Remaining triangles will flash as well after delayTime passes for each subsequent hit.
            // The triangles' order is based on the manhattan distance.
            // Also, we can set an ignoreColor that acts as a hard wall that the shockwave can't penetrate through.
            // We can also execute it as a loop, where it will execute the alt. shockcolor method. (but this is hacky, don't do it other than darenimo3)

            // Initialize the flags array, all as unmarked triangles.
            var flags = new bool [GridWidth, GridHeight];

            // What triangle in the grid would be closest to the target vector's coordinates?
            var closestX = 0;
            var closestY = 0;
            var closestD = float.MaxValue;

            for (int x = 0; x < GridWidth; x++)
            {
                for (int y = 0; y < GridHeight; y++)
                {
                    var curD = Distance(grid[x, y].PositionAt(startTime), target);
                    if (curD < closestD)
                    {
                        closestX = x; // New minimum found, so update the high score.
                        closestY = y;
                        closestD = curD;
                    }
                }
            }

            executeSprite shockColor     = delegate(OsbSprite s, float st, float step) { s.Color(0, st, st + step, newColor, s.ColorAt(st)); };
            executeSprite shockColorLoop = delegate(OsbSprite s, float st, float step) { s.StartLoopGroup(st, 4);
                                                                                         s.Color(OsbEasing.InBack, 0, step, newColor, s.ColorAt(st));
                                                                                         s.Fade(2400, 1);
                                                                                         s.EndGroup(); };
            querySprite shockWall = delegate(OsbSprite s, float st) { return(s.ColorAt(st) == ignoreColor); };

            // After this point, we should have found the best point to begin the shockwave. So let's do it!
            var startPoint = new Vector2(closestX, closestY);

            ShockwaveFill(startTime, stepTime, delayTime, startPoint, startPoint, flags, (executeAsLoop ? shockColorLoop : shockColor), shockWall);
        }
        public void VerseBackground()
        {
            // Goes for any of the verse sections.
            // Expects a triangle grid the size of... 40x10.
            // There are 8 measures of the verse, so every other measure we change colors.
            // Color change is random.
            // In the meantime, maybe a light light glitter.

            // Coolors are COOOOOL.
            var colorList = new CommandColor[5] {
                new CommandColor(39.0 / 255, 40.0 / 255, 56.0 / 255),
                new CommandColor(93.0 / 255, 83.0 / 255, 107.0 / 255),
                new CommandColor(125.0 / 255, 107.0 / 255, 145.0 / 255),
                new CommandColor(165.0 / 255, 148.0 / 255, 249.0 / 255),
                new CommandColor(52.0 / 255, 127.0 / 255, 196.0 / 255)
            };

            var hotSpots = new List <Vector2>();

            // Time to assign the hotspots that will actually be killed off.
            for (int i = GridWidth / 2 - GridWidth / 10; i < (GridWidth / 2 + GridWidth / 10) - 1; i++)
            {
                for (int j = GridHeight / 5; j < (GridHeight / 5) * 3; j++)
                {
                    hotSpots.Add(new Vector2(i - 2, j));
                }
            }

            var colorMarker       = Random(0, 4);
            var topographicPoints = 8;
            var greyMarker        = new Vector3((float)0.4, (float)0.4, (float)0.4);

            // Shockwave to kill off the hotspots.
            executeSprite killMe      = delegate(OsbSprite s, float st, float d) { s.ScaleVec(OsbEasing.OutBounce, st, st + d, s.ScaleAt(st), new CommandScale(0, (float)TriangleSize / baseSize)); };
            querySprite   isntHotSpot = delegate(OsbSprite s, float st) { return(s.ColorAt(st) != colorList[colorMarker]); };

            // OK so let's make the initial topography.
            ColorTopography(StartTime, 0, colorList[colorMarker], new CommandColor(greyMarker), topographicPoints, hotSpots.ToArray());
            Glitter(StartTime + 1, 600, 3);
            ScaleXYFlip(StartTime, 600, false, false);

            // Till death do you part.
            // TODO: Maybe get this to work or something.
            ShockwaveFill(StartTime + 600, 600, 75, hotSpots[0], new Vector2(320, 240), new bool[GridWidth, GridHeight], killMe, isntHotSpot);

            // Assign different colors.
            for (int i = 1; i < 4; i++)
            {
                // Update stuff
                colorMarker = (colorMarker + Random(1, 4)) % 5;
                greyMarker -= new Vector3((float)0.1, (float)0.1, (float)0.1);
                topographicPoints++;
                // And execute!
                ColorTopography(StartTime + (Duration / 4) * i - 600, 600, colorList[colorMarker], new CommandColor(greyMarker), topographicPoints, hotSpots.ToArray());
                Glitter(StartTime + (Duration / 4) * i, 600, 3);
            }


            // Bye bye.
            ScaleXYFlip(StartTime + Duration - 600, 600, true, false);
        }
        public void ShockwaveFill(float startTime, float stepTime, float delayTime, Vector2 slot, Vector2 startPoint, bool [,] flags, executeSprite Go, querySprite Wall)
        {
            // Flood-fill method. This method actually executes the color command to a single triangle on a shockwave.
            // If a triangle has already been filled, it is ignored. Otherwise, execute the Go method, mark the triangle,
            // and move to its orthogonal neighbors.
            // Also, we have the startPoint, so we can keep track of how far we are from the shockwave.
            // This helps us determine how much delaytime is needed for the flash to occur.
            // To make the distinction that target is pure coordinates and slot is based on array indices, the vector2 is named differently.

            // Base case
            if (slot.X >= GridWidth ||
                slot.Y >= GridHeight ||
                slot.X < 0 ||
                slot.Y < 0 ||
                flags[(int)slot.X, (int)slot.Y] ||
                Wall(grid[(int)slot.X, (int)slot.Y], startTime))
            {
                return;
            }

            // We're here, so that means it's time to flash and mark.
            var s = grid[(int)slot.X, (int)slot.Y];

            flags[(int)slot.X, (int)slot.Y] = true;
            var newStartTime = startTime + delayTime * ManhattanDistance(slot, startPoint);

            Go(s, newStartTime, stepTime); // Change me to executing the delegate

            // Use recursion to flash the other neighbors.
            ShockwaveFill(startTime, stepTime, delayTime, Vector2.Add(slot, new Vector2(0, -1)), startPoint, flags, Go, Wall); // N
            ShockwaveFill(startTime, stepTime, delayTime, Vector2.Add(slot, new Vector2(-1, 0)), startPoint, flags, Go, Wall); // W
            ShockwaveFill(startTime, stepTime, delayTime, Vector2.Add(slot, new Vector2(0, 1)), startPoint, flags, Go, Wall);  // S
            ShockwaveFill(startTime, stepTime, delayTime, Vector2.Add(slot, new Vector2(1, 0)), startPoint, flags, Go, Wall);  // E
        }