示例#1
0
        private static async Task <bool> ClearDevice()
        {
            var itemPositions = MapDevice.InventoryControl.Inventory.Items.Select(i => i.LocationTopLeft).ToList();

            if (itemPositions.Count == 0)
            {
                return(true);
            }

            GlobalLog.Error("[OpenMapTask] Map Device is not empty. Now going to clean it.");

            foreach (var itemPos in itemPositions)
            {
                if (!await PlayerAction.TryTo(() => FastMoveFromDevice(itemPos), null, 2))
                {
                    return(false);
                }
            }
            GlobalLog.Debug("[OpenMapTask] Map Device has been successfully cleaned.");
            return(true);
        }
示例#2
0
        public static async Task <bool> RerollMagic(Vector2i mapPos)
        {
            while (true)
            {
                var map = InventoryUi.InventoryControl_Main.Inventory.FindItemByPos(mapPos);
                if (map == null)
                {
                    GlobalLog.Error($"[RerollMagic] Fail to find a map at {mapPos}.");
                    return(false);
                }
                var rarity = map.RarityLite();
                if (rarity != Rarity.Magic)
                {
                    GlobalLog.Error($"[TakeMapTask] RerollMagic is called on {rarity} map.");
                    return(false);
                }
                var affix = map.GetBannedAffix();
                if (affix != null)
                {
                    GlobalLog.Info($"[RerollMagic] Rerolling banned \"{affix}\" affix.");

                    if (!await ApplyOrb(mapPos, CurrencyNames.Alteration))
                    {
                        return(false);
                    }

                    continue;
                }
                if (map.CanAugment() && HasCurrency(CurrencyNames.Augmentation))
                {
                    if (!await ApplyOrb(mapPos, CurrencyNames.Augmentation))
                    {
                        return(false);
                    }

                    continue;
                }
                return(true);
            }
        }
示例#3
0
        public static bool PremiumTabCanFit(string metadata)
        {
            var tabType = StashUi.StashTabInfo.TabType;

            if (tabType == InventoryTabType.Currency)
            {
                var control = StashUi.CurrencyTab.GetInventoryControlForMetadata(metadata);

                if (control != null && ControlCanFit(control, metadata))
                {
                    return(true);
                }

                return(StashUi.CurrencyTab.NonCurrency.Any(miscControl => ControlCanFit(miscControl, metadata)));
            }
            if (tabType == InventoryTabType.Essence)
            {
                var control = StashUi.EssenceTab.GetInventoryControlForMetadata(metadata);

                if (control != null && ControlCanFit(control, metadata))
                {
                    return(true);
                }

                return(StashUi.EssenceTab.NonEssences.Any(miscControl => ControlCanFit(miscControl, metadata)));
            }
            if (tabType == InventoryTabType.Divination)
            {
                var control = StashUi.DivinationTab.GetInventoryControlForMetadata(metadata);
                return(control != null && ControlCanFit(control, metadata));
            }
            if (tabType == InventoryTabType.Fragment)
            {
                var control = StashUi.FragmentTab.GetInventoryControlForMetadata(metadata);
                return(control != null && ControlCanFit(control, metadata));
            }
            GlobalLog.Error("[StashTask] PremiumTabCanFit was called for unknown premium tab type.");
            return(false);
        }
示例#4
0
        public static async Task <bool> ApplyChisels(Vector2i mapPos)
        {
            while (true)
            {
                var map = InventoryUi.InventoryControl_Main.Inventory.FindItemByPos(mapPos);
                if (map == null)
                {
                    GlobalLog.Error($"[ApplyChisels] Fail to find a map at {mapPos}.");
                    return(false);
                }

                if (map.Quality >= 18)
                {
                    return(true);
                }

                if (!await ApplyOrb(mapPos, CurrencyNames.Chisel))
                {
                    return(false);
                }
            }
        }
示例#5
0
        private static Position GetMovePosForInventoryItem(Item item)
        {
            var itemName = item.Name;

            var currency = Settings.Instance.InventoryCurrencies.FirstOrDefault(i => i.Name == itemName);

            if (currency != null && HasFixedPosition(currency))
            {
                var destination = new Position(currency.Column - 1, currency.Row - 1);
                if (destination != item.LocationTopLeft)
                {
                    if (OccupiedBySameItem(itemName, destination))
                    {
                        GlobalLog.Error($"[SortInventoryTask] Unexpected error. \"{itemName}\" will not be sorted correctly because destination position is already occupied by the same item.");
                        return(GetMovePosLessThanCurrent(item));
                    }
                    return(destination);
                }
                return(null);
            }
            return(GetMovePosLessThanCurrent(item));
        }
        public async Task <bool> Run()
        {
            if (_triggerMetadata == null || MapExplorationTask.MapCompleted)
            {
                return(false);
            }

            if (_trigger == null || _trigger.Ignored || _trigger.Unwalkable)
            {
                return(false);
            }

            if (!World.CurrentArea.IsMap)
            {
                return(false);
            }

            var pos = _trigger.Position;

            if (pos.Distance > 10 || pos.PathDistance > 12)
            {
                if (!pos.TryCome())
                {
                    GlobalLog.Error($"[ProximityTriggerTask] Fail to move to {pos}. Marking this trigger object as unwalkable.");
                    _trigger.Unwalkable = true;
                }
                return(true);
            }

            await Coroutines.FinishCurrentAction();

            if (_waitFunc != null)
            {
                await _waitFunc();
            }

            _triggerMetadata = null;
            return(true);
        }
示例#7
0
        private void Load()
        {
            if (!File.Exists(SettingsPath))
            {
                return;
            }

            var json = File.ReadAllText(SettingsPath);

            if (string.IsNullOrWhiteSpace(json))
            {
                GlobalLog.Error("[MapBot] Fail to load \"MapSettings.json\". File is empty.");
                return;
            }
            var parts = JsonConvert.DeserializeObject <Dictionary <string, EditablePart> >(json);

            if (parts == null)
            {
                GlobalLog.Error("[MapBot] Fail to load \"MapSettings.json\". Json deserealizer returned null.");
                return;
            }
            foreach (var data in MapList)
            {
                if (parts.TryGetValue(data.Name, out var part))
                {
                    data.Priority                 = part.Priority;
                    data.Ignored                  = part.Ignore;
                    data.IgnoredBossroom          = part.IgnoreBossroom;
                    data.Sextant                  = part.Sextant;
                    data.ZanaMod                  = part.ZanaMod;
                    data.MobRemaining             = part.MobRemaining;
                    data.StrictMobRemaining       = part.StrictMobRemaining;
                    data.ExplorationPercent       = part.ExplorationPercent;
                    data.StrictExplorationPercent = part.StrictExplorationPercent;
                    data.TrackMob                 = part.TrackMob;
                    data.FastTransition           = part.FastTransition;
                }
            }
        }
