Don't try queueing for duties when ilvl is too low
authorLiza Carvelli <liza@carvel.li>
Fri, 3 Jan 2025 00:46:31 +0000 (01:46 +0100)
committerLiza Carvelli <liza@carvel.li>
Fri, 3 Jan 2025 00:46:31 +0000 (01:46 +0100)
Questionable/Controller/Steps/Common/SendNotification.cs
Questionable/Controller/Steps/Interactions/Duty.cs
Questionable/Data/TerritoryData.cs
Questionable/External/AutoDutyIpc.cs
Questionable/QuestionablePlugin.cs
Questionable/Windows/ConfigWindow.cs

index e83a1186c29313d366ba5093dba7de1caaf0461a..cf116028ebc9aaa7ee5a6f8c0efc4f9da65bfadc 100644 (file)
@@ -24,7 +24,7 @@ internal static class SendNotification
                     new Task(step.InteractionType, step.Comment),
                 EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled) =>
                     new Task(step.InteractionType, step.ContentFinderConditionId.HasValue
-                        ? territoryData.GetContentFinderConditionName(step.ContentFinderConditionId.Value)
+                        ? territoryData.GetContentFinderCondition(step.ContentFinderConditionId.Value)?.Name
                         : step.Comment),
                 EInteractionType.SinglePlayerDuty => new Task(step.InteractionType, quest.Info.Name),
                 _ => null,
index fab5c6ed30a9a59b29b5661bb3effd86672303b9..5e20accf0641d94ce87fba92e4776b7ef45fa36c 100644 (file)
@@ -2,6 +2,9 @@
 using System.Collections.Generic;
 using Dalamud.Game.ClientState.Conditions;
 using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using LLib.Gear;
+using Questionable.Controller.Steps.Common;
 using Questionable.Controller.Steps.Shared;
 using Questionable.Data;
 using Questionable.External;
@@ -41,23 +44,54 @@ internal static class Duty
     }
 
     internal sealed class StartAutoDutyExecutor(
+        GearStatsCalculator gearStatsCalculator,
         AutoDutyIpc autoDutyIpc,
         TerritoryData territoryData,
-        IClientState clientState) : TaskExecutor<StartAutoDutyTask>
+        IClientState clientState,
+        IChatGui chatGui,
+        SendNotification.Executor sendNotificationExecutor) : TaskExecutor<StartAutoDutyTask>
     {
         protected override bool Start()
         {
+            if (!territoryData.TryGetContentFinderCondition(Task.ContentFinderConditionId,
+                    out var cfcData))
+                throw new TaskException("Failed to get territory ID for content finder condition");
+
+            unsafe
+            {
+                InventoryManager* inventoryManager = InventoryManager.Instance();
+                if (inventoryManager == null)
+                    throw new TaskException("Inventory unavailable");
+
+                var equippedItems = inventoryManager->GetInventoryContainer(InventoryType.EquippedItems);
+                if (equippedItems == null)
+                    throw new TaskException("Equipped items unavailable");
+
+                var currentItemLevel = gearStatsCalculator.CalculateAverageItemLevel(equippedItems);
+                if (cfcData.RequiredItemLevel > currentItemLevel)
+                {
+                    string errorText =
+                        $"Could not use AutoDuty to queue for {cfcData.Name}, required item level: {cfcData.RequiredItemLevel}, current item level: {currentItemLevel}.";
+                    if (!sendNotificationExecutor.Start(new SendNotification.Task(EInteractionType.Duty, errorText)))
+                        chatGui.PrintError(errorText, CommandHandler.MessageTag, CommandHandler.TagColor);
+
+                    return false;
+                }
+            }
+
             autoDutyIpc.StartInstance(Task.ContentFinderConditionId);
             return true;
         }
 
         public override ETaskResult Update()
         {
-            if (!territoryData.TryGetTerritoryIdForContentFinderCondition(Task.ContentFinderConditionId,
-                    out uint territoryId))
+            if (!territoryData.TryGetContentFinderCondition(Task.ContentFinderConditionId,
+                    out var cfcData))
                 throw new TaskException("Failed to get territory ID for content finder condition");
 
-            return clientState.TerritoryType == territoryId ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
+            return clientState.TerritoryType == cfcData.TerritoryId
+                ? ETaskResult.TaskComplete
+                : ETaskResult.StillRunning;
         }
     }
 
