Add quest priority window

This commit is contained in:
Liza 2024-08-25 01:30:42 +02:00
parent 581976b06b
commit b4a649189a
Signed by: liza
GPG Key ID: 7199F8D727D55F67
6 changed files with 259 additions and 5 deletions

View File

@ -138,6 +138,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
/// </summary> /// </summary>
public QuestProgress? PendingQuest => _pendingQuest; public QuestProgress? PendingQuest => _pendingQuest;
public List<Quest> ManualPriorityQuests { get; } = [];
public string? DebugState { get; private set; } public string? DebugState { get; private set; }
public void Reload() public void Reload()
@ -291,7 +293,13 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
} }
else else
{ {
(ElementId? currentQuestId, currentSequence) = _questFunctions.GetCurrentQuest(); (ElementId? currentQuestId, currentSequence) =
ManualPriorityQuests
.Where(x => _questFunctions.IsReadyToAcceptQuest(x.Id) || _questFunctions.IsQuestAccepted(x.Id))
.Select(x =>
((ElementId?, byte)?)(x.Id, _questFunctions.GetQuestProgressInfo(x.Id)?.Sequence ?? 0))
.FirstOrDefault() ??
_questFunctions.GetCurrentQuest();
if (currentQuestId == null || currentQuestId.Value == 0) if (currentQuestId == null || currentQuestId.Value == 0)
{ {
if (_startedQuest != null) if (_startedQuest != null)

View File

@ -36,6 +36,7 @@ internal sealed class DalamudInitializer : IDisposable
QuestSelectionWindow questSelectionWindow, QuestSelectionWindow questSelectionWindow,
QuestValidationWindow questValidationWindow, QuestValidationWindow questValidationWindow,
JournalProgressWindow journalProgressWindow, JournalProgressWindow journalProgressWindow,
PriorityWindow priorityWindow,
IToastGui toastGui, IToastGui toastGui,
ILogger<DalamudInitializer> logger) ILogger<DalamudInitializer> logger)
{ {
@ -55,6 +56,7 @@ internal sealed class DalamudInitializer : IDisposable
_windowSystem.AddWindow(questSelectionWindow); _windowSystem.AddWindow(questSelectionWindow);
_windowSystem.AddWindow(questValidationWindow); _windowSystem.AddWindow(questValidationWindow);
_windowSystem.AddWindow(journalProgressWindow); _windowSystem.AddWindow(journalProgressWindow);
_windowSystem.AddWindow(priorityWindow);
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw; _pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle; _pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle;

View File

@ -449,6 +449,18 @@ internal sealed unsafe class QuestFunctions
} }
public bool IsQuestLocked(QuestId questId, ElementId? extraCompletedQuest = null) public bool IsQuestLocked(QuestId questId, ElementId? extraCompletedQuest = null)
{
if (IsQuestUnobtainable(questId, extraCompletedQuest))
return true;
var questInfo = (QuestInfo)_questData.GetQuestInfo(questId);
if (questInfo.GrandCompany != GrandCompany.None && questInfo.GrandCompany != GetGrandCompany())
return true;
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
}
public bool IsQuestUnobtainable(QuestId questId, ElementId? extraCompletedQuest = null)
{ {
var questInfo = (QuestInfo)_questData.GetQuestInfo(questId); var questInfo = (QuestInfo)_questData.GetQuestInfo(questId);
if (questInfo.QuestLocks.Count > 0) if (questInfo.QuestLocks.Count > 0)
@ -460,13 +472,10 @@ internal sealed unsafe class QuestFunctions
return true; return true;
} }
if (questInfo.GrandCompany != GrandCompany.None && questInfo.GrandCompany != GetGrandCompany())
return true;
if (_questData.GetLockedClassQuests().Contains(questId)) if (_questData.GetLockedClassQuests().Contains(questId))
return true; return true;
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo); return false;
} }
public bool IsQuestLocked(LeveId leveId) public bool IsQuestLocked(LeveId leveId)

View File

@ -208,6 +208,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<QuestSelectionWindow>(); serviceCollection.AddSingleton<QuestSelectionWindow>();
serviceCollection.AddSingleton<QuestValidationWindow>(); serviceCollection.AddSingleton<QuestValidationWindow>();
serviceCollection.AddSingleton<JournalProgressWindow>(); serviceCollection.AddSingleton<JournalProgressWindow>();
serviceCollection.AddSingleton<PriorityWindow>();
} }
private static void AddQuestValidators(ServiceCollection serviceCollection) private static void AddQuestValidators(ServiceCollection serviceCollection)

