using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text.RegularExpressions; namespace IdleMafiaBot { sealed class KnockTab { public string Owner = ""; public bool Own, Used, Treat, Trick, JustNow, Loading; public OcrLine Knock; public int Knocks = -1, NextSeconds = -1, Paid = -1, Candy = -1; } sealed class MapDoor { public string Name; public Point At; } sealed partial class Game { static readonly Regex KnockCount = new Regex(@"(?:^|\s)([\dOoQ])\s*/\s*[5Ss](?:\s|$)"); static readonly Regex KnockDoor = new Regex(@"['’`]?S?['’`]?\s+DOOR\s*$", RegexOptions.IgnoreCase); static readonly Regex KnockPaid = new Regex(@"\+\s*(\d[\d,]*)\s*CANDY", RegexOptions.IgnoreCase); static readonly Regex KnockCandy = new Regex(@"^\s*(\d[\d,]*)\s*CANDY\s*$", RegexOptions.IgnoreCase); internal static bool KnockButton(Frame f, OcrLine l) { string key = Parse.Key(l.Text); return key.EndsWith("KNOCK") && key.Length <= 8 && l.CenterY > f.Height / 2 + 3 * l.Box.Height; } internal KnockTab ReadKnockTab(Frame f) { var lines = Ocr.Read(f, BigPopupArea(f), PopupScale(f), Prep.None); var own = lines.FirstOrDefault(l => Parse.Key(l.Text).StartsWith("YOURFAMI") && Parse.Key(l.Text).EndsWith("DOOR")); if (own == null && !lines.Any(l => Parse.Key(l.Text).StartsWith("KNOCKAND"))) return null; var t = new KnockTab { Own = own != null }; var door = lines.FirstOrDefault(l => l != own && KnockDoor.IsMatch(l.Text)); if (door != null) t.Owner = KnockDoor.Replace(door.Text, "").Trim(); bool spent = false; foreach (var l in lines) { string s = l.Text.Trim(); var m = KnockCount.Match(s); if (m.Success) t.Knocks = int.Parse(m.Groups[1].Value.Replace('O', '0').Replace('o', '0').Replace('Q', '0')); if (KnockButton(f, l)) t.Knock = l; if (Parse.Has(s, "LOADING")) t.Loading = true; if (Parse.Has(s, "NO KNOCKS LEFT")) spent = true; if (Parse.Has(s, "next knock is ready")) { spent = true; if (t.NextSeconds <= 0) t.NextSeconds = Parse.Duration(s.TrimEnd('.', ' ')); } if (Parse.Has(s, "KNOCKED THIS HOUR") || Parse.Has(s, "knock here again")) t.Used = true; if (Regex.IsMatch(s, @"^\W*Treat\b", RegexOptions.IgnoreCase)) t.Treat = true; if (Regex.IsMatch(s, @"^\W*Trick\b", RegexOptions.IgnoreCase)) t.Trick = true; if (Parse.Has(s, "Just now")) t.JustNow = true; if (Parse.Has(s, "Next in")) t.NextSeconds = Parse.Duration(s); m = KnockPaid.Match(s); if (m.Success) t.Paid = int.Parse(Parse.Digits(m.Groups[1].Value)); m = KnockCandy.Match(s); if (m.Success) t.Candy = int.Parse(Parse.Digits(m.Groups[1].Value)); } if (spent) t.Knocks = 0; return t; } public bool MapAllFamilies() { for (int tries = 0; tries < 3; tries++) { OcrLine mine, reset; bool on; using (var f = Capture()) { var lines = Ocr.Read(f, PageArea(f), PageScale(f), Prep.None); mine = lines.FirstOrDefault(l => Parse.Key(l.Text) == "MYFAMIIY"); if (mine == null) return false; on = lines.Any(l => Parse.Has(l.Text, "MATCHING")) || MatchingUnder(f, mine); reset = lines.FirstOrDefault(l => Parse.Key(l.Text).StartsWith("RESETZOOM")); } if (!on) { if (reset != null) Press("RESET ZOOM", reset.CenterX, reset.CenterY, 1200); return true; } Click(mine.CenterX, mine.CenterY, 1500); } return false; } public List MapDoors() { using (var f = Capture()) return MapDoors(f); } public List MapDoorsDrawn() { List doors = null; for (int look = 0; look < 8; look++) { if (look > 0) Wait(1000); doors = MapDoors(); if (doors != null && doors.Count > 0) return doors; } return doors; } internal List MapDoors(Frame f) { OcrLine cityBox, districtBox; string city = null; int top = 0, foot = 0; var area = PageArea(f); if (TerritoryBar(f, out cityBox, out districtBox)) { city = Districts.Cities.FirstOrDefault(c => Parse.Key(cityBox.Text).StartsWith(Parse.Key(c))); top = districtBox.Box.Bottom + 2 * Math.Max(8, cityBox.Box.Height); foot = cityBox.Box.Y - cityBox.Box.Height; } if (city == null) { var mine = Ocr.Read(f, area, PageScale(f), Prep.None).FirstOrDefault(l => Parse.Key(l.Text) == "MYFAMIIY"); if (mine == null) return null; int h = Math.Max(8, mine.Box.Height); top = mine.Box.Bottom + 2 * h; foot = area.Bottom - 6 * h; } var lines = new List(); foreach (var s in new[] { PageScale(f), PageScale(f) + 1 }) lines.AddRange(Ocr.Read(f, area, s, Prep.None).Where(l => l.Box.Y >= top && l.CenterY + 2 * l.Box.Height < foot)); Func> named = c => lines.Select(l => { int no; string name; return CityDistrict(l.Text, c, out no, out name) ? name : null; }).Where(n => n != null).Distinct(); if (city == null) { city = Districts.Cities.OrderByDescending(c => named(c).Count()).First(); if (named(city).Count() < 3) return null; } var got = new List(); foreach (var l in lines) { int no; string name; if (!CityDistrict(l.Text, city, out no, out name)) continue; if (!got.Any(d => d.Name == name)) got.Add(new MapDoor { Name = name, At = new Point(l.CenterX, l.CenterY + 2 * l.Box.Height) }); } return got; } public List DoorsInReach(List doors) { using (var f = Capture()) return doors.Where(d => !ToastOver(f, d.At)).ToList(); } public DistrictWindow OpenDoor(MapDoor door) { NotDisconnected(); Click(door.At.X, door.At.Y, 1500); for (int look = 0; look < 3; look++) { if (look > 0) Wait(700); using (var f = Capture()) { var w = ReadDistrictWindow(f); if (w != null) return w; } } return null; } internal OcrLine HalloweenTab(Frame f) { return Ocr.Read(f, BigPopupArea(f), PopupScale(f), Prep.None).FirstOrDefault(l => Parse.Key(l.Text).StartsWith("HAIIOWEEN")); } public KnockTab OpenKnockTab(out bool noTab) { noTab = false; OcrLine tab = null; for (int look = 0; look < 4 && tab == null; look++) { if (look > 0) Wait(700); using (var f = Capture()) { var seen = ReadKnockTab(f); if (seen != null) return Settled(seen); tab = HalloweenTab(f); if (tab == null && CloseRulingFamily(f)) Wait(500); } } if (tab == null) { noTab = true; return null; } Press(tab, 900); for (int look = 0; look < 4; look++) { if (look > 0) Wait(700); using (var f = Capture()) { var t = ReadKnockTab(f); if (t != null) return Settled(t); } } return null; } static bool KnockKnown(KnockTab t) { return t.Own || t.Used || t.Knock != null || t.Knocks == 0; } KnockTab Settled(KnockTab t) { for (int look = 0; (look < 3 || (t.Loading && look < 14)) && !KnockKnown(t); look++) { Wait(700); using (var f = Capture()) { var again = ReadKnockTab(f); if (again != null) t = again; } } return t; } public KnockTab Knock(KnockTab tab) { Press(tab.Knock, 2000); KnockTab used = null; for (int look = 0; look < 12; look++) { if (look > 0) Wait(1000); using (var f = Capture()) { var t = ReadKnockTab(f); if (t != null && t.JustNow) return t; if (t != null && t.Used) used = t; } } return used; } } }