using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Windowing; using QuestionableCompanion.Services; namespace QuestionableCompanion.Windows; public class ConfigWindow : Window, IDisposable { private readonly Configuration configuration; private readonly Plugin plugin; public ConfigWindow(Plugin plugin) : base("Questionable Companion Settings###QCSettings") { base.Size = new Vector2(600f, 400f); base.SizeCondition = ImGuiCond.FirstUseEver; this.plugin = plugin; configuration = plugin.Configuration; } public void Dispose() { } public override void PreDraw() { if (configuration.IsConfigWindowMovable) { base.Flags &= ~ImGuiWindowFlags.NoMove; } else { base.Flags |= ImGuiWindowFlags.NoMove; } } public override void Draw() { ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.8f, 0.2f, 1f)); ImGui.TextWrapped("Configuration Moved!"); ImGui.PopStyleColor(); ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); ImGui.TextWrapped("The configuration interface has been moved to the new Main Window for a better user experience."); ImGui.Spacing(); ImGui.TextWrapped("All settings are now available in the Main Window with improved organization and features:"); ImGui.Spacing(); ImGui.BulletText("Quest Rotation Management"); ImGui.BulletText("Event Quest Automation"); ImGui.BulletText("MSQ Progress Tracking"); ImGui.BulletText("DC Travel Configuration"); ImGui.BulletText("Advanced Settings"); ImGui.BulletText("And much more!"); ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); if (ImGui.Button(size: new Vector2(ImGui.GetContentRegionAvail().X, 50f), label: "Open Main Window (Settings Tab)")) { plugin.ToggleMainUi(); base.IsOpen = false; } ImGui.Spacing(); ImGui.Spacing(); ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0.7f, 0.7f, 0.7f, 1f)); ImGui.TextWrapped("This legacy configuration window will be removed in a future update."); ImGui.TextWrapped("Please use the Main Window for all configuration needs."); ImGui.PopStyleColor(); } private void DrawGeneralTab() { ImGui.Text("General Settings"); ImGui.Separator(); ImGui.Spacing(); bool autoStart = configuration.AutoStartOnLogin; if (ImGui.Checkbox("Auto-start on login", ref autoStart)) { configuration.AutoStartOnLogin = autoStart; configuration.Save(); } bool dryRun = configuration.EnableDryRun; if (ImGui.Checkbox("Enable Dry Run Mode (simulate without executing)", ref dryRun)) { configuration.EnableDryRun = dryRun; configuration.Save(); } bool restoreState = configuration.RestoreStateOnLoad; if (ImGui.Checkbox("Restore state on plugin load", ref restoreState)) { configuration.RestoreStateOnLoad = restoreState; configuration.Save(); } ImGui.Spacing(); ImGui.Text("Execution Settings"); ImGui.Separator(); ImGui.Spacing(); int maxRetries = configuration.MaxRetryAttempts; if (ImGui.SliderInt("Max retry attempts", ref maxRetries, 1, 10)) { configuration.MaxRetryAttempts = maxRetries; configuration.Save(); } int switchDelay = configuration.CharacterSwitchDelay; if (ImGui.SliderInt("Character switch delay (seconds)", ref switchDelay, 3, 15)) { configuration.CharacterSwitchDelay = switchDelay; configuration.Save(); } ImGui.Spacing(); ImGui.Text("Logging Settings"); ImGui.Separator(); ImGui.Spacing(); int maxLogs = configuration.MaxLogEntries; if (ImGui.SliderInt("Max log entries", ref maxLogs, 50, 500)) { configuration.MaxLogEntries = maxLogs; configuration.Save(); } bool showDebug = configuration.ShowDebugLogs; if (ImGui.Checkbox("Show debug logs", ref showDebug)) { configuration.ShowDebugLogs = showDebug; configuration.Save(); } bool logToFile = configuration.LogToFile; if (ImGui.Checkbox("Log to file", ref logToFile)) { configuration.LogToFile = logToFile; configuration.Save(); } ImGui.Spacing(); ImGui.Text("UI Settings"); ImGui.Separator(); ImGui.Spacing(); bool movable = configuration.IsConfigWindowMovable; if (ImGui.Checkbox("Movable config window", ref movable)) { configuration.IsConfigWindowMovable = movable; configuration.Save(); } } private void DrawAdvancedFeaturesTab() { ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Submarine Management"); ImGui.Separator(); ImGui.Spacing(); bool enableSubmarineCheck = configuration.EnableSubmarineCheck; if (ImGui.Checkbox("Enable Submarine Monitoring", ref enableSubmarineCheck)) { configuration.EnableSubmarineCheck = enableSubmarineCheck; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Automatically monitor submarines and pause quest rotation when submarines are ready"); } if (configuration.EnableSubmarineCheck) { ImGui.Indent(); int submarineCheckInterval = configuration.SubmarineCheckInterval; if (ImGui.SliderInt("Check Interval (seconds)", ref submarineCheckInterval, 30, 300)) { configuration.SubmarineCheckInterval = submarineCheckInterval; configuration.Save(); } int submarineReloginCooldown = configuration.SubmarineReloginCooldown; if (ImGui.SliderInt("Cooldown after Relog (seconds)", ref submarineReloginCooldown, 60, 300)) { configuration.SubmarineReloginCooldown = submarineReloginCooldown; configuration.Save(); } int submarineWaitTime = configuration.SubmarineWaitTime; if (ImGui.SliderInt("Wait time before submarine (seconds)", ref submarineWaitTime, 10, 120)) { configuration.SubmarineWaitTime = submarineWaitTime; configuration.Save(); } ImGui.Unindent(); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Movement Monitor"); ImGui.Separator(); ImGui.Spacing(); bool enableMovementMonitor = configuration.EnableMovementMonitor; if (ImGui.Checkbox("Enable Movement Monitor", ref enableMovementMonitor)) { configuration.EnableMovementMonitor = enableMovementMonitor; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Automatically detect if player is stuck and send /qst reload"); } if (configuration.EnableMovementMonitor) { ImGui.Indent(); int movementCheckInterval = configuration.MovementCheckInterval; if (ImGui.SliderInt("Check Interval (seconds)##movement", ref movementCheckInterval, 3, 30)) { configuration.MovementCheckInterval = movementCheckInterval; configuration.Save(); } int movementStuckThreshold = configuration.MovementStuckThreshold; if (ImGui.SliderInt("Stuck Threshold (seconds)", ref movementStuckThreshold, 15, 120)) { configuration.MovementStuckThreshold = movementStuckThreshold; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Time without movement before sending /qst reload"); } ImGui.Unindent(); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(1f, 0.5f, 0.3f, 1f), "Combat Handling"); ImGui.Separator(); ImGui.Spacing(); bool enableCombatHandling = configuration.EnableCombatHandling; if (ImGui.Checkbox("Enable Combat Handling", ref enableCombatHandling)) { configuration.EnableCombatHandling = enableCombatHandling; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Automatically enable RSR/VBMAI/BMRAI when HP drops below threshold during combat"); } if (configuration.EnableCombatHandling) { ImGui.Indent(); int combatHPThreshold = configuration.CombatHPThreshold; if (ImGui.SliderInt("HP Threshold (%)", ref combatHPThreshold, 1, 99)) { configuration.CombatHPThreshold = combatHPThreshold; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Enable combat automation when HP drops below this percentage\nCommands: /rsr manual, /vbmai on, /bmrai on"); } ImGui.TextWrapped("When HP drops below threshold:"); ImGui.BulletText("/rsr manual"); ImGui.BulletText("/vbmai on"); ImGui.BulletText("/bmrai on"); ImGui.Spacing(); ImGui.TextWrapped("When combat ends:"); ImGui.BulletText("/rsr off"); ImGui.BulletText("/vbmai off"); ImGui.BulletText("/bmrai off"); ImGui.Unindent(); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(1f, 0.3f, 0.3f, 1f), "Death Handling"); ImGui.Separator(); ImGui.Spacing(); bool enableDeathHandling = configuration.EnableDeathHandling; if (ImGui.Checkbox("Enable Death Handling", ref enableDeathHandling)) { configuration.EnableDeathHandling = enableDeathHandling; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Automatically respawn when player dies"); } if (configuration.EnableDeathHandling) { ImGui.Indent(); int deathRespawnDelay = configuration.DeathRespawnDelay; if (ImGui.SliderInt("Teleport Delay (seconds)", ref deathRespawnDelay, 1, 30)) { configuration.DeathRespawnDelay = deathRespawnDelay; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Time to wait after respawn before teleporting back to death location"); } ImGui.Spacing(); ImGui.TextWrapped("On Death:"); ImGui.BulletText("Detect 0% HP"); ImGui.BulletText("Save position & territory"); ImGui.BulletText("Auto-click SelectYesNo (respawn)"); ImU8String text = new ImU8String(13, 1); text.AppendLiteral("Wait "); text.AppendFormatted(configuration.DeathRespawnDelay); text.AppendLiteral(" seconds"); ImGui.BulletText(text); ImGui.BulletText("Teleport back to death location"); ImGui.Unindent(); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Logging Settings"); ImGui.Separator(); ImGui.Spacing(); bool logToDalamud = configuration.LogToDalamud; if (ImGui.Checkbox("Log to Dalamud Log", ref logToDalamud)) { configuration.LogToDalamud = logToDalamud; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Enable to also log to Dalamud log (can cause spam)"); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Dungeon Automation"); ImGui.Separator(); ImGui.Spacing(); bool enableAutoDutyUnsynced = configuration.EnableAutoDutyUnsynced; if (ImGui.Checkbox("Enable AutoDuty Unsynced", ref enableAutoDutyUnsynced)) { configuration.EnableAutoDutyUnsynced = enableAutoDutyUnsynced; configuration.Save(); plugin.GetDungeonAutomation()?.SetDutyModeBasedOnConfig(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Run dungeons unsynced for faster completion"); } if (configuration.EnableAutoDutyUnsynced) { ImGui.Indent(); int autoDutyPartySize = configuration.AutoDutyPartySize; if (ImGui.SliderInt("Party Size Check (members)", ref autoDutyPartySize, 1, 8)) { configuration.AutoDutyPartySize = autoDutyPartySize; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Minimum party size required before starting dungeon"); } int autoDutyMaxWaitForParty = configuration.AutoDutyMaxWaitForParty; if (ImGui.SliderInt("Max Wait for Party (seconds)", ref autoDutyMaxWaitForParty, 10, 120)) { configuration.AutoDutyMaxWaitForParty = autoDutyMaxWaitForParty; configuration.Save(); } int autoDutyReInviteInterval = configuration.AutoDutyReInviteInterval; if (ImGui.SliderInt("Re-invite Interval (seconds)", ref autoDutyReInviteInterval, 5, 60)) { configuration.AutoDutyReInviteInterval = autoDutyReInviteInterval; configuration.Save(); } ImGui.Unindent(); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Quest Automation"); ImGui.Separator(); ImGui.Spacing(); bool enableQSTReloadTracking = configuration.EnableQSTReloadTracking; if (ImGui.Checkbox("Enable QST Reload Tracking", ref enableQSTReloadTracking)) { configuration.EnableQSTReloadTracking = enableQSTReloadTracking; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Track Questionable reloads and switch character if too many reloads occur"); } if (configuration.EnableQSTReloadTracking) { ImGui.Indent(); int maxQSTReloadsBeforeSwitch = configuration.MaxQSTReloadsBeforeSwitch; if (ImGui.SliderInt("Max Reloads before switch", ref maxQSTReloadsBeforeSwitch, 1, 20)) { configuration.MaxQSTReloadsBeforeSwitch = maxQSTReloadsBeforeSwitch; configuration.Save(); } ImGui.Unindent(); } ImGui.Spacing(); ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Character Management"); ImGui.Separator(); ImGui.Spacing(); bool enableMultiModeAfterRotation = configuration.EnableMultiModeAfterRotation; if (ImGui.Checkbox("Enable Multi-Mode after Rotation", ref enableMultiModeAfterRotation)) { configuration.EnableMultiModeAfterRotation = enableMultiModeAfterRotation; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Enable AutoRetainer Multi-Mode after completing character rotation"); } bool returnToHomeworldOnStopQuest = configuration.ReturnToHomeworldOnStopQuest; if (ImGui.Checkbox("Return to Homeworld on Stop Quest", ref returnToHomeworldOnStopQuest)) { configuration.ReturnToHomeworldOnStopQuest = returnToHomeworldOnStopQuest; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Automatically return to homeworld when stop quest is completed"); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Safe Wait Settings"); ImGui.Separator(); ImGui.Spacing(); bool enableSafeWaitBefore = configuration.EnableSafeWaitBeforeCharacterSwitch; if (ImGui.Checkbox("Enable Safe Wait Before Character Switch", ref enableSafeWaitBefore)) { configuration.EnableSafeWaitBeforeCharacterSwitch = enableSafeWaitBefore; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Wait for character to stabilize (movement, actions) before switching"); } bool enableSafeWaitAfter = configuration.EnableSafeWaitAfterCharacterSwitch; if (ImGui.Checkbox("Enable Safe Wait After Character Switch", ref enableSafeWaitAfter)) { configuration.EnableSafeWaitAfterCharacterSwitch = enableSafeWaitAfter; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Wait for character to fully load after switching"); } ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "Quest Pre-Check"); ImGui.Separator(); ImGui.Spacing(); bool enableQuestPreCheck = configuration.EnableQuestPreCheck; if (ImGui.Checkbox("Enable Quest Pre-Check", ref enableQuestPreCheck)) { configuration.EnableQuestPreCheck = enableQuestPreCheck; configuration.Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Check quest completion status before starting rotation to skip completed characters"); } ImGui.TextWrapped("Quest Pre-Check scans all characters for completed quests before rotation starts, preventing unnecessary character switches."); ImGui.Spacing(); ImGui.Spacing(); ImGui.TextColored(new Vector4(0.3f, 0.8f, 1f, 1f), "DC Travel World Selector"); ImGui.Separator(); ImGui.Spacing(); ImGui.TextWrapped("Configure world travel for Data Center travel quests. Requires Lifestream plugin."); ImGui.Spacing(); string[] datacenters = configuration.WorldsByDatacenter.Keys.ToArray(); int currentDCIndex = Array.IndexOf(datacenters, configuration.SelectedDatacenter); if (currentDCIndex < 0) { currentDCIndex = 0; } ImGui.Text("Select Datacenter:"); if (ImGui.Combo((ImU8String)"##DCSelector", ref currentDCIndex, (ReadOnlySpan)datacenters, datacenters.Length)) { configuration.SelectedDatacenter = datacenters[currentDCIndex]; if (configuration.WorldsByDatacenter.TryGetValue(configuration.SelectedDatacenter, out List newWorlds) && newWorlds.Count > 0) { configuration.DCTravelWorld = newWorlds[0]; } configuration.Save(); } ImGui.Spacing(); if (configuration.WorldsByDatacenter.TryGetValue(configuration.SelectedDatacenter, out List worlds)) { string[] worldArray = worlds.ToArray(); int currentWorldIndex = Array.IndexOf(worldArray, configuration.DCTravelWorld); if (currentWorldIndex < 0) { currentWorldIndex = 0; } ImGui.Text("Select Target World:"); if (ImGui.Combo((ImU8String)"##WorldSelector", ref currentWorldIndex, (ReadOnlySpan)worldArray, worldArray.Length)) { configuration.DCTravelWorld = worldArray[currentWorldIndex]; configuration.EnableDCTravel = !string.IsNullOrEmpty(configuration.DCTravelWorld); configuration.Save(); } ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); LifestreamIPC lifestreamIPC = Plugin.Instance?.LifestreamIPC; if (lifestreamIPC != null && !lifestreamIPC.IsAvailable) { lifestreamIPC.ForceCheckAvailability(); } bool lifestreamAvailable = lifestreamIPC?.IsAvailable ?? false; if (!lifestreamAvailable) { ImGui.BeginDisabled(); } bool enableDCTravel = configuration.EnableDCTravel; if (ImGui.Checkbox("Enable DC Travel", ref enableDCTravel)) { configuration.EnableDCTravel = enableDCTravel; configuration.Save(); } if (!lifestreamAvailable) { ImGui.EndDisabled(); } if (ImGui.IsItemHovered()) { if (!lifestreamAvailable) { ImGui.SetTooltip("Lifestream plugin is required for DC Travel!\nPlease install and enable Lifestream to use this feature."); } else { ImGui.SetTooltip("Enable automatic DC travel when DC travel quests are detected"); } } if (!lifestreamAvailable) { ImGui.Spacing(); ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "⚠\ufe0f Lifestream plugin not available!"); ImGui.TextWrapped("DC Travel requires Lifestream to be installed and enabled."); } ImGui.Spacing(); if (configuration.EnableDCTravel && !string.IsNullOrEmpty(configuration.DCTravelWorld)) { Vector4 col = new Vector4(0.2f, 1f, 0.2f, 1f); ImU8String text2 = new ImU8String(21, 1); text2.AppendLiteral("✓ DC Travel ACTIVE → "); text2.AppendFormatted(configuration.DCTravelWorld); ImGui.TextColored(in col, text2); ImU8String text3 = new ImU8String(100, 1); text3.AppendLiteral("Character will travel to "); text3.AppendFormatted(configuration.DCTravelWorld); text3.AppendLiteral(" immediately after login, then return to homeworld before character switch."); ImGui.TextWrapped(text3); } else if (!configuration.EnableDCTravel) { ImGui.TextColored(new Vector4(0.7f, 0.7f, 0.7f, 1f), "○ DC Travel disabled"); } else { ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "⚠ DC Travel enabled but no world selected!"); } } else { ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "No worlds available for selected datacenter"); } } }