View File

@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Plugin;
using ImGuiNET;
using LLib.ImGui;
using Questionable.Controller;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;
internal sealed class PriorityWindow : LWindow
{
private readonly QuestController _questController;
private readonly QuestRegistry _questRegistry;
private readonly QuestFunctions _questFunctions;
private readonly QuestTooltipComponent _questTooltipComponent;
private readonly UiUtils _uiUtils;
private readonly IDalamudPluginInterface _pluginInterface;
private string _searchString = string.Empty;
private ElementId? _draggedItem;
public PriorityWindow(QuestController questController, QuestRegistry questRegistry, QuestFunctions questFunctions,
QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IDalamudPluginInterface pluginInterface)
: base("Quest Priority###QuestionableQuestPriority")
{
_questController = questController;
_questRegistry = questRegistry;
_questFunctions = questFunctions;
_questTooltipComponent = questTooltipComponent;
_uiUtils = uiUtils;
_pluginInterface = pluginInterface;
Size = new Vector2(400, 400);
SizeCondition = ImGuiCond.Once;
SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(400, 400),
MaximumSize = new Vector2(400, 999)
};
}
public override void Draw()
{
ImGui.Text("Quests to do first:");
DrawQuestFilter();
DrawQuestList();
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
ImGui.TextWrapped(
"If you have an active MSQ quest, Questionable will generally try to do:");
ImGui.BulletText("'Priority' quests: class quests, ARR primals, ARR raids");
ImGui.BulletText(
"Supported quests in your 'To-Do list'\n(quests from your Journal that are always on-screen)");
ImGui.BulletText("MSQ quest (if available, unless it is marked as 'ignored'\nin your Journal)");
ImGui.TextWrapped(
"If you don't have any active MSQ quest, it will always try to pick up the next quest in the MSQ first.");
}
private void DrawQuestFilter()
{
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.BeginCombo($"##QuestSelection", "Add Quest...", ImGuiComboFlags.HeightLarge))
{
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
bool addFirst = ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256,
ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.EnterReturnsTrue);
IEnumerable<Quest> foundQuests;
if (!string.IsNullOrEmpty(_searchString))
{
foundQuests = _questRegistry.AllQuests
.Where(x => x.Info.Name.Contains(_searchString, StringComparison.CurrentCultureIgnoreCase))
.Where(x => x.Id is not QuestId questId || !_questFunctions.IsQuestUnobtainable(questId));
}
else
{
foundQuests = _questRegistry.AllQuests.Where(x => _questFunctions.IsQuestAccepted(x.Id));
}
foreach (var quest in foundQuests)
{
if (quest.Info.IsMainScenarioQuest || _questController.ManualPriorityQuests.Contains(quest))
continue;
bool addThis = ImGui.Selectable(quest.Info.Name);
if (addThis || addFirst)
{
_questController.ManualPriorityQuests.Add(quest);
if (addFirst)
{
ImGui.CloseCurrentPopup();
addFirst = false;
}
}
}
ImGui.EndCombo();
}
ImGui.Spacing();
}
private void DrawQuestList()
{
List<Quest> priorityQuests = _questController.ManualPriorityQuests;
Quest? itemToRemove = null;
Quest? itemToAdd = null;
int indexToAdd = 0;
float width = ImGui.GetContentRegionAvail().X;
List<(Vector2 TopLeft, Vector2 BottomRight)> itemPositions = [];
for (int i = 0; i < priorityQuests.Count; ++i)
{
Vector2 topLeft = ImGui.GetCursorScreenPos() +
new Vector2(0, -ImGui.GetStyle().ItemSpacing.Y / 2);
var quest = priorityQuests[i];
ImGui.PushID($"Quest{quest.Id}");
var style = _uiUtils.GetQuestStyle(quest.Id);
bool hovered;
using (var _ = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
ImGui.AlignTextToFramePadding();
ImGui.TextColored(style.Color, style.Icon.ToIconString());
hovered = ImGui.IsItemHovered();
}
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.Text(quest.Info.Name);
hovered |= ImGui.IsItemHovered();
if (hovered)
_questTooltipComponent.Draw(quest.Info);
if (priorityQuests.Count > 1)
{
ImGui.PushFont(UiBuilder.IconFont);
ImGui.SameLine(ImGui.GetContentRegionAvail().X +
ImGui.GetStyle().WindowPadding.X -
ImGui.CalcTextSize(FontAwesomeIcon.ArrowsUpDown.ToIconString()).X -
ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X -
ImGui.GetStyle().FramePadding.X * 4 -
ImGui.GetStyle().ItemSpacing.X);
ImGui.PopFont();
if (_draggedItem == quest.Id)
{
ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown,
ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.ButtonActive)));
}
else
ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown);
if (_draggedItem == null && ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
_draggedItem = quest.Id;
ImGui.SameLine();
}
else
{
ImGui.PushFont(UiBuilder.IconFont);
ImGui.SameLine(ImGui.GetContentRegionAvail().X +
ImGui.GetStyle().WindowPadding.X -
ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X -
ImGui.GetStyle().FramePadding.X * 2);
ImGui.PopFont();
}
if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times))
itemToRemove = quest;
ImGui.PopID();
Vector2 bottomRight = new Vector2(topLeft.X + width,
ImGui.GetCursorScreenPos().Y - ImGui.GetStyle().ItemSpacing.Y + 2);
itemPositions.Add((topLeft, bottomRight));
}
if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left))
_draggedItem = null;
else if (_draggedItem != null)
{
var draggedItem = priorityQuests.Single(x => x.Id == _draggedItem);
int oldIndex = priorityQuests.IndexOf(draggedItem);
var (topLeft, bottomRight) = itemPositions[oldIndex];
ImGui.GetWindowDrawList().AddRect(topLeft, bottomRight, ImGui.GetColorU32(ImGuiColors.DalamudGrey), 3f,
ImDrawFlags.RoundCornersAll);
int newIndex = itemPositions.FindIndex(x => ImGui.IsMouseHoveringRect(x.TopLeft, x.BottomRight, true));
if (newIndex >= 0 && oldIndex != newIndex)
{
itemToAdd = priorityQuests.Single(x => x.Id == _draggedItem);
indexToAdd = newIndex;
}
}
if (itemToRemove != null)
{
priorityQuests.Remove(itemToRemove);
}
if (itemToAdd != null)
{
priorityQuests.Remove(itemToAdd);
priorityQuests.Insert(indexToAdd, itemToAdd);
}
}
}

