Automatically close job bar tutorials + Lv49 novice guide
authorLiza Carvelli <liza@carvel.li>
Fri, 24 Jan 2025 18:53:20 +0000 (19:53 +0100)
committerLiza Carvelli <liza@carvel.li>
Fri, 24 Jan 2025 18:53:20 +0000 (19:53 +0100)
Questionable/Controller/GameUi/HelpUiController.cs
Questionable/Controller/MiniTaskController.cs
Questionable/Controller/QuestController.cs

index 0d869add3ca8a3a5ba18b006280136094dfbb8d8..504f8cd06e02f1853feadf3972e05458fdf48981 100644 (file)
@@ -3,6 +3,7 @@ using Dalamud.Game.Addon.Lifecycle;
 using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
 using Dalamud.Plugin.Services;
 using FFXIVClientStructs.FFXIV.Component.GUI;
+using LLib.GameUI;
 using Microsoft.Extensions.Logging;
 
 namespace Questionable.Controller.GameUi;
@@ -11,17 +12,45 @@ internal sealed class HelpUiController : IDisposable
 {
     private readonly QuestController _questController;
     private readonly IAddonLifecycle _addonLifecycle;
+    private readonly IGameGui _gameGui;
     private readonly ILogger<HelpUiController> _logger;
 
-    public HelpUiController(QuestController questController, IAddonLifecycle addonLifecycle, ILogger<HelpUiController> logger)
+    public HelpUiController(
+        QuestController questController,
+        IAddonLifecycle addonLifecycle,
+        IGameGui gameGui,
+        ILogger<HelpUiController> logger)
     {
         _questController = questController;
         _addonLifecycle = addonLifecycle;
+        _gameGui = gameGui;
         _logger = logger;
 
+        _questController.AutomationTypeChanged += CloseHelpWindowsWhenStartingQuests;
+
         _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
         _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
         _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
+        _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "JobHudNotice", JobHudNoticePostSetup);
+        _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "Guide", GuidePostSetup);
+    }
+
+    private unsafe void CloseHelpWindowsWhenStartingQuests(object sender, QuestController.EAutomationType e)
+    {
+        if (e is QuestController.EAutomationType.Manual)
+            return;
+
+        if (_gameGui.TryGetAddonByName("Guide", out AtkUnitBase* addonGuide))
+        {
+            _logger.LogInformation("Guide window is open");
+            GuidePostSetup(addonGuide);
+        }
+
+        if (_gameGui.TryGetAddonByName("JobHudNotice", out AtkUnitBase* addonJobHudNotice))
+        {
+            _logger.LogInformation("JobHudNotice window is open");
+            JobHudNoticePostSetup(addonJobHudNotice);
+        }
     }
 
     private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
@@ -36,7 +65,7 @@ internal sealed class HelpUiController : IDisposable
 
     private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
     {
-        if (_questController.StartedQuest?.Quest.Id.Value == 245)
+        if (_questController.StartedQuest?.Quest.Id.Value is 245 or 3872)
         {
             _logger.LogInformation("Closing ContentsTutorial");
             AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
@@ -58,10 +87,38 @@ internal sealed class HelpUiController : IDisposable
         }
     }
 
+    private unsafe void JobHudNoticePostSetup(AddonEvent type, AddonArgs args)
+    {
+        if (_questController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual)
+            JobHudNoticePostSetup((AtkUnitBase*)args.Addon);
+    }
+
+    private unsafe void JobHudNoticePostSetup(AtkUnitBase* addon)
+    {
+        _logger.LogInformation("Clicking the JobHudNotice window to open the relevant Guide page");
+        addon->FireCallbackInt(0);
+    }
+
+    private unsafe void GuidePostSetup(AddonEvent type, AddonArgs args)
+    {
+        if (_questController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual)
+            GuidePostSetup((AtkUnitBase*)args.Addon);
+    }
+
+    private unsafe void GuidePostSetup(AtkUnitBase* addon)
+    {
+        _logger.LogInformation("Closing Guide window");
+        addon->FireCallbackInt(-1);
+    }
+
     public void Dispose()
     {
+        _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "Guide", GuidePostSetup);
+        _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "JobHudNotice", JobHudNoticePostSetup);
         _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
         _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
         _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
+
+        _questController.AutomationTypeChanged -= CloseHelpWindowsWhenStartingQuests;
     }
 }
index 949c19153037dac38afa98dcdca0960f9333c924..4099a17db6f24c99ab2a927613f2e442aae52f47 100644 (file)
@@ -28,6 +28,7 @@ internal abstract class MiniTaskController<T> : IDisposable
     private readonly ILogger<T> _logger;
 
     private readonly string _actionCanceledText;
+    private readonly string _cantExecuteDueToStatusText;
 
     protected MiniTaskController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider,
         InterruptHandler interruptHandler, IDataManager dataManager, ILogger<T> logger)
@@ -39,6 +40,7 @@ internal abstract class MiniTaskController<T> : IDisposable
         _condition = condition;
 
         _actionCanceledText = dataManager.GetString<LogMessage>(1314, x => x.Text)!;
+        _cantExecuteDueToStatusText = dataManager.GetString<LogMessage>(7728, x => x.Text)!;
         _interruptHandler.Interrupted += HandleInterruption;
     }
 
@@ -183,6 +185,19 @@ internal abstract class MiniTaskController<T> : IDisposable
         else
             _taskQueue.InterruptWith([new WaitAtEnd.WaitDelay()]);
 
+        LogTasksAfterInterruption();
+    }
+
+    private void InterruptWithoutCombat()
+    {
+        _logger.LogWarning("Interrupted, attempting to redo previous tasks (not in combat)");
+        _taskQueue.InterruptWith([new WaitAtEnd.WaitDelay()]);
+
+        LogTasksAfterInterruption();
+    }
+
+    private void LogTasksAfterInterruption()
+    {
         _logger.LogInformation("Remaining tasks after interruption:");
         foreach (ITask task in _taskQueue.RemainingTasks)
             _logger.LogInformation("- {TaskName}", task);
@@ -204,6 +219,8 @@ internal abstract class MiniTaskController<T> : IDisposable
                 !_condition[ConditionFlag.InFlight] &&
                 _taskQueue.CurrentTaskExecutor?.ShouldInterruptOnDamage() == true)
                 InterruptQueueWithCombat();
+            else if (GameFunctions.GameStringEquals(_cantExecuteDueToStatusText, message.TextValue))
+                InterruptWithoutCombat();
         }
     }
 
index 2c522556e2beee415696813b2ff1bf8d54dc8c8b..954bb5bea00db0447438eb908869ee818f454c03 100644 (file)
@@ -111,9 +111,13 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
             _logger.LogInformation("Setting automation type to {NewAutomationType} (previous: {OldAutomationType})",
                 value, _automationType);
             _automationType = value;
+            AutomationTypeChanged?.Invoke(this, value);
         }
     }
 
+    public delegate void AutomationTypeChangedEventHandler(object sender, EAutomationType e);
+    public event AutomationTypeChangedEventHandler? AutomationTypeChanged;
+
     public (QuestProgress Progress, ECurrentQuestType Type)? CurrentQuestDetails
     {
         get