muffin v7.4.12
This commit is contained in:
parent
e3e5a401c3
commit
0f9f445830
38 changed files with 13646 additions and 10442 deletions
|
|
@ -34,6 +34,10 @@ internal sealed class CommandHandler : IDisposable
|
|||
|
||||
private readonly PriorityWindow _priorityWindow;
|
||||
|
||||
private readonly FateController _fateController;
|
||||
|
||||
private readonly FateSelectionWindow _fateSelectionWindow;
|
||||
|
||||
private readonly ITargetManager _targetManager;
|
||||
|
||||
private readonly IClientState _clientState;
|
||||
|
|
@ -42,12 +46,13 @@ internal sealed class CommandHandler : IDisposable
|
|||
|
||||
private readonly ChangelogWindow _changelogWindow;
|
||||
|
||||
public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, AetheryteFunctions aetheryteFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration, ChangelogWindow changelogWindow)
|
||||
public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, FateController fateController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, FateSelectionWindow fateSelectionWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, AetheryteFunctions aetheryteFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration, ChangelogWindow changelogWindow)
|
||||
{
|
||||
_commandManager = commandManager;
|
||||
_chatGui = chatGui;
|
||||
_questController = questController;
|
||||
_movementController = movementController;
|
||||
_fateController = fateController;
|
||||
_configWindow = configWindow;
|
||||
_oneTimeSetupWindow = oneTimeSetupWindow;
|
||||
_questWindow = questWindow;
|
||||
|
|
@ -55,6 +60,7 @@ internal sealed class CommandHandler : IDisposable
|
|||
_questSequenceWindow = questSequenceWindow;
|
||||
_journalProgressWindow = journalProgressWindow;
|
||||
_priorityWindow = priorityWindow;
|
||||
_fateSelectionWindow = fateSelectionWindow;
|
||||
_targetManager = targetManager;
|
||||
_clientState = clientState;
|
||||
_configuration = configuration;
|
||||
|
|
@ -93,6 +99,7 @@ internal sealed class CommandHandler : IDisposable
|
|||
_chatGui.Print("/qst start - starts doing quests", "Questionable", 576);
|
||||
_chatGui.Print("/qst stop - stops doing quests", "Questionable", 576);
|
||||
_chatGui.Print("/qst reload - reload all quest data", "Questionable", 576);
|
||||
_chatGui.Print("/qst fate - toggles the FATE farming window", "Questionable", 576);
|
||||
break;
|
||||
case "ha":
|
||||
case "help-all":
|
||||
|
|
@ -109,6 +116,7 @@ internal sealed class CommandHandler : IDisposable
|
|||
_chatGui.Print("/qst zone - shows all quests starting in the current zone (only includes quests with a known quest path, and currently visible unaccepted quests)", "Questionable", 576);
|
||||
_chatGui.Print("/qst journal - toggles the Journal Progress window", "Questionable", 576);
|
||||
_chatGui.Print("/qst priority - toggles the Priority window", "Questionable", 576);
|
||||
_chatGui.Print("/qst fate - toggles the FATE farming window", "Questionable", 576);
|
||||
_chatGui.Print("/qst handle-interrupt - makes Questionable handle queued interrupts immediately (useful if you manually start combat)", "Questionable", 576);
|
||||
break;
|
||||
case "c":
|
||||
|
|
@ -126,6 +134,7 @@ internal sealed class CommandHandler : IDisposable
|
|||
case "stop":
|
||||
_movementController.Stop();
|
||||
_questController.Stop("Stop command");
|
||||
_fateController.Stop("Stop command");
|
||||
break;
|
||||
case "reload":
|
||||
_questWindow.Reload();
|
||||
|
|
@ -145,6 +154,10 @@ internal sealed class CommandHandler : IDisposable
|
|||
case "priority":
|
||||
_priorityWindow.ToggleOrUncollapse();
|
||||
break;
|
||||
case "f":
|
||||
case "fate":
|
||||
_fateSelectionWindow.ToggleOrUncollapse();
|
||||
break;
|
||||
case "handle-interrupt":
|
||||
_questController.InterruptQueueWithCombat();
|
||||
break;
|
||||
|
|
|
|||
157
Questionable/Questionable.Controller/FateController.cs
Normal file
157
Questionable/Questionable.Controller/FateController.cs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps;
|
||||
using Questionable.Controller.Steps.Common;
|
||||
using Questionable.Controller.Steps.Interactions;
|
||||
using Questionable.Controller.Steps.Movement;
|
||||
using Questionable.Controller.Steps.Shared;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller;
|
||||
|
||||
internal sealed class FateController : MiniTaskController<FateController>
|
||||
{
|
||||
private readonly MovementController _movementController;
|
||||
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
|
||||
private readonly IClientState _clientState;
|
||||
|
||||
private readonly ILogger<FateController> _logger;
|
||||
|
||||
private FateDefinition? _currentFate;
|
||||
|
||||
private int _completedCycles;
|
||||
|
||||
private int? _cycleLimit;
|
||||
|
||||
private DateTime _startTime;
|
||||
|
||||
public bool IsRunning => _currentFate != null;
|
||||
|
||||
public FateDefinition? CurrentFate => _currentFate;
|
||||
|
||||
public int CompletedCycles => _completedCycles;
|
||||
|
||||
public int? CycleLimit => _cycleLimit;
|
||||
|
||||
public TimeSpan ElapsedTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsRunning)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
return DateTime.UtcNow - _startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public FateController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, ILogger<FateController> logger, MovementController movementController, GameFunctions gameFunctions, IClientState clientState)
|
||||
: base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
|
||||
{
|
||||
_movementController = movementController;
|
||||
_gameFunctions = gameFunctions;
|
||||
_clientState = clientState;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Start(FateDefinition fate, int? cycleLimit = null)
|
||||
{
|
||||
_currentFate = fate;
|
||||
_completedCycles = 0;
|
||||
_cycleLimit = cycleLimit;
|
||||
_startTime = DateTime.UtcNow;
|
||||
_logger.LogInformation("Starting FATE farming: {FateName} (limit: {Limit})", fate.Name, cycleLimit?.ToString(CultureInfo.InvariantCulture) ?? "unlimited");
|
||||
EnqueueFateCycle();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_currentFate == null || _movementController.IsPathfinding || _movementController.IsPathRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_taskQueue.AllTasksComplete)
|
||||
{
|
||||
_completedCycles++;
|
||||
if (_cycleLimit.HasValue && _completedCycles >= _cycleLimit.Value)
|
||||
{
|
||||
_logger.LogInformation("FATE cycle limit reached ({Cycles}/{Limit})", _completedCycles, _cycleLimit.Value);
|
||||
Stop("Cycle limit reached");
|
||||
return;
|
||||
}
|
||||
EnqueueFateCycle();
|
||||
}
|
||||
UpdateCurrentTask();
|
||||
}
|
||||
|
||||
private void EnqueueFateCycle()
|
||||
{
|
||||
if (_currentFate != null)
|
||||
{
|
||||
_logger.LogInformation("Enqueuing FATE cycle for {FateName}", _currentFate.Name);
|
||||
if (_clientState.TerritoryType != _currentFate.TerritoryId)
|
||||
{
|
||||
_taskQueue.Enqueue(new AetheryteShortcut.Task(null, null, _currentFate.Aetheryte, _currentFate.TerritoryId));
|
||||
}
|
||||
if (_currentFate.RequiredStatusId.HasValue && _currentFate.TransformNpcDataId.HasValue && _currentFate.TransformNpcPosition.HasValue && !_gameFunctions.HasStatus(_currentFate.RequiredStatusId.Value))
|
||||
{
|
||||
_logger.LogInformation("Player missing required status {StatusId}, enqueuing transform", _currentFate.RequiredStatusId.Value);
|
||||
_taskQueue.Enqueue(new MoveTask(_currentFate.TerritoryId, _currentFate.TransformNpcPosition.Value, null, 3f));
|
||||
_taskQueue.Enqueue(new Mount.UnmountTask());
|
||||
_taskQueue.Enqueue(new Interact.Task(_currentFate.TransformNpcDataId.Value, null, EInteractionType.Interact, SkipMarkerCheck: true));
|
||||
_taskQueue.Enqueue(new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(2L)));
|
||||
}
|
||||
_taskQueue.Enqueue(new MoveTask(_currentFate.TerritoryId, _currentFate.Position, null, 5f));
|
||||
_taskQueue.Enqueue(new Mount.UnmountTask());
|
||||
_taskQueue.Enqueue(new FateFarming.WaitForFateTargets(_currentFate.Targets));
|
||||
_taskQueue.Enqueue(new FateFarming.SyncFateLevel());
|
||||
_taskQueue.Enqueue(new FateFarming.FateActionLoop(_currentFate.Targets));
|
||||
_taskQueue.Enqueue(new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(3L)));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop(string label)
|
||||
{
|
||||
if (_currentFate != null)
|
||||
{
|
||||
_logger.LogInformation("Stopping FATE farming: {Label} (completed {Cycles} cycles)", label, _completedCycles);
|
||||
_currentFate = null;
|
||||
_completedCycles = 0;
|
||||
_cycleLimit = null;
|
||||
_taskQueue.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public override IList<string> GetRemainingTaskNames()
|
||||
{
|
||||
ITask task = _taskQueue.CurrentTaskExecutor?.CurrentTask;
|
||||
if (task != null)
|
||||
{
|
||||
string text = task.ToString() ?? "?";
|
||||
IList<string> remainingTaskNames = base.GetRemainingTaskNames();
|
||||
int num = 1 + remainingTaskNames.Count;
|
||||
List<string> list = new List<string>(num);
|
||||
CollectionsMarshal.SetCount(list, num);
|
||||
Span<string> span = CollectionsMarshal.AsSpan(list);
|
||||
int num2 = 0;
|
||||
span[num2] = text;
|
||||
num2++;
|
||||
{
|
||||
foreach (string item in remainingTaskNames)
|
||||
{
|
||||
span[num2] = item;
|
||||
num2++;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
return base.GetRemainingTaskNames();
|
||||
}
|
||||
}
|
||||
148
Questionable/Questionable.Controller/FateDefinitionRegistry.cs
Normal file
148
Questionable/Questionable.Controller/FateDefinitionRegistry.cs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#define RELEASE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Dalamud.Plugin;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.FatePaths;
|
||||
using Questionable.Model.Questing;
|
||||
using Questionable.Windows.QuestComponents;
|
||||
|
||||
namespace Questionable.Controller;
|
||||
|
||||
internal sealed class FateDefinitionRegistry
|
||||
{
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
|
||||
private readonly ILogger<FateDefinitionRegistry> _logger;
|
||||
|
||||
private readonly Dictionary<ushort, FateDefinition> _definitions = new Dictionary<ushort, FateDefinition>();
|
||||
|
||||
public IReadOnlyDictionary<ushort, FateDefinition> Definitions => _definitions;
|
||||
|
||||
public FateDefinitionRegistry(IDalamudPluginInterface pluginInterface, ILogger<FateDefinitionRegistry> logger)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_logger = logger;
|
||||
Reload();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
_definitions.Clear();
|
||||
LoadFromAssembly();
|
||||
try
|
||||
{
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "FateDefinitions")));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Failed to load FATE definitions from user directory (some may have been successfully loaded)");
|
||||
}
|
||||
RemoveExpiredDefinitions();
|
||||
_logger.LogInformation("Loaded {Count} FATE definitions in total", _definitions.Count);
|
||||
}
|
||||
|
||||
[Conditional("RELEASE")]
|
||||
private void LoadFromAssembly()
|
||||
{
|
||||
_logger.LogInformation("Loading FATE definitions from assembly");
|
||||
IReadOnlyDictionary<ushort, FateDefinition> definitions = AssemblyFateDefinitionLoader.GetDefinitions();
|
||||
_logger.LogInformation("AssemblyFateDefinitionLoader returned {Count} definitions", definitions.Count);
|
||||
foreach (var (key, value) in definitions)
|
||||
{
|
||||
_definitions[key] = value;
|
||||
}
|
||||
_logger.LogInformation("Loaded {Count} FATE definitions from assembly", _definitions.Count);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void LoadFromProjectDirectory()
|
||||
{
|
||||
DirectoryInfo directoryInfo = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent;
|
||||
if (directoryInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DirectoryInfo directoryInfo2 = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "FatePaths"));
|
||||
if (directoryInfo2.Exists)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadFromDirectory(directoryInfo2);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_definitions.Clear();
|
||||
_logger.LogError(exception, "Failed to load FATE definitions from project directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadFromDirectory(DirectoryInfo directory)
|
||||
{
|
||||
if (!directory.Exists)
|
||||
{
|
||||
_logger.LogInformation("Not loading FATE definitions from {DirectoryName} (doesn't exist)", directory);
|
||||
return;
|
||||
}
|
||||
FileInfo[] files = directory.GetFiles("*.json");
|
||||
foreach (FileInfo fileInfo in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
ushort? num = ExtractIdFromName(fileInfo.Name);
|
||||
if (!num.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
using FileStream utf8Json = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
|
||||
FateDefinition fateDefinition = JsonSerializer.Deserialize<FateDefinition>(utf8Json);
|
||||
if (fateDefinition != null)
|
||||
{
|
||||
_definitions[num.Value] = fateDefinition;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Unable to load FATE definition file {FileName}", fileInfo.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveExpiredDefinitions()
|
||||
{
|
||||
foreach (ushort item in (from kvp in _definitions.Where<KeyValuePair<ushort, FateDefinition>>(delegate(KeyValuePair<ushort, FateDefinition> kvp)
|
||||
{
|
||||
DateTime? eventExpiry = kvp.Value.EventExpiry;
|
||||
if (eventExpiry.HasValue)
|
||||
{
|
||||
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
|
||||
return EventInfoComponent.NormalizeExpiry(valueOrDefault) < DateTime.UtcNow;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
select kvp.Key).ToList())
|
||||
{
|
||||
_logger.LogInformation("Removing expired FATE definition {Id} '{Name}'", item, _definitions[item].Name);
|
||||
_definitions.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
private static ushort? ExtractIdFromName(string fileName)
|
||||
{
|
||||
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
|
||||
if (!fileNameWithoutExtension.Contains('_', StringComparison.Ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (ushort.TryParse(fileNameWithoutExtension.Split('_', 2)[0], out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +99,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
|
||||
private readonly GatheringController _gatheringController;
|
||||
|
||||
private readonly FateController _fateController;
|
||||
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
|
||||
private readonly JournalData _journalData;
|
||||
|
|
@ -244,7 +246,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
|
||||
public event AutomationTypeChangedEventHandler? AutomationTypeChanged;
|
||||
|
||||
public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger<QuestController> logger, QuestRegistry questRegistry, JournalData journalData, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent, AutoDutyIpc autoDutyIpc, IDalamudPluginInterface pluginInterface)
|
||||
public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, FateController fateController, ILogger<QuestController> logger, QuestRegistry questRegistry, JournalData journalData, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent, AutoDutyIpc autoDutyIpc, IDalamudPluginInterface pluginInterface)
|
||||
: base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
|
||||
{
|
||||
_clientState = clientState;
|
||||
|
|
@ -254,6 +256,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
_movementController = movementController;
|
||||
_combatController = combatController;
|
||||
_gatheringController = gatheringController;
|
||||
_fateController = fateController;
|
||||
_questRegistry = questRegistry;
|
||||
_journalData = journalData;
|
||||
_keyState = keyState;
|
||||
|
|
@ -918,6 +921,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
_taskQueue.Reset();
|
||||
_combatController.Stop("ClearTasksInternal");
|
||||
_gatheringController.Stop("ClearTasksInternal");
|
||||
_fateController.Stop("ClearTasksInternal");
|
||||
}
|
||||
|
||||
public override void Stop(string label)
|
||||
|
|
@ -944,6 +948,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
_movementController.Stop();
|
||||
_combatController.Stop(label);
|
||||
_gatheringController.Stop(label);
|
||||
_fateController.Stop(label);
|
||||
}
|
||||
|
||||
private void CheckNextTasks(string label)
|
||||
|
|
@ -1242,6 +1247,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||
_movementController.Stop();
|
||||
_combatController.Stop("Execute next step");
|
||||
_gatheringController.Stop("Execute next step");
|
||||
_fateController.Stop("Execute next step");
|
||||
try
|
||||
{
|
||||
foreach (ITask item5 in _taskCreator.CreateTasks(CurrentQuest.Quest, CurrentQuest.Sequence, questSequence, step))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue