using System; using System.Numerics; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Functions; using Questionable.Model; namespace Questionable.Controller.Steps.Movement; internal sealed class MoveToObjectExecutor(MovementController movementController, GameFunctions gameFunctions, IClientState clientState, IObjectTable objectTable, ILogger logger) : TaskExecutor() { private long? _startedLookingAt; private Vector3 _destination; private bool _moving; protected override bool Start() { if (!base.Task.Step.DataId.HasValue) { logger.LogWarning("MoveToObject task has no DataId"); return false; } if (clientState.TerritoryType != base.Task.Step.TerritoryId) { logger.LogDebug("Not in correct territory yet, waiting"); return true; } _startedLookingAt = Environment.TickCount64; return TryStartMovement(); } private IGameObject? FindDistantObjectByDataId(uint dataId) { IPlayerCharacter localPlayer = objectTable.LocalPlayer; if (localPlayer == null) { return null; } float num = base.Task.Step.CalculateActualStopDistance(); IGameObject result = null; float num2 = float.MaxValue; foreach (IGameObject item in objectTable) { ObjectKind objectKind = item.ObjectKind; bool flag = ((objectKind == ObjectKind.Pc || objectKind - 8 <= ObjectKind.BattleNpc || objectKind == ObjectKind.HousingEventObject) ? true : false); if (!flag && item.BaseId == dataId) { float num3 = Vector3.Distance(localPlayer.Position, item.Position); if (!(num3 <= num) && num3 < num2) { result = item; num2 = num3; } } } return result; } private bool TryStartMovement() { IGameObject gameObject = FindDistantObjectByDataId(base.Task.Step.DataId.Value); if (gameObject == null) { gameObject = gameFunctions.FindObjectByDataId(base.Task.Step.DataId.Value, null, warnIfMissing: false); if (gameObject == null) { logger.LogDebug("Object {DataId} not found yet, waiting", base.Task.Step.DataId); return true; } IPlayerCharacter localPlayer = objectTable.LocalPlayer; float num = base.Task.Step.CalculateActualStopDistance(); if (localPlayer != null && Vector3.Distance(localPlayer.Position, gameObject.Position) <= num) { logger.LogDebug("Only nearby objects with DataId {DataId} remain, skipping", base.Task.Step.DataId); return true; } } _destination = gameObject.Position; logger.LogDebug("Moving to object {DataId} at {Position}", base.Task.Step.DataId, _destination); movementController.NavigateTo(EMovementType.Quest, base.Task.Step.DataId, _destination, fly: false, base.Task.Step.Sprint ?? true, base.Task.Step.CalculateActualStopDistance()); _moving = true; return true; } public override ETaskResult Update() { if (clientState.TerritoryType != base.Task.Step.TerritoryId) { return ETaskResult.StillRunning; } if (!_moving) { long? startedLookingAt = _startedLookingAt; if (startedLookingAt.HasValue) { long valueOrDefault = startedLookingAt.GetValueOrDefault(); if (Environment.TickCount64 > valueOrDefault + 3000) { logger.LogDebug("Object {DataId} not found after timeout, skipping step", base.Task.Step.DataId); return ETaskResult.TaskComplete; } } if (!TryStartMovement()) { return ETaskResult.TaskComplete; } if (!_moving) { return ETaskResult.StillRunning; } } if (movementController.IsPathfinding || movementController.IsPathRunning) { return ETaskResult.StillRunning; } if (!movementController.HasBeenMovingForAtLeast(2000)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } }