/// <summary> /// This coroutine waits for an area transition to be usable. /// </summary> /// <param name="name">The name of the area transition.</param> /// <param name="timeout">How long to wait before the coroutine fails.</param> /// <returns>true on succes and false on failure.</returns> public static async Task <bool> WaitForAreaTransition(string name, int timeout = 3000) { CommunityLib.Log.DebugFormat("[WaitForAreaTransition]"); var sw = Stopwatch.StartNew(); while (true) { var at = LokiPoe.ObjectManager.GetObjectByName <AreaTransition>(name); if (at != null) { if (at.IsTargetable) { break; } } if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.ErrorFormat("[WaitForAreaTransition] Timeout."); return(false); } CommunityLib.Log.DebugFormat( "[WaitForAreaTransition] We have been waiting {0} for the area transition {1} to be usable.", sw.Elapsed, name); await Coroutines.LatencyWait(); } return(true); }
/// <summary> /// Waits for a stash tab to change. Pass -1 to lastId to wait for the initial tab. /// </summary> /// <param name="lastId">The last InventoryId before changing tabs.</param> /// <param name="timeout">The timeout of the function.</param> /// <param name="guild">Whether it's the guild stash or not</param> /// <returns>true if the tab was changed and false otherwise.</returns> public static async Task <bool> WaitForStashTabChange(int lastId = -1, int timeout = 10000, bool guild = false) { var sw = Stopwatch.StartNew(); var invTab = guild ? GuildStashUI.StashTabInfo : StashUI.StashTabInfo; while (invTab == null || invTab.InventoryId == lastId) { await Coroutine.Sleep(1); if (guild) { if (!GuildStashUI.IsOpened) { return(false); } else if (!StashUI.IsOpened) { return(false); } } invTab = guild ? GuildStashUI.StashTabInfo : StashUI.StashTabInfo; if (sw.ElapsedMilliseconds > timeout) { return(false); } } await Coroutines.LatencyWait((float)MathEx.Random(.5d, 2d)); //await Coroutines.ReactionWait(); return(true); }
/// <summary> /// This task (awaitable) handles the whole process to talk to a NPC to reach the list of choices /// </summary> /// <param name="npcName">Name of the NPC to interact with</param> /// <returns>boolean</returns> public static async Task<bool> TalkToNpc(string npcName) { //await Coroutines.CloseBlockingWindows(); var ret = await LibCoroutines.TalkToNpc(npcName, true); await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); return ret == Results.TalkToNpcError.None; }
/// <summary> /// Overload for UpdateItemsInStash, taking a list of tabs as parameters /// </summary> /// <param name="tabs"></param> /// <returns></returns> private static async Task <bool> UpdateItemsInStash(IEnumerable <CommunityLibSettings.StringEntry> tabs) { foreach (var tab in tabs.Select(d => d.Name).Where(tab => !string.IsNullOrEmpty(tab))) { if (await UpdateSpecificTab(tab)) { continue; } CommunityLib.Log.Info($"[CommunityLib][UpdateItemsInStash (specific)] An error happend when caching the tab \"{tab}\""); //return false; } ItemsInStashAlreadyCached = true; await Coroutines.LatencyWait(); return(true); }
public static async Task <bool> WaitForCursorToHaveItem(int timeout = 5000) { var sw = Stopwatch.StartNew(); while (!LokiPoe.InstanceInfo.GetPlayerInventoryItemsBySlot(InventorySlot.Cursor).Any()) { CommunityLib.Log.InfoFormat("[CommunityLib][WaitForCursorToHaveItem] Waiting for the cursor to have an item."); await Coroutines.LatencyWait(); if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.InfoFormat("[CommunityLib][WaitForCursorToHaveItem] Timeout while waiting for the cursor to contain an item."); return(false); } } return(true); }
/// <summary> /// This coroutine attempts to highlight and interact with an object. /// Interaction only takes place if the object is highlighted or an object of type T is. /// </summary> /// <typeparam name="T">The type of object acceptable to be highlighted if the intended target is not highlighted.</typeparam> /// <param name="holdCtrl">Should control be held? For area transitions.</param> /// <param name="obj">The object to interact with.</param> /// <returns>true on success and false on failure.</returns> public static async Task <bool> InteractWith <T>(NetworkObject obj, bool holdCtrl = false) { if (obj == null) { CommunityLib.Log.ErrorFormat("[InteractWith] The object is null."); return(false); } var id = obj.Id; CommunityLib.Log.DebugFormat($"[InteractWith] Now attempting to highlight {id}."); await Coroutines.FinishCurrentAction(); if (!LokiPoe.Input.HighlightObject(obj)) { CommunityLib.Log.ErrorFormat("[InteractWith] The target could not be highlighted."); return(false); } var target = LokiPoe.InGameState.CurrentTarget; if (target != obj && !(target is T)) { CommunityLib.Log.ErrorFormat("[InteractWith] The target highlight has been lost."); return(false); } CommunityLib.Log.DebugFormat($"[InteractWith] Now attempting to interact with {id}."); if (holdCtrl) { LokiPoe.ProcessHookManager.SetKeyState(Keys.ControlKey, 0x8000); } LokiPoe.Input.ClickLMB(); await Coroutines.LatencyWait(); await Coroutines.FinishCurrentAction(false); LokiPoe.ProcessHookManager.ClearAllKeyStates(); return(true); }
/// <summary> /// This coroutines waits for the character to change positions from a local area transition. /// </summary> /// <param name="position">The starting position.</param> /// <param name="delta">The change in position required.</param> /// <param name="timeout">How long to wait before the coroutine fails.</param> /// <returns>true on success, and false on failure.</returns> public static async Task <bool> WaitForPositionChange(Vector2i position, int delta = 30, int timeout = 5000) { CommunityLib.Log.DebugFormat("[WaitForPositionChange]"); var sw = Stopwatch.StartNew(); while (LokiPoe.MyPosition.Distance(position) < delta) { if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.ErrorFormat("[WaitForLargerPositionChange] Timeout."); return(false); } CommunityLib.Log.DebugFormat("[WaitForLargerPositionChange] We have been waiting {0} for an area change.", sw.Elapsed); await Coroutines.LatencyWait(); } return(true); }
/// <summary> /// This coroutine waits for the instance manager to open. /// </summary> /// <param name="timeout">How long to wait before the coroutine fails.</param> /// <returns>true on succes and false on failure.</returns> public static async Task <bool> WaitForInstanceManager(int timeout = 1000) { CommunityLib.Log.DebugFormat("[WaitForInstanceManager]"); var sw = Stopwatch.StartNew(); while (!LokiPoe.InGameState.InstanceManagerUi.IsOpened) { if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.ErrorFormat("[WaitForInstanceManager] Timeout."); return(false); } CommunityLib.Log.DebugFormat("[WaitForInstanceManager] We have been waiting {0} for the instance manager to open.", sw.Elapsed); await Coroutines.LatencyWait(); } return(true); }
/// <summary> /// This coroutines waits for the character to change areas. /// </summary> /// <param name="original">The starting area's hash.</param> /// <param name="timeout">How long to wait before the coroutine fails.</param> /// <returns>true on success, and false on failure.</returns> public static async Task <bool> WaitForAreaChange(uint original, int timeout = 30000) { CommunityLib.Log.DebugFormat("[WaitForAreaChange]"); var sw = Stopwatch.StartNew(); while (LokiPoe.LocalData.AreaHash == original) { if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.ErrorFormat("[WaitForAreaChange] Timeout."); return(false); } CommunityLib.Log.DebugFormat("[WaitForAreaChange] We have been waiting {0} for an area change.", sw.Elapsed); await Coroutines.LatencyWait(); await Coroutine.Sleep(1000); } 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); }
/// <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); }
/// <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); }
/// <summary> /// This coroutine will attempt to take a portal /// </summary> /// <returns>true if the portal was taken, and an area change occurred, and false otherwise.</returns> public static async Task <bool> TakeClosestPortal() { var sw = Stopwatch.StartNew(); if (LokiPoe.ConfigManager.IsAlwaysHighlightEnabled) { CommunityLib.Log.InfoFormat("[TakeClosestPortal] Now disabling Always Highlight to avoid label issues."); LokiPoe.Input.SimulateKeyEvent(LokiPoe.Input.Binding.highlight_toggle, true, false, false); await Coroutine.Sleep(16); } NetworkObject portal = null; while (portal == null || !portal.IsTargetable) { CommunityLib.Log.DebugFormat($"[TakeClosestPortal] Now waiting for the portal to spawn. {sw.Elapsed} elapsed."); await Coroutines.LatencyWait(); portal = LokiPoe.ObjectManager.GetObjectsByType <Portal>() .Where(p => p.Distance < 50) .OrderBy(p => p.Distance) .FirstOrDefault(); if (sw.ElapsedMilliseconds > 10000) { break; } } if (portal == null) { CommunityLib.Log.ErrorFormat("[TakeClosestPortal] A portal was not found."); return(false); } var pos = ExilePather.FastWalkablePositionFor(portal); CommunityLib.Log.Debug($"[TakeClosestPortal] The portal was found at {pos}."); if (!await Navigation.MoveToLocation(pos, 5, 10000, () => false)) { return(false); } var hash = LokiPoe.LocalData.AreaHash; // Try to interact 3 times. for (var i = 0; i < 3; i++) { if (LokiPoe.Me.IsDead) { break; } await Coroutines.FinishCurrentAction(); CommunityLib.Log.Debug($"[TakeClosestPortal] The portal to interact with is {portal.Id} at {pos}."); if (await InteractWith(portal)) { if (await Areas.WaitForAreaChange(hash)) { CommunityLib.Log.Debug("[TakeClosestPortal] The portal has been taken."); return(true); } } await Coroutine.Sleep(1000); } CommunityLib.Log.ErrorFormat("[TakeClosestPortal] We have failed to take the portal 3 times."); return(false); }
/// <summary> /// This functions is meant to use an item on another one, for identification, chancing, anything you can think about /// It also supports the +X% quality using stones/scraps /// </summary> /// <param name="sourceWrapper">The source (inventory) that's holding the item meant to be used</param> /// <param name="sourceItem">The item meant to be used</param> /// <param name="destinationWrapper">The source (inventory) holding the item meant to be altered</param> /// <param name="destinationItem">The item menant to be altered</param> /// <param name="d">Delegate/Condition to stop using item</param> /// <returns>ApplyCursorResult enum entry</returns> public static async Task <ApplyCursorResult> UseItemOnItem(InventoryControlWrapper sourceWrapper, Item sourceItem, InventoryControlWrapper destinationWrapper, Item destinationItem, StopUsingDelegate d = null) { // If Any of these args are null, throw an application-level exception if (sourceWrapper == null) { throw new ArgumentNullException(nameof(sourceWrapper)); } if (sourceItem == null) { throw new ArgumentNullException(nameof(sourceItem)); } if (destinationWrapper == null) { throw new ArgumentNullException(nameof(destinationWrapper)); } if (destinationItem == null) { throw new ArgumentNullException(nameof(destinationItem)); } // If the item is not modifiable, prevent the logic to be executed if (destinationItem.IsCorrupted || destinationItem.IsMirrored) { CommunityLib.Log.DebugFormat("[CommunityLib] We can\'t alter the item (Corrupted/Mirrored). UnsupportedItem"); return(ApplyCursorResult.UnsupportedItem); } if (destinationItem.HasSkillGemsEquipped && sourceItem.FullName == "Jeweller's Orb") { CommunityLib.Log.DebugFormat("[CommunityLib] We can't change sockets on the item (Has skill gems in it). UnsupportedItem"); return(ApplyCursorResult.UnsupportedItem); } var onCursor = sourceWrapper.UseItem(sourceItem.LocalId); // We assume it's currency stash tab, do not use LocalId with it if (onCursor == UseItemResult.Unsupported) { CommunityLib.Log.DebugFormat("[CommunityLib] Failed to use item on item. Unsupported"); onCursor = sourceWrapper.UseItem(); } await Coroutines.LatencyWait(); // If something else than None is returned, the item can't be put on cursor properly if (onCursor != UseItemResult.None) { if (!await Inputs.WaitForCursorToHaveItem()) { CommunityLib.Log.ErrorFormat($"[CommunityLib] Failed to use item on item. OnCursor: {onCursor}. Returning item not found"); return(ApplyCursorResult.ItemNotFound); } } await Coroutines.LatencyWait(); // First, we put the item on cursor to start applying var err = InventoryControlWrapper.BeginApplyCursor(true); if (err != ApplyCursorResult.None) { CommunityLib.Log.Error($"[CommunityLib] Error returned for BeginApplyCursor : {err}"); return(ApplyCursorResult.ProcessHookManagerNotEnabled); } // We store the destination item's location to make sure it has been applied or the delegate (lower in the code) is valid var itemLocation = destinationItem.LocationTopLeft; int useCount = 0; while (true) { var initialId = destinationItem.LocalId; // Apply item on cursor to the destination item err = destinationWrapper.ApplyCursorTo(destinationItem.LocalId); //Destination is in utility currency slot? if (err == ApplyCursorResult.Unsupported) { err = destinationWrapper.ApplyCursorTo(); } // If the error is different of None, break the execution and return the error if (err != ApplyCursorResult.None) { break; } // Check if the item has been modified on memory-side if (!await WaitForItemToChange(destinationWrapper, initialId)) { break; } await Coroutines.LatencyWait(); //await Coroutines.ReactionWait(); // If the delegate is null, that means our processing is done, break the loop to return None if (d == null) { break; } // We increment usecount to make it usable in delegate useCount++; // Refresh item to test the delegate (or condition) destinationItem = destinationWrapper.Inventory.GetItemAtLocation(itemLocation.X, itemLocation.Y); if (d.Invoke(destinationItem, useCount)) { break; } } // End up the item application var err2 = InventoryControlWrapper.EndApplyCursor(); await Coroutine.Yield(); await Inputs.WaitForCursorToBeEmpty(); // IF an error is returned, let caller know if (err2 != ApplyCursorResult.None) { CommunityLib.Log.Error($"[CommunityLib] Error returned for EndApplyCursor : {err2}"); return(ApplyCursorResult.ProcessHookManagerNotEnabled); } if (err != ApplyCursorResult.None) { CommunityLib.Log.ErrorFormat($"[CommunityLib] Failed to use item on item. Error: {err}"); } return(err); }
/// <summary> /// Generic FastMove using new Inv Wrapper /// The inventory you refer is theinventory that will be used for moving the item from /// </summary> /// <param name="inv">This is the location where the item is picked up (can be stash or whatever you want)</param> /// <param name="id">This is the item localid</param> /// <param name="retries">Number of max fastmove attempts</param> /// <param name="breakFunc">If specified condition return true, FastMove will canceled and false will be returned</param> /// <returns>FastMoveResult enum entry</returns> public static async Task <bool> FastMove(InventoryControlWrapper inv, int id, int retries = 3, Func <bool> breakFunc = null) { // If the inventory is null for reasons, throw ana application-level error if (inv == null) { throw new ArgumentNullException(nameof(inv)); } // Here the idea is to make a first fastmove attempt to get an error // If the error is different of None, return the error var err = inv.FastMove(id); //We assume it's currency stash tab, do not use LocalId with it if (err == FastMoveResult.Unsupported) { err = inv.FastMove(); } if (err != FastMoveResult.None) { CommunityLib.Log.ErrorFormat("[CommunityLib][FastMove] FastMove has returned an error : {0}", err); return(false); } await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); // The idea is to have a maximum of tries, but we don't want to spam them. // A Timer is started to "cool-off" the tries and a random lapse is calculated between each checks var nextfastmovetimer = Stopwatch.StartNew(); var nextFastMove = LokiPoe.Random.Next(2500, 4000); int nextFastMoveTries = 0; while (nextFastMoveTries < retries) { if (breakFunc != null) { if (breakFunc()) { return(false); } } // Verifying if the item exists in the source inventory // If not, the item has been moved return true var itemExists = inv.Inventory.GetItemById(id); if (itemExists == null) { await Coroutines.ReactionWait(); return(true); } // If it exists, and the timer has reached the random lapse we calculated above, // Attempt to make a new move if (nextfastmovetimer.ElapsedMilliseconds > nextFastMove) { CommunityLib.Log.DebugFormat("[CommunityLib][FastMove] Attempt to fastmove ({0}/{1})", nextFastMoveTries, retries); var error = inv.FastMove(id); if (error == FastMoveResult.Unsupported) { inv.FastMove(); } await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); nextFastMove = LokiPoe.Random.Next(2500, 4000); nextfastmovetimer.Restart(); nextFastMoveTries++; } await Coroutine.Sleep(20); } // It failed after the number of tries referenced, just return false. CommunityLib.Log.ErrorFormat("[CommunityLib][FastMove] Operation failed after {0} tries", retries); return(false); }
/// <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); }
public static async Task <Results.ClearCursorResults> ClearCursorTask(int maxTries = 3) { var cursMode = LokiPoe.InGameState.CursorItemOverlay.Mode; if (cursMode == LokiPoe.InGameState.CursorItemModes.None) { CommunityLib.Log.DebugFormat("[CommunityLib][ClearCursorTask] Nothing is on cursor, continue execution"); return(Results.ClearCursorResults.None); } if (cursMode == LokiPoe.InGameState.CursorItemModes.VirtualMove || cursMode == LokiPoe.InGameState.CursorItemModes.VirtualUse) { CommunityLib.Log.DebugFormat("[CommunityLib][ClearCursorTask] VirtualMode detected, pressing escape to clear"); LokiPoe.Input.SimulateKeyEvent(Keys.Escape, true, false, false); return(Results.ClearCursorResults.None); } var cursorhasitem = LokiPoe.InGameState.CursorItemOverlay.Item; // there is a item on the cursor let clear it int attempts = 0; while (cursorhasitem != null && attempts < maxTries) { if (attempts > maxTries) { return(Results.ClearCursorResults.MaxTriesReached); } if (!LokiPoe.InGameState.InventoryUi.IsOpened) { await LibCoroutines.OpenInventoryPanel(); await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); if (!LokiPoe.InGameState.InventoryUi.IsOpened) { return(Results.ClearCursorResults.InventoryNotOpened); } } int col, row; if (!LokiPoe.InGameState.InventoryUi.InventoryControl_Main.Inventory.CanFitItem(cursorhasitem.Size, out col, out row)) { CommunityLib.Log.ErrorFormat("[CommunityLib][ClearCursorTask] Now stopping the bot because it cannot continue."); BotManager.Stop(); return(Results.ClearCursorResults.NoSpaceInInventory); } var res = LokiPoe.InGameState.InventoryUi.InventoryControl_Main.PlaceCursorInto(col, row); if (res == PlaceCursorIntoResult.None) { if (!await WaitForCursorToBeEmpty()) { CommunityLib.Log.ErrorFormat("[CommunityLib][ClearCursorTask] WaitForCursorToBeEmpty failed."); } await Coroutines.ReactionWait(); return(Results.ClearCursorResults.None); } CommunityLib.Log.DebugFormat("[CommunityLib][ClearCursorTask] Placing item into inventory failed, Err : {0}", res); switch (res) { case PlaceCursorIntoResult.ItemWontFit: return(Results.ClearCursorResults.NoSpaceInInventory); case PlaceCursorIntoResult.NoItemToMove: return(Results.ClearCursorResults.None); } await Coroutine.Sleep(3000); await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); cursorhasitem = LokiPoe.InGameState.CursorItemOverlay.Item; attempts++; } return(Results.ClearCursorResults.None); }
/// <summary> /// The function will use chat to move to hideout /// </summary> /// <param name="retries">Number of max fastmove attempts. One is 8-16s timeout</param> /// <returns></returns> public static async Task <Results.FastGoToHideoutResult> FastGoToHideout(int retries = 3) { // No need to proceed if we are already there. if (LokiPoe.Me.IsInHideout) { return(Results.FastGoToHideoutResult.None); } //I need to be in town to go to hideout using this method if (!LokiPoe.Me.IsInTown) { return(Results.FastGoToHideoutResult.NotInTown); } LokiPoe.InGameState.ChatPanel.Commands.hideout(); //Chat.SendChatMsg("/hideout", false); await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); // The idea is to have a maximum of tries, but we don't want to spam them. // A Timer is started to "cool-off" the tries and a random lapse is calculated between each checks var nextTimer = Stopwatch.StartNew(); var nextTry = LokiPoe.Random.Next(8000, 16000); int nextTryTries = 0; while (nextTryTries < retries) { if (LokiPoe.Me.IsInHideout) { await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); return(Results.FastGoToHideoutResult.None); } //Thanks pushedx for the function. I think I don't need it but I'll put a comment here so you'll feel better Kappa //LokiPoe.InGameState.IsEnteringAreaTextShown //User have no hideout var noHideoutMessage = Chat.GetNewChatMessages().Any(d => d.Message.Contains(Dat.LookupBackendError(BackendErrorEnum.NoHideout).Text)); if (noHideoutMessage) { return(Results.FastGoToHideoutResult.NoHideout); } // If it exists, and the timer has reached the random lapse we calculated above, if (nextTimer.ElapsedMilliseconds > nextTry) { CommunityLib.Log.DebugFormat("[CommunityLib][FastGoToHideout] Attempt to fastmove ({0}/{1})", nextTryTries, retries); if (LokiPoe.IsInGame) { LokiPoe.InGameState.ChatPanel.Commands.hideout(); } await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); nextTry = LokiPoe.Random.Next(8000, 16000); nextTimer.Restart(); nextTryTries++; } //No need to go that fast await Coroutine.Sleep(200); } CommunityLib.Log.ErrorFormat("[CommunityLib][FastGoToHideout] Operation failed after {0} tries", retries); return(Results.FastGoToHideoutResult.TimeOut); }
/// <summary> /// This function iterates through the stash to find an item by name /// If a tab is reached and the item is found, GUI will be stopped on this tab so you can directly interact with it. /// </summary> /// <param name="condition">Condition to pass item through</param> /// <returns></returns> public static async Task <Tuple <Results.FindItemInTabResult, CachedItemObject> > FindTabContainingItem(CommunityLib.FindItemDelegate condition) { // If stash isn't opened, abort this and return if (!await OpenStashTabTask()) { return(new Tuple <Results.FindItemInTabResult, CachedItemObject>(Results.FindItemInTabResult.GuiNotOpened, null)); } // If we fail to go to first tab, return // if (GoToFirstTab() != SwitchToTabResult.None) // return new Tuple<Results.FindItemInTabResult, StashItem>(Results.FindItemInTabResult.GoToFirstTabFailed, null); foreach (var tabName in StashUI.TabControl.TabNames) { // If the item has no occurences in this tab, switch to the next one var it = FindItemInStashTab(condition); if (it == null) { // On last tab? break execution if (StashUI.TabControl.IsOnLastTab) { break; } int switchAttemptsPerTab = 0; while (true) { // If we tried 3 times to switch and failed, return if (switchAttemptsPerTab > 2) { return(new Tuple <Results.FindItemInTabResult, CachedItemObject>(Results.FindItemInTabResult.SwitchToTabFailed, null)); } var switchTab = StashUI.TabControl.SwitchToTabMouse(tabName); // If the switch went fine, keep searching if (switchTab == SwitchToTabResult.None) { break; } switchAttemptsPerTab++; await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); } // Keep searching... await Coroutines.LatencyWait(); await Coroutines.ReactionWait(); continue; } // We Found a tab, return informations return(new Tuple <Results.FindItemInTabResult, CachedItemObject>(Results.FindItemInTabResult.None, it)); } return(new Tuple <Results.FindItemInTabResult, CachedItemObject>(Results.FindItemInTabResult.ItemNotFoundInTab, null)); }