示例#8
0
        private static async Task <bool> HandleNormalMap(Vector2i mapPos)
        {
            var map = InventoryUi.InventoryControl_Main.Inventory.FindItemByPos(mapPos);

            if (map == null)
            {
                GlobalLog.Error($"[HandleNormalMap] Fail to find a map at {mapPos}.");
                return(false);
            }
            if (map.ShouldUpgrade(Settings.ChiselUpgrade) && HasCurrency(CurrencyNames.Chisel))
            {
                if (!await ApplyChisels(mapPos))
                {
                    return(false);
                }

                UpdateMapReference(mapPos, ref map);
            }
            if (map.ShouldUpgrade(Settings.RareUpgrade) && HasRareOrbs)
            {
                if (!await ApplyOrb(mapPos, CurrencyNames.Alchemy))
                {
                    return(false);
                }

                return(await RerollRare(mapPos));
            }
            if (map.ShouldUpgrade(Settings.MagicUpgrade) && HasMagicOrbs)
            {
                if (!await ApplyOrb(mapPos, CurrencyNames.Transmutation))
                {
                    return(false);
                }

                return(await RerollMagic(mapPos));
            }
            return(true);
        }
示例#9
0
        public async Task <bool> Run()
        {
            if (!World.CurrentArea.IsCombatArea)
            {
                return(false);
            }

            var mapIconOwner = Abyss.CachedData.MapIconOwner;

            if (mapIconOwner == null || mapIconOwner.Unwalkable || mapIconOwner.Ignored)
            {
                return(false);
            }

            var pos = mapIconOwner.Position;

            if (pos.Distance > 10 || pos.PathDistance > 10)
            {
                if (!pos.TryCome())
                {
                    GlobalLog.Error($"[FollowAbyssTask] Fail to move to {pos}. Current abyss map icon owner is unwalkable.");
                    mapIconOwner.Unwalkable = true;
                }
                return(true);
            }
            var attempts = ++mapIconOwner.InteractionAttempts;

            if (attempts > MaxAttempts)
            {
                GlobalLog.Error("[FollowAbyssTask] Abyss map icon owner change timeout. Now ignoring it.");
                mapIconOwner.Ignored = true;
                return(true);
            }
            GlobalLog.Debug($"[FollowAbyssTask] Waiting for abyss map icon owner change ({attempts}/{MaxAttempts})");
            await Wait.Sleep(200);

            return(true);
        }
示例#10
0
        private static void HandleStuck()
        {
            if (StopBotOnStuck)
            {
                GlobalLog.Warn("[StuckDetection] StopBotOnStuck is true. Now reseting stuck counters and stopping the bot.");
                Reset();
                BotManager.Stop();
                return;
            }

            var cache = CombatAreaCache.Current;

            ++cache.StuckCount;

            GlobalLog.Error($"[StuckDetection] Stuck incidents in current area: {cache.StuckCount}");

            if (cache.StuckCount >= Settings.Instance.MaxStucksPerInstance)
            {
                GlobalLog.Error($"[StuckDetection] Too many stuck incidents in current area ({World.CurrentArea.Name}). Now requesting a new instance.");
                EXtensions.AbandonCurrentArea();
            }

            var err = LokiPoe.EscapeState.LogoutToTitleScreen();

            if (err != LokiPoe.EscapeState.LogoutError.None)
            {
                GlobalLog.Error($"[StuckDetection] Logout error: \"{err}\".");
                //stuck incident was not properly handled if we did not logout
                --cache.StuckCount;
            }
            else
            {
                GlobalLog.Info($"[StuckDetection] Triggered event ({cache.StuckCount})");
                Triggered?.Invoke(cache.StuckCount);
                Utility.BroadcastMessage(null, TriggeredMessage);
                Reset();
            }
        }
        public static async Task <bool> TurnInQuest()
        {
            if (!Helpers.PlayerHasQuestItem(QuestItemMetadata.EyeOfDesire))
            {
                return(false);
            }

            if (World.Act4.CrystalVeins.IsCurrentArea)
            {
                var dialla = Helpers.LadyDialla;
                if (dialla == null)
                {
                    GlobalLog.Error("[KingOfDesire] Fail to detect Lady Dialla in Crystal Veins.");
                    ErrorManager.ReportCriticalError();
                    return(true);
                }
                await Helpers.TalkTo(dialla);

                return(true);
            }
            await Travel.To(World.Act4.CrystalVeins);

            return(true);
        }
示例#12
0
        public static async Task <bool> ClearStrand()
        {
            if (_finished)
            {
                return(false);
            }

            if (World.Act6.TwilightStrand.IsCurrentArea)
            {
                if (await TrackMobLogic.Execute())
                {
                    return(true);
                }

                if (!await CombatAreaCache.Current.Explorer.Execute())
                {
                    if (QuestManager.GetState(Quests.FallenFromGrace) <= FinishedStateMinimum)
                    {
                        return(false);
                    }

                    GlobalLog.Error("[ClearTwilightStrand] Twilight Strand is fully explored but not all monsters were killed. Now going to create a new Twilight Strand instance.");

                    Travel.RequestNewInstance(World.Act6.TwilightStrand);

                    if (!await PlayerAction.TpToTown())
                    {
                        ErrorManager.ReportError();
                    }
                }
                return(true);
            }
            await Travel.To(World.Act6.TwilightStrand);

            return(true);
        }
示例#13
0
        private static async Task <bool> OpenDevice()
        {
            if (MapDevice.IsOpen)
            {
                return(true);
            }

            var device = LokiPoe.ObjectManager.MapDevice;

            if (device == null)
            {
                if (World.CurrentArea.IsHideoutArea)
                {
                    GlobalLog.Error("[OpenMapTask] Fail to find Map Device in hideout.");
                }
                else
                {
                    GlobalLog.Error("[OpenMapTask] Unknown error. Fail to find Map Device in Templar Laboratory.");
                }
                GlobalLog.Error("[OpenMapTask] Now stopping the bot because it cannot continue.");
                BotManager.Stop();
                return(false);
            }

            GlobalLog.Debug("[OpenMapTask] Now going to open Map Device.");

            await device.WalkablePosition().ComeAtOnce();

            if (await PlayerAction.Interact(device, () => MapDevice.IsOpen, "Map Device opening"))
            {
                GlobalLog.Debug("[OpenMapTask] Map Device has been successfully opened.");
                return(true);
            }
            GlobalLog.Debug("[OpenMapTask] Fail to open Map Device.");
            return(false);
        }
