/// <summary>
        /// If there are enough resources, return TimeSpan(0)
        /// Otherwise calculate how long it will take to get enough resources and transit res from
        /// main village, if we have that enabled. Return the one that takes less time.
        /// DateTime for usage in nextExecution time
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="vill">(target) Village</param>
        /// <param name="res">Resources required</param>
        /// <returns></returns>
        public static DateTime EnoughResourcesOrTransit(Account acc, Village vill, Resources res)
        {
            DateTime enoughRes = DateTime.Now.Add(TimeHelper.EnoughResToUpgrade(vill, res));

            //We have enough resources, return DateTime.Now
            if (enoughRes < DateTime.Now.AddMilliseconds(1))
            {
                return(DateTime.Now);
            }
            //Not enough resources, send resources
            //TODO: this isn't working like supposed to. If we have small transport time, (below 2min, return 2min, so bot has time to send res
            DateTime resTransit = MarketHelper.TransitResources(acc, vill);

            return(enoughRes < resTransit ? enoughRes : resTransit);
        }
Example #2
0
        /// <summary>
        /// Finds out which transits will be over soonest and returns the datetime.
        /// </summary>
        /// <param name="transitsO"></param>
        /// <returns></returns>
        public static DateTime SoonestAvailableMerchants(Account acc, Village vill1, Village vill2, List <MerchantsUnderWay> transitsO)
        {
            var transits = transitsO
                           .Where(x => x.Transit == TransitType.Outgoin || x.Transit == TransitType.Returning)
                           .ToList();

            var ret = DateTime.MaxValue;

            foreach (var transit in transits)
            {
                DateTime time = DateTime.MaxValue;

                switch (transit.Transit)
                {
                case TransitType.Outgoin:
                    var oneTransitTime = MarketHelper.CalculateTransitTime(acc, vill1, vill2);
                    time = transit.Arrival.Add(oneTransitTime);
                    if (transit.RepeatTimes > 1)
                    {
                        time += TimeHelper.MultiplyTimespan(
                            oneTransitTime,
                            (2 * (transit.RepeatTimes - 1))
                            );
                    }
                    break;

                case TransitType.Returning:
                    time = transit.Arrival;
                    if (transit.RepeatTimes > 1)
                    {
                        time += TimeHelper.MultiplyTimespan(
                            MarketHelper.CalculateTransitTime(acc, vill1, vill2),
                            (2 * (transit.RepeatTimes - 1))
                            );
                    }
                    break;
                }
                if (ret > time)
                {
                    ret = time;
                }
            }
            return(ret);
        }
 public static void StartAccountTasks(Account acc)
 {
     // If we don't know server speed, go and get it
     if (acc.AccInfo.ServerSpeed == 0)
     {
         TaskExecutor.AddTaskIfNotExists(acc, new GetServerSpeed()
         {
             ExecuteAt = DateTime.MinValue.AddHours(2)
         });
     }
     if (acc.AccInfo.MapSize == 0)
     {
         TaskExecutor.AddTaskIfNotExists(acc, new GetMapSize()
         {
             ExecuteAt = DateTime.MinValue.AddHours(2)
         });
     }
     //FL
     if (acc.Farming.Enabled)
     {
         TaskExecutor.AddTaskIfNotExists(acc, new SendFLs()
         {
             ExecuteAt = DateTime.Now
         });
     }
     //research / improve / train troops
     foreach (var vill in acc.Villages)
     {
         //if (vill.Troops.Researched.Count == 0) TaskExecutor.AddTask(acc, new UpdateTroops() { ExecuteAt = DateTime.Now, vill = vill });
         TroopsHelper.ReStartResearchAndImprovement(acc, vill);
         if (!TroopsHelper.EverythingFilled(acc, vill))
         {
             TroopsHelper.ReStartTroopTraining(acc, vill);
         }
         BuildingHelper.ReStartBuilding(acc, vill);
         BuildingHelper.ReStartDemolishing(acc, vill);
         MarketHelper.ReStartSendingToMain(acc, vill);
         //todo
     }
 }
