private static bool ScanCharacter(out Character character)
        {
            character = new Character();
            Navigation.SelectCharacterAttributes();
            string name    = null;
            string element = null;

            // Scan the Name and element of Character. Attempt 75 times max.
            ScanNameAndElement(ref name, ref element);

            if (string.IsNullOrWhiteSpace(name))
            {
                if (string.IsNullOrWhiteSpace(name))
                {
                    UserInterface.AddError("Could not determine character's name");
                }
                if (string.IsNullOrWhiteSpace(element))
                {
                    UserInterface.AddError("Could not determine character's element");
                }
                return(false);
            }

            // Check if character was first scanned
            if (name != firstCharacterName)
            {
                if (string.IsNullOrWhiteSpace(firstCharacterName))
                {
                    firstCharacterName = name;
                }

                bool ascended = false;
                // Scan Level and ascension
                int level = ScanLevel(ref ascended);
                if (level == -1)
                {
                    UserInterface.AddError($"Could not determine {name}'s level");
                    return(false);
                }

                // Scan Experience
                //experience = ScanExperience();
                Navigation.SystemRandomWait(Navigation.Speed.Normal);

                // Scan Constellation
                Navigation.SelectCharacterConstellation();
                int constellation = ScanConstellations();
                Navigation.SystemRandomWait(Navigation.Speed.Normal);

                // Scan Talents
                Navigation.SelectCharacterTalents();
                int[] talents = ScanTalents(name);
                Navigation.SystemRandomWait(Navigation.Speed.Normal);

                // Scale down talents due to constellations
                if (constellation >= 3)
                {
                    if (Scraper.Characters.ContainsKey(name.ToLower()))
                    {
                        // get talent if character
                        if (constellation >= 5)
                        {
                            talents[1] -= 3;
                            talents[2] -= 3;
                        }
                        else if ((string)Scraper.Characters[name.ToLower()]["ConstellationOrder"][0] == "skill")
                        {
                            talents[1] -= 3;
                        }
                        else
                        {
                            talents[2] -= 3;
                        }
                    }
                    else
                    {
                        return(false);
                    }
                }

                var weaponType = Scraper.Characters[name.ToLower()]["WeaponType"].ToObject <int>();

                int experience = 0;
                character = new Character(name, element, level, ascended, experience, constellation, talents, (WeaponType)weaponType);
                return(true);
            }
            Logger.Info("Repeat character {0} detected. Finishing character scan...", name);
            return(false);
        }
        private static int[] ScanTalents(string name)
        {
            int[] talents = { -1, -1, -1 };

            int specialOffset = 0;

            // Check if character has a movement talent like
            // Mona or Ayaka
            if (name.Contains("Mona") || name.Contains("Ayaka"))
            {
                specialOffset = 1;
            }

            var xRef = 1280.0;
            var yRef = 720.0;

            if (Navigation.GetAspectRatio() == new Size(8, 5))
            {
                yRef = 800.0;
            }

            Rectangle region = new RECT(
                Left:   (int)(160 / xRef * Navigation.GetWidth()),
                Top:    (int)(116 / yRef * Navigation.GetHeight()),
                Right:  (int)(225 / xRef * Navigation.GetWidth()),
                Bottom: (int)(141 / yRef * Navigation.GetHeight()));

            for (int i = 0; i < 3; i++)
            {
                // Change y-offset for talent clicking
                int yOffset = (int)(110 / yRef * Navigation.GetHeight()) + (i + ((i == 2) ? specialOffset : 0)) * (int)(60 / yRef * Navigation.GetHeight());

                Navigation.SetCursorPos(Navigation.GetPosition().Left + (int)(1130 / xRef * Navigation.GetWidth()), Navigation.GetPosition().Top + yOffset);
                Navigation.Click();
                Navigation.Speed speed = i == 0 ? Navigation.Speed.Normal : Navigation.Speed.Fast;
                Navigation.SystemRandomWait(speed);

                while (talents[i] < 1 || talents[i] > 15)
                {
                    Bitmap talentLevel = Navigation.CaptureRegion(region);

                    talentLevel = Scraper.ResizeImage(talentLevel, talentLevel.Width * 2, talentLevel.Height * 2);

                    Bitmap n = Scraper.ConvertToGrayscale(talentLevel);
                    Scraper.SetContrast(60, ref n);
                    Scraper.SetInvert(ref n);

                    string text = Scraper.AnalyzeText(n).Trim();
                    text = Regex.Replace(text, @"\D", string.Empty);

                    if (int.TryParse(text, out int level))
                    {
                        if (level >= 1 && level <= 15)
                        {
                            talents[i] = level;
                            UserInterface.SetCharacter_Talent(talentLevel, text, i);
                        }
                    }

                    n.Dispose();
                    talentLevel.Dispose();
                }
            }

            Navigation.sim.Keyboard.KeyPress(WindowsInput.Native.VirtualKeyCode.ESCAPE);
            return(talents);
        }
        private static void ScanNameAndElement(ref string name, ref string element)
        {
            int       attempts    = 0;
            int       maxAttempts = 75;
            Rectangle region      = new RECT(
                Left:   (int)(85 / 1280.0 * Navigation.GetWidth()),
                Top:    (int)(10 / 720.0 * Navigation.GetHeight()),
                Right:  (int)(305 / 1280.0 * Navigation.GetWidth()),
                Bottom: (int)(55 / 720.0 * Navigation.GetHeight()));

            do
            {
                Navigation.SystemRandomWait(Navigation.Speed.Fast);
                using (Bitmap bm = Navigation.CaptureRegion(region))
                {
                    Bitmap n = Scraper.ConvertToGrayscale(bm);
                    Scraper.SetThreshold(110, ref n);
                    Scraper.SetInvert(ref n);

                    n = Scraper.ResizeImage(n, n.Width * 2, n.Height * 2);
                    string block = Scraper.AnalyzeText(n, Tesseract.PageSegMode.Auto).ToLower().Trim();
                    string line  = Scraper.AnalyzeText(n, Tesseract.PageSegMode.SingleLine).ToLower().Trim();

                    // Characters with wrapped names will not have a slash
                    string nameAndElement = line.Contains("/") ? line : block;

                    if (nameAndElement.Contains("/"))
                    {
                        var split = nameAndElement.Split('/');

                        // Search for element and character name in block

                        // Long name characters might look like
                        // <Element>   <First Name>
                        // /           <Last Name>
                        element = !split[0].Contains(" ") ? Scraper.FindElementByName(split[0].Trim()) : Scraper.FindElementByName(split[0].Split(' ')[0].Trim());

                        // Find character based on string after /
                        // Long name characters might search by their last name only but it'll still work.
                        name = Scraper.FindClosestCharacterName(Regex.Replace(split[1], @"[\W]", string.Empty));
                        if (name == "Traveler")
                        {
                            foreach (var item in from item in Scraper.Characters
                                     where item.Value["GOOD"].ToString() == "Traveler"
                                     select item)
                            {
                                name = item.Key;
                            }
                        }
                    }
                    n.Dispose();

                    if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(element))
                    {
                        UserInterface.SetCharacter_NameAndElement(bm, name, element);
                        return;
                    }
                }
                attempts++;
                Navigation.SystemRandomWait(Navigation.Speed.Normal);
            } while ((string.IsNullOrWhiteSpace(name) || string.IsNullOrEmpty(element)) && (attempts < maxAttempts));
            name    = null;
            element = null;
        }