public void updateCachedLevels(ParsedHeroes ph) { for (int i = 0; i < ph.HeroStats.Count; i++) { int heroID = ph.FirstHeroIndex + i; int heroLevel = ph.HeroStats[i].Level; setHeroLevel(heroID, heroLevel); } }
/// <summary> /// Tries to buy a given hero upgrade, if there is enough money. /// </summary> /// <param name="heroIndex"></param> /// <param name="desiredUpgrade"></param> /// <param name="currentMoney"></param> /// <returns></returns> public static bool TryUpgradeHero(ParsedHeroes ph, int heroIndex, int desiredUpgrade, double currentMoney) { if (ph.FirstHeroIndex > heroIndex) { AddAction(new Action(GameEngine.GetScrollbarUpPoint(), 0), 3); return false; } else if (ph.LastHeroIndex < heroIndex) { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } int adjustedIndex = heroIndex - ph.FirstHeroIndex; HeroStats hs = ph.HeroStats[adjustedIndex]; int upgradeStatus = hs.HasUpgrade(desiredUpgrade); if (upgradeStatus == 1) { return true; } else if (upgradeStatus == -1) { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } else { Point upgradeButton; if (hs.Hero.Upgrades[desiredUpgrade].Cost < currentMoney && hs.GetUpgradeButton(out upgradeButton, desiredUpgrade)) { AddAction(new Action(upgradeButton, 0)); return false; } } return false; }
/// <summary> /// The main function which drives autoplaying. It will iterate through the task list sequentially, /// performing each task as necessary. When it reaches the Ascend task, it will restart from the beginning. /// </summary> /// <param name="ph">The parsed heroes obtained from the GameEngine</param> /// <param name="curMoney">The current money</param> /// <returns>A string giving a human-readable representation of the next task</returns> public static string TryNextTask(ParsedHeroes ph, double curMoney) { if (nextTaskToPerform >= Tasks.Count()) { return string.Empty; } Task nextTask = Tasks[nextTaskToPerform]; // Iris -- after ascending, we may potentially jump many levels into the game, but we don't have any money to buy any heroes. // To get around this, we do what human players do. We click candies when (and only when) we're on the very first task (aka // immediately after we've ascended). // Also sets the maximum time the run has to end, as a fail safe if the app gets stuck (example, faulty OCR money read) if (nextTaskToPerform == 0) { Point[] pts = GameEngine.GetCandyButtons(); foreach (Point p in pts) { AddAction(new Action(p, Modifiers.NONE)); } // Toggle Progress button if (!GameEngine.IsProgressModeOn()) { AddAction(new Action(GameEngine.GetProgressButton(), 0)); } maxEndTime = DateTime.Now.AddMinutes(Properties.Settings.Default.maxRunDuration); } // Check if the max run time has been reached // Allow maxRunDuration to be set to 0 for unlimited time // By setting nextTaskToPerform to an arbitary number, no way to guarantee anything will change // Hopefully by restarting task list, the game will sort itself out if (Properties.Settings.Default.maxRunDuration != 0 && DateTime.Now > maxEndTime) { nextTaskToPerform = 0; } if (nextTask is AscendTask) { Ascend(ph, curMoney); nextTaskToPerform = 0; return "Ascending"; } if (nextTask is BuyAllÙpgradesTask) { BuyAllUpgrades(); nextTaskToPerform++; return "Buying all upgrades"; } if (nextTask is ActiveTask) { autoClick = true; useSkils = true; nextTaskToPerform++; return "Going Active"; } if (nextTask is IdleTask) { autoClick = false; useSkils = false; nextTaskToPerform++; return "Going Idle"; } if (nextTask is ReloadBrowserTask) { ReloadBrowser(); nextTaskToPerform++; return "Reloading browser window"; } if (nextTask is MidasStartTask) { MidasStart(); nextTaskToPerform++; return "Performing Midas Start"; } if (nextTask is SalvageRelicTask) { SalvageRelic(); nextTaskToPerform++; return "Salvaging Relics"; } if (nextTask is ToggleProgressOff) { if (GameEngine.IsProgressModeOn()) { AddAction(new Action(GameEngine.GetProgressButton(), 0)); } nextTaskToPerform++; return "Turning Progress Mode Off"; } if (nextTask is ToggleProgressOn) { if (!GameEngine.IsProgressModeOn()) { AddAction(new Action(GameEngine.GetProgressButton(), 0)); } nextTaskToPerform++; return "Turning Progress Mode On"; } if (nextTask is MoveZonesForward) { //Progress to level 60-64 AddAction(new Action(GameEngine.GetMoveZoneRightButtion(), 0), 425); nextTaskToPerform++; return "Advancing Zones"; } VerifyTask vt = nextTask as VerifyTask; string retStr = string.Empty; if (!TryLevelHero(ph, nextTask.HeroIndex, nextTask.Level, curMoney, nextTask.Wait)) { if (vt != null) { vt.verifyCount = 0; } retStr = string.Format("Level {0} to {1}", GameEngine.HeroList[nextTask.HeroIndex].Name, nextTask.Level); return retStr; } if (nextTask.Upgrade != -1) { for (int i = 0; i <= nextTask.Upgrade; i++) { if (!TryUpgradeHero(ph, nextTask.HeroIndex, i, curMoney)) { if (vt != null) { vt.verifyCount = 0; } retStr = string.Format("Get upgrade {0} for {1}", nextTask.Upgrade + 1, GameEngine.HeroList[nextTask.HeroIndex].Name); return retStr; } } } if (vt != null) { vt.verifyCount++; if (vt.verifyCount == 5) { nextTaskToPerform++; vt.verifyCount = 0; return TryNextTask(ph, curMoney); } return string.Format("Verify that {0} is properly leveled and upgraded", GameEngine.HeroList[nextTask.HeroIndex].Name); } nextTaskToPerform++; return TryNextTask(ph, curMoney); }
/// <summary> /// Tries to level a given hero to a desired level, if there is enough money. /// </summary> /// <param name="heroIndex">The hero to level</param> /// <param name="desiredLevel">The level desired</param> /// <param name="currentMoney">The current amount of money</param> /// <returns>True if and only if the hero is currently already at that level</returns> public static bool TryLevelHero(ParsedHeroes ph, int heroIndex, int desiredLevel, double currentMoney, bool wait) { //If Active and using auto click mode, click candies if (autoClick) { Point[] pts = GameEngine.GetCandyButtons(); foreach (Point p in pts) { AddAction(new Action(p, Modifiers.NONE)); } } if (ph.FirstHeroIndex > heroIndex) { AddAction(new Action(GameEngine.GetScrollbarUpPoint(), 0), 3); return false; } else if (ph.LastHeroIndex < heroIndex) { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } int adjustedIndex = heroIndex - ph.FirstHeroIndex; HeroStats hs = ph.HeroStats[adjustedIndex]; if (hs.Level == -1) { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } int heroLevel = hs.Level; if (heroLevel >= desiredLevel) { return true; } if (wait && hs.Hero.GetCostToLevel(desiredLevel, heroLevel) > currentMoney) { return false; } Point pt; if (hs.GetBuyButton(out pt)) { while (hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel) < currentMoney && desiredLevel > heroLevel) { if (desiredLevel - heroLevel >= 100 && hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel) < currentMoney) { AddAction(new Action(pt, Modifiers.CTRL)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel); heroLevel += 100; } else if (desiredLevel - heroLevel >= 25 && hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel) < currentMoney) { AddAction(new Action(pt, Modifiers.Z)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel); heroLevel += 25; } else if (desiredLevel - heroLevel >= 10 && hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel) < currentMoney) { AddAction(new Action(pt, Modifiers.SHIFT)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel); heroLevel += 10; } else { AddAction(new Action(pt, 0)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel); heroLevel++; } } return false; } else { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } }
/// <summary> /// Don't leave this method until after we've ascended /// </summary> /// <param name="ph"></param> /// <param name="curMoney"></param> public static void Ascend(ParsedHeroes ph, double curMoney) { Point AscendButton = GameEngine.GetAscendButton(); // use candy height and width cuz it's close enough int candyHeight = GameEngine.GetCandyHeight(); int candyWidth = GameEngine.GetCandyWidth(); Rectangle c = new Rectangle(AscendButton.X - candyWidth / 2, AscendButton.Y - candyHeight / 2, candyWidth, candyHeight); while (true) { using (Bitmap bitmap = GameEngine.GetImage(c)) { if (OCREngine.GetBlobDensity(bitmap, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1), new Color[] { Color.FromArgb(68, 215, 35) }) > 0.10) { AddAction(new Action(AscendButton, 0)); Thread.Sleep(1000); return; } } TryUpgradeHero(ph, 19, 3, curMoney); Thread.Sleep(1000); ph = GameEngine.GetHeroes(); curMoney = GameEngine.GetMoney(); } }
/// <summary> /// Helper method for GetHeroes /// </summary> /// <param name="lines"></param> /// <param name="lb"></param> /// <returns></returns> private static ParsedHeroes ParseHeroes(List<Line> lines, LockBitmap lb) { ParsedHeroes parsedHeroes = new ParsedHeroes(); if (lines.Count() == 0) { return null; } else if (lines.Count() <= 3) { // it's cid it's cid it's cid // Note: Cid is special because of 2 reasons: when she appears it's the only time in the game // there can be less than 3 heroes on screen. Second, she doesn't have a number to represent DPS, // so we need to special case some stuff to make OCR work. parsedHeroes.HeroStats = new List<HeroStats>(); parsedHeroes.FirstHeroIndex = 0; parsedHeroes.LastHeroIndex = 0; HeroStats cid = new HeroStats(); cid.Hero = GameEngine.HeroList[0]; cid.Level = 0; cid.bottomright.X = lines[0].GetBoundingRectangle().Right; cid.bottomright.Y = lines[0].GetBoundingRectangle().Bottom; parsedHeroes.HeroStats.Add(cid); return parsedHeroes; } List<Line> widths = new List<Line>(); List<Line> levels = new List<Line>(); int i = -1; bool rectSkipped = false; if (lines[1].GetBoundingRectangle().Top - lines[0].GetBoundingRectangle().Top > HeroSeperatorHeight) { i = 1; rectSkipped = true; } else { i = 0; } for (; i < lines.Count(); i+=2) { widths.Add(lines[i]); if (i + 1 < lines.Count()) { levels.Add(lines[i + 1]); } } // If less than 6 lines -- it's Cid if (lines.Count() < 6) { parsedHeroes.FirstHeroIndex = 0; parsedHeroes.LastHeroIndex = lines.Count() / 2; } else { int smallestIndex = -1; int smallestScore = int.MaxValue; for (int j = 0; j + widths.Count() <= HeroList.Count(); j++) { int thisScore = 0; for (int k = 0; k < widths.Count(); k++) { thisScore += Math.Abs((int)(HeroList[j + k].Namewidth * PlayableArea.Width - widths[k].GetBoundingRectangle().Width)); } if (thisScore < smallestScore) { smallestScore = thisScore; smallestIndex = j; } } parsedHeroes.FirstHeroIndex = smallestIndex; parsedHeroes.LastHeroIndex = smallestIndex + widths.Count() - 1; } List<HeroStats> heroStats = new List<HeroStats>(); if (parsedHeroes.FirstHeroIndex == 1 && rectSkipped) { // oh god, is it cid? why is it always cid if (lines[0].GetBoundingRectangle().Left < lb.Width / 2) { HeroStats stats = new HeroStats(); stats.Hero = HeroList[0]; stats.Level = 0; stats.bottomright = new Point(lines[0].GetBoundingRectangle().Right, lines[0].GetBoundingRectangle().Bottom); heroStats.Add(stats); parsedHeroes.FirstHeroIndex = 0; } } for (int j = 0; j < widths.Count(); j++) { HeroStats stats = new HeroStats(); stats.Hero = HeroList[parsedHeroes.FirstHeroIndex + j]; stats.bottomright = new Point(widths[j].GetBoundingRectangle().Right, widths[j].GetBoundingRectangle().Bottom); if (j < levels.Count()) { levels[j].DoOcr(lb, PlayableArea.Width * PlayableArea.Height, false, lb.Width / 2, 3 /* lvl */); if (!int.TryParse(levels[j].OcrString, out stats.Level)) { stats.Level = 0; } } else { stats.Level = -1; } heroStats.Add(stats); } parsedHeroes.HeroStats = heroStats; return parsedHeroes; }
/// <summary> /// The main function which drives autoplaying. It will iterate through the task list sequentially, /// performing each task as necessary. When it reaches the Ascend task, it will restart from the beginning. /// </summary> /// <param name="ph">The parsed heroes obtained from the GameEngine</param> /// <param name="curMoney">The current money</param> /// <returns>A string giving a human-readable representation of the next task</returns> public static string TryNextTask(ParsedHeroes ph, double curMoney) { if (nextTaskToPerform >= Tasks.Count()) { return string.Empty; } Task nextTask = Tasks[nextTaskToPerform]; // Iris -- after ascending, we may potentially jump many levels into the game, but we don't have any money to buy any heroes. // To get around this, we do what human players do. We click candies when (and only when) we're on the very first task (aka // immediately after we've ascended). // Also sets the maximum time the run has to end, as a fail safe if the app gets stuck (example, faulty OCR money read) if (nextTaskToPerform == 0) { Point[] pts = GameEngine.GetCandyButtons(); foreach (Point p in pts) { AddAction(new Action(p, Modifiers.NONE)); } maxEndTime = DateTime.Now.AddMinutes(Properties.Settings.Default.maxRunDuration); } // Check if the max run time has been reached if (DateTime.Now > maxEndTime && nextTaskToPerform < Tasks.Count() - 3) { nextTaskToPerform = Tasks.Count() - 3; } if (nextTask is AscendTask) { Ascend(ph, curMoney); nextTaskToPerform = 0; return "Ascending"; } if (nextTask is BuyAllÙpgradesTask) { BuyAllUpgrades(); nextTaskToPerform++; return "Buying all upgrades"; } if (nextTask is ActiveTask) { autoClick = true; useSkils = true; nextTaskToPerform++; return "Going Active"; } if (nextTask is IdleTask) { autoClick = false; useSkils = false; nextTaskToPerform++; return "Going Idle"; } if (nextTask is ReloadBrowserTask) { ReloadBrowser(); nextTaskToPerform++; return "Reloading browser window"; } VerifyTask vt = nextTask as VerifyTask; string retStr = string.Empty; if (!TryLevelHero(ph, nextTask.HeroIndex, nextTask.Level, curMoney, nextTask.Wait)) { if (vt != null) { vt.verifyCount = 0; } retStr = string.Format("Level {0} to {1}", GameEngine.HeroList[nextTask.HeroIndex].Name, nextTask.Level); return retStr; } if (nextTask.Upgrade != -1) { for (int i = 0; i <= nextTask.Upgrade; i++) { if (!TryUpgradeHero(ph, nextTask.HeroIndex, i, curMoney)) { if (vt != null) { vt.verifyCount = 0; } retStr = string.Format("Get upgrade {0} for {1}", nextTask.Upgrade + 1, GameEngine.HeroList[nextTask.HeroIndex].Name); return retStr; } } } if (vt != null) { vt.verifyCount++; if (vt.verifyCount == 5) { nextTaskToPerform++; vt.verifyCount = 0; return TryNextTask(ph, curMoney); } return string.Format("Verify that {0} is properly leveled and upgraded", GameEngine.HeroList[nextTask.HeroIndex].Name); } nextTaskToPerform++; return TryNextTask(ph, curMoney); }
/// <summary> /// Don't leave this method until after we've ascended /// </summary> /// <param name="ph"></param> /// <param name="curMoney"></param> public static void Ascend(ParsedHeroes ph, double curMoney) { TrashRelics(); Point AscendButton = GameEngine.GetAscendButton(); // use candy height and width cuz it's close enough int candyHeight = GameEngine.GetCandyHeight(); int candyWidth = GameEngine.GetCandyWidth(); Rectangle c = new Rectangle(AscendButton.X - candyWidth / 2, AscendButton.Y - candyHeight / 2, candyWidth, candyHeight); while (true) { using (Bitmap bitmap = new Bitmap(c.Width, c.Height)) { using (Graphics g = Graphics.FromImage(bitmap)) { g.CopyFromScreen(new Point(c.Left, c.Top), Point.Empty, c.Size); } if (OCREngine.GetBlobDensity(bitmap, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1), new Color[] { Color.FromArgb(68, 215, 35) }) > 0.10) { AddAction(new Action(AscendButton, 0)); Thread.Sleep(1000); return; } } TryUpgradeHero(ph, 19, 3, curMoney); Thread.Sleep(1000); ph = GameEngine.GetHeroes(); curMoney = GameEngine.GetMoney(); } }
/// <summary> /// Tries to level a given hero to a desired level, if there is enough money. /// </summary> /// <param name="heroIndex">The hero to level</param> /// <param name="desiredLevel">The level desired</param> /// <param name="currentMoney">The current amount of money</param> /// <returns>True if and only if the hero is currently already at that level</returns> public static bool TryLevelHero(ParsedHeroes ph, int heroIndex, int desiredLevel, double currentMoney, bool wait) { if (GameEngine.GetCachedLevel(heroIndex) >= desiredLevel) { return true; } if (ph.FirstHeroIndex > heroIndex) { AddAction(new Action(GameEngine.GetScrollbarUpPoint(), 0), 3); return false; } else if (ph.LastHeroIndex < heroIndex) { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } // Check for stepping out of bounds (happens when part of the view is blocked while attempting to do certain actions) int adjustedIndex = heroIndex - ph.FirstHeroIndex; if (adjustedIndex >= ph.HeroStats.Count ) { return false; } HeroStats hs = ph.HeroStats[adjustedIndex]; if (hs.Level == -1) { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } int heroLevel = hs.Level; if (heroLevel >= desiredLevel) { return true; } if (wait && hs.Hero.GetCostToLevel(desiredLevel, heroLevel) > currentMoney) { return false; } Point pt; if (hs.GetBuyButton(out pt)) { while (hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel) < currentMoney && desiredLevel > heroLevel) { if (desiredLevel - heroLevel >= 100 && hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel) < currentMoney) { AddAction(new Action(pt, Modifiers.CTRL)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 100, heroLevel); heroLevel += 100; } else if (desiredLevel - heroLevel >= 25 && hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel) < currentMoney) { AddAction(new Action(pt, Modifiers.Z)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 25, heroLevel); heroLevel += 25; } else if (desiredLevel - heroLevel >= 10 && hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel) < currentMoney) { AddAction(new Action(pt, Modifiers.SHIFT)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 10, heroLevel); heroLevel += 10; } else { AddAction(new Action(pt, 0)); currentMoney -= hs.Hero.GetCostToLevel(heroLevel + 1, heroLevel); heroLevel++; } } return false; } else { AddAction(new Action(GameEngine.GetScrollbarDownPoint(), 0), 3); return false; } }