示例#14
0
        private bool BasicExploration()
        {
            var explorer = BasicExplorer;

            if (!explorer.HasLocation)
            {
                return(false);
            }

            var location = explorer.Location;

            if (LogInterval.Elapsed)
            {
                var distance = LokiPoe.MyPosition.Distance(location);
                var percent  = Math.Round(explorer.PercentComplete, 1);
                GlobalLog.Debug($"[ComplexExplorer] Exploring to the location {location} ({distance}) [{percent} %].");
            }
            if (!PlayerMoverManager.MoveTowards(location))
            {
                GlobalLog.Error($"[ComplexExplorer] MoveTowards failed for {location}. Adding this location to ignore list.");
                explorer.Ignore(location);
            }
            return(true);
        }
示例#15
0
        public static async Task <bool> Kill(NetworkObject bandit)
        {
            if (bandit == null)
            {
                GlobalLog.Error("[KillBandit] Bandit object is null.");
                return(false);
            }

            await bandit.WalkablePosition().ComeAtOnce();

            if (!await OpenBanditPanel(bandit))
            {
                return(false);
            }

            var err = LokiPoe.InGameState.BanditPanel.KillBandit();

            if (err != LokiPoe.InGameState.TalkToBanditResult.None)
            {
                GlobalLog.Error($"[KillBandit] Fail to select \"Kill\" option. Error: \"{err}\".");
                return(false);
            }
            return(true);
        }
示例#16
0
        public async Task <bool> Run()
        {
            if (MapBot.IsOnRun)
            {
                return(false);
            }

            var area = World.CurrentArea;

            if (!area.IsTown && !area.IsHideoutArea)
            {
                return(false);
            }

            if (Settings.StopRequested)
            {
                GlobalLog.Warn("Stopping the bot by a user's request (stop after current map)");
                Settings.StopRequested = false;
                BotManager.Stop();
                return(true);
            }

            var mapTabs = ExSettings.Instance.GetTabsForCategory(ExSettings.StashingCategory.Map);

            Item map;

            foreach (var tab in mapTabs)
            {
                if (!await Inventories.OpenStashTab(tab))
                {
                    ErrorManager.ReportError();
                    return(true);
                }
                if (StashUi.StashTabInfo.IsPremiumMap)
                {
                    GlobalLog.Error("Map stash tab is unsupported and there are no plans to support it in the future. Please remove it from stashing settings.");
                    BotManager.Stop();
                    return(true);
                }

                if ((map = FindProperMap()) != null)
                {
                    goto hasProperMap;
                }

                GlobalLog.Debug($"[TakeMapTask] Fail to find a proper map in \"{tab}\" tab.");
            }

            GlobalLog.Error("[TakeMapTask] Fail to find a proper map in all map tabs. Now stopping the bot because it cannot continue.");
            BotManager.Stop();
            return(true);

hasProperMap:
            GlobalLog.Info($"[TakeMapTask] Map of choice is \"{map.Name}\" (Tier: {map.MapTier})");

            if (!await Inventories.FastMoveFromStashTab(map.LocationTopLeft))
            {
                ErrorManager.ReportError();
                return(true);
            }
            if (!await Wait.For(() => (map = Inventories.InventoryItems.Find(i => i.IsMap())) != null, "map appear in inventory"))
            {
                GlobalLog.Error("[TakeMapTask] Unexpected error. Map did not appear in player's inventory after fast move from stash.");
                return(true);
            }

            var mapPos    = map.LocationTopLeft;
            var mapRarity = map.RarityLite();

            if (mapRarity == Rarity.Unique || !map.IsIdentified || map.IsMirrored || map.IsCorrupted)
            {
                ChooseMap(mapPos);
                return(false);
            }

            switch (mapRarity)
            {
            case Rarity.Normal:
                if (!await HandleNormalMap(mapPos))
                {
                    return(true);
                }
                break;

            case Rarity.Magic:
                if (!await HandleMagicMap(mapPos))
                {
                    return(true);
                }
                break;

            case Rarity.Rare:
                if (!await HandleRareMap(mapPos))
                {
                    return(true);
                }
                break;

            default:
                GlobalLog.Error($"[TakeMapTask] Unknown map rarity: \"{mapRarity}\".");
                ErrorManager.ReportCriticalError();
                return(true);
            }

            UpdateMapReference(mapPos, ref map);

            if (map.ShouldUpgrade(Settings.VaalUpgrade) && HasCurrency(CurrencyNames.Vaal))
            {
                if (!await CorruptMap(mapPos))
                {
                    return(true);
                }

                UpdateMapReference(mapPos, ref map);
            }
            if (map.ShouldUpgrade(Settings.FragmentUpgrade) && _hasFragments)
            {
                await GetFragment();
            }
            ChooseMap(mapPos);
            return(false);
        }