Example #4
0
        /// <summary>
        /// Used by BotTasks to insert resources/coordinates into the page.
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="resources">Target resources</param>
        /// <param name="coordinates">Target coordinates</param>
        /// <returns>Time it will take for transit to complete</returns>
        public static async Task <TimeSpan> MarketSendResource(Account acc, long[] resources, Village targetVillage, BotTask botTask)
        {
            var times = 1;

            if (acc.AccInfo.GoldClub ?? false)
            {
                times = 3;
            }
            else if (acc.AccInfo.PlusAccount)
            {
                times = 2;
            }

            // No resources to send
            if (resources.Sum() == 0)
            {
                return(TimeSpan.Zero);
            }

            var sendRes = resources.Select(x => x / times).ToArray();

            //round the resources that we want to send, so it looks less like a bot

            (var merchantsCapacity, var merchantsNum) = MarketHelper.ParseMerchantsInfo(acc.Wb.Html);
            // We don't have any merchants.
            if (merchantsNum == 0)
            {
                //Parse currently ongoing transits
                var transits   = MarketParser.ParseTransits(acc.Wb.Html);
                var activeVill = acc.Villages.FirstOrDefault(x => x.Active); // Could also just pass that in params

                var nextTry = SoonestAvailableMerchants(acc, activeVill, targetVillage, transits);
                if (nextTry != DateTime.MaxValue)
                {
                    nextTry = nextTry.AddSeconds(5);
                }

                botTask.NextExecute = nextTry;
                // Just return something, will get overwritten anyways.
                return(new TimeSpan((int)(nextTry - DateTime.Now).TotalHours + 1, 0, 0));
            }

            var maxRes = merchantsCapacity * times;
            var allRes = resources.Sum();

            if (allRes > maxRes)
            {
                // We don't have enough merchants to transit all the resources. Divide all resources by some divider.
                var     resDivider = (float)allRes / maxRes;
                float[] resFloat   = sendRes.Select(x => x / resDivider).ToArray();
                sendRes = resFloat.Select(x => (long)Math.Floor(x)).ToArray();
            }

            var wb = acc.Wb.Driver;

            for (int i = 0; i < 4; i++)
            {
                // To avoid exception devide by zero
                if (50 <= sendRes[i])
                {
                    //round the number to about -1%, for rounder numbers
                    var digits    = Math.Ceiling(Math.Log10(sendRes[i]));
                    var remainder = sendRes[i] % (long)Math.Pow(10, digits - 2);
                    sendRes[i] -= remainder;
                    await DriverHelper.WriteById(acc, "r" + (i + 1), sendRes[i]);
                }
                await Task.Delay(AccountHelper.Delay() / 5);
            }

            // Input coordinates
            await DriverHelper.WriteCoordinates(acc, targetVillage.Coordinates);

            //Select x2/x3
            if (times != 1)
            {
                wb.ExecuteScript($"document.getElementById('x2').value='{times}'");
                await Task.Delay(AccountHelper.Delay() / 5);
            }
            await DriverHelper.ClickById(acc, "enabledButton");

            var durNode = acc.Wb.Html.GetElementbyId("target_validate");

            if (durNode == null && acc.Wb.Html.GetElementbyId("prepareError") != null)
            {
                // Error "Abuse! You have not enough resources." is displayed.
            }
            //get duration of transit
            var dur = durNode.Descendants("td").ToList()[3].InnerText.Replace("\t", "").Replace("\n", "");

            // Will NOT trigger a page reload! Thus we should await some time before continuing.
            await DriverHelper.ClickById(acc, "enabledButton");

            targetVillage.Market.LastTransit = DateTime.Now;

            var duration = TimeParser.ParseDuration(dur);

            return(TimeSpan.FromTicks(duration.Ticks * (times * 2 - 1)));
        }
