using System.Collections.Generic;using System.Text.RegularExpressions; public string Owner = ""; public bool Own, Used, Treat, Trick, JustNow, Loading; public int Knocks = -1, NextSeconds = -1, Paid = -1, Candy = -1; 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(); 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); if (m.Success) t.Paid = int.Parse(Parse.Digits(m.Groups[1].Value)); if (m.Success) t.Candy = int.Parse(Parse.Digits(m.Groups[1].Value)); public bool MapAllFamilies() for (int tries = 0; tries < 3; tries++) 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 (reset != null) Press("RESET ZOOM", reset.CenterX, reset.CenterY, 1200); Click(mine.CenterX, mine.CenterY, 1500); public List<MapDoor> MapDoors() using (var f = Capture()) return MapDoors(f); public List<MapDoor> MapDoorsDrawn() List<MapDoor> doors = null; for (int look = 0; look < 8; look++) if (look > 0) Wait(1000); if (doors != null && doors.Count > 0) return doors; internal List<MapDoor> MapDoors(Frame f) OcrLine cityBox, districtBox; 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; 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<OcrLine>(); 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<string, IEnumerable<string>> 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(); city = Districts.Cities.OrderByDescending(c => named(c).Count()).First(); if (named(city).Count() < 3) return null; var got = new List<MapDoor>(); 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) }); public List<MapDoor> DoorsInReach(List<MapDoor> doors) using (var f = Capture()) return doors.Where(d => !ToastOver(f, d.At)).ToList(); public DistrictWindow OpenDoor(MapDoor door) Click(door.At.X, door.At.Y, 1500); for (int look = 0; look < 3; look++) using (var f = Capture()) var w = ReadDistrictWindow(f); 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) for (int look = 0; look < 4 && tab == null; look++) using (var f = Capture()) var seen = ReadKnockTab(f); if (seen != null) return Settled(seen); if (tab == null && CloseRulingFamily(f)) Wait(500); if (tab == null) { noTab = true; return null; } for (int look = 0; look < 4; look++) using (var f = Capture()) if (t != null) return Settled(t); 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++) using (var f = Capture()) var again = ReadKnockTab(f); if (again != null) t = again; public KnockTab Knock(KnockTab tab) for (int look = 0; look < 12; look++) if (look > 0) Wait(1000); using (var f = Capture()) if (t != null && t.JustNow) return t; if (t != null && t.Used) used = t;