View File

@ -31,6 +31,7 @@ internal sealed partial class ActiveQuestComponent
private readonly ICommandManager _commandManager; private readonly ICommandManager _commandManager;
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly QuestRegistry _questRegistry; private readonly QuestRegistry _questRegistry;
private readonly PriorityWindow _priorityWindow;
private readonly IChatGui _chatGui; private readonly IChatGui _chatGui;
public ActiveQuestComponent( public ActiveQuestComponent(
@ -42,6 +43,7 @@ internal sealed partial class ActiveQuestComponent
ICommandManager commandManager, ICommandManager commandManager,
Configuration configuration, Configuration configuration,
QuestRegistry questRegistry, QuestRegistry questRegistry,
PriorityWindow priorityWindow,
IChatGui chatGui) IChatGui chatGui)
{ {
_questController = questController; _questController = questController;
@ -52,6 +54,7 @@ internal sealed partial class ActiveQuestComponent
_commandManager = commandManager; _commandManager = commandManager;
_configuration = configuration; _configuration = configuration;
_questRegistry = questRegistry; _questRegistry = questRegistry;
_priorityWindow = priorityWindow;
_chatGui = chatGui; _chatGui = chatGui;
} }
@ -111,6 +114,10 @@ internal sealed partial class ActiveQuestComponent
_questController.Stop("Manual (no active quest)"); _questController.Stop("Manual (no active quest)");
_gatheringController.Stop("Manual (no active quest)"); _gatheringController.Stop("Manual (no active quest)");
} }
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.SortAmountDown))
_priorityWindow.Toggle();
} }
} }
@ -293,6 +300,10 @@ internal sealed partial class ActiveQuestComponent
ImGui.PopStyleColor(); ImGui.PopStyleColor();
ImGui.EndDisabled(); ImGui.EndDisabled();
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.SortAmountDown))
_priorityWindow.Toggle();
if (_commandManager.Commands.TryGetValue("/questinfo", out var commandInfo)) if (_commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
{ {
ImGui.SameLine(); ImGui.SameLine();