Example #5
0
        /// <summary>
        /// If there are enough resources, return TimeSpan(0)
        /// Otherwise calculate how long it will take to get enough resources and transit res from
        /// main village, if we have that enabled. Return the one that takes less time.
        /// DateTime for usage in nextExecution time
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="vill">(target) Village</param>
        /// <param name="requiredRes">Resources required</param>
        /// <param name="task">Bot task that doesn't have enough resources</param>
        /// <param name="buildingTask">Potential building task</param>
        /// <returns>When next village update should occur</returns>
        private static DateTime?NewUnfinishedTask(Account acc, Village vill, Resources requiredRes, BotTask task, BuildingTask buildingTask = null)
        {
            var stillNeededRes = SubtractResources(requiredRes.ToArray(), vill.Res.Stored.Resources.ToArray(), true);

            // Whether we have enough resources. This should already be checked before calling this method!
            if (IsZeroResources(stillNeededRes))
            {
                ResSpendingHelper.AddUnfinishedTask(vill, task, requiredRes);
                return(DateTime.Now);
            }

            acc.Wb.Log($"Not enough resources for the task {task.GetName()}! Needed {requiredRes}. Bot will try finish the task later");

            if (IsStorageTooLow(acc, vill, requiredRes))
            {
                acc.Wb.Log($"Storage is too low.");
                ResSpendingHelper.AddUnfinishedTask(vill, task, requiredRes);
                return(null);
            }

            // Try to use hero resources first
            if (vill.Settings.UseHeroRes &&
                acc.AccInfo.ServerVersion == ServerVersionEnum.T4_5) // Only T4.5 has resources in hero inv
            {
                var heroRes = HeroHelper.GetHeroResources(acc);

                // If we have some hero resources, we should use those first
                if (!IsZeroResources(heroRes))
                {
                    var heroEquipTask = UseHeroResources(acc, vill, ref stillNeededRes, heroRes, buildingTask);

                    // If we have enough hero res for our task, execute the task
                    // right after hero equip finishes
                    if (IsZeroResources(SubtractResources(stillNeededRes, heroRes, true)))
                    {
                        heroEquipTask.NextTask = task;
                        return(null);
                    }
                }
            }

            ResSpendingHelper.AddUnfinishedTask(vill, task, requiredRes);

            // When will we have enough resources from production
            DateTime enoughRes = TimeHelper.EnoughResToUpgrade(vill, stillNeededRes);

            var mainVill = AccountHelper.GetMainVillage(acc);

            if (mainVill == vill)
            {
                return(enoughRes);
            }

            DateTime resTransit = MarketHelper.TransitResourcesFromMain(acc, vill);

            if (resTransit < enoughRes)
            {
                enoughRes = resTransit;
            }

            if (enoughRes < DateTime.Now)
            {
                return(DateTime.Now);
            }

            return(enoughRes);
        }
