forked from aly/qstbak
muffin v7.38.9
This commit is contained in:
parent
ada27cf05b
commit
8a7847ff37
21 changed files with 1296 additions and 689 deletions
|
|
@ -173,6 +173,10 @@ internal static class Interact
|
|||
protected override bool Start()
|
||||
{
|
||||
InteractionType = base.Task.InteractionType;
|
||||
_interactionState = EInteractionState.None;
|
||||
_needsUnmount = false;
|
||||
delayedFinalCheck = false;
|
||||
_continueAt = DateTime.MinValue;
|
||||
IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId);
|
||||
if (gameObject == null)
|
||||
{
|
||||
|
|
@ -259,6 +263,7 @@ internal static class Interact
|
|||
{
|
||||
if (base.ProgressContext.WasInterrupted())
|
||||
{
|
||||
logger.LogDebug("Interaction with {DataId} was interrupted", base.Task.DataId);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
if (base.ProgressContext.WasSuccessful() || _interactionState == EInteractionState.InteractionConfirmed)
|
||||
|
|
|
|||
|
|
@ -196,15 +196,40 @@ internal static class WaitAtEnd
|
|||
}
|
||||
}
|
||||
|
||||
internal sealed class WaitNextStepOrSequenceExecutor : TaskExecutor<WaitNextStepOrSequence>
|
||||
internal sealed class WaitNextStepOrSequenceExecutor(QuestController questController) : TaskExecutor<WaitNextStepOrSequence>()
|
||||
{
|
||||
private ElementId? _questId;
|
||||
|
||||
private byte _initialSequence;
|
||||
|
||||
private int _initialStep;
|
||||
|
||||
protected override bool Start()
|
||||
{
|
||||
QuestController.QuestProgress currentQuest = questController.CurrentQuest;
|
||||
if (currentQuest != null)
|
||||
{
|
||||
_questId = currentQuest.Quest.Id;
|
||||
_initialSequence = currentQuest.Sequence;
|
||||
_initialStep = currentQuest.Step;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
if (_questId != null)
|
||||
{
|
||||
QuestController.QuestProgress currentQuest = questController.CurrentQuest;
|
||||
if (currentQuest == null || currentQuest.Quest.Id != _questId)
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
if (currentQuest.Sequence != _initialSequence || currentQuest.Step != _initialStep)
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
}
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ internal abstract class TaskExecutor<T> : ITaskExecutor where T : class, ITask
|
|||
if (task is T task2)
|
||||
{
|
||||
Task = task2;
|
||||
ProgressContext = null;
|
||||
return Start();
|
||||
}
|
||||
throw new TaskException($"Unable to cast {task.GetType()} to {typeof(T)}");
|
||||
|
|
|
|||
|
|
@ -15,11 +15,6 @@ internal sealed class InterruptHandler : IDisposable
|
|||
{
|
||||
private unsafe delegate void ProcessActionEffect(uint sourceId, Character* sourceCharacter, Vector3* pos, EffectHeader* effectHeader, EffectEntry* effectArray, ulong* effectTail);
|
||||
|
||||
private static class Signatures
|
||||
{
|
||||
internal const string ActionEffect = "40 ?? 56 57 41 ?? 41 ?? 41 ?? 48 ?? ?? ?? ?? ?? ?? ?? 48";
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct EffectEntry
|
||||
{
|
||||
|
|
@ -150,7 +145,7 @@ internal sealed class InterruptHandler : IDisposable
|
|||
_objectTable = objectTable;
|
||||
_territoryData = territoryData;
|
||||
_logger = logger;
|
||||
_processActionEffectHook = gameInteropProvider.HookFromSignature<ProcessActionEffect>("40 ?? 56 57 41 ?? 41 ?? 41 ?? 48 ?? ?? ?? ?? ?? ?? ?? 48", HandleProcessActionEffect);
|
||||
_processActionEffectHook = gameInteropProvider.HookFromAddress<ProcessActionEffect>(ActionEffectHandler.Addresses.Receive.Value, HandleProcessActionEffect);
|
||||
_processActionEffectHook.Enable();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
private readonly AetheryteData _aetheryteData;
|
||||
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
private readonly ILogger<MovementController> _logger;
|
||||
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
|
@ -93,6 +95,14 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
private long _pathfindStartTime;
|
||||
|
||||
private Vector3? _lastKnownPosition;
|
||||
|
||||
private long _lastPositionUpdateTime;
|
||||
|
||||
private Vector3? _expectedPosition;
|
||||
|
||||
private bool _isTrackingPlayerInput;
|
||||
|
||||
public bool IsNavmeshReady
|
||||
{
|
||||
get
|
||||
|
|
@ -146,7 +156,9 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
public int NumQueuedPathfindRequests => _navmeshIpc.NumQueuedPathfindRequests;
|
||||
|
||||
public MovementController(NavmeshIpc navmeshIpc, IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, ChatFunctions chatFunctions, ICondition condition, MovementOverrideController movementOverrideController, AetheryteData aetheryteData, ILogger<MovementController> logger)
|
||||
public event EventHandler? PlayerInputDetected;
|
||||
|
||||
public MovementController(NavmeshIpc navmeshIpc, IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, ChatFunctions chatFunctions, ICondition condition, MovementOverrideController movementOverrideController, AetheryteData aetheryteData, Configuration configuration, ILogger<MovementController> logger)
|
||||
{
|
||||
_navmeshIpc = navmeshIpc;
|
||||
_clientState = clientState;
|
||||
|
|
@ -156,11 +168,19 @@ internal sealed class MovementController : IDisposable
|
|||
_condition = condition;
|
||||
_movementOverrideController = movementOverrideController;
|
||||
_aetheryteData = aetheryteData;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public unsafe void Update()
|
||||
{
|
||||
if (IsPathRunning && _isTrackingPlayerInput && DetectPlayerInputInterference())
|
||||
{
|
||||
_logger.LogInformation("Player input detected during automatic movement, raising event to stop automation");
|
||||
this.PlayerInputDetected?.Invoke(this, EventArgs.Empty);
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
if (_pathfindTask != null && Destination != null)
|
||||
{
|
||||
if (!_pathfindTask.IsCompleted && Environment.TickCount64 - _pathfindStartTime > 30000 && _navmeshIpc.NumQueuedPathfindRequests > 5)
|
||||
|
|
@ -188,6 +208,11 @@ internal sealed class MovementController : IDisposable
|
|||
if (Destination.IsFlying && Destination.Land)
|
||||
{
|
||||
_logger.LogWarning("Adjusted destination failed, trying tolerance-based pathfinding");
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready for tolerance-based pathfinding");
|
||||
return;
|
||||
}
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(30L));
|
||||
Vector3 vector2 = _objectTable[0]?.Position ?? Vector3.Zero;
|
||||
|
|
@ -218,6 +243,11 @@ internal sealed class MovementController : IDisposable
|
|||
(list, _) = tuple;
|
||||
if (tuple.Item2 && Destination.ShouldRecalculateNavmesh())
|
||||
{
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready for recalculation");
|
||||
return;
|
||||
}
|
||||
Destination.NavmeshCalculations++;
|
||||
Destination.PartialRoute.AddRange(list);
|
||||
_logger.LogInformation("Running navmesh recalculation with fudged point ({From} to {To})", list.Last(), Destination.Position);
|
||||
|
|
@ -232,6 +262,7 @@ internal sealed class MovementController : IDisposable
|
|||
_logger.LogInformation("Navigating via route: [{Route}]", string.Join(" → ", _pathfindTask.Result.Select((Vector3 x) => x.ToString("G", CultureInfo.InvariantCulture))));
|
||||
_navmeshIpc.MoveTo(list, Destination.IsFlying);
|
||||
MovementStartedAt = DateTime.Now;
|
||||
StartPlayerInputTracking();
|
||||
ResetPathfinding();
|
||||
}
|
||||
else if (_pathfindTask.IsCompleted)
|
||||
|
|
@ -321,6 +352,72 @@ internal sealed class MovementController : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private void StartPlayerInputTracking()
|
||||
{
|
||||
IGameObject gameObject = _objectTable[0];
|
||||
if (gameObject != null)
|
||||
{
|
||||
_lastKnownPosition = gameObject.Position;
|
||||
_expectedPosition = gameObject.Position;
|
||||
_lastPositionUpdateTime = Environment.TickCount64;
|
||||
_isTrackingPlayerInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool DetectPlayerInputInterference()
|
||||
{
|
||||
if (!_configuration.General.StopOnPlayerInput)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_isTrackingPlayerInput || !_lastKnownPosition.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IGameObject gameObject = _objectTable[0];
|
||||
if (gameObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector3 position = gameObject.Position;
|
||||
long tickCount = Environment.TickCount64;
|
||||
if (tickCount - _lastPositionUpdateTime < 100)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
List<Vector3> waypoints = _navmeshIpc.GetWaypoints();
|
||||
if (waypoints.Count > 0)
|
||||
{
|
||||
_expectedPosition = waypoints[0];
|
||||
}
|
||||
if (_expectedPosition.HasValue)
|
||||
{
|
||||
Vector3 vector = Vector3.Normalize(_expectedPosition.Value - _lastKnownPosition.Value);
|
||||
Vector3 value = position - _lastKnownPosition.Value;
|
||||
if (value.Length() > 0.1f)
|
||||
{
|
||||
Vector3 vector2 = Vector3.Normalize(value);
|
||||
float num = Vector3.Dot(vector, vector2);
|
||||
if (num < 0.7f)
|
||||
{
|
||||
_logger.LogDebug("Player movement detected: alignment={Alignment:F2}, actual={Actual}, expected={Expected}", num, value.ToString("G", CultureInfo.InvariantCulture), vector.ToString("G", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_lastKnownPosition = position;
|
||||
_lastPositionUpdateTime = tickCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void StopPlayerInputTracking()
|
||||
{
|
||||
_isTrackingPlayerInput = false;
|
||||
_lastKnownPosition = null;
|
||||
_expectedPosition = null;
|
||||
_lastPositionUpdateTime = 0L;
|
||||
}
|
||||
|
||||
private void Restart(DestinationData destination)
|
||||
{
|
||||
Stop();
|
||||
|
|
@ -364,6 +461,11 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
public void NavigateTo(EMovementType type, uint? dataId, Vector3 to, bool fly, bool sprint, float? stopDistance = null, float? verticalStopDistance = null, bool land = false)
|
||||
{
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready, cannot start navigation to {Position}", to.ToString("G", CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
fly |= _condition[ConditionFlag.Diving];
|
||||
if (fly && land)
|
||||
{
|
||||
|
|
@ -404,6 +506,11 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
public void NavigateTo(EMovementType type, uint? dataId, List<Vector3> to, bool fly, bool sprint, float? stopDistance, float? verticalStopDistance = null, bool land = false)
|
||||
{
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready, cannot start navigation to {Position}", to.Last().ToString("G", CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
fly |= _condition[ConditionFlag.Diving];
|
||||
if (fly && land && to.Count > 0)
|
||||
{
|
||||
|
|
@ -416,6 +523,7 @@ internal sealed class MovementController : IDisposable
|
|||
_logger.LogInformation("Moving to {Destination}", Destination);
|
||||
_navmeshIpc.MoveTo(to, fly);
|
||||
MovementStartedAt = DateTime.Now;
|
||||
StartPlayerInputTracking();
|
||||
}
|
||||
|
||||
public void ResetPathfinding()
|
||||
|
|
@ -436,6 +544,11 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
private Vector3? TryFindAccessibleDestination(Vector3 target, bool flying, bool landing)
|
||||
{
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready, cannot find accessible destination");
|
||||
return null;
|
||||
}
|
||||
float[] array = ((!(flying && landing)) ? ((!flying) ? new float[3] { 1f, 3f, 5f } : new float[3] { 2f, 5f, 10f }) : new float[3] { 5f, 10f, 15f });
|
||||
float[] array2 = ((!flying) ? new float[3] { 1f, 2f, 3f } : new float[3] { 3f, 5f, 10f });
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
|
|
@ -504,17 +617,52 @@ internal sealed class MovementController : IDisposable
|
|||
if (Math.Abs((double)num - Destination.LastWaypoint.Distance2DAtLastUpdate) < 0.5)
|
||||
{
|
||||
int navmeshCalculations = Destination.NavmeshCalculations;
|
||||
if (navmeshCalculations % 6 == 1)
|
||||
switch (navmeshCalculations)
|
||||
{
|
||||
_logger.LogWarning("Jumping to try and resolve navmesh problem (n = {Calculations})", navmeshCalculations);
|
||||
case 1:
|
||||
case 7:
|
||||
_logger.LogWarning("Jumping to try and resolve navmesh problem (n = {Calculations}) at {Position}", navmeshCalculations, Destination.Position.ToString("G", CultureInfo.InvariantCulture));
|
||||
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null);
|
||||
Destination.NavmeshCalculations++;
|
||||
Destination.LastWaypoint.UpdatedAt = Environment.TickCount64;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Recalculating navmesh (n = {Calculations})", navmeshCalculations);
|
||||
break;
|
||||
case 5:
|
||||
_logger.LogWarning("Reloading navmesh (n = {Calculations}) at {Position}", navmeshCalculations, Destination.Position.ToString("G", CultureInfo.InvariantCulture));
|
||||
_navmeshIpc.Reload();
|
||||
Destination.LastWaypoint.UpdatedAt = Environment.TickCount64;
|
||||
break;
|
||||
case 6:
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready after reload (n = {Calculations})", navmeshCalculations);
|
||||
return false;
|
||||
}
|
||||
_logger.LogInformation("Navmesh ready after reload, restarting navigation (n = {Calculations})", navmeshCalculations);
|
||||
Restart(Destination);
|
||||
break;
|
||||
case 8:
|
||||
_logger.LogWarning("Rebuilding navmesh (n = {Calculations}) at {Position}", navmeshCalculations, Destination.Position.ToString("G", CultureInfo.InvariantCulture));
|
||||
_navmeshIpc.Rebuild();
|
||||
Destination.LastWaypoint.UpdatedAt = Environment.TickCount64;
|
||||
break;
|
||||
case 9:
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready after rebuild (n = {Calculations})", navmeshCalculations);
|
||||
return false;
|
||||
}
|
||||
_logger.LogInformation("Navmesh ready after rebuild, restarting navigation (n = {Calculations})", navmeshCalculations);
|
||||
Restart(Destination);
|
||||
break;
|
||||
default:
|
||||
if (!IsNavmeshReady)
|
||||
{
|
||||
_logger.LogWarning("Navmesh not ready for recalculation (n = {Calculations})", navmeshCalculations);
|
||||
return false;
|
||||
}
|
||||
_logger.LogWarning("Recalculating navmesh (n = {Calculations}) at {Position}", navmeshCalculations, Destination.Position.ToString("G", CultureInfo.InvariantCulture));
|
||||
Restart(Destination);
|
||||
break;
|
||||
}
|
||||
Destination.NavmeshCalculations = navmeshCalculations + 1;
|
||||
return true;
|
||||
|
|
@ -570,6 +718,7 @@ internal sealed class MovementController : IDisposable
|
|||
|
||||
public void Stop()
|
||||
{
|
||||
StopPlayerInputTracking();
|
||||
_navmeshIpc.Stop();
|
||||
ResetPathfinding();
|
||||
Destination = null;
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
_toastGui.Toast += OnNormalToast;
|
||||
_condition.ConditionChange += OnConditionChange;
|
||||
_clientState.Logout += OnLogout;
|
||||
_movementController.PlayerInputDetected += OnPlayerInputDetected;
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
|
|
@ -683,55 +684,71 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
{
|
||||
DebugState = "No quest active";
|
||||
Stop("No quest active");
|
||||
return;
|
||||
}
|
||||
if (_gameFunctions.IsOccupied() && !_gameFunctions.IsOccupiedWithCustomDeliveryNpc(questProgress.Quest))
|
||||
else if (questProgress.Step == 255)
|
||||
{
|
||||
DebugState = $"Waiting for sequence update (current: {questProgress.Sequence})";
|
||||
if (!_taskQueue.AllTasksComplete)
|
||||
{
|
||||
DebugState = "Step 255 - processing interrupted tasks";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.CurrentQuest == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TimeSpan timeSpan = DateTime.Now - this.CurrentQuest.StepProgress.StartedAt;
|
||||
if (timeSpan > TimeSpan.FromSeconds(3L))
|
||||
{
|
||||
_logger.LogWarning("Step 255 with no tasks for {WaitTime:F1}s, retrying step to ensure completion (quest: {QuestId}, sequence: {Sequence})", timeSpan.TotalSeconds, questProgress.Quest.Id, questProgress.Sequence);
|
||||
QuestSequence questSequence = questProgress.Quest.FindSequence(questProgress.Sequence);
|
||||
if (questSequence != null && questSequence.Steps.Count > 0)
|
||||
{
|
||||
this.CurrentQuest.SetStep(questSequence.Steps.Count - 1);
|
||||
CheckNextTasks("Retry last step at 255");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_gameFunctions.IsOccupied() && !_gameFunctions.IsOccupiedWithCustomDeliveryNpc(questProgress.Quest))
|
||||
{
|
||||
DebugState = "Occupied";
|
||||
return;
|
||||
}
|
||||
if (_movementController.IsPathfinding)
|
||||
else if (_movementController.IsPathfinding)
|
||||
{
|
||||
DebugState = "Pathfinding is running";
|
||||
return;
|
||||
}
|
||||
if (_movementController.IsPathRunning)
|
||||
else if (_movementController.IsPathRunning)
|
||||
{
|
||||
DebugState = "Path is running";
|
||||
return;
|
||||
}
|
||||
if (DateTime.Now < _safeAnimationEnd)
|
||||
else if (DateTime.Now < _safeAnimationEnd)
|
||||
{
|
||||
DebugState = "Waiting for Animation";
|
||||
return;
|
||||
}
|
||||
if (questProgress.Sequence != b)
|
||||
else if (questProgress.Sequence != b)
|
||||
{
|
||||
questProgress.SetSequence(b);
|
||||
CheckNextTasks($"New sequence {questProgress == _startedQuest}/{_questFunctions.GetCurrentQuestInternal(allowNewMsq: true)}");
|
||||
}
|
||||
QuestSequence questSequence = questProgress.Quest.FindSequence(questProgress.Sequence);
|
||||
if (questSequence == null)
|
||||
{
|
||||
DebugState = $"Sequence {questProgress.Sequence} not found";
|
||||
Stop("Unknown sequence");
|
||||
}
|
||||
else if (questProgress.Step == 255)
|
||||
{
|
||||
DebugState = "Step completed";
|
||||
if (!_taskQueue.AllTasksComplete)
|
||||
{
|
||||
CheckNextTasks("Step complete");
|
||||
}
|
||||
}
|
||||
else if (questSequence.Steps.Count > 0 && questProgress.Step >= questSequence.Steps.Count)
|
||||
{
|
||||
DebugState = "Step not found";
|
||||
Stop("Unknown step");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugState = null;
|
||||
QuestSequence questSequence2 = questProgress.Quest.FindSequence(questProgress.Sequence);
|
||||
if (questSequence2 == null)
|
||||
{
|
||||
DebugState = $"Sequence {questProgress.Sequence} not found";
|
||||
Stop("Unknown sequence");
|
||||
}
|
||||
else if (questSequence2.Steps.Count > 0 && questProgress.Step >= questSequence2.Steps.Count)
|
||||
{
|
||||
DebugState = "Step not found";
|
||||
Stop("Unknown step");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugState = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -778,15 +795,21 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
_logger.LogWarning("Ignoring 'increase step count' for different sequence (expected {ExpectedSequence}, but we are at {CurrentSequence}", sequence, questSequence.Sequence);
|
||||
}
|
||||
_logger.LogInformation("Increasing step count from {CurrentValue}", CurrentQuest.Step);
|
||||
if (CurrentQuest.Step + 1 < questSequence.Steps.Count)
|
||||
{
|
||||
CurrentQuest.SetStep(CurrentQuest.Step + 1);
|
||||
}
|
||||
else
|
||||
bool num = CurrentQuest.Step + 1 >= questSequence.Steps.Count;
|
||||
if (num)
|
||||
{
|
||||
CurrentQuest.SetStep(255);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentQuest.SetStep(CurrentQuest.Step + 1);
|
||||
}
|
||||
ResetAutoRefreshState();
|
||||
if (num)
|
||||
{
|
||||
_logger.LogInformation("Completed last step in sequence, waiting for game to update sequence");
|
||||
return;
|
||||
}
|
||||
}
|
||||
using (_logger.BeginScope("IncStepCt"))
|
||||
{
|
||||
|
|
@ -1291,12 +1314,23 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
}
|
||||
}
|
||||
|
||||
private void OnPlayerInputDetected(object? sender, EventArgs e)
|
||||
{
|
||||
if (AutomationType != EAutomationType.Manual && IsRunning)
|
||||
{
|
||||
_logger.LogInformation("Player input detected during movement, stopping quest automation");
|
||||
_chatGui.Print("Player input detected - stopping quest automation.", "Questionable", 576);
|
||||
Stop("Player input detected");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_toastGui.ErrorToast -= base.OnErrorToast;
|
||||
_toastGui.Toast -= OnNormalToast;
|
||||
_condition.ConditionChange -= OnConditionChange;
|
||||
_clientState.Logout -= OnLogout;
|
||||
_movementController.PlayerInputDetected -= OnPlayerInputDetected;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -39,6 +39,10 @@ internal sealed class NavmeshIpc
|
|||
|
||||
private readonly ICallGateSubscriber<float> _buildProgress;
|
||||
|
||||
private readonly ICallGateSubscriber<bool> _navReload;
|
||||
|
||||
private readonly ICallGateSubscriber<bool> _navRebuild;
|
||||
|
||||
public bool IsReady
|
||||
{
|
||||
get
|
||||
|
|
@ -115,6 +119,8 @@ internal sealed class NavmeshIpc
|
|||
_queryPointOnFloor = pluginInterface.GetIpcSubscriber<Vector3, bool, float, Vector3?>("vnavmesh.Query.Mesh.PointOnFloor");
|
||||
_queryNearestPoint = pluginInterface.GetIpcSubscriber<Vector3, float, float, Vector3?>("vnavmesh.Query.Mesh.NearestPoint");
|
||||
_buildProgress = pluginInterface.GetIpcSubscriber<float>("vnavmesh.Nav.BuildProgress");
|
||||
_navReload = pluginInterface.GetIpcSubscriber<bool>("vnavmesh.Nav.Reload");
|
||||
_navRebuild = pluginInterface.GetIpcSubscriber<bool>("vnavmesh.Nav.Rebuild");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
|
@ -129,6 +135,30 @@ internal sealed class NavmeshIpc
|
|||
}
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
try
|
||||
{
|
||||
_navReload.InvokeFunc();
|
||||
}
|
||||
catch (IpcError exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "Could not reload navmesh");
|
||||
}
|
||||
}
|
||||
|
||||
public void Rebuild()
|
||||
{
|
||||
try
|
||||
{
|
||||
_navRebuild.InvokeFunc();
|
||||
}
|
||||
catch (IpcError exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "Could not rebuild navmesh");
|
||||
}
|
||||
}
|
||||
|
||||
public Task<List<Vector3>> Pathfind(Vector3 localPlayerPosition, Vector3 targetPosition, bool fly, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
|
|
@ -147,8 +177,8 @@ internal sealed class NavmeshIpc
|
|||
{
|
||||
try
|
||||
{
|
||||
_pathSetTolerance.InvokeAction(0.25f);
|
||||
return _pluginInterface.GetIpcSubscriber<Vector3, Vector3, bool, float, CancellationToken, Task<List<Vector3>>>("vnavmesh.Nav.PathfindWithTolerance").InvokeFunc(localPlayerPosition, targetPosition, fly, tolerance, cancellationToken);
|
||||
_pathSetTolerance.InvokeAction(tolerance);
|
||||
return _pluginInterface.GetIpcSubscriber<Vector3, Vector3, bool, float, Task<List<Vector3>>>("vnavmesh.Nav.PathfindWithTolerance").InvokeFunc(localPlayerPosition, targetPosition, fly, tolerance);
|
||||
}
|
||||
catch (IpcError exception)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Dalamud.Plugin.Services;
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Model.Questing;
|
||||
|
|
@ -21,11 +22,6 @@ internal sealed class ChatFunctions
|
|||
{
|
||||
private delegate void ProcessChatBoxDelegate(nint uiModule, nint message, nint unused, byte a4);
|
||||
|
||||
private static class Signatures
|
||||
{
|
||||
internal const string SendChat = "48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F2 48 8B F9 45 84 C9";
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private readonly struct ChatPayload : IDisposable
|
||||
{
|
||||
|
|
@ -72,7 +68,7 @@ internal sealed class ChatFunctions
|
|||
_gameFunctions = gameFunctions;
|
||||
_targetManager = targetManager;
|
||||
_logger = logger;
|
||||
_processChatBox = Marshal.GetDelegateForFunctionPointer<ProcessChatBoxDelegate>(sigScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F2 48 8B F9 45 84 C9"));
|
||||
_processChatBox = Marshal.GetDelegateForFunctionPointer<ProcessChatBoxDelegate>(UIModule.Addresses.ProcessChatBoxEntry.Value);
|
||||
_emoteCommands = (from x in dataManager.GetExcelSheet<Emote>()
|
||||
where x.RowId != 0
|
||||
where x.TextCommand.IsValid
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using Dalamud.Plugin.Services;
|
|||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Event;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Fate;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
|
|
@ -34,11 +35,6 @@ internal sealed class GameFunctions
|
|||
{
|
||||
private delegate void AbandonDutyDelegate(bool a1);
|
||||
|
||||
private static class Signatures
|
||||
{
|
||||
internal const string AbandonDuty = "E8 ?? ?? ?? ?? 41 B2 01 EB 39";
|
||||
}
|
||||
|
||||
private readonly QuestFunctions _questFunctions;
|
||||
|
||||
private readonly IDataManager _dataManager;
|
||||
|
|
@ -74,7 +70,7 @@ internal sealed class GameFunctions
|
|||
_gameGui = gameGui;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
_abandonDuty = Marshal.GetDelegateForFunctionPointer<AbandonDutyDelegate>(sigScanner.ScanText("E8 ?? ?? ?? ?? 41 B2 01 EB 39"));
|
||||
_abandonDuty = Marshal.GetDelegateForFunctionPointer<AbandonDutyDelegate>(EventFramework.Addresses.LeaveCurrentContent.Value);
|
||||
_territoryToAetherCurrentCompFlgSet = (from x in dataManager.GetExcelSheet<TerritoryType>()
|
||||
where x.RowId != 0
|
||||
where x.AetherCurrentCompFlgSet.RowId != 0
|
||||
|
|
|
|||
5
Questionable/Questionable.Functions/GameSignatures.cs
Normal file
5
Questionable/Questionable.Functions/GameSignatures.cs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
namespace Questionable.Functions;
|
||||
|
||||
internal static class GameSignatures
|
||||
{
|
||||
}
|
||||
|
|
@ -183,22 +183,28 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
base.Configuration.General.UseEscToCancelQuesting = v2;
|
||||
Save();
|
||||
}
|
||||
bool v3 = base.Configuration.General.ShowIncompleteSeasonalEvents;
|
||||
if (ImGui.Checkbox("Show details for incomplete seasonal events", ref v3))
|
||||
bool v3 = base.Configuration.General.StopOnPlayerInput;
|
||||
if (ImGui.Checkbox("Stop automation when manually moving character", ref v3))
|
||||
{
|
||||
base.Configuration.General.ShowIncompleteSeasonalEvents = v3;
|
||||
base.Configuration.General.StopOnPlayerInput = v3;
|
||||
Save();
|
||||
}
|
||||
bool v4 = base.Configuration.General.HideSeasonalEventsFromJournalProgress;
|
||||
if (ImGui.Checkbox("Hide Seasonal Events from Journal Progress", ref v4))
|
||||
bool v4 = base.Configuration.General.ShowIncompleteSeasonalEvents;
|
||||
if (ImGui.Checkbox("Show details for incomplete seasonal events", ref v4))
|
||||
{
|
||||
base.Configuration.General.HideSeasonalEventsFromJournalProgress = v4;
|
||||
base.Configuration.General.ShowIncompleteSeasonalEvents = v4;
|
||||
Save();
|
||||
}
|
||||
bool v5 = base.Configuration.General.ShowChangelogOnUpdate;
|
||||
if (ImGui.Checkbox("Show changelog window when plugin updates", ref v5))
|
||||
bool v5 = base.Configuration.General.HideSeasonalEventsFromJournalProgress;
|
||||
if (ImGui.Checkbox("Hide Seasonal Events from Journal Progress", ref v5))
|
||||
{
|
||||
base.Configuration.General.ShowChangelogOnUpdate = v5;
|
||||
base.Configuration.General.HideSeasonalEventsFromJournalProgress = v5;
|
||||
Save();
|
||||
}
|
||||
bool v6 = base.Configuration.General.ShowChangelogOnUpdate;
|
||||
if (ImGui.Checkbox("Show changelog window when plugin updates", ref v6))
|
||||
{
|
||||
base.Configuration.General.ShowChangelogOnUpdate = v6;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
|
@ -206,16 +212,16 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
ImGui.Text("Questing");
|
||||
using (ImRaii.PushIndent())
|
||||
{
|
||||
bool v6 = base.Configuration.General.ConfigureTextAdvance;
|
||||
if (ImGui.Checkbox("Automatically configure TextAdvance with the recommended settings", ref v6))
|
||||
bool v7 = base.Configuration.General.ConfigureTextAdvance;
|
||||
if (ImGui.Checkbox("Automatically configure TextAdvance with the recommended settings", ref v7))
|
||||
{
|
||||
base.Configuration.General.ConfigureTextAdvance = v6;
|
||||
base.Configuration.General.ConfigureTextAdvance = v7;
|
||||
Save();
|
||||
}
|
||||
bool v7 = base.Configuration.General.SkipLowPriorityDuties;
|
||||
if (ImGui.Checkbox("Unlock certain optional dungeons and raids (instead of waiting for completion)", ref v7))
|
||||
bool v8 = base.Configuration.General.SkipLowPriorityDuties;
|
||||
if (ImGui.Checkbox("Unlock certain optional dungeons and raids (instead of waiting for completion)", ref v8))
|
||||
{
|
||||
base.Configuration.General.SkipLowPriorityDuties = v7;
|
||||
base.Configuration.General.SkipLowPriorityDuties = v8;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
|
@ -243,10 +249,10 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
}
|
||||
}
|
||||
ImGui.Spacing();
|
||||
bool v8 = base.Configuration.General.AutoStepRefreshEnabled;
|
||||
if (ImGui.Checkbox("Automatically refresh quest steps when stuck", ref v8))
|
||||
bool v9 = base.Configuration.General.AutoStepRefreshEnabled;
|
||||
if (ImGui.Checkbox("Automatically refresh quest steps when stuck", ref v9))
|
||||
{
|
||||
base.Configuration.General.AutoStepRefreshEnabled = v8;
|
||||
base.Configuration.General.AutoStepRefreshEnabled = v9;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
|
@ -262,20 +268,20 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
ImGui.Text("This helps resume automated quest completion when interruptions occur.");
|
||||
}
|
||||
}
|
||||
using (ImRaii.Disabled(!v8))
|
||||
using (ImRaii.Disabled(!v9))
|
||||
{
|
||||
ImGui.Indent();
|
||||
int v9 = base.Configuration.General.AutoStepRefreshDelaySeconds;
|
||||
int v10 = base.Configuration.General.AutoStepRefreshDelaySeconds;
|
||||
ImGui.SetNextItemWidth(150f);
|
||||
if (ImGui.SliderInt("Refresh delay (seconds)", ref v9, 10, 180))
|
||||
if (ImGui.SliderInt("Refresh delay (seconds)", ref v10, 10, 180))
|
||||
{
|
||||
base.Configuration.General.AutoStepRefreshDelaySeconds = v9;
|
||||
base.Configuration.General.AutoStepRefreshDelaySeconds = v10;
|
||||
Save();
|
||||
}
|
||||
Vector4 col = new Vector4(0.7f, 0.7f, 0.7f, 1f);
|
||||
ImU8String text2 = new ImU8String(77, 1);
|
||||
text2.AppendLiteral("Quest steps will refresh automatically after ");
|
||||
text2.AppendFormatted(v9);
|
||||
text2.AppendFormatted(v10);
|
||||
text2.AppendLiteral(" seconds if no progress is made.");
|
||||
ImGui.TextColored(in col, text2);
|
||||
ImGui.Unindent();
|
||||
|
|
@ -283,16 +289,16 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
ImGui.Spacing();
|
||||
ImGui.Separator();
|
||||
ImGui.Text("Priority Quest Management");
|
||||
bool v10 = base.Configuration.General.ClearPriorityQuestsOnLogout;
|
||||
if (ImGui.Checkbox("Clear priority quests on character logout", ref v10))
|
||||
bool v11 = base.Configuration.General.ClearPriorityQuestsOnLogout;
|
||||
if (ImGui.Checkbox("Clear priority quests on character logout", ref v11))
|
||||
{
|
||||
base.Configuration.General.ClearPriorityQuestsOnLogout = v10;
|
||||
base.Configuration.General.ClearPriorityQuestsOnLogout = v11;
|
||||
Save();
|
||||
}
|
||||
bool v11 = base.Configuration.General.ClearPriorityQuestsOnCompletion;
|
||||
if (ImGui.Checkbox("Remove priority quests when completed", ref v11))
|
||||
bool v12 = base.Configuration.General.ClearPriorityQuestsOnCompletion;
|
||||
if (ImGui.Checkbox("Remove priority quests when completed", ref v12))
|
||||
{
|
||||
base.Configuration.General.ClearPriorityQuestsOnCompletion = v11;
|
||||
base.Configuration.General.ClearPriorityQuestsOnCompletion = v12;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
|
||||
private static readonly IReadOnlyList<PluginInfo> RequiredPlugins = new global::_003C_003Ez__ReadOnlyArray<PluginInfo>(new PluginInfo[3]
|
||||
{
|
||||
new PluginInfo("vnavmesh", "vnavmesh", "vnavmesh handles the navigation within a zone, moving\nyour character to the next quest-related objective.", new Uri("https://github.com/awgil/ffxiv_navmesh/"), new Uri("https://puni.sh/api/repository/veyn")),
|
||||
new PluginInfo("Lifestream", "Lifestream", "Used to travel to aethernet shards in cities.", new Uri("https://github.com/NightmareXIV/Lifestream"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
|
||||
new PluginInfo("TextAdvance", "TextAdvance", "Automatically accepts and turns in quests, skips cutscenes\nand dialogue.", new Uri("https://github.com/NightmareXIV/TextAdvance"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json"))
|
||||
new PluginInfo("TextAdvance", "TextAdvance", "Automatically accepts and turns in quests, skips cutscenes\nand dialogue.", new Uri("https://github.com/NightmareXIV/TextAdvance"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
|
||||
new PluginInfo("vnavmesh", "vnavmesh", "vnavmesh handles the navigation within a zone, moving\nyour character to the next quest-related objective.", new Uri("https://github.com/awgil/ffxiv_navmesh/"), new Uri("https://puni.sh/api/repository/veyn"))
|
||||
});
|
||||
|
||||
private static readonly ReadOnlyDictionary<Configuration.ECombatModule, PluginInfo> CombatPlugins = new Dictionary<Configuration.ECombatModule, PluginInfo>
|
||||
|
|
@ -36,13 +36,13 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
Questionable.Configuration.ECombatModule.BossMod,
|
||||
new PluginInfo("Boss Mod (VBM)", "BossMod", string.Empty, new Uri("https://github.com/awgil/ffxiv_bossmod"), new Uri("https://puni.sh/api/repository/veyn"))
|
||||
},
|
||||
{
|
||||
Questionable.Configuration.ECombatModule.WrathCombo,
|
||||
new PluginInfo("Wrath Combo", "WrathCombo", string.Empty, new Uri("https://github.com/PunishXIV/WrathCombo"), new Uri("https://puni.sh/api/plugins"))
|
||||
},
|
||||
{
|
||||
Questionable.Configuration.ECombatModule.RotationSolverReborn,
|
||||
new PluginInfo("Rotation Solver Reborn", "RotationSolver", string.Empty, new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"), new Uri("https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json"))
|
||||
},
|
||||
{
|
||||
Questionable.Configuration.ECombatModule.WrathCombo,
|
||||
new PluginInfo("Wrath Combo", "WrathCombo", string.Empty, new Uri("https://github.com/PunishXIV/WrathCombo"), new Uri("https://puni.sh/api/plugins"))
|
||||
}
|
||||
}.AsReadOnly();
|
||||
|
||||
|
|
@ -66,9 +66,10 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
_pluginInterface = pluginInterface;
|
||||
_uiUtils = uiUtils;
|
||||
_commandManager = commandManager;
|
||||
PluginInfo[] obj = new PluginInfo[5]
|
||||
PluginInfo[] obj = new PluginInfo[6]
|
||||
{
|
||||
new PluginInfo("Artisan", "Artisan", "Handles automatic crafting for quests that require\ncrafted items.", new Uri("https://github.com/PunishXIV/Artisan"), new Uri("https://puni.sh/api/plugins")),
|
||||
new PluginInfo("AutoDuty", "AutoDuty", "Automatically handles dungeon and trial completion during\nMain Scenario Quest progression.", new Uri("https://github.com/erdelf/AutoDuty"), new Uri("https://puni.sh/api/repository/erdelf")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
|
|
@ -82,7 +83,8 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
Span<PluginDetailInfo> span = CollectionsMarshal.AsSpan(list);
|
||||
int index = 0;
|
||||
span[index] = new PluginDetailInfo("'Sniper no sniping' enabled", "Automatically completes sniping tasks introduced in Stormblood", () => automatonIpc.IsAutoSnipeEnabled);
|
||||
obj[1] = new PluginInfo("CBT (formerly known as Automaton)", "Automaton", "Automaton is a collection of automation-related tweaks.", websiteUri, dalamudRepositoryUri, "/cbt", list);
|
||||
obj[2] = new PluginInfo("CBT (formerly known as Automaton)", "Automaton", "Automaton is a collection of automation-related tweaks.", websiteUri, dalamudRepositoryUri, "/cbt", list);
|
||||
obj[3] = new PluginInfo("NotificationMaster", "NotificationMaster", "Sends a configurable out-of-game notification if a quest\nrequires manual actions.", new Uri("https://github.com/NightmareXIV/NotificationMaster"), null);
|
||||
Uri websiteUri2 = new Uri("https://github.com/PunishXIV/PandorasBox");
|
||||
Uri dalamudRepositoryUri2 = new Uri("https://puni.sh/api/plugins");
|
||||
index = 1;
|
||||
|
|
@ -91,9 +93,8 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
span = CollectionsMarshal.AsSpan(list2);
|
||||
num = 0;
|
||||
span[num] = new PluginDetailInfo("'Auto Active Time Maneuver' enabled", "Automatically completes active time maneuvers in\nsingle player instances, trials and raids\"", () => pandorasBoxIpc.IsAutoActiveTimeManeuverEnabled);
|
||||
obj[2] = new PluginInfo("Pandora's Box", "PandorasBox", "Pandora's Box is a collection of tweaks.", websiteUri2, dalamudRepositoryUri2, "/pandora", list2);
|
||||
obj[3] = new PluginInfo("QuestMap", "QuestMap", "Displays quest objectives and markers on the map for\nbetter navigation and tracking.", new Uri("https://github.com/rreminy/QuestMap"), null);
|
||||
obj[4] = new PluginInfo("NotificationMaster", "NotificationMaster", "Sends a configurable out-of-game notification if a quest\nrequires manual actions.", new Uri("https://github.com/NightmareXIV/NotificationMaster"), null);
|
||||
obj[4] = new PluginInfo("Pandora's Box", "PandorasBox", "Pandora's Box is a collection of tweaks.", websiteUri2, dalamudRepositoryUri2, "/pandora", list2);
|
||||
obj[5] = new PluginInfo("QuestMap", "QuestMap", "Displays quest objectives and markers on the map for\nbetter navigation and tracking.", new Uri("https://github.com/rreminy/QuestMap"), null);
|
||||
_recommendedPlugins = new global::_003C_003Ez__ReadOnlyArray<PluginInfo>(obj);
|
||||
}
|
||||
|
||||
|
|
@ -147,8 +148,8 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
_pluginInterface.SavePluginConfig(_configuration);
|
||||
}
|
||||
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.BossMod, checklistPadding);
|
||||
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.WrathCombo, checklistPadding);
|
||||
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.RotationSolverReborn, checklistPadding);
|
||||
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.WrathCombo, checklistPadding);
|
||||
}
|
||||
}
|
||||
ImGui.Spacing();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Dalamud.Interface.Colors;
|
|||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using LLib.GameData;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
|
@ -84,12 +84,9 @@ internal sealed class GatheringJournalComponent
|
|||
|
||||
private string _searchText = string.Empty;
|
||||
|
||||
[Signature("48 89 5C 24 ?? 57 48 83 EC 20 8B D9 8B F9")]
|
||||
private GetIsGatheringItemGatheredDelegate _getIsGatheringItemGathered;
|
||||
|
||||
private bool IsGatheringItemGathered(uint item)
|
||||
private static bool IsGatheringItemGathered(uint item)
|
||||
{
|
||||
return _getIsGatheringItemGathered((ushort)item) != 0;
|
||||
return QuestManager.IsGatheringItemGathered((ushort)item);
|
||||
}
|
||||
|
||||
public GatheringJournalComponent(IDataManager dataManager, IDalamudPluginInterface pluginInterface, UiUtils uiUtils, IGameInteropProvider gameInteropProvider, GatheringPointRegistry gatheringPointRegistry)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ internal sealed class Configuration : IPluginConfiguration
|
|||
|
||||
public bool AutoStepRefreshEnabled { get; set; } = true;
|
||||
|
||||
public int AutoStepRefreshDelaySeconds { get; set; } = 10;
|
||||
public int AutoStepRefreshDelaySeconds { get; set; } = 60;
|
||||
|
||||
public bool HideSeasonalEventsFromJournalProgress { get; set; }
|
||||
|
||||
|
|
@ -43,6 +43,8 @@ internal sealed class Configuration : IPluginConfiguration
|
|||
public bool ClearPriorityQuestsOnCompletion { get; set; }
|
||||
|
||||
public bool ShowChangelogOnUpdate { get; set; } = true;
|
||||
|
||||
public bool StopOnPlayerInput { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class StopConfiguration
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue