public async Task <bool> Run() { if (!World.CurrentArea.IsMap) { return(false); } await Coroutines.FinishCurrentAction(); var maxPulses = MaxPulses; if (_pulse < maxPulses) { ++_pulse; GlobalLog.Info($"[FinishMapTask] Final pulse {_pulse}/{maxPulses}"); await Wait.SleepSafe(500); return(true); } GlobalLog.Warn("[FinishMapTask] Now leaving current map."); if (!await PlayerAction.TpToTown()) { ErrorManager.ReportError(); return(true); } MapBot.IsOnRun = false; Statistics.Instance.OnMapFinish(); GlobalLog.Info("[MapBot] MapFinished event."); Utility.BroadcastMessage(this, MapBot.Messages.MapFinished); return(true); }
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); }
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); }
/// <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); }
private static async Task <bool> KillArchitect(string type) { var arch = CachedIncursionData.Architects.Find(a => !a.Unwalkable && !a.Ignored && a.Type == type); if (arch == null) { return(false); } var pos = arch.Position; if (pos.IsFar) { if (!pos.TryCome()) { GlobalLog.Error($"[HandleIncursionTask] Fail to move to {pos}. Now marking it as unwalkable."); arch.Unwalkable = true; } return(true); } var attempts = ++arch.InteractionAttempts; if (attempts > MaxArchitectAttempts) { GlobalLog.Error($"[HandleIncursionTask] {pos.Name} was not killed. Now ignoring it."); arch.Ignored = true; return(true); } await Coroutines.FinishCurrentAction(); GlobalLog.Debug($"[HandleIncursionTask] Waiting for combat routine to kill the architect ({attempts}/{MaxArchitectAttempts})"); await Wait.StuckDetectionSleep(200); return(true); }
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(); }
/// <summary> /// This coroutine moves towards a position until it is within the specified stop distance. /// </summary> /// <param name="position">The position to move ot.</param> /// <param name="stopDistance">How close to the location should we get.</param> /// <param name="timeout">How long should the coroutine execute for before stopping due to timeout.</param> /// <param name="stopCondition">delegate to stop moving</param> /// <returns></returns> public static async Task <bool> MoveToLocation(Vector2i position, int stopDistance, int timeout, Func <bool> stopCondition) { var sw = Stopwatch.StartNew(); var dsw = Stopwatch.StartNew(); var da = (bool)PlayerMover.Instance.Execute("GetDoAdjustments"); PlayerMover.Instance.Execute("SetDoAdjustments", false); while (LokiPoe.MyPosition.Distance(position) > stopDistance) { if (LokiPoe.Me.IsDead) { CommunityLib.Log.ErrorFormat("[MoveToLocation] The player is dead."); PlayerMover.Instance.Execute("SetDoAdjustments", da); return(false); } if (sw.ElapsedMilliseconds > timeout) { CommunityLib.Log.ErrorFormat("[MoveToLocation] Timeout."); PlayerMover.Instance.Execute("SetDoAdjustments", da); return(false); } if (stopCondition()) { break; } if (dsw.ElapsedMilliseconds > 100) { CommunityLib.Log.DebugFormat( "[MoveToLocation] Now moving towards {0}. We have been performing this task for {1}.", position, sw.Elapsed); dsw.Restart(); } if (!PlayerMover.MoveTowards(position)) { CommunityLib.Log.ErrorFormat("[MoveToLocation] MoveTowards failed for {0}.", position); } await Coroutine.Yield(); } PlayerMover.Instance.Execute("SetDoAdjustments", da); await Coroutines.FinishCurrentAction(); return(true); }
public static async Task <bool> StopBeforeBoss(string name) { if (Settings.Instance.StopBeforeBoss(name)) { NotifyBoss(name); await Coroutines.FinishCurrentAction(); await Wait.StuckDetectionSleep(1000); return(true); } return(false); }
public static async Task <bool> TakeTransition(AreaTransition transition, bool newInstance = false) { if (transition == null) { GlobalLog.Error("[TakeTransition] Transition object is null."); return(false); } var pos = transition.WalkablePosition(); var type = transition.TransitionType; GlobalLog.Debug($"[TakeTransition] Now going to enter \"{pos.Name}\"."); await pos.ComeAtOnce(); await Coroutines.FinishCurrentAction(); await Wait.SleepSafe(100); var hash = LokiPoe.LocalData.AreaHash; var myPos = LokiPoe.MyPosition; bool entered = newInstance ? await CreateNewInstance(transition) : await Interact(transition); if (!entered) { return(false); } if (type == TransitionTypes.Local) { if (!await Wait.For(() => myPos.Distance(LokiPoe.MyPosition) > 15, "local transition")) { return(false); } await Wait.SleepSafe(250); } else { if (!await Wait.ForAreaChange(hash)) { return(false); } } GlobalLog.Debug($"[TakeTransition] \"{pos.Name}\" has been successfully entered."); return(true); }
private static async Task Click() { StuckDetection.Reset(); await Wait.LatencySleep(); var target = LokiPoe.InGameState.CurrentTarget; if (target != null) { GlobalLog.Info($"[HandleBlockingChestsTask] \"{target.Name}\" ({target.Id}) is under the cursor. Now clicking on it."); LokiPoe.Input.PressLMB(); await Coroutines.FinishCurrentAction(false); } }
public static async Task <bool> KillShavronne() { if (_shavronneKilled) { return(false); } if (World.Act6.ShavronneTower.IsCurrentArea) { UpdateShavronneFightObjects(); if (_shavronneRoomObj != null) { if (await Helpers.StopBeforeBoss(Settings.BossNames.Shavronne)) { return(true); } if (_brutus != null && _brutus.IsActive) { await Helpers.MoveAndWait(_brutus.WalkablePosition()); return(true); } if (_shavronne != null) { int distance = _shavronne.IsActive ? 20 : 35; var pos = _shavronne.WalkablePosition(); if (pos.Distance > distance) { pos.Come(); return(true); } GlobalLog.Debug($"Waiting for {pos.Name}"); await Coroutines.FinishCurrentAction(); await Wait.StuckDetectionSleep(200); return(true); } await Helpers.MoveAndWait(_shavronneRoomObj.WalkablePosition(), "Waiting for any Shavronne fight object"); return(true); } } await Travel.To(World.Act6.PrisonerGate); 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); }
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); }
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); }
/// <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() { if (!World.CurrentArea.IsTempleOfAtzoatl) { return(false); } var settings = Settings.Instance; if (settings.SkipTemple) { await Leave(); return(true); } if (settings.TrackMobInTemple && await TrackMobLogic.Execute()) { return(true); } var explorer = CombatAreaCache.Current.Explorer; var isExplored = explorer.BasicExplorer.PercentComplete >= settings.ExplorationPercent; if (settings.IgnoreBossroom) { if (isExplored) { GlobalLog.Warn($"[HandleTempleTask] Exploration limit has been reached ({settings.ExplorationPercent}%). Now leaving the temple."); await Leave(); return(true); } } else { if (isExplored && !explorer.Settings.FastTransition) { GlobalLog.Warn($"[HandleTempleTask] Exploration limit has been reached ({settings.ExplorationPercent}%). Now seeking the bossroom."); explorer.Settings.FastTransition = true; return(true); } var boss = Incursion.CachedOmnitect; if (boss != null && !boss.Ignored && !boss.Unwalkable) { var pos = boss.Position; if (pos.IsFar) { if (!pos.TryCome()) { GlobalLog.Error("[HandleTempleTask] Fail to move to Vaal Omnitect. Now marking it as unwalkable."); boss.Unwalkable = true; } return(true); } var attempts = ++boss.InteractionAttempts; if (attempts > MaxOmnitectAttempts) { GlobalLog.Error("[HandleTempleTask] Vaal Omnitect did not become active. Now ignoring it."); boss.Ignored = true; return(true); } await Coroutines.FinishCurrentAction(); GlobalLog.Debug($"[HandleTempleTask] Waiting for Vaal Omnitect to become active ({attempts}/{MaxOmnitectAttempts})"); await Wait.StuckDetectionSleep(200); return(true); } } if (await explorer.Execute()) { return(true); } GlobalLog.Warn("[HandleTempleTask] Temple is fully explored."); await Leave(); 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); }
/// <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); }
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); }
/// <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> /// 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 async Task <bool> Run() { if (BossKilled || MapExplorationTask.MapCompleted) { return(false); } var area = World.CurrentArea; if (!area.IsMap) { return(false); } if (_currentTarget == null) { if ((_currentTarget = CachedBosses.ClosestValid(b => !b.IsDead)) == null) { return(false); } } if (Blacklist.Contains(_currentTarget.Id)) { GlobalLog.Warn("[KillBossTask] Boss is in global blacklist. Now marking it as killed."); _currentTarget.IsDead = true; _currentTarget = null; RegisterDeath(); return(true); } if (_priorityBossName != null && _currentTarget.Position.Name != _priorityBossName) { var priorityBoss = CachedBosses.ClosestValid(b => !b.IsDead && b.Position.Name == _priorityBossName); if (priorityBoss != null) { GlobalLog.Debug($"[KillBossTask] Switching current target to \"{priorityBoss}\"."); _currentTarget = priorityBoss; return(true); } } if (_currentTarget.IsDead) { var newBoss = CachedBosses.Valid().FirstOrDefault(b => !b.IsDead); if (newBoss != null) { _currentTarget = newBoss; } } var pos = _currentTarget.Position; if (pos.Distance <= 50 && pos.PathDistance <= 55) { var bossObj = _currentTarget.Object as Monster; if (bossObj == null) { if (_teleportingBoss) { CachedBosses.Remove(_currentTarget); _currentTarget = null; return(true); } GlobalLog.Debug("[KillBossTask] We are close to last know position of map boss, but boss object does not exist anymore."); GlobalLog.Debug("[KillBossTask] Most likely this boss does not spawn a corpse or was shattered/exploded."); _currentTarget.IsDead = true; _currentTarget = null; RegisterDeath(); return(true); } } if (pos.Distance > _bossRange) { if (LogInterval.Elapsed) { GlobalLog.Debug($"[KillBossTask] Going to {pos}"); } if (!pos.TryCome()) { GlobalLog.Error(MapData.Current.Type == MapType.Regular ? $"[KillBossTask] Unexpected error. Fail to move to map boss ({pos.Name}) in a regular map." : $"[KillBossTask] Fail to move to the map boss \"{pos.Name}\". Will try again after area transition."); _currentTarget.Unwalkable = true; _currentTarget = null; } return(true); } var attempts = ++_currentTarget.InteractionAttempts; // Helps to trigger Gorge and Underground River bosses if (attempts == MaxKillAttempts / 4) { GlobalLog.Debug("[KillBossTask] Trying to move around to trigger a boss."); var distantPos = WorldPosition.FindPathablePositionAtDistance(40, 70, 5); if (distantPos != null) { await Move.AtOnce(distantPos, "distant position", 10); } } if (attempts > MaxKillAttempts) { GlobalLog.Error("[KillBossTask] Boss did not become active. Now ignoring it."); _currentTarget.Ignored = true; _currentTarget = null; RegisterDeath(); return(true); } await Coroutines.FinishCurrentAction(); GlobalLog.Debug($"[KillBossTask] Waiting for map boss to become active ({attempts}/{MaxKillAttempts})"); await Wait.StuckDetectionSleep(200); return(true); }
/// <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); }