forked from liza/Questionable
Add Statistics/Progress window
This commit is contained in:
parent
ba0edc562b
commit
0d2ce03164
@ -44,6 +44,8 @@ internal sealed class QuestRegistry
|
||||
public int ValidationIssueCount => _questValidator.IssueCount;
|
||||
public int ValidationErrorCount => _questValidator.ErrorCount;
|
||||
|
||||
public event EventHandler? Reloaded;
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
_questValidator.Reset();
|
||||
@ -63,6 +65,7 @@ internal sealed class QuestRegistry
|
||||
}
|
||||
|
||||
ValidateQuests();
|
||||
Reloaded?.Invoke(this, EventArgs.Empty);
|
||||
_logger.LogInformation("Loaded {Count} quests in total", _quests.Count);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,8 @@ internal sealed class DalamudInitializer : IDisposable
|
||||
DebugOverlay debugOverlay,
|
||||
ConfigWindow configWindow,
|
||||
QuestSelectionWindow questSelectionWindow,
|
||||
QuestValidationWindow questValidationWindow)
|
||||
QuestValidationWindow questValidationWindow,
|
||||
JournalProgressWindow journalProgressWindow)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_framework = framework;
|
||||
@ -46,6 +47,7 @@ internal sealed class DalamudInitializer : IDisposable
|
||||
_windowSystem.AddWindow(debugOverlay);
|
||||
_windowSystem.AddWindow(questSelectionWindow);
|
||||
_windowSystem.AddWindow(questValidationWindow);
|
||||
_windowSystem.AddWindow(journalProgressWindow);
|
||||
|
||||
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||
_pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle;
|
||||
|
96
Questionable/Data/JournalData.cs
Normal file
96
Questionable/Data/JournalData.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Questionable.Model;
|
||||
|
||||
namespace Questionable.Data;
|
||||
|
||||
internal sealed class JournalData
|
||||
{
|
||||
public JournalData(IDataManager dataManager, QuestData questData)
|
||||
{
|
||||
var genres = dataManager.GetExcelSheet<JournalGenre>()!
|
||||
.Where(x => x.RowId > 0 && x.Icon > 0)
|
||||
.Select(x => new Genre(x, questData.GetAllByJournalGenre(x.RowId)))
|
||||
.ToList();
|
||||
|
||||
var limsaStart = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(1)!;
|
||||
var gridaniaStart = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(2)!;
|
||||
var uldahStart = dataManager.GetExcelSheet<QuestRedo>()!.GetRow(3)!;
|
||||
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
|
||||
new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
|
||||
.Where(x => x != 0)
|
||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF))).ToList());
|
||||
var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1,
|
||||
new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
|
||||
.Where(x => x != 0)
|
||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF))).ToList());
|
||||
var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1,
|
||||
new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
|
||||
.Where(x => x != 0)
|
||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF)))
|
||||
.ToList());
|
||||
genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]);
|
||||
genres.Single(x => x.Id == 1)
|
||||
.Quests
|
||||
.RemoveAll(x =>
|
||||
genreLimsa.Quests.Contains(x) || genreGridania.Quests.Contains(x) || genreUldah.Quests.Contains(x));
|
||||
|
||||
Genres = genres.AsReadOnly();
|
||||
Categories = dataManager.GetExcelSheet<JournalCategory>()!
|
||||
.Where(x => x.RowId > 0)
|
||||
.Select(x => new Category(x, Genres.Where(y => y.CategoryId == x.RowId).ToList()))
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
Sections = dataManager.GetExcelSheet<JournalSection>()!
|
||||
.Select(x => new Section(x, Categories.Where(y => y.SectionId == x.RowId).ToList()))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IReadOnlyList<Genre> Genres { get; }
|
||||
public IReadOnlyList<Category> Categories { get; }
|
||||
public List<Section> Sections { get; set; }
|
||||
|
||||
internal sealed class Genre
|
||||
{
|
||||
public Genre(JournalGenre journalGenre, List<QuestInfo> quests)
|
||||
{
|
||||
Id = journalGenre.RowId;
|
||||
Name = journalGenre.Name.ToString();
|
||||
CategoryId = journalGenre.JournalCategory.Row;
|
||||
Quests = quests;
|
||||
}
|
||||
|
||||
public Genre(uint id, string name, uint categoryId, List<QuestInfo> quests)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
CategoryId = categoryId;
|
||||
Quests = quests;
|
||||
}
|
||||
|
||||
public uint Id { get; }
|
||||
public string Name { get; }
|
||||
public uint CategoryId { get; }
|
||||
public List<QuestInfo> Quests { get; }
|
||||
public int QuestCount => Quests.Count;
|
||||
}
|
||||
|
||||
internal sealed class Category(JournalCategory journalCategory, IReadOnlyList<Genre> genres)
|
||||
{
|
||||
public uint Id { get; } = journalCategory.RowId;
|
||||
public string Name { get; } = journalCategory.Name.ToString();
|
||||
public uint SectionId { get; } = journalCategory.JournalSection.Row;
|
||||
public IReadOnlyList<Genre> Genres { get; } = genres;
|
||||
public int QuestCount => Genres.Sum(x => x.QuestCount);
|
||||
}
|
||||
|
||||
internal sealed class Section(JournalSection journalSection, IReadOnlyList<Category> categories)
|
||||
{
|
||||
public uint Id { get; } = journalSection.RowId;
|
||||
public string Name { get; } = journalSection.Name.ToString();
|
||||
public IReadOnlyList<Category> Categories { get; } = categories;
|
||||
public int QuestCount => Categories.Sum(x => x.QuestCount);
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ internal sealed class QuestData
|
||||
_quests = dataManager.GetExcelSheet<Quest>()!
|
||||
.Where(x => x.RowId > 0)
|
||||
.Where(x => x.IssuerLocation.Row > 0)
|
||||
.Where(x => x.Festival.Row == 0)
|
||||
.Select(x => new QuestInfo(x))
|
||||
.ToImmutableDictionary(x => x.QuestId, x => x);
|
||||
}
|
||||
@ -34,4 +35,13 @@ internal sealed class QuestData
|
||||
}
|
||||
|
||||
public bool IsIssuerOfAnyQuest(uint targetId) => _quests.Values.Any(x => x.IssuerDataId == targetId);
|
||||
|
||||
public List<QuestInfo> GetAllByJournalGenre(uint journalGenre)
|
||||
{
|
||||
return _quests.Values
|
||||
.Where(x => x.JournalGenre == journalGenre)
|
||||
.OrderBy(x => x.SortKey)
|
||||
.ThenBy(x => x.QuestId)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,23 @@ internal sealed class QuestInfo
|
||||
public QuestInfo(ExcelQuest quest)
|
||||
{
|
||||
QuestId = (ushort)(quest.RowId & 0xFFFF);
|
||||
Name = quest.Name.ToString();
|
||||
|
||||
string suffix = QuestId switch
|
||||
{
|
||||
85 => " (LNC)",
|
||||
108 => " (MRD)",
|
||||
109 => " (ACN)",
|
||||
123 => " (ARC)",
|
||||
124 => " (CNJ)",
|
||||
568 => " (GLA)",
|
||||
569 => " (PGL)",
|
||||
570 => " (THM)",
|
||||
673 => " (Ul'dah)",
|
||||
674 => " (Limsa/Gridania)",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
Name = $"{quest.Name}{suffix}";
|
||||
Level = quest.ClassJobLevel0;
|
||||
IssuerDataId = quest.IssuerStart;
|
||||
IsRepeatable = quest.IsRepeatable;
|
||||
@ -22,6 +38,8 @@ internal sealed class QuestInfo
|
||||
PreviousQuestJoin = (QuestJoin)quest.PreviousQuestJoin;
|
||||
QuestLocks = quest.QuestLock.Select(x => (ushort)(x.Row & 0xFFFFF)).Where(x => x != 0).ToImmutableList();
|
||||
QuestLockJoin = (QuestJoin)quest.QuestLockJoin;
|
||||
JournalGenre = quest.JournalGenre?.Row;
|
||||
SortKey = quest.SortKey;
|
||||
IsMainScenarioQuest = quest.JournalGenre?.Value?.JournalCategory?.Value?.JournalSection?.Row is 0 or 1;
|
||||
CompletesInstantly = quest.ToDoCompleteSeq[0] == 0;
|
||||
PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
|
||||
@ -42,6 +60,8 @@ internal sealed class QuestInfo
|
||||
public QuestJoin QuestLockJoin { get; }
|
||||
public List<ushort> PreviousInstanceContent { get; }
|
||||
public QuestJoin PreviousInstanceContentJoin { get; }
|
||||
public uint? JournalGenre { get; }
|
||||
public ushort SortKey { get; set; }
|
||||
public bool IsMainScenarioQuest { get; }
|
||||
public bool CompletesInstantly { get; }
|
||||
public GrandCompany GrandCompany { get; }
|
||||
|
@ -89,6 +89,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
serviceCollection.AddSingleton<ChatFunctions>();
|
||||
serviceCollection.AddSingleton<AetherCurrentData>();
|
||||
serviceCollection.AddSingleton<AetheryteData>();
|
||||
serviceCollection.AddSingleton<JournalData>();
|
||||
serviceCollection.AddSingleton<QuestData>();
|
||||
serviceCollection.AddSingleton<TerritoryData>();
|
||||
serviceCollection.AddSingleton<NavmeshIpc>();
|
||||
@ -159,6 +160,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
serviceCollection.AddSingleton<ActiveQuestComponent>();
|
||||
serviceCollection.AddSingleton<ARealmRebornComponent>();
|
||||
serviceCollection.AddSingleton<CreationUtilsComponent>();
|
||||
serviceCollection.AddSingleton<QuestTooltipComponent>();
|
||||
serviceCollection.AddSingleton<QuickAccessButtonsComponent>();
|
||||
serviceCollection.AddSingleton<RemainingTasksComponent>();
|
||||
|
||||
@ -167,6 +169,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
serviceCollection.AddSingleton<DebugOverlay>();
|
||||
serviceCollection.AddSingleton<QuestSelectionWindow>();
|
||||
serviceCollection.AddSingleton<QuestValidationWindow>();
|
||||
serviceCollection.AddSingleton<JournalProgressWindow>();
|
||||
}
|
||||
|
||||
private static void AddQuestValidators(ServiceCollection serviceCollection)
|
||||
|
268
Questionable/Windows/JournalProgressWindow.cs
Normal file
268
Questionable/Windows/JournalProgressWindow.cs
Normal file
@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Plugin.Services;
|
||||
using ImGuiNET;
|
||||
using LLib.ImGui;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Windows.QuestComponents;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
internal sealed class JournalProgressWindow : LWindow, IDisposable
|
||||
{
|
||||
private readonly JournalData _journalData;
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly UiUtils _uiUtils;
|
||||
private readonly QuestTooltipComponent _questTooltipComponent;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly ICommandManager _commandManager;
|
||||
|
||||
private readonly Dictionary<JournalData.Genre, (int Available, int Completed)> _genreCounts = new();
|
||||
private readonly Dictionary<JournalData.Category, (int Available, int Completed)> _categoryCounts = new();
|
||||
private readonly Dictionary<JournalData.Section, (int Available, int Completed)> _sectionCounts = new();
|
||||
|
||||
public JournalProgressWindow(JournalData journalData,
|
||||
QuestRegistry questRegistry,
|
||||
GameFunctions gameFunctions,
|
||||
UiUtils uiUtils,
|
||||
QuestTooltipComponent questTooltipComponent,
|
||||
IClientState clientState,
|
||||
ICommandManager commandManager)
|
||||
: base("Journal Progress###QuestionableJournalProgress")
|
||||
{
|
||||
_journalData = journalData;
|
||||
_questRegistry = questRegistry;
|
||||
_gameFunctions = gameFunctions;
|
||||
_uiUtils = uiUtils;
|
||||
_questTooltipComponent = questTooltipComponent;
|
||||
_clientState = clientState;
|
||||
_commandManager = commandManager;
|
||||
|
||||
_clientState.Login += RefreshCounts;
|
||||
_clientState.Logout -= ClearCounts;
|
||||
_questRegistry.Reloaded += OnQuestsReloaded;
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints
|
||||
{
|
||||
MinimumSize = new Vector2(700, 500)
|
||||
};
|
||||
}
|
||||
|
||||
private void OnQuestsReloaded(object? sender, EventArgs e) => RefreshCounts();
|
||||
|
||||
public override void OnOpen() => RefreshCounts();
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
ImGui.Text("The list below contains all quests that appear in your journal.");
|
||||
ImGui.BulletText("'Supported' lists quests that Questionable can do for you");
|
||||
ImGui.BulletText("'Completed' lists quests your current character has completed.");
|
||||
ImGui.BulletText(
|
||||
"Not all quests can be completed even if they're listed as available, e.g. starting city quest chains.");
|
||||
|
||||
ImGui.Spacing();
|
||||
ImGui.Separator();
|
||||
ImGui.Spacing();
|
||||
|
||||
using var table = ImRaii.Table("Quests", 3, ImGuiTableFlags.NoSavedSettings);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.NoHide);
|
||||
ImGui.TableSetupColumn("Supported", ImGuiTableColumnFlags.WidthFixed, 120 * ImGui.GetIO().FontGlobalScale);
|
||||
ImGui.TableSetupColumn("Completed", ImGuiTableColumnFlags.WidthFixed, 120 * ImGui.GetIO().FontGlobalScale);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var section in _journalData.Sections)
|
||||
{
|
||||
DrawSection(section);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSection(JournalData.Section section)
|
||||
{
|
||||
if (section.QuestCount == 0)
|
||||
return;
|
||||
|
||||
(int supported, int completed) = _sectionCounts.GetValueOrDefault(section);
|
||||
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
bool open = ImGui.TreeNodeEx(section.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
DrawCount(supported, section.QuestCount);
|
||||
ImGui.TableNextColumn();
|
||||
DrawCount(completed, section.QuestCount);
|
||||
|
||||
if (open)
|
||||
{
|
||||
foreach (var category in section.Categories)
|
||||
DrawCategory(category);
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCategory(JournalData.Category category)
|
||||
{
|
||||
if (category.QuestCount == 0)
|
||||
return;
|
||||
|
||||
(int supported, int completed) = _categoryCounts.GetValueOrDefault(category);
|
||||
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
bool open = ImGui.TreeNodeEx(category.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
DrawCount(supported, category.QuestCount);
|
||||
ImGui.TableNextColumn();
|
||||
DrawCount(completed, category.QuestCount);
|
||||
|
||||
if (open)
|
||||
{
|
||||
foreach (var genre in category.Genres)
|
||||
DrawGenre(genre);
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGenre(JournalData.Genre genre)
|
||||
{
|
||||
if (genre.QuestCount == 0)
|
||||
return;
|
||||
|
||||
(int supported, int completed) = _genreCounts.GetValueOrDefault(genre);
|
||||
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
bool open = ImGui.TreeNodeEx(genre.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
DrawCount(supported, genre.QuestCount);
|
||||
ImGui.TableNextColumn();
|
||||
DrawCount(completed, genre.QuestCount);
|
||||
|
||||
if (open)
|
||||
{
|
||||
foreach (var quest in genre.Quests)
|
||||
DrawQuest(quest);
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawQuest(QuestInfo questInfo)
|
||||
{
|
||||
_questRegistry.TryGetQuest(questInfo.QuestId, out var quest);
|
||||
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TreeNodeEx(questInfo.Name,
|
||||
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.SpanFullWidth);
|
||||
|
||||
|
||||
if (ImGui.IsItemClicked() && _commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
|
||||
{
|
||||
_commandManager.DispatchCommand("/questinfo",
|
||||
questInfo.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
_questTooltipComponent.Draw(questInfo);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
List<string> authors = quest?.Root.Author ?? [];
|
||||
_uiUtils.ChecklistItem(authors.Count > 0 ? string.Join(", ", authors) : string.Empty,
|
||||
quest is { Root.Disabled: false });
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var (color, icon, text) = _uiUtils.GetQuestStyle(questInfo.QuestId);
|
||||
_uiUtils.ChecklistItem(text, color, icon);
|
||||
}
|
||||
|
||||
private static void DrawCount(int count, int total)
|
||||
{
|
||||
string len = 9999.ToString(CultureInfo.CurrentCulture);
|
||||
ImGui.PushFont(UiBuilder.MonoFont);
|
||||
|
||||
string text =
|
||||
$"{count.ToString(CultureInfo.CurrentCulture).PadLeft(len.Length)} / {total.ToString(CultureInfo.CurrentCulture).PadLeft(len.Length)}";
|
||||
if (count == total)
|
||||
ImGui.TextColored(ImGuiColors.ParsedGreen, text);
|
||||
else
|
||||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
private void RefreshCounts()
|
||||
{
|
||||
_genreCounts.Clear();
|
||||
_categoryCounts.Clear();
|
||||
_sectionCounts.Clear();
|
||||
|
||||
foreach (var genre in _journalData.Genres)
|
||||
{
|
||||
int available = genre.Quests.Count(x =>
|
||||
_questRegistry.TryGetQuest(x.QuestId, out var quest) && !quest.Root.Disabled);
|
||||
int completed = genre.Quests.Count(x => _gameFunctions.IsQuestComplete(x.QuestId));
|
||||
_genreCounts[genre] = (available, completed);
|
||||
}
|
||||
|
||||
foreach (var category in _journalData.Categories)
|
||||
{
|
||||
var counts = _genreCounts
|
||||
.Where(x => category.Genres.Contains(x.Key))
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
int available = counts.Sum(x => x.Available);
|
||||
int completed = counts.Sum(x => x.Completed);
|
||||
_categoryCounts[category] = (available, completed);
|
||||
}
|
||||
|
||||
foreach (var section in _journalData.Sections)
|
||||
{
|
||||
var counts = _categoryCounts
|
||||
.Where(x => section.Categories.Contains(x.Key))
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
int available = counts.Sum(x => x.Available);
|
||||
int completed = counts.Sum(x => x.Completed);
|
||||
_sectionCounts[section] = (available, completed);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearCounts()
|
||||
{
|
||||
foreach (var genreCount in _genreCounts.ToList())
|
||||
_genreCounts[genreCount.Key] = (genreCount.Value.Available, 0);
|
||||
|
||||
foreach (var categoryCount in _categoryCounts.ToList())
|
||||
_categoryCounts[categoryCount.Key] = (categoryCount.Value.Available, 0);
|
||||
|
||||
foreach (var sectionCount in _sectionCounts.ToList())
|
||||
_sectionCounts[sectionCount.Key] = (sectionCount.Value.Available, 0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_questRegistry.Reloaded -= OnQuestsReloaded;
|
||||
_clientState.Logout -= ClearCounts;
|
||||
_clientState.Login -= RefreshCounts;
|
||||
}
|
||||
}
|
170
Questionable/Windows/QuestComponents/QuestTooltipComponent.cs
Normal file
170
Questionable/Windows/QuestComponents/QuestTooltipComponent.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using ImGuiNET;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
|
||||
namespace Questionable.Windows.QuestComponents;
|
||||
|
||||
internal sealed class QuestTooltipComponent
|
||||
{
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly QuestData _questData;
|
||||
private readonly TerritoryData _territoryData;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly UiUtils _uiUtils;
|
||||
|
||||
public QuestTooltipComponent(
|
||||
QuestRegistry questRegistry,
|
||||
QuestData questData,
|
||||
TerritoryData territoryData,
|
||||
GameFunctions gameFunctions,
|
||||
UiUtils uiUtils)
|
||||
{
|
||||
_questRegistry = questRegistry;
|
||||
_questData = questData;
|
||||
_territoryData = territoryData;
|
||||
_gameFunctions = gameFunctions;
|
||||
_uiUtils = uiUtils;
|
||||
}
|
||||
|
||||
public void Draw(QuestInfo quest)
|
||||
{
|
||||
using var tooltip = ImRaii.Tooltip();
|
||||
if (tooltip)
|
||||
{
|
||||
var (color, _, tooltipText) = _uiUtils.GetQuestStyle(quest.QuestId);
|
||||
ImGui.TextColored(color, tooltipText);
|
||||
if (quest.IsRepeatable)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Repeatable");
|
||||
}
|
||||
|
||||
if (quest.CompletesInstantly)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Instant");
|
||||
}
|
||||
|
||||
if (!_questRegistry.IsKnownQuest(quest.QuestId))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("NoQuestPath");
|
||||
}
|
||||
|
||||
DrawQuestUnlocks(quest, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawQuestUnlocks(QuestInfo quest, int counter)
|
||||
{
|
||||
if (counter >= 10)
|
||||
return;
|
||||
|
||||
if (counter != 0 && quest.IsMainScenarioQuest)
|
||||
return;
|
||||
|
||||
if (counter > 0)
|
||||
ImGui.Indent();
|
||||
|
||||
if (quest.PreviousQuests.Count > 0)
|
||||
{
|
||||
if (counter == 0)
|
||||
ImGui.Separator();
|
||||
|
||||
if (quest.PreviousQuests.Count > 1)
|
||||
{
|
||||
if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.All)
|
||||
ImGui.Text("Requires all:");
|
||||
else if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne)
|
||||
ImGui.Text("Requires one:");
|
||||
}
|
||||
|
||||
foreach (var q in quest.PreviousQuests)
|
||||
{
|
||||
var qInfo = _questData.GetQuestInfo(q);
|
||||
var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
|
||||
if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
|
||||
iconColor = ImGuiColors.DalamudGrey;
|
||||
|
||||
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
|
||||
|
||||
DrawQuestUnlocks(qInfo, counter + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == 0 && quest.QuestLocks.Count > 0)
|
||||
{
|
||||
ImGui.Separator();
|
||||
if (quest.QuestLocks.Count > 1)
|
||||
{
|
||||
if (quest.QuestLockJoin == QuestInfo.QuestJoin.All)
|
||||
ImGui.Text("Blocked by (if all completed):");
|
||||
else if (quest.QuestLockJoin == QuestInfo.QuestJoin.AtLeastOne)
|
||||
ImGui.Text("Blocked by (if at least completed):");
|
||||
}
|
||||
else
|
||||
ImGui.Text("Blocked by (if completed):");
|
||||
|
||||
foreach (var q in quest.QuestLocks)
|
||||
{
|
||||
var qInfo = _questData.GetQuestInfo(q);
|
||||
var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
|
||||
if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
|
||||
iconColor = ImGuiColors.DalamudGrey;
|
||||
|
||||
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == 0 && quest.PreviousInstanceContent.Count > 0)
|
||||
{
|
||||
ImGui.Separator();
|
||||
if (quest.PreviousInstanceContent.Count > 1)
|
||||
{
|
||||
if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.All)
|
||||
ImGui.Text("Requires all:");
|
||||
else if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne)
|
||||
ImGui.Text("Requires one:");
|
||||
}
|
||||
else
|
||||
ImGui.Text("Requires:");
|
||||
|
||||
foreach (var instanceId in quest.PreviousInstanceContent)
|
||||
{
|
||||
string instanceName = _territoryData.GetInstanceName(instanceId) ?? "?";
|
||||
var (iconColor, icon) = UiUtils.GetInstanceStyle(instanceId);
|
||||
_uiUtils.ChecklistItem(instanceName, iconColor, icon);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == 0 && quest.GrandCompany != GrandCompany.None)
|
||||
{
|
||||
ImGui.Separator();
|
||||
string gcName = quest.GrandCompany switch
|
||||
{
|
||||
GrandCompany.Maelstrom => "Maelstrom",
|
||||
GrandCompany.TwinAdder => "Twin Adder",
|
||||
GrandCompany.ImmortalFlames => "Immortal Flames",
|
||||
_ => "None",
|
||||
};
|
||||
|
||||
GrandCompany currentGrandCompany = _gameFunctions.GetGrandCompany();
|
||||
_uiUtils.ChecklistItem($"Grand Company: {gcName}", quest.GrandCompany == currentGrandCompany);
|
||||
}
|
||||
|
||||
if (counter > 0)
|
||||
ImGui.Unindent();
|
||||
}
|
||||
|
||||
private static string FormatQuestUnlockName(QuestInfo questInfo)
|
||||
{
|
||||
if (questInfo.IsMainScenarioQuest)
|
||||
return $"{questInfo.Name} ({questInfo.QuestId}, MSQ)";
|
||||
else
|
||||
return $"{questInfo.Name} ({questInfo.QuestId})";
|
||||
}
|
||||
}
|
@ -25,15 +25,25 @@ internal sealed class QuickAccessButtonsComponent
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly NavmeshIpc _navmeshIpc;
|
||||
private readonly QuestValidationWindow _questValidationWindow;
|
||||
private readonly JournalProgressWindow _journalProgressWindow;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly ICondition _condition;
|
||||
private readonly IFramework _framework;
|
||||
private readonly ICommandManager _commandManager;
|
||||
|
||||
public QuickAccessButtonsComponent(QuestController questController, MovementController movementController,
|
||||
GameUiController gameUiController, GameFunctions gameFunctions, ChatFunctions chatFunctions,
|
||||
QuestRegistry questRegistry, NavmeshIpc navmeshIpc, QuestValidationWindow questValidationWindow,
|
||||
IClientState clientState, ICondition condition, IFramework framework, ICommandManager commandManager)
|
||||
public QuickAccessButtonsComponent(QuestController questController,
|
||||
MovementController movementController,
|
||||
GameUiController gameUiController,
|
||||
GameFunctions gameFunctions,
|
||||
ChatFunctions chatFunctions,
|
||||
QuestRegistry questRegistry,
|
||||
NavmeshIpc navmeshIpc,
|
||||
QuestValidationWindow questValidationWindow,
|
||||
JournalProgressWindow journalProgressWindow,
|
||||
IClientState clientState,
|
||||
ICondition condition,
|
||||
IFramework framework,
|
||||
ICommandManager commandManager)
|
||||
{
|
||||
_questController = questController;
|
||||
_movementController = movementController;
|
||||
@ -43,6 +53,7 @@ internal sealed class QuickAccessButtonsComponent
|
||||
_questRegistry = questRegistry;
|
||||
_navmeshIpc = navmeshIpc;
|
||||
_questValidationWindow = questValidationWindow;
|
||||
_journalProgressWindow = journalProgressWindow;
|
||||
_clientState = clientState;
|
||||
_condition = condition;
|
||||
_framework = framework;
|
||||
@ -80,6 +91,11 @@ internal sealed class QuickAccessButtonsComponent
|
||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.RedoAlt,"Reload Data"))
|
||||
Reload();
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.ChartColumn))
|
||||
_journalProgressWindow.IsOpen = true;
|
||||
|
||||
|
||||
if (_questRegistry.ValidationIssueCount > 0)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
|
@ -20,6 +20,7 @@ using Questionable.Controller;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.V1;
|
||||
using Questionable.Windows.QuestComponents;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
@ -36,6 +37,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
private readonly TerritoryData _territoryData;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly UiUtils _uiUtils;
|
||||
private readonly QuestTooltipComponent _questTooltipComponent;
|
||||
|
||||
private List<QuestInfo> _quests = [];
|
||||
private List<QuestInfo> _offeredQuests = [];
|
||||
@ -43,7 +45,8 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
|
||||
public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, GameFunctions gameFunctions,
|
||||
QuestController questController, QuestRegistry questRegistry, IDalamudPluginInterface pluginInterface,
|
||||
TerritoryData territoryData, IClientState clientState, UiUtils uiUtils)
|
||||
TerritoryData territoryData, IClientState clientState, UiUtils uiUtils,
|
||||
QuestTooltipComponent questTooltipComponent)
|
||||
: base($"Quest Selection{WindowId}")
|
||||
{
|
||||
_questData = questData;
|
||||
@ -56,6 +59,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
_territoryData = territoryData;
|
||||
_clientState = clientState;
|
||||
_uiUtils = uiUtils;
|
||||
_questTooltipComponent = questTooltipComponent;
|
||||
|
||||
Size = new Vector2(500, 200);
|
||||
SizeCondition = ImGuiCond.Once;
|
||||
@ -169,7 +173,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
if (ImGui.TableNextColumn())
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
var (color, icon, tooltipText) = _uiUtils.GetQuestStyle(quest.QuestId);
|
||||
var (color, icon, _) = _uiUtils.GetQuestStyle(quest.QuestId);
|
||||
using (var _ = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
|
||||
{
|
||||
if (isKnownQuest)
|
||||
@ -179,32 +183,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tooltip = ImRaii.Tooltip();
|
||||
if (tooltip)
|
||||
{
|
||||
ImGui.TextColored(color, tooltipText);
|
||||
if (quest.IsRepeatable)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Repeatable");
|
||||
}
|
||||
|
||||
if (quest.CompletesInstantly)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Instant");
|
||||
}
|
||||
|
||||
if (!isKnownQuest)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("NoQuestPath");
|
||||
}
|
||||
|
||||
DrawQuestUnlocks(quest, 0);
|
||||
}
|
||||
}
|
||||
_questTooltipComponent.Draw(quest);
|
||||
}
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
@ -272,113 +251,4 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
ImGui.SetClipboardText(fileName);
|
||||
_chatGui.Print($"Copied '{fileName}' to clipboard");
|
||||
}
|
||||
|
||||
private void DrawQuestUnlocks(QuestInfo quest, int counter)
|
||||
{
|
||||
if (counter >= 10)
|
||||
return;
|
||||
|
||||
if (counter != 0 && quest.IsMainScenarioQuest)
|
||||
return;
|
||||
|
||||
if (counter > 0)
|
||||
ImGui.Indent();
|
||||
|
||||
if (quest.PreviousQuests.Count > 0)
|
||||
{
|
||||
if (counter == 0)
|
||||
ImGui.Separator();
|
||||
|
||||
if (quest.PreviousQuests.Count > 1)
|
||||
{
|
||||
if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.All)
|
||||
ImGui.Text("Requires all:");
|
||||
else if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne)
|
||||
ImGui.Text("Requires one:");
|
||||
}
|
||||
|
||||
foreach (var q in quest.PreviousQuests)
|
||||
{
|
||||
var qInfo = _questData.GetQuestInfo(q);
|
||||
var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
|
||||
if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
|
||||
iconColor = ImGuiColors.DalamudGrey;
|
||||
|
||||
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
|
||||
|
||||
DrawQuestUnlocks(qInfo, counter + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == 0 && quest.QuestLocks.Count > 0)
|
||||
{
|
||||
ImGui.Separator();
|
||||
if (quest.QuestLocks.Count > 1)
|
||||
{
|
||||
if (quest.QuestLockJoin == QuestInfo.QuestJoin.All)
|
||||
ImGui.Text("Blocked by (if all completed):");
|
||||
else if (quest.QuestLockJoin == QuestInfo.QuestJoin.AtLeastOne)
|
||||
ImGui.Text("Blocked by (if at least completed):");
|
||||
}
|
||||
else
|
||||
ImGui.Text("Blocked by (if completed):");
|
||||
|
||||
foreach (var q in quest.QuestLocks)
|
||||
{
|
||||
var qInfo = _questData.GetQuestInfo(q);
|
||||
var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
|
||||
if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
|
||||
iconColor = ImGuiColors.DalamudGrey;
|
||||
|
||||
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == 0 && quest.PreviousInstanceContent.Count > 0)
|
||||
{
|
||||
ImGui.Separator();
|
||||
if (quest.PreviousInstanceContent.Count > 1)
|
||||
{
|
||||
if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.All)
|
||||
ImGui.Text("Requires all:");
|
||||
else if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne)
|
||||
ImGui.Text("Requires one:");
|
||||
}
|
||||
else
|
||||
ImGui.Text("Requires:");
|
||||
|
||||
foreach (var instanceId in quest.PreviousInstanceContent)
|
||||
{
|
||||
string instanceName = _territoryData.GetInstanceName(instanceId) ?? "?";
|
||||
var (iconColor, icon) = UiUtils.GetInstanceStyle(instanceId);
|
||||
_uiUtils.ChecklistItem(instanceName, iconColor, icon);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter == 0 && quest.GrandCompany != GrandCompany.None)
|
||||
{
|
||||
ImGui.Separator();
|
||||
string gcName = quest.GrandCompany switch
|
||||
{
|
||||
GrandCompany.Maelstrom => "Maelstrom",
|
||||
GrandCompany.TwinAdder => "Twin Adder",
|
||||
GrandCompany.ImmortalFlames => "Immortal Flames",
|
||||
_ => "None",
|
||||
};
|
||||
|
||||
GrandCompany currentGrandCompany = _gameFunctions.GetGrandCompany();
|
||||
_uiUtils.ChecklistItem($"Grand Company: {gcName}", quest.GrandCompany == currentGrandCompany);
|
||||
}
|
||||
|
||||
if (counter > 0)
|
||||
ImGui.Unindent();
|
||||
}
|
||||
|
||||
private static string FormatQuestUnlockName(QuestInfo questInfo)
|
||||
{
|
||||
if (questInfo.IsMainScenarioQuest)
|
||||
return $"{questInfo.Name} ({questInfo.QuestId}, MSQ)";
|
||||
else
|
||||
return $"{questInfo.Name} ({questInfo.QuestId})";
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user