/// <summary>
        /// Returns the state of the game.
        /// <para>In lobby: GameState.InLobby</para>
        /// <para>Waiting for players: GameState.Waiting</para>
        /// <para>Ingame: GameState.Ingame</para>
        /// <para>Commending players: GameState.Ending_Commend</para>
        /// </summary>
        /// <returns>Returns the state of the game.</returns>
        public GameState GetGameState()
        {
            using (LockHandler.Passive)
            {
                UpdateScreen();

                // Check if in lobby
                if (Capture.CompareColor(Points.LOBBY_START_GAME, Colors.LOBBY_START_GAME, Fades.LOBBY_START_GAME)) // Get "START GAME" color
                {
                    return(GameState.InLobby);
                }

                // Check if waiting
                if (Capture.CompareColor(Points.LOBBY_START_GAMEMODE, Colors.LOBBY_CHANGE, Fades.LOBBY_CHANGE)) // Check if "START GAMEMODE" button exists.
                {
                    return(GameState.Waiting);
                }

                if (Capture.CompareColor(Points.ENDING_COMMEND_DEFEAT, Colors.ENDING_COMMEND_DEFEAT, Fades.ENDING_COMMEND_DEFEAT)) // Check if commending by testing red color of defeat at top left corner
                {
                    return(GameState.Ending_Commend);
                }

                if (Capture.CompareColor(Points.LOBBY_BACK_TO_LOBBY, Colors.LOBBY_CHANGE, Fades.LOBBY_CHANGE)) // Check if ingame by checking if "START GAMEMODE" button does not exist and the "BACK TO LOBBY" button does.
                {
                    return(GameState.Ingame);
                }

                return(GameState.Unknown);
            }
        }
 /// <summary>
 /// Obtains the markup of an AI's difficulty.
 /// </summary>
 /// <param name="scalar">Garanteed index of difficulty. (0 = easy, 1 = medium, 2 = hard)</param>
 /// <param name="saveAt">Location to save markup at.</param>
 public void GetAIDifficultyMarkup(int scalar, string saveAt)
 {
     using (cg.LockHandler.Passive)
     {
         cg.UpdateScreen();
         int[]        scales = new int[] { 33, 49, 34 };
         DirectBitmap tmp    = Capture.Clone(401, 244, scales[scalar], 17);
         for (int x = 0; x < tmp.Width; x++)
         {
             for (int y = 0; y < tmp.Height; y++)
             {
                 if (tmp.CompareColor(x, y, Colors.WHITE, 30))
                 {
                     tmp.SetPixel(x, y, Color.Black);
                 }
                 else
                 {
                     tmp.SetPixel(x, y, Color.White);
                 }
             }
         }
         tmp.Save(saveAt);
         tmp.Dispose();
     }
 }
        private static bool WaitForMainMenu(ScreenshotMethod screenshotMethod, IntPtr hwnd, DirectBitmap bmp, int maxTime)
        {
            Stopwatch elapsed = new Stopwatch();

            elapsed.Start();
            while (elapsed.ElapsedMilliseconds < maxTime || maxTime == -1)
            {
                Screenshot(screenshotMethod, hwnd, ref bmp);
                if (bmp.CompareColor(Points.MAIN_MENU_OVERWATCH_WATERMARK, Colors.WHITE, 10))
                {
                    Thread.Sleep(2000);
                    return(true);
                }
                Thread.Sleep(500);
            }

            return(false);
        }
        // Checks if an executed command should be added to the list of commands, then adds it.
        private string AddExecutedCommand(int y, int namelength, int[] seed, string command
#if DEBUG
                                          , List <LetterResult> letterInfos
#endif
                                          )
        {
            #region Command Cleanup
            ListenTo ltd = null;

            int nameSeperator = command.IndexOf(']') + 2;

            if (nameSeperator == 1 || nameSeperator > command.Length)
            {
                return(command);
            }

            command = command.Substring(nameSeperator)
                      .Trim();
            string firstWord = command.Split(' ')[0];

#if DEBUG
            letterInfos.RemoveRange(0, nameSeperator);
            cg.DebugMenu?.ShowScan(letterInfos);
#endif

            lock (ListenToAccessLock)
                foreach (ListenTo listenData in ListenTo)
                {
                    if (listenData.Command == firstWord)
                    {
                        ltd = listenData;
                        break;
                    }
                }

            if (ltd == null || !ltd.Listen)
            {
                return(command);
            }
            #endregion

            #region Chat Identity
            // Store executor noise data in a bitmap.
            var          executorscan = new Rectangle(0, y - 4, namelength, 6);
            DirectBitmap executor     = Capture.Clone(executorscan);
            // Set name pixels to black and everything else to white
            for (int xi = 0; xi < executor.Width; xi++)
            {
                for (int yi = 0; yi < executor.Height; yi++)
                {
                    if (executor.CompareColor(xi, yi, seed, Chat.ChatFade))
                    {
                        executor.SetPixel(xi, yi, Color.Black);
                    }
                    else
                    {
                        executor.SetPixel(xi, yi, Color.White);
                    }
                }
            }

            ChatIdentity ci = new ChatIdentity(executor);
            #endregion

            #region Profile and Friend Check
            bool           isFriend = false;
            string         name     = null;
            PlayerIdentity pi       = null;

            // If it was not found, pi is still null. Register the profile if _registerPlayerProfiles is true.
            if (ltd.GetNameAndProfile || ltd.CheckIfFriend)
            {
                Point openMenuAt = new Point(56, y);

                // Open the chat
                cg.Chat.OpenChat();

                // Open the career profile
                cg.RightClick(openMenuAt, Timing.OPTION_MENU);

                // If the Send Friend Request option exists, they are not a friend.
                isFriend = !(bool)cg.Interact.MenuOptionScan(openMenuAt, OptionScanFlags.ReturnFound, null, Markups.SEND_FRIEND_REQUEST);

                if (ltd.GetNameAndProfile)
                {
                    using (cg.LockHandler.Interactive)
                    {
                        // By default, the career profile option is selected and we can just press enter to open it.
                        cg.KeyPress(Keys.Enter);

                        // Wait for the career profile to load.
                        WaitForCareerProfileToLoad();

                        // Get the player name
                        name = cg.GetPlayerName();

                        // Take a screenshot of the career profile.
                        DirectBitmap careerProfileSnapshot = Capture.Clone(Rectangles.LOBBY_CAREER_PROFILE);

                        // Register the player identity.
                        pi = new PlayerIdentity(careerProfileSnapshot);

                        // Go back to the lobby.
                        cg.GoBack(1);
                        //cg.//ResetMouse();

                        // If opening the career profile failed, the state of the chat could be incorrect,
                        // like being wrongly opened or wrongly closed because of when enter was pressed earlier.
                        // This will fix it.
                        cg.Chat.OpenChat();
                        if (!cg.OpenChatIsDefault)
                        {
                            cg.KeyPress(Keys.Enter);
                        }
                    }
                }
                else
                {
                    cg.Interact.CloseOptionMenu();
                }
            }
            #endregion

            CommandData commandData = new CommandData(command, GetChannelFromSeed(seed), ci, isFriend, name, pi);
            if (ltd.Callback != null)
            {
                ltd.Callback.Invoke(commandData);
            }
            else
            {
                commandData.ChatIdentity.Dispose();
                commandData.PlayerIdentity?.Dispose();
            }

            return(command);
        }
        /// <summary>
        /// Creates a new Overwatch process by logging into an account. Since this requires your username and password, I recommend using <see cref="StartOverwatch(OverwatchInfoAuto)"/> instead.
        /// </summary>
        /// <param name="processInfo">Parameters for creating the process.</param>
        /// <returns>The created Overwatch process.</returns>
        public static Process StartOverwatch(OverwatchInfoManual processInfo)
        {
            if (processInfo == null)
            {
                throw new ArgumentNullException(nameof(processInfo));
            }

            int maxWaitTime = 5000;

            if (!File.Exists(processInfo.OverwatchExecutableFilePath))
            {
                throw new FileNotFoundException(string.Format("Overwatch's executable at {0} was not found. " +
                                                              "Change OverwatchProcessInfo.OverwatchExecutableFilePath to the location of the Overwatch executable.", processInfo.OverwatchExecutableFilePath));
            }

            if (!File.Exists(processInfo.OverwatchSettingsFilePath))
            {
                throw new FileNotFoundException(string.Format("Overwatch's settings at {0} was not found. " +
                                                              "Change OverwatchProcessInfo.OverwatchSettingsFilePath to the location of Overwatch's settings.", processInfo.OverwatchSettingsFilePath));
            }

            // Set the video settings.
            var initialSettings = ChangeVideoSettings(processInfo.OverwatchSettingsFilePath, VideoSettings.Item1, VideoSettings.Item2);

            Process OWProcess = new Process();

            OWProcess.StartInfo.FileName  = processInfo.OverwatchExecutableFilePath;
            OWProcess.StartInfo.Arguments = "-Displaymode 0";
            OWProcess.Start();

            // Wait for the window to start
            WaitForVisibleProcessWindow(OWProcess);

            // Show the window
            SetupWindow(OWProcess.MainWindowHandle, processInfo.ScreenshotMethod);

            Stopwatch elapsed = new Stopwatch();

            DirectBitmap bmp = null;

            elapsed.Start();
            while (true)
            {
                Screenshot(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, ref bmp);

                if (elapsed.ElapsedMilliseconds >= maxWaitTime)
                {
                    bmp.Dispose();
                    ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Failed to start Overwatch."));
                }

                // If the text input for the log in info is found, break out of the loop.
                if (bmp.CompareColor(407, 384, new int[] { 168, 168, 170 }, 10))
                {
                    break;
                }
                // If the log in button is yellow, there is not a connection.
                else if (bmp.CompareColor(Points.PRE_MAIN_MENU_LOGIN, Colors.CONFIRM, Fades.CONFIRM))
                {
                    ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Could not log in; no internet connection."));
                }
                Thread.Sleep(500);
            }
            elapsed.Reset();

            Thread.Sleep(100);

            // At this point login info is ready to be inputed
            TextInput(OWProcess.MainWindowHandle, processInfo.Username);
            KeyPress(OWProcess.MainWindowHandle, Keys.Tab);
            TextInput(OWProcess.MainWindowHandle, processInfo.Password);

            // Log in
            Thread.Sleep(50);
            Screenshot(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, ref bmp);
            if (bmp.CompareColor(Points.PRE_MAIN_MENU_LOGIN, Colors.CONFIRM, Fades.CONFIRM))
            {
                KeyPress(OWProcess.MainWindowHandle, Keys.Enter);
            }
            else
            {
                ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Could not log in with the input username or password."));
            }

            Thread.Sleep(500);

            elapsed.Start();
            while (true)
            {
                if (elapsed.ElapsedMilliseconds >= maxWaitTime)
                {
                    ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Failed to start Overwatch."));
                }

                Screenshot(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, ref bmp);
                if (bmp.CompareColor(469, 437, Colors.CONFIRM, Fades.CONFIRM) == false)
                {
                    break;
                }
                Thread.Sleep(500);
            }
            elapsed.Reset();

            Thread.Sleep(500);

            Screenshot(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, ref bmp);
            // Check if login failed
            // s0 will equal true and s1 will equal false if the login failed. s0 and s1 will equal true if an authenticator is required.
            bool s0 = bmp.CompareColor(518, 482, Colors.CONFIRM, Fades.CONFIRM); // "Cancel" button
            bool s1 = bmp.CompareColor(605, 475, Colors.CONFIRM, Fades.CONFIRM); // "Authenticate" button.

            if (s0 && !s1)
            {
                ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Could not log in with the input username or password."));
            }

            // Enter authenticator code if it is required
            if (s0 && s1)
            {
                if (String.IsNullOrEmpty(processInfo.Authenticator))
                {
                    ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Authenticator is required"));
                }

                TextInput(OWProcess.MainWindowHandle, processInfo.Authenticator);
                Thread.Sleep(10);
                KeyPress(OWProcess.MainWindowHandle, Keys.Enter);
                Thread.Sleep(500);

                elapsed.Start();
                while (true)
                {
                    if (elapsed.ElapsedMilliseconds >= maxWaitTime)
                    {
                        ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Failed to start Overwatch."));
                    }

                    Screenshot(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, ref bmp);
                    if (bmp.CompareColor(469, 437, Colors.CONFIRM, Fades.CONFIRM) == false)
                    {
                        break;
                    }
                    Thread.Sleep(500);
                }
                Thread.Sleep(500);

                Screenshot(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, ref bmp);
                if (bmp.CompareColor(518, 482, Colors.CONFIRM, Fades.CONFIRM))
                {
                    ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException(string.Format("Authenticator number \"{0}\" is invalid.", processInfo.Authenticator)));
                }
            }

            if (!WaitForMainMenu(processInfo.ScreenshotMethod, OWProcess.MainWindowHandle, bmp, maxWaitTime))
            {
                ProcessCreateError(initialSettings, processInfo, OWProcess, bmp, new OverwatchStartFailedException("Failed to start Overwatch."));
            }

            if (processInfo.AutomaticallyCreateCustomGame)
            {
                CreateCustomGame(OWProcess.MainWindowHandle);
            }

            // Reset the contrast to its initial value
            RestoreVideoSettings(processInfo.OverwatchSettingsFilePath, initialSettings);

            return(OWProcess);
        }
        /// <summary>
        /// Scans for an option at the specified point.
        /// </summary>
        /// <param name="scanLocation">The location to scan at.</param>
        /// <param name="flags">The flags for scanning.</param>
        /// <param name="saveMarkupsToFolder">The location to save the markup of the scanned options. Set to null to ignore.</param>
        /// <param name="markup">The markup to scan for. Set to null to ignore.</param>
        /// <returns><para>Returns a bool determining if the option is found if <paramref name="markup"/> is not null and <paramref name="flags"/> has the <see cref="OptionScanFlags.ReturnFound"/> flag.</para>
        /// <para>Returns the location of the option if <paramref name="markup"/> is not null and <paramref name="flags"/> has the <see cref="OptionScanFlags.ReturnLocation"/> flag.</para></returns>
        public object MenuOptionScan(Point scanLocation, OptionScanFlags flags, string saveMarkupsToFolder, DirectBitmap markup)
        {
            if (saveMarkupsToFolder != null)
            {
                saveMarkupsToFolder = System.IO.Path.GetDirectoryName(saveMarkupsToFolder) + System.IO.Path.DirectorySeparatorChar;
            }

            using (cg.LockHandler.SemiInteractive)
            {
                if (scanLocation == Point.Empty)
                {
                    if (flags.HasFlag(OptionScanFlags.ReturnFound))
                    {
                        return(false);
                    }
                    else if (flags.HasFlag(OptionScanFlags.ReturnLocation))
                    {
                        return(Point.Empty);
                    }
                    else
                    {
                        return(null);
                    }
                }

                if (flags.HasFlag(OptionScanFlags.OpenMenu))
                {
                    cg.RightClick(scanLocation);
                }

                int xStart       = scanLocation.X + 14, // X position to start scanning.
                    yStart       = 0,                   // Y position to start scanning.
                    optionWidth  = 79,                  // The width of the option.
                    optionHeight = 6,                   // The height of the option.
                    yIncrement   = 1;                   // Pixel distance between options.
                int[] textcolor  = new int[] { 169, 169, 169 };
                int   fade       = 80;

                bool menuPointsDown = MenuPointsDown(scanLocation);

                if (menuPointsDown) // The menu points down.
                {
                    yStart = scanLocation.Y + 12;
                }
                else // The menu points up.
                {
                    yStart     = scanLocation.Y - 18;
                    yIncrement = -yIncrement;
                }

                cg.UpdateScreen();
                var options = new List <Tuple <Point, int> >();

                int optionIndex = 0;
                for (int y = yStart; optionHeight < y && y < cg.Capture.Height - optionHeight; y += yIncrement)
                {
                    bool oob = false;
                    while (!(oob = optionHeight > y || y > cg.Capture.Height - optionHeight) && !Capture.CompareColor(xStart, y + (menuPointsDown ? 1 : -optionHeight), textcolor, fade + 20))
                    {
                        y += yIncrement;
                    }

                    // If the y is out of range of the bitmap, stop scanning the options.
                    if (oob || !Capture.CompareColor(xStart - 8, y, new int[] { 67, 67, 68 }, 30))
                    {
                        break;
                    }

                    int percent = 0;
                    if (markup != null)
                    {
                        int success = 0;
                        int total   = 0;

                        for (int xi = 0; xi < markup.Width; xi++)
                        {
                            for (int yi = 0; yi < markup.Height; yi++)
                            {
                                total++;

                                bool bmpPixelIsBlack    = Capture.CompareColor(xStart + xi, y + yi, new int[] { 170, 170, 170 }, 80);
                                bool markupPixelIsBlack = markup.GetPixel(xi, yi) == Color.FromArgb(0, 0, 0);

                                if (bmpPixelIsBlack == markupPixelIsBlack)
                                {
                                    success++;
                                }
                            }
                        }
                        percent = (int)(Convert.ToDouble(success) / Convert.ToDouble(total) * 100);
                    }

                    // Get bitmap of option
                    if (saveMarkupsToFolder != null)
                    {
                        DirectBitmap work = Capture.Clone(xStart, y, optionWidth, optionHeight);

                        for (int xi = 0; xi < work.Width; xi++)
                        {
                            for (int yi = 0; yi < work.Height; yi++)
                            {
                                if (work.CompareColor(xi, yi, textcolor, fade))
                                {
                                    work.SetPixel(xi, yi, Color.Black);
                                }
                                else
                                {
                                    work.SetPixel(xi, yi, Color.White);
                                }
                            }
                        }

                        work.Save($@"{saveMarkupsToFolder}Option Markup-{optionIndex}.png");
                        work.Dispose();
                    }

#if DEBUG
                    if (cg.DebugMenu != null)
                    {
                        Console.WriteLine($"{optionIndex} - {percent}%");
                    }
#endif

                    options.Add(new Tuple <Point, int>(new Point(xStart, y), percent));
                    optionIndex++;
                }

                Point optionLocation = Point.Empty;

                if (markup != null && options.Count > 0)
                {
                    optionLocation = options.Where(o => o.Item2 > 75).OrderByDescending(o => o.Item2).FirstOrDefault()?.Item1 ?? Point.Empty;
                }

                if (flags.HasFlag(OptionScanFlags.Click))
                {
                    SelectMenuOption(optionLocation);
                }

                // Close the menu.
                if (flags.HasFlag(OptionScanFlags.CloseMenu) || (flags.HasFlag(OptionScanFlags.CloseIfNotFound) && optionLocation == Point.Empty))
                {
                    CloseOptionMenu();
                }

                if (flags.HasFlag(OptionScanFlags.ReturnFound))
                {
                    return(optionLocation != Point.Empty);
                }
                else if (flags.HasFlag(OptionScanFlags.ReturnLocation))
                {
                    return(optionLocation);
                }
                return(null);
            }
        }