示例#17
0
        private static async Task GetFragment()
        {
            var tabs = new List <string>(ExSettings.Instance.GetTabsForCategory(ExSettings.StashingCategory.Fragment));

            if (tabs.Count > 1 && StashUi.IsOpened)
            {
                var currentTab = StashUi.StashTabInfo.DisplayName;
                var index      = tabs.IndexOf(currentTab);
                if (index > 0)
                {
                    var tab = tabs[index];
                    tabs.RemoveAt(index);
                    tabs.Insert(0, tab);
                }
            }

            foreach (var tab in tabs)
            {
                GlobalLog.Debug($"[TakeMapTask] Looking for Sacrifice Fragment in \"{tab}\" tab.");

                if (!await Inventories.OpenStashTab(tab))
                {
                    return;
                }

                if (StashUi.StashTabInfo.IsPremiumSpecial)
                {
                    var tabType = StashUi.StashTabInfo.TabType;
                    if (tabType == InventoryTabType.Fragment)
                    {
                        foreach (var control in SacrificeControls)
                        {
                            var fragment = control.CustomTabItem;
                            if (fragment != null)
                            {
                                GlobalLog.Debug($"[TakeMapTask] Found \"{fragment.Name}\" in \"{tab}\" tab.");
                                await Inventories.FastMoveFromPremiumStashTab(control);

                                return;
                            }
                            GlobalLog.Debug($"[TakeMapTask] There are no Sacrifice Fragments in \"{tab}\" tab.");
                        }
                    }
                    else
                    {
                        GlobalLog.Error($"[TakeMapTask] Incorrect tab type ({tabType}) for sacrifice fragments.");
                    }
                }
                else
                {
                    var fragment = Inventories.StashTabItems
                                   .Where(i => i.IsSacrificeFragment())
                                   .OrderBy(i => i.Name == "Sacrifice at Midnight") // move midnights to the end of the list
                                   .FirstOrDefault();

                    if (fragment != null)
                    {
                        GlobalLog.Debug($"[TakeMapTask] Found \"{fragment.Name}\" in \"{tab}\" tab.");
                        await Inventories.FastMoveFromStashTab(fragment.LocationTopLeft);

                        return;
                    }
                    GlobalLog.Debug($"[TakeMapTask] There are no Sacrifice Fragments in \"{tab}\" tab.");
                }
            }
            GlobalLog.Info("[TakeMapTask] There are no Sacrifice Fragments in all tabs assigned to them. Now marking them as unavailable.");
            _hasFragments = false;
        }
        private static async Task <bool> CurrencyPurchase(CurrencyInfo currency)
        {
            if (!await TownNpcs.GetCurrencyVendor().OpenPurchasePanel())
            {
                return(false);
            }

            await Wait.SleepSafe(500);

            var name = currency.Name;

            var item = PurchaseUi.InventoryControl.Inventory.Items.Find(i => i.Name == name);

            if (item == null)
            {
                GlobalLog.Error($"[CurrencyPurchase] Unexpected error. Fail to find \"{name}\" in vendor's inventory.");
                return(false);
            }

            var id = item.LocalId;

            using (new InputDelayOverride(10))
            {
                while (currency.Amount > 0)
                {
                    if (BotManager.IsStopping)
                    {
                        GlobalLog.Debug("[CurrencyPurchase] Bot is stopping. Now breaking from purchase loop.");
                        break;
                    }
                    if (!LokiPoe.IsInGame)
                    {
                        GlobalLog.Error("[CurrencyPurchase] Disconnected during currency purchase.");
                        break;
                    }
                    if (!HasInvenotorySpaceForCurrency(name))
                    {
                        GlobalLog.Warn("[CurrencyPurchase] Not enough inventory space.");
                        break;
                    }

                    GlobalLog.Info($"Purchasing \"{name}\" ({currency.Amount})");

                    var moved = PurchaseUi.InventoryControl.FastMove(id);
                    if (moved != FastMoveResult.None)
                    {
                        GlobalLog.Error($"[CurrencyPurchase] Fail to purchase. Error: \"{moved}\".");
                        return(false);
                    }

                    --currency.Amount;
                }
            }

            if (currency.Amount == 0)
            {
                CurrencyToBuy.RemoveAt(0);
            }

            return(true);
        }
示例#19
0
        public static async Task <bool> FullTabCheck()
        {
            if (!await Inventories.OpenStash())
            {
                return(false);
            }

            var actualTabs  = StashUi.TabControl.TabNames;
            var cleanedTabs = new List <Settings.FullTabInfo>();

            foreach (var tab in Settings.Instance.FullTabs)
            {
                var tabName = tab.Name;

                if (!actualTabs.Contains(tabName))
                {
                    GlobalLog.Warn($"[FullTabCheck] \"{tabName}\" tab no longer exists. Removing it from full tab list.");
                    cleanedTabs.Add(tab);
                    continue;
                }

                if (!await Inventories.OpenStashTab(tabName))
                {
                    return(false);
                }

                // Can happen only if user gave map tab a name that was marked as full previously
                if (StashUi.StashTabInfo.IsPremiumMap)
                {
                    GlobalLog.Error("[FullTabCheck] Map tab is unsupported. Removing it from full tab list.");
                    cleanedTabs.Add(tab);
                    continue;
                }

                if (StashUi.StashTabInfo.IsPremiumSpecial)
                {
                    var controlsMetadata = tab.ControlsMetadata;
                    var cleanedControls  = new List <string>();

                    foreach (var metadata in controlsMetadata)
                    {
                        if (PremiumTabCanFit(metadata))
                        {
                            GlobalLog.Warn($"[FullTabCheck] \"{tabName}\" tab is no longer full for \"{metadata}\".");
                            cleanedControls.Add(metadata);
                        }
                        else
                        {
                            GlobalLog.Warn($"[FullTabCheck] \"{tabName}\" tab is still full for \"{metadata}\".");
                        }
                    }
                    foreach (var cleaned in cleanedControls)
                    {
                        controlsMetadata.Remove(cleaned);
                    }
                    if (controlsMetadata.Count == 0)
                    {
                        GlobalLog.Warn($"[FullTabCheck] \"{tabName}\" tab does not contain any controls metadata. Removing it from full tab list.");
                        cleanedTabs.Add(tab);
                    }
                }
                else
                {
                    if (StashUi.InventoryControl.Inventory.AvailableInventorySquares >= 8)
                    {
                        GlobalLog.Warn($"[FullTabCheck] \"{tabName}\" tab is no longer full.");
                        cleanedTabs.Add(tab);
                    }
                    else
                    {
                        GlobalLog.Warn($"[FullTabCheck] \"{tabName}\" tab is still full.");
                    }
                }
            }
            foreach (var tab in cleanedTabs)
            {
                Settings.Instance.FullTabs.Remove(tab);
            }
            return(true);
        }
示例#20
0
        public static async Task <bool> Execute(int range = -1)
        {
            var cachedMonsters = CombatAreaCache.Current.Monsters;

            if (CurrentTarget == null)
            {
                CurrentTarget = range == -1
                    ? cachedMonsters.ClosestValid()
                    : cachedMonsters.ClosestValid(m => m.Position.Distance <= range);

                if (CurrentTarget == null)
                {
                    return(false);
                }
            }

            if (Blacklist.Contains(CurrentTarget.Id))
            {
                GlobalLog.Debug("[TrackMobLogic] Current target is in global blacklist. Now abandoning it.");
                CurrentTarget.Ignored = true;
                CurrentTarget         = null;
                return(true);
            }

            var pos = CurrentTarget.Position;

            if (pos.IsFar || pos.IsFarByPath)
            {
                if (LogInterval.Elapsed)
                {
                    GlobalLog.Debug($"[TrackMobTask] Cached monster locations: {cachedMonsters.Valid().Count()}");
                    GlobalLog.Debug($"[TrackMobTask] Moving to {pos}");
                }
                if (!PlayerMoverManager.MoveTowards(pos))
                {
                    GlobalLog.Error($"[TrackMobTask] Fail to move to {pos}. Marking this monster as unwalkable.");
                    CurrentTarget.Unwalkable = true;
                    CurrentTarget            = null;
                }
                return(true);
            }

            var monsterObj = CurrentTarget.Object as Monster;

            // Untested fix to not wait on a captured beast. Will be changed once confirmed issue is solved.
            //if (monsterObj == null || monsterObj.IsDead || (Loki.Game.LokiPoe.InstanceInfo.Bestiary.IsActive && (monsterObj.HasBestiaryCapturedAura || monsterObj.HasBestiaryDisappearingAura)))

            if (monsterObj == null || monsterObj.IsDead)
            {
                cachedMonsters.Remove(CurrentTarget);
                CurrentTarget = null;
            }
            else
            {
                var attempts = ++CurrentTarget.InteractionAttempts;
                if (attempts > MaxKillAttempts)
                {
                    GlobalLog.Error("[TrackMobTask] All attempts to kill current monster have been spent. Now ignoring it.");
                    CurrentTarget.Ignored = true;
                    CurrentTarget         = null;
                    return(true);
                }
                GlobalLog.Debug($"[TrackMobTask] Alive monster is nearby, this is our {attempts}/{MaxKillAttempts} attempt to kill it.");
                await Coroutine.Sleep(200);
            }
            return(true);
        }