Example #6
0
        public static void StartAccountTasks(Account acc)
        {
            // Get the server info (on first running the account)
            if (acc.AccInfo.ServerSpeed == 0 || acc.AccInfo.MapSize == 0)
            {
                TaskExecutor.AddTaskIfNotExists(acc, new GetServerInfo()
                {
                    ExecuteAt = DateTime.MinValue.AddHours(2)
                });
            }

            if (acc.AccInfo.Tribe == null)
            {
                TaskExecutor.AddTaskIfNotExists(acc, new GetTribe()
                {
                    ExecuteAt = DateTime.MinValue.AddHours(3)
                });
            }

            //FL
            if (acc.Farming.Enabled)
            {
                TaskExecutor.AddTaskIfNotExists(acc, new SendFLs()
                {
                    ExecuteAt = DateTime.Now
                });
            }

            // Bot sleep
            TaskExecutor.AddTaskIfNotExists(acc, new Sleep()
            {
                ExecuteAt = DateTime.Now + TimeHelper.GetWorkTime(acc),
                AutoSleep = true
            });

            // Access change
            var nextAccessChange = TimeHelper.GetNextProxyChange(acc);

            if (nextAccessChange != TimeSpan.MaxValue)
            {
                TaskExecutor.AddTaskIfNotExists(acc, new ChangeAccess()
                {
                    ExecuteAt = DateTime.Now + nextAccessChange
                });
            }
            //research / improve / train troops
            foreach (var vill in acc.Villages)
            {
                //if (vill.Troops.Researched.Count == 0) TaskExecutor.AddTask(acc, new UpdateTroops() { ExecuteAt = DateTime.Now, vill = vill });
                TroopsHelper.ReStartResearchAndImprovement(acc, vill);
                TroopsHelper.ReStartTroopTraining(acc, vill);
                BuildingHelper.ReStartBuilding(acc, vill);
                BuildingHelper.ReStartDemolishing(acc, vill);
                MarketHelper.ReStartSendingToMain(acc, vill);
                ReStartCelebration(acc, vill);
                VillageHelper.SetNextRefresh(acc, vill);
                if (vill.FarmingNonGold.OasisFarmingEnabled)
                {
                    TaskExecutor.AddTaskIfNotExistInVillage(acc, vill, new AttackOasis()
                    {
                        Vill = vill
                    });
                }

                // Remove in later updates!
                if (vill.Settings.RefreshMin == 0)
                {
                    vill.Settings.RefreshMin = 30;
                }
                if (vill.Settings.RefreshMax == 0)
                {
                    vill.Settings.RefreshMax = 60;
                }
            }
            // Remove in later updates!
            if (acc.Hero.Settings.MinUpdate == 0)
            {
                acc.Hero.Settings.MinUpdate = 40;
            }
            if (acc.Hero.Settings.MaxUpdate == 0)
            {
                acc.Hero.Settings.MaxUpdate = 80;
            }

            // Hero update info
            if (acc.Hero.Settings.AutoRefreshInfo)
            {
                Random ran = new Random();
                TaskExecutor.AddTask(acc, new HeroUpdateInfo()
                {
                    ExecuteAt = DateTime.Now.AddMinutes(ran.Next(40, 80)),
                    Priority  = Tasks.BotTask.TaskPriority.Low
                });
            }
        }
        /// <summary>
        /// Used by BotTasks to insert resources/coordinates into the page.
        /// </summary>
        /// <param name="acc">Account</param>
        /// <param name="resources">Target resources</param>
        /// <param name="coordinates">Target coordinates</param>
        /// <returns>Time it will take for transit to complete</returns>
        public static async Task <TimeSpan> MarketSendResource(Account acc, long[] resources, Village targetVillage, BotTask botTask)
        {
            var times = 1;

            if (acc.AccInfo.GoldClub ?? false)
            {
                times = 3;
            }
            else if (acc.AccInfo.PlusAccount)
            {
                times = 2;
            }

            var sendRes = resources.Select(x => x / times).ToArray();

            //round the resources that we want to send, so it looks less like a bot

            //TODO: check if we have enough merchants
            (var merchantsCapacity, var merchantsNum) = MarketHelper.ParseMerchantsInfo(acc.Wb.Html);
            // We don't have any merchants.
            if (merchantsNum == 0)
            {
                //Parse currently ongoing transits
                var transits   = MarketParser.ParseTransits(acc.Wb.Html);
                var activeVill = acc.Villages.FirstOrDefault(x => x.Active); // Could also just pass that in params

                var nextTry = SoonestAvailableMerchants(acc, activeVill, targetVillage, transits);

                botTask.NextExecute = nextTry.AddSeconds(5);
                // Just return something, will get overwritten anyways.
                return(new TimeSpan((int)(nextTry - DateTime.Now).TotalHours + 1, 0, 0));
            }

            var maxRes = merchantsCapacity * times;
            var allRes = resources.Sum();

            if (allRes > maxRes)
            {
                // We don't have enough merchants to transit all the resources. Divide all resources by some divider.
                var     resDivider = (float)allRes / maxRes;
                float[] resFloat   = sendRes.Select(x => x / resDivider).ToArray();
                sendRes = resFloat.Select(x => (long)Math.Floor(x)).ToArray();
            }

            var wb = acc.Wb.Driver;

            for (int i = 0; i < 4; i++)
            {
                // To avoid exception devide by zero
                if (sendRes[i] != 0)
                {
                    //round the number to about -1%, for rounder numbers
                    var digits    = Math.Ceiling(Math.Log10(sendRes[i]));
                    var remainder = sendRes[i] % (long)Math.Pow(10, digits - 2);
                    sendRes[i] -= remainder;
                }
                wb.ExecuteScript($"document.getElementById('r{i + 1}').value='{sendRes[i]}'");
                await Task.Delay(AccountHelper.Delay() / 5);
            }

            wb.ExecuteScript($"document.getElementById('xCoordInput').value='{targetVillage.Coordinates.x}'");
            await Task.Delay(AccountHelper.Delay() / 5);

            wb.ExecuteScript($"document.getElementById('yCoordInput').value='{targetVillage.Coordinates.y}'");
            await Task.Delay(AccountHelper.Delay() / 5);

            //Select x2/x3
            if (times != 1)
            {
                wb.ExecuteScript($"document.getElementById('x2').value='{times}'");
                await Task.Delay(AccountHelper.Delay() / 5);
            }

            // Some bot protection here I guess. Just remove the class of the DOM.
            var script = @"
                    var button = document.getElementById('enabledButton');
                    button.click();
                    ";

            wb.ExecuteScript(script); //Prepare

            //update htmlDoc, parse duration, TODO: maybe some other method to wait until the page is loaded?
            HtmlNode durNode = null;

            do
            {
                await Task.Delay(AccountHelper.Delay());

                HtmlDocument html2 = new HtmlDocument();
                html2.LoadHtml(wb.PageSource);
                durNode = html2.GetElementbyId("target_validate");
            }while (durNode == null);

            //get duration of transit
            var dur = durNode.Descendants("td").ToList()[3].InnerText.Replace("\t", "").Replace("\n", "");

            // Will NOT trigger a page reload! Thus we should await some time before continuing.
            wb.ExecuteScript("document.getElementById('enabledButton').click()"); //SendRes

            await Task.Delay(AccountHelper.Delay() * 2);

            var duration = TimeParser.ParseDuration(dur);

            return(TimeSpan.FromTicks(duration.Ticks * (times * 2 - 1)));
        }
        /// <summary>
        /// Will be called before each task to update resources/msg?,villages,quests,hero health, adventures num, gold/silver
        /// </summary>
        /// <param name="acc">Account</param>
        /// <returns>True if successful, false if error</returns>
        private static bool PreTaskRefresh(Account acc)
        {
            var html = acc.Wb.Html;

            try
            {
                //check & update dorf1/dorf2
                if (!UpdateAccountObject.UpdateVillages(html, acc))
                {
                    return(false);                                                //Web browser not initiali
                }
                var activeVill = acc.Villages.FirstOrDefault(x => x.Active);
                //update dorf1/dorf2
                if (acc.Wb.CurrentUrl.Contains("dorf1"))
                {
                    UpdateDorf1Info(acc);
                }
                else if (acc.Wb.CurrentUrl.Contains("dorf2"))
                {
                    UpdateDorf2Info(acc);
                }


                acc.AccInfo.CulturePoints = RightBarParser.GetCulurePoints(html, acc.AccInfo.ServerVersion);

                var villExpansionReady = acc.Villages.FirstOrDefault(x => x.Expansion.ExpensionAvailable);
                if (acc.AccInfo.CulturePoints.MaxVillages > acc.AccInfo.CulturePoints.VillageCount &&
                    villExpansionReady != null)
                {
                    villExpansionReady.Expansion.ExpensionAvailable = false;
                    TaskExecutor.AddTaskIfNotExists(acc, new SendSettlers()
                    {
                        ExecuteAt = DateTime.Now, vill = villExpansionReady
                    });
                }

                acc.AccInfo.Tribe = LeftBarParser.GetAccountTribe(acc, html);
                acc.Quests        = RightBarParser.GetQuests(html);
                var goldSilver = RightBarParser.GetGoldAndSilver(html, acc.AccInfo.ServerVersion);
                acc.AccInfo.Gold        = goldSilver[0];
                acc.AccInfo.Silver      = goldSilver[1];
                acc.AccInfo.PlusAccount = RightBarParser.HasPlusAccount(html, acc.AccInfo.ServerVersion);
                //Check reports/msg count
                if (MsgParser.UnreadMessages(html, acc.AccInfo.ServerVersion) > 0 &&
                    !acc.Wb.CurrentUrl.Contains("messages.php") &&
                    !IsTaskOnQueue(acc, typeof(ReadMessage)))
                {
                    TaskExecutor.AddTask(acc, new ReadMessage()
                    {
                        ExecuteAt = DateTime.Now.AddMilliseconds(AccountHelper.Delay() * 30)
                    });
                }

                //update loyalty of village


                activeVill.Res.FreeCrop = RightBarParser.GetFreeCrop(html);
                activeVill.Res.Capacity = ResourceParser.GetResourceCapacity(html, acc.AccInfo.ServerVersion);
                activeVill.Res.Stored   = ResourceParser.GetResources(html);

                float ratio = (float)activeVill.Res.Stored.Resources.Crop / activeVill.Res.Capacity.GranaryCapacity;
                if (ratio >= 0.99 &&
                    acc.AccInfo.Gold >= 3 &&
                    activeVill.Market.Npc.Enabled &&
                    (activeVill.Market.Npc.NpcIfOverflow || !MarketHelper.NpcWillOverflow(activeVill)))
                {  //npc crop!
                    TaskExecutor.AddTaskIfNotExistInVillage(acc, activeVill, new NPC()
                    {
                        ExecuteAt = DateTime.MinValue,
                        vill      = activeVill
                    });
                }
                if (acc.Settings.AutoActivateProductionBoost && CheckProductionBoost(acc))
                {
                    TaskExecutor.AddTask(acc, new TTWarsPlusAndBoost()
                    {
                        ExecuteAt = DateTime.Now.AddSeconds(1)
                    });
                }

                acc.Hero.AdventureNum    = HeroParser.GetAdventureNum(html, acc.AccInfo.ServerVersion);
                acc.Hero.Status          = HeroParser.HeroStatus(html, acc.AccInfo.ServerVersion);
                acc.Hero.HeroInfo.Health = HeroParser.GetHeroHealth(html, acc.AccInfo.ServerVersion);

                bool heroReady = (acc.Hero.HeroInfo.Health > acc.Hero.Settings.MinHealth &&
                                  acc.Hero.Settings.AutoSendToAdventure &&
                                  acc.Hero.Status == Hero.StatusEnum.Home &&
                                  acc.Hero.NextHeroSend < DateTime.Now);
                // Update adventures
                if (heroReady &&
                    (acc.Hero.AdventureNum != acc.Hero.Adventures.Count() || HeroHelper.AdventureInRange(acc))) //update adventures
                {
                    AddTaskIfNotExists(acc, new StartAdventure()
                    {
                        ExecuteAt = DateTime.Now.AddSeconds(10)
                    });
                }
                if (acc.Hero.AdventureNum == 0 && acc.Hero.Settings.BuyAdventures) //for UNL servers, buy adventures
                {
                    AddTaskIfNotExists(acc, new TTWarsBuyAdventure()
                    {
                        ExecuteAt = DateTime.Now.AddSeconds(5)
                    });
                }
                if (acc.Hero.Status == Hero.StatusEnum.Dead && acc.Hero.Settings.AutoReviveHero) //if hero is dead, revive him
                {
                    AddTaskIfNotExists(acc, new ReviveHero()
                    {
                        ExecuteAt = DateTime.Now.AddSeconds(5), vill = AccountHelper.GetHeroReviveVillage(acc)
                    });
                }
                if (HeroParser.LeveledUp(html) && acc.Hero.Settings.AutoSetPoints)
                {
                    AddTaskIfNotExists(acc, new HeroSetPoints()
                    {
                        ExecuteAt = DateTime.Now
                    });
                }
                return(true);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error in PreTask " + e.Message + "\n\nStack Trace: " + e.StackTrace + "\n-----------------------");
                return(false);
            }
        }