@@ -75,11 +109,11 @@ internal static class Duty
 
         public override ETaskResult Update()
         {
-            if (!territoryData.TryGetTerritoryIdForContentFinderCondition(Task.ContentFinderConditionId,
-                    out uint territoryId))
+            if (!territoryData.TryGetContentFinderCondition(Task.ContentFinderConditionId,
+                    out var cfcData))
                 throw new TaskException("Failed to get territory ID for content finder condition");
 
-            return clientState.TerritoryType != territoryId && autoDutyIpc.IsStopped()
+            return clientState.TerritoryType != cfcData.TerritoryId && autoDutyIpc.IsStopped()
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
         }
index 970718b2fcfab02722641e25137b268b74ad5267..f269b138abd8d0584efecd3276cfcec034cfd261 100644 (file)
@@ -1,12 +1,11 @@
 using System;
-using System.Collections.Generic;
 using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Linq;
 using Dalamud.Game;
 using Dalamud.Plugin.Services;
 using Dalamud.Utility;
-using FFXIVClientStructs.FFXIV.Client.Game.Character;
 using Lumina.Excel.Sheets;
 
 namespace Questionable.Data;
@@ -17,8 +16,7 @@ internal sealed class TerritoryData
     private readonly ImmutableHashSet<ushort> _territoriesWithMount;
     private readonly ImmutableDictionary<ushort, uint> _dutyTerritories;
     private readonly ImmutableDictionary<uint, string> _instanceNames;
-    private readonly ImmutableDictionary<uint, string> _contentFinderConditionNames;
-    private readonly ImmutableDictionary<uint, uint> _contentFinderConditionIds;
+    private readonly ImmutableDictionary<uint, ContentFinderConditionData> _contentFinderConditions;
 
     public TerritoryData(IDataManager dataManager)
     {
@@ -46,12 +44,10 @@ internal sealed class TerritoryData
             .Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
             .ToImmutableDictionary(x => x.Content.RowId, x => x.Name.ToDalamudString().ToString());
 
-        _contentFinderConditionNames = dataManager.GetExcelSheet<ContentFinderCondition>()
+        _contentFinderConditions = dataManager.GetExcelSheet<ContentFinderCondition>()
             .Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
-            .ToImmutableDictionary(x => x.RowId, x => FixName(x.Name.ToDalamudString().ToString(), dataManager.Language));
-        _contentFinderConditionIds = dataManager.GetExcelSheet<ContentFinderCondition>()
-            .Where(x => x.RowId > 0 && x.Content.RowId != 0 && x.ContentLinkType == 1 && x.ContentType.RowId != 6)
-            .ToImmutableDictionary(x => x.RowId, x => x.TerritoryType.RowId);
+            .Select(x => new ContentFinderConditionData(x, dataManager.Language))
+            .ToImmutableDictionary(x => x.ContentFinderConditionId, x => x);
     }
 
     public string? GetName(ushort territoryId) => _territoryNames.GetValueOrDefault(territoryId);
@@ -74,10 +70,12 @@ internal sealed class TerritoryData
 
     public string? GetInstanceName(ushort instanceId) => _instanceNames.GetValueOrDefault(instanceId);
 
-    public string? GetContentFinderConditionName(uint cfcId) => _contentFinderConditionNames.GetValueOrDefault(cfcId);
+    public ContentFinderConditionData? GetContentFinderCondition(uint cfcId) =>
+        _contentFinderConditions.GetValueOrDefault(cfcId);
 
-    public bool TryGetTerritoryIdForContentFinderCondition(uint cfcId, out uint territoryId) =>
-        _contentFinderConditionIds.TryGetValue(cfcId, out territoryId);
+    public bool TryGetContentFinderCondition(uint cfcId,
+        [NotNullWhen(true)] out ContentFinderConditionData? contentFinderConditionData) =>
+        _contentFinderConditions.TryGetValue(cfcId, out contentFinderConditionData);
 
     private static string FixName(string name, ClientLanguage language)
     {
@@ -86,4 +84,17 @@ internal sealed class TerritoryData
 
         return string.Concat(name[0].ToString().ToUpper(CultureInfo.InvariantCulture), name.AsSpan(1));
     }