示例#21
0
        private static async Task ProcessShrine()
        {
            //check if current shrine was blacklisted by Combat Routine
            if (Blacklist.Contains(_shrine.Id))
            {
                GlobalLog.Error("[OpenChestTask] Current shrine was blacklisted from outside.");
                _shrine.Ignored = true;
                _shrine         = null;
                return;
            }

            var pos = _shrine.Position;

            if (Settings.ShrineOpenRange != -1)
            {
                if (pos.Distance > Settings.ShrineOpenRange * AbandonDistanceMult)
                {
                    GlobalLog.Debug("[OpenChestTask] Abandoning current shrine because its too far away.");
                    TemporaryIgnore(_shrine.Id);
                    _shrine = null;
                    return;
                }
            }

            if (pos.IsFar)
            {
                if (!pos.TryCome())
                {
                    GlobalLog.Error($"[OpenChestTask] Fail to move to {pos}. Marking this shrine as unwalkable.");
                    _shrine.Unwalkable = true;
                    _shrine            = null;
                }
                return;
            }
            var shrineObj = _shrine.Object as Shrine;

            if (shrineObj == null || shrineObj.IsDeactivated)
            {
                CombatAreaCache.Current.Shrines.Remove(_shrine);
                _shrine = null;
                return;
            }
            var attempts = ++_shrine.InteractionAttempts;

            if (attempts > MaxShrineAttempts)
            {
                GlobalLog.Error("[OpenChestTask] All attempts to take a shrine have been spent. Now ignoring it.");
                _shrine.Ignored = true;
                _shrine         = null;
                return;
            }
            if (await PlayerAction.Interact(shrineObj))
            {
                await Wait.LatencySleep();

                if (await Wait.For(() => shrineObj.IsDeactivated, "shrine deactivation", 100, 400))
                {
                    CombatAreaCache.Current.Shrines.Remove(_shrine);
                    _shrine = null;
                }
                return;
            }
            await Wait.SleepSafe(400);
        }
        private static async Task HandleCorruptedArea()
        {
            var cachedData = CachedData;

            if (cachedData.IsAreaFinished)
            {
                DisableEntrance();

                var exit = LokiPoe.ObjectManager.Objects.Closest <AreaTransition>(a => a.IsTargetable);

                if (exit != null && await PlayerAction.TakeTransition(exit))
                {
                    return;
                }

                await PlayerAction.TpToTown();

                return;
            }

            var vessel = cachedData.Vessel;

            if (vessel != null && cachedData.IsBossKilled)
            {
                var pos = vessel.Position;
                if (pos.IsFar || pos.IsFarByPath)
                {
                    if (!pos.TryCome())
                    {
                        GlobalLog.Error("[CorruptedAreaTask] Unexpected error. Vaal Vessel position is unwalkable.");
                        cachedData.IsAreaFinished = true;
                    }
                    return;
                }
                var vesselObj = vessel.Object as Chest;
                if (vesselObj == null)
                {
                    GlobalLog.Error("[CorruptedAreaTask] Unexpected error. Vaal Vessel object is null.");
                    cachedData.IsAreaFinished = true;
                    return;
                }
                var attempts = ++vessel.InteractionAttempts;
                if (attempts > 5)
                {
                    GlobalLog.Error("[CorruptedAreaTask] All attempts to open Vaal Vessel have been spent.");
                    cachedData.IsAreaFinished = true;
                    return;
                }
                if (await PlayerAction.Interact(vesselObj, () => vesselObj.Fresh().IsOpened, "Vaal Vessel opening"))
                {
                    await Wait.For(() => ExitExists, "corrupted area exit spawning", 500, 5000);
                }
                else
                {
                    await Wait.SleepSafe(500);
                }
                return;
            }

            var boss = cachedData.Boss;

            if (boss != null)
            {
                var pos = boss.Position;
                if (pos.IsFar || pos.IsFarByPath)
                {
                    if (!pos.TryCome())
                    {
                        GlobalLog.Error("[CorruptedAreaTask] Unexpected error. Corrupted area boss is unwalkable.");
                        cachedData.IsAreaFinished = true;
                    }
                    return;
                }
                var bossObj = boss.Object;
                if (bossObj == null)
                {
                    GlobalLog.Warn("[CorruptedAreaTask] There is no boss object near cached position. Marking it as dead.");
                    cachedData.IsBossKilled = true;
                    return;
                }
                GlobalLog.Debug("[CorruptedAreaTask] Waiting for combat routine to kill the boss.");
                await Wait.StuckDetectionSleep(500);

                return;
            }

            if (!await CombatAreaCache.Current.Explorer.Execute())
            {
                GlobalLog.Warn("[CorruptedAreaTask] Now leaving the corrupted area because it is fully explored.");
                cachedData.IsAreaFinished = true;
            }
        }
