using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text.RegularExpressions; namespace IdleMafiaBot { sealed partial class Game { public bool OnlyNavigate; static readonly HashSet CloseKeys = Keys("DONE", "OK", "OKAY", "CONTINUE", "CLOSE", "NICE", "AWESOME", "COOL", "GOT IT"); static bool IsMenuEntry(string text) { var one = new List { new OcrLine { Text = text ?? "" } }; return View.TabLabels.Values.Any(l => View.Label(one, l) != null) || Parse.Key(text ?? "") == "EVENTS"; } static bool IsSubTab(string text) { string k = Parse.Key(text); return FamilyTabKeys.Contains(k) || k == "AUDITIOG" || k == "BROWSEFAMIIIES" || IsBackToMyFamily(new OcrLine { Text = text ?? "" }); } internal bool NavigationPress(string text, int x, int y) { var v = view; bool known = v != null && v.Problem == null; if (IsMenuEntry(text) && known && x < v.Menu.Right) return true; if (IsSubTab(text) && known && y < v.Page.Top + v.Page.Height / 4) return true; return CloseKeys.Contains(Parse.Key(text)); } readonly HashSet refusedInCheck = new HashSet(); void RefuseInCheck(string text) { if (refusedInCheck.Add(Parse.Key(text))) log("The check didn't press \"" + (text ?? "").Trim() + "\": it only opens tabs and reads"); throw new NeverPressException(text, "the check only opens tabs"); } public sealed class MenuRead { public List Seen = new List(); public bool Whole; public int Unread; } public MenuRead ReadWholeMenu() { var r = new MenuRead(); var seen = new HashSet(); using (var f = Capture()) if (HideChat(f)) ForgetView(); Dictionary last = null; bool atTop = false; for (int step = 0; step < 16; step++) { View v; Dictionary now; using (var f = Capture()) { v = ViewOf(f); if (v.Problem != null || !v.ReadMenu(f)) return null; int unread = MenuInSight(f, v); now = v.MenuSignature(); if (atTop) { foreach (var kv in v.Tabs) if (!IsLockedEntry(f, v, kv.Value)) seen.Add(kv.Key); r.Unread = Math.Max(r.Unread, unread); } } bool moved = last == null || !View.SameMenu(last, now); last = now; if (!atTop) { if (!moved) { atTop = true; step--; last = null; continue; } Scroll(v.Menu.X + v.Menu.Width / 2, v.Menu.Y + v.Menu.Height / 2, 5); ForgetView(); continue; } if (!moved) { r.Whole = true; break; } Scroll(v.Menu.X + v.Menu.Width / 2, v.Menu.Y + v.Menu.Height / 2, -5); ForgetView(); } r.Seen = seen.OrderBy(t => (int)t).ToList(); return r; } internal static int MenuInSight(Frame f, View v) { for (int i = 0; i < 3 && v.UnreadButtons(f) > 0 && v.ReadAgain(f, v.Strip); i++) { } return v.UnreadButtons(f); } public int ReadBankFee(Frame f, out string line) { line = ""; var lines = Ocr.Read(f, PageArea(f), PageScale(f), Prep.None); var card = BankCard(lines); if (card == null) return -1; var words = lines.Where(l => l.Box.Bottom > card.Box.Y).OrderBy(l => l.Box.Y).ThenBy(l => l.Box.X).Select(l => l.Text.Trim()).ToList(); int from = words.FindIndex(w => Parse.Has(w, "FEE") || w.Contains("%")); if (from >= 0) line = string.Join(" ", words.Skip(from)); if (line.Length > 220) line = line.Substring(0, 220); return FeeIn(string.Join(" ", words)); } internal static int FeeIn(string text) { if (string.IsNullOrEmpty(text)) return -1; foreach (Match m in Regex.Matches(text, @"(? 0) return n; } if (NearIn(Parse.Key(text), "NODEPOSITFEENOBANKFEESPASS", 2)) return 0; if (Regex.IsMatch(text, @"\(\s*no\s+fee\s*\)", RegexOptions.IgnoreCase)) return 0; return -1; } static bool NearIn(string key, string want, int off) { if (key.Contains(want)) return true; for (int start = 0; start + want.Length - off <= key.Length; start++) for (int len = want.Length - off; len <= want.Length + off && start + len <= key.Length; len++) if (View.Distance(key.Substring(start, len), want) <= off) return true; return false; } public sealed class JobsLook { public List Cities = new List(), Folded = new List(); public int Cash = -1; } public JobsLook ReadJobsList(List catalog) { var r = new JobsLook(); JobsTop(); MeasureNotch(catalog); string last = null; for (int page = 0; page < 40; page++) { string sig; using (var f = Capture()) sig = JobsPage(f, catalog, r); if (sig == last) break; last = sig; ScrollPage(0.54, -ListNotches(6)); } JobsTop(); return r; } public string JobsPage(Frame f, List catalog, JobsLook r) { var lines = Ocr.Read(f, JobsArea(f), 2, Prep.None); foreach (var h in ReadCityHeaders(f, lines)) { if (!r.Cities.Contains(h.Index)) r.Cities.Add(h.Index); if (h.Collapsed && !r.Folded.Contains(h.Index)) r.Folded.Add(h.Index); } if (r.Cash < 0 && CardsFromLines(f, lines, catalog, false).Count > 0) r.Cash = lines.Any(l => Regex.IsMatch(l.Text, @"\$\s?[0-9OoIlS]") && (Parse.Has(l.Text, "XP") || Parse.Has(l.Text, "ITEM"))) ? 1 : 0; return string.Join(",", lines.Select(l => l.Text + "@" + l.Box.Y)); } public sealed class StageLook { public int Stage = -1, Highest = -1, NeedLevel = -1, ResetSeconds = -1; public bool Ready, Cleared; } public StageLook ReadStageLook(int level) { for (int look = 0; look < 3; look++) { if (look > 0) Wait(400); StageLook r; using (var f = Capture()) r = StageLookIn(f, level); if (r != null) return r; } return null; } internal StageLook StageLookIn(Frame f, int level) { var area = PageArea(f); var lines = Ocr.Read(f, area, PageScale(f), Prep.None); var st = ReadBossStage(lines, area); if (st == null || (st.Stage < 0 && st.Highest < 0)) return null; var r = new StageLook { Stage = st.Stage, Highest = st.Highest, NeedLevel = st.NeedLevel, ResetSeconds = st.ResetSeconds, Ready = st.Attack != null, Cleared = st.Cleared || st.DefeatedToday }; int split = area.Left + (int)(area.Width * 0.70); bool fromList = r.NeedLevel < 0; foreach (var l in lines.Where(l => fromList && l.CenterX >= split)) { var m = StageLevelRx.Match(l.Text); int n; if (m.Success && int.TryParse(Parse.Digits(m.Groups[1].Value), out n) && n > level && (r.NeedLevel < 0 || n < r.NeedLevel)) r.NeedLevel = n; } return r; } public bool OffersOfflineOps(Frame f) { return Ocr.Read(f, PageArea(f), PageScale(f), Prep.None).Any(l => Parse.Key(l.Text).StartsWith("OFFIINEOPERATION")); } } }