3368 lines
123 KiB
C#
3368 lines
123 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Colors;
|
|
using Dalamud.Interface.Utility;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Interface.Windowing;
|
|
using Dalamud.Plugin.Services;
|
|
using Lumina.Excel;
|
|
using Lumina.Excel.Sheets;
|
|
using QuestionableCompanion.Data;
|
|
using QuestionableCompanion.Models;
|
|
using QuestionableCompanion.Services;
|
|
|
|
namespace QuestionableCompanion.Windows;
|
|
|
|
public class NewMainWindow : Window, IDisposable
|
|
{
|
|
private class Particle
|
|
{
|
|
public Vector2 Position;
|
|
|
|
public Vector2 Velocity;
|
|
|
|
public float Size;
|
|
|
|
public float Alpha;
|
|
|
|
public Vector4 Color;
|
|
}
|
|
|
|
private readonly Plugin plugin;
|
|
|
|
private readonly AutoRetainerIPC autoRetainerIpc;
|
|
|
|
private readonly QuestTrackingService questTrackingService;
|
|
|
|
private readonly QuestRotationExecutionService questRotationService;
|
|
|
|
private readonly EventQuestExecutionService eventQuestService;
|
|
|
|
private readonly AlliedSocietyRotationService alliedSocietyRotationService;
|
|
|
|
private readonly AlliedSocietyPriorityWindow alliedSocietyPriorityWindow;
|
|
|
|
private readonly DataCenterService dataCenterService;
|
|
|
|
private readonly MSQProgressionService msqProgressionService;
|
|
|
|
private readonly IPluginLog log;
|
|
|
|
private readonly IUiBuilder uiBuilder;
|
|
|
|
private readonly IDataManager dataManager;
|
|
|
|
private readonly Vector4 colorPrimary = new Vector4(0.478f, 0.686f, 0.878f, 1f);
|
|
|
|
private readonly Vector4 colorSecondary = new Vector4(0.949f, 0.769f, 0.388f, 1f);
|
|
|
|
private readonly Vector4 colorAccent = new Vector4(0.729f, 0.294f, 0.184f, 1f);
|
|
|
|
private readonly Vector4 colorDarkBg = new Vector4(0.12f, 0.12f, 0.12f, 1f);
|
|
|
|
private readonly Vector4 colorSidebarBg = new Vector4(0.08f, 0.08f, 0.08f, 1f);
|
|
|
|
private readonly Vector4 colorHover = new Vector4(0.2f, 0.2f, 0.2f, 1f);
|
|
|
|
private float animTime;
|
|
|
|
private float glowPulse;
|
|
|
|
private float particleTime;
|
|
|
|
private List<Particle> particles = new List<Particle>();
|
|
|
|
private int selectedTab;
|
|
|
|
private int selectedDCFilter;
|
|
|
|
private bool charactersExpanded = true;
|
|
|
|
private bool menuExpanded = true;
|
|
|
|
private bool isMinimized;
|
|
|
|
private List<string> registeredCharacters = new List<string>();
|
|
|
|
private Dictionary<string, bool> characterSelection = new Dictionary<string, bool>();
|
|
|
|
private Dictionary<string, CharacterProgressInfo> characterProgressCache = new Dictionary<string, CharacterProgressInfo>();
|
|
|
|
private DateTime lastCharacterRefresh = DateTime.MinValue;
|
|
|
|
private bool initialCharacterLoadComplete;
|
|
|
|
private DateTime initialLoadStartTime = DateTime.MinValue;
|
|
|
|
private int characterLoadAttempts;
|
|
|
|
private const int MaxCharacterLoadAttempts = 5;
|
|
|
|
private readonly int[] retryDelaysSeconds = new int[5] { 1, 2, 4, 8, 15 };
|
|
|
|
private bool warningMenuRetryCycleComplete;
|
|
|
|
private DateTime warningMenuRetryStartTime = DateTime.MinValue;
|
|
|
|
private int warningMenuRetryAttempts;
|
|
|
|
private const int MaxWarningMenuRetryAttempts = 4;
|
|
|
|
private readonly int[] warningMenuRetryDelaysSeconds = new int[4] { 1, 2, 4, 8 };
|
|
|
|
private Dictionary<string, List<string>> charactersByDataCenter = new Dictionary<string, List<string>>();
|
|
|
|
private List<string> availableDataCenters = new List<string> { "All", "EU", "NA", "JP", "OCE" };
|
|
|
|
private bool showSelectWorldDialog;
|
|
|
|
private bool showDeselectWorldDialog;
|
|
|
|
private string selectedWorldForBulkAction = "";
|
|
|
|
private List<string> availableWorlds = new List<string>();
|
|
|
|
private string selectedWorldFilter = "All";
|
|
|
|
private bool showWorldActionDropdown;
|
|
|
|
private uint inputStopQuestId;
|
|
|
|
private int inputStopSequence = -1;
|
|
|
|
private string selectedEventQuestId = "";
|
|
|
|
private List<(string QuestId, string QuestName)> availableEventQuests = new List<(string, string)>();
|
|
|
|
private List<string> resolvedPrerequisites = new List<string>();
|
|
|
|
private int eventQuestViewMode;
|
|
|
|
private DateTime lastEventQuestRefresh = DateTime.MinValue;
|
|
|
|
private readonly Dictionary<string, List<string>> dataCenterWorlds = new Dictionary<string, List<string>>
|
|
{
|
|
{
|
|
"Aether",
|
|
new List<string> { "Adamantoise", "Cactuar", "Faerie", "Gilgamesh", "Jenova", "Midgardsormr", "Sargatanas", "Siren" }
|
|
},
|
|
{
|
|
"Primal",
|
|
new List<string> { "Behemoth", "Excalibur", "Exodus", "Hyperion", "Lamia", "Leviathan", "Ultros" }
|
|
},
|
|
{
|
|
"Crystal",
|
|
new List<string> { "Balmung", "Brynhildr", "Coeurl", "Diabolos", "Goblin", "Malboro", "Mateus", "Zalera" }
|
|
},
|
|
{
|
|
"Dynamis",
|
|
new List<string> { "Halicarnassus", "Maduin", "Marilith", "Seraph" }
|
|
},
|
|
{
|
|
"Chaos",
|
|
new List<string> { "Cerberus", "Louisoix", "Moogle", "Omega", "Phantom", "Ragnarok", "Sagittarius", "Spriggan" }
|
|
},
|
|
{
|
|
"Light",
|
|
new List<string> { "Alpha", "Lich", "Odin", "Phoenix", "Raiden", "Shiva", "Twintania", "Zodiark" }
|
|
},
|
|
{
|
|
"Materia",
|
|
new List<string> { "Bismarck", "Ravana", "Sephirot", "Sophia", "Zurvan" }
|
|
},
|
|
{
|
|
"Elemental",
|
|
new List<string> { "Aegis", "Atomos", "Carbuncle", "Garuda", "Gungnir", "Kujata", "Tonberry", "Typhon" }
|
|
},
|
|
{
|
|
"Gaia",
|
|
new List<string> { "Alexander", "Bahamut", "Durandal", "Fenrir", "Ifrit", "Ridill", "Tiamat", "Ultima" }
|
|
},
|
|
{
|
|
"Mana",
|
|
new List<string> { "Anima", "Asura", "Chocobo", "Hades", "Ixion", "Masamune", "Pandaemonium", "Titan" }
|
|
},
|
|
{
|
|
"Meteor",
|
|
new List<string> { "Belias", "Mandragora", "Ramuh", "Shinryu", "Unicorn", "Valefor", "Yojimbo", "Zeromus" }
|
|
}
|
|
};
|
|
|
|
private string selectedDataCenter = "";
|
|
|
|
private string selectedWorld = "";
|
|
|
|
private void DrawAlliedSocietyTab()
|
|
{
|
|
ImGui.TextColored(in colorSecondary, "Allied Society Rotation");
|
|
ImGui.Separator();
|
|
if (ImGui.Button("Configure Priorities"))
|
|
{
|
|
alliedSocietyPriorityWindow.IsOpen = true;
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.TextDisabled("(Use Up/Down buttons to reorder)");
|
|
ImGui.Spacing();
|
|
ImGui.Separator();
|
|
ImGui.TextColored(in colorSecondary, "Quest Selection Mode");
|
|
AlliedSocietyConfiguration config = plugin.Configuration.AlliedSociety.RotationConfig;
|
|
bool modeChanged = false;
|
|
if (ImGui.RadioButton("Only 3 Quests per Society", config.QuestMode == AlliedSocietyQuestMode.OnlyThreePerSociety))
|
|
{
|
|
config.QuestMode = AlliedSocietyQuestMode.OnlyThreePerSociety;
|
|
modeChanged = true;
|
|
}
|
|
if (ImGui.RadioButton("All Available Quests (until 0 allowances)", config.QuestMode == AlliedSocietyQuestMode.AllAvailableQuests))
|
|
{
|
|
config.QuestMode = AlliedSocietyQuestMode.AllAvailableQuests;
|
|
modeChanged = true;
|
|
}
|
|
if (modeChanged)
|
|
{
|
|
plugin.Configuration.Save();
|
|
}
|
|
ImGui.Spacing();
|
|
ImGui.Separator();
|
|
List<string> selectedCharacters = (from kvp in characterSelection
|
|
where kvp.Value
|
|
select kvp.Key).ToList();
|
|
ImGui.TextColored(in colorSecondary, "Rotation Control");
|
|
if (alliedSocietyRotationService.IsRotationActive)
|
|
{
|
|
if (ImGui.Button("Stop Rotation", new Vector2(150f, 30f)))
|
|
{
|
|
alliedSocietyRotationService.StopRotation();
|
|
}
|
|
ImGui.SameLine();
|
|
Vector4 col = ImGuiColors.DalamudYellow;
|
|
ImU8String text = new ImU8String(18, 1);
|
|
text.AppendLiteral("Running... Phase: ");
|
|
text.AppendFormatted(alliedSocietyRotationService.CurrentPhase);
|
|
ImGui.TextColored(in col, text);
|
|
ImU8String text2 = new ImU8String(19, 1);
|
|
text2.AppendLiteral("Current Character: ");
|
|
text2.AppendFormatted(alliedSocietyRotationService.CurrentCharacterId);
|
|
ImGui.Text(text2);
|
|
}
|
|
else
|
|
{
|
|
if (ImGui.Button("Start Rotation", new Vector2(150f, 30f)))
|
|
{
|
|
if (selectedCharacters.Count == 0)
|
|
{
|
|
ImGui.OpenPopup("NoCharactersSelected");
|
|
}
|
|
else
|
|
{
|
|
alliedSocietyRotationService.StartRotation(selectedCharacters);
|
|
}
|
|
}
|
|
if (ImGui.BeginPopup("NoCharactersSelected"))
|
|
{
|
|
ImGui.Text("Please select at least one character from the Characters tab.");
|
|
if (ImGui.Button("OK", new Vector2(120f, 0f)))
|
|
{
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
ImGui.EndPopup();
|
|
}
|
|
}
|
|
ImGui.Spacing();
|
|
ImGui.Separator();
|
|
ref readonly Vector4 col2 = ref colorSecondary;
|
|
ImU8String text3 = new ImU8String(28, 1);
|
|
text3.AppendLiteral("Character Status (");
|
|
text3.AppendFormatted(selectedCharacters.Count);
|
|
text3.AppendLiteral(" selected)");
|
|
ImGui.TextColored(in col2, text3);
|
|
if (selectedCharacters.Count > 0 && ImGui.BeginTable("AlliedSocietyStatusTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg))
|
|
{
|
|
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Status", ImGuiTableColumnFlags.WidthFixed, 150f);
|
|
ImGui.TableSetupColumn("Allowances", ImGuiTableColumnFlags.WidthFixed, 100f);
|
|
ImGui.TableHeadersRow();
|
|
foreach (string character in selectedCharacters)
|
|
{
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
if (character == alliedSocietyRotationService.CurrentCharacterId)
|
|
{
|
|
ImGui.TextColored(ImGuiColors.DalamudYellow, character);
|
|
}
|
|
else
|
|
{
|
|
ImGui.Text(character);
|
|
}
|
|
ImGui.TableNextColumn();
|
|
AlliedSocietyCharacterStatus status = (plugin.Configuration.AlliedSociety.CharacterStatuses.ContainsKey(character) ? plugin.Configuration.AlliedSociety.CharacterStatuses[character] : new AlliedSocietyCharacterStatus
|
|
{
|
|
CharacterId = character
|
|
});
|
|
ImGui.TextColored((status.Status == AlliedSocietyRotationStatus.Complete) ? ImGuiColors.HealerGreen : ImGuiColors.DalamudWhite, status.Status.ToString());
|
|
ImGui.TableNextColumn();
|
|
ImGui.Text("-");
|
|
}
|
|
ImGui.EndTable();
|
|
}
|
|
else if (selectedCharacters.Count == 0)
|
|
{
|
|
ImGui.TextColored(ImGuiColors.DalamudGrey, "No characters selected. Please select characters in the Characters tab.");
|
|
}
|
|
}
|
|
|
|
public NewMainWindow(Plugin plugin, AutoRetainerIPC autoRetainerIpc, QuestTrackingService questTrackingService, QuestRotationExecutionService questRotationService, EventQuestExecutionService eventQuestService, AlliedSocietyRotationService alliedSocietyRotationService, AlliedSocietyPriorityWindow alliedSocietyPriorityWindow, DataCenterService dataCenterService, MSQProgressionService msqProgressionService, IPluginLog log, IUiBuilder uiBuilder, IDataManager dataManager)
|
|
: base("Questionable Companion##NewMainWindow", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoBackground)
|
|
{
|
|
this.plugin = plugin;
|
|
this.autoRetainerIpc = autoRetainerIpc;
|
|
this.questTrackingService = questTrackingService;
|
|
this.questRotationService = questRotationService;
|
|
this.eventQuestService = eventQuestService;
|
|
this.alliedSocietyRotationService = alliedSocietyRotationService;
|
|
this.alliedSocietyPriorityWindow = alliedSocietyPriorityWindow;
|
|
this.dataCenterService = dataCenterService;
|
|
this.msqProgressionService = msqProgressionService;
|
|
this.log = log;
|
|
this.uiBuilder = uiBuilder;
|
|
this.dataManager = dataManager;
|
|
base.SizeConstraints = new WindowSizeConstraints
|
|
{
|
|
MinimumSize = new Vector2(900f, 600f),
|
|
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
|
};
|
|
base.BgAlpha = 0f;
|
|
try
|
|
{
|
|
dataCenterService.InitializeWorldMapping();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[NewMainWindow] Failed to initialize data center mapping: " + ex.Message);
|
|
}
|
|
initialLoadStartTime = DateTime.Now;
|
|
log.Information("[NewMainWindow] Delayed character loading started (will retry with exponential backoff)");
|
|
Random random = new Random();
|
|
for (int i = 0; i < 80; i++)
|
|
{
|
|
Vector4 particleColor = random.Next(3) switch
|
|
{
|
|
0 => colorPrimary,
|
|
1 => colorSecondary,
|
|
_ => colorAccent,
|
|
};
|
|
particles.Add(new Particle
|
|
{
|
|
Position = new Vector2(random.Next(0, 900), random.Next(0, 600)),
|
|
Velocity = new Vector2((float)(random.NextDouble() - 0.5) * 25f, (float)(random.NextDouble() - 0.5) * 25f),
|
|
Size = (float)random.NextDouble() * 3f + 1f,
|
|
Alpha = (float)random.NextDouble() * 0.5f + 0.2f,
|
|
Color = particleColor
|
|
});
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
float deltaTime = ImGui.GetIO().DeltaTime;
|
|
animTime += deltaTime;
|
|
particleTime += deltaTime * 0.5f;
|
|
glowPulse = (MathF.Sin(animTime * 2f) + 1f) * 0.5f;
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
Vector2 windowSize = ImGui.GetWindowSize();
|
|
Vector2 windowPos = ImGui.GetWindowPos();
|
|
float sidebarWidth = 200f;
|
|
float titleBarHeight = 30f;
|
|
if (isMinimized)
|
|
{
|
|
float minHeight = titleBarHeight + 5f;
|
|
base.SizeConstraints = new WindowSizeConstraints
|
|
{
|
|
MinimumSize = new Vector2(400f, minHeight),
|
|
MaximumSize = new Vector2(float.MaxValue, minHeight)
|
|
};
|
|
DrawCustomTitleBar(drawList, windowPos, windowSize, titleBarHeight);
|
|
ImGui.SetWindowSize(new Vector2(windowSize.X, minHeight));
|
|
return;
|
|
}
|
|
base.SizeConstraints = new WindowSizeConstraints
|
|
{
|
|
MinimumSize = new Vector2(900f, 600f),
|
|
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
|
};
|
|
TryInitialCharacterLoad();
|
|
DrawGradientBackground();
|
|
DrawAnimatedParticles(drawList, windowPos, windowSize, deltaTime);
|
|
DrawScanningLine(drawList, windowPos, windowSize);
|
|
DrawCustomTitleBar(drawList, windowPos, windowSize, titleBarHeight);
|
|
DrawSemiTransparentBackgrounds(windowPos + new Vector2(0f, titleBarHeight), windowSize - new Vector2(0f, titleBarHeight), sidebarWidth);
|
|
ImGui.SetCursorPos(new Vector2(0f, titleBarHeight));
|
|
DrawSidebar(sidebarWidth, windowSize.Y - titleBarHeight);
|
|
ImGui.SameLine();
|
|
DrawContentArea(windowSize.X - sidebarWidth - 20f, windowSize.Y - titleBarHeight);
|
|
DrawWorldSelectionDialogs();
|
|
}
|
|
|
|
private void RefreshCharacterList(bool forceIpcCheck = false)
|
|
{
|
|
try
|
|
{
|
|
log.Debug($"[NewMainWindow] RefreshCharacterList called (forceIpcCheck: {forceIpcCheck})");
|
|
if (forceIpcCheck)
|
|
{
|
|
log.Debug("[NewMainWindow] Forcing IPC availability check...");
|
|
autoRetainerIpc.TryReinitialize();
|
|
}
|
|
if (!autoRetainerIpc.IsAvailable)
|
|
{
|
|
log.Warning("[NewMainWindow] AutoRetainer IPC not available during character refresh");
|
|
return;
|
|
}
|
|
registeredCharacters = autoRetainerIpc.GetRegisteredCharacters();
|
|
lastCharacterRefresh = DateTime.Now;
|
|
log.Information($"[NewMainWindow] Loaded {registeredCharacters.Count} characters from AutoRetainer");
|
|
if (registeredCharacters.Count > 0)
|
|
{
|
|
initialCharacterLoadComplete = true;
|
|
log.Information("[NewMainWindow] ✅ Initial character load successful");
|
|
}
|
|
charactersByDataCenter = dataCenterService.GroupCharactersByDataCenter(registeredCharacters);
|
|
availableWorlds = (from w in (from c in registeredCharacters
|
|
select c.Split('@') into parts
|
|
where parts.Length > 1
|
|
select parts[1]).Distinct()
|
|
orderby w
|
|
select w).ToList();
|
|
foreach (string character in registeredCharacters)
|
|
{
|
|
if (!characterSelection.ContainsKey(character))
|
|
{
|
|
characterSelection[character] = false;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[NewMainWindow] RefreshCharacterList failed: " + ex.Message);
|
|
log.Error("[NewMainWindow] Stack trace: " + ex.StackTrace);
|
|
}
|
|
}
|
|
|
|
private void TryInitialCharacterLoad()
|
|
{
|
|
if (initialCharacterLoadComplete || characterLoadAttempts >= 5)
|
|
{
|
|
return;
|
|
}
|
|
double elapsedSeconds = (DateTime.Now - initialLoadStartTime).TotalSeconds;
|
|
int requiredDelay = ((characterLoadAttempts < retryDelaysSeconds.Length) ? retryDelaysSeconds[characterLoadAttempts] : retryDelaysSeconds[^1]);
|
|
if (!(elapsedSeconds < (double)requiredDelay))
|
|
{
|
|
characterLoadAttempts++;
|
|
log.Information($"[NewMainWindow] Character load attempt {characterLoadAttempts}/{5} (after {elapsedSeconds:F1}s)");
|
|
RefreshCharacterList(forceIpcCheck: true);
|
|
if (!initialCharacterLoadComplete)
|
|
{
|
|
initialLoadStartTime = DateTime.Now;
|
|
log.Debug($"[NewMainWindow] Next retry in {((characterLoadAttempts < retryDelaysSeconds.Length) ? retryDelaysSeconds[characterLoadAttempts] : retryDelaysSeconds[^1])}s");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TryWarningMenuQuestionableCheck()
|
|
{
|
|
if (warningMenuRetryCycleComplete)
|
|
{
|
|
return;
|
|
}
|
|
if (plugin.QuestionableIPC.TryEnsureAvailableSilent() && plugin.QuestionableIPC.ValidateFeatureCompatibility())
|
|
{
|
|
warningMenuRetryCycleComplete = true;
|
|
selectedTab = 5;
|
|
log.Information("[NewMainWindow] Warning Menu retry cycle complete - Questionable IPC is now available");
|
|
}
|
|
else if (warningMenuRetryAttempts < 4)
|
|
{
|
|
if (warningMenuRetryAttempts == 0)
|
|
{
|
|
warningMenuRetryStartTime = DateTime.Now;
|
|
}
|
|
double elapsedSeconds = (DateTime.Now - warningMenuRetryStartTime).TotalSeconds;
|
|
int requiredDelay = ((warningMenuRetryAttempts < warningMenuRetryDelaysSeconds.Length) ? warningMenuRetryDelaysSeconds[warningMenuRetryAttempts] : warningMenuRetryDelaysSeconds[^1]);
|
|
if (!(elapsedSeconds < (double)requiredDelay))
|
|
{
|
|
warningMenuRetryAttempts++;
|
|
log.Debug($"[NewMainWindow] Warning Menu retry attempt {warningMenuRetryAttempts}/{4} (after {elapsedSeconds:F1}s)");
|
|
warningMenuRetryStartTime = DateTime.Now;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawSidebar(float width, float height)
|
|
{
|
|
using ImRaii.IEndObject child = ImRaii.Child("Sidebar", new Vector2(width, height - 10f), border: false);
|
|
if (!child.Success)
|
|
{
|
|
return;
|
|
}
|
|
DrawSidebarCategory("CHARACTERS", ref charactersExpanded, delegate
|
|
{
|
|
DrawSidebarItem("All", 0, registeredCharacters.Count);
|
|
DrawSidebarItem("EU", 1, GetCharacterCountForDC("EU"));
|
|
DrawSidebarItem("NA", 2, GetCharacterCountForDC("NA"));
|
|
DrawSidebarItem("JP", 3, GetCharacterCountForDC("JP"));
|
|
DrawSidebarItem("OCE", 4, GetCharacterCountForDC("OCE"));
|
|
});
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
DrawSidebarCategory("MENU", ref menuExpanded, delegate
|
|
{
|
|
if (plugin.QuestionableIPC.ValidateFeatureCompatibility())
|
|
{
|
|
DrawSidebarItem("Stop Points", 5, 0);
|
|
DrawSidebarItem("Allied Society", 10, 0);
|
|
DrawSidebarItem("Event Quest", 6, 0);
|
|
DrawSidebarItem("MSQ Progression", 7, 0);
|
|
DrawSidebarItem("Data Center Travel", 8, 0);
|
|
DrawSidebarItem("Settings", 9, 0);
|
|
}
|
|
else
|
|
{
|
|
DrawSidebarItem("Warning", 11, 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void DrawSidebarCategory(string name, ref bool expanded, System.Action drawItems)
|
|
{
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
Vector2 cursorScreenPos = ImGui.GetCursorScreenPos();
|
|
float itemWidth = ImGui.GetContentRegionAvail().X - 10f;
|
|
float itemHeight = 30f;
|
|
Vector4 headerBg = new Vector4(colorPrimary.X * 0.25f, colorPrimary.Y * 0.25f, colorPrimary.Z * 0.25f, 0.7f);
|
|
uint borderColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorPrimary.X, colorPrimary.Y, colorPrimary.Z, 0.6f));
|
|
Vector2 boxStart = cursorScreenPos + new Vector2(5f, 0f);
|
|
drawList.AddRectFilled(boxStart, boxStart + new Vector2(itemWidth, itemHeight), ImGui.ColorConvertFloat4ToU32(headerBg), 6f);
|
|
drawList.AddRect(boxStart, boxStart + new Vector2(itemWidth, itemHeight), borderColor, 6f, ImDrawFlags.None, 1.5f);
|
|
string obj = (expanded ? "v" : ">");
|
|
ImGui.SetCursorScreenPos(boxStart + new Vector2(10f, 8f));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.Text(obj);
|
|
ImGui.PopStyleColor();
|
|
ImGui.SameLine();
|
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 1f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.Text(name);
|
|
ImGui.PopStyleColor();
|
|
if (ImGui.IsMouseHoveringRect(boxStart, boxStart + new Vector2(itemWidth, itemHeight)) && ImGui.IsMouseClicked(ImGuiMouseButton.Left))
|
|
{
|
|
expanded = !expanded;
|
|
}
|
|
ImGui.Dummy(new Vector2(itemWidth + 10f, itemHeight));
|
|
ImGui.Spacing();
|
|
if (expanded)
|
|
{
|
|
ImGui.Indent(10f);
|
|
drawItems();
|
|
ImGui.Unindent(10f);
|
|
ImGui.Spacing();
|
|
}
|
|
}
|
|
|
|
private void DrawSidebarItem(string label, int tabIndex, int count)
|
|
{
|
|
bool isSelected = selectedTab == tabIndex;
|
|
string obj = ((count > 0) ? $"{label} ({count})" : label);
|
|
if (isSelected)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Header, colorPrimary);
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.HeaderHovered, colorHover);
|
|
}
|
|
if (ImGui.Selectable(obj, isSelected, ImGuiSelectableFlags.None, new Vector2(0f, 22f)))
|
|
{
|
|
selectedTab = tabIndex;
|
|
if (tabIndex <= 4)
|
|
{
|
|
selectedDCFilter = tabIndex;
|
|
}
|
|
}
|
|
ImGui.PopStyleColor();
|
|
}
|
|
|
|
private int GetCharacterCountForDC(string dcName)
|
|
{
|
|
if (charactersByDataCenter.TryGetValue(dcName, out List<string> chars))
|
|
{
|
|
return chars.Count;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private void DrawCustomTitleBar(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, float height)
|
|
{
|
|
uint leftColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorPrimary.X * 0.4f, colorPrimary.Y * 0.4f, colorPrimary.Z * 0.4f, 1f));
|
|
uint rightColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorSecondary.X * 0.3f, colorSecondary.Y * 0.3f, colorSecondary.Z * 0.3f, 1f));
|
|
drawList.AddRectFilledMultiColor(windowPos, windowPos + new Vector2(windowSize.X, height), leftColor, rightColor, rightColor, leftColor);
|
|
Vector2 titlePos = windowPos + new Vector2(10f, 7f);
|
|
drawList.AddText(titlePos, ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 0.9f)), "Questionable Companion V1.0.1");
|
|
Vector2 minimizeButtonPos = windowPos + new Vector2(windowSize.X - 60f, 3f);
|
|
Vector2 minimizeButtonSize = new Vector2(24f, 24f);
|
|
if (ImGui.IsMouseHoveringRect(minimizeButtonPos, minimizeButtonPos + minimizeButtonSize))
|
|
{
|
|
drawList.AddRectFilled(minimizeButtonPos, minimizeButtonPos + minimizeButtonSize, ImGui.ColorConvertFloat4ToU32(new Vector4(0.4f, 0.4f, 0.4f, 0.8f)), 4f);
|
|
if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
|
|
{
|
|
isMinimized = !isMinimized;
|
|
}
|
|
}
|
|
drawList.AddText(minimizeButtonPos + new Vector2(8f, 2f), ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f)), "_");
|
|
Vector2 closeButtonPos = windowPos + new Vector2(windowSize.X - 30f, 3f);
|
|
Vector2 closeButtonSize = new Vector2(24f, 24f);
|
|
if (ImGui.IsMouseHoveringRect(closeButtonPos, closeButtonPos + closeButtonSize))
|
|
{
|
|
drawList.AddRectFilled(closeButtonPos, closeButtonPos + closeButtonSize, ImGui.ColorConvertFloat4ToU32(new Vector4(0.8f, 0.2f, 0.2f, 0.8f)), 4f);
|
|
if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
|
|
{
|
|
base.IsOpen = false;
|
|
}
|
|
}
|
|
drawList.AddText(closeButtonPos + new Vector2(7f, 2f), ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f)), "X");
|
|
}
|
|
|
|
private void DrawSemiTransparentBackgrounds(Vector2 windowPos, Vector2 windowSize, float sidebarWidth)
|
|
{
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
drawList.AddRectFilled(col: ImGui.ColorConvertFloat4ToU32(new Vector4(0.12f, 0.12f, 0.15f, 0.85f)), pMin: windowPos, pMax: windowPos + new Vector2(sidebarWidth, windowSize.Y));
|
|
float gradientWidth = 20f;
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
float alpha = (float)i / 20f;
|
|
drawList.AddRectFilled(col: ImGui.ColorConvertFloat4ToU32(new Vector4(0.12f + 0.030000009f * alpha, 0.12f + 0.030000009f * alpha, 0.15f + 0.030000001f * alpha, 0.85f - 0.05f * alpha)), pMin: windowPos + new Vector2(sidebarWidth + (float)i, 0f), pMax: windowPos + new Vector2(sidebarWidth + (float)i + 1f, windowSize.Y));
|
|
}
|
|
drawList.AddRectFilled(col: ImGui.ColorConvertFloat4ToU32(new Vector4(0.15f, 0.15f, 0.18f, 0.8f)), pMin: windowPos + new Vector2(sidebarWidth + gradientWidth, 0f), pMax: windowPos + windowSize);
|
|
}
|
|
|
|
private void DrawGradientBackground()
|
|
{
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
Vector2 windowPos = ImGui.GetWindowPos();
|
|
Vector2 windowSize = ImGui.GetWindowSize();
|
|
float extend = 2f;
|
|
float colorShift = MathF.Sin(animTime * 0.3f) * 0.15f;
|
|
float colorShift2 = MathF.Cos(animTime * 0.25f) * 0.12f;
|
|
uint blue = ImGui.ColorConvertFloat4ToU32(new Vector4(0.47843137f + colorShift, 35f / 51f + colorShift2, 0.8784314f - colorShift * 0.5f, 1f));
|
|
uint gold = ImGui.ColorConvertFloat4ToU32(new Vector4(0.9490196f - colorShift2 * 0.5f, 0.76862746f + colorShift, 33f / 85f + colorShift2, 1f));
|
|
uint brown = ImGui.ColorConvertFloat4ToU32(new Vector4(62f / 85f + colorShift2, 0.29411766f - colorShift, 0.18431373f + colorShift * 0.8f, 1f));
|
|
drawList.AddRectFilledMultiColor(windowPos - new Vector2(extend, extend), windowPos + windowSize + new Vector2(extend, extend), blue, gold, brown, blue);
|
|
}
|
|
|
|
private void DrawContentArea(float width, float height)
|
|
{
|
|
using ImRaii.IEndObject child = ImRaii.Child("ContentArea", new Vector2(width, height - 10f), border: false);
|
|
if (child.Success)
|
|
{
|
|
switch (selectedTab)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
DrawCharactersTab();
|
|
break;
|
|
case 5:
|
|
DrawStopPointsTab();
|
|
break;
|
|
case 6:
|
|
DrawEventQuestTab();
|
|
break;
|
|
case 7:
|
|
DrawMSQProgressionTab();
|
|
break;
|
|
case 8:
|
|
DrawDCTravelTab();
|
|
break;
|
|
case 9:
|
|
DrawSettingsTab();
|
|
break;
|
|
case 10:
|
|
DrawAlliedSocietyTab();
|
|
break;
|
|
case 11:
|
|
DrawWarningTab();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawAnimatedParticles(ImDrawListPtr drawList, Vector2 pos, Vector2 size, float deltaTime)
|
|
{
|
|
foreach (Particle p in particles)
|
|
{
|
|
if (p.Position.X < pos.X)
|
|
{
|
|
p.Position.X = pos.X + size.X;
|
|
}
|
|
if (p.Position.X > pos.X + size.X)
|
|
{
|
|
p.Position.X = pos.X;
|
|
}
|
|
if (p.Position.Y < pos.Y)
|
|
{
|
|
p.Position.Y = pos.Y + size.Y;
|
|
}
|
|
if (p.Position.Y > pos.Y + size.Y)
|
|
{
|
|
p.Position.Y = pos.Y;
|
|
}
|
|
float colorPulse = 0.8f + glowPulse * 0.2f;
|
|
float alpha = p.Alpha * (0.6f + glowPulse * 0.4f);
|
|
uint color = ImGui.ColorConvertFloat4ToU32(new Vector4(p.Color.X * colorPulse, p.Color.Y * colorPulse, p.Color.Z * colorPulse, alpha));
|
|
float glowAlpha = alpha * 0.3f;
|
|
uint glowColor = ImGui.ColorConvertFloat4ToU32(new Vector4(p.Color.X * colorPulse, p.Color.Y * colorPulse, p.Color.Z * colorPulse, glowAlpha));
|
|
drawList.AddCircleFilled(p.Position, p.Size * 2f, glowColor, 12);
|
|
drawList.AddCircleFilled(p.Position, p.Size, color, 8);
|
|
}
|
|
}
|
|
|
|
private void DrawScanningLine(ImDrawListPtr drawList, Vector2 pos, Vector2 size)
|
|
{
|
|
float scan1Y = pos.Y + animTime * 0.3f % 1f * size.Y;
|
|
float scan2Y = pos.Y + (animTime * 0.25f + 0.33f) % 1f * size.Y;
|
|
float scan3Y = pos.Y + (animTime * 0.2f + 0.66f) % 1f * size.Y;
|
|
uint blueColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorPrimary.X, colorPrimary.Y, colorPrimary.Z, 0.15f * glowPulse));
|
|
uint goldColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorSecondary.X, colorSecondary.Y, colorSecondary.Z, 0.15f * glowPulse));
|
|
uint redColor = ImGui.ColorConvertFloat4ToU32(new Vector4(colorAccent.X, colorAccent.Y, colorAccent.Z, 0.15f * glowPulse));
|
|
drawList.AddLine(new Vector2(pos.X, scan1Y), new Vector2(pos.X + size.X, scan1Y), blueColor, 2f);
|
|
drawList.AddLine(new Vector2(pos.X, scan2Y), new Vector2(pos.X + size.X, scan2Y), goldColor, 2f);
|
|
drawList.AddLine(new Vector2(pos.X, scan3Y), new Vector2(pos.X + size.X, scan3Y), redColor, 2f);
|
|
}
|
|
|
|
private void DrawDCTravelTab()
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Data Center Travel Configuration");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
Configuration config = plugin.Configuration;
|
|
if (string.IsNullOrEmpty(selectedDataCenter) && !string.IsNullOrEmpty(config.DCTravelDataCenter))
|
|
{
|
|
selectedDataCenter = config.DCTravelDataCenter;
|
|
}
|
|
if (string.IsNullOrEmpty(selectedWorld) && !string.IsNullOrEmpty(config.DCTravelTargetWorld))
|
|
{
|
|
selectedWorld = config.DCTravelTargetWorld;
|
|
}
|
|
if (string.IsNullOrEmpty(selectedDataCenter))
|
|
{
|
|
selectedDataCenter = dataCenterWorlds.Keys.First();
|
|
}
|
|
if (string.IsNullOrEmpty(selectedWorld))
|
|
{
|
|
List<string> worlds = dataCenterWorlds.GetValueOrDefault(selectedDataCenter) ?? new List<string>();
|
|
if (worlds.Count > 0)
|
|
{
|
|
selectedWorld = worlds[0];
|
|
}
|
|
}
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextWrapped("Configure automatic Data Center travel for quest rotation. The plugin will travel to the specified Data Center and World before starting quests.");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(15f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Select Data Center:");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.SetNextItemWidth(350f);
|
|
ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0.15f, 0.15f, 0.18f, 0.9f));
|
|
ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, new Vector4(colorPrimary.X * 0.3f, colorPrimary.Y * 0.3f, colorPrimary.Z * 0.3f, 0.9f));
|
|
if (ImGui.BeginCombo("##DataCenterCombo", selectedDataCenter))
|
|
{
|
|
foreach (string dc in dataCenterWorlds.Keys.OrderBy((string k) => k))
|
|
{
|
|
bool isSelected = selectedDataCenter == dc;
|
|
if (ImGui.Selectable(dc, isSelected))
|
|
{
|
|
selectedDataCenter = dc;
|
|
List<string> worlds2 = dataCenterWorlds.GetValueOrDefault(selectedDataCenter) ?? new List<string>();
|
|
if (worlds2.Count > 0)
|
|
{
|
|
selectedWorld = worlds2[0];
|
|
}
|
|
}
|
|
if (isSelected)
|
|
{
|
|
ImGui.SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(15f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Select World:");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
List<string> availableWorlds = dataCenterWorlds.GetValueOrDefault(selectedDataCenter) ?? new List<string>();
|
|
ImGui.SetNextItemWidth(350f);
|
|
ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0.15f, 0.15f, 0.18f, 0.9f));
|
|
ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, new Vector4(colorPrimary.X * 0.3f, colorPrimary.Y * 0.3f, colorPrimary.Z * 0.3f, 0.9f));
|
|
if (ImGui.BeginCombo("##WorldCombo", selectedWorld))
|
|
{
|
|
foreach (string world in availableWorlds)
|
|
{
|
|
bool isSelected2 = selectedWorld == world;
|
|
if (ImGui.Selectable(world, isSelected2))
|
|
{
|
|
selectedWorld = world;
|
|
}
|
|
if (isSelected2)
|
|
{
|
|
ImGui.SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
ImGuiHelpers.ScaledDummy(15f);
|
|
LifestreamIPC lifestreamIPC = Plugin.Instance?.LifestreamIPC;
|
|
if (lifestreamIPC != null && !lifestreamIPC.IsAvailable)
|
|
{
|
|
lifestreamIPC.ForceCheckAvailability();
|
|
}
|
|
int num;
|
|
if (lifestreamIPC == null)
|
|
{
|
|
num = 0;
|
|
}
|
|
else
|
|
{
|
|
num = (lifestreamIPC.IsAvailable ? 1 : 0);
|
|
if (num != 0)
|
|
{
|
|
goto IL_0421;
|
|
}
|
|
}
|
|
ImGui.BeginDisabled();
|
|
goto IL_0421;
|
|
IL_0421:
|
|
bool enableDCTravel = config.EnableDCTravel;
|
|
if (ImGui.Checkbox("Enable Data Center Travel", ref enableDCTravel))
|
|
{
|
|
config.EnableDCTravel = enableDCTravel;
|
|
config.Save();
|
|
}
|
|
if (num == 0)
|
|
{
|
|
ImGui.EndDisabled();
|
|
}
|
|
ImGui.SameLine();
|
|
DrawInfoIcon("Automatically travels to the specified Data Center and World before starting quest rotation.\nRequires Lifestream plugin to be installed and configured.\nImpact: Characters will travel to target DC/World at rotation start.");
|
|
if (num == 0)
|
|
{
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.5f, 0f, 1f));
|
|
ImGui.TextWrapped("Lifestream plugin is not available! DC Travel requires Lifestream to be installed and enabled.");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
if (ImGui.Button("Check Lifestream Again") && lifestreamIPC != null)
|
|
{
|
|
bool result = lifestreamIPC.ForceCheckAvailability();
|
|
log.Information($"[DCTravel UI] Manual Lifestream check result: {result}");
|
|
}
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Manually check if Lifestream is available.\nCheck the logs for detailed information.");
|
|
}
|
|
}
|
|
ImGuiHelpers.ScaledDummy(20f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(15f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Current Configuration:");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.Indent(10f);
|
|
ImU8String text = new ImU8String(13, 0);
|
|
text.AppendLiteral("Data Center: ");
|
|
ImGui.TextUnformatted(text);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted((config.DCTravelDataCenter.Length > 0) ? config.DCTravelDataCenter : "Not Set");
|
|
ImGui.PopStyleColor();
|
|
ImU8String text2 = new ImU8String(14, 0);
|
|
text2.AppendLiteral("Target World: ");
|
|
ImGui.TextUnformatted(text2);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted((config.DCTravelTargetWorld.Length > 0) ? config.DCTravelTargetWorld : "Not Set");
|
|
ImGui.PopStyleColor();
|
|
ImU8String text3 = new ImU8String(8, 0);
|
|
text3.AppendLiteral("Status: ");
|
|
ImGui.TextUnformatted(text3);
|
|
ImGui.SameLine();
|
|
if (config.EnableDCTravel)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Enabled");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorAccent);
|
|
ImGui.TextUnformatted("Disabled");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGui.Unindent(10f);
|
|
ImGuiHelpers.ScaledDummy(20f);
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
if (ImGui.Button("Apply", new Vector2(120f, 30f)))
|
|
{
|
|
config.DCTravelDataCenter = selectedDataCenter;
|
|
config.DCTravelTargetWorld = selectedWorld;
|
|
config.Save();
|
|
log.Information("[DCTravel] Configuration saved: " + selectedDataCenter + " -> " + selectedWorld);
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
ImGui.SameLine();
|
|
if (ImGui.Button("Cancel", new Vector2(120f, 30f)))
|
|
{
|
|
selectedDataCenter = config.DCTravelDataCenter;
|
|
selectedWorld = config.DCTravelTargetWorld;
|
|
if (string.IsNullOrEmpty(selectedDataCenter))
|
|
{
|
|
selectedDataCenter = dataCenterWorlds.Keys.First();
|
|
}
|
|
if (string.IsNullOrEmpty(selectedWorld))
|
|
{
|
|
List<string> worlds3 = dataCenterWorlds.GetValueOrDefault(selectedDataCenter) ?? new List<string>();
|
|
if (worlds3.Count > 0)
|
|
{
|
|
selectedWorld = worlds3[0];
|
|
}
|
|
}
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (!config.EnableDCTravel)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextWrapped("Note: Data Center Travel is currently disabled. Enable it above to use this feature.");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
|
|
private void DrawSettingsTabFull()
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Plugin Settings");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
using ImRaii.IEndObject child = ImRaii.Child("SettingsScrollArea", new Vector2(0f, 0f), border: false, ImGuiWindowFlags.None);
|
|
if (!child.Success)
|
|
{
|
|
return;
|
|
}
|
|
Configuration config = plugin.Configuration;
|
|
DrawSettingSection("Submarine Management", delegate
|
|
{
|
|
config.EnableSubmarineCheck = DrawSettingWithInfo("Enable Submarine Monitoring", config.EnableSubmarineCheck, "Automatically monitors submarines and pauses quest rotation when submarines are ready.\nPrevents quest progression while submarines need attention.\nImpact: Rotation will pause when submarines are detected.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
if (config.EnableSubmarineCheck)
|
|
{
|
|
ImGui.Indent();
|
|
int v = config.SubmarineCheckInterval;
|
|
if (ImGui.SliderInt("Check Interval (seconds)", ref v, 30, 300))
|
|
{
|
|
config.SubmarineCheckInterval = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("How often to check for submarine status.\nLower values = more frequent checks but higher CPU usage.");
|
|
int v2 = config.SubmarineReloginCooldown;
|
|
if (ImGui.SliderInt("Cooldown after Relog (seconds)", ref v2, 60, 300))
|
|
{
|
|
config.SubmarineReloginCooldown = v2;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Time to wait after character switch before checking submarines again.");
|
|
int v3 = config.SubmarineWaitTime;
|
|
if (ImGui.SliderInt("Wait time before submarine (seconds)", ref v3, 10, 120))
|
|
{
|
|
config.SubmarineWaitTime = v3;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Delay before starting submarine operations after detection.");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.EnableSubmarineCheck);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("AutoRetainer Post Process Event Quests", delegate
|
|
{
|
|
config.RunEventQuestsOnARPostProcess = DrawSettingWithInfo("Run Event Quests on AR Post Process", config.RunEventQuestsOnARPostProcess, "AUTO-DETECTION: Automatically detects and runs active Event Quests when AutoRetainer completes a character.\nEvent Quests are detected via Questionable IPC (same as manual Event Quest tab).\nAll prerequisites will be automatically resolved and executed.\nAutoRetainer will wait until all Event Quests are completed before proceeding.\nImpact: Extends AR post-process time but ensures Event Quests are completed.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
if (config.RunEventQuestsOnARPostProcess)
|
|
{
|
|
ImGui.Indent();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0.4f, 0.8f, 0.4f, 1f));
|
|
ImGui.TextUnformatted("Auto-Detection Enabled");
|
|
ImGui.PopStyleColor();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextWrapped("Event Quests will be automatically detected from Questionable when AR Post Process starts. No manual configuration needed - just enable this setting and the plugin will handle the rest!");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
int v = config.EventQuestPostProcessTimeoutMinutes;
|
|
if (ImGui.SliderInt("Timeout (minutes)", ref v, 10, 60))
|
|
{
|
|
config.EventQuestPostProcessTimeoutMinutes = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Maximum time to wait for Event Quests to complete.\nAfter timeout, AR will proceed with next character.");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.RunEventQuestsOnARPostProcess);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Dungeon Automation", delegate
|
|
{
|
|
bool enableAutoDutyUnsynced = config.EnableAutoDutyUnsynced;
|
|
config.EnableAutoDutyUnsynced = DrawSettingWithInfo("Enable Auto Duty (Unsynced)", config.EnableAutoDutyUnsynced, "Automatically handles dungeon entries and party formation.\nUses AutoDuty plugin for unsynced dungeon runs.\nImpact: Dungeons will be automated during quest rotation.");
|
|
if (config.EnableAutoDutyUnsynced != enableAutoDutyUnsynced)
|
|
{
|
|
config.Save();
|
|
plugin.GetDungeonAutomation()?.SetDutyModeBasedOnConfig();
|
|
}
|
|
if (config.EnableAutoDutyUnsynced)
|
|
{
|
|
ImGui.Indent();
|
|
int v = config.AutoDutyPartySize;
|
|
if (ImGui.SliderInt("Minimum Party Size", ref v, 1, 4))
|
|
{
|
|
config.AutoDutyPartySize = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Minimum number of party members required before entering dungeon.");
|
|
int v2 = config.AutoDutyMaxWaitForParty;
|
|
if (ImGui.SliderInt("Max Wait for Party (seconds)", ref v2, 10, 120))
|
|
{
|
|
config.AutoDutyMaxWaitForParty = v2;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Maximum time to wait for party members before timing out.");
|
|
int v3 = config.AutoDutyReInviteInterval;
|
|
if (ImGui.SliderInt("Re-Invite Interval (seconds)", ref v3, 5, 60))
|
|
{
|
|
config.AutoDutyReInviteInterval = v3;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("How often to re-send party invites if members don't join.");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.EnableAutoDutyUnsynced);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Multi-Client Role", delegate
|
|
{
|
|
ImGui.TextWrapped("Select your role for multi-client features (party management, chauffeur mode):");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
int num = 0;
|
|
if (config.IsQuester)
|
|
{
|
|
num = 1;
|
|
}
|
|
else if (config.IsHighLevelHelper)
|
|
{
|
|
num = 2;
|
|
}
|
|
if (ImGui.RadioButton("None", num == 0))
|
|
{
|
|
config.IsQuester = false;
|
|
config.IsHighLevelHelper = false;
|
|
config.Save();
|
|
}
|
|
ImGui.SameLine();
|
|
DrawInfoIcon("No multi-client features enabled");
|
|
if (ImGui.RadioButton("Quester", num == 1))
|
|
{
|
|
config.IsQuester = true;
|
|
config.IsHighLevelHelper = false;
|
|
config.Save();
|
|
}
|
|
ImGui.SameLine();
|
|
DrawInfoIcon("This client will quest and invite helpers for dungeons");
|
|
if (ImGui.RadioButton("High-Level Helper", num == 2))
|
|
{
|
|
config.IsQuester = false;
|
|
config.IsHighLevelHelper = true;
|
|
Plugin.Framework.RunOnFrameworkThread(delegate
|
|
{
|
|
Plugin.Instance?.GetHelperManager()?.AnnounceIfHelper();
|
|
});
|
|
config.Save();
|
|
}
|
|
ImGui.SameLine();
|
|
DrawInfoIcon("This client will help with dungeons.\nAutoDuty starts/stops automatically on duty enter/leave");
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (config.IsQuester)
|
|
{
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.TextColored(in colorPrimary, "Auto-Discovered Helpers");
|
|
ImGui.TextWrapped("Helpers are automatically discovered via IPC when they have 'I'm a High-Level Helper' enabled:");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
List<(string, ushort)> availableHelpers = plugin.GetAvailableHelpers();
|
|
if (availableHelpers.Count != 0)
|
|
{
|
|
Vector4 col = new Vector4(0.2f, 1f, 0.2f, 1f);
|
|
ImU8String text = new ImU8String(21, 1);
|
|
text.AppendFormatted(availableHelpers.Count);
|
|
text.AppendLiteral(" helper(s) available:");
|
|
ImGui.TextColored(in col, text);
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.TextUnformatted("Preferred Helper for Chauffeur:");
|
|
ImGui.SetNextItemWidth(250f);
|
|
List<string> list = new List<string> { "Auto (First Available)" };
|
|
foreach (var item5 in availableHelpers)
|
|
{
|
|
string item = item5.Item1;
|
|
ushort item2 = item5.Item2;
|
|
ExcelSheet<World> excelSheet = Plugin.DataManager.GetExcelSheet<World>();
|
|
string text2 = "Unknown";
|
|
if (excelSheet != null)
|
|
{
|
|
foreach (World current2 in excelSheet)
|
|
{
|
|
if (current2.RowId == item2)
|
|
{
|
|
text2 = current2.Name.ExtractText();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
list.Add(item + "@" + text2);
|
|
}
|
|
string text3 = (string.IsNullOrEmpty(config.PreferredHelper) ? "Auto (First Available)" : config.PreferredHelper);
|
|
if (ImGui.BeginCombo("##PreferredHelper", text3))
|
|
{
|
|
foreach (string current3 in list)
|
|
{
|
|
bool flag = text3 == current3;
|
|
if (ImGui.Selectable(current3, flag))
|
|
{
|
|
config.PreferredHelper = ((current3 == "Auto (First Available)") ? "" : current3);
|
|
config.Save();
|
|
}
|
|
if (flag)
|
|
{
|
|
ImGui.SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGui.SameLine();
|
|
DrawInfoIcon("Select which helper to use for Chauffeur Mode.\n'Auto' will use the first available helper.");
|
|
if (!string.IsNullOrEmpty(config.PreferredHelper))
|
|
{
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
string text4 = (Plugin.Instance?.GetChauffeurMode())?.GetHelperStatus(config.PreferredHelper);
|
|
Vector4 col2;
|
|
ImU8String text5;
|
|
switch (text4)
|
|
{
|
|
case "Available":
|
|
col = new Vector4(0.2f, 1f, 0.2f, 1f);
|
|
goto IL_04f7;
|
|
case "Transporting":
|
|
col = new Vector4(1f, 0.8f, 0f, 1f);
|
|
goto IL_04f7;
|
|
case "InDungeon":
|
|
col = new Vector4(1f, 0.3f, 0.3f, 1f);
|
|
goto IL_04f7;
|
|
default:
|
|
col = colorSecondary;
|
|
goto IL_04f7;
|
|
case null:
|
|
{
|
|
ImGui.TextColored(in colorSecondary, "Helper Status: Unknown (waiting for update...)");
|
|
break;
|
|
}
|
|
IL_04f7:
|
|
col2 = col;
|
|
text5 = new ImU8String(15, 1);
|
|
text5.AppendLiteral("Helper Status: ");
|
|
text5.AppendFormatted(text4);
|
|
ImGui.TextColored(in col2, text5);
|
|
break;
|
|
}
|
|
}
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.TextUnformatted("Available Helpers:");
|
|
ChauffeurModeService chauffeurModeService = Plugin.Instance?.GetChauffeurMode();
|
|
{
|
|
foreach (var item6 in availableHelpers)
|
|
{
|
|
string item3 = item6.Item1;
|
|
ushort item4 = item6.Item2;
|
|
ExcelSheet<World> excelSheet2 = Plugin.DataManager.GetExcelSheet<World>();
|
|
string text6 = "Unknown";
|
|
if (excelSheet2 != null)
|
|
{
|
|
foreach (World current5 in excelSheet2)
|
|
{
|
|
if (current5.RowId == item4)
|
|
{
|
|
text6 = current5.Name.ExtractText();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
string text7 = item3 + "@" + text6;
|
|
string text8 = chauffeurModeService?.GetHelperStatus(text7);
|
|
ImU8String text9 = new ImU8String(4, 1);
|
|
text9.AppendLiteral(" • ");
|
|
text9.AppendFormatted(text7);
|
|
ImGui.TextUnformatted(text9);
|
|
if (text8 != null)
|
|
{
|
|
ImGui.SameLine();
|
|
Vector4 col3 = text8 switch
|
|
{
|
|
"Available" => new Vector4(0.2f, 1f, 0.2f, 1f),
|
|
"Transporting" => new Vector4(1f, 0.8f, 0f, 1f),
|
|
"InDungeon" => new Vector4(1f, 0.3f, 0.3f, 1f),
|
|
_ => colorSecondary,
|
|
};
|
|
ImU8String text10 = new ImU8String(2, 1);
|
|
text10.AppendLiteral("[");
|
|
text10.AppendFormatted(text8);
|
|
text10.AppendLiteral("]");
|
|
ImGui.TextColored(in col3, text10);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
ImGui.TextColored(new Vector4(1f, 0.8f, 0.2f, 1f), "No helpers discovered yet");
|
|
ImGui.TextWrapped("Make sure helper clients are running with 'I'm a High-Level Helper' enabled.");
|
|
}
|
|
}, config.IsQuester || config.IsHighLevelHelper);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Chauffeur Mode", delegate
|
|
{
|
|
ImGui.TextWrapped("Multi-character transport system. Helper transports Quester to quest objectives using multi-seater mounts.");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
if (!config.IsQuester && !config.IsHighLevelHelper)
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.8f, 0.2f, 1f), "Please select a role above to configure Chauffeur Mode");
|
|
}
|
|
else
|
|
{
|
|
bool v = config.ChauffeurModeEnabled;
|
|
if (ImGui.Checkbox("Enable Chauffeur Mode", ref v))
|
|
{
|
|
config.ChauffeurModeEnabled = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Enable automatic helper summoning for long-distance travel in non-flying zones");
|
|
if (config.ChauffeurModeEnabled)
|
|
{
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
if (config.IsQuester)
|
|
{
|
|
ImGui.TextColored(in colorPrimary, "Quester Settings");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
float v2 = config.ChauffeurDistanceThreshold;
|
|
ImGui.SetNextItemWidth(200f);
|
|
if (ImGui.SliderFloat("Distance Threshold (yalms)", ref v2, 50f, 300f, "%.0f"))
|
|
{
|
|
config.ChauffeurDistanceThreshold = v2;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Helper will be summoned when task is further than this distance\nand flying is not available in the zone");
|
|
ImU8String text = new ImU8String(15, 1);
|
|
text.AppendLiteral("Current: ");
|
|
text.AppendFormatted(config.ChauffeurDistanceThreshold, "F0");
|
|
text.AppendLiteral(" yalms");
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
if (config.IsHighLevelHelper)
|
|
{
|
|
ImGui.TextColored(in colorPrimary, "Helper Settings");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
Vector4 col = config.CurrentHelperStatus switch
|
|
{
|
|
HelperStatus.Available => new Vector4(0.2f, 1f, 0.2f, 1f),
|
|
HelperStatus.Transporting => new Vector4(1f, 0.8f, 0f, 1f),
|
|
HelperStatus.InDungeon => new Vector4(1f, 0.3f, 0.3f, 1f),
|
|
_ => colorSecondary,
|
|
};
|
|
string value = config.CurrentHelperStatus switch
|
|
{
|
|
HelperStatus.Available => "Available",
|
|
HelperStatus.Transporting => "Transporting",
|
|
HelperStatus.InDungeon => "In Dungeon",
|
|
_ => "Unknown",
|
|
};
|
|
ImU8String text2 = new ImU8String(8, 1);
|
|
text2.AppendLiteral("Status: ");
|
|
text2.AppendFormatted(value);
|
|
ImGui.TextColored(in col, text2);
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
if (!string.IsNullOrEmpty(config.AssignedQuester))
|
|
{
|
|
Vector4 col2 = new Vector4(0.2f, 1f, 0.2f, 1f);
|
|
ImU8String text3 = new ImU8String(18, 1);
|
|
text3.AppendLiteral("Assigned Quester: ");
|
|
text3.AppendFormatted(config.AssignedQuester);
|
|
ImGui.TextColored(in col2, text3);
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextColored(in colorSecondary, "Assigned Quester: None");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
}
|
|
float v3 = config.ChauffeurStopDistance;
|
|
ImGui.SetNextItemWidth(200f);
|
|
if (ImGui.SliderFloat("Stop Distance (yalms)", ref v3, 2f, 15f, "%.1f"))
|
|
{
|
|
config.ChauffeurStopDistance = v3;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("How close you bring the quester to their destination\n(2-15 yalms, default: 5)");
|
|
ImU8String text4 = new ImU8String(15, 1);
|
|
text4.AppendLiteral("Current: ");
|
|
text4.AppendFormatted(config.ChauffeurStopDistance, "F1");
|
|
text4.AppendLiteral(" yalms");
|
|
ImGui.TextWrapped(text4);
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
List<(uint, string, byte)> list = (Plugin.Instance?.GetChauffeurMode())?.GetMultiSeaterMounts() ?? new List<(uint, string, byte)>();
|
|
if (list.Count == 0)
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.3f, 0.3f, 1f), "No multi-seater mounts found!");
|
|
ImGui.TextWrapped("Make sure you have unlocked at least one multi-seater mount.");
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextWrapped("Select Multi-Seater Mount:");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
int num = 0;
|
|
List<string> list2 = new List<string>();
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
var (num2, value2, value3) = list[i];
|
|
list2.Add($"{value2} (Passengers: {value3})");
|
|
if (num2 == config.ChauffeurMountId)
|
|
{
|
|
num = i;
|
|
}
|
|
}
|
|
list2.Insert(0, "-- Not Selected --");
|
|
num = ((config.ChauffeurMountId != 0) ? (num + 1) : 0);
|
|
ImGui.SetNextItemWidth(300f);
|
|
if (ImGui.Combo((ImU8String)"##MountSelect", ref num, (ReadOnlySpan<string>)list2.ToArray(), list2.Count))
|
|
{
|
|
if (num == 0)
|
|
{
|
|
config.ChauffeurMountId = 0u;
|
|
}
|
|
else
|
|
{
|
|
(uint, string, byte) tuple2 = list[num - 1];
|
|
config.ChauffeurMountId = tuple2.Item1;
|
|
}
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("This mount will be used to transport the Quester");
|
|
if (config.ChauffeurMountId == 0)
|
|
{
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
ImGui.TextColored(new Vector4(1f, 0.8f, 0.2f, 1f), "Please select a mount to enable Chauffeur Mode");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, config.ChauffeurModeEnabled);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Helper Following", delegate
|
|
{
|
|
ImGui.TextWrapped("Helper passively follows Quester and maintains a configurable distance. Automatically navigates when too far away.");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
if (config.IsQuester)
|
|
{
|
|
ImGui.TextColored(new Vector4(0.7f, 0.9f, 1f, 1f), "Quester Settings:");
|
|
ImGui.TextWrapped("Select which Helper should follow you. Your position will be broadcasted to this Helper.");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
string assignedHelperForFollowing = config.AssignedHelperForFollowing;
|
|
ImGui.Text("Assigned Helper:");
|
|
ImGui.SameLine();
|
|
if (string.IsNullOrEmpty(assignedHelperForFollowing))
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "None");
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextColored(new Vector4(0.2f, 1f, 0.2f, 1f), assignedHelperForFollowing);
|
|
}
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
ImGui.Text("Auto-Discovered Helpers:");
|
|
ImGui.SetNextItemWidth(300f);
|
|
List<(string, ushort)> availableHelpers = plugin.GetAvailableHelpers();
|
|
if (ImGui.BeginCombo("##HelperDropdown", string.IsNullOrEmpty(assignedHelperForFollowing) ? "Select Helper..." : assignedHelperForFollowing))
|
|
{
|
|
if (availableHelpers.Count == 0)
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "No helpers discovered yet");
|
|
ImGui.TextWrapped("Helpers are auto-discovered via IPC when they have 'I'm a High-Level Helper' enabled.");
|
|
}
|
|
else
|
|
{
|
|
foreach (var item7 in availableHelpers)
|
|
{
|
|
string item = item7.Item1;
|
|
ushort item2 = item7.Item2;
|
|
ExcelSheet<World> excelSheet = Plugin.DataManager.GetExcelSheet<World>();
|
|
string text = "Unknown";
|
|
if (excelSheet != null)
|
|
{
|
|
foreach (World current2 in excelSheet)
|
|
{
|
|
if (current2.RowId == item2)
|
|
{
|
|
text = current2.Name.ToString();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
string text2 = item + "@" + text;
|
|
bool selected = assignedHelperForFollowing == text2;
|
|
if (ImGui.Selectable(text2, selected))
|
|
{
|
|
config.AssignedHelperForFollowing = text2;
|
|
config.Save();
|
|
}
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
DrawInfoIcon("Select the Helper from auto-discovered helpers.\nHelpers are discovered via IPC when they broadcast their status.");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
bool v = config.EnableHelperFollowing;
|
|
if (string.IsNullOrEmpty(config.AssignedHelperForFollowing))
|
|
{
|
|
ImGui.BeginDisabled();
|
|
}
|
|
if (ImGui.Checkbox("Enable Position Broadcasting", ref v))
|
|
{
|
|
config.EnableHelperFollowing = v;
|
|
config.Save();
|
|
}
|
|
if (string.IsNullOrEmpty(config.AssignedHelperForFollowing))
|
|
{
|
|
ImGui.EndDisabled();
|
|
}
|
|
DrawInfoIcon("Enable to broadcast your position to the assigned Helper.\nThe Helper can then follow you automatically.");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
if (config.EnableHelperFollowing && !string.IsNullOrEmpty(config.AssignedHelperForFollowing))
|
|
{
|
|
ImGui.TextColored(new Vector4(0.2f, 1f, 0.2f, 1f), "✓ Broadcasting position to Helper");
|
|
}
|
|
else if (!string.IsNullOrEmpty(config.AssignedHelperForFollowing))
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "⚠ Enable broadcasting to start");
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "⚠ Select a Helper first");
|
|
}
|
|
}
|
|
else if (!config.IsHighLevelHelper)
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.8f, 0.2f, 1f), "Please select a role (Quester or Helper) above");
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextColored(new Vector4(0.7f, 0.9f, 1f, 1f), "Helper Settings:");
|
|
ImGui.TextWrapped("Select which Quester to follow. You will only follow this specific Quester.");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
string assignedQuesterForFollowing = config.AssignedQuesterForFollowing;
|
|
ImGui.Text("Assigned Quester:");
|
|
ImGui.SameLine();
|
|
if (string.IsNullOrEmpty(assignedQuesterForFollowing))
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.3f, 0.3f, 1f), "⚠ None - Helper Following disabled!");
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextColored(new Vector4(0.2f, 1f, 0.2f, 1f), assignedQuesterForFollowing);
|
|
}
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
ImGui.Text("Auto-Discovered Questers:");
|
|
ImGui.SetNextItemWidth(300f);
|
|
List<string> list = (Plugin.Instance?.GetChauffeurMode())?.GetDiscoveredQuesters() ?? new List<string>();
|
|
if (ImGui.BeginCombo("##QuesterDropdown", string.IsNullOrEmpty(assignedQuesterForFollowing) ? "Select Quester..." : assignedQuesterForFollowing))
|
|
{
|
|
if (list.Count == 0)
|
|
{
|
|
ImGui.TextColored(new Vector4(1f, 0.5f, 0f, 1f), "No questers discovered yet");
|
|
ImGui.TextWrapped("Questers are auto-discovered when they broadcast position.");
|
|
ImGui.TextWrapped("Make sure the Quester has Helper Following enabled and has assigned you as Helper.");
|
|
}
|
|
else
|
|
{
|
|
foreach (string current3 in list)
|
|
{
|
|
bool selected2 = assignedQuesterForFollowing == current3;
|
|
if (ImGui.Selectable(current3, selected2))
|
|
{
|
|
config.AssignedQuesterForFollowing = current3;
|
|
config.Save();
|
|
}
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
DrawInfoIcon("Questers are automatically discovered via IPC when they broadcast position.\nSelect the Quester you want to follow from the list.");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
bool v2 = config.EnableHelperFollowing;
|
|
if (string.IsNullOrEmpty(config.AssignedQuesterForFollowing))
|
|
{
|
|
ImGui.BeginDisabled();
|
|
}
|
|
if (ImGui.Checkbox("Enable Helper Following", ref v2))
|
|
{
|
|
config.EnableHelperFollowing = v2;
|
|
config.Save();
|
|
}
|
|
if (string.IsNullOrEmpty(config.AssignedQuesterForFollowing))
|
|
{
|
|
ImGui.EndDisabled();
|
|
}
|
|
DrawInfoIcon("Helper will automatically follow the assigned Quester and maintain distance.\nStops in restricted zones (Main Cities) and when Chauffeur Mode is active.");
|
|
if (config.EnableHelperFollowing)
|
|
{
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.Indent();
|
|
float v3 = config.HelperFollowDistance;
|
|
if (ImGui.SliderFloat("Follow Distance (yalms)", ref v3, 50f, 200f, "%.0f"))
|
|
{
|
|
config.HelperFollowDistance = v3;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Distance to maintain from Quester.\nHelper will navigate when further than this distance.\nRecommended: 80-120 yalms");
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
int v4 = config.HelperFollowCheckInterval;
|
|
if (ImGui.SliderInt("Check Interval (seconds)", ref v4, 3, 15))
|
|
{
|
|
config.HelperFollowCheckInterval = v4;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("How often to check distance to Quester.\nLower values = more responsive but more CPU usage.\nRecommended: 5 seconds");
|
|
ImGui.Unindent();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
ImGui.TextColored(new Vector4(0.7f, 0.9f, 1f, 1f), "Status:");
|
|
if (Plugin.Instance?.GetChauffeurMode() != null)
|
|
{
|
|
ImU8String text3 = new ImU8String(23, 1);
|
|
text3.AppendLiteral("Follow Distance: ");
|
|
text3.AppendFormatted(config.HelperFollowDistance, "F0");
|
|
text3.AppendLiteral(" yalms");
|
|
ImGui.TextWrapped(text3);
|
|
ImU8String text4 = new ImU8String(17, 1);
|
|
text4.AppendLiteral("Check Interval: ");
|
|
text4.AppendFormatted(config.HelperFollowCheckInterval);
|
|
text4.AppendLiteral("s");
|
|
ImGui.TextWrapped(text4);
|
|
}
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
ImGui.TextColored(new Vector4(0.8f, 0.8f, 0.8f, 1f), "Note: Helper Following automatically stops when:");
|
|
ImGui.BulletText("Entering restricted zones (Main Cities)");
|
|
ImGui.BulletText("Chauffeur Mode summon is active");
|
|
ImGui.BulletText("Quester leaves party");
|
|
ImGui.BulletText("Quester changes to different zone");
|
|
}
|
|
}
|
|
}, config.EnableHelperFollowing);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Movement Monitor", delegate
|
|
{
|
|
config.EnableMovementMonitor = DrawSettingWithInfo("Enable Movement Monitor", config.EnableMovementMonitor, "Automatically detects if player is stuck and sends /qst reload.\nMonitors player position and detects lack of movement.\nImpact: Quest will auto-reload if stuck for too long.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
if (config.EnableMovementMonitor)
|
|
{
|
|
ImGui.Indent();
|
|
int v = config.MovementCheckInterval;
|
|
if (ImGui.SliderInt("Check Interval (seconds)", ref v, 3, 30))
|
|
{
|
|
config.MovementCheckInterval = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("How often to check player position.\nLower values = faster stuck detection.");
|
|
int v2 = config.MovementStuckThreshold;
|
|
if (ImGui.SliderInt("Stuck Threshold (seconds)", ref v2, 15, 120))
|
|
{
|
|
config.MovementStuckThreshold = v2;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Time without movement before considering player stuck.\nHigher values = less false positives.");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.EnableMovementMonitor);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Combat Handling", delegate
|
|
{
|
|
config.EnableCombatHandling = DrawSettingWithInfo("Enable Combat Handling", config.EnableCombatHandling, "Automatically enables combat automation when HP drops below threshold.\nActivates RSR/VBMAI/BMRAI during dangerous situations.\nImpact: Combat plugins will auto-enable when HP is low.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
if (config.EnableCombatHandling)
|
|
{
|
|
ImGui.Indent();
|
|
int v = config.CombatHPThreshold;
|
|
if (ImGui.SliderInt("HP Threshold (%)", ref v, 1, 99))
|
|
{
|
|
config.CombatHPThreshold = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Enable combat automation when HP drops below this percentage.\nCommands: /rsr manual, /vbmai on, /bmrai on");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.EnableCombatHandling);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Death Handling", delegate
|
|
{
|
|
config.EnableDeathHandling = DrawSettingWithInfo("Enable Death Handling", config.EnableDeathHandling, "Automatically respawns and teleports back to death location.\nSaves position before death and returns after respawn.\nImpact: Deaths will be handled automatically during rotation.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
if (config.EnableDeathHandling)
|
|
{
|
|
ImGui.Indent();
|
|
int v = config.DeathRespawnDelay;
|
|
if (ImGui.SliderInt("Teleport Delay (seconds)", ref v, 1, 30))
|
|
{
|
|
config.DeathRespawnDelay = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Time to wait after respawn before teleporting back to death location.\nAllows time for loading and stabilization.");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.EnableDeathHandling);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Quest Automation", delegate
|
|
{
|
|
config.EnableQSTReloadTracking = DrawSettingWithInfo("Enable QST Reload Tracking", config.EnableQSTReloadTracking, "Tracks how many times /qst reload is called.\nSwitches character if reload count exceeds threshold.\nImpact: Prevents infinite reload loops by switching characters.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
if (config.EnableQSTReloadTracking)
|
|
{
|
|
ImGui.Indent();
|
|
int v = config.MaxQSTReloadsBeforeSwitch;
|
|
if (ImGui.SliderInt("Max Reloads Before Switch", ref v, 3, 20))
|
|
{
|
|
config.MaxQSTReloadsBeforeSwitch = v;
|
|
config.Save();
|
|
}
|
|
DrawInfoIcon("Maximum number of /qst reload commands before switching to next character.\nPrevents getting stuck on problematic quests.");
|
|
ImGui.Unindent();
|
|
}
|
|
}, config.EnableQSTReloadTracking);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Character Management", delegate
|
|
{
|
|
config.EnableMultiModeAfterRotation = DrawSettingWithInfo("Enable Multi-Mode After Rotation", config.EnableMultiModeAfterRotation, "Automatically enables AutoRetainer multi-mode after rotation completes.\nAllows retainer/submarine management after quest rotation.\nImpact: Multi-mode will activate when all quests are done.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
config.ReturnToHomeworldOnStopQuest = DrawSettingWithInfo("Return to Homeworld on Stop Quest", config.ReturnToHomeworldOnStopQuest, "Automatically returns character to home world when rotation stops.\nUses /li command to return home.\nImpact: Characters will be sent home after completing their quests.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
}, config.EnableMultiModeAfterRotation);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Safe Wait Settings", delegate
|
|
{
|
|
config.EnableSafeWaitBeforeCharacterSwitch = DrawSettingWithInfo("Enable Safe Wait Before Character Switch", config.EnableSafeWaitBeforeCharacterSwitch, "Waits for safe conditions before switching characters.\nChecks for combat, cutscenes, and loading screens.\nImpact: Character switches will be delayed until safe.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
config.EnableSafeWaitAfterCharacterSwitch = DrawSettingWithInfo("Enable Safe Wait After Character Switch", config.EnableSafeWaitAfterCharacterSwitch, "Waits for safe conditions after logging in new character.\nEnsures character is fully loaded before starting quests.\nImpact: Quest start will be delayed until character is ready.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
}, config.EnableSafeWaitBeforeCharacterSwitch);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
DrawSettingSection("Quest Pre-Check", delegate
|
|
{
|
|
config.EnableQuestPreCheck = DrawSettingWithInfo("Enable Quest Pre-Check", config.EnableQuestPreCheck, "Scans completed quests before starting rotation.\nSkips characters who already completed target quests.\nImpact: Saves time by not processing already-completed quests.");
|
|
if (ImGui.IsItemDeactivatedAfterEdit())
|
|
{
|
|
config.Save();
|
|
}
|
|
}, config.EnableQuestPreCheck);
|
|
ImGuiHelpers.ScaledDummy(20f);
|
|
}
|
|
|
|
private void DrawSettingSection(string title, System.Action drawContent, bool isEnabled = false)
|
|
{
|
|
Vector2 cursorScreenPos = ImGui.GetCursorScreenPos();
|
|
float availWidth = ImGui.GetContentRegionAvail().X;
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
ImGui.ColorConvertFloat4ToU32(new Vector4(0.12f, 0.12f, 0.15f, 0.8f));
|
|
Vector4 borderColor;
|
|
if (isEnabled)
|
|
{
|
|
float pulse = (MathF.Sin((float)ImGui.GetTime() * 2f) + 1f) / 2f;
|
|
borderColor = new Vector4(0.47f, 0.69f, 0.88f, 0.5f + pulse * 0.5f);
|
|
}
|
|
else
|
|
{
|
|
borderColor = new Vector4(colorPrimary.X * 0.5f, colorPrimary.Y * 0.5f, colorPrimary.Z * 0.5f, 0.6f);
|
|
}
|
|
uint borderColorU32 = ImGui.ColorConvertFloat4ToU32(borderColor);
|
|
Vector2 boxStart = cursorScreenPos;
|
|
ImGui.PushStyleColor(ImGuiCol.Header, new Vector4(colorPrimary.X * 0.3f, colorPrimary.Y * 0.3f, colorPrimary.Z * 0.3f, 0.5f));
|
|
ImGui.PushStyleColor(ImGuiCol.HeaderHovered, new Vector4(colorPrimary.X * 0.4f, colorPrimary.Y * 0.4f, colorPrimary.Z * 0.4f, 0.6f));
|
|
ImGui.PushStyleColor(ImGuiCol.HeaderActive, new Vector4(colorPrimary.X * 0.5f, colorPrimary.Y * 0.5f, colorPrimary.Z * 0.5f, 0.7f));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
bool num = ImGui.CollapsingHeader(title, ImGuiTreeNodeFlags.DefaultOpen);
|
|
ImGui.PopStyleColor(4);
|
|
if (num)
|
|
{
|
|
ImGui.Indent(10f);
|
|
drawContent();
|
|
ImGui.Unindent(10f);
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
}
|
|
Vector2 boxEnd = ImGui.GetCursorScreenPos();
|
|
drawList.AddRect(boxStart, boxEnd + new Vector2(availWidth, 0f), borderColorU32, 4f, ImDrawFlags.None, isEnabled ? 2.5f : 1.5f);
|
|
}
|
|
|
|
private bool DrawSettingWithInfo(string label, bool value, string infoText)
|
|
{
|
|
ImGui.Checkbox(label, ref value);
|
|
ImGui.SameLine();
|
|
DrawInfoIcon(infoText);
|
|
return value;
|
|
}
|
|
|
|
private void DrawInfoIcon(string tooltipText)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("[i]");
|
|
ImGui.PopStyleColor();
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.PopupBg, new Vector4(0.1f, 0.1f, 0.1f, 0.95f));
|
|
ImGui.PushStyleColor(ImGuiCol.Border, colorPrimary);
|
|
ImGui.BeginTooltip();
|
|
ImGui.PushTextWrapPos(400f);
|
|
ImGui.TextUnformatted(tooltipText);
|
|
ImGui.PopTextWrapPos();
|
|
ImGui.EndTooltip();
|
|
ImGui.PopStyleColor(2);
|
|
}
|
|
}
|
|
|
|
private void DrawCharactersTab()
|
|
{
|
|
object obj = selectedDCFilter switch
|
|
{
|
|
0 => "All Characters",
|
|
1 => "EU Characters",
|
|
2 => "NA Characters",
|
|
3 => "JP Characters",
|
|
4 => "OCE Characters",
|
|
_ => "Characters",
|
|
};
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted((string?)obj);
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
List<string> filteredChars = GetFilteredCharacters();
|
|
if (!initialCharacterLoadComplete)
|
|
{
|
|
if (characterLoadAttempts < 5)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
double elapsedSeconds = (DateTime.Now - initialLoadStartTime).TotalSeconds;
|
|
int nextRetryIn = ((characterLoadAttempts < retryDelaysSeconds.Length) ? (retryDelaysSeconds[characterLoadAttempts] - (int)elapsedSeconds) : 0);
|
|
if (nextRetryIn > 0)
|
|
{
|
|
ImU8String text = new ImU8String(36, 3);
|
|
text.AppendLiteral("Loading characters... (Retry ");
|
|
text.AppendFormatted(characterLoadAttempts);
|
|
text.AppendLiteral("/");
|
|
text.AppendFormatted(5);
|
|
text.AppendLiteral(" in ");
|
|
text.AppendFormatted(nextRetryIn);
|
|
text.AppendLiteral("s)");
|
|
ImGui.TextUnformatted(text);
|
|
}
|
|
else
|
|
{
|
|
ImU8String text2 = new ImU8String(33, 2);
|
|
text2.AppendLiteral("Loading characters... (Attempt ");
|
|
text2.AppendFormatted(characterLoadAttempts + 1);
|
|
text2.AppendLiteral("/");
|
|
text2.AppendFormatted(5);
|
|
text2.AppendLiteral(")");
|
|
ImGui.TextUnformatted(text2);
|
|
}
|
|
ImGui.PopStyleColor();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Waiting for AutoRetainer to initialize...");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (ImGui.Button("Retry Now"))
|
|
{
|
|
characterLoadAttempts = 0;
|
|
initialLoadStartTime = DateTime.MinValue;
|
|
autoRetainerIpc.TryReinitialize();
|
|
RefreshCharacterList();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.3f, 0.3f, 1f));
|
|
ImGui.TextUnformatted("AutoRetainer not available");
|
|
ImGui.PopStyleColor();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Please ensure AutoRetainer plugin is installed and enabled.");
|
|
ImU8String text3 = new ImU8String(29, 1);
|
|
text3.AppendLiteral("Tried ");
|
|
text3.AppendFormatted(5);
|
|
text3.AppendLiteral(" times without success.");
|
|
ImGui.TextUnformatted(text3);
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (ImGui.Button("Retry Connection"))
|
|
{
|
|
characterLoadAttempts = 0;
|
|
initialLoadStartTime = DateTime.Now;
|
|
initialCharacterLoadComplete = false;
|
|
autoRetainerIpc.TryReinitialize();
|
|
RefreshCharacterList();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (!autoRetainerIpc.IsAvailable)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.8f, 0f, 1f));
|
|
ImGui.TextUnformatted("AutoRetainer connection lost");
|
|
ImGui.PopStyleColor();
|
|
ImGui.TextUnformatted("The connection to AutoRetainer was lost.");
|
|
if (ImGui.Button("Reconnect"))
|
|
{
|
|
autoRetainerIpc.TryReinitialize();
|
|
RefreshCharacterList();
|
|
}
|
|
return;
|
|
}
|
|
if (filteredChars.Count == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("No characters found");
|
|
ImGui.PopStyleColor();
|
|
return;
|
|
}
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
if (ImGui.Button("Refresh"))
|
|
{
|
|
RefreshCharacterList();
|
|
characterProgressCache.Clear();
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
ImGui.SameLine();
|
|
if (ImGui.Button("Select All"))
|
|
{
|
|
foreach (string character in filteredChars)
|
|
{
|
|
characterSelection[character] = true;
|
|
}
|
|
}
|
|
ImGui.SameLine();
|
|
if (ImGui.Button("Deselect All"))
|
|
{
|
|
foreach (string character2 in filteredChars)
|
|
{
|
|
characterSelection[character2] = false;
|
|
}
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.SetNextItemWidth(150f);
|
|
List<string> currentDCWorlds = GetWorldsForCurrentDatacenter();
|
|
if (ImGui.BeginCombo("##WorldFilter", selectedWorldFilter))
|
|
{
|
|
if (ImGui.Selectable("All", selectedWorldFilter == "All"))
|
|
{
|
|
selectedWorldFilter = "All";
|
|
}
|
|
foreach (string world in currentDCWorlds.OrderBy((string w) => w))
|
|
{
|
|
if (ImGui.Selectable(world, selectedWorldFilter == world))
|
|
{
|
|
selectedWorldFilter = world;
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
float cardWidth = (ImGui.GetContentRegionAvail().X - 20f) / 2f;
|
|
using ImRaii.IEndObject child = ImRaii.Child("CharacterCards", new Vector2(0f, 0f), border: false);
|
|
if (!child.Success)
|
|
{
|
|
return;
|
|
}
|
|
int cardIndex = 0;
|
|
foreach (string character3 in filteredChars)
|
|
{
|
|
string[] parts = character3.Split('@');
|
|
string charName = ((parts.Length != 0) ? parts[0] : character3);
|
|
string world2 = ((parts.Length > 1) ? parts[1] : "Unknown");
|
|
if (cardIndex % 2 == 1)
|
|
{
|
|
ImGui.SameLine();
|
|
}
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
Vector2 cursorPos = ImGui.GetCursorScreenPos();
|
|
float cardHeight = 90f;
|
|
Vector4 cardBg = new Vector4(0.15f, 0.15f, 0.18f, 0.9f);
|
|
uint borderColor = (characterSelection.GetValueOrDefault(character3, defaultValue: false) ? ImGui.ColorConvertFloat4ToU32(new Vector4(colorPrimary.X, colorPrimary.Y, colorPrimary.Z, 0.8f)) : ImGui.ColorConvertFloat4ToU32(new Vector4(colorPrimary.X * 0.3f, colorPrimary.Y * 0.3f, colorPrimary.Z * 0.3f, 0.5f)));
|
|
drawList.AddRectFilled(cursorPos, cursorPos + new Vector2(cardWidth, cardHeight), ImGui.ColorConvertFloat4ToU32(cardBg), 6f);
|
|
drawList.AddRect(cursorPos, cursorPos + new Vector2(cardWidth, cardHeight), borderColor, 6f, ImDrawFlags.None, 2f);
|
|
ImGui.SetCursorScreenPos(cursorPos + new Vector2(10f, 10f));
|
|
using (ImRaii.PushId(character3))
|
|
{
|
|
bool isSelected = characterSelection.GetValueOrDefault(character3, defaultValue: false);
|
|
if (ImGui.Checkbox("##Select", ref isSelected))
|
|
{
|
|
characterSelection[character3] = isSelected;
|
|
}
|
|
}
|
|
ImGui.SetCursorScreenPos(cursorPos + new Vector2(40f, 8f));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.Text(charName);
|
|
ImGui.PopStyleColor();
|
|
ImGui.SetCursorScreenPos(cursorPos + new Vector2(40f, 26f));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorAccent);
|
|
ImGui.Text(world2);
|
|
ImGui.PopStyleColor();
|
|
if (characterProgressCache.TryGetValue(character3, out CharacterProgressInfo progressInfo))
|
|
{
|
|
ImGui.SetCursorScreenPos(cursorPos + new Vector2(10f, 50f));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text4 = new ImU8String(7, 1);
|
|
text4.AppendFormatted(progressInfo.CompletedQuestCount);
|
|
text4.AppendLiteral(" Quests");
|
|
ImGui.Text(text4);
|
|
ImGui.PopStyleColor();
|
|
ImGui.SetCursorScreenPos(cursorPos + new Vector2(10f, 68f));
|
|
float barWidth = cardWidth - 20f;
|
|
float progress = progressInfo.MSQCompletionPercentage / 100f;
|
|
uint barBg = ImGui.ColorConvertFloat4ToU32(new Vector4(0.2f, 0.2f, 0.2f, 0.8f));
|
|
uint barFg = ImGui.ColorConvertFloat4ToU32(new Vector4(colorSecondary.X, colorSecondary.Y, colorSecondary.Z, 0.9f));
|
|
drawList.AddRectFilled(cursorPos + new Vector2(10f, 68f), cursorPos + new Vector2(10f + barWidth, 82f), barBg, 3f);
|
|
drawList.AddRectFilled(cursorPos + new Vector2(10f, 68f), cursorPos + new Vector2(10f + barWidth * progress, 82f), barFg, 3f);
|
|
string progressText = $"{progressInfo.MSQCompletionPercentage:F0}%";
|
|
Vector2 textSize = ImGui.CalcTextSize(progressText);
|
|
drawList.AddText(cursorPos + new Vector2(10f + barWidth / 2f - textSize.X / 2f, 69f), ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f)), progressText);
|
|
}
|
|
else
|
|
{
|
|
ImGui.SetCursorScreenPos(cursorPos + new Vector2(10f, 50f));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.Text("Loading...");
|
|
ImGui.PopStyleColor();
|
|
GetCharacterProgress(character3);
|
|
}
|
|
ImGui.SetCursorScreenPos(cursorPos);
|
|
ImGui.Dummy(new Vector2(cardWidth, cardHeight));
|
|
cardIndex++;
|
|
}
|
|
}
|
|
|
|
private void DrawStopPointsTab()
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Quest Rotation System");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
List<string> selectedCharacters = (from kvp in characterSelection
|
|
where kvp.Value
|
|
select kvp.Key).ToList();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextWrapped("Rotates your selected Characters depending on the Stop Configurations you have enabled in Questionable. Please Configure a Quest and / or Level for a Rotation to be able to start.");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorPrimary);
|
|
if (ImGui.Button("Import from Questionable"))
|
|
{
|
|
questRotationService.ImportStopPointsFromQuestionable();
|
|
log.Information("[StopPoints] Imported stop points from Questionable");
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Pull stop quests and sequences from Questionable configuration");
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
if (ImGui.Button("Open Questionable Settings"))
|
|
{
|
|
Plugin.CommandManager.ProcessCommand("/qst config");
|
|
log.Information("[StopPoints] Opened Questionable settings");
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Open Questionable plugin settings window");
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Level Stop Condition:");
|
|
ImGui.PopStyleColor();
|
|
StopConditionData levelStopCondition = plugin.QuestionableIPC.GetLevelStopCondition();
|
|
if (levelStopCondition != null && levelStopCondition.Enabled)
|
|
{
|
|
ImGui.SetWindowFontScale(1.2f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text = new ImU8String(15, 1);
|
|
text.AppendLiteral("Stop at Level: ");
|
|
text.AppendFormatted(levelStopCondition.TargetValue);
|
|
ImGui.TextUnformatted(text);
|
|
ImGui.PopStyleColor();
|
|
ImGui.SetWindowFontScale(1f);
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0.6f, 0.6f, 0.6f, 1f));
|
|
ImGui.TextUnformatted("Not configured");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.TextUnformatted("Active Stop Points:");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
List<StopPoint> stopPoints = questRotationService.GetAllStopPoints();
|
|
if (stopPoints.Count == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("No stop points configured.");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else
|
|
{
|
|
using ImRaii.IEndObject table = ImRaii.Table("StopPointsTable", 5, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg);
|
|
if (table.Success)
|
|
{
|
|
ImGui.TableSetupColumn("Stop Point", ImGuiTableColumnFlags.WidthFixed, 150f);
|
|
ImGui.TableSetupColumn("Status", ImGuiTableColumnFlags.WidthFixed, 80f);
|
|
ImGui.TableSetupColumn("Remaining", ImGuiTableColumnFlags.WidthFixed, 100f);
|
|
ImGui.TableSetupColumn("Progress", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 60f);
|
|
ImGui.TableHeadersRow();
|
|
for (int i = 0; i < stopPoints.Count; i++)
|
|
{
|
|
StopPoint stopPoint = stopPoints[i];
|
|
(int completed, int total) rotationProgress = questRotationService.GetRotationProgress(stopPoint.QuestId);
|
|
int completed = rotationProgress.completed;
|
|
int total = rotationProgress.total;
|
|
int num = total - completed;
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(stopPoint.DisplayName);
|
|
ImGui.TableNextColumn();
|
|
if (stopPoint.IsActive)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Active");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Queued");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGui.TableNextColumn();
|
|
Vector4 remainingColor = ((num == 0) ? colorPrimary : colorSecondary);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, remainingColor);
|
|
ImU8String text2 = new ImU8String(1, 2);
|
|
text2.AppendFormatted(completed);
|
|
text2.AppendLiteral("/");
|
|
text2.AppendFormatted(total);
|
|
ImGui.TextUnformatted(text2);
|
|
ImGui.PopStyleColor();
|
|
ImGui.TableNextColumn();
|
|
float progress = ((total > 0) ? ((float)completed / (float)total) : 0f);
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
Vector2 cursorPos = ImGui.GetCursorScreenPos();
|
|
float barWidth = ImGui.GetContentRegionAvail().X;
|
|
float barHeight = 20f;
|
|
uint barBg = ImGui.ColorConvertFloat4ToU32(new Vector4(0.2f, 0.2f, 0.2f, 0.8f));
|
|
drawList.AddRectFilled(cursorPos, cursorPos + new Vector2(barWidth, barHeight), barBg, 4f);
|
|
Vector4 barColor = ((progress >= 0.85f) ? colorPrimary : ((progress >= 0.5f) ? colorSecondary : colorAccent));
|
|
uint barFg = ImGui.ColorConvertFloat4ToU32(new Vector4(barColor.X, barColor.Y, barColor.Z, 0.9f));
|
|
drawList.AddRectFilled(cursorPos, cursorPos + new Vector2(barWidth * progress, barHeight), barFg, 4f);
|
|
string progressText = $"{(int)(progress * 100f)}%";
|
|
Vector2 textSize = ImGui.CalcTextSize(progressText);
|
|
drawList.AddText(cursorPos + new Vector2(barWidth / 2f - textSize.X / 2f, 2f), ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f)), progressText);
|
|
ImGui.Dummy(new Vector2(barWidth, barHeight));
|
|
ImGui.TableNextColumn();
|
|
using (ImRaii.PushId(i))
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
if (ImGui.Button("X"))
|
|
{
|
|
questRotationService.RemoveStopPoint(stopPoint.QuestId);
|
|
log.Information($"[StopPoints] Removed stop quest {stopPoint.QuestId}");
|
|
}
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ImGuiHelpers.ScaledDummy(15f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Current Status:");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
RotationState state = questRotationService.GetCurrentState();
|
|
ImU8String text3 = new ImU8String(7, 0);
|
|
text3.AppendLiteral("Phase: ");
|
|
ImGui.TextUnformatted(text3);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, state.Phase switch
|
|
{
|
|
RotationPhase.Idle => colorSecondary,
|
|
RotationPhase.Questing => colorPrimary,
|
|
RotationPhase.QuestActive => colorPrimary,
|
|
RotationPhase.InCombat => new Vector4(1f, 0.5f, 0.2f, 1f),
|
|
RotationPhase.InDungeon => new Vector4(0.8f, 0.4f, 1f, 1f),
|
|
RotationPhase.HandlingSubmarines => new Vector4(0.2f, 0.8f, 1f, 1f),
|
|
RotationPhase.WaitingForChauffeur => new Vector4(1f, 1f, 0.4f, 1f),
|
|
RotationPhase.TravellingWithChauffeur => new Vector4(0.4f, 1f, 0.4f, 1f),
|
|
RotationPhase.DCTraveling => new Vector4(0.5f, 0.5f, 1f, 1f),
|
|
RotationPhase.Completed => colorPrimary,
|
|
RotationPhase.Error => colorAccent,
|
|
_ => colorSecondary,
|
|
});
|
|
ImGui.TextUnformatted(state.Phase.ToString());
|
|
ImGui.PopStyleColor();
|
|
if (!string.IsNullOrEmpty(state.CurrentCharacter))
|
|
{
|
|
ImU8String text4 = new ImU8String(11, 0);
|
|
text4.AppendLiteral("Logged In: ");
|
|
ImGui.TextUnformatted(text4);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted(state.CurrentCharacter);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (state.CurrentStopQuestId != 0)
|
|
{
|
|
ImU8String text5 = new ImU8String(14, 0);
|
|
text5.AppendLiteral("Target Quest: ");
|
|
ImGui.TextUnformatted(text5);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted(state.CurrentStopQuestId.ToString());
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (!string.IsNullOrEmpty(state.NextCharacter))
|
|
{
|
|
ImU8String text6 = new ImU8String(16, 0);
|
|
text6.AppendLiteral("Next Character: ");
|
|
ImGui.TextUnformatted(text6);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted(state.NextCharacter);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (state.RemainingCharacters.Count > 0)
|
|
{
|
|
ImU8String text7 = new ImU8String(11, 1);
|
|
text7.AppendLiteral("Remaining: ");
|
|
text7.AppendFormatted(string.Join(", ", state.RemainingCharacters));
|
|
ImGui.TextUnformatted(text7);
|
|
}
|
|
if (state.Phase == RotationPhase.Error && !string.IsNullOrEmpty(state.ErrorMessage))
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorAccent);
|
|
ImU8String text8 = new ImU8String(7, 1);
|
|
text8.AppendLiteral("Error: ");
|
|
text8.AppendFormatted(state.ErrorMessage);
|
|
ImGui.TextUnformatted(text8);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (state.SelectedCharacters.Count > 0)
|
|
{
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
float fraction = (float)state.CompletedCharacters.Count / (float)state.SelectedCharacters.Count;
|
|
Vector2 sizeArg = new Vector2(-1f, 0f);
|
|
ImU8String overlay = new ImU8String(11, 2);
|
|
overlay.AppendFormatted(state.CompletedCharacters.Count);
|
|
overlay.AppendLiteral("/");
|
|
overlay.AppendFormatted(state.SelectedCharacters.Count);
|
|
overlay.AppendLiteral(" completed");
|
|
ImGui.ProgressBar(fraction, sizeArg, overlay);
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (selectedCharacters.Count == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Select characters in the Characters tab to start rotation");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else if (stopPoints.Count == 0 && (levelStopCondition == null || !levelStopCondition.Enabled))
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Configure a Quest or Level stop condition above to start rotation");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else if (state.Phase == RotationPhase.Idle || state.Phase == RotationPhase.Completed || state.Phase == RotationPhase.Error)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
if (ImGui.Button("Start Rotation", new Vector2(200f, 30f)))
|
|
{
|
|
log.Information("[StopPoints] Start Rotation button clicked!");
|
|
log.Information($"[StopPoints] Selected characters: {selectedCharacters.Count}");
|
|
log.Information($"[StopPoints] Stop points: {stopPoints.Count}");
|
|
if (stopPoints.Count > 0)
|
|
{
|
|
foreach (StopPoint item in stopPoints)
|
|
{
|
|
item.IsActive = false;
|
|
}
|
|
bool foundValidStopPoint = false;
|
|
for (int i2 = 0; i2 < stopPoints.Count; i2++)
|
|
{
|
|
StopPoint stopPoint2 = stopPoints[i2];
|
|
var (completed2, total2) = questRotationService.GetRotationProgress(stopPoint2.QuestId, selectedCharacters);
|
|
if (completed2 < total2)
|
|
{
|
|
log.Information("[StopPoints] Starting rotation with: " + stopPoint2.DisplayName);
|
|
log.Information($"[StopPoints] Progress: {completed2}/{total2} completed");
|
|
log.Information($"[StopPoints] Total stop points in queue: {stopPoints.Count - i2}");
|
|
for (int j = i2; j < stopPoints.Count; j++)
|
|
{
|
|
stopPoints[j].IsActive = j == i2;
|
|
}
|
|
if (questRotationService.StartRotation(stopPoint2.QuestId, selectedCharacters))
|
|
{
|
|
log.Information("[StopPoints] Rotation started successfully!");
|
|
foundValidStopPoint = true;
|
|
}
|
|
else
|
|
{
|
|
log.Error("[StopPoints] Failed to start rotation");
|
|
}
|
|
break;
|
|
}
|
|
log.Information($"[StopPoints] Skipping {stopPoint2.DisplayName} - all characters completed ({completed2}/{total2})");
|
|
}
|
|
if (!foundValidStopPoint)
|
|
{
|
|
log.Warning("[StopPoints] All stop points already completed by all characters!");
|
|
}
|
|
}
|
|
else if (levelStopCondition != null && levelStopCondition.Enabled)
|
|
{
|
|
log.Information($"[StopPoints] Starting level-only rotation (target level: {levelStopCondition.TargetValue})");
|
|
if (questRotationService.StartRotationLevelOnly(selectedCharacters))
|
|
{
|
|
log.Information("[StopPoints] Level-only rotation started successfully!");
|
|
}
|
|
else
|
|
{
|
|
log.Error("[StopPoints] Failed to start level-only rotation");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log.Warning("[StopPoints] No stop points or level condition configured!");
|
|
}
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
if (ImGui.Button("Stop Rotation", new Vector2(200f, 30f)))
|
|
{
|
|
questRotationService.AbortRotation();
|
|
log.Information("[StopPoints] Stopped rotation");
|
|
}
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
|
|
private void DrawMSQProgressionTab()
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImGui.TextUnformatted("Main Scenario Quest Progression");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
List<string> filteredChars = GetFilteredCharacters();
|
|
Configuration config = plugin.Configuration;
|
|
if (filteredChars.Count == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("No characters to display");
|
|
ImGui.PopStyleColor();
|
|
return;
|
|
}
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
if (ImGui.Button("Refresh Progress"))
|
|
{
|
|
characterProgressCache.Clear();
|
|
log.Information("[MSQProgression] Manual progress refresh requested");
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
ImGui.SameLine();
|
|
RotationState state = questRotationService.GetCurrentState();
|
|
if (state.Phase == RotationPhase.Idle || state.Phase == RotationPhase.Completed || state.Phase == RotationPhase.Error || state.CurrentStopQuestId != 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorSecondary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorPrimary);
|
|
if (ImGui.Button("First Time Sync"))
|
|
{
|
|
log.Information("[MSQProgression] First Time Sync requested");
|
|
if (questRotationService.StartSyncRotation(filteredChars))
|
|
{
|
|
log.Information("[MSQProgression] Sync rotation started - will go through all characters without data");
|
|
}
|
|
else
|
|
{
|
|
log.Information("[MSQProgression] No characters need sync or failed to start");
|
|
}
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Automatically sync MSQ data for all characters without existing data");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorPrimary);
|
|
if (ImGui.Button("Stop Syncing"))
|
|
{
|
|
log.Information("[MSQProgression] Stop Syncing requested");
|
|
questRotationService.AbortRotation();
|
|
characterProgressCache.Clear();
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Stop the sync rotation");
|
|
}
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.TextUnformatted("Display Mode:");
|
|
ImGui.SameLine();
|
|
int displayMode = (int)config.MSQDisplayMode;
|
|
ImGui.SetNextItemWidth(200f);
|
|
if (ImGui.Combo("##MSQDisplayMode", ref displayMode, "Current Expansion\0Overall Progress\0Expansion Breakdown\0"))
|
|
{
|
|
config.MSQDisplayMode = (MSQDisplayMode)displayMode;
|
|
config.Save();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
switch (config.MSQDisplayMode)
|
|
{
|
|
case MSQDisplayMode.CurrentExpansion:
|
|
DrawMSQCurrentExpansion(filteredChars);
|
|
break;
|
|
case MSQDisplayMode.Overall:
|
|
DrawMSQOverall(filteredChars);
|
|
break;
|
|
case MSQDisplayMode.ExpansionBreakdown:
|
|
DrawMSQExpansionBreakdown(filteredChars);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void DrawMSQCurrentExpansion(List<string> characters)
|
|
{
|
|
using ImRaii.IEndObject table = ImRaii.Table("MSQCurrentExpTable", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY);
|
|
if (!table.Success)
|
|
{
|
|
return;
|
|
}
|
|
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Current Expansion", ImGuiTableColumnFlags.WidthFixed, 150f);
|
|
ImGui.TableSetupColumn("Progress", ImGuiTableColumnFlags.WidthFixed, 100f);
|
|
ImGui.TableSetupColumn("Completion", ImGuiTableColumnFlags.WidthFixed, 120f);
|
|
ImGui.TableSetupScrollFreeze(0, 1);
|
|
ImGui.TableHeadersRow();
|
|
foreach (string character in characters)
|
|
{
|
|
if (!characterProgressCache.TryGetValue(character, out CharacterProgressInfo _))
|
|
{
|
|
GetCharacterProgress(character);
|
|
continue;
|
|
}
|
|
List<uint> completedQuestsList = questRotationService.GetCompletedQuestsByCharacter(character);
|
|
MSQExpansionData.Expansion currentExpansion = MSQExpansionData.GetCurrentExpansion(completedQuestsList);
|
|
ExpansionInfo currentExp = new ExpansionInfo
|
|
{
|
|
Name = MSQExpansionData.GetExpansionName(currentExpansion),
|
|
ShortName = MSQExpansionData.GetExpansionShortName(currentExpansion),
|
|
MinQuestId = 0u,
|
|
MaxQuestId = 0u,
|
|
ExpectedQuestCount = MSQExpansionData.GetExpectedQuestCount(currentExpansion)
|
|
};
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
string[] parts = character.Split('@');
|
|
ImGui.TextUnformatted((parts.Length != 0) ? parts[0] : character);
|
|
ImGui.TableNextColumn();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted(currentExp?.Name ?? "A Realm Reborn");
|
|
ImGui.PopStyleColor();
|
|
ImGui.TableNextColumn();
|
|
if (currentExp != null)
|
|
{
|
|
(int completed, int total) valueOrDefault = msqProgressionService.GetExpansionProgressForCharacter(completedQuestsList).GetValueOrDefault(currentExp.ShortName, (0, 0));
|
|
int completed = valueOrDefault.completed;
|
|
int total = valueOrDefault.total;
|
|
ImU8String text = new ImU8String(1, 2);
|
|
text.AppendFormatted(completed);
|
|
text.AppendLiteral("/");
|
|
text.AppendFormatted(total);
|
|
ImGui.TextUnformatted(text);
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextUnformatted("0/0");
|
|
}
|
|
ImGui.TableNextColumn();
|
|
if (currentExp != null)
|
|
{
|
|
(int completed, int total) valueOrDefault2 = msqProgressionService.GetExpansionProgressForCharacter(completedQuestsList).GetValueOrDefault(currentExp.ShortName, (0, 0));
|
|
int completed2 = valueOrDefault2.completed;
|
|
int total2 = valueOrDefault2.total;
|
|
float percentage = ((total2 > 0) ? ((float)completed2 / (float)total2) : 0f);
|
|
Vector2 sizeArg = new Vector2(-1f, 0f);
|
|
ImU8String overlay = new ImU8String(1, 1);
|
|
overlay.AppendFormatted((int)(percentage * 100f));
|
|
overlay.AppendLiteral("%");
|
|
ImGui.ProgressBar(percentage, sizeArg, overlay);
|
|
}
|
|
else
|
|
{
|
|
ImGui.ProgressBar(0f, new Vector2(-1f, 0f), "0%");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawMSQOverall(List<string> characters)
|
|
{
|
|
using ImRaii.IEndObject table = ImRaii.Table("MSQOverallTable", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY);
|
|
if (!table.Success)
|
|
{
|
|
return;
|
|
}
|
|
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("MSQ Progress", ImGuiTableColumnFlags.WidthFixed, 120f);
|
|
ImGui.TableSetupColumn("Current MSQ", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Completion %", ImGuiTableColumnFlags.WidthFixed, 100f);
|
|
ImGui.TableSetupScrollFreeze(0, 1);
|
|
ImGui.TableHeadersRow();
|
|
foreach (string character in characters)
|
|
{
|
|
if (!characterProgressCache.TryGetValue(character, out CharacterProgressInfo progressInfo))
|
|
{
|
|
GetCharacterProgress(character);
|
|
continue;
|
|
}
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
string[] parts = character.Split('@');
|
|
ImGui.TextUnformatted((parts.Length != 0) ? parts[0] : character);
|
|
ImGui.TableNextColumn();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text = new ImU8String(3, 2);
|
|
text.AppendFormatted(progressInfo.CompletedMSQCount);
|
|
text.AppendLiteral(" / ");
|
|
text.AppendFormatted(msqProgressionService.GetTotalMSQCount());
|
|
ImGui.TextUnformatted(text);
|
|
ImGui.PopStyleColor();
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(progressInfo.LastCompletedMSQName);
|
|
ImGui.TableNextColumn();
|
|
float percentage = progressInfo.MSQCompletionPercentage;
|
|
float fraction = percentage / 100f;
|
|
Vector2 sizeArg = new Vector2(-1f, 0f);
|
|
ImU8String overlay = new ImU8String(1, 1);
|
|
overlay.AppendFormatted(percentage, "F1");
|
|
overlay.AppendLiteral("%");
|
|
ImGui.ProgressBar(fraction, sizeArg, overlay);
|
|
}
|
|
}
|
|
|
|
private void DrawMSQExpansionBreakdown(List<string> characters)
|
|
{
|
|
List<ExpansionInfo> expansions = msqProgressionService.GetExpansions();
|
|
foreach (string character in characters)
|
|
{
|
|
string[] parts = character.Split('@');
|
|
string obj = ((parts.Length != 0) ? parts[0] : character);
|
|
string worldName = ((parts.Length > 1) ? parts[1] : "Unknown");
|
|
string displayName = obj + " @ " + worldName;
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImU8String label = new ImU8String(15, 2);
|
|
label.AppendFormatted(displayName);
|
|
label.AppendLiteral("##MSQBreakdown_");
|
|
label.AppendFormatted(character);
|
|
if (ImGui.CollapsingHeader(label))
|
|
{
|
|
ImGui.PopStyleColor();
|
|
ImGui.Indent(15f);
|
|
List<uint> completedQuests = questRotationService.GetCompletedQuestsByCharacter(character);
|
|
int currentExpansionIndex = (int)MSQExpansionData.GetCurrentExpansion(completedQuests);
|
|
Dictionary<string, (int, int)> expProgress = msqProgressionService.GetExpansionProgressForCharacter(completedQuests);
|
|
int totalCompleted = 0;
|
|
int totalQuests = 0;
|
|
foreach (ExpansionInfo exp in expansions)
|
|
{
|
|
var (completed, total) = expProgress.GetValueOrDefault(exp.ShortName, (0, 0));
|
|
if ((int)MSQExpansionData.GetAllExpansions().FirstOrDefault((MSQExpansionData.Expansion e) => MSQExpansionData.GetExpansionShortName(e) == exp.ShortName) < currentExpansionIndex)
|
|
{
|
|
completed = total;
|
|
}
|
|
totalCompleted += completed;
|
|
totalQuests += total;
|
|
}
|
|
ImU8String text = new ImU8String(13, 0);
|
|
text.AppendLiteral("Overall MSQ: ");
|
|
ImGui.TextUnformatted(text);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text2 = new ImU8String(1, 2);
|
|
text2.AppendFormatted(totalCompleted);
|
|
text2.AppendLiteral("/");
|
|
text2.AppendFormatted(totalQuests);
|
|
ImGui.TextUnformatted(text2);
|
|
ImGui.PopStyleColor();
|
|
float overallPercentage = ((totalQuests > 0) ? ((float)totalCompleted / (float)totalQuests) : 0f);
|
|
Vector2 sizeArg = new Vector2(-1f, 0f);
|
|
ImU8String overlay = new ImU8String(1, 1);
|
|
overlay.AppendFormatted((int)(overallPercentage * 100f));
|
|
overlay.AppendLiteral("%");
|
|
ImGui.ProgressBar(overallPercentage, sizeArg, overlay);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.TextUnformatted("Expansion Breakdown:");
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
foreach (ExpansionInfo exp2 in expansions)
|
|
{
|
|
var (completed2, total2) = expProgress.GetValueOrDefault(exp2.ShortName, (0, 0));
|
|
if ((int)MSQExpansionData.GetAllExpansions().FirstOrDefault((MSQExpansionData.Expansion e) => MSQExpansionData.GetExpansionShortName(e) == exp2.ShortName) < currentExpansionIndex)
|
|
{
|
|
completed2 = total2;
|
|
}
|
|
float percentage = ((total2 > 0) ? ((float)completed2 / (float)total2) : 0f);
|
|
bool num = completed2 == total2 && total2 > 0;
|
|
ImU8String text3 = new ImU8String(6, 2);
|
|
text3.AppendLiteral(" ");
|
|
text3.AppendFormatted(exp2.Name);
|
|
text3.AppendLiteral(" (");
|
|
text3.AppendFormatted(exp2.ShortName);
|
|
text3.AppendLiteral("):");
|
|
ImGui.TextUnformatted(text3);
|
|
ImGui.SameLine();
|
|
if (num)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
ImU8String text4 = new ImU8String(10, 2);
|
|
text4.AppendFormatted(completed2);
|
|
text4.AppendLiteral("/");
|
|
text4.AppendFormatted(total2);
|
|
text4.AppendLiteral(" Complete");
|
|
ImGui.TextUnformatted(text4);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else if (completed2 == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorAccent);
|
|
ImU8String text5 = new ImU8String(13, 2);
|
|
text5.AppendFormatted(completed2);
|
|
text5.AppendLiteral("/");
|
|
text5.AppendFormatted(total2);
|
|
text5.AppendLiteral(" Not Started");
|
|
ImGui.TextUnformatted(text5);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text6 = new ImU8String(1, 2);
|
|
text6.AppendFormatted(completed2);
|
|
text6.AppendLiteral("/");
|
|
text6.AppendFormatted(total2);
|
|
ImGui.TextUnformatted(text6);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGui.Indent(20f);
|
|
Vector2 sizeArg2 = new Vector2(-1f, 0f);
|
|
ImU8String overlay2 = new ImU8String(1, 1);
|
|
overlay2.AppendFormatted((int)(percentage * 100f));
|
|
overlay2.AppendLiteral("%");
|
|
ImGui.ProgressBar(percentage, sizeArg2, overlay2);
|
|
ImGui.Unindent(20f);
|
|
}
|
|
ImGui.Unindent(15f);
|
|
}
|
|
else
|
|
{
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
}
|
|
}
|
|
|
|
private void DrawSettingsTab()
|
|
{
|
|
DrawSettingsTabFull();
|
|
}
|
|
|
|
private List<string> GetWorldsForCurrentDatacenter()
|
|
{
|
|
if (selectedDCFilter == 0)
|
|
{
|
|
return availableWorlds;
|
|
}
|
|
string dcName = selectedDCFilter switch
|
|
{
|
|
1 => "EU",
|
|
2 => "NA",
|
|
3 => "JP",
|
|
4 => "OCE",
|
|
_ => "All",
|
|
};
|
|
List<string> charactersForDataCenter = dataCenterService.GetCharactersForDataCenter(registeredCharacters, dcName, charactersByDataCenter);
|
|
HashSet<string> worlds = new HashSet<string>();
|
|
foreach (string item in charactersForDataCenter)
|
|
{
|
|
string[] parts = item.Split('@');
|
|
if (parts.Length > 1)
|
|
{
|
|
worlds.Add(parts[1]);
|
|
}
|
|
}
|
|
return worlds.ToList();
|
|
}
|
|
|
|
private List<string> GetFilteredCharacters()
|
|
{
|
|
List<string> dcFiltered;
|
|
if (selectedDCFilter == 0)
|
|
{
|
|
dcFiltered = registeredCharacters;
|
|
}
|
|
else
|
|
{
|
|
string dcName = selectedDCFilter switch
|
|
{
|
|
1 => "EU",
|
|
2 => "NA",
|
|
3 => "JP",
|
|
4 => "OCE",
|
|
_ => "All",
|
|
};
|
|
dcFiltered = dataCenterService.GetCharactersForDataCenter(registeredCharacters, dcName, charactersByDataCenter);
|
|
}
|
|
if (selectedWorldFilter != "All")
|
|
{
|
|
return dcFiltered.Where((string c) => c.EndsWith("@" + selectedWorldFilter)).ToList();
|
|
}
|
|
return dcFiltered;
|
|
}
|
|
|
|
private CharacterProgressInfo GetCharacterProgress(string characterName)
|
|
{
|
|
if (characterProgressCache.TryGetValue(characterName, out CharacterProgressInfo cached) && (DateTime.Now - cached.LastUpdatedUtc).TotalSeconds < 300.0)
|
|
{
|
|
return cached;
|
|
}
|
|
string[] parts = characterName.Split('@');
|
|
string world = ((parts.Length > 1) ? parts[1] : "Unknown");
|
|
List<uint> completedQuests = questRotationService.GetCompletedQuestsByCharacter(characterName);
|
|
uint lastQuestId = 0u;
|
|
string lastQuestName = "—";
|
|
if (completedQuests.Count > 0)
|
|
{
|
|
lastQuestId = completedQuests.Max();
|
|
lastQuestName = msqProgressionService.GetQuestName(lastQuestId);
|
|
if (lastQuestName == "Unknown Quest")
|
|
{
|
|
lastQuestName = $"Quest {lastQuestId}";
|
|
}
|
|
}
|
|
List<uint> completedMSQs = completedQuests.Where((uint q) => msqProgressionService.IsMSQ(q)).ToList();
|
|
uint lastMSQId = 0u;
|
|
string lastMSQName = "—";
|
|
MSQExpansionData.Expansion currentExpansion = MSQExpansionData.GetCurrentExpansion(completedQuests);
|
|
string currentExpansionName = MSQExpansionData.GetExpansionName(currentExpansion);
|
|
List<uint> completedMSQsInCurrentExpansion = completedMSQs.Where((uint q) => MSQExpansionData.GetExpansionForQuest(q) == currentExpansion).ToList();
|
|
if (completedMSQsInCurrentExpansion.Count > 0)
|
|
{
|
|
lastMSQId = completedMSQsInCurrentExpansion.Max();
|
|
lastMSQName = msqProgressionService.GetQuestName(lastMSQId);
|
|
if (lastMSQName == "Unknown Quest")
|
|
{
|
|
lastMSQName = $"Quest {lastMSQId}";
|
|
}
|
|
lastMSQName = "[" + currentExpansionName + "] " + lastMSQName;
|
|
}
|
|
else if (completedMSQs.Count > 0)
|
|
{
|
|
lastMSQId = completedMSQs.Max();
|
|
lastMSQName = msqProgressionService.GetQuestName(lastMSQId);
|
|
if (lastMSQName == "Unknown Quest")
|
|
{
|
|
lastMSQName = $"Quest {lastMSQId}";
|
|
}
|
|
}
|
|
int totalMSQCount = msqProgressionService.GetTotalMSQCount();
|
|
float msqPercentage = ((totalMSQCount > 0) ? ((float)completedMSQs.Count / (float)totalMSQCount * 100f) : 0f);
|
|
CharacterProgressInfo progressInfo = new CharacterProgressInfo
|
|
{
|
|
World = world,
|
|
CompletedQuestCount = completedQuests.Count,
|
|
LastQuestId = lastQuestId,
|
|
LastQuestName = lastQuestName,
|
|
LastCompletedMSQId = lastMSQId,
|
|
LastCompletedMSQName = lastMSQName,
|
|
CompletedMSQCount = completedMSQs.Count,
|
|
MSQCompletionPercentage = msqPercentage,
|
|
LastUpdatedUtc = DateTime.UtcNow
|
|
};
|
|
characterProgressCache[characterName] = progressInfo;
|
|
return progressInfo;
|
|
}
|
|
|
|
public void DrawWorldSelectionDialogs()
|
|
{
|
|
if (showSelectWorldDialog)
|
|
{
|
|
ImGui.OpenPopup("Select World##SelectWorldDialog");
|
|
}
|
|
if (ImGui.BeginPopupModal("Select World##SelectWorldDialog", ref showSelectWorldDialog, ImGuiWindowFlags.AlwaysAutoResize))
|
|
{
|
|
ImGui.TextUnformatted("Select a world to check all characters:");
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.SetNextItemWidth(200f);
|
|
if (ImGui.BeginCombo("##WorldSelect", selectedWorldForBulkAction))
|
|
{
|
|
foreach (string world in availableWorlds)
|
|
{
|
|
bool isSelected = selectedWorldForBulkAction == world;
|
|
if (ImGui.Selectable(world, isSelected))
|
|
{
|
|
selectedWorldForBulkAction = world;
|
|
}
|
|
if (isSelected)
|
|
{
|
|
ImGui.SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (ImGui.Button("Cancel", new Vector2(100f, 0f)))
|
|
{
|
|
showSelectWorldDialog = false;
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
if (ImGui.Button("Select", new Vector2(100f, 0f)))
|
|
{
|
|
foreach (string character in registeredCharacters)
|
|
{
|
|
if (character.EndsWith("@" + selectedWorldForBulkAction))
|
|
{
|
|
characterSelection[character] = true;
|
|
}
|
|
}
|
|
showSelectWorldDialog = false;
|
|
log.Information("[NewMainWindow] Selected all characters from " + selectedWorldForBulkAction);
|
|
}
|
|
ImGui.PopStyleColor();
|
|
ImGui.EndPopup();
|
|
}
|
|
if (showDeselectWorldDialog)
|
|
{
|
|
ImGui.OpenPopup("Deselect World##DeselectWorldDialog");
|
|
}
|
|
if (!ImGui.BeginPopupModal("Deselect World##DeselectWorldDialog", ref showDeselectWorldDialog, ImGuiWindowFlags.AlwaysAutoResize))
|
|
{
|
|
return;
|
|
}
|
|
ImGui.TextUnformatted("Select a world to uncheck all characters:");
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.SetNextItemWidth(200f);
|
|
if (ImGui.BeginCombo("##WorldDeselect", selectedWorldForBulkAction))
|
|
{
|
|
foreach (string world2 in availableWorlds)
|
|
{
|
|
bool isSelected2 = selectedWorldForBulkAction == world2;
|
|
if (ImGui.Selectable(world2, isSelected2))
|
|
{
|
|
selectedWorldForBulkAction = world2;
|
|
}
|
|
if (isSelected2)
|
|
{
|
|
ImGui.SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (ImGui.Button("Cancel", new Vector2(100f, 0f)))
|
|
{
|
|
showDeselectWorldDialog = false;
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
if (ImGui.Button("Deselect", new Vector2(100f, 0f)))
|
|
{
|
|
foreach (string character2 in registeredCharacters)
|
|
{
|
|
if (character2.EndsWith("@" + selectedWorldForBulkAction))
|
|
{
|
|
characterSelection[character2] = false;
|
|
}
|
|
}
|
|
showDeselectWorldDialog = false;
|
|
log.Information("[NewMainWindow] Deselected all characters from " + selectedWorldForBulkAction);
|
|
}
|
|
ImGui.PopStyleColor();
|
|
ImGui.EndPopup();
|
|
}
|
|
|
|
private void DrawEventQuestTab()
|
|
{
|
|
Vector4 eventQuestColor = new Vector4(0.949f, 0.769f, 0.388f, 1f);
|
|
Vector4 eventQuestAccent = new Vector4(1f, 0.6f, 0.2f, 1f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImGui.TextUnformatted("Event Quest System");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
List<string> selectedCharacters = (from keyValuePair in characterSelection
|
|
where keyValuePair.Value
|
|
select keyValuePair.Key).ToList();
|
|
if ((DateTime.Now - lastEventQuestRefresh).TotalSeconds > 5.0 || availableEventQuests.Count == 0)
|
|
{
|
|
List<string> currentlyActiveEventQuests = plugin.QuestionableIPC.GetCurrentlyActiveEventQuests();
|
|
EventQuestResolver resolver = new EventQuestResolver(dataManager, log);
|
|
availableEventQuests.Clear();
|
|
foreach (string questId in currentlyActiveEventQuests)
|
|
{
|
|
log.Information($"[EventQuest] Questionable returned quest ID: '{questId}' (Length: {questId.Length})");
|
|
string questName = resolver.GetQuestName(questId);
|
|
availableEventQuests.Add((questId, questName));
|
|
}
|
|
lastEventQuestRefresh = DateTime.Now;
|
|
log.Debug($"[EventQuest] Loaded {availableEventQuests.Count} active event quests from Questionable");
|
|
}
|
|
ImGui.TextUnformatted("Active Event Quests:");
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Select an active Event Quest. Prerequisites will be automatically resolved.");
|
|
ImU8String text = new ImU8String(54, 1);
|
|
text.AppendLiteral("Currently ");
|
|
text.AppendFormatted(availableEventQuests.Count);
|
|
text.AppendLiteral(" event quest(s) available from Questionable.");
|
|
ImGui.TextUnformatted(text);
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.TextUnformatted("Event Quest:");
|
|
ImGui.SameLine();
|
|
ImGui.SetNextItemWidth(400f);
|
|
string currentQuestDisplay = (string.IsNullOrEmpty(selectedEventQuestId) ? "Select Event Quest..." : (availableEventQuests.FirstOrDefault<(string, string)>(((string QuestId, string QuestName) q) => q.QuestId == selectedEventQuestId).Item2 ?? selectedEventQuestId));
|
|
if (ImGui.BeginCombo("##EventQuestCombo", currentQuestDisplay))
|
|
{
|
|
foreach (var availableEventQuest in availableEventQuests)
|
|
{
|
|
string questId2 = availableEventQuest.QuestId;
|
|
string questName2 = availableEventQuest.QuestName;
|
|
bool isSelected = selectedEventQuestId == questId2;
|
|
ImU8String label = new ImU8String(3, 2);
|
|
label.AppendFormatted(questName2);
|
|
label.AppendLiteral(" (");
|
|
label.AppendFormatted(questId2);
|
|
label.AppendLiteral(")");
|
|
if (ImGui.Selectable(label, isSelected))
|
|
{
|
|
selectedEventQuestId = questId2;
|
|
EventQuestResolver resolver2 = new EventQuestResolver(dataManager, log);
|
|
resolvedPrerequisites = resolver2.ResolveEventQuestDependencies(questId2);
|
|
log.Information($"[EventQuest] Selected quest {questId2}, resolved {resolvedPrerequisites.Count} prerequisites");
|
|
}
|
|
if (isSelected)
|
|
{
|
|
ImGui.SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui.EndCombo();
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Button, eventQuestColor);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, eventQuestAccent);
|
|
if (ImGui.Button("Refresh from Questionable"))
|
|
{
|
|
List<string> currentlyActiveEventQuests2 = plugin.QuestionableIPC.GetCurrentlyActiveEventQuests();
|
|
EventQuestResolver resolver3 = new EventQuestResolver(dataManager, log);
|
|
availableEventQuests.Clear();
|
|
foreach (string questId3 in currentlyActiveEventQuests2)
|
|
{
|
|
string questName3 = resolver3.GetQuestName(questId3);
|
|
availableEventQuests.Add((questId3, questName3));
|
|
}
|
|
lastEventQuestRefresh = DateTime.Now;
|
|
log.Information($"[EventQuest] Refreshed event quest list from Questionable: {availableEventQuests.Count} quests found");
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (!string.IsNullOrEmpty(selectedEventQuestId))
|
|
{
|
|
string questName4 = availableEventQuests.FirstOrDefault<(string, string)>(((string QuestId, string QuestName) q) => q.QuestId == selectedEventQuestId).Item2 ?? "Unknown";
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImU8String text2 = new ImU8String(16, 1);
|
|
text2.AppendLiteral("Selected Quest: ");
|
|
text2.AppendFormatted(questName4);
|
|
ImGui.TextUnformatted(text2);
|
|
ImGui.PopStyleColor();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text3 = new ImU8String(10, 1);
|
|
text3.AppendLiteral("Quest ID: ");
|
|
text3.AppendFormatted(selectedEventQuestId);
|
|
ImGui.TextUnformatted(text3);
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
}
|
|
if (!string.IsNullOrEmpty(selectedEventQuestId) && resolvedPrerequisites.Count > 0)
|
|
{
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestAccent);
|
|
ImU8String text4 = new ImU8String(17, 1);
|
|
text4.AppendLiteral("Prerequisites (");
|
|
text4.AppendFormatted(resolvedPrerequisites.Count);
|
|
text4.AppendLiteral("):");
|
|
ImGui.TextUnformatted(text4);
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(3f);
|
|
EventQuestResolver resolver4 = new EventQuestResolver(dataManager, log);
|
|
using (ImRaii.IEndObject table = ImRaii.Table("PrerequisitesTable", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg))
|
|
{
|
|
if (table.Success)
|
|
{
|
|
ImGui.TableSetupColumn("Quest Name", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Quest ID", ImGuiTableColumnFlags.WidthFixed, 80f);
|
|
ImGui.TableHeadersRow();
|
|
foreach (string prereqId in resolvedPrerequisites)
|
|
{
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(resolver4.GetQuestName(prereqId));
|
|
ImGui.TableNextColumn();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted(prereqId);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
}
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImGui.TextUnformatted("Current Status:");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
EventQuestState state = eventQuestService.GetCurrentState();
|
|
ImU8String text5 = new ImU8String(7, 0);
|
|
text5.AppendLiteral("Phase: ");
|
|
ImGui.TextUnformatted(text5);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, state.Phase switch
|
|
{
|
|
EventQuestPhase.Idle => colorSecondary,
|
|
EventQuestPhase.QuestActive => eventQuestColor,
|
|
EventQuestPhase.Completed => colorPrimary,
|
|
EventQuestPhase.Error => colorAccent,
|
|
_ => colorSecondary,
|
|
});
|
|
ImGui.TextUnformatted(state.Phase.ToString());
|
|
ImGui.PopStyleColor();
|
|
if (!string.IsNullOrEmpty(state.CurrentCharacter))
|
|
{
|
|
ImU8String text6 = new ImU8String(19, 0);
|
|
text6.AppendLiteral("Current Character: ");
|
|
ImGui.TextUnformatted(text6);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImGui.TextUnformatted(state.CurrentCharacter);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (!string.IsNullOrEmpty(state.EventQuestName))
|
|
{
|
|
ImU8String text7 = new ImU8String(13, 0);
|
|
text7.AppendLiteral("Event Quest: ");
|
|
ImGui.TextUnformatted(text7);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImU8String text8 = new ImU8String(3, 2);
|
|
text8.AppendFormatted(state.EventQuestName);
|
|
text8.AppendLiteral(" (");
|
|
text8.AppendFormatted(state.EventQuestId);
|
|
text8.AppendLiteral(")");
|
|
ImGui.TextUnformatted(text8);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (state.DependencyQuests.Count > 0)
|
|
{
|
|
ImU8String text9 = new ImU8String(15, 2);
|
|
text9.AppendLiteral("Dependencies: ");
|
|
text9.AppendFormatted(state.DependencyIndex + 1);
|
|
text9.AppendLiteral("/");
|
|
text9.AppendFormatted(state.DependencyQuests.Count);
|
|
ImGui.TextUnformatted(text9);
|
|
}
|
|
if (!string.IsNullOrEmpty(state.NextCharacter))
|
|
{
|
|
ImU8String text10 = new ImU8String(16, 0);
|
|
text10.AppendLiteral("Next Character: ");
|
|
ImGui.TextUnformatted(text10);
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted(state.NextCharacter);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (state.Phase == EventQuestPhase.Error && !string.IsNullOrEmpty(state.ErrorMessage))
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorAccent);
|
|
ImU8String text11 = new ImU8String(7, 1);
|
|
text11.AppendLiteral("Error: ");
|
|
text11.AppendFormatted(state.ErrorMessage);
|
|
ImGui.TextUnformatted(text11);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
if (state.SelectedCharacters.Count > 0)
|
|
{
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
float progress = (float)state.CompletedCharacters.Count / (float)state.SelectedCharacters.Count;
|
|
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
|
|
Vector2 cursorPos = ImGui.GetCursorScreenPos();
|
|
float barWidth = ImGui.GetContentRegionAvail().X;
|
|
float barHeight = 25f;
|
|
uint barBg = ImGui.ColorConvertFloat4ToU32(new Vector4(0.2f, 0.2f, 0.2f, 0.8f));
|
|
drawList.AddRectFilled(cursorPos, cursorPos + new Vector2(barWidth, barHeight), barBg, 4f);
|
|
uint barFg = ImGui.ColorConvertFloat4ToU32(new Vector4(eventQuestColor.X, eventQuestColor.Y, eventQuestColor.Z, 0.9f));
|
|
drawList.AddRectFilled(cursorPos, cursorPos + new Vector2(barWidth * progress, barHeight), barFg, 4f);
|
|
string progressText = $"{state.CompletedCharacters.Count}/{state.SelectedCharacters.Count} completed";
|
|
Vector2 textSize = ImGui.CalcTextSize(progressText);
|
|
drawList.AddText(cursorPos + new Vector2(barWidth / 2f - textSize.X / 2f, 4f), ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 1f)), progressText);
|
|
ImGui.Dummy(new Vector2(barWidth, barHeight));
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (state.SelectedCharacters.Count > 0)
|
|
{
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Button, (eventQuestViewMode == 0) ? eventQuestColor : colorDarkBg);
|
|
ImU8String label2 = new ImU8String(12, 1);
|
|
label2.AppendLiteral("Remaining (");
|
|
label2.AppendFormatted(state.RemainingCharacters.Count);
|
|
label2.AppendLiteral(")");
|
|
if (ImGui.Button(label2))
|
|
{
|
|
eventQuestViewMode = 0;
|
|
}
|
|
ImGui.PopStyleColor();
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Button, (eventQuestViewMode == 1) ? eventQuestColor : colorDarkBg);
|
|
ImU8String label3 = new ImU8String(12, 1);
|
|
label3.AppendLiteral("Completed (");
|
|
label3.AppendFormatted(state.CompletedCharacters.Count);
|
|
label3.AppendLiteral(")");
|
|
if (ImGui.Button(label3))
|
|
{
|
|
eventQuestViewMode = 1;
|
|
}
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
List<string> displayList = ((eventQuestViewMode == 0) ? state.RemainingCharacters : state.CompletedCharacters);
|
|
if (displayList.Count > 0)
|
|
{
|
|
using ImRaii.IEndObject child = ImRaii.Child("CharacterList", new Vector2(0f, 150f), border: true);
|
|
if (child.Success)
|
|
{
|
|
foreach (string character in displayList)
|
|
{
|
|
string[] parts = character.Split('@');
|
|
string charName = ((parts.Length != 0) ? parts[0] : character);
|
|
string world = ((parts.Length > 1) ? parts[1] : "Unknown");
|
|
ImGui.PushStyleColor(ImGuiCol.Text, (eventQuestViewMode == 0) ? colorSecondary : eventQuestColor);
|
|
ImU8String text12 = new ImU8String(2, 1);
|
|
text12.AppendLiteral("• ");
|
|
text12.AppendFormatted(charName);
|
|
ImGui.TextUnformatted(text12);
|
|
ImGui.PopStyleColor();
|
|
ImGui.SameLine();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text13 = new ImU8String(2, 1);
|
|
text13.AppendLiteral("@ ");
|
|
text13.AppendFormatted(world);
|
|
ImGui.TextUnformatted(text13);
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted((eventQuestViewMode == 0) ? "No remaining characters" : "No completed characters");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
}
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (selectedCharacters.Count == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Select characters in the Characters tab to start Event Quest rotation");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else if (string.IsNullOrEmpty(selectedEventQuestId))
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Select an Event Quest from the dropdown above to start");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else if (!eventQuestService.IsRotationActive)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, eventQuestColor);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, eventQuestAccent);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonActive, eventQuestAccent);
|
|
if (ImGui.Button("▶ Start Event Quest Rotation", new Vector2(250f, 35f)))
|
|
{
|
|
log.Information("[EventQuest] Start button clicked!");
|
|
log.Information("[EventQuest] Event Quest ID: " + selectedEventQuestId);
|
|
log.Information($"[EventQuest] Selected characters: {selectedCharacters.Count}");
|
|
log.Information($"[EventQuest] Prerequisites: {resolvedPrerequisites.Count}");
|
|
if (eventQuestService.StartEventQuestRotation(selectedEventQuestId, selectedCharacters))
|
|
{
|
|
log.Information("[EventQuest] Rotation started successfully!");
|
|
}
|
|
else
|
|
{
|
|
log.Error("[EventQuest] Failed to start rotation");
|
|
}
|
|
}
|
|
ImGui.PopStyleColor(3);
|
|
}
|
|
else
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorAccent);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonActive, colorAccent);
|
|
if (ImGui.Button("⏹ Abort Rotation", new Vector2(200f, 30f)))
|
|
{
|
|
eventQuestService.AbortRotation();
|
|
log.Information("[EventQuest] Rotation aborted");
|
|
}
|
|
ImGui.PopStyleColor(3);
|
|
}
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
if (ImGui.Button("Refresh Characters"))
|
|
{
|
|
RefreshCharacterList();
|
|
log.Information("[EventQuest] Character list refreshed");
|
|
}
|
|
ImGui.PopStyleColor(2);
|
|
ImGuiHelpers.ScaledDummy(15f);
|
|
ImGui.Separator();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImGui.TextUnformatted("Completion Data:");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(5f);
|
|
Dictionary<string, List<string>> completionData = plugin.Configuration.EventQuestCompletionByCharacter;
|
|
if (completionData.Count == 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("No completion data recorded yet.");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
else
|
|
{
|
|
using ImRaii.IEndObject table2 = ImRaii.Table("CompletionDataTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, new Vector2(0f, 150f));
|
|
if (table2.Success)
|
|
{
|
|
ImGui.TableSetupColumn("Event Quest ID", ImGuiTableColumnFlags.WidthFixed, 120f);
|
|
ImGui.TableSetupColumn("Completed", ImGuiTableColumnFlags.WidthFixed, 100f);
|
|
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupScrollFreeze(0, 1);
|
|
ImGui.TableHeadersRow();
|
|
EventQuestResolver resolver5 = new EventQuestResolver(dataManager, log);
|
|
foreach (KeyValuePair<string, List<string>> kvp in completionData)
|
|
{
|
|
string questId4 = kvp.Key;
|
|
List<string> completedChars = kvp.Value;
|
|
ImGui.TableNextRow();
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(resolver5.GetQuestName(questId4));
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImU8String text14 = new ImU8String(4, 1);
|
|
text14.AppendLiteral("ID: ");
|
|
text14.AppendFormatted(questId4);
|
|
ImGui.TextUnformatted(text14);
|
|
ImGui.PopStyleColor();
|
|
ImGui.TableNextColumn();
|
|
ImGui.PushStyleColor(ImGuiCol.Text, eventQuestColor);
|
|
ImU8String text15 = new ImU8String(0, 1);
|
|
text15.AppendFormatted(completedChars.Count);
|
|
ImGui.TextUnformatted(text15);
|
|
ImGui.PopStyleColor();
|
|
ImGui.TableNextColumn();
|
|
using (ImRaii.PushId(questId4))
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
if (ImGui.Button("Clear"))
|
|
{
|
|
completionData.Remove(questId4);
|
|
plugin.Configuration.Save();
|
|
log.Information("[EventQuest] Cleared completion data for quest " + questId4);
|
|
}
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
if (completionData.Count > 0)
|
|
{
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorAccent);
|
|
if (ImGui.Button("\ud83d\uddd1 Clear All Completion Data"))
|
|
{
|
|
completionData.Clear();
|
|
plugin.Configuration.Save();
|
|
log.Information("[EventQuest] Cleared all completion data");
|
|
}
|
|
ImGui.PopStyleColor();
|
|
}
|
|
}
|
|
|
|
private void DrawWarningTab()
|
|
{
|
|
TryWarningMenuQuestionableCheck();
|
|
ImGuiHelpers.ScaledDummy(50f);
|
|
float windowWidth = ImGui.GetContentRegionAvail().X;
|
|
string text2 = "Please ensure you are using Questionable from WigglyMuffin!";
|
|
ImGui.SetWindowFontScale(2f);
|
|
Vector2 textSize = ImGui.CalcTextSize("Incorrect Questionable Version.");
|
|
Vector2 textSize2 = ImGui.CalcTextSize(text2);
|
|
ImGui.SetCursorPosX((windowWidth - textSize.X) * 0.5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorAccent);
|
|
ImGui.TextUnformatted("Incorrect Questionable Version.");
|
|
ImGui.PopStyleColor();
|
|
ImGuiHelpers.ScaledDummy(10f);
|
|
float flashAlpha = (MathF.Sin((float)ImGui.GetTime() * 5f) + 1f) * 0.5f;
|
|
Vector4 flashColor = new Vector4(1f, 0f, 0f, flashAlpha);
|
|
ImGui.SetCursorPosX((windowWidth - textSize2.X) * 0.5f);
|
|
ImGui.TextColored(in flashColor, text2);
|
|
ImGuiHelpers.ScaledDummy(30f);
|
|
ImGui.SetWindowFontScale(1f);
|
|
if (warningMenuRetryAttempts < 4)
|
|
{
|
|
double elapsedSeconds = (DateTime.Now - warningMenuRetryStartTime).TotalSeconds;
|
|
int nextRetryIn = ((warningMenuRetryAttempts < warningMenuRetryDelaysSeconds.Length) ? (warningMenuRetryDelaysSeconds[warningMenuRetryAttempts] - (int)elapsedSeconds) : 0);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorPrimary);
|
|
if (nextRetryIn > 0)
|
|
{
|
|
string obj = $"Checking for Questionable... (Retry {warningMenuRetryAttempts}/{4} in {nextRetryIn}s)";
|
|
ImGui.SetCursorPosX((windowWidth - ImGui.CalcTextSize(obj).X) * 0.5f);
|
|
ImGui.TextUnformatted(obj);
|
|
}
|
|
else
|
|
{
|
|
string obj2 = $"Checking for Questionable... (Attempt {warningMenuRetryAttempts + 1}/{4})";
|
|
ImGui.SetCursorPosX((windowWidth - ImGui.CalcTextSize(obj2).X) * 0.5f);
|
|
ImGui.TextUnformatted(obj2);
|
|
}
|
|
ImGui.PopStyleColor();
|
|
ImGui.SetCursorPosX((windowWidth - ImGui.CalcTextSize("Waiting for Questionable IPC to become available...").X) * 0.5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Text, colorSecondary);
|
|
ImGui.TextUnformatted("Waiting for Questionable IPC to become available...");
|
|
ImGui.PopStyleColor();
|
|
return;
|
|
}
|
|
float buttonWidth = 120f;
|
|
float buttonHeight = 40f;
|
|
ImGui.SetCursorPosX((windowWidth - buttonWidth) * 0.5f);
|
|
ImGui.PushStyleColor(ImGuiCol.Button, colorPrimary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, colorSecondary);
|
|
ImGui.PushStyleColor(ImGuiCol.ButtonActive, colorSecondary);
|
|
if (ImGui.Button("Refresh", new Vector2(buttonWidth, buttonHeight)))
|
|
{
|
|
if (plugin.QuestionableIPC.TryEnsureAvailableSilent() && plugin.QuestionableIPC.ValidateFeatureCompatibility())
|
|
{
|
|
selectedTab = 5;
|
|
}
|
|
else
|
|
{
|
|
warningMenuRetryAttempts = 0;
|
|
warningMenuRetryStartTime = DateTime.MinValue;
|
|
warningMenuRetryCycleComplete = false;
|
|
}
|
|
}
|
|
ImGui.PopStyleColor(3);
|
|
}
|
|
}
|