Seasonal event info panel
authorLiza Carvelli <liza@carvel.li>
Mon, 19 Aug 2024 13:19:15 +0000 (15:19 +0200)
committerLiza Carvelli <liza@carvel.li>
Mon, 19 Aug 2024 13:19:15 +0000 (15:19 +0200)
Questionable/Configuration.cs
Questionable/QuestionablePlugin.cs
Questionable/Windows/ConfigWindow.cs
Questionable/Windows/QuestComponents/EventInfoComponent.cs [new file with mode: 0644]
Questionable/Windows/QuestComponents/QuestTooltipComponent.cs
Questionable/Windows/QuestWindow.cs
Questionable/Windows/UiUtils.cs

index 2e8339957e06f35265f22e8e5afaec1b12c9fdf9..9f86cb94f98020fafc5212abbd14cbf30546a637 100644 (file)
@@ -18,6 +18,7 @@ internal sealed class Configuration : IPluginConfiguration
         public GrandCompany GrandCompany { get; set; } = GrandCompany.None;
         public bool HideInAllInstances { get; set; } = true;
         public bool UseEscToCancelQuesting { get; set; } = true;
+        public bool ShowIncompleteSeasonalEvents { get; set; } = true;
     }
 
     internal sealed class AdvancedConfiguration
index 8517d135ec08dd24bcbf32302979d9a39057a2f3..4c7f05b7386081beb57edac12a9ba7b5d3523dee 100644 (file)
@@ -208,6 +208,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<ActiveQuestComponent>();
         serviceCollection.AddSingleton<ARealmRebornComponent>();
         serviceCollection.AddSingleton<CreationUtilsComponent>();
+        serviceCollection.AddSingleton<EventInfoComponent>();
         serviceCollection.AddSingleton<QuestTooltipComponent>();
         serviceCollection.AddSingleton<QuickAccessButtonsComponent>();
         serviceCollection.AddSingleton<RemainingTasksComponent>();
index 0fa60d69c1c57926be61ae907e781e2a1ee04f16..c2148aa1e7de9c6339dbae1103ea29eb571b6a94 100644 (file)
@@ -83,6 +83,13 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
                     Save();
                 }
 
+                bool showIncompleteSeasonalEvents = _configuration.General.ShowIncompleteSeasonalEvents;
+                if (ImGui.Checkbox("Show details for incomplete seasonal events", ref showIncompleteSeasonalEvents))
+                {
+                    _configuration.General.ShowIncompleteSeasonalEvents = showIncompleteSeasonalEvents;
+                    Save();
+                }
+
                 ImGui.EndTabItem();
             }
 