示例#23
0
        public async Task <bool> Run()
        {
            if (!World.CurrentArea.IsCombatArea)
            {
                return(false);
            }

            if (AbyssChest == null)
            {
                if ((AbyssChest = Abyss.CachedData.Chests.ClosestValid()) == null)
                {
                    return(false);
                }
            }

            var pos = AbyssChest.Position;

            if (pos.IsFar || pos.IsFarByPath)
            {
                if (!pos.TryCome())
                {
                    GlobalLog.Error($"[OpenAbyssChest] Fail to move to {pos}. Abyssal Trove position is unwalkable.");
                    AbyssChest.Unwalkable = true;
                    AbyssChest            = null;
                }
                return(true);
            }
            var chestObj = AbyssChest.Object as Chest;

            if (chestObj == null)
            {
                GlobalLog.Error("[OpenAbyssChest] Unexpected error. Chest object is null.");
                AbyssChest.Ignored = true;
                AbyssChest         = null;
                return(true);
            }
            if (!chestObj.IsTargetable)
            {
                GlobalLog.Debug("[OpenAbyssChest] Waiting for Abyssal Trove activation.");
                await Wait.Sleep(500);

                return(true);
            }
            var attempts = ++AbyssChest.InteractionAttempts;

            if (attempts > MaxAttempts)
            {
                GlobalLog.Error($"[OpenAbyssChest] All attempts to interact with {pos.Name} have been spent. Now ignoring it.");
                AbyssChest.Ignored = true;
                AbyssChest         = null;
                return(true);
            }
            if (await PlayerAction.Interact(chestObj, () => chestObj.Fresh().IsOpened, "Abyssal Trove opening", 1500))
            {
                Abyss.CachedData.Chests.Remove(AbyssChest);
                AbyssChest = null;
                await Wait.Sleep(1500);

                BotStructure.TaskManager.GetTaskByName("LootItemTask")?.Message(new Message("ResetCurrentItem"));
            }
            else
            {
                await Wait.StuckDetectionSleep(500);
            }
            return(true);
        }
示例#24
0
        public async Task <bool> Run()
        {
            var area = World.CurrentArea;

            if (!area.IsTown && !area.IsHideoutArea)
            {
                return(false);
            }

            var itemsToId  = new List <Vector2i>();
            var itemFilter = ItemEvaluator.Instance;

            foreach (var item in Inventories.InventoryItems)
            {
                if (item.IsIdentified || item.IsCorrupted || item.IsMirrored)
                {
                    continue;
                }

                if (!itemFilter.Match(item, EvaluationType.Id))
                {
                    continue;
                }

                itemsToId.Add(item.LocationTopLeft);
            }

            if (itemsToId.Count == 0)
            {
                GlobalLog.Info("[IdTask] No items to identify.");
                return(false);
            }

            GlobalLog.Info($"[IdTask] {itemsToId.Count} items to id.");

            int scrollsAmount = LokiPoe.InstanceInfo.GetPlayerInventoryBySlot(InventorySlot.Main).ItemAmount(CurrencyNames.Wisdom);

            if (scrollsAmount == 0 && Inventories.AvailableInventorySquares == 0)
            {
                GlobalLog.Error("[IdTask] No id scrolls and no free space in inventory. Now stopping the bot because it cannot continue.");
                BotManager.Stop();
                return(true);
            }

            GlobalLog.Info($"[IdTask] {scrollsAmount} id scrolls.");

            if (scrollsAmount < itemsToId.Count)
            {
                GlobalLog.Warn("[IdTask] Not enough id scrolls to identify all items. Now going to take them from stash.");

                var result = await Inventories.WithdrawCurrency(CurrencyNames.Wisdom);

                if (result == WithdrawResult.Error)
                {
                    ErrorManager.ReportError();
                    return(true);
                }
                if (result == WithdrawResult.Unavailable)
                {
                    GlobalLog.Error("[IdTask] There are no id scrolls in all tabs assigned to them. Now stopping the bot because it cannot continue.");
                    BotManager.Stop();
                    return(true);
                }
            }

            if (!await Inventories.OpenInventory())
            {
                ErrorManager.ReportError();
                return(true);
            }

            itemsToId.Sort(Position.Comparer.Instance);

            foreach (var pos in itemsToId)
            {
                if (!await Identify(pos))
                {
                    ErrorManager.ReportError();
                    return(true);
                }
            }
            await Coroutines.CloseBlockingWindows();

            return(true);
        }
        public async Task <bool> Run()
        {
            if (ErrorLimitReached)
            {
                return(false);
            }

            var area = World.CurrentArea;

            if (!area.IsTown && !area.IsHideoutArea)
            {
                return(false);
            }

            var errors = GetUserErrors();

            if (errors.Count > 0)
            {
                foreach (var error in errors)
                {
                    GlobalLog.Error(error);
                }
                BotManager.Stop();
                return(true);
            }

            var currencyToRestock = new List <string>();

            foreach (var currency in Settings.Instance.InventoryCurrencies)
            {
                var name    = currency.Name;
                var restock = currency.Restock;

                if (restock < 0)
                {
                    continue;
                }

                GetCurrentAndMaxStackCount(name, out var count, out var maxStackCount);

                if (restock >= maxStackCount)
                {
                    GlobalLog.Error($"[InventoryCurrency] Invalid restock value for \"{name}\". Restock: {restock}. Max stack count: {maxStackCount}.");
                    GlobalLog.Error("[InventoryCurrency] Restock value must be less than item's max stack count. Please correct your settings.");
                    BotManager.Stop();
                    return(true);
                }

                if (count > restock)
                {
                    continue;
                }

                if (Unavailable.Contains(name))
                {
                    GlobalLog.Debug($"[CurrencyRestockTask] Skipping \"{name}\" restock because it is marked as unavailable.");
                    continue;
                }

                GlobalLog.Debug($"[CurrencyRestockTask] Restock is needed for \"{name}\". Current count: {count}. Count to restock: {restock}.");
                currencyToRestock.Add(name);
            }

            if (currencyToRestock.Count == 0)
            {
                GlobalLog.Info("[CurrencyRestockTask] No currency to restock.");
                return(false);
            }

            foreach (var currency in currencyToRestock)
            {
                GlobalLog.Debug($"[CurrencyRestockTask] Now going to restock \"{currency}\".");

                var result = await Inventories.WithdrawCurrency(currency);

                if (result == WithdrawResult.Error)
                {
                    ReportError();
                    return(true);
                }
                if (result == WithdrawResult.Unavailable)
                {
                    GlobalLog.Warn($"[CurrencyRestockTask] There are no \"{currency}\" in all tabs assigned to them. Now marking this currency as unavailable.");
                    Unavailable.Add(currency);
                }
            }
            await Coroutines.CloseBlockingWindows();

            return(true);
        }
        public async Task <bool> Run()
        {
            if (!settings.Instance.ExaltRecipeEnabled)
            {
                return(false);
            }

            if (_shaperStashTabIsFull || ErrorLimitReached)
            {
                return(false);
            }

            var area = World.CurrentArea;

            if (!area.IsTown && !area.IsHideoutArea)
            {
                return(false);
            }

            if (_shouldUpdateStashData)
            {
                GlobalLog.Debug("[StashRecipeTask] Updating shaper exalt recipe stash data (every Start)");

                if (settings.Instance.ExaltRecipeEnabled)
                {
                    if (!await OpenShaperRecipeTab())
                    {
                        return(true);
                    }
                    else
                    {
                        Class1.ShaperStashData.SyncWithStashTab(RecipeData.ExaltRecipeItemType.Shaper);
                    }
                }

                _shouldUpdateStashData = false;
            }
            else
            {
                if (AnyItemToStash)
                {
                    GlobalLog.Debug("[StashRecipeTask] Updating shaper exalt recipe stash data before actually stashing the items.");

                    if (settings.Instance.ExaltRecipeEnabled)
                    {
                        if (!await OpenShaperRecipeTab())
                        {
                            return(true);
                        }
                        else
                        {
                            Class1.ShaperStashData.SyncWithStashTab(RecipeData.ExaltRecipeItemType.Shaper);
                        }
                    }
                }
                else
                {
                    GlobalLog.Info("[StashRecipeTask] AnyItemsToStash empty, No items to stash for shaper exalt recipe.");
                    await Coroutines.CloseBlockingWindows();

                    return(false);
                }
            }

            var itemsToStash = ItemsToStash;

            if (itemsToStash.Count == 0)
            {
                GlobalLog.Info("[StashRecipeTask] No items to stash for shaper exalt recipe.");
                await Coroutines.CloseBlockingWindows();

                return(false);
            }

            GlobalLog.Info($"[StashRecipeTask] {itemsToStash.Count} items to stash for shaper exalt recipe.");

            await OpenShaperRecipeTab();

            // Shaper Loop
            foreach (var item in itemsToStash.OrderBy(i => i.Position, Position.Comparer.Instance))
            {
                if (item.RecipeType != RecipeData.ExaltRecipeItemType.Shaper)
                {
                    continue;
                }

                GlobalLog.Debug($"[StashRecipeTask] Now stashing \"{item.Name}\" for shaper exalt recipe.");
                var itemPos = item.Position;
                if (!Inventories.StashTabCanFitItem(itemPos))
                {
                    GlobalLog.Error("[StashRecipeTask] Stash tab for shaper exalt recipe is full and must be cleaned.");
                    _shaperStashTabIsFull = true;
                    return(true);
                }
                if (!await Inventories.FastMoveFromInventory(itemPos))
                {
                    ReportError();
                    return(true);
                }
                Class1.ShaperStashData.IncreaseItemCount(item.ItemType);
                GlobalLog.Info($"[Events] Item stashed ({item.FullName})");
                Utility.BroadcastMessage(this, Events.Messages.ItemStashedEvent, item);
            }
            await Wait.SleepSafe(300);

            Class1.ShaperStashData.SyncWithStashTab(RecipeData.ExaltRecipeItemType.Shaper);
            Class1.ShaperStashData.Log();
            return(true);
        }
