Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        /// <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);
        }
Exemple #6
0
        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();
        }
Exemple #7
0
        /// <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);
        }
Exemple #8
0
        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);
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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);
            }
        }
Exemple #11
0
        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);
        }
Exemple #12
0
        /// <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);
        }
Exemple #15
0
        /// <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);
        }
Exemple #16
0
        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);
        }
Exemple #18
0
        /// <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);
        }
Exemple #19
0
        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);
        }
Exemple #20
0
        /// <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);
        }
Exemple #21
0
        /// <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);
        }
Exemple #24
0
        /// <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);
        }