+
+    public sealed record ContentFinderConditionData(
+        uint ContentFinderConditionId,
+        string Name,
+        uint TerritoryId,
+        ushort RequiredItemLevel)
+    {
+        public ContentFinderConditionData(ContentFinderCondition condition, ClientLanguage clientLanguage)
+            : this(condition.RowId, FixName(condition.Name.ToDalamudString().ToString(), clientLanguage),
+                condition.TerritoryType.RowId, condition.ItemLevelRequired)
+        {
+        }
+    }
 }
index 1089efcc3337e1732709701f70cf8f45fb2dccd1..71bae7fd16ac8fd08e2e75f4336d1b8e92712354 100644 (file)
@@ -38,7 +38,7 @@ internal sealed class AutoDutyIpc
             return false;
 
         if (_configuration.Duties.WhitelistedDutyCfcIds.Contains(cfcId.Value) &&
-            _territoryData.TryGetTerritoryIdForContentFinderCondition(cfcId.Value, out _))
+            _territoryData.TryGetContentFinderCondition(cfcId.Value, out _))
             return true;
 
         return autoDutyEnabled && HasPath(cfcId.Value);
@@ -46,28 +46,28 @@ internal sealed class AutoDutyIpc
 
     public bool HasPath(uint cfcId)
     {
-        if (!_territoryData.TryGetTerritoryIdForContentFinderCondition(cfcId, out uint territoryType))
+        if (!_territoryData.TryGetContentFinderCondition(cfcId, out var cfcData))
             return false;
 
         try
         {
-            return _contentHasPath.InvokeFunc(territoryType);
+            return _contentHasPath.InvokeFunc(cfcData.TerritoryId);
         }
         catch (IpcError e)
         {
-            _logger.LogWarning("Unable to query AutoDuty for path in territory {TerritoryType}: {Message}", territoryType, e.Message);
+            _logger.LogWarning("Unable to query AutoDuty for path in territory {TerritoryType}: {Message}", cfcData.TerritoryId, e.Message);
             return false;
         }
     }
 
     public void StartInstance(uint cfcId)
     {
-        if (!_territoryData.TryGetTerritoryIdForContentFinderCondition(cfcId, out uint territoryType))
+        if (!_territoryData.TryGetContentFinderCondition(cfcId, out var cfcData))
             throw new TaskException($"Unknown ContentFinderConditionId {cfcId}");
 
         try
         {
-            _run.InvokeAction(territoryType, 0, true);
+            _run.InvokeAction(cfcData.TerritoryId, 0, true);
         }
         catch (IpcError e)
         {
index f5fa51ee11a405519fa32e55b66aabad2c782851..a4b5bae9e166f83cd2d68ddbe8a79a937740c232 100644 (file)
@@ -7,6 +7,7 @@ using Dalamud.Interface.Windowing;
 using Dalamud.Plugin;
 using Dalamud.Plugin.Services;
 using LLib;
+using LLib.Gear;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller;
@@ -130,6 +131,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<NotificationMasterIpc>();
         serviceCollection.AddSingleton<AutomatonIpc>();
         serviceCollection.AddSingleton<AutoDutyIpc>();
+
+        serviceCollection.AddSingleton<GearStatsCalculator>();
     }
 
     private static void AddTaskFactories(ServiceCollection serviceCollection)
index 397faeba416de2c45e982dfd1958562c333cee63..131f3726d6c4b130ce3e3adea26716bd0e3f1a7c 100644 (file)
@@ -99,7 +99,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
             {
                 Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId,
                 CfcId = x.RowId,
-                Name = territoryData.GetContentFinderConditionName(x.RowId) ?? "?",
+                Name = territoryData.GetContentFinderCondition(x.RowId)?.Name ?? "?",
                 TerritoryId = x.TerritoryType.RowId,
                 ContentType = x.ContentType.RowId,
                 Level = x.ClassJobLevelRequired,