muffin v6.12

This commit is contained in:
alydev 2025-10-09 07:53:51 +10:00
parent e786325cda
commit 0950798597
64 changed files with 40100 additions and 58121 deletions

View file

@ -6,8 +6,11 @@ using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using Dalamud.Utility;
using Microsoft.Extensions.Logging;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Functions;
@ -42,13 +45,15 @@ internal sealed class QuestJournalComponent
public bool HideNoPaths;
public bool HideUnobtainable;
public bool AdvancedFiltersActive
{
get
{
if (!AvailableOnly)
if (!AvailableOnly && !HideNoPaths)
{
return HideNoPaths;
return HideUnobtainable;
}
return true;
}
@ -59,7 +64,8 @@ internal sealed class QuestJournalComponent
return new FilterConfiguration
{
AvailableOnly = AvailableOnly,
HideNoPaths = HideNoPaths
HideNoPaths = HideNoPaths,
HideUnobtainable = HideUnobtainable
};
}
}
@ -86,11 +92,19 @@ internal sealed class QuestJournalComponent
private readonly QuestValidator _questValidator;
private readonly Configuration _configuration;
private readonly ILogger<QuestJournalComponent> _logger;
private const uint SeasonalJournalCategoryRowId = 96u;
private List<FilteredSection> _filteredSections = new List<FilteredSection>();
private bool _lastHideSeasonalGlobally;
internal FilterConfiguration Filter { get; } = new FilterConfiguration();
public QuestJournalComponent(JournalData journalData, QuestRegistry questRegistry, QuestFunctions questFunctions, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, IDalamudPluginInterface pluginInterface, QuestJournalUtils questJournalUtils, QuestValidator questValidator)
public QuestJournalComponent(JournalData journalData, QuestRegistry questRegistry, QuestFunctions questFunctions, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, IDalamudPluginInterface pluginInterface, QuestJournalUtils questJournalUtils, QuestValidator questValidator, Configuration configuration, ILogger<QuestJournalComponent> logger)
{
_journalData = journalData;
_questRegistry = questRegistry;
@ -100,6 +114,9 @@ internal sealed class QuestJournalComponent
_pluginInterface = pluginInterface;
_questJournalUtils = questJournalUtils;
_questValidator = questValidator;
_configuration = configuration;
_logger = logger;
_lastHideSeasonalGlobally = _configuration.General.HideSeasonalEventsFromJournalProgress;
}
public void DrawQuests()
@ -109,18 +126,34 @@ internal sealed class QuestJournalComponent
{
return;
}
bool hideSeasonalEventsFromJournalProgress = _configuration.General.HideSeasonalEventsFromJournalProgress;
if (hideSeasonalEventsFromJournalProgress != _lastHideSeasonalGlobally)
{
_lastHideSeasonalGlobally = hideSeasonalEventsFromJournalProgress;
_logger.LogDebug("Configuration change detected: HideSeasonalEventsFromJournalProgress={Hide} - refreshing journal", hideSeasonalEventsFromJournalProgress);
UpdateFilter();
}
if (ImGui.CollapsingHeader("Explanation", ImGuiTreeNodeFlags.DefaultOpen))
{
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.BulletText("Not all quests can be completed even if they're listed as available, e.g. starting city quest chains or past seasonal events.");
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
}
QuestJournalUtils.ShowFilterContextMenu(this);
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.GlobeEurope))
{
Util.OpenLink("https://wigglymuffin.github.io/FFXIV-Tools/");
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("View All Quest Paths Online");
}
ImGui.SameLine();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputTextWithHint(string.Empty, "Search quests and categories", ref Filter.SearchText, 256))
{
@ -270,16 +303,57 @@ internal sealed class QuestJournalComponent
{
_uiUtils.ChecklistItem(string.Empty, complete: true);
}
goto IL_0215;
goto IL_0210;
}
}
_uiUtils.ChecklistItem(string.Empty, complete: false);
}
goto IL_0215;
IL_0215:
goto IL_0210;
IL_0210:
ImGui.TableNextColumn();
var (color, icon, text) = _uiUtils.GetQuestStyle(questInfo.QuestId);
_uiUtils.ChecklistItem(text, color, icon);
if (_questFunctions.IsQuestComplete(questInfo.QuestId))
{
if (questInfo.IsRepeatable && _questFunctions.IsReadyToAcceptQuest(questInfo.QuestId))
{
_uiUtils.ChecklistItem("Complete", ImGuiColors.ParsedBlue, FontAwesomeIcon.Check);
}
else
{
_uiUtils.ChecklistItem("Complete", ImGuiColors.ParsedGreen, FontAwesomeIcon.Check);
}
return;
}
if (_questFunctions.IsQuestAccepted(questInfo.QuestId))
{
_uiUtils.ChecklistItem("Active", ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight);
return;
}
bool flag = false;
bool flag2 = _questFunctions.IsQuestUnobtainable(questInfo.QuestId);
bool flag3 = _questFunctions.IsQuestLocked(questInfo.QuestId);
bool flag4 = _questFunctions.IsReadyToAcceptQuest(questInfo.QuestId);
DateTime? seasonalQuestExpiry = questInfo.SeasonalQuestExpiry;
if (seasonalQuestExpiry.HasValue)
{
DateTime valueOrDefault = seasonalQuestExpiry.GetValueOrDefault();
DateTime dateTime = ((valueOrDefault.Kind == DateTimeKind.Utc) ? valueOrDefault : valueOrDefault.ToUniversalTime());
if (DateTime.UtcNow > dateTime)
{
flag = true;
}
}
if (flag || flag2)
{
_uiUtils.ChecklistItem("Unobtainable", ImGuiColors.DalamudGrey, FontAwesomeIcon.Minus);
}
else if (flag3 || !flag4 || !_questRegistry.IsKnownQuest(questInfo.QuestId))
{
_uiUtils.ChecklistItem("Locked", ImGuiColors.DalamudRed, FontAwesomeIcon.Times);
}
else
{
_uiUtils.ChecklistItem("Available", ImGuiColors.DalamudYellow, FontAwesomeIcon.Running);
}
}
private static void DrawCount(int count, int total)
@ -321,19 +395,34 @@ internal sealed class QuestJournalComponent
private FilteredSection FilterSection(JournalData.Section section, FilterConfiguration filter)
{
IEnumerable<FilteredCategory> source = ((!IsCategorySectionGenreMatch(filter, section.Name)) ? section.Categories.Select((JournalData.Category category) => FilterCategory(category, filter)) : section.Categories.Select((JournalData.Category x) => FilterCategory(x, filter.WithoutName())));
return new FilteredSection(section, source.Where((FilteredCategory x) => x.Genres.Count > 0).ToList());
IEnumerable<JournalData.Category> enumerable;
if (!_configuration.General.HideSeasonalEventsFromJournalProgress)
{
IEnumerable<JournalData.Category> categories = section.Categories;
enumerable = categories;
}
else
{
enumerable = section.Categories.Where((JournalData.Category c) => c.Id != 96);
}
IEnumerable<JournalData.Category> source = enumerable;
return new FilteredSection(Categories: ((!IsCategorySectionGenreMatch(filter, section.Name)) ? source.Select((JournalData.Category category) => FilterCategory(category, filter, section)) : source.Select((JournalData.Category x) => FilterCategory(x, filter.WithoutName(), section))).Where((FilteredCategory x) => x.Genres.Count > 0).ToList(), Section: section);
}
private FilteredCategory FilterCategory(JournalData.Category category, FilterConfiguration filter)
private FilteredCategory FilterCategory(JournalData.Category category, FilterConfiguration filter, JournalData.Section? parentSection = null)
{
IEnumerable<FilteredGenre> source = ((!IsCategorySectionGenreMatch(filter, category.Name)) ? category.Genres.Select((JournalData.Genre genre) => FilterGenre(genre, filter)) : category.Genres.Select((JournalData.Genre x) => FilterGenre(x, filter.WithoutName())));
IEnumerable<FilteredGenre> source = ((!IsCategorySectionGenreMatch(filter, category.Name)) ? category.Genres.Select((JournalData.Genre genre) => FilterGenre(genre, filter, parentSection)) : category.Genres.Select((JournalData.Genre x) => FilterGenre(x, filter.WithoutName(), parentSection)));
return new FilteredCategory(category, source.Where((FilteredGenre x) => x.Quests.Count > 0).ToList());
}
private FilteredGenre FilterGenre(JournalData.Genre genre, FilterConfiguration filter)
private FilteredGenre FilterGenre(JournalData.Genre genre, FilterConfiguration filter, JournalData.Section? parentSection = null)
{
bool hideSeasonalEventsFromJournalProgress = _configuration.General.HideSeasonalEventsFromJournalProgress;
IEnumerable<IQuestInfo> source = ((!IsCategorySectionGenreMatch(filter, genre.Name)) ? genre.Quests.Where((IQuestInfo x) => IsQuestMatch(filter, x)) : genre.Quests.Where((IQuestInfo x) => IsQuestMatch(filter.WithoutName(), x)));
if (hideSeasonalEventsFromJournalProgress && genre.CategoryId == 96)
{
source = source.Where((IQuestInfo q) => !IsSeasonal(q));
}
return new FilteredGenre(genre, source.ToList());
}
@ -342,37 +431,50 @@ internal sealed class QuestJournalComponent
_genreCounts.Clear();
_categoryCounts.Clear();
_sectionCounts.Clear();
bool hideSeasonalEventsFromJournalProgress = _configuration.General.HideSeasonalEventsFromJournalProgress;
_logger.LogInformation("Refreshing journal counts. HideSeasonalEventsFromJournalProgress={Hide}", hideSeasonalEventsFromJournalProgress);
foreach (JournalData.Genre genre in _journalData.Genres)
{
List<IQuestInfo> source = ((hideSeasonalEventsFromJournalProgress && genre.CategoryId == 96) ? genre.Quests.Where((IQuestInfo q) => !IsSeasonal(q)).ToList() : genre.Quests.ToList());
Quest quest;
int available = genre.Quests.Count((IQuestInfo x) => _questRegistry.TryGetQuest(x.QuestId, out quest) && !quest.Root.Disabled && !_questFunctions.IsQuestRemoved(x.QuestId));
int total = genre.Quests.Count((IQuestInfo x) => !_questFunctions.IsQuestRemoved(x.QuestId));
int obtainable = genre.Quests.Count((IQuestInfo x) => !_questFunctions.IsQuestUnobtainable(x.QuestId));
int completed = genre.Quests.Count((IQuestInfo x) => _questFunctions.IsQuestComplete(x.QuestId));
int available = source.Count((IQuestInfo x) => _questRegistry.TryGetQuest(x.QuestId, out quest) && !quest.Root.Disabled && !_questFunctions.IsQuestRemoved(x.QuestId));
int total = source.Count((IQuestInfo x) => !_questFunctions.IsQuestRemoved(x.QuestId));
int obtainable = source.Count((IQuestInfo x) => !_questFunctions.IsQuestUnobtainable(x.QuestId));
int completed = source.Count((IQuestInfo x) => _questFunctions.IsQuestComplete(x.QuestId));
_genreCounts[genre] = new JournalCounts(available, total, obtainable, completed);
}
foreach (JournalData.Category category in _journalData.Categories)
{
List<JournalCounts> source = (from x in _genreCounts
where category.Genres.Contains(x.Key)
select x.Value).ToList();
int available2 = source.Sum((JournalCounts x) => x.Available);
int total2 = source.Sum((JournalCounts x) => x.Total);
int obtainable2 = source.Sum((JournalCounts x) => x.Obtainable);
int completed2 = source.Sum((JournalCounts x) => x.Completed);
_categoryCounts[category] = new JournalCounts(available2, total2, obtainable2, completed2);
if (!hideSeasonalEventsFromJournalProgress || category.Id != 96)
{
List<JournalCounts> source2 = _genre_counts_or_default(category);
int available2 = source2.Sum((JournalCounts x) => x.Available);
int total2 = source2.Sum((JournalCounts x) => x.Total);
int obtainable2 = source2.Sum((JournalCounts x) => x.Obtainable);
int completed2 = source2.Sum((JournalCounts x) => x.Completed);
_categoryCounts[category] = new JournalCounts(available2, total2, obtainable2, completed2);
}
}
foreach (JournalData.Section section in _journalData.Sections)
{
List<JournalCounts> source2 = (from x in _categoryCounts
List<JournalCounts> source3 = (from x in _categoryCounts
where section.Categories.Contains(x.Key)
select x.Value).ToList();
int available3 = source2.Sum((JournalCounts x) => x.Available);
int total3 = source2.Sum((JournalCounts x) => x.Total);
int obtainable3 = source2.Sum((JournalCounts x) => x.Obtainable);
int completed3 = source2.Sum((JournalCounts x) => x.Completed);
int available3 = source3.Sum((JournalCounts x) => x.Available);
int total3 = source3.Sum((JournalCounts x) => x.Total);
int obtainable3 = source3.Sum((JournalCounts x) => x.Obtainable);
int completed3 = source3.Sum((JournalCounts x) => x.Completed);
_sectionCounts[section] = new JournalCounts(available3, total3, obtainable3, completed3);
}
int num = _sectionCounts.Values.Sum((JournalCounts x) => x.Total);
_logger.LogDebug("RefreshCounts complete. Sections={Sections}, Categories={Categories}, Genres={Genres}, TotalQuests={Total}", _sectionCounts.Count, _categoryCounts.Count, _genreCounts.Count, num);
}
private List<JournalCounts> _genre_counts_or_default(JournalData.Category category)
{
return (from x in _genreCounts
where category.Genres.Contains(x.Key)
select x.Value).ToList();
}
internal void ClearCounts(int type, int code)
@ -423,6 +525,31 @@ internal sealed class QuestJournalComponent
{
return false;
}
if (filter.HideUnobtainable && _questFunctions.IsQuestUnobtainable(questInfo.QuestId))
{
return false;
}
return true;
}
private static bool IsSeasonal(IQuestInfo q)
{
if (q == null)
{
return false;
}
if (q.IsSeasonalQuest)
{
return true;
}
if (q.SeasonalQuestExpiry.HasValue)
{
return true;
}
if (q is UnlockLinkQuestInfo { QuestExpiry: not null })
{
return true;
}
return false;
}
}