diff --git a/Questionable/Windows/QuestComponents/EventInfoComponent.cs b/Questionable/Windows/QuestComponents/EventInfoComponent.cs
new file mode 100644 (file)
index 0000000..12e3f49
--- /dev/null
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Dalamud.Interface;
+using Dalamud.Interface.Components;
+using Dalamud.Plugin;
+using Humanizer;
+using Humanizer.Localisation;
+using ImGuiNET;
+using Questionable.Controller;
+using Questionable.Data;
+using Questionable.Functions;
+using Questionable.Model;
+using Questionable.Model.Questing;
+
+namespace Questionable.Windows.QuestComponents;
+
+internal sealed class EventInfoComponent
+{
+    private readonly List<EventQuest> _eventQuests =
+    [
+        new EventQuest("Moonfire Faire", [new(5182), new(5183)],
+            new DateTime(new DateOnly(2024, 8, 26), new TimeOnly(14, 59), DateTimeKind.Utc)),
+    ];
+
+    private readonly QuestData _questData;
+    private readonly QuestRegistry _questRegistry;
+    private readonly QuestFunctions _questFunctions;
+    private readonly UiUtils _uiUtils;
+    private readonly QuestController _questController;
+    private readonly QuestTooltipComponent _questTooltipComponent;
+    private readonly Configuration _configuration;
+    private readonly IDalamudPluginInterface _pluginInterface;
+
+    public EventInfoComponent(QuestData questData, QuestRegistry questRegistry, QuestFunctions questFunctions,
+        UiUtils uiUtils, QuestController questController, QuestTooltipComponent questTooltipComponent,
+        Configuration configuration, IDalamudPluginInterface pluginInterface)
+    {
+        _questData = questData;
+        _questRegistry = questRegistry;
+        _questFunctions = questFunctions;
+        _uiUtils = uiUtils;
+        _questController = questController;
+        _questTooltipComponent = questTooltipComponent;
+        _configuration = configuration;
+        _pluginInterface = pluginInterface;
+    }
+
+    public bool ShouldDraw => _configuration.General.ShowIncompleteSeasonalEvents && _eventQuests.Any(IsIncomplete);
+
+    public void Draw()
+    {
+        foreach (var eventQuest in _eventQuests)
+        {
+            if (IsIncomplete(eventQuest))
+                DrawEventQuest(eventQuest);
+        }
+    }
+
+    private void DrawEventQuest(EventQuest eventQuest)
+    {
+        string time = (eventQuest.EndsAtUtc - DateTime.UtcNow).Humanize(
+            precision: 1,
+            culture: CultureInfo.InvariantCulture,
+            minUnit: TimeUnit.Minute,
+            maxUnit: TimeUnit.Day);
+        ImGui.Text($"{eventQuest.Name} ({time})");
+
+        float width;
+        using (var _ = _pluginInterface.UiBuilder.IconFontHandle.Push())
+            width = ImGui.CalcTextSize(FontAwesomeIcon.Play.ToIconString()).X + ImGui.GetStyle().FramePadding.X;
+
+        using (var _ = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
+            width -= ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X;
+
+        List<QuestId> startableQuests = eventQuest.QuestIds.Where(x =>
+            _questRegistry.IsKnownQuest(x) &&
+            _questFunctions.IsReadyToAcceptQuest(x) &&
+            x != _questController.StartedQuest?.Quest.Id &&
+            x != _questController.NextQuest?.Quest.Id)
+            .ToList();
+        if (startableQuests.Count == 0)
+            width = 0;
+
+        foreach (var questId in eventQuest.QuestIds)
+        {
+            if (_questFunctions.IsQuestComplete(questId))
+                continue;
+
+            string questName = _questData.GetQuestInfo(questId).Name;
+            if (startableQuests.Contains(questId) &&
+                _questRegistry.TryGetQuest(questId, out Quest? quest))
+            {
+                ImGuiComponents.IconButton(FontAwesomeIcon.Play);
+                if (ImGui.IsItemClicked())
+                {
+                    _questController.SetNextQuest(quest);
+                    _questController.Start("SeasonalEventSelection");
+                }
+
+                bool hovered = ImGui.IsItemHovered();
+
+                ImGui.SameLine();
+                ImGui.AlignTextToFramePadding();
+                ImGui.Text(questName);
+                hovered |= ImGui.IsItemHovered();
+
+                if (hovered)
+                    _questTooltipComponent.Draw(quest.Info);
+            }
+            else
+            {
+                if (width > 0)
+                    ImGui.SetCursorPosX(ImGui.GetCursorPosX() + width);
+
+                var style = _uiUtils.GetQuestStyle(questId);
+                if (_uiUtils.ChecklistItem(questName, style.Color, style.Icon, ImGui.GetStyle().FramePadding.X))
+                    _questTooltipComponent.Draw(_questData.GetQuestInfo(questId));
+            }
+        }
+    }
+
+    private bool IsIncomplete(EventQuest eventQuest)
+    {
+        if (eventQuest.EndsAtUtc <= DateTime.UtcNow)
+            return false;
+
+        return !eventQuest.QuestIds.All(x => _questFunctions.IsQuestComplete(x));
+    }
+
+    private sealed record EventQuest(string Name, List<QuestId> QuestIds, DateTime EndsAtUtc);
+}
index 65bcf24063c65836b71e729e8e76e10d753caf76..1f8a19a0f949d25d9baf24a678c76d55a86d6f3e 100644 (file)
@@ -1,4 +1,5 @@
-using Dalamud.Interface;
+using Dalamud.Game.Text;
+using Dalamud.Interface;
 using Dalamud.Interface.Colors;
 using Dalamud.Interface.Utility.Raii;
 using FFXIVClientStructs.FFXIV.Client.UI.Agent;
@@ -44,6 +45,9 @@ internal sealed class QuestTooltipComponent
         using var tooltip = ImRaii.Tooltip();
         if (tooltip)
         {
+            ImGui.Text($"{SeIconChar.LevelEn.ToIconString()}{quest.Level}");
+            ImGui.SameLine();
+
             var (color, _, tooltipText) = _uiUtils.GetQuestStyle(quest.QuestId);
             ImGui.TextColored(color, tooltipText);
             if (quest.IsRepeatable)
index 296f0ac5475eb05eb434d53739c04dc725b62bb6..fa880ec4c790ded7babbba9810e60a450253fe55 100644 (file)
@@ -25,6 +25,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
     private readonly ActiveQuestComponent _activeQuestComponent;
     private readonly ARealmRebornComponent _aRealmRebornComponent;
     private readonly CreationUtilsComponent _creationUtilsComponent;
+    private readonly EventInfoComponent _eventInfoComponent;
     private readonly QuickAccessButtonsComponent _quickAccessButtonsComponent;
     private readonly RemainingTasksComponent _remainingTasksComponent;
     private readonly IFramework _framework;
@@ -38,6 +39,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
         TerritoryData territoryData,
         ActiveQuestComponent activeQuestComponent,
         ARealmRebornComponent aRealmRebornComponent,
+        EventInfoComponent eventInfoComponent,
         CreationUtilsComponent creationUtilsComponent,
         QuickAccessButtonsComponent quickAccessButtonsComponent,
         RemainingTasksComponent remainingTasksComponent,
@@ -53,6 +55,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
         _territoryData = territoryData;
         _activeQuestComponent = activeQuestComponent;
         _aRealmRebornComponent = aRealmRebornComponent;
+        _eventInfoComponent = eventInfoComponent;
         _creationUtilsComponent = creationUtilsComponent;
         _quickAccessButtonsComponent = quickAccessButtonsComponent;
         _remainingTasksComponent = remainingTasksComponent;
@@ -135,6 +138,12 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
                     ImGui.Separator();
                 }
 
+                if (_eventInfoComponent.ShouldDraw)
+                {
+                    _eventInfoComponent.Draw();
+                    ImGui.Separator();
+                }
+
                 _creationUtilsComponent.Draw();
                 ImGui.Separator();
 
index d04d5dac59f65dc2656b25c9591245d6ce8ec2ac..4e1fb9c3acce77e143c680ef8f52470a5eeb6dc4 100644 (file)
@@ -20,7 +20,7 @@ internal sealed class UiUtils
         _pluginInterface = pluginInterface;
     }
 
-    public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ElementId elementId)
+    public (Vector4 Color, FontAwesomeIcon Icon, string Status) GetQuestStyle(ElementId elementId)
     {
         if (_questFunctions.IsQuestAccepted(elementId))
             return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight, "Active");
@@ -42,7 +42,7 @@ internal sealed class UiUtils
             return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times);
     }
 
-    public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon)
+    public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon, float extraPadding = 0)
     {
         // ReSharper disable once UnusedVariable
         using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
@@ -53,6 +53,8 @@ internal sealed class UiUtils
         bool hover = ImGui.IsItemHovered();
 
         ImGui.SameLine();
+        if (extraPadding > 0)
+            ImGui.SetCursorPosX(ImGui.GetCursorPosX() + extraPadding);
         ImGui.TextUnformatted(text);
         return hover;
     }