public static async Task <bool> Interact(NetworkObject obj, int attempts) { if (obj == null) { GlobalLog.Error("[Interact] Object for interaction is null."); return(false); } var name = obj.Name; GlobalLog.Debug($"[Interact] Now going to interact with \"{name}\"."); for (int i = 1; i <= attempts; i++) { if (!LokiPoe.IsInGame || LokiPoe.Me.IsDead) { break; } await Coroutines.CloseBlockingWindows(); await Coroutines.FinishCurrentAction(); await Wait.LatencySleep(); if (await Coroutines.InteractWith(obj)) { GlobalLog.Debug($"[Interact] \"{name}\" has been successfully interacted."); return(true); } GlobalLog.Error($"[Interact] Fail to interact with \"{name}\". Attempt: {i}/{attempts}."); await Wait.SleepSafe(100, 200); } return(false); }
public static async Task <bool> Interact(NetworkObject obj, Func <bool> success, string desc, int timeout = 3000) { if (obj == null) { GlobalLog.Error("[Interact] Object for interaction is null."); return(false); } var name = obj.Name; GlobalLog.Debug($"[Interact] Now going to interact with \"{name}\"."); await Coroutines.CloseBlockingWindows(); await Coroutines.FinishCurrentAction(); await Wait.LatencySleep(); if (await Coroutines.InteractWith(obj)) { if (!await Wait.For(success, desc, 100, timeout)) { return(false); } GlobalLog.Debug($"[Interact] \"{name}\" has been successfully interacted."); return(true); } GlobalLog.Error($"[Interact] Fail to interact with \"{name}\"."); await Wait.SleepSafe(300, 500); return(false); }
/// <summary> /// This coroutine interacts with the waypoint and waits for the world panel to open. When called from a hideout, /// the waypoint must be in spawn range, otherwise the coroutine will fail. The movement is done without returning, /// so this should be carefully used when not in town. /// </summary> /// <returns>An OpenStashError that describes the result.</returns> public static async Task <Results.OpenWaypointError> OpenWaypoint() { await Coroutines.CloseBlockingWindows(); await Coroutines.FinishCurrentAction(); var waypoint = LokiPoe.ObjectManager.Waypoint; if (waypoint == null) { if (!LokiPoe.Me.IsInTown) { return(Results.OpenWaypointError.NoWaypoint); } if ( !await Navigation.MoveToLocation(ExilePather.FastWalkablePositionFor(Actor.GuessWaypointLocation()), 25, 60000, () => LokiPoe.ObjectManager.Waypoint != null)) { return(Results.OpenWaypointError.CouldNotMoveToWaypoint); } waypoint = LokiPoe.ObjectManager.Waypoint; if (waypoint == null) { return(Results.OpenWaypointError.NoWaypoint); } } if (ExilePather.PathDistance(LokiPoe.MyPosition, waypoint.Position) > 30) { if (!await Navigation.MoveToLocation(ExilePather.FastWalkablePositionFor(waypoint.Position), 25, 15000, () => false)) { return(Results.OpenWaypointError.CouldNotMoveToWaypoint); } } await Coroutines.FinishCurrentAction(); waypoint = LokiPoe.ObjectManager.Waypoint; if (waypoint == null) { return(Results.OpenWaypointError.NoWaypoint); } if (!await InteractWith(waypoint)) { return(Results.OpenWaypointError.InteractFailed); } if (!await WaitForWorldPanel()) { return(Results.OpenWaypointError.WorldPanelDidNotOpen); } await Coroutine.Sleep(1000); // Adding this in to let the gui load more return(Results.OpenWaypointError.None); }
public static async Task AtOnce(Vector2i pos, string destination, int minDistance = 20) { if (LokiPoe.MyPosition.Distance(pos) <= minDistance) { return; } while (LokiPoe.MyPosition.Distance(pos) > minDistance) { if (LogInterval.Elapsed) { await Coroutines.CloseBlockingWindows(); GlobalLog.Debug($"[MoveAtOnce] Moving to {destination} at {pos} (distance: {LokiPoe.MyPosition.Distance(pos)})"); } if (!LokiPoe.IsInGame || LokiPoe.Me.IsDead || BotManager.IsStopping) { return; } TowardsWalkable(pos, destination); await Wait.Sleep(50); } await Coroutines.FinishCurrentAction(); }
public static async Task TalkTo(NetworkObject npc) { if (await npc.AsTownNpc().Talk()) { await Coroutines.CloseBlockingWindows(); } else { ErrorManager.ReportError(); } }
public async Task <bool> Run() { if (!Enabled || !World.CurrentArea.IsCombatArea) { return(false); } var chests = LokiPoe.ObjectManager.Objects .Where <Chest>(c => c.Distance <= 10 && !c.IsOpened && c.IsStompable && !Processed.Contains(c.Id)) .OrderBy(c => c.DistanceSqr) .ToList(); if (chests.Count == 0) { //run this task only once, by StuckDetection demand Enabled = false; return(false); } LokiPoe.ProcessHookManager.Reset(); await Coroutines.CloseBlockingWindows(); var positions1 = new List <Vector2i> { LokiPoe.MyPosition }; var positions2 = new List <Vector2> { LokiPoe.MyWorldPosition }; foreach (var chest in chests) { Processed.Add(chest.Id); positions1.Add(chest.Position); positions2.Add(chest.WorldPosition); } foreach (var position in positions1) { MouseManager.SetMousePos("EXtensions.CommonTasks.HandleBlockingChestsTask", position); await Click(); } foreach (var position in positions2) { MouseManager.SetMousePos("EXtensions.CommonTasks.HandleBlockingChestsTask", position); await Click(); } return(true); }
public static async Task <bool> TalkToSin() { if (World.Act9.Highgate.IsCurrentArea) { if (!await Helpers.Sin_A9.Talk()) { ErrorManager.ReportError(); return(true); } await Coroutines.CloseBlockingWindows(); return(false); } await Travel.To(World.Act9.Highgate); return(true); }
public static async Task <bool> UseQuestItem(string metadata) { var item = Inventories.InventoryItems.Find(i => i.Class == ItemClasses.QuestItem && i.Metadata.ContainsIgnorecase(metadata)); if (item == null) { GlobalLog.Error($"[UseQuestItem] Fail to find item with metadata \"{metadata}\" in inventory."); return(false); } var pos = item.LocationTopLeft; var id = item.LocalId; var name = item.Name; GlobalLog.Debug($"[UseQuestItem] Now going to use \"{name}\"."); if (!await Inventories.OpenInventory()) { ErrorManager.ReportError(); return(false); } var err = InventoryUi.InventoryControl_Main.UseItem(id); if (err != UseItemResult.None) { GlobalLog.Error($"[UseQuestItem] Fail to use \"{name}\". Error: \"{err}\"."); return(false); } if (!await Wait.For(() => InventoryUi.InventoryControl_Main.Inventory.FindItemByPos(pos) == null, "quest item despawn", 100, 2000)) { return(false); } if (LokiPoe.InGameState.CursorItemOverlay.Item != null) { GlobalLog.Error($"[UseQuestItem] Error. \"{name}\" has been picked to cursor."); return(false); } await Wait.SleepSafe(500); //give a quest state time to update await Coroutines.CloseBlockingWindows(); return(true); }
private static async Task <bool> OpenChallenges() { await Coroutines.CloseBlockingWindows(); LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.open_challenges_panel, true, false, false); if (!await Wait.For(() => LokiPoe.InGameState.ChallengesUi.IsOpened, "challenges panel opening")) { return(false); } if (Settings.Instance.ArtificialDelays) { await Wait.ArtificialDelay(); } return(true); }
public static async Task <bool> TurnInStaff() { if (!Helpers.PlayerHasQuestItem(QuestItemMetadata.StaffOfPurity)) { return(false); } if (World.Act10.OriathDocks.IsCurrentArea) { if (await TownBannon.Talk()) { await Coroutines.CloseBlockingWindows(); } else { ErrorManager.ReportError(); } return(true); } await Travel.To(World.Act10.OriathDocks); return(true); }
public static async Task <bool> OpenInventory() { if (InventoryUi.IsOpened && !LokiPoe.InGameState.PurchaseUi.IsOpened && !LokiPoe.InGameState.SellUi.IsOpened) { return(true); } await Coroutines.CloseBlockingWindows(); LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.open_inventory_panel, true, false, false); if (!await Wait.For(() => InventoryUi.IsOpened, "inventory panel opening")) { return(false); } if (Settings.Instance.ArtificialDelays) { await Wait.ArtificialDelay(); } return(true); }
/// <summary> /// Opens the inventory panel. /// </summary> /// <returns></returns> public static async Task <bool> OpenInventoryPanel(int timeout = 10000) { CommunityLib.Log.DebugFormat("[OpenInventoryPanel]"); var sw = Stopwatch.StartNew(); // Make sure we close all blocking windows so we can actually open the inventory. if (!LokiPoe.InGameState.InventoryUi.IsOpened) { await Coroutines.CloseBlockingWindows(); } // Open the inventory panel like a player would. while (!LokiPoe.InGameState.InventoryUi.IsOpened) { CommunityLib.Log.DebugFormat("[OpenInventoryPanel] The InventoryUi is not opened. Now opening it."); if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.ErrorFormat("[OpenInventoryPanel] Timeout."); return(false); } if (LokiPoe.Me.IsDead) { CommunityLib.Log.ErrorFormat("[OpenInventoryPanel] We are now dead."); return(false); } LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.open_inventory_panel, true, false, false); await Coroutines.LatencyWait(2); await Coroutines.ReactionWait(); } return(true); }
public async Task <bool> Run() { var area = World.CurrentArea; if (!area.IsHideoutArea && !area.IsMapRoom) { return(false); } await Coroutines.CloseBlockingWindows(); var golemSkill = SkillBar.Skills.FirstOrDefault(s => s.IsOnSkillBar && s.SkillTags.Contains("golem")); if (golemSkill != null) { var golemObj = golemSkill.DeployedObjects.FirstOrDefault() as Monster; if (golemObj == null || golemObj.HealthPercent < MinGolemHpPercent) { GlobalLog.Debug($"[CastAuraTask] Now summoning \"{golemSkill.Name}\"."); SkillBar.Use(golemSkill.Slot, false); await Wait.SleepSafe(100); await Coroutines.FinishCurrentAction(); await Wait.SleepSafe(100); } } var auras = GetAurasForCast(); if (auras.Count > 0 && AllAuras.Any(a => a.IsOnSkillBar)) { GlobalLog.Info($"[CastAuraTask] Found {auras.Count} aura(s) for casting."); await CastAuras(auras); } return(false); }
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); }
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); }
/// <summary> /// This coroutine interacts with a npc and waits for the npc dialog panel to open. When called for a non-main town npc /// the npc must be in spawn range, otherwise the coroutine will fail. The movement is done without returning, /// so this should be carefully used when not in town. /// </summary> /// <param name="name">Name of the NPC to interact with</param> /// <param name="skipTalk">Should the bot skip all the dialog NPC's have before the dialog panel appears"</param> /// <returns>An OpenStashError that describes the result.</returns> public static async Task <Results.TalkToNpcError> TalkToNpc(string name, bool skipTalk = true) { await Coroutines.CloseBlockingWindows(); await Coroutines.FinishCurrentAction(); var npc = LokiPoe.ObjectManager.GetObjectByName(name); if (npc == null) { var pos = Actor.GuessNpcLocation(name); if (pos == Vector2i.Zero) { return(Results.TalkToNpcError.NoNpc); } if (!await Navigation.MoveToLocation(ExilePather.FastWalkablePositionFor(pos), 25, 60000, () => LokiPoe.ObjectManager.GetObjectByName(name) != null)) { return(Results.TalkToNpcError.CouldNotMoveToNpc); } npc = LokiPoe.ObjectManager.GetObjectByName(name); if (npc == null) { return(Results.TalkToNpcError.NoNpc); } } if (ExilePather.PathDistance(LokiPoe.MyPosition, npc.Position) > 30) { if (!await Navigation.MoveToLocation(ExilePather.FastWalkablePositionFor(npc.Position), 25, 15000, () => false)) { return(Results.TalkToNpcError.CouldNotMoveToNpc); } npc = LokiPoe.ObjectManager.GetObjectByName(name); if (npc == null) { return(Results.TalkToNpcError.NoNpc); } } await Coroutines.FinishCurrentAction(); if (!await InteractWith(npc)) { return(Results.TalkToNpcError.InteractFailed); } // Clicking continue if NPC is blablaing (xD) while (skipTalk && LokiPoe.InGameState.NpcDialogUi.DialogDepth != 1) { if (LokiPoe.InGameState.NpcDialogUi.DialogDepth == 2) { CommunityLib.Log.DebugFormat("[CommunityLib][TalkToNpc] Now closing a dialog/reward window."); LokiPoe.Input.SimulateKeyEvent(Keys.Escape, true, false, false); // Give the client enough time to close the gui itself. It waits for the server to show the new one. await Coroutine.Sleep(LokiPoe.Random.Next(800, 1000)); await Coroutines.ReactionWait(); } else { CommunityLib.Log.InfoFormat("[CommunityLib][TalkToNpc] Waiting for the Npc window to open."); await Coroutines.ReactionWait(); } } if (!await Dialog.WaitForPanel(Dialog.PanelType.NpcDialog)) { return(Results.TalkToNpcError.NpcDialogPanelDidNotOpen); } return(Results.TalkToNpcError.None); }
/// <summary> /// This coroutine interacts with stash and waits for the stash panel to open. When called from a hideout, /// the stash must be in spawn range, otherwise the coroutine will fail. /// </summary> ///<param name="guild">Should the guild stash be opened?</param> /// <returns>An OpenStashError that describes the result.</returns> public static async Task <Results.OpenStashError> OpenStash(bool guild = false) { await Coroutines.CloseBlockingWindows(); await Coroutines.FinishCurrentAction(); var stash = Stash.DetermineStash(guild); if (stash == null) { if (LokiPoe.Me.IsInHideout) { return(Results.OpenStashError.NoStash); } var mtl = await Navigation.MoveToLocation( ExilePather.FastWalkablePositionFor(Actor.GuessStashLocation()), 25, 60000, () => Stash.DetermineStash(guild) != null && Stash.DetermineStash(guild).Distance < 75); if (!mtl) { return(Results.OpenStashError.CouldNotMoveToStash); } stash = Stash.DetermineStash(guild); if (stash == null) { return(Results.OpenStashError.NoStash); } } if (stash.Distance > 30) { var p = stash.Position; if (!await Navigation.MoveToLocation(ExilePather.FastWalkablePositionFor(p), 25, 15000, () => false)) { return(Results.OpenStashError.CouldNotMoveToStash); } } await Coroutines.FinishCurrentAction(); stash = Stash.DetermineStash(guild); if (stash == null) { return(Results.OpenStashError.NoStash); } if (!await InteractWith(stash)) { return(Results.OpenStashError.InteractFailed); } if (guild) { if (!await Dialog.WaitForPanel(Dialog.PanelType.GuildStash)) { return(Results.OpenStashError.StashPanelDidNotOpen); } await Stash.WaitForStashTabChange(guild : true); } else { if (!await Dialog.WaitForPanel(Dialog.PanelType.Stash)) { return(Results.OpenStashError.StashPanelDidNotOpen); } await Stash.WaitForStashTabChange(); } return(Results.OpenStashError.None); }
public static async Task <bool> SellItems(List <Vector2i> itemPositions) { if (itemPositions == null) { GlobalLog.Error("[SellItems] Item list for sell is null."); return(false); } if (itemPositions.Count == 0) { GlobalLog.Error("[SellItems] Item list for sell is empty."); return(false); } var vendor = GetSellVendor(); if (vendor == null) { return(false); } if (!await vendor.OpenSellPanel()) { return(false); } itemPositions.Sort(Position.Comparer.Instance); var soldItems = new List <CachedItem>(itemPositions.Count); foreach (var itemPos in itemPositions) { var item = InventoryUi.InventoryControl_Main.Inventory.FindItemByPos(itemPos); if (item == null) { GlobalLog.Error($"[SellItems] Fail to find item at {itemPos}. Skipping it."); continue; } soldItems.Add(new CachedItem(item)); if (!SellUi.TradeControl.InventoryControl_YourOffer.Inventory.CanFitItem(item.Size)) { break; } if (!await Inventories.FastMoveToVendor(itemPos)) { return(false); } } var gainedItems = new List <CachedItem>(); foreach (var item in SellUi.TradeControl.InventoryControl_OtherOffer.Inventory.Items) { gainedItems.Add(new CachedItem(item)); } var accepted = SellUi.TradeControl.Accept(); if (accepted != TradeResult.None) { GlobalLog.Error($"[SellItems] Fail to accept sell. Error: \"{accepted}\"."); return(false); } if (!await Wait.For(() => !SellUi.IsOpened, "sell panel closing")) { await Coroutines.CloseBlockingWindows(); return(false); } // game needs some time do despawn sold items from inventory await Wait.SleepSafe(200); GlobalLog.Info($"[Events] Items sold ({soldItems.Count})"); Utility.BroadcastMessage(null, Events.Messages.ItemsSoldEvent, soldItems, gainedItems); return(true); }
/// <summary> /// Coroutine logic to execute. /// </summary> /// <returns>true if logic was executed to handle this type and false otherwise.</returns> public async Task <bool> Run() { // NOTE: This task's Run function is triggered from "hook_post_combat" Logic, as it's added via a secondary TaskManager! // If this task needs to be disabled due to errors, support doing so. if (_skip) { return(false); } // Don't do anything in these cases. if (LokiPoe.Me.IsDead || LokiPoe.Me.IsInHideout || LokiPoe.Me.IsInTown || LokiPoe.Me.IsInMapRoom) { return(false); } // If we're currently disabled, skip logic. if (!BreachesSettings.Instance.Enabled) { return(false); } var myPos = LokiPoe.MyPosition; var active = BreachDataManager.Active; if (active == null) { return(false); } // Make sure the breach is still valid and not blacklisted if it's set. // We don't re-eval current against settings, because of the performance overhead. if (_current != null) { if (!_current.IsValid || Blacklist.Contains(_current.Id)) { _current = null; } } // Find the next best breach. if (_current == null) { _current = active.Breaches.Where(m => m.IsValid && !Blacklist.Contains(m.Id) && ShouldActivate(m)) .OrderBy(m => m.Position.Distance(myPos)) .FirstOrDefault(); _moveErrors = 0; } // Nothing to do if there's no breach. if (_current == null) { return(false); } // If we can't move to the breach, blacklist it. if (_moveErrors > 5) { Blacklist.Add(_current.Id, TimeSpan.FromHours(1), string.Format("[HandleBreachesTask::Logic] Unable to move to the Breach.")); _current = null; return(true); } // If we are too far away to interact, move towards the object. if (myPos.Distance(_current.WalkablePosition) > 50) { // Make sure nothing is in the way. await Coroutines.CloseBlockingWindows(); // Try to move towards the location. if (!PlayerMoverManager.MoveTowards(_current.WalkablePosition)) { Log.ErrorFormat("[HandleBreachesTask::Logic] PlayerMoverManager.MoveTowards({0}) failed for Breach [{1}].", _current.WalkablePosition, _current.Id); _moveErrors++; return(true); } _moveErrors = 0; return(true); } // Make sure we're not doing anything before we interact. await Coroutines.FinishCurrentAction(); // If the user wants to manually open, or just find them without opening, this task just blocks everything else after it, // rather than stopping the bot, to avoid deaths. if (!BreachesSettings.Instance.Open) { return(true); } // Now process the object, but make sure it exists. var breach = _current.NetworkObject; if (breach == null) { _current.Activate = false; Log.ErrorFormat("[HandleBreachesTask::Logic] The NetworkObject does not exist for the Breach [{0}] yet.", _current.Id); _current = null; return(true); } // Try to move towards the location. if (!PlayerMoverManager.MoveTowards(_current.WalkablePosition)) { Log.ErrorFormat("[HandleBreachesTask::Logic] PlayerMoverManager.MoveTowards({0}) failed for Breach [{1}].", _current.WalkablePosition, _current.Id); _moveErrors++; return(true); } return(true); }
public async Task <bool> Run() { if (ErrorLimitReached) { return(false); } var area = World.CurrentArea; if (!area.IsTown && !area.IsHideoutArea) { return(false); } if (!ChaosRecipe.StashData.HasCompleteSet()) { GlobalLog.Info("[SellRecipeTask] No chaos recipe set to sell."); return(false); } GlobalLog.Debug("[SellRecipeTask] Now going to take and sell a set of rare items for chaos recipe."); if (!await Inventories.OpenStashTab(Settings.Instance.StashTab)) { ReportError(); return(true); } ChaosRecipe.StashData.SyncWithStashTab(); if (!ChaosRecipe.StashData.HasCompleteSet()) { GlobalLog.Error("[SellRecipeTask] Saved stash data does not match actual stash data."); await Coroutines.CloseBlockingWindows(); return(false); } var takenItemData = new RecipeData(); int lowestItemType = RecipeItemType.None; var lowestItem = Inventories.StashTabItems .OrderBy(i => i.ItemLevel) .FirstOrDefault(i => RecipeData.IsItemForChaosRecipe(i, out lowestItemType)); if (lowestItem == null) { GlobalLog.Error("[SellRecipeTask] Unknown error. Fail to find actual item for chaos recipe."); ReportError(); return(true); } GlobalLog.Debug($"[SellRecipeTask] Now taking \"{lowestItem.Name}\" (iLvl: {lowestItem.ItemLevel})"); if (!await Inventories.FastMoveFromStashTab(lowestItem.LocationTopLeft)) { ReportError(); return(true); } takenItemData.IncreaseItemCount(lowestItemType); while (true) { int type = RecipeItemType.None; for (int i = 0; i < RecipeItemType.TotalTypeCount; i++) { var count = takenItemData.GetItemCount(i); if (count == 0 || (count == 1 && i == RecipeItemType.Ring)) { type = i; break; } } if (type == RecipeItemType.None) { break; } var item = Inventories.StashTabItems .Where(i => RecipeData.IsItemForChaosRecipe(i, out var t) && t == type) .OrderByDescending(i => i.ItemLevel) .FirstOrDefault(); if (item == null) { GlobalLog.Error("[SellRecipeTask] Unknown error. Fail to find actual item for chaos recipe."); ReportError(); return(true); } GlobalLog.Debug($"[SellRecipeTask] Now taking \"{item.Name}\" (iLvl: {item.ItemLevel})"); if (!await Inventories.FastMoveFromStashTab(item.LocationTopLeft)) { ReportError(); return(true); } takenItemData.IncreaseItemCount(type); } await Wait.SleepSafe(300); ChaosRecipe.StashData.SyncWithStashTab(); var recipeItems = Inventories.InventoryItems .Where(i => RecipeData.IsItemForChaosRecipe(i, out var t)) .Select(i => i.LocationTopLeft) .ToList(); if (recipeItems.Count == 0) { GlobalLog.Error("[SellRecipeTask] Unknown error. There are no items in player's inventory after taking them from stash."); ReportError(); return(true); } if (!await TownNpcs.SellItems(recipeItems)) { ReportError(); } return(true); }
public async Task <bool> Run() { var area = World.CurrentArea; if (!area.IsMap && !area.IsOverworldArea) { return(false); } if (CombatAreaCache.IsInIncursion) { return(false); } if (LokiPoe.InstanceInfo.Incursion.IncursionsRemaining == 0) { return(false); } var cache = CombatAreaCache.Current; if (cache.Storage["IsIncursionCompleted"] != null) { return(false); } var alva = cache.Storage["AlvaValai"] as CachedObject; if (alva == null || alva.Unwalkable || alva.Ignored) { return(false); } var pos = alva.Position; if (pos.IsFar || pos.IsFarByPath) { if (!pos.TryCome()) { GlobalLog.Error($"[EnterIncursionTask] Fail to move to {pos}. Alva is unwalkable."); alva.Unwalkable = true; } return(true); } var alvaObj = alva.Object; if (alvaObj == null) { GlobalLog.Error("[EnterIncursionTask] Unexpected error. We are near cached Alva, but actual object is null."); alva.Ignored = true; return(true); } var timePortal = TimePortal; if (timePortal == null) { GlobalLog.Error("[EnterIncursionTask] Unexpected error. There is no Time Portal near Alva."); cache.Storage["IsIncursionCompleted"] = true; return(true); } if (timePortal.Components.TransitionableComponent.Flag2 == 3) { GlobalLog.Warn("[EnterIncursionTask] Incursion in this area has been completed."); cache.Storage["IsIncursionCompleted"] = true; if (Settings.Instance.LeaveAfterIncursion) { FinishGridning(); } return(true); } if (!timePortal.IsTargetable) { var attempts = ++alva.InteractionAttempts; if (attempts > 5) { GlobalLog.Error("[EnterIncursionTask] 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 Incursion")) { await Coroutines.CloseBlockingWindows(); await Wait.For(() => timePortal.Fresh().IsTargetable, "Time Portal activation", 200, 5000); } return(true); } if (Settings.Instance.PortalBeforeIncursion && area.IsMap && !AnyPortalsNearby) { // Have to do this because portal spawned right near Time Portal has a high chance to overlap labels var distantPos = WorldPosition.FindPathablePositionAtDistance(30, 35, 5); if (distantPos != null) { await Move.AtOnce(distantPos, "away from Time Portal", 10); await PlayerAction.CreateTownPortal(); } else { await PlayerAction.CreateTownPortal(); } } if (ErrorManager.GetErrorCount("EnterIncursion") >= 5) { GlobalLog.Error("[EnterIncursionTask] Failed to enter Time Portal 5 times."); cache.Storage["IsIncursionCompleted"] = true; return(true); } if (await PlayerAction.TakeTransition(timePortal)) { GlobalLog.Warn("[EnterIncursionTask] IsInIncursion: true"); CombatAreaCache.IsInIncursion = true; SetRoomSettings(); } else { ErrorManager.ReportError("EnterIncursion"); await Wait.SleepSafe(500); } return(true); }
/// <summary> /// Implements the ability to handle a logic passed through the system. /// </summary> /// <param name="logic">The logic to be processed.</param> /// <returns>A LogicResult that describes the result..</returns> public async Task <LogicResult> Logic(Logic logic) { if (logic.Id == "hook_post_combat") { // Don't update while we are not in the game. if (!LokiPoe.IsInGame) { return(LogicResult.Unprovided); } // When the game is paused, don't try to run. if (LokiPoe.InstanceInfo.IsGamePaused) { return(LogicResult.Unprovided); } // Don't try to do anything when the escape state is active. if (LokiPoe.StateManager.IsEscapeStateActive) { return(LogicResult.Unprovided); } // Don't level skill gems if we're dead. if (LokiPoe.Me.IsDead) { return(LogicResult.Unprovided); } // Can't level skill gems under this scenario either. if (LokiPoe.InGameState.IsTopMostOverlayActive) { return(LogicResult.Unprovided); } // Can't level skill gems under this scenario either. if (LokiPoe.InGameState.SkillsUi.IsOpened) { return(LogicResult.Unprovided); } // Only check for skillgem leveling at a fixed interval. if (!_needsToUpdate && !_levelWait.IsFinished) { return(LogicResult.Unprovided); } Func <Inventory, Item, Item, bool> eval = (inv, holder, gem) => { // Ignore any "globally ignored" gems. This just lets the user move gems around // equipment, without having to worry about where or what it is. if (ContainsHelper(gem.Name)) { if (GemLevelerSettings.Instance.DebugStatements) { Log.DebugFormat("[LevelSkillGemTask] {0} => {1}.", gem.Name, "GlobalNameIgnoreList"); } return(false); } // Now look though the list of skillgem strings to level, and see if the current gem matches any of them. var ss = string.Format("{0} [{1}: {2}]", gem.Name, inv.PageSlot, holder.GetSocketIndexOfGem(gem)); foreach (var str in GemLevelerSettings.Instance.SkillGemsToLevelList) { if (str.Equals(ss, StringComparison.OrdinalIgnoreCase)) { if (GemLevelerSettings.Instance.DebugStatements) { Log.DebugFormat("[LevelSkillGemTask] {0} => {1}.", gem.Name, str); } return(true); } } // No match, we shouldn't level this gem. return(false); }; // If we have icons on the hud to process. if (LokiPoe.InGameState.SkillGemHud.AreIconsDisplayed) { // If the InventoryUi is already opened, skip this logic and let the next set run. if (!LokiPoe.InGameState.InventoryUi.IsOpened) { // We need to close blocking windows. await Coroutines.CloseBlockingWindows(); // We need to let skills finish casting, because of 2.6 changes. await Coroutines.FinishCurrentAction(); await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); var res = LokiPoe.InGameState.SkillGemHud.HandlePendingLevelUps(eval); Log.InfoFormat("[LevelSkillGemTask] SkillGemHud.HandlePendingLevelUps returned {0}.", res); if (res == LokiPoe.InGameState.HandlePendingLevelUpResult.GemDismissed || res == LokiPoe.InGameState.HandlePendingLevelUpResult.GemLeveled) { await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); return(LogicResult.Provided); } } } if (_needsToUpdate || LokiPoe.InGameState.InventoryUi.IsOpened) { if (LokiPoe.InGameState.InventoryUi.IsOpened) { _needsToCloseInventory = false; } else { _needsToCloseInventory = true; } // We need the inventory panel open. if (!await OpenInventoryPanel()) { Log.ErrorFormat("[LevelSkillGemTask] OpenInventoryPanel failed."); return(LogicResult.Provided); } // If we have icons on the inventory ui to process. // This is only valid when the inventory panel is opened. if (LokiPoe.InGameState.InventoryUi.AreIconsDisplayed) { var res = LokiPoe.InGameState.InventoryUi.HandlePendingLevelUps(eval); Log.InfoFormat("[LevelSkillGemTask] InventoryUi.HandlePendingLevelUps returned {0}.", res); if (res == LokiPoe.InGameState.HandlePendingLevelUpResult.GemDismissed || res == LokiPoe.InGameState.HandlePendingLevelUpResult.GemLeveled) { await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); return(LogicResult.Provided); } } } // Just wait 5-10s between checks. _levelWait.Reset(TimeSpan.FromMilliseconds(LokiPoe.Random.Next(5000, 10000))); //if (_needsToCloseInventory) { await Coroutines.CloseBlockingWindows(); _needsToCloseInventory = false; } _needsToUpdate = false; } return(LogicResult.Unprovided); }
/// <summary> /// This coroutine interacts with an area transition in order to change areas. It assumes /// you are in interaction range with the area transition itself. It can be used both in town, /// and out of town, given the previous conditions are met. /// </summary> /// <param name="obj">The area transition object to take.</param> /// <param name="newInstance">Should a new instance be created.</param> /// <param name="isLocal">Is the area transition local? In other words, should the couroutine not wait for an area change.</param> /// <param name="maxInstances">The max number of instance entries allowed to Join a new instance or -1 to not check.</param> /// <returns>A TakeAreaTransitionError that describes the result.</returns> public static async Task <Results.TakeAreaTransitionError> TakeAreaTransition(NetworkObject obj, bool newInstance, int maxInstances, bool isLocal = false) { CommunityLib.Log.InfoFormat("[TakeAreaTransition] {0} {1} {2}", obj.Name, newInstance ? "(new instance)" : "", isLocal ? "(local)" : ""); await Coroutines.CloseBlockingWindows(); await Coroutines.FinishCurrentAction(); var hash = LokiPoe.LocalData.AreaHash; var pos = LokiPoe.MyPosition; if (!await InteractWith(obj, newInstance)) { return(Results.TakeAreaTransitionError.InteractFailed); } if (newInstance) { if (!await WaitForInstanceManager(5000)) { LokiPoe.ProcessHookManager.ClearAllKeyStates(); return(Results.TakeAreaTransitionError.InstanceManagerDidNotOpen); } LokiPoe.ProcessHookManager.ClearAllKeyStates(); await Coroutines.LatencyWait(); await Coroutine.Sleep(1000); // Let the gui stay open a bit before clicking too fast. if (LokiPoe.InGameState.InstanceManagerUi.InstanceCount >= maxInstances) { return(Results.TakeAreaTransitionError.TooManyInstances); } var nierr = LokiPoe.InGameState.InstanceManagerUi.JoinNewInstance(); if (nierr != LokiPoe.InGameState.JoinInstanceResult.None) { CommunityLib.Log.ErrorFormat("[TakeAreaTransition] InstanceManagerUi.JoinNew returned {0}.", nierr); return(Results.TakeAreaTransitionError.JoinNewFailed); } // Wait for the action to take place first. await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); } if (isLocal) { if (!await WaitForPositionChange(pos)) { CommunityLib.Log.ErrorFormat("[TakeAreaTransition] WaitForPositionChange failed."); return(Results.TakeAreaTransitionError.WaitForAreaChangeFailed); } } else { if (!await Areas.WaitForAreaChange(hash)) { CommunityLib.Log.ErrorFormat("[TakeAreaTransition] WaitForAreaChange failed."); return(Results.TakeAreaTransitionError.WaitForAreaChangeFailed); } } return(Results.TakeAreaTransitionError.None); }
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); }
/// <summary> /// Coroutine logic to execute. /// </summary> /// <returns>true if logic was executed to handle this type and false otherwise.</returns> public async Task <bool> Run() { // NOTE: This task's Run function is triggered from "hook_post_combat" Logic, as it's added via a secondary TaskManager! if (_skip) { return(false); } // Don't level passives if we're dead. if (LokiPoe.Me.IsDead) { return(false); } // Don't try to level passives if we only want to allocate in town, and are not currently in town. if (AutoPassivesSettings.Instance.OnlyAllocateInTown && !(LokiPoe.Me.IsInTown || LokiPoe.Me.IsInHideout)) { return(false); } // Try to not allocate if there's a monster somewhat close, will avoid lots of issues. // More lightweight check to just get an idea of what is around us, rather than the heavy IsActive. if ( LokiPoe.ObjectManager.GetObjectsByType <Monster>() .Any(m => m.IsAliveHostile && m.Distance < 100)) { return(false); } // Only check for passive leveling at a fixed interval. if (!_levelWait.IsFinished) { return(false); } if (LokiPoe.InstanceInfo.PassiveSkillPointsAvailable == 0) { _levelWait.Reset(TimeSpan.FromMilliseconds(LokiPoe.Random.Next(5000, 10000))); // Close the window to prevent other issues. if (LokiPoe.InGameState.SkillsUi.IsOpened) { Log.DebugFormat("[AssignPassivesTask] The SkillsPanel is open. Now closing it to avoid issues."); await Coroutines.CloseBlockingWindows(); return(true); } return(false); } var pendingPassives = AutoPassivesSettings.Instance.Passives.Where(p => !LokiPoe.InstanceInfo.PassiveSkillIds.Contains(p.Id)).ToList(); if (!pendingPassives.Any()) { _levelWait.Reset(TimeSpan.FromMilliseconds(LokiPoe.Random.Next(5000, 10000))); // Close the window to prevent other issues. if (LokiPoe.InGameState.SkillsUi.IsOpened) { Log.DebugFormat("[AssignPassivesTask] The SkillsPanel is open. Now closing it to avoid issues."); await Coroutines.CloseBlockingWindows(); return(true); } return(false); } if (!await OpenSkillsUi()) { Log.ErrorFormat("[AssignPassivesTask] OpenSkillsUi failed."); await Coroutines.CloseBlockingWindows(); return(true); } // Handle the passive tree reset by closing the dialog. if (LokiPoe.InGameState.GlobalWarningDialog.IsPassiveTreeWarningOverlayOpen) { Log.DebugFormat("[AssignPassivesTask] IsPassiveTreeWarningOverlayOpen. Now attempting to close it."); LokiPoe.Input.SimulateKeyEvent(Keys.Escape, true, false, false); await Coroutine.Sleep(250); return(true); } // Make sure we don't screw up the user's character ;) if (LokiPoe.InGameState.SkillsUi.IsResetAllPassivesEnabled && LokiPoe.InGameState.SkillsUi.IsResetAllPassivesVisible) { Log.ErrorFormat( "[AssignPassivesTask] The \"Reset All Passives\" button is available. Please reset your passive tree, or manually allocate a point first."); _skip = true; LokiPoe.Input.SimulateKeyEvent(Keys.Escape, true, false, false); await Coroutine.Sleep(250); return(true); } var passive = pendingPassives.First(); var error = true; var ret = LokiPoe.InGameState.SkillsUi.ChoosePassive(passive.Id); if (ret == LokiPoe.InGameState.ChoosePassiveError.None) { var ret2 = LokiPoe.InGameState.SkillsUi.ConfirmOperation(); if (ret2 == LokiPoe.InGameState.PassiveAllocationActionError.None) { Log.InfoFormat("[AssignPassivesTask] The passive {0} was assigned.", passive.Id); error = false; } else { Log.ErrorFormat("[AssignPassivesTask] ConfirmOperation returned {0} for {1}.", ret2, passive.Id); } } else { Log.ErrorFormat("[AssignPassivesTask] ChoosePassive returned {0} for {1}.", ret, passive.Id); } // If there was an error, stop trying to allocate passives until user intervention. if (error) { _skip = true; return(true); } // If we have no more points, then reset the delay, otherwise, try to keep allocating the next time it executes. if (LokiPoe.InstanceInfo.PassiveSkillPointsAvailable == 0) { _levelWait.Reset(TimeSpan.FromMilliseconds(LokiPoe.Random.Next(5000, 10000))); await Coroutines.CloseBlockingWindows(); } await Coroutine.Sleep(LokiPoe.Random.Next(750, 1250)); return(true); }
/// <summary> /// Goes to the specified area using waypoint. /// </summary> /// <param name="name">Name of the area eg "Highgate"</param> /// <param name="difficulty"></param> /// <param name="newInstance">Do you want to open new instance?</param> /// <returns></returns> public static async Task <LokiPoe.InGameState.TakeWaypointResult> TakeWaypoint(string name, Difficulty difficulty = Difficulty.Unknown, bool newInstance = false) { //We are already there if (LokiPoe.LocalData.WorldArea.Name == name && LokiPoe.LocalData.WorldArea.Difficulty == difficulty && !newInstance) { return(LokiPoe.InGameState.TakeWaypointResult.None); } //await Coroutines.CloseBlockingWindows(); // First try of fastgotohideout instead of LokiPoe.InGameState.WorldUi.GoToHideout() if (name.Equals("Hideout", StringComparison.OrdinalIgnoreCase) && (LokiPoe.Me.IsInHideout || LokiPoe.Me.IsInTown)) { await Coroutines.CloseBlockingWindows(); var res = await FastGoToHideout(); switch (res) { case Results.FastGoToHideoutResult.None: return(LokiPoe.InGameState.TakeWaypointResult.None); case Results.FastGoToHideoutResult.NoHideout: return(LokiPoe.InGameState.TakeWaypointResult.AreaNotFound); case Results.FastGoToHideoutResult.NotInGame: return(LokiPoe.InGameState.TakeWaypointResult.UiNotOpen); //if we timed out then try to use default method like below } } if (!LokiPoe.InGameState.WorldUi.IsOpened) { var opened = await LibCoroutines.OpenWaypoint(); if (opened != Results.OpenWaypointError.None) { CommunityLib.Log.ErrorFormat("[TakeWaypoint] Fail to open waypoint. Error: \"{0}\".", opened); return(LokiPoe.InGameState.TakeWaypointResult.WaypointControlNotVisible); } } if (difficulty == Difficulty.Unknown) { difficulty = LokiPoe.CurrentWorldArea.Difficulty; } //var areaId = name == "Hideout" ? "" : GetZoneId(difficulty.ToString(), name); CommunityLib.Log.InfoFormat($"[TakeWaypoint] Going to {name} at {difficulty}."); var areaHash = LokiPoe.LocalData.AreaHash; var taken = name.Equals("Hideout", StringComparison.OrdinalIgnoreCase) ? LokiPoe.InGameState.WorldUi.GoToHideout() : LokiPoe.InGameState.WorldUi.TakeWaypoint(LokiPoe.GetZoneId(difficulty.ToString(), name), newInstance, Int32.MaxValue); if (taken != LokiPoe.InGameState.TakeWaypointResult.None) { CommunityLib.Log.ErrorFormat("[TakeWaypoint] Failed to take waypoint to \"{0}\". Error: \"{1}\".", name, taken); return(taken); } var awaited = await Areas.WaitForAreaChange(areaHash); return(awaited ? LokiPoe.InGameState.TakeWaypointResult.None : LokiPoe.InGameState.TakeWaypointResult.CouldNotJoinNewInstance); }
public static async Task <Portal> CreateTownPortal() { var portalSkill = LokiPoe.InGameState.SkillBarHud.Skills.FirstOrDefault(s => s.Name == "Portal" && s.IsOnSkillBar); if (portalSkill != null) { await Coroutines.FinishCurrentAction(); await Wait.SleepSafe(100); var err = LokiPoe.InGameState.SkillBarHud.Use(portalSkill.Slot, false); if (err != LokiPoe.InGameState.UseResult.None) { GlobalLog.Error($"[CreateTownPortal] Fail to cast portal skill. Error: \"{err}\"."); return(null); } await Coroutines.FinishCurrentAction(); await Wait.SleepSafe(100); } else { var portalScroll = Inventories.InventoryItems .Where(i => i.Name == CurrencyNames.Portal) .OrderBy(i => i.StackCount) .FirstOrDefault(); if (portalScroll == null) { GlobalLog.Error("[CreateTownPortal] Out of portal scrolls."); return(null); } int itemId = portalScroll.LocalId; if (!await Inventories.OpenInventory()) { return(null); } await Coroutines.FinishCurrentAction(); await Wait.SleepSafe(100); var err = LokiPoe.InGameState.InventoryUi.InventoryControl_Main.UseItem(itemId); if (err != UseItemResult.None) { GlobalLog.Error($"[CreateTownPortal] Fail to use a Portal Scroll. Error: \"{err}\"."); return(null); } if (Settings.Instance.ArtificialDelays) { await Wait.ArtificialDelay(); } await Coroutines.CloseBlockingWindows(); } Portal portal = null; await Wait.For(() => (portal = PortalInRangeOf(40)) != null, "portal spawning"); return(portal); }
public async Task <bool> Run() { var area = World.CurrentArea; if (!area.IsTown && !area.IsHideoutArea) { return(false); } var itemsToStash = new List <StashItem>(); var inventoryCurrency = Settings.Instance.InventoryCurrencies; foreach (var item in Inventories.InventoryItems) { var c = item.Class; if (c == ItemClasses.QuestItem || c == ItemClasses.PantheonSoul) { continue; } if (c == ItemClasses.StackableCurrency && inventoryCurrency.Any(i => i.Name == item.Name)) { continue; } itemsToStash.Add(new StashItem(item)); } foreach (var currency in inventoryCurrency) { foreach (var excess in Inventories.GetExcessCurrency(currency.Name)) { itemsToStash.Add(new StashItem(excess)); } } if (itemsToStash.Count == 0) { GlobalLog.Info("[StashTask] No items to stash."); return(false); } if (_checkInvalidTabs) { if (!await Inventories.OpenStash()) { ErrorManager.ReportError(); return(true); } var wrongTabs = GetNonexistentTabs(); if (wrongTabs.Count > 0) { GlobalLog.Error("[StashTask] The following tabs are specified in stashing rules but do not exist in stash:"); GlobalLog.Error($"{string.Join(", ", wrongTabs)}"); GlobalLog.Error("[StashTask] Please provide correct tab names."); BotManager.Stop(); return(true); } GlobalLog.Debug("[StashTask] All tabs specified in stashing rules exist in stash."); _checkInvalidTabs = false; } if (_checkFullTabs) { if (Settings.Instance.FullTabs.Count > 0) { if (!await FullTabCheck()) { ErrorManager.ReportError(); return(true); } } _checkFullTabs = false; } GlobalLog.Info($"[StashTask] {itemsToStash.Count} items to stash."); AssignStashTabs(itemsToStash); foreach (var item in itemsToStash.OrderBy(i => i.StashTab).ThenBy(i => i.Position, Position.Comparer.Instance)) { var itemName = item.Name; var itemPos = item.Position; var tabName = item.StashTab; GlobalLog.Debug($"[StashTask] Now going to stash \"{itemName}\" to \"{tabName}\" tab."); if (!await Inventories.OpenStashTab(tabName)) { 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 (!Inventories.StashTabCanFitItem(itemPos)) { if (StashUi.StashTabInfo.IsPremiumSpecial) { var metadata = item.Metadata; GlobalLog.Warn($"[StashTask] Cannot fit \"{itemName}\" to \"{tabName}\" tab."); GlobalLog.Warn($"[StashTask] Now marking inventory control for \"{metadata}\" as full."); Settings.Instance.MarkTabAsFull(tabName, metadata); } else { GlobalLog.Warn($"[StashTask] Cannot fit \"{itemName}\" to \"{tabName}\" tab. Now marking this tab as full."); Settings.Instance.MarkTabAsFull(tabName, null); } return(true); } if (!await Inventories.FastMoveFromInventory(itemPos)) { ErrorManager.ReportError(); return(true); } GlobalLog.Info($"[Events] Item stashed ({item.FullName})"); Utility.BroadcastMessage(this, Events.Messages.ItemStashedEvent, item); } await Coroutines.CloseBlockingWindows(); return(true); }
/// <summary> /// This coroutine creates a portal to town from a Portal Scroll in the inventory. /// </summary> /// <returns>true if the Portal Scroll was used and false otherwise.</returns> public static async Task <bool> CreatePortalToTown() { if (LokiPoe.Me.IsInTown) { CommunityLib.Log.ErrorFormat("[CreatePortalToTown] Town portals are not allowed in town."); return(false); } if (LokiPoe.Me.IsInHideout) { CommunityLib.Log.ErrorFormat("[CreatePortalToTown] Town portals are not allowed in hideouts."); return(false); } if (LokiPoe.CurrentWorldArea.IsMissionArea || LokiPoe.CurrentWorldArea.IsDenArea || LokiPoe.CurrentWorldArea.IsRelicArea || LokiPoe.CurrentWorldArea.IsDailyArea) { CommunityLib.Log.ErrorFormat("[CreatePortalToTown] Town Portals are not allowed in mission areas."); return(false); } await Coroutines.FinishCurrentAction(); await Coroutines.CloseBlockingWindows(); var portalSkill = LokiPoe.InGameState.SkillBarHud.Skills.FirstOrDefault(s => s.Name == "Portal"); if (portalSkill != null && portalSkill.CanUse(true)) { CommunityLib.Log.DebugFormat("[CreatePortalToTown] We have a Portal skill on the skill bar. Now using it."); var err = LokiPoe.InGameState.SkillBarHud.Use(portalSkill.Slot, false); CommunityLib.Log.InfoFormat($"[CreatePortalToTown] SkillBarHud.Use returned {err}."); await Coroutines.LatencyWait(); await Coroutines.FinishCurrentAction(); if (err == LokiPoe.InGameState.UseResult.None) { var sw = Stopwatch.StartNew(); while (sw.ElapsedMilliseconds < 3000) { var portal = LokiPoe.ObjectManager.Objects.OfType <Portal>().FirstOrDefault(p => p.Distance < 50); if (portal != null) { return(true); } CommunityLib.Log.DebugFormat("[CreatePortalToTown] No portal was detected yet, waiting..."); await Coroutines.LatencyWait(); } } } CommunityLib.Log.DebugFormat("[CreatePortalToTown] Now opening the inventory panel."); // We need the inventory panel open. if (!await OpenInventoryPanel()) { return(false); } await Coroutines.ReactionWait(); CommunityLib.Log.DebugFormat("[CreatePortalToTown] Now searching the main inventory for a Portal Scroll."); var item = LokiPoe.InstanceInfo.GetPlayerInventoryItemsBySlot(InventorySlot.Main).FirstOrDefault(i => i.Name == "Portal Scroll"); if (item == null) { CommunityLib.Log.ErrorFormat("[CreatePortalToTown] There are no Portal Scrolls in the inventory."); return(false); } CommunityLib.Log.DebugFormat("[CreatePortalToTown] Now using the Portal Scroll."); var err2 = LokiPoe.InGameState.InventoryUi.InventoryControl_Main.UseItem(item.LocalId); if (err2 != UseItemResult.None) { CommunityLib.Log.ErrorFormat($"[CreatePortalToTown] UseItem returned {err2}."); return(false); } await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); await Coroutines.CloseBlockingWindows(); return(true); }