using System; using System.Collections.Generic; using System.Linq; namespace IdleMafiaBot { sealed partial class Bot { DateTime nextAnimals, nextAnimalShop; int animalFails; bool AnimalCratesOpen() { return S.Crates && !CratesHeld(DateTime.UtcNow); } bool AnimalShopOn() { return S.AnimalBuyFood || S.AnimalBuyCrates || AnimalCratesOpen(); } bool AnimalsDue(DateTime now) { return S.Animals && (S.AnimalFeed || S.AnimalTrain || AnimalShopOn()) && now >= nextAnimals && Can(Tab.Safehouse) && !TutorialOn; } void DoAnimals(Header h) { var now = DateTime.UtcNow; nextAnimals = now.AddMinutes(20); bool shopNow = AnimalShopOn() && now >= nextAnimalShop; double bankedBefore = -1; if (shopNow && CanWithdraw()) using (var f = game.Capture()) { var hd = game.ReadHeader(f); bankedBefore = hd != null ? hd.Banked : -1; } if (!game.OpenAnimals()) { if (++animalFails >= 2) nextAnimals = S.LastLevel >= 10 ? now.AddMinutes(30) : now.AddHours(3); return; } animalFails = 0; bool took = false; double spent = -1; try { AnimalCard card = null; for (int look = 0; look < 3 && (card == null || card.Hunger < 0); look++) { if (look > 0) game.Wait(800); using (var f = game.Capture()) { var w = game.ReadAnimalsWindow(f); if (w != null) card = game.ReadAnimalCard(f, w); } } if (card != null && card.None) { SayOnce("no animal", "Animals: no animal yet - pick the free one in the game's ANIMALS window; the bot looks again in 3 hours"); nextAnimals = now.AddHours(3); return; } if (card == null || card.Hunger < 0) { bool hasOne = S.LastLevel >= 10; SayOnce("animal card", "Animals: the active animal's card didn't read (no animal yet? the game gives one from level 10) - the bot looks again in " + (hasOne ? "30 minutes" : "3 hours")); game.Snapshot("animal card not read", 180); nextAnimals = hasOne ? now.AddMinutes(30) : now.AddHours(3); return; } Info("animal", card.Name.Length > 0 ? card.Name + (card.Level > 0 ? ", level " + card.Level : "") + ", hunger " + card.Hunger + "%" : ""); if (S.AnimalFeed) card = FeedAnimal(card); if (S.AnimalTrain) TrainAnimal(h, card); if (shopNow) took = AnimalShop(h, bankedBefore, out spent); } finally { game.CloseAnimals(); if (took) DepositBack(spent); } } AnimalCard FeedPremium(AnimalCard card) { if (card.Hunger < 0 || card.Hunger > 50 || card.Premium <= 0 || !game.AnimalsTab("owned")) return card; int from = card.Hunger, fed = 0; for (int i = 0; i < 2; i++) { using (var f = game.Capture()) { var w = game.ReadAnimalsWindow(f); if (w == null) break; card = game.ReadAnimalCard(f, w); } if (card.Hunger < 0 || card.Hunger > 50 || card.Premium <= 0 || card.FeedPremium == null) break; int before = card.Hunger, left = card.Premium; game.PressFeedPremium(card); game.Snapshot("animal fed premium", 0, true, false); AnimalCard after = null; for (int look = 0; look < 4 && (after == null || (after.Hunger <= before && after.Premium == left)); look++) { if (look > 0) game.Wait(500); using (var f = game.Capture()) { var w = game.ReadAnimalsWindow(f); if (w != null) after = game.ReadAnimalCard(f, w); } } if (after == null || (after.Hunger <= before && after.Premium == left)) { Log("Animals: FEED 50% pressed, but the hunger (" + before + "%) and the premium food (" + left + ") didn't change"); game.Snapshot("animal premium feed did nothing", 60); break; } card = after; fed++; } if (fed > 0) Log("Animals: fed " + (card.Name.Length > 0 ? card.Name : "the animal") + " " + fed + " Premium Animal Food (hunger " + from + "% -> " + card.Hunger + "%, " + (card.Premium >= 0 ? card.Premium + " left" : "left not read") + ")"); return card; } AnimalCard FeedAnimal(AnimalCard card) { if (S.AnimalPremium) card = FeedPremium(card); if (card.Hunger >= 50) return card; if (card.Food == 0) { SayOnce("animal no food " + DateTime.UtcNow.DayOfYear, "Animals: " + card.Name + " is hungry (" + card.Hunger + "%) and there's no Animal Food left"); return card; } if (!game.AnimalsTab("owned")) return card; int fed = 0, from = card.Hunger; for (int i = 0; i < 8 && card.Hunger >= 0 && card.Hunger < 80 && card.Food != 0; i++) { using (var f = game.Capture()) { var w = game.ReadAnimalsWindow(f); if (w == null) break; card = game.ReadAnimalCard(f, w); } if (card.Feed == null) { game.Snapshot("animal feed not found", 60); break; } if (card.Hunger >= 80) break; int hungerBefore = card.Hunger, foodBefore = card.Food; game.PressFeed(card); if (fed == 0) game.Snapshot("animal fed", 0, true, false); AnimalCard after = null; for (int look = 0; look < 4 && (after == null || (after.Hunger <= hungerBefore && after.Food == foodBefore)); look++) { if (look > 0) game.Wait(500); using (var f = game.Capture()) { var w = game.ReadAnimalsWindow(f); if (w != null) after = game.ReadAnimalCard(f, w); } } if (after == null || (after.Hunger <= hungerBefore && after.Food == foodBefore)) { Log("Animals: FEED 10% pressed, but the hunger (" + hungerBefore + "%) and the food (" + foodBefore + ") didn't change"); game.Snapshot("animal feed did nothing", 60); break; } card = after; fed++; } if (fed > 0) Log("Animals: fed " + (card.Name.Length > 0 ? card.Name : "the animal") + " " + fed + " Animal Food (hunger " + from + "% -> " + card.Hunger + "%, " + (card.Food >= 0 ? card.Food + " food left" : "food left not read") + ")"); return card; } void TrainAnimal(Header h, AnimalCard card) { if (!game.AnimalsTab("training")) { game.Snapshot("animal training tab not opened", 60); return; } OcrLine train; int energy, used; string rest; bool ready; AnimalsWindow w; using (var f = game.Capture()) w = game.ReadAnimalsWindow(f); if (w == null) return; ready = game.ReadTraining(w, out train, out energy, out used, out rest); if (!ready) { var m = System.Text.RegularExpressions.Regex.Match(rest ?? "", @"(\d{1,2})\s?[:.]\s?(\d{2})"); int secs = m.Success ? int.Parse(m.Groups[1].Value) * 60 + int.Parse(m.Groups[2].Value) : -1; if (secs > 0) nextAnimals = DateTime.UtcNow.AddSeconds(Math.Min(secs + 20, 20 * 60 + 20)); return; } if (energy <= 0) energy = 10; if (used < 0) used = 5; if (card.Hunger >= 0 && card.Hunger - used < 30) { SayOnce("animal train hunger " + DateTime.UtcNow.DayOfYear, "Animals: training waits - the hunger (" + card.Hunger + "%) would drop under 30%"); return; } int e = h != null ? h.Energy : -1; using (var f = game.Capture()) { var now = game.ReadHeader(f); if (now != null && now.Energy >= 0) e = now.Energy; } if (e < energy) { nextAnimals = DateTime.UtcNow.AddMinutes(10); return; } if (train == null) using (var f = game.Capture()) train = game.FindAnimalTrain(f, w); if (train == null) { SayOnce("animal train " + DateTime.UtcNow.DayOfYear, "Animals: the animal is ready, but TRAIN didn't read (the window read: " + string.Join(" | ", w.Lines.Where(l => l.Box.Y > w.Title.Box.Bottom + 15 * w.H && l.Box.X > w.Area.X + w.Area.Width / 3).Select(l => l.Text)) + ")"); game.Snapshot("animal train not found", 60); return; } game.PressAnimalTrain(train, w); game.Snapshot("animal trained", 0, true, false); bool still = true; for (int look = 0; look < 3 && still; look++) { if (look > 0) game.Wait(1000); using (var f = game.Capture()) { var after = game.ReadAnimalsWindow(f); OcrLine t2; int e2, u2; string r2; still = after != null && game.ReadTraining(after, out t2, out e2, out u2, out r2); } } if (still) { Log("Animals: TRAIN pressed, but the animal still shows ready to train"); game.Snapshot("animal train did nothing", 60); nextAnimals = DateTime.UtcNow.AddMinutes(5); return; } Log("Animals: trained " + (card.Name.Length > 0 ? card.Name : "the animal") + " (" + energy + " energy, " + used + "% hunger)"); nextAnimals = DateTime.UtcNow.AddMinutes(20).AddSeconds(20); } bool AnimalShop(Header h, double bankedBefore, out double spent) { nextAnimalShop = DateTime.UtcNow.AddHours(1); spent = 0; bool took = false; double tookFloor = 0; var bought = new List(); var skipped = new HashSet(); for (int round = 0; round < 12; round++) { if (!game.AnimalsTab("shop")) { game.Snapshot("animal shop tab not opened", 60); break; } List buys; AnimalsWindow w; double cash, banked; using (var f = game.Capture()) { w = game.ReadAnimalsWindow(f); if (w == null) break; buys = game.ReadAnimalShop(f, w); var hd = game.ReadHeader(f); cash = hd != null ? hd.Cash : -1; banked = hd != null && CanWithdraw() ? hd.Banked : -1; } if (round == 0 && buys.Count == 0) { game.Snapshot("animal shop not read", 60); break; } double keep = ShopKeep(true); var want = buys.Where(b => !skipped.Contains(b.Name) && !b.SoldOut && b.Price > 0 && b.Price <= S.AnimalMaxPrice && (b.Crate ? S.AnimalBuyCrates : S.AnimalBuyFood)).OrderBy(b => b.Crate ? 1 : 0).ThenBy(b => b.Price).FirstOrDefault(); if (want == null) { var open = AnimalCratesOpen() ? buys.FirstOrDefault(b => b.Crate && b.Open != null && b.OpenLit && !skipped.Contains("open " + b.Name)) : null; if (open == null) break; game.PressAnimalOpen(open, w); if (!game.AfterAnimalOpen()) { Log("Animals: opened a " + open.Name + ", but what it showed didn't close - crates wait 6 hours"); nextAnimalShop = DateTime.UtcNow.AddHours(6); break; } Log("Animals: opened a " + open.Name); Count.Rewards++; skipped.Add("open " + open.Name); continue; } if (cash < 0) break; if (banked < 0 && !took) banked = bankedBefore; double free = banked >= 0 || !took ? FreeCash(cash, banked, keep) : cash - tookFloor; if (free < want.Price) { skipped.Add(want.Name); continue; } if (cash < want.Price) { if (took || !CanWithdraw() || BankFee() != 0) { skipped.Add(want.Name); continue; } game.CloseAnimals(); if (WithdrawKeeping(want.Price, false, want.Price, keep, out tookFloor)) took = true; else skipped.Add(want.Name); if (!game.OpenAnimals()) break; continue; } if (!want.BuyLit) { skipped.Add(want.Name); continue; } game.PressAnimalBuy(want, w); game.Snapshot("animal shop bought", 0, true, false); AnimalBuy again = null; double cashAfter = -1; bool shop = false; for (int look = 0; look < 3 && !shop; look++) { if (look > 0) game.Wait(900); using (var f = game.Capture()) { var w2 = game.ReadAnimalsWindow(f); if (w2 == null) continue; var list = game.ReadAnimalShop(f, w2); shop = list.Count > 0; again = list.FirstOrDefault(b => b.Name == want.Name); var hd = game.ReadHeader(f); cashAfter = hd != null ? hd.Cash : -1; } } if (!shop) { Log("Animals: BUY pressed on " + want.Name + ", then a window the bot doesn't know - the animal shop waits 6 hours"); game.Snapshot("animal shop after buy", 60); nextAnimalShop = DateTime.UtcNow.AddHours(6); break; } bool moved = again == null || again.SoldOut || Math.Abs(again.Price - want.Price) > 0.5 || again.Left != want.Left || (cashAfter >= 0 && cashAfter < cash - want.Price * 0.5); if (!moved) { Log("Animals: BUY pressed on " + want.Name + " for " + M(want.Price) + ", but nothing changed"); game.Snapshot("animal buy did nothing", 60); skipped.Add(want.Name); continue; } bought.Add(want.Name + " for " + M(want.Price)); if (took) spent += want.Price; } if (bought.Count > 0) Log("Animals: bought " + string.Join(", ", bought)); return took; } } }