/// <summary>
        /// Override default ZapDarlElixirDrills with SmartZap if user select that.
        /// </summary>
        /// <returns></returns>
        public override IEnumerable <int> ZapDarkElixirDrills()
        {
            if (!isZapped)
            {
                if (GetCurrentSetting("Smart Zap Drills") == 1)
                {
                    // Call Smart zap method
                    var minDEAmount     = GetCurrentSetting("Min Dark Elixir per Zap");
                    var minDEDrillLevel = UserSettings.MinDarkElixirDrillLevel;
                    var spells          = Deploy.GetTroops().Extract(u => u.ElementType == DeployElementType.Spell);

                    foreach (var t in SmartZapping.SmartZap(minDEAmount, minDEDrillLevel, spells, GetCurrentSetting("Use EarthQuake spell on drills")))
                    {
                        yield return(t);
                    }
                }
                else
                {
                    // Call the default Zap drills method.
                    foreach (var t in base.ZapDarkElixirDrills())
                    {
                        yield return(t);
                    }
                }
            }

            isZapped = true;
        }
Esempio n. 2
0
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[16 Fingers] Attack start");

            // Get all the units available
            Log.Debug("Scanning troops");

            var unitDeployElements = Deploy.GetTroops();

            // remove spells
            unitDeployElements.Extract(DeployElementType.Spell);

            var heroesAndClanCastle = unitDeployElements.Extract(u => u.IsHero || u.Id == DeployId.ClanCastle);

            var unitGroups = new Dictionary <string, DeployElement[]>
            {
                { "tank units", unitDeployElements.Extract(AttackType.Tank).ToArray() },
                { "attack units", unitDeployElements.Extract(AttackType.Damage).ToArray() },
                { "heal units", unitDeployElements.Extract(AttackType.Heal).ToArray() },
                { "wallbreak units", unitDeployElements.Extract(AttackType.Wallbreak).ToArray() },
            };

            PointFT left   = new PointFT(PointFT.MinRedZoneX, PointFT.MaxRedZoneY);
            PointFT top    = new PointFT(PointFT.MaxRedZoneX, PointFT.MaxRedZoneY);
            PointFT right  = new PointFT(PointFT.MaxRedZoneX, PointFT.MinRedZoneY);
            PointFT bottom = new PointFT(PointFT.MinRedZoneX, PointFT.MinRedZoneY);

            Tuple <PointFT, PointFT>[] lines =
            {
                new Tuple <PointFT, PointFT>(left,  top),
                new Tuple <PointFT, PointFT>(right, top),
                new Tuple <PointFT, PointFT>(left,  bottom),
                new Tuple <PointFT, PointFT>(right, bottom),
            };

            foreach (var unitGroup in unitGroups)
            {
                Logger.Info("[16 Fingers] Deploying " + unitGroup.Key);
                foreach (var y in Deploy.AlongLines(unitGroup.Value, lines, 4))
                {
                    yield return(y);
                }
            }

            Logger.Info("[16 Fingers] Deploying heroes");
            var heroPoint = new Container <PointFT> {
                Item = new PointFT((lines[0].Item1.X + lines[0].Item2.X) / 2, (lines[0].Item1.Y + lines[0].Item2.Y) / 2)
            };

            foreach (var delay in Deploy.AtPoint(heroesAndClanCastle.Where(d => d?.Count > 0).ToArray(), heroPoint, 1, 0, (int)(UserSettings.WaveDelay * 1000)))
            {
                yield return(delay);
            }

            // Remove clan castle before watching heroes
            heroesAndClanCastle.ExtractOne(x => x.ElementType == DeployElementType.ClanTroops);
            Deploy.WatchHeroes(heroesAndClanCastle);

            Logger.Info("[16 Fingers] Deploy done");
        }
