diff --git a/TextAdvance/Config.cs b/TextAdvance/Config.cs index 64f191b..2441217 100644 --- a/TextAdvance/Config.cs +++ b/TextAdvance/Config.cs @@ -3,6 +3,13 @@ namespace TextAdvance; +public enum RequestFillQualityPreference +{ + NQ = 0, // Prefer Normal Quality items over HQ + HQ = 1, // Prefer High Quality items over NQ + Any = 2 // Use first available (game default) +} + [Serializable] public class Config : IEzConfig { @@ -100,6 +107,15 @@ public bool GetEnableRequestFill() } return this.MainConfig.EnableRequestFill; } + public RequestFillQualityPreference GetRequestFillQualityPreference() + { + if (S.IPCProvider.IsInExternalControl()) return S.IPCProvider.ExternalConfig.Merge(this.MainConfig).RequestFillQualityPreference; + if (!(this.GlobalOverridesLocal && P.Enabled) && this.TerritoryConditions.TryGetValue(Svc.ClientState.TerritoryType, out var val)) + { + return val.RequestFillQualityPreference; + } + return this.MainConfig.RequestFillQualityPreference; + } public Vector4 GetQTAQuestColor() @@ -186,6 +202,7 @@ public class TerritoryConfig public bool EnableCutsceneSkipConfirm = true; public bool EnableTalkSkip = true; public bool EnableRequestFill = true; + public RequestFillQualityPreference RequestFillQualityPreference = RequestFillQualityPreference.Any; public bool EnableAutoInteract = false; public bool QTIQuestEnabled = true; @@ -222,6 +239,7 @@ public class ExternalTerritoryConfig public bool? EnableCutsceneSkipConfirm = null; public bool? EnableTalkSkip = null; public bool? EnableRequestFill = null; + public RequestFillQualityPreference? RequestFillQualityPreference = null; public bool? EnableAutoInteract = null; public TerritoryConfig Merge(TerritoryConfig other) @@ -237,6 +255,7 @@ public TerritoryConfig Merge(TerritoryConfig other) ret.EnableCutsceneSkipConfirm = this.EnableCutsceneSkipConfirm ?? other.EnableCutsceneSkipConfirm; ret.EnableTalkSkip = this.EnableTalkSkip ?? other.EnableTalkSkip; ret.EnableRequestFill = this.EnableRequestFill ?? other.EnableRequestFill; + ret.RequestFillQualityPreference = this.RequestFillQualityPreference ?? other.RequestFillQualityPreference; ret.EnableAutoInteract = this.EnableAutoInteract ?? other.EnableAutoInteract; return ret; } diff --git a/TextAdvance/Executors/ExecRequestFill.cs b/TextAdvance/Executors/ExecRequestFill.cs index b552b5c..39f1983 100644 --- a/TextAdvance/Executors/ExecRequestFill.cs +++ b/TextAdvance/Executors/ExecRequestFill.cs @@ -11,16 +11,16 @@ namespace TextAdvance.Executors; internal static unsafe class ExecRequestFill { private static bool active = false; - private static List SlotsFilled { get; set; } = []; - private static TaskManager TaskManager => P.TaskManager; public static bool DontFillThisWindow = false; + internal static void Tick() { if (TryGetAddonByName("Request", out var addon) && IsAddonReady((AtkUnitBase*)addon)) { if (DontFillThisWindow) return; + for (var i = 1; i <= addon->EntryCount; i++) { active = true; @@ -52,16 +52,39 @@ internal static void Tick() if (contextMenu is null || !contextMenu->IsVisible) { var slot = i - 1; - var unk = 44 * i + (i - 1); Callback.Fire(&addon->AtkUnitBase, false, 2, slot, 0, 0); - return false; } else { - Callback.Fire(contextMenu, false, 0, 0, 1021003, 0, 0); - PluginLog.Debug($"Filled slot {i}"); + var contextIconMenu = (AddonContextIconMenu*)contextMenu; + var entryCount = contextIconMenu->EntryCount; + + // Determine which option to select based on quality preference + var qualityPref = C.GetRequestFillQualityPreference(); + int selectedIndex = 0; // Default to first option + + if (entryCount > 1 && qualityPref == RequestFillQualityPreference.HQ) + { + // When both NQ and HQ exist, game lists HQ first (index 0), NQ second (index 1) + selectedIndex = 0; // Select HQ + PluginLog.Debug($"Slot {i}: {entryCount} qualities, selecting HQ (index {selectedIndex})"); + } + else if (entryCount > 1 && qualityPref == RequestFillQualityPreference.NQ) + { + selectedIndex = 1; // Select NQ (second option when both available) + PluginLog.Debug($"Slot {i}: {entryCount} qualities, selecting NQ (index {selectedIndex})"); + } + else + { + // Any quality or only one option available - use first + selectedIndex = 0; + PluginLog.Debug($"Slot {i}: Using first available option (index {selectedIndex})"); + } + + // Fire callback to select item from context menu. + Callback.Fire(contextMenu, false, 0, selectedIndex, 1021003, 0, 0); SlotsFilled.Add(i); return true; } diff --git a/TextAdvance/Gui/TabConfig.cs b/TextAdvance/Gui/TabConfig.cs index d2ea25c..e440150 100644 --- a/TextAdvance/Gui/TabConfig.cs +++ b/TextAdvance/Gui/TabConfig.cs @@ -78,6 +78,24 @@ internal static void Draw() ImGuiComponents.HelpMarker("Automatically confirms most of item requests once filled. Some requests may not be automatically confirmed."); ImGui.Checkbox("Automatic request fill (RF)", ref C.MainConfig.EnableRequestFill); ImGuiComponents.HelpMarker("Automatically fills item request window. Some requests may not be automatically filled."); + if (C.MainConfig.EnableRequestFill) + { + ImGui.Indent(); + ImGui.SetNextItemWidth(200f); + if (ImGui.BeginCombo("##RequestFillQuality", C.MainConfig.RequestFillQualityPreference.ToString())) + { + if (ImGui.Selectable("NQ", C.MainConfig.RequestFillQualityPreference == RequestFillQualityPreference.NQ)) + C.MainConfig.RequestFillQualityPreference = RequestFillQualityPreference.NQ; + if (ImGui.Selectable("HQ", C.MainConfig.RequestFillQualityPreference == RequestFillQualityPreference.HQ)) + C.MainConfig.RequestFillQualityPreference = RequestFillQualityPreference.HQ; + if (ImGui.Selectable("Any", C.MainConfig.RequestFillQualityPreference == RequestFillQualityPreference.Any)) + C.MainConfig.RequestFillQualityPreference = RequestFillQualityPreference.Any; + ImGui.EndCombo(); + } + ImGui.SameLine(); + ImGuiComponents.HelpMarker("NQ: Use Normal Quality items first, preserving valuable HQ items.\nHQ: Use High Quality items first.\nAny: Use first available (game default)."); + ImGui.Unindent(); + } ImGui.Checkbox("Automatic ESC press during cutscene (CS)", ref C.MainConfig.EnableCutsceneEsc); ImGuiComponents.HelpMarker("Automatically presses ESC key during cutscene when cutscene is skippable. Does not skips normally unskippable cutscenes."); ImGui.Checkbox("Automatic cutscene skip confirmation (CC)", ref C.MainConfig.EnableCutsceneSkipConfirm); diff --git a/TextAdvance/Services/IPCProvider.cs b/TextAdvance/Services/IPCProvider.cs index 7942ddb..029bddd 100644 --- a/TextAdvance/Services/IPCProvider.cs +++ b/TextAdvance/Services/IPCProvider.cs @@ -50,6 +50,7 @@ public bool IsInExternalControl() [EzIPC] public bool GetEnableCutsceneSkipConfirm() => C.GetEnableCutsceneSkipConfirm(); [EzIPC] public bool GetEnableRequestHandin() => C.GetEnableRequestHandin(); [EzIPC] public bool GetEnableRequestFill() => C.GetEnableRequestFill(); + [EzIPC] public RequestFillQualityPreference GetRequestFillQualityPreference() => C.GetRequestFillQualityPreference(); [EzIPC] public bool GetEnableTalkSkip() => C.GetEnableTalkSkip(); [EzIPC] public bool GetEnableAutoInteract() => C.GetEnableAutoInteract(); [EzIPC] public bool IsPaused() => P.BlockList.Count != 0; diff --git a/TextAdvance/Services/IPCTester.cs b/TextAdvance/Services/IPCTester.cs index 14524b2..ca9fa86 100644 --- a/TextAdvance/Services/IPCTester.cs +++ b/TextAdvance/Services/IPCTester.cs @@ -44,6 +44,7 @@ public class IPCTester [EzIPC] public Func GetEnableCutsceneSkipConfirm; [EzIPC] public Func GetEnableRequestHandin; [EzIPC] public Func GetEnableRequestFill; + [EzIPC] public Func GetRequestFillQualityPreference; [EzIPC] public Func GetEnableTalkSkip; [EzIPC] public Func GetEnableAutoInteract;