示例#27
0
        public async Task <bool> Run()
        {
            var area = World.CurrentArea;

            if (!area.IsMap && !area.IsOverworldArea)
            {
                return(false);
            }

            if (LokiPoe.InstanceInfo.Incursion.IncursionsRemaining != 0)
            {
                return(false);
            }

            var cache = CombatAreaCache.Current;

            if (cache.Storage["IsTempleCompleted"] != null)
            {
                return(false);
            }

            var alva = Incursion.CachedAlva;

            if (alva == null || alva.Unwalkable || alva.Ignored)
            {
                return(false);
            }

            var pos = alva.Position;

            if (pos.Distance > 20 || pos.PathDistance > 20)
            {
                if (!pos.TryCome())
                {
                    GlobalLog.Error($"[EnterTempleTask] Fail to move to {pos}. Alva is unwalkable.");
                    alva.Unwalkable = true;
                }
                return(true);
            }

            var alvaObj = alva.Object;

            if (alvaObj == null)
            {
                GlobalLog.Error("[EnterTempleTask] Unexpected error. We are near cached Alva, but actual object is null.");
                alva.Ignored = true;
                return(true);
            }

            var portal = ActiveTemplePortal;

            if (portal == null)
            {
                var attempts = ++alva.InteractionAttempts;
                if (attempts > 7)
                {
                    GlobalLog.Error("[EnterTempleTask] All attempts to interact with Alva have been spent.");
                    alva.Ignored = true;
                    return(true);
                }
                if (alvaObj.HasNpcFloatingIcon)
                {
                    if (await PlayerAction.Interact(alvaObj))
                    {
                        await Wait.Sleep(200);

                        await Coroutines.CloseBlockingWindows();
                    }
                    return(true);
                }
                if (await alvaObj.AsTownNpc().Converse("Enter Temple"))
                {
                    await Coroutines.CloseBlockingWindows();

                    await Wait.For(() => ActiveTemplePortal != null, "Temple portals activation", 500, 10000);
                }
                return(true);
            }

            if (Settings.Instance.SkipTemple)
            {
                cache.Storage["IsTempleCompleted"] = true;
                return(true);
            }

            if (ErrorManager.GetErrorCount("EnterTemple") >= 5)
            {
                GlobalLog.Error("[EnterTempleTask] Failed to enter Temple portal 5 times.");
                alva.Ignored = true;
                return(true);
            }
            if (!await PlayerAction.TakePortal(portal))
            {
                ErrorManager.ReportError("EnterTemple");
                await Wait.SleepSafe(500);
            }
            return(true);
        }
示例#28
0
        public async Task <bool> Run()
        {
            if (ErrorLimitReached)
            {
                return(false);
            }

            var area = World.CurrentArea;

            if (!area.IsTown && !area.IsHideoutArea)
            {
                return(false);
            }

            if (_shouldUpdateStashData)
            {
                GlobalLog.Debug("[SellExtraMapsTask] Updating map tab stash data (every Start)");

                if (!await OpenMapTab())
                {
                    return(true);
                }
                else
                {
                    Class1.MapStashData.SyncWithStashTab();
                }
                _shouldUpdateStashData = false;
            }
            else
            {
                if (!await Inventories.OpenStashTab(settings.Instance.MapStashTab))
                {
                    ReportError();
                    return(true);
                }

                Class1.MapStashData.SyncWithStashTab();

                if (Class1.MapStashData.GetMapCount() > settings.Instance.MaxNumMaps)
                {
                    var mapsInStashTab = Inventories.StashTabItems
                                         .Where(m => m.IsMap() && m.Rarity != Rarity.Unique)
                                         .OrderBy(m => m.Priority())
                                         .ThenBy(m => m.MapTier).Take(settings.Instance.SellNumMaps);



                    foreach (var mapItem in mapsInStashTab)
                    {
                        if (!await Inventories.FastMoveFromStashTab(mapItem.LocationTopLeft))
                        {
                            ReportError();
                            return(true);
                        }
                    }

                    await Wait.SleepSafe(300);

                    Class1.MapStashData.SyncWithStashTab();

                    var mapSellItems = Inventories.InventoryItems
                                       .Where(m => m.IsMap())
                                       .Select(i => i.LocationTopLeft)
                                       .ToList();

                    if (mapSellItems.Count == 0)
                    {
                        GlobalLog.Error("[SellExtraMapsTask] Unknown error. There are no map items in player's inventory after taking them from stash.");
                        ReportError();
                        return(true);
                    }

                    if (!await TownNpcs.SellItems(mapSellItems))
                    {
                        ReportError();
                    }
                }
                else
                {
                    return(false);
                }
            }

            return(true);
        }
