qstbak/QuestionableCompanion/QuestionableCompanion.Windows/ConfigWindow.cs
2025-12-04 04:39:08 +10:00

564 lines
19 KiB
C#

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<string>)datacenters, datacenters.Length))
{
configuration.SelectedDatacenter = datacenters[currentDCIndex];
if (configuration.WorldsByDatacenter.TryGetValue(configuration.SelectedDatacenter, out List<string> newWorlds) && newWorlds.Count > 0)
{
configuration.DCTravelWorld = newWorlds[0];
}
configuration.Save();
}
ImGui.Spacing();
if (configuration.WorldsByDatacenter.TryGetValue(configuration.SelectedDatacenter, out List<string> 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<string>)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");
}
}
}