using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; namespace IdleMafiaBot { sealed partial class Game { static readonly Regex RespectChip = new Regex(@"Respect\s*[:.]?\s*([0-9OoIlS][0-9OoIlS,. ]{0,9})\s*$", RegexOptions.IgnoreCase); internal static int FightRespectIn(List lines, Rectangle area) { foreach (var l in lines.Where(x => x.CenterX > area.Left + area.Width / 2 && x.CenterY < area.Top + area.Height / 3)) { var m = RespectChip.Match(l.Text); int v; if (m.Success && int.TryParse(Parse.Digits(m.Groups[1].Value), out v) && v >= 0) return v; } return -1; } public int FightRespect(Frame f, Prep prep) { var area = PageArea(f); return FightRespectIn(Ocr.Read(f, area, PageScale(f), prep), area); } internal OcrLine CollectionWithdrawBeside(Frame f, OcrLine deposit) { var area = PageArea(f); int h = Math.Max(8, deposit.Box.Height); foreach (var prep in new[] { Prep.None, Prep.DarkText, Prep.WhiteText }) { var hit = Ocr.Read(f, area, PageScale(f), prep).FirstOrDefault(l => Parse.Key(l.Text) == "WITHDRAWAII" && l.Box.X >= deposit.Box.Right && l.Box.X - deposit.Box.Right < Math.Max(12 * h, 5 * deposit.Box.Width) && Math.Abs(l.CenterY - deposit.CenterY) < 2 * h); if (hit != null) return hit; } return null; } public sealed class WithdrawResult { public CollectionState Before, After; public bool Out; } internal static OcrLine CollectionWithdrawConfirm(List lines) { if (!lines.Any(l => Parse.Has(l.Text, "RETURN ALL") || Parse.Has(l.Text, "COLLECTION TO"))) return null; var cancel = lines.FirstOrDefault(l => Parse.Key(l.Text) == "CANCEI"); if (cancel == null) return null; int h = Math.Max(8, cancel.Box.Height); return lines.FirstOrDefault(l => Parse.Key(l.Text) == "WITHDRAWAII" && l.Box.Right < cancel.Box.X && Math.Abs(l.CenterY - cancel.CenterY) <= h); } public WithdrawResult WithdrawCollection() { if (!OpenTab(Tab.Collection)) return null; var r = new WithdrawResult(); OcrLine button = null; for (int look = 0; look < 3 && button == null; look++) { if (look > 0) Wait(1200); using (var f = Capture()) { var deposit = FindCollectionDeposit(f, out r.Before); if (deposit != null) button = CollectionWithdrawBeside(f, deposit); } } if (r.Before != null && r.Before.Deposited == 0 && r.Before.Attack <= 0) { log("Collection: nothing in it to withdraw (" + r.Before + ")"); r.After = r.Before; r.Out = true; return r; } if (button == null) { log("Collection: WITHDRAW ALL not found - nothing pressed (" + collectionSeen + ")"); Snapshot("collection withdraw not found", 60); return null; } CheckForPurchaseWindow(); NotDisconnected(); if (OnlyNavigate) RefuseInCheck(button.Text); Click(button.CenterX, button.CenterY, 1200); Snapshot("collection withdraw pressed", 0, true); for (int look = 0; look < 4; look++) { using (var f = Capture()) { var popup = Ocr.Read(f, PopupArea(f), PopupScale(f), Prep.None); CheckForPurchasePrompt(popup); var yes = CollectionWithdrawConfirm(popup); if (yes != null) { Snapshot("collection withdraw window", 0, true); Click(yes.CenterX, yes.CenterY, 1500); continue; } FindCollectionDeposit(f, out r.After); } if (r.After != null && (r.After.Attack >= 0 && r.Before.Attack > r.After.Attack || r.After.Deposited >= 0 && r.Before.Deposited > r.After.Deposited)) { r.Out = true; break; } Wait(800); } return r; } internal string LowestFlaggedCity(Frame f) { var flagged = FlaggedCities(f); return Districts.Cities.FirstOrDefault(flagged.Contains); } public bool LowestFamilyDistrict(out string city, out int number, out string name) { city = name = null; number = -1; if (!OpenTab(Tab.Territory)) return false; CloseStrayDistrict(); OcrLine cityBox, districtBox; cityBox = districtBox = null; for (int look = 0; look < 6 && cityBox == null; look++) { if (look > 0) Wait(1000); using (var f = Capture()) if (!TerritoryBar(f, out cityBox, out districtBox)) cityBox = null; } if (cityBox == null) { log("Territory: the page's city and district drop-downs didn't read"); Snapshot("respect cycle territory not read", 60); return false; } Click(cityBox.CenterX, cityBox.CenterY, 900); using (var f = Capture()) city = LowestFlaggedCity(f); Click(cityBox.CenterX, cityBox.CenterY, 600); if (city == null) return false; if (!SelectCity(city, out cityBox, out districtBox) || !MyFamilyOn()) return false; Click(districtBox.CenterX, districtBox.CenterY, 900); int h = Math.Max(8, cityBox.Box.Height), bottom = districtBox.Box.Bottom, left = districtBox.Box.X; var rows = new List>(); using (var f = Capture()) foreach (var l in Ocr.Read(f, PageArea(f), PageScale(f) + 1, Prep.None).Where(l => l.Box.Y > bottom && l.Box.X < left + 8 * h)) { int no; string nm; if (ListedDistrict(l.Text, city, out no, out nm)) rows.Add(Tuple.Create(no, l, nm)); } Click(districtBox.CenterX, districtBox.CenterY, 600); var first = ListColumn(rows).FirstOrDefault(); if (first == null) return false; number = first.Item1; name = first.Item3; return true; } } sealed partial class Bot { bool Lowering { get { return S.RespectState.Length > 0; } } internal static bool RespectUnfinished(string state) { return state == "0|0" || state.EndsWith("|s"); } int lastRespect = -1, respectOnce = -1, depositTries; DateTime respectWait = DateTime.MinValue, stationAgain = DateTime.MinValue; int ReadRespect() { int a, b; using (var f = game.Capture()) { a = game.FightRespect(f, Prep.None); b = game.FightRespect(f, Prep.WhiteText); } int v = a >= 0 && a == b ? a : -1; if (v < 0) { int one = a >= 0 && b < 0 ? a : b >= 0 && a < 0 ? b : -1; if (one >= 0 && (one == respectOnce || (lastRespect >= 0 && Math.Abs(one - lastRespect) <= 300))) v = one; respectOnce = one; } if (v >= 0) lastRespect = v; return v; } bool RespectTurn() { if (DateTime.UtcNow < respectWait) return false; int r = ReadRespect(); if (r < 0) { SayOnce("respect unread " + DateTime.UtcNow.ToString("yyyyMMddHH"), "Respect cycle: couldn't read your respect on the Fight page"); return false; } SayOnce("respect read " + DateTime.UtcNow.ToString("yyyyMMddHH"), "Respect cycle: respect is " + r.ToString("N0", CultureInfo.InvariantCulture) + ", limits " + S.RespectLow.ToString("N0", CultureInfo.InvariantCulture) + " to " + S.RespectHighNow.ToString("N0", CultureInfo.InvariantCulture) + (Lowering ? " - lowering" : "")); if (!Lowering) return r >= S.RespectHighNow && StartLowering(r); if (r > S.RespectLow && RespectUnfinished(S.RespectState)) return StartLowering(r); if (r > S.RespectLow && S.RespectState.Split('|').Length > 1 && S.RespectState.Split('|')[1] != "1" && DateTime.UtcNow >= stationAgain) { stationAgain = DateTime.UtcNow.AddMinutes(15); Log("Respect cycle: the crew isn't garrisoned yet - trying again"); if (StationCrewLowest(false)) { S.RespectState = S.RespectState.Split('|')[0] + "|1"; SaveSettings(); } return true; } if (r > S.RespectLow) return false; RespectBack("respect is " + r.ToString("N0", CultureInfo.InvariantCulture)); return true; } bool StartLowering(int respect) { bool again = Lowering; var p = S.RespectState.Split('|'); bool taken = p[0] == "1", stationed = p.Length > 1 && p[1] == "1"; Log("Respect cycle: respect is " + respect.ToString("N0", CultureInfo.InvariantCulture) + (again ? " - finishing the start that was cut off, " : " - ") + "garrisoning the crew and taking out the collection, down to " + S.RespectLow.ToString("N0", CultureInfo.InvariantCulture)); if (!again) { S.RespectState = "0|0|s"; SaveSettings(); if (S.Bank && Can(Tab.Bank)) DoBank(); } if (!stationed) { stationed = StationCrewLowest(again); S.RespectState = "0|" + (stationed ? "1" : "0") + "|s"; SaveSettings(); } if (!taken) { var w = game.WithdrawCollection(); taken = w != null || again; if (w != null && !w.Out) { Log("Respect cycle: WITHDRAW ALL pressed, but the collection's totals didn't drop (" + w.Before + ")"); game.Snapshot("collection withdraw no change", 60); } } S.RespectState = (taken ? "1" : "0") + "|" + (stationed ? "1" : "0"); if (!taken && !stationed) { S.RespectState = ""; respectWait = DateTime.UtcNow.AddMinutes(30); Log("Respect cycle: couldn't garrison or take out the collection - tried again in 30 minutes"); } SaveSettings(); return true; } internal static List> GarrisonPicks(string list, List unknown = null) { var got = new List>(); foreach (var e in NeverAttackEntries(list)) { string c, n; int no; if (Game.MatchDistrict(e, out c, out no, out n)) { if (!got.Any(t => t.Item3 == n)) got.Add(Tuple.Create(c, no, n)); } else if (unknown != null) unknown.Add(e); if (got.Count == 5) break; } return got; } bool StationCrewLowest(bool again) { string city = null, name = null; int no = -1; game.CloseDistrict(); var picks = GarrisonPicks(S.GarrisonPicks); if (picks.Count > 0) { bool opened = false; foreach (var p in picks) { if (game.OpenDistrict(p.Item1, p.Item2, p.Item3)) { city = p.Item1; no = p.Item2; name = p.Item3; opened = true; break; } if (!game.LastDistrictGone) { game.Snapshot("respect cycle district not opened", 60); return false; } SayOnce("garrison pick gone " + p.Item3 + " " + DateTime.UtcNow.ToString("yyyyMMddHH"), "Respect cycle: " + p.Item3 + " isn't your family's any more - the next pick"); } if (!opened) { SayOnce("garrison picks gone " + DateTime.UtcNow.ToString("yyyyMMddHH"), "Respect cycle: none of your picked districts is your family's any more - the crew stays with you until you pick a new one"); return false; } } else { if (!game.LowestFamilyDistrict(out city, out no, out name)) { Log("Respect cycle: no district of your family found - the crew stays with you"); return false; } if (!game.OpenDistrict(city, no, name)) { game.Snapshot("respect cycle district not opened", 60); return false; } } int n = game.StationAllHere(); game.CloseDistrict(); Log("Respect cycle: " + (n > 0 ? "stationed " + n + " henchmen at " + name + " (" + city + ")" : "no henchmen stationed at " + name)); SnapshotOnce("respect cycle crew stationed"); return n > 0 || again; } void RespectBack(string why) { Log("Respect cycle: " + why + " - putting the collection back and recalling the crew"); var p = S.RespectState.Split('|'); bool taken = p[0] == "1", stationed = p.Length > 1 && p[1] == "1"; if (taken) { var d = game.DepositCollection(); bool seen = d != null && (d.Toast != null || (d.After != null && d.Before != null && d.After.Attack > d.Before.Attack)); if (d != null) Log("Respect cycle: DEPOSIT ALL pressed" + (d.Toast != null ? " - \"" + d.Toast.Trim() + "\"" : seen ? "" : ", nothing showed it took")); if (seen || (d != null && ++depositTries >= 3)) { taken = false; depositTries = 0; } } int recalled = game.OpenTab(Tab.Crew) ? game.RecallEveryone() : -1; if (recalled > 0) Log("Respect cycle: recalled " + recalled + " henchmen"); stationed = recalled < 0; S.RespectState = taken || stationed ? (taken ? "1" : "0") + "|" + (stationed ? "1" : "0") : ""; SaveSettings(); if (Lowering) { respectWait = DateTime.UtcNow.AddMinutes(2); Log("Respect cycle: not everything went back - tried again in 2 minutes"); } else { nextCrew = nextEquip = DateTime.UtcNow; Log("Respect cycle: done, back to normal fights"); } } } }