示例#29
0
        public async Task <bool> Run()
        {
            if (MapBot.IsOnRun)
            {
                return(false);
            }

            var area = World.CurrentArea;

            if (!area.IsTown && !area.IsHideoutArea)
            {
                return(false);
            }

            MapExtensions.AtlasData.Update();

            if (!Settings.SellEnabled)
            {
                return(false);
            }

            if (Inventories.AvailableInventorySquares < 3)
            {
                GlobalLog.Error("[SellMapTask] Not enough inventory space.");
                return(false);
            }

            var firstMapTab = ExSettings.Instance.GetTabsForCategory(ExSettings.StashingCategory.Map).First();

            if (!await Inventories.OpenStashTab(firstMapTab))
            {
                GlobalLog.Error($"[SellMapTask] Fail to open stash tab \"{firstMapTab}\".");
                return(false);
            }

            var maps = Inventories.StashTabItems
                       .Where(m => m.IsMap() && m.ShouldSell())
                       .OrderBy(m => m.Priority())
                       .ThenBy(m => m.MapTier)
                       .ToList();

            if (maps.Count == 0)
            {
                return(false);
            }

            var mapGroups = new List <Item[]>();

            foreach (var mapGroup in maps.GroupBy(m => m.Name))
            {
                var groupList = mapGroup.ToList();
                for (int i = 3; i <= groupList.Count; i += 3)
                {
                    var group = new Item[3];
                    group[0] = groupList[i - 3];
                    group[1] = groupList[i - 2];
                    group[2] = groupList[i - 1];
                    mapGroups.Add(group);
                }
            }
            if (mapGroups.Count == 0)
            {
                GlobalLog.Info("[SellMapTask] No map group for sale was found.");
                return(false);
            }

            GlobalLog.Info($"[SellMapTask] Map groups for sale: {mapGroups.Count}");

            foreach (var mapGroup in mapGroups)
            {
                if (Inventories.AvailableInventorySquares < 3)
                {
                    GlobalLog.Error("[SellMapTask] Not enough inventory space.");
                    break;
                }

                //exclude ignored maps from min map amount check, if sell ignored maps is enabled
                if (!Settings.SellIgnoredMaps || !mapGroup[0].Ignored())
                {
                    int mapAmount = Inventories.StashTabItems.Count(i => i.IsMap() && i.Rarity != Rarity.Unique);
                    if ((mapAmount - 3) < Settings.MinMapAmount)
                    {
                        GlobalLog.Warn($"[SellMapTask] Min map amount is reached {mapAmount}(-3) from required {Settings.MinMapAmount}");
                        break;
                    }
                }

                for (int i = 0; i < 3; i++)
                {
                    var map = mapGroup[i];
                    GlobalLog.Info($"[SellMapTask] Now getting {i + 1}/{3} \"{map.Name}\".");
                    if (!await Inventories.FastMoveFromStashTab(map.LocationTopLeft))
                    {
                        ErrorManager.ReportError();
                        return(true);
                    }
                }
            }

            await Wait.SleepSafe(200);

            var forSell = Inventories.InventoryItems.Where(i => i.IsMap()).Select(m => m.LocationTopLeft).ToList();

            if (forSell.Count == 0)
            {
                return(false);
            }

            if (!await TownNpcs.SellItems(forSell))
            {
                ErrorManager.ReportError();
            }

            return(true);
        }
示例#30
0
        private async Task EnterTransition()
        {
            var pos = _transition.Position;

            if (pos.IsFar)
            {
                if (!pos.TryCome())
                {
                    GlobalLog.Debug($"[ComplexExplorer] Fail to move to {pos}. Marking this transition as unwalkable.");
                    _transition.Unwalkable = true;
                    _transition            = null;
                }
                return;
            }
            var transitionObj = _transition.Object;

            if (transitionObj == null)
            {
                GlobalLog.Error("[ComplexExplorer] Unknown error. There is no transition near cached position.");
                _transition.Ignored = true;
                _transition         = null;
                return;
            }
            if (!transitionObj.IsTargetable)
            {
                if (transitionObj.Metadata.Contains("sarcophagus_transition"))
                {
                    if (await HandleSarcophagus() && transitionObj.Fresh().IsTargetable)
                    {
                        return;
                    }
                }
                if (_transition.InteractionAttempts >= MaxTransitionAttempts)
                {
                    GlobalLog.Error("[ComplexExplorer] Area transition did not become targetable. Now ignoring it.");
                    _transition.Ignored = true;
                    _transition         = null;
                    return;
                }
                var attempts = ++_transition.InteractionAttempts;
                GlobalLog.Debug($"[ComplexExplorer] Waiting for \"{pos.Name}\" to become targetable ({attempts}/{MaxTransitionAttempts})");
                await Wait.SleepSafe(1000);

                return;
            }

            _disableTick = true;
            if (!await PlayerAction.TakeTransition(transitionObj))
            {
                var attempts = ++_transition.InteractionAttempts;
                GlobalLog.Error($"[ComplexExplorer] Fail to enter {pos}. Attempt: {attempts}/{MaxTransitionAttempts}");
                if (attempts >= MaxTransitionAttempts)
                {
                    GlobalLog.Error("[ComplexExplorer] All attempts to enter an area transition have been spent. Now ignoring it.");
                    _transition.Ignored = true;
                    _transition         = null;
                }
                else
                {
                    await Wait.SleepSafe(500);
                }
            }
            else
            {
                PostEnter();

                GlobalLog.Info("[ComplexExplorer] LocalTransitionEntered event.");
                LocalTransitionEntered?.Invoke();
                Utility.BroadcastMessage(this, LocalTransitionEnteredMessage);

                if (Settings.OpenPortals)
                {
                    GlobalLog.Info("[ComplexExplorer] Opening a portal.");
                    await PlayerAction.CreateTownPortal();
                }
            }
            _disableTick = false;
        }