Esempio n. 3
0
        public override IEnumerable <int> AttackRoutine()
        {
            // Bottom right side
            var rightBottom = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MinY);
            var bottomRight = new PointFT((float)GameGrid.MinX + 8, (float)GameGrid.DeployExtents.MinY);

            var center = new PointFT(bottomRight.X + 0.5f * (rightBottom.X - bottomRight.X),
                                     bottomRight.Y + 0.5f * (rightBottom.Y - bottomRight.Y));

            // Screenshot for 3 points for the deployment line
            using (var bmp = Screenshot.Capture())
            {
                using (var g = Graphics.FromImage(bmp))
                {
                    Visualize.RectangleT(bmp, new RectangleT((int)rightBottom.X, (int)rightBottom.Y, 1, 1), new Pen(Color.Blue));
                    Visualize.RectangleT(bmp, new RectangleT((int)bottomRight.X, (int)bottomRight.Y, 1, 1), new Pen(Color.Blue));

                    Visualize.RectangleT(bmp, new RectangleT((int)center.X, (int)center.Y, 1, 1), new Pen(Color.White));
                }
                var d = DateTime.UtcNow;
                Screenshot.Save(bmp, "Bottom Right {d.Year}-{d.Month}-{d.Day} {d.Hour}-{d.Minute}-{d.Second}-{d.Millisecond}");
            }

            // Deploy troops
            var units = Deploy.GetTroops();

            units.OrderForDeploy();
            for (var i = 3; i >= 1; i--)
            {
                switch (i)
                {
                case 3:
                    point = rightBottom;
                    break;

                case 2:
                    point = bottomRight;
                    break;

                case 1:
                    point = center;
                    break;
                }

                foreach (var unit in units)
                {
                    if (unit?.Count > 0)
                    {
                        foreach (var t in Deploy.AtPoint(unit, point, unit.Count / i))
                        {
                            yield return(t);
                        }
                    }
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Read troops to check if it's air attack or not
        /// </summary>
        public static void IsAirAttack()
        {
            var deployElements = Deploy.GetTroops();

            var dragon     = deployElements.ExtractOne(DeployId.Dragon);
            var babyDragon = deployElements.ExtractOne(DeployId.BabyDragon);
            var balloon    = deployElements.ExtractOne(DeployId.Balloon);
            var Lava       = deployElements.ExtractOne(DeployId.LavaHound);
            var minion     = deployElements.ExtractOne(DeployId.Minion);

            AllInOnePushDeploy.IsAirAttack = (Lava?.Count > 0 || balloon?.Count > 5 || dragon?.Count > 5 || babyDragon?.Count > 5 || minion?.Count >= 10) ? true : false;
        }
        public static IEnumerable <int> DeployUnusedTroops()
        {
            // Check unit bar for unused troops
            Log.Warning($"{AllInOnePushDeploy.AttackName} search for unused troops !!");
            var unusedTroops = Deploy.GetTroops();
            var spell        = unusedTroops.Extract(DeployElementType.Spell);

            if (unusedTroops.Sum(u => u.Count) > 0)
            {
                unusedTroops.OrderForDeploy();
                foreach (var u in unusedTroops)
                {
                    if (u?.Count > 0)
                    {
                        Log.Warning($"we found {u.Count}x {u.PrettyName}");
                    }
                }

                Log.Info($"[{AllInOnePushDeploy.AttackName}] deploy unused troops");
                foreach (var unit in unusedTroops)
                {
                    if (unit?.Count > 0)
                    {
                        if (unit.IsRanged)
                        {
                            foreach (var t in Deploy.AlongLine(unit, AllInOnePushDeploy.FirstFunnellingPoint, AllInOnePushDeploy.SecondFunnellingPoint, unit.Count, 4))
                            {
                                yield return(t);
                            }
                        }
                        else
                        {
                            foreach (var t in Deploy.AtPoint(unit, AllInOnePushDeploy.Origin, unit.Count))
                            {
                                yield return(t);
                            }
                        }
                    }
                }
            }
            else
            {
                Log.Info($"[{AllInOnePushDeploy.AttackName}] all Troops have been deployed");
            }
        }
Esempio n. 6
0
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[Random] Attack start");

            // Get all units, regardless of type
            List <DeployElement> units = Deploy.GetTroops();

            // Scatter them across the map
            Random rng = new Random();

            foreach (DeployElement unit in units)
            {
                Log.Info("[Random] Deploying " + unit.PrettyName);
                for (int i = 0; i < 2; i++)
                {
                    Input.Click(unit.Rect.GetCenter());
                }

                while (unit.Count > 0)
                {
                    Log.Debug($"Trying to deploy {unit.Count:N0} {unit.PrettyName}s");
                    for (int i = 0; i < unit.Count; i++)
                    {
                        Point p = new PointFT(
                            (float)rng.Range(PointFT.MinRedZoneX, PointFT.MaxRedZoneX),
                            (float)rng.Range(PointFT.MinRedZoneY, PointFT.MaxRedZoneY))
                                  .ToScreenAbsolute();
                        Input.Click(p);
                    }
                    int deployed = unit.Recount();
                    Log.Debug($"Deployed {deployed:N0} {unit.PrettyName}s");
                }

                yield return(1000);
            }

            // Watch for hero health etc.
            Deploy.WatchHeroes(units.Where(u => u.IsHero).ToList());

            Log.Info("[Random] Deploy done");
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[Deploy] Deploy start");

            // Get all the units available
            Log.Debug("Scanning troops");
            var deployElements = Deploy.GetTroops();
            var spells         = deployElements.Extract(DeployElementType.Spell);
            var tankUnits      = deployElements.Extract(AttackType.Tank).ToArray();
            var attackUnits    = deployElements.Extract(AttackType.Damage).ToArray();
            var healUnits      = deployElements.Extract(AttackType.Heal).ToArray();

            var waveCounter = 1;

            // Get starting resources
            LootResources preLoot = Opponent.GetAvailableLoot();

            if (preLoot == null)
            {
                Log.Error("[Deploy] Milking deploy could not read available starting loot");
                Attack.Surrender();
                yield break;
            }

            Log.Debug($"[Deploy] Pre-attack resources - G: {preLoot.Gold}, E: {preLoot.Elixir}, DE: {preLoot.DarkElixir}");

            // Make sure we wait at least 15 seconds in this attack, in case we snipe TH

            // Loop until surrender conditions are met
            while (true)
            {
                // Get deploy points for each mine that is on the outside of the base
                //resourcesFull = GetResourcesState();
                GenerateDeployPointsFromMinesToMilk(CacheBehavior.CheckForDestroyed);
                if (deployPoints == null || deployPoints.Length < 1)
                {
                    Log.Debug("Surrendering because deployPoints = " + deployPoints?.Length);
                    break;
                }

                if (tankUnits.Any())
                {
                    foreach (var t in Deploy.AtPoints(tankUnits, deployPoints))
                    {
                        yield return(t);
                    }
                    yield return(1000);
                }

                if (attackUnits.Any())
                {
                    foreach (var t in Deploy.AtPoints(attackUnits, deployPoints, 6))
                    {
                        yield return(t);
                    }
                    yield return(1000);
                }

                if (healUnits.Any())
                {
                    foreach (var t in Deploy.AtPoints(healUnits, deployPoints))
                    {
                        yield return(t);
                    }
                    yield return(1000);
                }

                // Wait for the wave to finish
                Log.Info($"[Deploy] Wave {waveCounter} Deployed. Waiting to finish...");
                foreach (var t in Attack.WaitForNoResourceChange())
                {
                    yield return(t);
                }

                // Get starting resources, cache needs to be false to force a new check
                LootResources postLoot = Opponent.GetAvailableLoot(false);

                if (postLoot == null)
                {
                    Log.Warning("[Deploy] Milking Deploy could not read available loot this wave");
                }
                else
                {
                    Log.Debug($"[Deploy] Wave {waveCounter} resources - G: {postLoot.Gold}, E: {postLoot.Elixir}, DE: {postLoot.DarkElixir}");
                    int newGold   = preLoot.Gold - postLoot.Gold;
                    int newElixir = preLoot.Elixir - postLoot.Elixir;
                    int newDark   = preLoot.DarkElixir - postLoot.DarkElixir;
                    Log.Debug($"[Deploy] Wave {waveCounter} resource diff - G: {newGold}, E: {newElixir}, DE: {newDark}, points: {deployPoints.Length}");
                    if (newGold + newElixir < 5000 * deployPoints.Length)
                    {
                        Log.Debug("Surrendering because gained resources isn't enough");
                        break;
                    }
                    preLoot = postLoot;
                }

                waveCounter++;
            }

            // Wait for the wave to finish
            Log.Info("[Deploy] Deploy done. Waiting to finish...");
            foreach (var t in Attack.WaitForNoResourceChange(10))
            {
                yield return(t);
            }

            if (spells.Any(u => u.Id == DeployId.Lightning) && DarkElixirDrill.Find(CacheBehavior.ForceScan, 4).Length > 0)
            {
                Log.Debug("Level 4 or greater drills found, waiting for attack to finish.");
                foreach (var t in Attack.WaitForNoResourceChange(5))
                {
                    yield return(t);
                }

                foreach (var t in ZapDarkElixirDrills())
                {
                    yield return(t);
                }
            }

            Attack.Surrender();
        }
Esempio n. 8
0
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info($"[{AttackName}] V{Version} Deploy start");

            // Get user settings
            ClanCastleSettings = GetCurrentSetting("use Clan Castle troops as");
            QWSettings         = GetCurrentSetting("Use Queen Walk");
            HealerOnQWSettings = GetCurrentSetting("Number of healers to use on Queen");
            RageOnQWSettings   = GetCurrentSetting("Drop 1 rage in the first of the QW");
            ShiftSpells        = GetCurrentSetting("Shift Spells In(+) and Out(-)");
            int customOrder = GetCurrentSetting("use custom deploy order");

            Debug = GetCurrentSetting("Debug Mode") == 1 ? true : false;

            // Set core point
            AllInOnePushHelper.SetCore();

            // Set the target
            var targetFromUserSettings = GetCurrentSetting("Select Your Target");

            foreach (var t in AllInOnePushHelper.SeTarget(targetFromUserSettings))
            {
                yield return(t);
            }

            // Set Origin
            var originPoints = new[]
            {
                new PointFT(GameGrid.DeployExtents.MaxX, Core.Y),
                new PointFT(GameGrid.DeployExtents.MinX, Core.Y),
                new PointFT(Core.X, GameGrid.DeployExtents.MaxY),
                new PointFT(Core.X, GameGrid.DeployExtents.MinY)
            };

            Origin = originPoints.OrderBy(point => point.DistanceSq(Target)).First();


            // Set deploy elements
            deployElements = Deploy.GetTroops(useCache: false);
            Log.Info($"[{AttackName}] Deployable Troops: " + ToUnitString(deployElements));

            // Check attack type (air or ground attack)
            AllInOnePushHelper.IsAirAttack();

            // Set deployment points
            AllInOnePushHelper.SetDeployPoints();

            // Set custom order list
            if (customOrder == 1)
            {
                CustomOrderList = new List <int>
                {
                    GetCurrentSetting("#1"),
                    GetCurrentSetting("#2"),
                    GetCurrentSetting("#3"),
                    GetCurrentSetting("#4"),
                    GetCurrentSetting("#5"),
                    GetCurrentSetting("#6")
                };
            }

            // Deploy troops
            if (!IsAirAttack)
            {
                Log.Info($"[{AttackName}] Ground attack start");
                foreach (var t in TroopsDeployment.GroundAttack(customOrder))
                {
                    yield return(t);
                }
            }
            else
            {
                Log.Info($"[{AttackName}] Air attack start");
                foreach (var t in TroopsDeployment.AirAttack(customOrder))
                {
                    yield return(t);
                }
            }

            yield break;
        }
        public override IEnumerable <int> AttackRoutine()
        {
#if DEBUG
            debugMode = true;
#endif
            var visuals = new List <VisualObject>();
            // Call it once in order to cache the RedPoints in the beginning
            // If this will be called after spell deployment the redline will disappear!! - Do not remove this!
            var redPoints = GameGrid.RedPoints;

            // get a list of all deployable units
            var deployElements = Deploy.GetTroops();
            Log.Info("[BabyLoon] Deployable Troops: " + ToUnitString(deployElements));
            if (!HasNeededTroops(deployElements))
            {
                Log.Error("[Smart Air] Couldn't find a known troop composition. Consider using one of the known troop compositions. Check our forums to learn more about " +
                          "the Smart Air Deploy in order to achieve the best possible results.");
                Surrender();
                yield break;
            }

            // extract special units into their own list under respect of usersettings
            var heroes = deployElements
                         .Extract(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                  (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                  (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops && UserSettings.UseClanTroops);

            // remove left special units so we won't use them accidentially
            deployElements.
            RemoveAll(u => u.ElementType == DeployElementType.ClanTroops ||
                      u.ElementType == DeployElementType.HeroKing ||
                      u.ElementType == DeployElementType.HeroQueen ||
                      u.ElementType == DeployElementType.HeroWarden);

            // extract units into their own lists in order to use them for more specific behaviours
            var lightningSpell  = deployElements.ExtractOne(x => x.Id == DeployId.Lightning);
            var earthQuakeSpell = deployElements.ExtractOne(x => x.Id == DeployId.Earthquake);
            var rageSpell       = deployElements.ExtractOne(x => x.Id == DeployId.Rage);
            var freezeSpels     = deployElements.ExtractOne(x => x.Id == DeployId.Freeze);
            var tanks           = deployElements.Extract(AttackType.Tank).ToArray();
            var balloon         = deployElements.ExtractOne(x => x.Id == DeployId.Balloon);
            var babyDragon      = deployElements.ExtractOne(x => x.Id == DeployId.BabyDragon);
            var wallBreakers    = deployElements.ExtractOne(x => x.Id == DeployId.WallBreaker);
            var damageDealers   = deployElements.Extract(AttackType.Damage).OrderByDescending(x => x.UnitData.HP).ToArray();

            // 1. Find the redpoint which is the closest to the TownHall
            var townHall          = TownHall.Find();
            var townHallPosition  = townHall == null ? new PointFT(0, 0) : townHall.Location.GetCenter();
            var closestThRedPoint = GameGrid.RedPoints.OrderBy(x => x.DistanceSq(townHallPosition)).First();
            visuals.Add(new PointsObject("Closest TH Point", Color.FromArgb(200, Color.Cyan), new PointFT[] { closestThRedPoint }));

            // 2. Prepare DeployPoint Calculation
            var    babyDragonSpread = babyDragon?.Count * 0.031 ?? 0;
            var    spreadFactor     = 0.7 + babyDragonSpread;
            double minAngle         = closestThRedPoint.Angle - spreadFactor;
            double maxAngle         = closestThRedPoint.Angle + spreadFactor;
            double twoPi            = 2 * Math.PI;
            double circleStart      = -Math.PI;
            double circleEnd        = Math.PI;
            if (minAngle < -Math.PI)
            {
                circleEnd = minAngle + twoPi;
                minAngle  = -Math.PI;
            }
            else if (maxAngle > Math.PI)
            {
                circleStart = maxAngle - twoPi;
                maxAngle    = Math.PI;
            }

            // 1. Condition: All Points between minAngle and MaxAngle
            // OR
            // 2. Condition: All Points between 0 and circleStart
            // OR
            // 3. Condition: All Points between circleEnd and twoPi
            var potentialDeployPoints = GameGrid.RedPoints.Where(x => x.Angle >= minAngle && x.Angle <= maxAngle ||
                                                                 x.Angle >= -Math.PI && x.Angle <circleStart ||
                                                                                                 x.Angle> circleEnd && x.Angle <= Math.PI).ToArray();
            potentialDeployPoints = OrderDeployPoints(potentialDeployPoints);
            visuals.Add(new PointsObject("Potential DeployPoints", Color.FromArgb(100, Color.Red), potentialDeployPoints, 1));

            // 3. If possible - zapquake 2nd closest AirDefense
            if (earthQuakeSpell?.Count >= 1 && lightningSpell?.Count >= 2)
            {
                var target = FindZapQuakeTarget(closestThRedPoint, visuals);
                if (target != null)
                {
                    Log.Info("[BabyLoon] Going to ZapQuake second closest AirDefense from deployside");
                    foreach (var t in ZapQuakeTarget(target.Location.GetCenter(), earthQuakeSpell, lightningSpell))
                    {
                        yield return(t);
                    }
                }
                else
                {
                    Log.Info("[BabyLoon] Couldn't find more than 1 AirDefense - hence skipping ZapQuake");
                }
            }
            else
            {
                Log.Debug("[BabyLoon] Not enough spells for ZapQuake available");
            }


            // 4. Calculate all deployPoints
            var babyDragonDeployPoints = CalculateBabyDragonDeployPoints(babyDragon, potentialDeployPoints, visuals);
            var balloonDeployPoints    = CalculateBalloonDeployPoints(balloon, potentialDeployPoints, visuals);
            var rageDeployPoints       = CalculateRageDeployPoints(rageSpell, potentialDeployPoints, visuals);
            var tankDeployPoints       = CalculateTankDeployPoints(tanks, potentialDeployPoints, visuals);
            // Deploy Clancastle 2 tiles away from the closestThRedPoint
            var clanCastleDeployPoint = closestThRedPoint.TransformPositionAlongAxis(2).Constrain();

            // 5. Deploy Tanks and ClanCastle in the center of the DeploySide
            if (tanks.Length > 0)
            {
                foreach (var tank in tanks)
                {
                    Log.Info($"[BabyLoon] Deploying {tank.PrettyName} x{tank.Count}");
                }

                // If there is just one tank use closestThRedPoint as deploypoint
                if (tanks.Sum(x => x.Count) == 1)
                {
                    while (tanks.Sum(x => x.Count) > 0)
                    {
                        var initialTroopCount = tanks.Sum(x => x.Count);
                        foreach (var t in Deploy.AtPoint(tanks, closestThRedPoint))
                        {
                            yield return(t);
                        }

                        // Prevent an infinite loop if deploy point is inside of the redzone
                        if (tanks.Sum(x => x.Count) != initialTroopCount)
                        {
                            continue;
                        }

                        foreach (var tank in tanks)
                        {
                            Log.Warning($"[BabyLoon] Couldn't deploy x{tank.Count} {tank.PrettyName}");
                        }
                        break;
                    }
                }

                // If there is more than one tank to deploy, deploy them along the attacking side
                while (tanks.Sum(x => x.Count) > 0)
                {
                    var initialTroopCount = tanks.Sum(x => x.Count);
                    foreach (var t in Deploy.AtPoints(tanks, tankDeployPoints))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (tanks.Sum(x => x.Count) != initialTroopCount)
                    {
                        continue;
                    }

                    foreach (var tank in tanks)
                    {
                        Log.Warning($"[BabyLoon] Couldn't deploy x{tank.Count} {tank.PrettyName}");
                    }
                    break;
                }
            }

            // 6. Deploy BabyDragons
            if (babyDragon?.Count > 0)
            {
                Log.Info($"[BabyLoon] Deploying {babyDragon.PrettyName} x{babyDragon.Count}");

                while (babyDragon.Count > 0)
                {
                    var initialBabyDragonCount = babyDragon.Count;
                    foreach (var t in Deploy.AtPoints(babyDragon, babyDragonDeployPoints, 1, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy points are inside of the redzone
                    if (babyDragon.Count != initialBabyDragonCount)
                    {
                        continue;
                    }

                    Log.Warning($"[BabyLoon] Couldn't deploy {babyDragon.PrettyName}");
                    break;
                }

                // Humanizing pause
                yield return(500);
            }

            // 7. Deploy balloons
            if (balloon?.Count > 0)
            {
                Log.Debug($"[BabyLoon] Wait 2 seconds before deploying {balloon.PrettyName} x{balloon.Count}!");
                yield return(2000);

                Log.Info($"[BabyLoon] Deploying {balloon.PrettyName} x{balloon.Count}");

                while (balloon.Count > 0)
                {
                    var initialBalloonCount = balloon.Count;
                    foreach (var t in Deploy.AtPoints(balloon, balloonDeployPoints, 1, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy points are inside of the redzone
                    if (balloon.Count != initialBalloonCount)
                    {
                        continue;
                    }

                    Log.Warning($"[BabyLoon] Couldn't deploy {balloon.PrettyName}");
                    break;
                }
            }

            // 8. Deploy Ragespells
            if (rageSpell?.Count > 0)
            {
                Log.Info($"[BabyLoon] Deploying {rageSpell.PrettyName} x{rageSpell.Count}");

                while (rageSpell.Count > 0)
                {
                    var initialRageSpellCount = rageSpell.Count;
                    foreach (var t in Deploy.AtPoints(rageSpell, rageDeployPoints, 1, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy points are inside of the redzone
                    if (balloon.Count != initialRageSpellCount)
                    {
                        continue;
                    }

                    Log.Warning($"[BabyLoon] Couldn't deploy {rageSpell.PrettyName}");
                    break;
                }
            }

            // 9. Deploy all other troops
            if (damageDealers.Length > 0)
            {
                foreach (var damagedealer in damageDealers)
                {
                    Log.Info($"[BabyLoon] Deploying {damagedealer.PrettyName} x{damagedealer.Count}");
                }
                while (damageDealers.Sum(x => x.Count) > 0)
                {
                    var initialTroopCount = damageDealers.Sum(x => x.Count);
                    foreach (var t in Deploy.AtPoint(damageDealers, closestThRedPoint))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (tanks.Sum(x => x.Count) != initialTroopCount)
                    {
                        continue;
                    }

                    foreach (var damagedealer in damageDealers)
                    {
                        Log.Warning($"[BabyLoon] Couldn't deploy x{damagedealer.Count} {damagedealer.PrettyName}");
                    }
                    break;
                }
            }

            // 10. Deploy Heroes and watch them to activate abilities
            if (heroes.Any())
            {
                var heroDeployPoints = GameGrid.RedPoints.OrderBy(x => x.DistanceSq(closestThRedPoint)).Take(10).ToArray();
                heroDeployPoints = heroDeployPoints.Select(x => x.TransformPositionAlongAxis(4).Constrain()).ToArray();

                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[BabyLoon] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoints(hero, heroDeployPoints))
                    {
                        yield return(t);
                    }
                }

                Deploy.WatchHeroes(heroes, 7000);
            }

            // 11. Deploy Wallbreakers in 3 unit stacks near heroes
            while (wallBreakers?.Count > 0)
            {
                // Shift Heroes' deploypoint by 1 tile in both directions to prevent deployment on the same spot (wallbreakers may die due to splashdamage against heroes)
                var wallbreakerDeployPoints = GameGrid.RedPoints.OrderBy(x => x.DistanceSq(closestThRedPoint)).Take(15).ToArray();
                var count = wallBreakers.Count;

                Log.Info($"[BabyLoon] Deploying {wallBreakers.PrettyName} x3");
                foreach (var t in Deploy.AtPoints(wallBreakers, wallbreakerDeployPoints, 3))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (wallBreakers.Count != count)
                {
                    continue;
                }

                Log.Warning($"[BabyLoon] Couldn't deploy {wallBreakers.PrettyName}");
                break;
            }

            if (clanCastle?.Count > 0)
            {
                Log.Info($"[BabyLoon] Deploying {clanCastle.PrettyName}");
                foreach (var t in Deploy.AtPoint(clanCastle, clanCastleDeployPoint))
                {
                    yield return(t);
                }
            }


            if (debugMode)
            {
                VisualizeDeployment(visuals);
            }
        }
Esempio n. 10
0
        public override IEnumerable <int> AttackRoutine()
        {
            deployPoints = DeployHelper.GetRectPoints(15);

            OrderPointsClockwise(deployPoints);

            if (deployPoints == null)
            {
                throw new ArgumentNullException("deployPoints");
            }
            if (deployPoints.Count == 0)
            {
                throw new ArgumentException("deployPoints must contain at least one point");
            }

            // Get outer border to move ranges towards there if possible
            List <Point> outerBorderPoints = DeployHelper.GetRectPoints(15);

            if (_surrenderOnFirstStar)
            {
                Logger.Info("[Deploy] Bot will surrender as soon as the first star is reached to save troops (Trophy Push Mode)");
            }


            var allDeployElements = Deploy.GetTroops();

            var heroes = allDeployElements
                         .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            int    deployPointCounter = 0;
            int    waveLimit          = UserSettings.WaveSize;
            double waitTimeSeconds    = UserSettings.WaveDelay;
            Random rng = new Random();

            while (true)
            {
                Logger.Debug("Scan Troops");
                // Scan available troops
                var units = Deploy.GetTroops()
                            .Where(u => u.ElementType == DeployElementType.NormalUnit ||
                                   (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                   (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                   (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden) ||
                                   (UserSettings.UseClanTroops && u.ElementType == DeployElementType.ClanTroops))
                            .ToList();

                // Remove king/queen
                ExtractHeroes(units, heroes);

                Logger.DebugDev("Deployable Troops: " + ToUnitString(units));
                // Break if we don't have any left
                if (units.Count == 0 || units.All(u => u.Count == 0))
                {
                    break;
                }


                // Order units by priority
                // Tank > Wallbreaker > Heal > Damage > Heroes
                Logger.Debug("OrderTroops");
                units.OrderForDeploy();
                int waveCounter = 0;
                foreach (var u in units)
                {
                    if (u == null)
                    {
                        Logger.Info("Wave #{0} complete, short deploy delay now...", waveCounter);
                        yield return(rng.Next(900, 2000));

                        continue;
                    }

                    if (u.Count == 0)
                    {
                        continue;
                    }

                    // Select unit
                    Logger.Debug("[Deploy] Deploying '{0}'", u);
                    u.Select();

                    // Deploy them
                    while (true)
                    {
                        int unitCount             = u.Count;
                        int totalDeployedThisWave = 0;
                        while (unitCount > 0)
                        {
                            //var line = deployLines[lineCounter++ % deployLines.Length];
                            //
                            //DeployHelper.ClickAlongLine(line.Item1, line.Item2, deployCount, 10);

                            var deployCount = unitCount;
                            Logger.Debug("Deploy Start");
                            for (int i = 0; i < deployCount; i++)
                            {
                                if (_surrenderOnFirstStar)
                                {
                                    if (SurrenderIfWeHaveAStar())
                                    {
                                        yield return(500);

                                        yield break;
                                    }
                                }

                                if (deployPointCounter >= deployPoints.Count)
                                {
                                    deployPointCounter = 0;
                                }

                                Logger.Debug("deploy at point index {0} of {1}", deployPointCounter, deployPoints.Count);

                                // We want to deploy our units evenly around those points.
                                // Lets say we have 10 units, 100 points.
                                // Then we deploy a unit at 0, 10, 20, ...
                                double fraction = 1.618034;
                                int    index    = (int)((deployPointCounter * fraction * deployPoints.Count) % deployPoints.Count);
                                deployPointCounter++;

                                var point = deployPoints[index];


                                // If this unit is ranged, we deploy further back
                                if (u.IsRanged)
                                {
                                    var borderPoint = outerBorderPoints.OrderBy(p => p.DistanceSq(point)).First();
                                    var distance    = (int)Math.Sqrt(borderPoint.DistanceSq(point));
                                    if (distance > 10)
                                    {
                                        var maxMove = Math.Min(u.UnitData.Range * 16, distance);
                                        var dir     = borderPoint.Normalize();
                                        // Clamp the distance to the max move distance so we dont deploy too far behind
                                        borderPoint = new Point((int)(dir.Item1 * maxMove) + point.X, (int)(dir.Item2 * maxMove) + point.Y);
                                        var t = (float)rng.Range(0.85, 1.05);
                                        point = point.Lerp(borderPoint, t);
                                    }
                                }

                                // Modify this point a bit so its not too ovbious
                                point.X += rng.Next(-12, 12);
                                point.Y += rng.Next(-12, 12);

                                Input.Click(point);
                                totalDeployedThisWave++;
                                if (totalDeployedThisWave >= waveLimit)
                                {
                                    Logger.Info("WaveLimit {0} reached. Wait {1:0.0} sec.", waveLimit, waitTimeSeconds);
                                    yield return((int)(waitTimeSeconds * 1000));

                                    totalDeployedThisWave = 0;
                                }

                                Thread.Sleep(10);
                                Thread.Sleep(5);
                            }
                            Logger.Debug("Deploy End");

                            unitCount -= deployCount;
                        }

                        // Refresh unit count, if its really 0, break.
                        Logger.Debug("RecountA");
                        int countA = u.Count;
                        u.Recount();
                        int countB = u.Count;
                        Logger.Debug("RecountB");

                        if (countA != countB)
                        {
                            Logger.Info("Recount of '{0}'. {1}->{2}", u.PrettyName, countA, countB);
                        }

                        if (u.Count <= 0)
                        {
                            Logger.Info("Unit '{0}' depleted. Break.", u.PrettyName);
                            yield return(500);

                            break;
                        }
                    }


                    waveCounter++;
                }
                yield return(50);
            }

            if (heroes.Count > 0)
            {
                foreach (var y in DeployHeroes(heroes, deployPoints))
                {
                    if (_surrenderOnFirstStar)
                    {
                        if (SurrenderIfWeHaveAStar())
                        {
                            break;
                        }
                    }

                    yield return(y);
                }
            }

            Logger.Info("[Deploy] Deploy done.");
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[Breakthrough] Deploy start");

            var funnelIds  = new[] { DeployId.Archer, DeployId.Barbarian, DeployId.Minion, DeployId.Wizard };
            var byLineIds  = new[] { DeployId.Archer, DeployId.Barbarian, DeployId.Minion, DeployId.Wizard, DeployId.Balloon, DeployId.Dragon, DeployId.BabyDragon, DeployId.Miner };
            var byPointIds = new[] { DeployId.Valkyrie, DeployId.Pekka, DeployId.Witch, DeployId.Goblin, DeployId.Bowler };

            // get a list of all deployable units
            var deployElements = Deploy.GetTroops();

            // extract spells into their own list
            var spells = deployElements.Extract(DeployElementType.Spell);

            // extract heores into their own list
            var heroes = deployElements.Extract(u => u.IsHero);

            // extract clanCastle into its own list
            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops);

            // get tanks
            var tanks = deployElements.Extract(AttackType.Tank).ToArray();

            // get wallbreakers
            var wallBreakers = deployElements.ExtractOne(DeployId.WallBreaker);

            // get healers
            var healers = deployElements.ExtractOne(DeployId.Healer);

            // get funnel troops
            var funnel = funnelIds.Select(id => deployElements.FirstOrDefault(u => u.Id == id)).Where(u => u != null).ToArray();

            // get deploy all in a line
            var byLine = deployElements.Extract(byLineIds).ToArray();

            // get deploy all by point
            var byPoint = deployElements.Extract(byPointIds).ToArray();

            // get hogs
            var hogs = deployElements.ExtractOne(u => u.Id == DeployId.HogRider);

            // get heal spells
            var healSpells = spells.ExtractOne(u => u.Id == DeployId.Heal);

            // get rage spells
            var rageSpells = spells.ExtractOne(u => u.Id == DeployId.Rage);

            // user's wave delay setting
            var waveDelay = (int)(UserSettings.WaveDelay * 1000);

            // check if queen walk is an option
            if (heroes.Any(u => u.Id == DeployId.Queen) && healers?.Count >= 4)
            {
                var queen = heroes.ExtractOne(u => u.Id == DeployId.Queen);

                // get deploy points with queen walk
                CreateDeployPoints(true);

                // deploy queen walk
                Log.Info("[Breakthrough] Queen walk available.");
                Log.Info($"[Breakthrough] Deploying {queen.PrettyName}");
                foreach (var t in Deploy.AtPoint(queen, _qwPoint, waveDelay: waveDelay))
                {
                    yield return(t);
                }

                var healerCount = Math.Min(healers.Count, 4);
                Log.Info($"[Breakthrough] Deploying {healers.PrettyName} x{healerCount}");
                foreach (var t in Deploy.AtPoint(healers, _healerPoint, healerCount, waveDelay: waveDelay))
                {
                    yield return(t);
                }

                // watch queen
                Deploy.WatchHeroes(new List <DeployElement> {
                    queen
                });

                if (rageSpells?.Count > 1)
                {
                    Log.Info($"[Breakthrough] Deploying {rageSpells.PrettyName} x1");
                    foreach (var t in Deploy.AtPoint(rageSpells, _queenRagePoint, waveDelay: waveDelay))
                    {
                        yield return(t);
                    }
                }

                // wait 15 seconds
                yield return(15000);
            }
            else
            {
                // get deploy points without queen walk
                CreateDeployPoints(false);
            }

            var funnelTank = tanks.FirstOrDefault(u => u.Id == DeployId.Giant) ?? tanks.FirstOrDefault();

            // deploy four tanks if available
            if (funnelTank != null)
            {
                var deployCount = Math.Min(funnelTank.Count, 4);

                Log.Info($"[Breakthrough] Deploying {funnelTank.PrettyName} x{deployCount}");
                foreach (var t in Deploy.AlongLine(funnelTank, _attackLine.Item1, _attackLine.Item2, deployCount,
                                                   deployCount, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            // deploy funnel
            foreach (var unit in funnel.Where(u => u.Count > 0))
            {
                var deployElementCount = Math.Min(unit.Count, UserSettings.WaveSize / unit.UnitData.HousingSpace);

                Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{deployElementCount}");
                foreach (
                    var t in
                    Deploy.AlongLine(unit, _attackLine.Item1, _attackLine.Item2, deployElementCount, 4,
                                     waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            // deploy Wallbreakers
            while (wallBreakers?.Count > 0)
            {
                var count = wallBreakers.Count;

                Log.Info($"[Breakthrough] Deploying {wallBreakers.PrettyName} x3");
                foreach (var t in Deploy.AtPoint(wallBreakers, _orgin, 3))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (wallBreakers.Count != count)
                {
                    continue;
                }

                Log.Warning($"[Breakthrough] Couldn't deploy {wallBreakers.PrettyName}");
                break;
            }


            // deploy the rest of the tanks
            while (tanks.Any(u => u.Count > 0))
            {
                var deployError = false;

                foreach (var unit in tanks.Where(u => u.Count > 0))
                {
                    var count = unit.Count;

                    Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{unit.Count}");
                    foreach (var t in Deploy.AtPoint(unit, _orgin, unit.Count, waveDelay: waveDelay))
                    {
                        yield return(t);
                    }

                    // prevent infinite loop if deploy point is on red
                    if (unit.Count != count)
                    {
                        continue;
                    }

                    Log.Warning($"[Breakthrough] Couldn't deploy {unit.PrettyName}");
                    deployError = true;
                    break;
                }
                if (deployError)
                {
                    break;
                }
            }

            if (rageSpells?.Count > 0)
            {
                Log.Info($"[Breakthrough] Deploying {rageSpells.PrettyName} x1");
                foreach (var t in Deploy.AtPoint(rageSpells, _ragePoint, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            if (healSpells?.Count > 0)
            {
                Log.Info($"[Breakthrough] Deploying {healSpells.PrettyName} x1");
                foreach (var t in Deploy.AtPoint(healSpells, _healPoint, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            while (byLine.Any(u => u.Count > 0))
            {
                foreach (var unit in byLine.Where(u => u.Count > 0))
                {
                    Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{unit.Count}");
                    foreach (
                        var t in
                        Deploy.AlongLine(unit, _attackLine.Item1, _attackLine.Item2, unit.Count, 4,
                                         waveDelay: waveDelay))
                    {
                        yield return(t);
                    }
                }
            }

            while (byPoint.Any(u => u.Count > 0))
            {
                var deployError = false;

                foreach (var unit in byPoint.Where(u => u.Count > 0))
                {
                    var count = unit.Count;

                    Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{unit.Count}");
                    foreach (var t in Deploy.AtPoint(unit, _orgin, unit.Count, waveDelay: waveDelay))
                    {
                        yield return(t);
                    }

                    // prevent infinite loop if deploy point is on red
                    if (unit.Count != count)
                    {
                        continue;
                    }

                    Log.Warning($"[Breakthrough] Couldn't deploy {unit.PrettyName}");
                    deployError = true;
                    break;
                }
                if (deployError)
                {
                    break;
                }
            }

            if (clanCastle?.Count > 0)
            {
                Log.Info($"[Breakthrough] Deploying {clanCastle.PrettyName}");
                foreach (var t in Deploy.AtPoint(clanCastle, _orgin, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            if (heroes.Any())
            {
                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[Breakthrough] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoint(hero, _orgin, waveDelay: waveDelay))
                    {
                        yield return(t);
                    }
                }
            }

            if (healers?.Count > 0)
            {
                Log.Info($"[Breakthrough] Deploying {healers.PrettyName} x{healers.Count}");
                foreach (var t in Deploy.AtPoint(healers, _healerPoint, healers.Count, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            if (hogs?.Count > 0)
            {
                Log.Info($"[Breakthrough] Deploying {hogs.PrettyName} x{hogs.Count}");
                foreach (var t in Deploy.AtPoint(hogs, _orgin, hogs.Count, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            Deploy.WatchHeroes(heroes);

            // get freeze spells
            _freezeSpell = spells.ExtractOne(u => u.Id == DeployId.Freeze);

            // no freeze spells so end deployment
            if (!(_freezeSpell?.Count > 0))
            {
                yield break;
            }

            // find and watch inferno towers
            var infernos = InfernoTower.Find();

            foreach (var inferno in infernos)
            {
                inferno.FirstActivated += DropFreeze;

                inferno.StartWatching();
            }
        }
Esempio n. 12
0
        public override IEnumerable <int> AttackRoutine()
        {
            int waveLimit   = UserSettings.WaveSize;
            int waveDelay   = (int)(UserSettings.WaveDelay * 1000);
            int heroesIndex = -1;

            var core = new PointFT(-0.01f, 0.01f);

            // Points to draw lines in deploy extends area.
            var topLeft  = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MaxY);
            var topRight = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MaxY - 2);

            var rightTop    = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MinY + 2);
            var rightBottom = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MinY);

            // Move 8 tiles from bottom corner due to unitsbar.
            var bottomLeft  = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MinY + 8);
            var bottomRight = new PointFT((float)GameGrid.MinX + 8, (float)GameGrid.DeployExtents.MinY);

            var leftTop    = new PointFT((float)GameGrid.MinX + 2, (float)GameGrid.DeployExtents.MaxY);
            var leftBottom = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MaxY - 2);

            var linesPointsList = new List <PointFT>
            {
                topLeft, topRight,
                rightTop, rightBottom,
                bottomLeft, bottomRight,
                leftBottom, leftTop
            };

            // Main four lines of attack.
            var topRightLine    = new Tuple <PointFT, PointFT>(topRight, rightTop);
            var bottomRightLine = new Tuple <PointFT, PointFT>(bottomRight, rightBottom);
            var bottomLeftLine  = new Tuple <PointFT, PointFT>(bottomLeft, leftBottom);
            var topLeftLine     = new Tuple <PointFT, PointFT>(topLeft, leftTop);

            // List of the four attack lines in clocwise order
            var attackLines = new List <Tuple <PointFT, PointFT> >
            {
                topLeftLine,
                topRightLine,
                bottomRightLine,
                bottomLeftLine
            };

            var deployHeroesAt = GetCurrentSetting("Deploy Heroes At");


            var target = SmartFourFingersHelper.GetHeroesTarget(deployHeroesAt);

            var nearestRedPointToTarget = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(target)).FirstOrDefault();
            var nearestLinePoint        = linesPointsList.OrderBy(p => p.DistanceSq(nearestRedPointToTarget)).FirstOrDefault();

            heroesIndex = attackLines.FindIndex(u => (u.Item1.X == nearestLinePoint.X && u.Item1.Y == nearestLinePoint.Y) || (u.Item2.X == nearestLinePoint.X && u.Item2.Y == nearestLinePoint.Y));

            var units  = Deploy.GetTroops();
            var heroes = units.Extract(x => x.IsHero);
            var cc     = units.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops);
            var spells = units.Extract(u => u.ElementType == DeployElementType.Spell);

            units.OrderForDeploy();

            // Set first attack line
            // Start from the next line to user defined to end with user defined line
            var line  = attackLines.NextOf(attackLines[heroesIndex]);
            var index = attackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y);

            Log.Info($"{AttackName} {Version} starts");
            // Start troops deployment on four sides.
            for (var i = 4; i >= 1; i--)
            {
                foreach (var unit in units)
                {
                    if (unit?.Count > 0)
                    {
                        var count   = unit.Count / i;
                        var fingers = count % 4 <= 1 ? count : 4;
                        foreach (var t in Deploy.AlongLine(unit, line.Item1, line.Item2, count, fingers, 0, waveDelay))
                        {
                            yield return(t);
                        }
                    }
                }
                if (i != 1)
                {
                    line  = attackLines.NextOf(attackLines[index]);
                    index = attackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y);
                }
            }

            if (cc?.Count > 0)
            {
                Log.Info($"{AttackName} Deploy Clan Castle troops");
                foreach (var t in Deploy.AlongLine(cc, line.Item1, line.Item2, 1, 1, 0, waveDelay))
                {
                    yield return(t);
                }
            }

            if (heroes.Any())
            {
                Log.Info($"{AttackName} Deploy Heroes");
                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    foreach (var t in Deploy.AlongLine(hero, line.Item1, line.Item2, 1, 1, 0, waveDelay))
                    {
                        yield return(t);
                    }
                }
                Deploy.WatchHeroes(heroes, 5000);
            }


            var minDEDrillLevel = GetCurrentSetting("Min Drill Level");

            // start smart zap
            if (GetCurrentSetting("Smart Zap Drills") == 1)
            {
                var waitBeforeSmartZap = GetCurrentSetting("Start Zap Drills After ?(sec)") * 1000;
                var minDEAmount        = GetCurrentSetting("Min Dark Elixir per Zap");


                yield return(waitBeforeSmartZap);

                foreach (var t in SmartZapping.SmartZap(minDEAmount, minDEDrillLevel, spells))
                {
                    yield return(t);
                }
            }

            // start Use EarthQuake spell on drills
            if (GetCurrentSetting("Use EarthQuake spell on drills") == 1)
            {
                foreach (var t in SmartZapping.UseEQOnDrills(minDEDrillLevel, spells))
                {
                    yield return(t);
                }
            }

            // end battle
            var endBattleTime = GetCurrentSetting("End Battle after zap ?(sec)");

            foreach (var t in SmartZapping.EndBattle(endBattleTime))
            {
                yield return(t);
            }
        }
Esempio n. 13
0
        public override double ShouldAccept()
        {
            if (!PassesBasicAcceptRequirements())
            {
                return(0);
            }

            //TODO - Check which kind of army we have trained. Calculate an Air Offense Score, and Ground Offense Score.

            //TODO - Find all Base Defenses, and calculate an AIR and Ground Defensive Score.

            //TODO - From Collector/Storage fill levels, determine if loot is in Collectors, or Storages... (Will help to decide which alg to use.)

            //Verify that the Attacking Army contains at least 6 Dragons.
            deployElements = Deploy.GetTroops();
            var dragons = deployElements.FirstOrDefault(u => u.Id == DeployId.Dragon);

            if (dragons == null || dragons?.Count < 6)
            {
                Log.Error($"{Tag} Army not correct! - Dark Dragon Deploy Requires at least 6 Dragons to function Properly. (You have {dragons?.Count ?? 0} dragons)");
                return(0);
            }

            //Verify that there are enough spells to take out at least ONE air defense.
            var lightningSpells = deployElements.FirstOrDefault(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Lightning);
            List <DeployElement> earthquakeSpells = deployElements.Where(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Earthquake).ToList();

            var lightningCount  = lightningSpells?.Count ?? 0;
            var earthquakeCount = 0;

            //Get a count of all earthquake spells... donated, or brewed...
            foreach (var spell in earthquakeSpells.Where(s => s.Count > 0))
            {
                earthquakeCount += spell.Count;
            }

            if (lightningCount < 2 || lightningCount < 3 && earthquakeCount < 1)
            {
                //We dont have the Spells to take out the Closest Air Defense... Surrender before we drop any Dragons!
                Log.Error($"{Tag} We don't have enough spells to take out at least 1 air defense... Lightning Spells:{lightningCount}, Earthquake Spells:{earthquakeCount}");
                return(0);
            }

            if (deployElements.Count >= 11)
            {
                //Possibly Too Many Deployment Elements!  Bot Doesnt Scroll - Change Army Composition to have less than 12 unit types!
                Log.Warning($"{Tag} Warning! Full Army! - The Bot does not scroll through choices when deploying units... If your army has more than 11 unit types, The bot will not see them all, and cannot deploy everything!)");
            }

            //Write out all the unit pretty names we found...
            Log.Debug($"{Tag} Deployable Troops: {ToUnitString(deployElements)}");

            Log.Info($"{Tag} Base meets minimum Requirements... Checking DE Storage/Air Defense Locations...");

            //Grab the Locations of the DE Storage
            darkElixirStorage = HumanLikeAlgorithms.TargetDarkElixirStorage();

            if (!darkElixirStorage.ValidTarget)
            {
                Log.Warning($"{Tag} No Dark Elixir Storage Found - Skipping");
                return(0);
            }

            //Get the locaiton of all Air Defenses
            var airDefensesTest = AirDefense.Find();

            if (airDefensesTest.Length == 0)
            {
                Log.Warning($"{Tag} Could not find ANY air defenses - Skipping");
                return(0);
            }

            Log.Info($"{Tag} Found {airDefensesTest.Length} Air Defense Buildings.. Continuing Attack..");

            if (airDefensesTest.Length > 1)
            {
                //Now that we found all Air Defenses, order them in the array with closest AD to Target first.
                Array.Sort(airDefensesTest, delegate(AirDefense ad1, AirDefense ad2)
                {
                    return(HumanLikeAlgorithms.DistanceFromPoint(ad1, darkElixirStorage.DeployGrunts)
                           .CompareTo(HumanLikeAlgorithms.DistanceFromPoint(ad2, darkElixirStorage.DeployGrunts)));
                });
            }

            //Create the Funnel Points
            deFunnelPoints      = darkElixirStorage.GetFunnelingPoints(30);
            balloonFunnelPoints = darkElixirStorage.GetFunnelingPoints(20);

#if DEBUG
            //During Debug, Create an Image of the base including what we found.
            CreateDebugImages();
#endif

            //We are Good to attack!
            return(1);
        }
        public override IEnumerable <int> AttackRoutine()
        {
            var allDeployElements = Deploy.GetTroops();

            List <Point> redPoints = new List <Point>();

            foreach (var s in AttackHelper.FindRedPoints(redPoints))
            {
                yield return(s);
            }

            var leftCorner = DeployHelper.DeployPointALeft;
            var topCorner  = DeployHelper.DeployPointATop;

            double tankPaddingFraction = 0.35;
            var    tankDeployA         = leftCorner.Lerp(topCorner, tankPaddingFraction);
            var    tankDeployB         = leftCorner.Lerp(topCorner, 1 - tankPaddingFraction);

            double wallBreakerFraction = 0.43;
            var    wallBreakDeployA    = leftCorner.Lerp(topCorner, wallBreakerFraction);
            var    wallBreakDeployB    = leftCorner.Lerp(topCorner, 1 - wallBreakerFraction);

            double damageFraction     = 0.15;
            var    damageDeployPointA = leftCorner.Lerp(topCorner, damageFraction);
            var    damageDeployPointB = leftCorner.Lerp(topCorner, 1 - damageFraction);

            // We want to drag our deploy points to the closest redline point
            // For that we get the vector from left to top, then we get its orthogonal vector (the one of both that points more towards the center)
            // then we can order the redline points. we want the one whos direction is closest to the orthogonal vector.
            var vecLeftTop = topCorner.Subtract(leftCorner).Normalize();
            var ortho      = GetOrthogonalDirection(tankDeployA, new Point((int)(vecLeftTop.Item1 * 30), (int)(vecLeftTop.Item2 * 30)), new PointF(0.5f, 0.5f).ToAbsolute());

            tankDeployA = GetBestRedlinePoint(redPoints, tankDeployA, ortho);
            tankDeployB = GetBestRedlinePoint(redPoints, tankDeployB, ortho);

            wallBreakDeployA = GetBestRedlinePoint(redPoints, wallBreakDeployA, ortho);
            wallBreakDeployB = GetBestRedlinePoint(redPoints, wallBreakDeployB, ortho);


            var attackLine = DeployHelper.GetPointsForLine(damageDeployPointA, damageDeployPointB, 30).ToArray();

            for (int i = 0; i < attackLine.Length; i++)
            {
                attackLine[i] = GetBestRedlinePoint(redPoints, attackLine[i], ortho);
            }
            attackLine = attackLine.Distinct().ToArray();


            var validUnits = allDeployElements.Where(u => u.UnitData != null).ToArray();
            var clanTroops = allDeployElements.FirstOrDefault(u => u.ElementType == DeployElementType.ClanTroops);
            //Func<DeployElement, bool> isAir = e => e.UnitData.UnitType == UnitType.Air;
            Func <DeployElement, bool> isBalloon = e => e.UnitData.NameSimple.Contains("balloon");
            Func <DeployElement, bool> isMinion  = e => e.UnitData.NameSimple.Contains("minion");

            var tankUnits        = validUnits.Where(u => u.UnitData.AttackType == AttackType.Tank).ToArray();
            var wallBreakerUnits = validUnits.Where(u => u.UnitData.AttackType == AttackType.Wallbreak).ToArray();
            var attackUnits      = validUnits.Where(u => u.UnitData.AttackType == AttackType.Damage && !isMinion(u)).ToArray();

            if (clanTroops != null && UserSettings.UseClanTroops)
            {
                attackUnits = attackUnits.Concat(new[] { clanTroops }).ToArray();
            }
            var healUnits    = validUnits.Where(u => u.UnitData.AttackType == AttackType.Heal).ToArray();
            var balloonUnits = validUnits.Where(isBalloon).ToArray();
            var minionUnits  = validUnits.Where(isMinion).ToArray();

            // Deploy tank units
            if (tankUnits.Any())
            {
                Logger.Debug($"{tankUnits.Length} tanking element{(tankUnits.Length > 1 ? "s" : "")} available to deploy.");
                foreach (var s in DeployUnits(tankUnits, new[] { tankDeployA, tankDeployB }, 20))
                {
                    yield return(s);
                }
                yield return(2000);
            }

            // Deploy wallbreakers
            if (tankUnits.Any())
            {
                Logger.Debug("Wallbreakers available to deploy.");
                foreach (var s in DeployUnits(wallBreakerUnits, new[] { wallBreakDeployA, wallBreakDeployB }, 40))
                {
                    yield return(s);
                }
                yield return(1000);
            }

            // Check whether we got an air troopset and decide to perform an air attack or not
            var balloonCount = balloonUnits.Sum(i => i.Count);

            if (balloonCount > 10)
            {
                // Ok, we have an air troopset, so we will deploy the air units first according to different deploy rules.
                attackUnits = attackUnits.Where(u => !isBalloon(u)).ToArray();

                int spotCount = (int)Math.Ceiling(balloonCount / 4.0);
                // We want to make x spots where balloons are deployed
                var airPoints = DeployHelper.GetPointsForLine(damageDeployPointA, damageDeployPointB, spotCount);
                for (int i = 0; i < airPoints.Count; i++)
                {
                    airPoints[i] = GetBestRedlinePoint(redPoints, airPoints[i], ortho);
                }
                airPoints = airPoints.Distinct().ToList();

                // Deploy those air units
                foreach (var s in DeployUnits(balloonUnits, airPoints.ToArray(), firstCycleYield: 1000))
                {
                    yield return(s);
                }
            }

            // Deploy atackers
            if (attackUnits.Any())
            {
                Logger.Debug($"{attackUnits.Length} attacking element{(attackUnits.Length > 1 ? "s" : "")} available to deploy.");
                foreach (var s in DeployUnits(attackUnits, attackLine, 0, 5, 2500))
                {
                    yield return(s);
                }
                yield return(500);
            }

            // Minions
            if (minionUnits.Any())
            {
                foreach (var s in DeployUnits(minionUnits, attackLine))
                {
                    yield return(s);
                }
            }

            // Deploy healers
            foreach (var s in DeployUnits(healUnits, attackLine))
            {
                yield return(s);
            }

            // Deploy heroes
            var heroes = allDeployElements
                         .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            if (heroes.Count > 0)
            {
                foreach (var y in DeployHeroes(heroes, attackLine))
                {
                    yield return(y);
                }
            }
        }
Esempio n. 15
0
        public override IEnumerable <int> AttackRoutine()
        {
            List <Point> deployPoints = new List <Point>();

            Logger.Info("[Attack-DeadBase] Now deploying main attack wave.");
            var analysisPoints = new List <Point>();
            var mineRects      = new List <Rectangle>();
            var fiber          = new Fiber <int>(DeployHelper.GenerateDeployPointsFromMines(analysisPoints, RedPoints, mineRects));

            while (fiber.Run())
            {
                yield return(1);
            }
            deployPoints.AddRange(analysisPoints);

            // Debug: show deploy points
            using (var frame = Screenshot.Capture(true))
            {
                using (var g = Graphics.FromImage(frame))
                {
                    foreach (var p in RedPoints)
                    {
                        g.RectWithOutline(p.ToRectangle(2, 2), Color.Red);
                    }
                    foreach (var m in mineRects)
                    {
                        g.RectWithOutline(m, Color.DarkOrange);
                    }
                    foreach (var p in deployPoints)
                    {
                        g.RectWithOutline(p.ToRectangle(4, 4), Color.White);
                    }
                    foreach (var m in mineRects)
                    {
                        var closure = m;
                        foreach (var closest in deployPoints.OrderBy(p => p.DistanceSq(closure.GetCenter())).Take(2))
                        {
                            g.DrawLine(Pens.Red, m.GetCenter(), closest);
                        }
                    }
                }

                if (UserSettings.SaveAttackAnalysisImage)
                {
                    Screenshot.Save(frame, $"AttackAnalysis_Dead {deployPoints.Count} points {RedPoints.Count} red");
                }
                if (UserSettings.DisplayAttackAnalysisImage)
                {
                    Screenshot.Show(frame);
                }
            }


            _deployPoints = deployPoints.ToArray();


            if (_deployPoints == null)
            {
                throw new ArgumentNullException("deployPoints");
            }
            if (_deployPoints.Length == 0)
            {
                throw new ArgumentException("deployPoints must contain at least one point");
            }

            // Get outer border to move ranges towards there if possible
            List <Point> outerBorderPoints = DeployHelper.GetRectPoints(15);

            if (surrenderOnFirstStar)
            {
                Logger.Info("[Deploy] Bot will surrender as soon as the first star is reached to save troops (Trophy Push Mode)");
            }


            var allDeployElements = Deploy.GetTroops();

            var heroes = allDeployElements
                         .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            int    deployPointCounter = 0;
            int    waveLimit          = UserSettings.WaveSize;
            double waitTimeSeconds    = UserSettings.WaveDelay;
            Random rng = new Random();

            while (true)
            {
                Logger.Debug("Scan Troops");
                // Scan available troops
                var units = Deploy.GetTroops()
                            .Where(u => u.ElementType == DeployElementType.NormalUnit ||
                                   (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                   (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                   (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden) ||
                                   (UserSettings.UseClanTroops && u.ElementType == DeployElementType.ClanTroops))
                            .ToList();

                // Remove king/queen
                ExtractHeroes(units, heroes);

                Logger.DebugDev("Deployable Troops: " + ToUnitString(units));
                // Break if we don't have any left
                if (units.Count == 0 || units.All(u => u.Count == 0))
                {
                    break;
                }


                // Order units by priority
                // Tank > Wallbreaker > Heal > Damage > Heroes
                Logger.Debug("OrderTroops");
                units.OrderForDeploy();
                int waveCounter = 0;
                foreach (var u in units)
                {
                    if (u == null)
                    {
                        Logger.Info("Wave #{0} complete, short deploy delay now...", waveCounter);
                        yield return(rng.Next(900, 2000));

                        continue;
                    }

                    if (u.Count == 0)
                    {
                        continue;
                    }

                    // Select unit
                    Logger.Debug("[Deploy] Deploying '{0}'", u);
                    u.Select();

                    // Deploy them
                    while (true)
                    {
                        int unitCount             = u.Count;
                        int totalDeployedThisWave = 0;
                        while (unitCount > 0)
                        {
                            //var line = deployLines[lineCounter++ % deployLines.Length];
                            //
                            //DeployHelper.ClickAlongLine(line.Item1, line.Item2, deployCount, 10);

                            var deployCount = Math.Min(u.Count, 4);
                            Logger.Debug("Deploy Start");
                            for (int i = 0; i < deployCount; i++)
                            {
                                if (surrenderOnFirstStar)
                                {
                                    if (SurrenderIfWeHaveAStar())
                                    {
                                        yield return(500);

                                        yield break;
                                    }
                                }

                                if (deployPointCounter >= _deployPoints.Length)
                                {
                                    deployPointCounter = 0;
                                }

                                Logger.Debug("deploy at point index {0} of {1}", deployPointCounter, _deployPoints.Length);
                                var point = _deployPoints[deployPointCounter++];


                                // If this unit is ranged, we deploy further back
                                if (u.IsRanged)
                                {
                                    var borderPoint = outerBorderPoints.OrderBy(p => p.DistanceSq(point)).First();
                                    var distance    = (int)Math.Sqrt(borderPoint.DistanceSq(point));
                                    if (distance > 10)
                                    {
                                        var maxMove = Math.Min(u.UnitData.Range * 16, distance);
                                        var dir     = borderPoint.Normalize();
                                        // Clamp the distance to the max move distance so we dont deploy too far behind
                                        borderPoint = new Point((int)(dir.Item1 * maxMove) + point.X, (int)(dir.Item2 * maxMove) + point.Y);
                                        var t = (float)rng.Range(0.85, 1.05);
                                        point = point.Lerp(borderPoint, t);
                                    }
                                }

                                // Modify this point a bit so its not too ovbious
                                point.X += rng.Next(-12, 12);
                                point.Y += rng.Next(-12, 12);

                                Input.Click(point);
                                totalDeployedThisWave++;
                                if (totalDeployedThisWave >= waveLimit)
                                {
                                    Logger.Info("WaveLimit {0} reached. Wait {1:0.0} sec.", waveLimit, waitTimeSeconds);
                                    yield return((int)(waitTimeSeconds * 1000));

                                    totalDeployedThisWave = 0;
                                }

                                Thread.Sleep(10);
                                Thread.Sleep(5);
                            }
                            Logger.Debug("Deploy End");

                            unitCount -= deployCount;
                        }

                        // Refresh unit count, if its really 0, break.
                        Logger.Debug("RecountA");
                        int countA = u.Count;
                        u.Recount();
                        int countB = u.Count;
                        Logger.Debug("RecountB");

                        if (countA != countB)
                        {
                            Logger.Info("Recount of '{0}'. {1}->{2}", u.PrettyName, countA, countB);
                        }

                        if (u.Count <= 0)
                        {
                            Logger.Info("Unit '{0}' depleted. Break.", u.PrettyName);
                            yield return(500);

                            break;
                        }
                    }


                    waveCounter++;
                }
                yield return(50);
            }

            if (heroes.Count > 0)
            {
                foreach (var y in DeployHeroes(heroes, _deployPoints))
                {
                    if (surrenderOnFirstStar)
                    {
                        if (SurrenderIfWeHaveAStar())
                        {
                            break;
                        }
                    }

                    yield return(y);
                }
            }

            Logger.Info("[Deploy] Deploy done.");
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[Red Line] Attack start");

            var deployElements = Deploy.GetTroops();

            deployElements.Extract(DeployElementType.Spell);
            var heroes = deployElements.Extract(u => u.IsHero);
            var cc     = deployElements.ExtractOne(DeployId.ClanCastle);
            var ranged = deployElements.Extract(u => u.IsRanged);
            var units  = deployElements.OrderBy(u => u.UnitData?.AttackType != AttackType.Tank).ToList();

            var clockwisePoints = GameGrid.RedPoints
                                  .Where(
                point =>
                !(point.X > 18 && point.Y > 18 || point.X > 18 && point.Y < -18 || point.X < -18 && point.Y > 18 ||
                  point.X < -18 && point.Y < -18))
                                  .OrderBy(point => Math.Atan2(point.X, point.Y)).ToList();

            var inflatedPoints = clockwisePoints
                                 .Select(
                point =>
                new PointFT((float)(point.X + (point.X / Math.Sqrt(point.DistanceSq(new PointFT(0, 0)))) * 4),
                            (float)(point.Y + (point.Y / Math.Sqrt(point.DistanceSq(new PointFT(0, 0)))) * 4)))
                                 .ToList();

            while (units.Count > 0)
            {
                Log.Info("[Red Line] Deploying melee and tanking troops");
                foreach (var element in units)
                {
                    foreach (
                        var t in
                        Deploy.AtPoints(element,
                                        clockwisePoints.GetNth(GameGrid.RedPoints.Length / (double)element.Count).ToArray()))
                    {
                        yield return(t);
                    }
                }
                units.Recount();
                units.RemoveAll(unit => unit.Count < 1);
            }

            while (ranged.Count > 0)
            {
                Log.Info("[Red Line] Deploying ranged troops");
                foreach (var element in ranged)
                {
                    foreach (
                        var t in
                        Deploy.AtPoints(element,
                                        inflatedPoints.GetNth(GameGrid.RedPoints.Length / (double)element.Count).ToArray()))
                    {
                        yield return(t);
                    }
                }
                ranged.Recount();
                ranged.RemoveAll(unit => unit.Count < 1);
            }

            var pt = new Container <PointFT> {
                Item = new PointFT((float)GameGrid.MinX, GameGrid.MinY)
            };

            if (cc?.Count > 0 && UserSettings.UseClanTroops)
            {
                Log.Info($"[Red Line] Deploying {cc.PrettyName}");
                foreach (var y in Deploy.AtPoint(cc, pt))
                {
                    yield return(y);
                }
            }

            if (heroes.Any())
            {
                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[Red Line] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoint(hero, pt))
                    {
                        yield return(t);
                    }
                }
            }

            Deploy.WatchHeroes(heroes);

            Log.Info("[Red Line] Deploy done");
        }
Esempio n. 17
0
        public override IEnumerable <int> AttackRoutine()
        {
#if DEBUG
            debugMode = true;
#endif
            var visuals = new List <VisualObject>();
            // user's wave delay setting
            var waveDelay = (int)(UserSettings.WaveDelay * 1000);

            // Call it once in order to cache the RedPoints in the beginning
            // If this will be called after spell deployment the redline will disappear!! - Do not remove this!
            var redPoints = GameGrid.RedPoints;

            // get a list of all deployable units
            var deployElements = Deploy.GetTroops();
            Log.Debug("[Debug] Deployable Troops: " + ToUnitString(deployElements));
            if (!HasNeededTroops(deployElements))
            {
                Log.Error("[LavaLoon] Couldn't find a known troop composition. Consider using one of the known troop compositions. Check our forums to learn more about " +
                          "the LavaLoon Deploy in order to achieve the best possible results.");
                Surrender();
                yield break;
            }

            // extract heores into their own list
            var heroes = deployElements
                         .Extract(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                  (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                  (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            // extract clanCastle into its own list
            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops && UserSettings.UseClanTroops);

            // extract spells into their own list
            var lightningSpells  = deployElements.ExtractOne(x => x.Id == DeployId.Lightning);
            var earthQuakeSpells = deployElements.ExtractOne(x => x.Id == DeployId.Earthquake);
            var rageSpells       = deployElements.ExtractOne(x => x.Id == DeployId.Rage);
            var freezeSpells     = deployElements.ExtractOne(x => x.Id == DeployId.Freeze);

            // extract tank units into their own list
            var tanks = deployElements.Extract(AttackType.Tank).ToArray();

            // extract balloons into their own list
            var balloon = deployElements.ExtractOne(x => x.Id == DeployId.Balloon);

            // extract the attack units into their own list
            var damageDealer = deployElements.Extract(AttackType.Damage).OrderByDescending(x => x.UnitData.HP).ToArray();

            // extract wallbreakers into their own list
            var wallBreakers = deployElements.ExtractOne(x => x.Id == DeployId.WallBreaker);

            #region ZapQuake AirDefenses if possible and rescan with a CheckForDestroyed
            // ZapQuake AirDefenses if required spells exist
            List <AirDefense> zapQuakeTargets = null;
            if (earthQuakeSpells?.Count > 0 && lightningSpells?.Count >= 2)
            {
                zapQuakeTargets = FindAirDefenseTargets(earthQuakeSpells, lightningSpells, visuals);

                if (zapQuakeTargets?.Count > 0)
                {
                    foreach (var t in ZapQuakeAirDefenses(zapQuakeTargets, earthQuakeSpells, lightningSpells))
                    {
                        yield return(t);
                    }
                }
                else
                {
                    Log.Warning("[LavaLoon] Couldn't find AirDefense targets for ZapQuaking!");
                }
            }
            else
            {
                Log.Info("[LavaLoon] Could not find enough spells (at least 1 Earthquake and 2 Lightning spells) to zapquake AirDefenses");
            }

            // If we have zapquaked something we should rescan to check for the remaining AirDefenses
            if (zapQuakeTargets != null)
            {
                Log.Info("[LavaLoon] Rescanning AirDefenses to check for remaining AirDefenses");
                AirDefense.Find(CacheBehavior.CheckForDestroyed);
            }
            #endregion

            var airDefenses               = AirDefense.Find();
            var deployInfo                = PrepareDeployCalculation(airDefenses, visuals);
            var tankDeployPoints          = CreateLavaHoundDeployPoints(deployInfo, visuals).ToArray();
            var balloonDeployPoints       = CreateBalloonDeployPoints(deployInfo, balloon, visuals).ToArray();
            var attackWaveDeployPoints    = CreateAttackWaveDeployPoints(deployInfo, visuals).ToArray();
            var funnelCreatorDeployPoints = CreateFunnelCreatorDeployPoints(deployInfo, visuals).ToArray();
            var intersectionDeployPoint   = CreateIntersectionDeployPoint(deployInfo, visuals).First();

            // deploy all tanks if available
            if (tanks != null)
            {
                foreach (var tank in tanks)
                {
                    var deployCount = tank.Count;
                    Log.Info($"[Breakthrough] Deploying {tank.PrettyName} x{deployCount}");

                    // Deploy all Tanks alternating to each tank deploypoint (e. g.: left - right, left - right, left)
                    while (tank?.Count > 0)
                    {
                        var initialTankCount = tank.Count;

                        foreach (var deployPoint in tankDeployPoints)
                        {
                            foreach (var t in Deploy.AtPoint(tank, deployPoint, 1))
                            {
                                yield return(t);
                            }
                        }

                        // Prevent an infinite loop if deploy point is inside of the redzone
                        if (tank.Count != initialTankCount)
                        {
                            continue;
                        }

                        Log.Warning($"[LavaLoon] Couldn't deploy {tank.PrettyName}");
                        break;
                    }
                }
            }

            if (balloon != null)
            {
                Log.Info($"[LavaLoon] Deploying {balloon.PrettyName} x{balloon.Count}");

                while (balloon?.Count > 0)
                {
                    var initialWallBreakersCount = balloon?.Count;
                    foreach (var t in Deploy.AtPoints(balloon, balloonDeployPoints, 3, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (balloon.Count != initialWallBreakersCount)
                    {
                        yield return(1200);

                        continue;
                    }

                    Log.Warning($"[LavaLoon] Couldn't deploy {wallBreakers.PrettyName}");
                    break;
                }
            }

            if (clanCastle?.Count > 0)
            {
                Log.Info($"[LavaLoon] Deploying {clanCastle.PrettyName}");
                foreach (var t in Deploy.AtPoint(clanCastle, intersectionDeployPoint, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            if (damageDealer.Any())
            {
                Log.Debug("[LavaLoon] 1500ms Delay before deploying the attack wave");
                yield return(1500);

                foreach (var troop in damageDealer)
                {
                    Log.Info($"[LavaLoon] Deploying {troop.PrettyName} x{troop.Count}");
                }
                while (damageDealer.Sum(x => x.Count) > 0)
                {
                    var initialTroopCount = damageDealer.Sum(x => x.Count);
                    foreach (var t in Deploy.AtPoints(damageDealer, funnelCreatorDeployPoints))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (damageDealer.Sum(x => x.Count) != initialTroopCount)
                    {
                        continue;
                    }

                    var remainingTroops = damageDealer.Where(x => x.Count > 0).ToList();
                    foreach (var troop in remainingTroops)
                    {
                        Log.Warning($"[LavaLoon] Couldn't deploy x{troop.Count} {troop.PrettyName}");
                    }
                    break;
                }
            }

            if (heroes.Any())
            {
                Log.Debug("[LavaLoon] 1500ms Delay before deploying heroes");
                yield return(1500);

                var heroDeployPoint = intersectionDeployPoint.TransformPositionAlongAxis(4).Constrain();

                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[LavaLoon] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoint(hero, heroDeployPoint))
                    {
                        yield return(t);
                    }
                }

                Deploy.WatchHeroes(heroes, 7000);
            }

            if (wallBreakers?.Count > 0)
            {
                var wallBreakersDeployPoint = intersectionDeployPoint.TransformPositionAlongAxis(4).Constrain();

                Log.Info($"[LavaLoon] Deploying {wallBreakers.PrettyName} x{wallBreakers.Count}");

                while (wallBreakers?.Count > 0)
                {
                    var initialWallBreakersCount = wallBreakers?.Count;

                    // Deploy Wallbreakers in 3 unit stacks (which is enough to crush walls) and deploy further stacks with 1.2s delay
                    // in order to avoid that all of them get destroyed by splash damages
                    foreach (var t in Deploy.AtPoint(wallBreakers, wallBreakersDeployPoint, 3, waveDelay: waveDelay))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (wallBreakers.Count != initialWallBreakersCount)
                    {
                        yield return(1200);

                        continue;
                    }

                    Log.Warning($"[LavaLoon] Couldn't deploy {wallBreakers.PrettyName}");
                    break;
                }
            }

            if (debugMode)
            {
                VisualizeDeployment(visuals);
            }
        }
        public override double ShouldAccept()
        {
            if (!PassesBasicAcceptRequirements())
            {
                return(0);
            }

            //TODO - Check which kind of army we have trained. Calculate an Air Offense Score, and Ground Offense Score.

            //TODO - Find all Base Defenses, and calculate an AIR and Ground Defensive Score.

            //TODO - From Collector/Storage fill levels, determine if loot is in Collectors, or Storages... (Will help to decide which alg to use.)

            //Verify that the Attacking Army contains at least 6 Dragons.
            deployElements = Deploy.GetTroops();
            var dragons = deployElements.FirstOrDefault(u => u.Id == DeployId.Dragon);

            if (dragons == null || dragons?.Count < 6)
            {
                Log.Error($"{Tag} Army not correct! - Dark Dragon Deploy Requires at least 6 Dragons to function Properly. (You have {dragons?.Count ?? 0} dragons)");
                return(0);
            }

            //Verify that there are enough spells to take out at least ONE air defense.
            var lightningSpells = deployElements.FirstOrDefault(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Lightning);
            List <DeployElement> earthquakeSpells = deployElements.Where(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Earthquake).ToList();

            var lightningCount  = lightningSpells?.Count ?? 0;
            var earthquakeCount = 0;

            //Get a count of all earthquake spells... donated, or brewed...
            foreach (var spell in earthquakeSpells.Where(s => s.Count > 0))
            {
                earthquakeCount += spell.Count;
            }

            if (lightningCount < 2 || lightningCount < 3 && earthquakeCount < 1)
            {
                //We dont have the Spells to take out the Closest Air Defense... Surrender before we drop any Dragons!
                Log.Error($"{Tag} We don't have enough spells to take out at least 1 air defense... Lightning Spells:{lightningCount}, Earthquake Spells:{earthquakeCount}");
                return(0);
            }

            if (deployElements.Count >= 11)
            {
                //Possibly Too Many Deployment Elements!  Bot Doesnt Scroll - Change Army Composition to have less than 12 unit types!
                Log.Warning($"{Tag} Warning! Full Army! - The Bot does not scroll through choices when deploying units... If your army has more than 11 unit types, The bot will not see them all, and cannot deploy everything!)");
            }

            //Write out all the unit pretty names we found...
            Log.Debug($"{Tag} Deployable Troops: {ToUnitString(deployElements)}");

            Log.Info($"{Tag} Base meets minimum Requirements... Checking DE Storage/Air Defense Locations...");


            //Check to see if we can find ANY air Defenses... (Could Skip here if not all are found.)
            var airDefensesTest = AirDefense.Find();

            if (airDefensesTest.Length == 0)
            {
                Log.Warning($"{Tag} Could not find ANY air defenses - Skipping");
                return(0);
            }

            //For now just log the Trophy counts...
            //TODO - Later we will want to use these to see if the base is worth attacking when we are in Trophy mode...
            try
            {
                int trophiesWin, trophiesDefeat = -1;
                if (Opponent.GetLootableTrophies(out trophiesWin, out trophiesDefeat))
                {
                    Log.Info($"Trophies if we Win: {trophiesWin}, Trophies if we lose: {trophiesDefeat}");
                }
            }
            catch (Exception ex)
            {
                Log.Error($"Error getting trophy values... - {ex.Message} - {ex.StackTrace}");
            }

            Log.Info($"{Tag} Found {airDefensesTest.Length} Air Defense Buildings.. Continuing Attack..");

            //We are Good to attack!
            return(1);
        }
        public override IEnumerable <int> AttackRoutine()
        {
            var DE = DarkElixirStorage.Find()?.FirstOrDefault()?.Location.GetCenter();

            //set the target
            if (DE == null)
            {
                for (var i = 1; i <= 3; i++)
                {
                    Log.Warning($"bot didn't found the DE Storage .. we will attemp search NO. {i + 1}");
                    yield return(1000);

                    DE = DarkElixirStorage.Find()?.FirstOrDefault()?.Location.GetCenter();
                    if (DE != null)
                    {
                        Log.Warning($"DE Storage found after {i + 1} retries");
                        break;
                    }
                }
            }

            if (DE != null)
            {
                _target = (PointFT)DE;
            }
            else
            {
                Log.Debug("[Goblin Knife] coundn't locate the target after aligning the base");
                Log.Error("Couldn't find DE Storage we will return home");
                Surrender();

                yield break;
            }
            CreateDeployPoints();
            Log.Info($"[Goblin Knife] V{Version} Deploy start");

            //get troops
            var deployElements = Deploy.GetTroops();

            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops && UserSettings.UseClanTroops);

            var earthQuakeSpell = deployElements.Extract(u => u.Id == DeployId.Earthquake);
            var jumpSpell       = deployElements.ExtractOne(DeployId.Jump);
            var giant           = deployElements.ExtractOne(DeployId.Giant);
            var goblin          = deployElements.ExtractOne(DeployId.Goblin);
            var wizard          = deployElements.ExtractOne(DeployId.Wizard);
            var barbarian       = deployElements.ExtractOne(DeployId.Barbarian);
            var archer          = deployElements.ExtractOne(DeployId.Archer);
            var wallbreaker     = deployElements.ExtractOne(DeployId.WallBreaker);
            var ragespell       = deployElements.ExtractOne(DeployId.Rage);
            var healspell       = deployElements.ExtractOne(DeployId.Heal);

            _freezeSpell = deployElements.ExtractOne(DeployId.Freeze);

            var heroes = deployElements
                         .Extract(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                  (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                  (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            //open near to dark elixer with 4 earthquakes
            if (earthQuakeSpell?.Sum(u => u.Count) >= 4)
            {
                foreach (var unit in earthQuakeSpell)
                {
                    foreach (int t in Deploy.AtPoint(unit, _earthQuakePoint, unit.Count))
                    {
                        yield return(t);
                    }
                }
            }
            else
            {
                _useJump = true;
            }
            yield return(1000);

            if (giant?.Count > 0)
            {
                foreach (int t in Deploy.AlongLine(giant, _attackLine.Item1, _attackLine.Item2, 6, 6))
                {
                    yield return(t);
                }
            }

            yield return(1000);

            if (wizard?.Count > 0)
            {
                foreach (int t in Deploy.AlongLine(wizard, _attackLine.Item1, _attackLine.Item2, 8, 4))
                {
                    yield return(t);
                }
            }

            if (barbarian?.Count > 0)
            {
                while (barbarian.Count > 0)
                {
                    int count = barbarian.Count;

                    Log.Info($"[Goblin Knife] Deploying {barbarian.PrettyName}");
                    foreach (int t in Deploy.AlongLine(barbarian, _attackLine.Item1, _attackLine.Item2, count, 4))
                    {
                        yield return(t);
                    }

                    // prevent infinite loop if deploy point is on red
                    if (barbarian.Count != count)
                    {
                        continue;
                    }

                    Log.Warning($"[Goblin Knife] Couldn't deploy {barbarian.PrettyName}");
                    break;
                }
            }

            if (archer?.Count > 0)
            {
                int archerCount = (int)(archer.Count / 2);
                Log.Info($"[Goblin Knife] Deploying {archer.PrettyName} ");
                foreach (int t in Deploy.AlongLine(archer, _attackLine.Item1, _attackLine.Item2, archerCount, 4))
                {
                    yield return(t);
                }
            }

            yield return(3000);

            if (ragespell?.Count >= 2)
            {
                foreach (int t in Deploy.AtPoint(ragespell, _ragePoint))
                {
                    yield return(t);
                }
            }
            if (wallbreaker?.Count > 0)
            {
                Log.Info($"[Goblin Knife] send test {wallbreaker.PrettyName} to check for bombs");
                foreach (int t in Deploy.AtPoint(wallbreaker, _orgin, 1))
                {
                    yield return(t);
                }
            }

            yield return(1000);

            while (wallbreaker?.Count > 0)
            {
                int count = wallbreaker.Count;
                Log.Info("[Goblin Knife] send wall breakers in groups");
                foreach (int t in Deploy.AtPoint(wallbreaker, _orgin, 3))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (wallbreaker.Count != count)
                {
                    continue;
                }

                Log.Warning($"[Goblin Knife] Couldn't deploy {wallbreaker.PrettyName}");
                break;
            }

            while (giant?.Count > 0)
            {
                int count = giant.Count;

                Log.Info($"[Goblin Knife] Deploying {giant.PrettyName} x{count}");
                foreach (int t in Deploy.AtPoint(giant, _orgin, count))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (giant.Count != count)
                {
                    continue;
                }

                Log.Warning($"[Goblin Knife] Couldn't deploy {giant.PrettyName}");
                break;
            }

            yield return(1000);

            while (wizard?.Count > 0)
            {
                int count = wizard.Count;

                Log.Info($"[Goblin Knife] Deploying {wizard}");
                foreach (int t in Deploy.AlongLine(wizard, _attackLine.Item1, _attackLine.Item2, 4, 2))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (wizard.Count != count)
                {
                    continue;
                }

                Log.Warning($"[Goblin Knife] Couldn't deploy {wizard.PrettyName}");
                break;
            }
            if (archer?.Count > 0)
            {
                Log.Info($"[Goblin Knife] Deploying {archer.PrettyName} ");
                foreach (int t in Deploy.AlongLine(archer, _attackLine.Item1, _attackLine.Item2, archer.Count, 4))
                {
                    yield return(t);
                }
            }

            yield return(1500);

            if (_useJump == true && jumpSpell?.Count > 0)
            {
                foreach (int t in Deploy.AtPoint(jumpSpell, _jumpPoint))
                {
                    yield return(t);
                }
            }

            if (healspell?.Count > 0)
            {
                foreach (int t in Deploy.AtPoint(healspell, _healPoint))
                {
                    yield return(t);
                }
            }

            if (clanCastle?.Count > 0)
            {
                Log.Info($"[Goblin Knife] Deploying {clanCastle.PrettyName}");
                foreach (int t in Deploy.AtPoint(clanCastle, _orgin))
                {
                    yield return(t);
                }
            }

            if (heroes.Any())
            {
                _delay = 1000;
                foreach (DeployElement hero in heroes.Where(u => u.Count > 0))
                {
                    foreach (int t in Deploy.AtPoint(hero, _orgin))
                    {
                        yield return(t);
                    }
                }
                watchHeroes = true;
            }
            yield return(_delay);

            while (goblin?.Count > 0)
            {
                int testGoblins = (int)(goblin.Count / 3);
                Log.Info($"[Goblin Knife] Deploying {goblin.PrettyName} x{testGoblins}");

                foreach (int t in Deploy.AtPoint(goblin, _orgin, testGoblins))
                {
                    yield return(t);
                }

                yield return(2000);

                int count = goblin.Count;

                Log.Info($"[Goblin Knife] Deploying {goblin.PrettyName} x{count}");
                foreach (int t in Deploy.AtPoint(goblin, _orgin, count))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (goblin.Count != count)
                {
                    continue;
                }

                Log.Warning($"[Goblin Knife] Couldn't deploy {goblin.PrettyName}");
                break;
            }
            //use freeze if inferno is found
            if (_freezeSpell?.Count > 0)
            {
                var infernos = InfernoTower.Find();
                // find and watch inferno towers
                if (infernos != null)
                {
                    foreach (var inferno in infernos)
                    {
                        inferno.FirstActivated += DropFreeze;

                        inferno.StartWatching();
                    }
                }
            }
            yield return(200);

            foreach (int t in Deploy.AtPoint(healspell, _target))
            {
                yield return(t);
            }

            foreach (int t in Deploy.AtPoint(ragespell, _target))
            {
                yield return(t);
            }
            if (watchHeroes == true)
            {
                Deploy.WatchHeroes(heroes, 7000);
            }
        }
        public override IEnumerable <int> AttackRoutine()
        {
            // Set start battle time.
            startTime = DateTime.Now;

            int waveLimit   = UserSettings.WaveSize;
            int waveDelay   = (int)(UserSettings.WaveDelay * 1000);
            int heroesIndex = -1;

            // Set the core point of the base
            SmartFourFingersHelper.SetCore();

            if (!IsTargetsCalculated)
            {
                SmartFourFingersHelper.CalculateTargets();
            }


            // Points to draw lines in deploy extends area.
            var topLeft  = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MaxY);
            var topRight = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MaxY - 2);

            var rightTop    = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MinY + 2);
            var rightBottom = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MinY);

            // Move 8 tiles from bottom corner due to unitsbar.
            var bottomLeft  = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MinY + 8);
            var bottomRight = new PointFT((float)GameGrid.MinX + 8, (float)GameGrid.DeployExtents.MinY);

            var leftTop    = new PointFT((float)GameGrid.MinX + 2, (float)GameGrid.DeployExtents.MaxY);
            var leftBottom = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MaxY - 2);

            var linesPointsList = new List <PointFT>
            {
                topLeft, topRight,
                rightTop, rightBottom,
                bottomLeft, bottomRight,
                leftBottom, leftTop
            };

            // Main four lines of attack.
            var topRightLine    = new Tuple <PointFT, PointFT>(topRight, rightTop);
            var bottomRightLine = new Tuple <PointFT, PointFT>(bottomRight, rightBottom);
            var bottomLeftLine  = new Tuple <PointFT, PointFT>(bottomLeft, leftBottom);
            var topLeftLine     = new Tuple <PointFT, PointFT>(topLeft, leftTop);

            // List of the four attack lines in clocwise order
            AttackLines = new List <Tuple <PointFT, PointFT> >
            {
                topRightLine,
                bottomRightLine,
                bottomLeftLine,
                topLeftLine
            };



            var deployHeroesAt = GetCurrentSetting("Deploy Heroes At");

            var target = SmartFourFingersHelper.GetHeroesTarget(deployHeroesAt);

            // Search for target if not found for 3 more times
            if (target.X == 0f && target.Y == 0f)
            {
                for (var i = 1; i <= 3; i++)
                {
                    yield return(1000);

                    target = SmartFourFingersHelper.GetHeroesTarget(deployHeroesAt);
                    if (target.X != 0f || target.Y != 0f)
                    {
                        break;
                    }
                }
            }

            var nearestRedPointToTarget = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(target)).FirstOrDefault();
            var nearestLinePoint        = linesPointsList.OrderBy(p => p.DistanceSq(nearestRedPointToTarget)).FirstOrDefault();

            heroesIndex = AttackLines.FindIndex(u => (u.Item1.X == nearestLinePoint.X && u.Item1.Y == nearestLinePoint.Y) || (u.Item2.X == nearestLinePoint.X && u.Item2.Y == nearestLinePoint.Y));

            var units  = Deploy.GetTroops();
            var heroes = units.Extract(x => x.IsHero);
            var cc     = units.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops);
            var spells = units.Extract(u => u.ElementType == DeployElementType.Spell);

            units.OrderForDeploy();

            // Set first attack line
            // Start from the next line to user defined to end with user defined line
            var line  = AttackLines.NextOf(AttackLines[heroesIndex]);
            var index = AttackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y);

            var deploymentMode = GetCurrentSetting("Set Troops Deployment");

            if (deploymentMode == 2)
            {
                AddFakeTargets();
            }

            var targetsCount = TargetsAtLine[index];

            Log.Info($"{AttackName} {Version} starts");

            var deployCC = GetCurrentSetting("CC Deployment");

            if (deployCC == 0 && cc?.Count > 0)
            {
                Log.Info($"{AttackName} Deploy Clan Castle troops first");
                foreach (var t in Deploy.AlongLine(cc, line.Item1, line.Item2, 1, 1, 0, waveDelay))
                {
                    yield return(t);
                }
            }

            // Start troops deployment on four sides.
            for (var i = 4; i >= 1; i--)
            {
                Log.Info($"Targets at this side = {targetsCount} targets from total {TotalTargetsCount} targets");
                foreach (var unit in units)
                {
                    if (unit?.Count > 0)
                    {
                        var count   = deploymentMode == 0 ? unit.Count / i : (TotalTargetsCount > 0 ? unit.Count * targetsCount / TotalTargetsCount : 0);
                        var housing = unit.UnitData.HousingSpace;
                        var fingers = housing < 4 ? (count < 8 ? count : 4) : 1;

                        Log.Info($"{AttackName} Deploy {count}x {unit.PrettyName}");
                        foreach (var t in Deploy.AlongLine(unit, line.Item1, line.Item2, count, fingers, 0, waveDelay))
                        {
                            yield return(t);
                        }
                    }
                }

                if (i != 1)
                {
                    line  = AttackLines.NextOf(AttackLines[index]);
                    index = AttackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y);

                    TotalTargetsCount -= targetsCount;
                    targetsCount       = TargetsAtLine[index];
                }
            }

            if (deployCC == 1 && cc?.Count > 0)
            {
                Log.Info($"{AttackName} Deploy Clan Castle troops");
                foreach (var t in Deploy.AlongLine(cc, line.Item1, line.Item2, 1, 1, 0, waveDelay))
                {
                    yield return(t);
                }
            }

            if (heroes.Any())
            {
                Log.Info($"{AttackName} Deploy Heroes");
                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    foreach (var t in Deploy.AlongLine(hero, line.Item1, line.Item2, 1, 1, 0, waveDelay))
                    {
                        yield return(t);
                    }
                }
                Deploy.WatchHeroes(heroes, 5000);
            }

            // Call FinalizeAttack and ForceZap at the same time
            var zapSpells = Deploy.GetTroops().Extract(u => u.ElementType == DeployElementType.Spell);

            if (spells.Extract(u => u.Id == DeployId.Lightning).Sum(u => u.Count) > 0 ||
                spells.Extract(u => u.Id == DeployId.Earthquake).Sum(u => u.Count) > 0)
            {
                var finalize = this.FinalizeAttack(units).GetEnumerator();
                var force    = ForceZap().GetEnumerator();

                var firstEnumMoreItems  = finalize.MoveNext();
                var secondEnumMoreItems = force.MoveNext();

                // Start both FinalizeAttack and ForceZap
                while (firstEnumMoreItems && secondEnumMoreItems)
                {
                    firstEnumMoreItems  = finalize.MoveNext();
                    secondEnumMoreItems = force.MoveNext();
                    yield return(200);
                }
                // Complete ForceZap if FinalizeAttack finished
                while (!firstEnumMoreItems && secondEnumMoreItems)
                {
                    secondEnumMoreItems = force.MoveNext();
                    yield return(200);
                }
                // Complete FinalizeAttack if ForceZap finished
                while (!secondEnumMoreItems && firstEnumMoreItems)
                {
                    firstEnumMoreItems = finalize.MoveNext();
                    yield return(200);
                }
            }
            else
            {
                yield break;
            }
        }
Esempio n. 21
0
        /// <summary>
        /// Set deploy points for troops and spells
        /// </summary>
        public static void SetDeployPoints()
        {
            var GreenPoints = AllInOnePushHelper.GenerateGreenPoints();
            var redPoints   = GameGrid.RedPoints
                              .Where(
                point =>
                !(point.X > 18 && point.Y > 18 || point.X > 18 && point.Y < -18 || point.X < -18 && point.Y > 18 ||
                  point.X < -18 && point.Y < -18))
                              .OrderBy(point => Math.Atan2(point.X, point.Y)).ToArray();

            AllPoints = GreenPoints.Concat(redPoints).ToArray();

            // Top right side
            var topRight = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MaxY - 2))).First();
            var rightTop = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MinY + 2))).First();

            // Bottom right side
            var rightBottom = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MaxX - 5, (float)GameGrid.DeployExtents.MinY - 2))).First();
            var bottomRight = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MinX + 10, (float)GameGrid.DeployExtents.MinY - 2))).First();

            // Bottom left side
            // Move 8 tiles from bottom corner due to unitsbar.
            var bottomLeft = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MinY + 8))).First();
            var leftBottom = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MaxY - 2))).First();

            // Top Left side
            var leftTop = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MinX + 2, (float)GameGrid.DeployExtents.MaxY))).First();
            var topLeft = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MaxY))).First();

            var isJumpSpell = Deploy.GetTroops().ExtractOne(DeployId.Jump)?.Count > 0 ? true : false;


            if (AllInOnePushDeploy.Origin.X > AllInOnePushDeploy.Core.X)
            {
                Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the top right");

                AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(topRight, rightTop);

                var distance = Math.Abs(AllInOnePushDeploy.Origin.X) - Math.Abs(AllInOnePushDeploy.Target.X);
                var target   = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core;

                var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.Y, "Y");
                AllInOnePushDeploy.FirstJumpPoint = new PointFT(firstWall.X - 2.75f, AllInOnePushDeploy.Core.Y);

                var maxX  = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.X - 5f : firstWall.X - 1.5f;
                var start = target.X + 4f;

                var earthQuakePoints = new List <PointFT> {
                    new PointFT(AllInOnePushDeploy.Target.X + 6f, AllInOnePushDeploy.Core.Y)
                };
                var jumpPoints = new List <PointFT> {
                    new PointFT(AllInOnePushDeploy.Target.X + 5.5f, AllInOnePushDeploy.Core.Y)
                };

                if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8)
                {
                    while (maxX > start)
                    {
                        earthQuakePoints.Add(new PointFT(start, AllInOnePushDeploy.Core.Y));
                        jumpPoints.Add(new PointFT(start - 0.5f, AllInOnePushDeploy.Core.Y));
                        start += 0.25f;
                    }
                }

                AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault();

                // Prevent overlaping EQ with jump
                if (isJumpSpell && AllInOnePushDeploy.FirstJumpPoint.X - AllInOnePushDeploy.EqPoint.X < 7f)
                {
                    AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X - 7f, AllInOnePushDeploy.FirstJumpPoint.Y);
                }
                AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.EqPoint.X - 0.5f, AllInOnePushDeploy.EqPoint.Y);

                var shiftSpells = AllInOnePushDeploy.ShiftSpells;
                AllInOnePushDeploy.FirstRagePoint  = new PointFT(AllInOnePushDeploy.Origin.X - 11f - shiftSpells, AllInOnePushDeploy.Core.Y);
                AllInOnePushDeploy.FirstHealPoint  = new PointFT(AllInOnePushDeploy.Origin.X - 17f - shiftSpells, AllInOnePushDeploy.Core.Y);
                AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Origin.X - 22f - shiftSpells, AllInOnePushDeploy.Core.Y);
                AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Origin.X - 26f - shiftSpells, AllInOnePushDeploy.Core.Y);

                //try to find better funneling points
                var frac = 0.75f;

                AllInOnePushDeploy.FirstFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac *
                                                                      (AllInOnePushDeploy.AttackLine.Item1.X - AllInOnePushDeploy.Origin.X),
                                                                      AllInOnePushDeploy.Origin.Y + frac *
                                                                      (AllInOnePushDeploy.AttackLine.Item1.Y - AllInOnePushDeploy.Origin.Y));

                AllInOnePushDeploy.SecondFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac *
                                                                       (AllInOnePushDeploy.AttackLine.Item2.X - AllInOnePushDeploy.Origin.X),
                                                                       AllInOnePushDeploy.Origin.Y + frac *
                                                                       (AllInOnePushDeploy.AttackLine.Item2.Y - AllInOnePushDeploy.Origin.Y));

                AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT>
                                                    (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 11f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 11f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                    );
                AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT>
                                                   (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 19f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 19f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                   );

                AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT>
                                                     (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                     );
                AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT>
                                                    (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                    );

                AllInOnePushDeploy.QWHealer    = new PointFT(GameGrid.DeployExtents.MaxX, AllInOnePushDeploy.FirstFunnellingPoint.Y);
                AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 3, AllInOnePushDeploy.FirstFunnellingPoint.Y - 1);
                AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 3, AllInOnePushDeploy.SecondFunnellingPoint.Y + 1);
            }

            else if (AllInOnePushDeploy.Origin.X < AllInOnePushDeploy.Core.X)
            {
                Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the bottom left");

                AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(leftBottom, bottomLeft);

                var distance = Math.Abs(AllInOnePushDeploy.Origin.X) - Math.Abs(AllInOnePushDeploy.Target.X);
                var target   = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core;

                var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.Y, "Y");
                AllInOnePushDeploy.FirstJumpPoint = new PointFT(firstWall.X + 2.75f, AllInOnePushDeploy.Core.Y);

                var maxX  = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.X + 5f : firstWall.X + 1.5f;
                var start = target.X - 4f;

                var earthQuakePoints = new List <PointFT> {
                    new PointFT(AllInOnePushDeploy.Target.X - 6f, AllInOnePushDeploy.Core.Y)
                };
                var jumpPoints = new List <PointFT> {
                    new PointFT(AllInOnePushDeploy.Target.X - 5.5f, AllInOnePushDeploy.Core.Y)
                };

                if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8)
                {
                    while (maxX < start)
                    {
                        earthQuakePoints.Add(new PointFT(start, AllInOnePushDeploy.Core.Y));
                        jumpPoints.Add(new PointFT(start + 0.5f, AllInOnePushDeploy.Core.Y));
                        start -= 0.25f;
                    }
                }

                AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault();

                // Prevent overlaping EQ with jump
                if (isJumpSpell && Math.Abs(AllInOnePushDeploy.FirstJumpPoint.X - AllInOnePushDeploy.EqPoint.X) < 7f)
                {
                    AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X + 7f, AllInOnePushDeploy.FirstJumpPoint.Y);
                }

                AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.EqPoint.X + 0.5f, AllInOnePushDeploy.EqPoint.Y);

                var shiftSpells = AllInOnePushDeploy.ShiftSpells;
                AllInOnePushDeploy.FirstRagePoint  = new PointFT(AllInOnePushDeploy.Origin.X + 11f + shiftSpells, AllInOnePushDeploy.Core.Y);
                AllInOnePushDeploy.FirstHealPoint  = new PointFT(AllInOnePushDeploy.Origin.X + 17f + shiftSpells, AllInOnePushDeploy.Core.Y);
                AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Origin.X + 22f + shiftSpells, AllInOnePushDeploy.Core.Y);
                AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Origin.X + 26f + shiftSpells, AllInOnePushDeploy.Core.Y);

                //try to find better funneling points
                var frac = 0.75f;

                AllInOnePushDeploy.FirstFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac *
                                                                      (AllInOnePushDeploy.AttackLine.Item1.X - AllInOnePushDeploy.Origin.X),
                                                                      AllInOnePushDeploy.Origin.Y + frac *
                                                                      (AllInOnePushDeploy.AttackLine.Item1.Y - AllInOnePushDeploy.Origin.Y));

                AllInOnePushDeploy.SecondFunnellingPoint = bottomLeft;

                AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT>
                                                    (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 11f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 11f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                    );

                AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT>
                                                   (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 19f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 19f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                   );

                AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT>
                                                     (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                     );

                AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT>
                                                    (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y)
                                                    );

                AllInOnePushDeploy.QWHealer    = new PointFT(GameGrid.DeployExtents.MinX, AllInOnePushDeploy.FirstFunnellingPoint.Y);
                AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 3, AllInOnePushDeploy.FirstFunnellingPoint.Y - 1);
                AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 3, AllInOnePushDeploy.SecondFunnellingPoint.Y + 1);
            }

            else if (AllInOnePushDeploy.Origin.Y > AllInOnePushDeploy.Core.Y)
            {
                Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the top left");

                AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(leftTop, topLeft);

                var distance = Math.Abs(AllInOnePushDeploy.Origin.Y) - Math.Abs(AllInOnePushDeploy.Target.Y);
                var target   = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core;

                var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.X, "X");
                AllInOnePushDeploy.FirstJumpPoint = new PointFT(AllInOnePushDeploy.Core.X, firstWall.Y - 2.75f);

                var maxX  = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.Y - 5f : firstWall.Y - 1.5f;
                var start = target.Y + 4f;

                var earthQuakePoints = new List <PointFT> {
                    new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y + 6f)
                };
                var jumpPoints = new List <PointFT> {
                    new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y + 5.5f)
                };

                if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8)
                {
                    while (maxX > start)
                    {
                        earthQuakePoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start));
                        jumpPoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start - 0.5f));
                        start += 0.25f;
                    }
                }

                AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault();

                // Prevent overlaping EQ with jump
                if (isJumpSpell && AllInOnePushDeploy.FirstJumpPoint.Y - AllInOnePushDeploy.EqPoint.Y < 7f)
                {
                    AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X, AllInOnePushDeploy.FirstJumpPoint.Y - 7f);
                }
                AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.EqPoint.Y - 0.5f);

                var shiftSpells = AllInOnePushDeploy.ShiftSpells;
                AllInOnePushDeploy.FirstRagePoint  = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 11f - shiftSpells);
                AllInOnePushDeploy.FirstHealPoint  = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 17f - shiftSpells);
                AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 22f - shiftSpells);
                AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 26f - shiftSpells);

                //try to find better funneling points
                var frac = 0.75f;

                AllInOnePushDeploy.FirstFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac *
                                                                      (AllInOnePushDeploy.AttackLine.Item1.X - AllInOnePushDeploy.Origin.X),
                                                                      AllInOnePushDeploy.Origin.Y + frac *
                                                                      (AllInOnePushDeploy.AttackLine.Item1.Y - AllInOnePushDeploy.Origin.Y));

                AllInOnePushDeploy.SecondFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac *
                                                                       (AllInOnePushDeploy.AttackLine.Item2.X - AllInOnePushDeploy.Origin.X),
                                                                       AllInOnePushDeploy.Origin.Y + frac *
                                                                       (AllInOnePushDeploy.AttackLine.Item2.Y - AllInOnePushDeploy.Origin.Y));

                AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT>
                                                    (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 11f - shiftSpells),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 11f - shiftSpells)
                                                    );

                AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT>
                                                   (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 19f - shiftSpells),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 19f - shiftSpells)
                                                   );

                AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT>
                                                     (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 24f - shiftSpells),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 24f - shiftSpells)
                                                     );

                AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT>
                                                    (
                    new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 24f - shiftSpells),
                    new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 24f - shiftSpells)
                                                    );

                AllInOnePushDeploy.QWHealer    = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, GameGrid.DeployExtents.MaxY);
                AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 1, AllInOnePushDeploy.FirstFunnellingPoint.Y - 3);
                AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 1, AllInOnePushDeploy.SecondFunnellingPoint.Y - 3);
            }

            else // (orgin.Y < core.Y)
            {
                Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the bottom right");

                // Avoid bottom right side until fix zoom out on attack progress issue
                var avoidBottomRight = AllInOnePushDeploy.AvoidBottomRight;
                if (avoidBottomRight == 1)
                {
                    var originPoints = new[]
                    {
                        new PointFT(GameGrid.DeployExtents.MaxX, AllInOnePushDeploy.Core.Y),
                        new PointFT(GameGrid.DeployExtents.MinX, AllInOnePushDeploy.Core.Y),
                        new PointFT(AllInOnePushDeploy.Core.X, GameGrid.DeployExtents.MaxY),
                        new PointFT(AllInOnePushDeploy.Core.X, GameGrid.DeployExtents.MinY)
                    };
                    AllInOnePushDeploy.Origin = originPoints.OrderBy(point => point.DistanceSq(AllInOnePushDeploy.Target)).ElementAt(1);
                    Log.Warning($"Avoid bottom right side set to true, We will attack from next closest side to the target");

                    SetDeployPoints();
                }
                else
                {
                    AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(rightBottom, bottomRight);

                    var distance = Math.Abs(AllInOnePushDeploy.Origin.Y) - Math.Abs(AllInOnePushDeploy.Target.Y);
                    var target   = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core;

                    var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.X, "X");
                    AllInOnePushDeploy.FirstJumpPoint = new PointFT(AllInOnePushDeploy.Core.X, firstWall.Y + 2.75f);

                    var maxX  = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.Y + 5f : firstWall.Y + 1.5f;
                    var start = target.Y - 4f;

                    var earthQuakePoints = new List <PointFT> {
                        new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y - 6f)
                    };
                    var jumpPoints = new List <PointFT> {
                        new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y - 5.5f)
                    };

                    if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8)
                    {
                        while (maxX < start)
                        {
                            earthQuakePoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start));
                            jumpPoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start + 0.5f));
                            start -= 0.25f;
                        }
                    }

                    AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault();

                    // Prevent overlaping EQ with jump
                    if (isJumpSpell && Math.Abs(AllInOnePushDeploy.FirstJumpPoint.Y - AllInOnePushDeploy.EqPoint.Y) < 7f)
                    {
                        AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X, AllInOnePushDeploy.FirstJumpPoint.Y + 7f);
                    }
                    AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.EqPoint.X, AllInOnePushDeploy.EqPoint.Y + 0.5f);

                    var shiftSpells = AllInOnePushDeploy.ShiftSpells;
                    AllInOnePushDeploy.FirstRagePoint  = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 11f + shiftSpells);
                    AllInOnePushDeploy.FirstHealPoint  = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 17f + shiftSpells);
                    AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 22f + shiftSpells);
                    AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 26f + shiftSpells);

                    // Set Funneling point
                    AllInOnePushDeploy.FirstFunnellingPoint  = rightBottom;
                    AllInOnePushDeploy.SecondFunnellingPoint = bottomRight;

                    AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT>
                                                        (
                        new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 11f + shiftSpells),
                        new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 11f + shiftSpells)
                                                        );

                    AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT>
                                                       (
                        new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 19f + shiftSpells),
                        new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 19f + shiftSpells)
                                                       );

                    AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT>
                                                         (
                        new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 24f + shiftSpells),
                        new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 24f + shiftSpells)
                                                         );

                    AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT>
                                                        (
                        new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 24f + shiftSpells),
                        new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 24f + shiftSpells)
                                                        );

                    AllInOnePushDeploy.QWHealer    = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, GameGrid.DeployExtents.MinY);
                    AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 1, AllInOnePushDeploy.FirstFunnellingPoint.Y + 3);
                    AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 1, AllInOnePushDeploy.SecondFunnellingPoint.Y + 3);
                }
            }
            AllInOnePushDeploy.SecondJumpPoint = AllInOnePushDeploy.EqPoint;

            AllInOnePushDeploy.FirstFunnellingPoint  = AllPoints.OrderBy(p => p.DistanceSq(AllInOnePushDeploy.FirstFunnellingPoint)).First();
            AllInOnePushDeploy.SecondFunnellingPoint = AllPoints.OrderBy(p => p.DistanceSq(AllInOnePushDeploy.SecondFunnellingPoint)).First();
            AllInOnePushDeploy.Origin = AllPoints.OrderBy(p => p.DistanceSq(AllInOnePushDeploy.Origin)).First();

            DebugBottomRightSidePoints();
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Debug("[Snipe Deploy] Getting the best townhall deploy point.");
            var snipePoint = new Container <PointFT> {
                Item = GetTownHallDeployPoint()
            };

            Log.Debug($"[Snipe Deploy] Deploy point is ({snipePoint.Item})");

            var troops = Deploy.GetTroops();

            var hero = troops.GetQueen() ?? troops.GetKing() ?? troops.GetWarden();

            if (hero != null)
            {
                Log.Info("[Snipe Deploy] Deploying the " + hero.PrettyName + " to snipe.");
                foreach (var t in Deploy.AtPoint(hero, snipePoint))
                {
                    yield return(t);
                }

                hero.Recount();

                if (hero.Count > 0)
                {
                    var th = TownHall.Find();

                    using (var bmp = Screenshot.Capture(true))
                    {
                        Visualize.RectangleT(bmp, th.Location);
                        Visualize.RectangleT(bmp, new RectangleT((int)snipePoint.Item.X, (int)snipePoint.Item.Y, 1, 1));
                        var d = DateTime.UtcNow;
                        Screenshot.Save(bmp, $"Snipe Deploy_{d.Year}-{d.Month}-{d.Day}_{d.Hour}-{d.Minute}-{d.Second}");
                    }
                }

                yield return(5000);

                Deploy.WatchHeroes(new List <DeployElement> {
                    hero
                });

                var countdown = new Countdown(15.0);

                if (hero.Count < 1)
                {
                    while (countdown.IsRunning)
                    {
                        if (Attack.SurrenderIfWeHaveAStar())
                        {
                            yield break;
                        }
                        else
                        {
                            yield return(200);
                        }
                    }
                }
                else
                {
                    Log.Info("[Snipe Deploy] Hero failed to deploy; trying to use troops");
                }
            }

            var snipeTroops =
                troops.GetByAttackType(AttackType.Damage)
                .GetByType(DeployElementType.NormalUnit)
                .Where(u => u.UnitData?.TargetType == TargetType.Loot || u.UnitData?.TargetType == TargetType.None)
                .ToArray();

            if (snipeTroops.Length > 0)
            {
                Log.Info("[Snipe Deploy] Deploying troops to snipe.");

                var countdown = new Countdown(15.0, true);
                var pt        = new Container <PointFT> {
                    Item = GetTownHallDeployPoint()
                };
                while (true)
                {
                    if (countdown.IsFinished)
                    {
                        foreach (var t in Deploy.AtPoint(snipeTroops, snipePoint))
                        {
                            yield return(t);
                        }

                        snipeTroops.Recount();

                        if (snipeTroops.All(u => u.Count < 1))
                        {
                            yield break;
                        }

                        countdown.Restart();
                    }

                    if (Attack.SurrenderIfWeHaveAStar())
                    {
                        yield break;
                    }
                }
            }
        }
        public override IEnumerable <int> AttackRoutine()
        {
#if DEBUG
            debugMode = true;
#endif
            var visuals = new List <VisualObject>();

            // get a list of all deployable units
            var deployElements = Deploy.GetTroops();
            Log.Debug("[Debug] Deployable Troops: " + ToUnitString(deployElements));
            if (!HasNeededTroops(deployElements))
            {
                Log.Error("[Smart Air] Couldn't find a known troop composition. Consider using one of the known troop compositions. Check our forums to learn more about " +
                          "the Smart Air Deploy in order to achieve the best possible results.");
                Surrender();
                yield break;
            }

            // extract heores into their own list
            var heroes = deployElements.Extract(u => u.IsHero);

            // extract clanCastle into its own list
            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops);

            // extract spells into their own list
            var lightningSpells  = deployElements.ExtractOne(x => x.Id == DeployId.Lightning);
            var earthQuakeSpells = deployElements.ExtractOne(x => x.Id == DeployId.Earthquake);
            var rageSpells       = deployElements.ExtractOne(x => x.Id == DeployId.Rage);
            var freezeSpells     = deployElements.ExtractOne(x => x.Id == DeployId.Freeze);


            #region ZapQuake AirDefenses if possible and rescan with a CheckForDestroyed
            // ZapQuake AirDefenses if required spells exist
            List <AirDefense> zapQuakeTargets = null;
            if (earthQuakeSpells?.Count > 0 && lightningSpells?.Count >= 2)
            {
                zapQuakeTargets = FindAirDefenseTargets(earthQuakeSpells, lightningSpells, visuals);

                if (!debugMode)
                {
                    if (zapQuakeTargets?.Count > 0)
                    {
                        foreach (var t in ZapQuakeAirDefenses(zapQuakeTargets, earthQuakeSpells, lightningSpells))
                        {
                            yield return(t);
                        }
                    }
                    else
                    {
                        Log.Warning("[Smart Air] Couldn't find AirDefense targets for ZapQuaking!");
                    }
                }
            }
            else
            {
                Log.Info("[Smart Air] Could not find enough spells (at least 1 Earthquake and 2 Lightning spells) to zapquake AirDefenses");
            }

            // If we have zapquaked something we should rescan to check for the remaining AirDefenses
            if (zapQuakeTargets != null)
            {
                Log.Info("[Smart Air] Rescanning AirDefenses to check for remaining AirDefenses");
                AirDefense.Find(CacheBehavior.CheckForDestroyed);
            }
            #endregion

            var airDefenses          = AirDefense.Find();
            var lavaLoonDeployPoints = CreateLavaHoundDeployPoints(airDefenses, visuals);

            if (debugMode)
            {
                VisualizeDeployment(visuals);
            }
        }