using System.Numerics; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using ImGuiNET; using Microsoft.AspNetCore.SignalR.Client; using QuestShare.Services; using static QuestShare.Services.ApiService; namespace QuestShare.Windows.MainWindow; public class MainWindow : Window, IDisposable { public MainWindow() : base(Plugin.Name + "###Main", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse) { SizeConstraints = new WindowSizeConstraints { MinimumSize = new Vector2(600, 650), MaximumSize = new Vector2(float.MaxValue, float.MaxValue) }; } public override void OnOpen() { } public void Dispose() { } private ApiService ApiService => (ApiService)Plugin.GetService(); private ShareService ShareService => (ShareService)Plugin.GetService(); private PartyService PartyService => (PartyService)Plugin.GetService(); private HostService HostService => (HostService)Plugin.GetService(); private GameQuest? selectedQuest = GameQuestManager.GetActiveQuest(); private enum ActiveTab { Host, Join, Settings } public override void Draw() { ImGui.TextUnformatted("Server Status: "); ImGui.SameLine(); DrawConnectionState(); ImGui.SameLine(); if (ApiService.IsConnected) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Unlink, ImGuiColors.DPSRed)) { _ = ApiService.Disconnect(); } } else { if (ImGuiComponents.IconButton(FontAwesomeIcon.Link, ImGuiColors.DPSRed)) { _ = ApiService.Connect(); } } // ImGui.SameLine(); ImGui.Separator(); using (ImRaii.TabBar("MainTabBar", ImGuiTabBarFlags.NoCloseWithMiddleMouseButton)) { ImGui.BeginDisabled(!ApiService.IsConnected); if (ImGui.BeginTabItem("Host Group")) { DrawHostTab(); ImGui.EndTabItem(); } if (ImGui.BeginTabItem("Join Group")) { DrawJoinTab(); ImGui.EndTabItem(); } ImGui.EndDisabled(); if (ImGui.BeginTabItem("Settings")) { DrawSettingsTab(); ImGui.EndTabItem(); } } ImGui.Separator(); if (UiService.LastErrorMessage != null) { // ImGui.TextColored(ImGuiColors.DPSRed, UiService.LastErrorMessage); } } private void DrawConnectionState() { switch (this.ApiService.ConnectionState) { case HubConnectionState.Connecting: ImGui.TextColored(ImGuiColors.DalamudYellow, "Connecting..."); break; case HubConnectionState.Connected: ImGui.TextColored(ImGuiColors.HealerGreen, "Connected"); break; case HubConnectionState.Disconnected: ImGui.TextColored(ImGuiColors.DPSRed, "Disconnected"); break; case HubConnectionState.Reconnecting: ImGui.TextColored(ImGuiColors.DalamudYellow, "Reconnecting..."); break; default: break; } } private bool generatePending = false; private void DrawHostTab() { if (HostService.ActiveSession != null && HostService.ActiveSession.ShareCode != null) generatePending = false; var isEnabled = ConfigurationManager.Instance.EnableHosting; if (ImGuiComponents.ToggleButton(Namespace + "/Enable Hosting", ref isEnabled)) { ConfigurationManager.Instance.EnableHosting = isEnabled; ConfigurationManager.Save(); } ImGui.SameLine(); ImGui.TextUnformatted("Enable Hosting"); ImGui.Separator(); if (isEnabled) { ImGui.TextUnformatted("Share Code:"); ImGui.SameLine(); if (HostService.ActiveSession != null) { ImGui.TextColored(ImGuiColors.HealerGreen, HostService.ActiveSession.ShareCode); if (ImGui.IsItemClicked()) { ImGui.SetClipboardText(HostService.ActiveSession.ShareCode); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Click to copy to clipboard."); } ImGui.SameLine(); if (ImGui.Button("Cancel")) { DispatchCancel(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Cancel the current session. This will permanently remove the share code and all connected clients."); } var allowJoins = HostService.AllowJoins; var skipPartyCheck = HostService.SkipPartyCheck; var sendUpdates = HostService.IsActive; if (ImGui.Checkbox("Allow new Joins", ref allowJoins)) { HostService.SetAllowJoins(allowJoins); } ImGui.SameLine(); if (ImGui.Checkbox("Skip Party Check", ref skipPartyCheck)) { HostService.SetSkipPartyCheck(skipPartyCheck); } ImGui.SameLine(); if (ImGui.Checkbox("Send Updates", ref sendUpdates)) { HostService.SetIsActive(sendUpdates); } var shareMsq = ConfigurationManager.Instance.TrackMSQ; ImGui.BeginDisabled(shareMsq); using (var combo = ImRaii.Combo("##Quests", GameQuestManager.GetActiveQuest()?.QuestName ?? "---SELECT---", ImGuiComboFlags.HeightRegular)) { if (combo) { foreach (var quest in GameQuestManager.GameQuests.OrderBy(q => q.QuestName)) { if (ImGui.Selectable(quest.QuestName)) { selectedQuest = quest; GameQuestManager.SetActiveFlag(quest.QuestId); HostService.Update((int)quest.QuestId, quest.CurrentStep); ConfigurationManager.Save(); } } } } ImGui.SameLine(); if (ImGui.Button("Refresh")) { GameQuestManager.LoadQuests(); } ImGui.EndDisabled(); ImGui.SameLine(); var track = ConfigurationManager.Instance.TrackMSQ; if (ImGui.Checkbox("Track MSQ", ref track)) { ConfigurationManager.Instance.TrackMSQ = track; ConfigurationManager.Save(); } if (selectedQuest == null && GameQuestManager.GetActiveQuest() == null) { ImGui.TextUnformatted("No quest selected."); return; } else if (GameQuestManager.GetActiveQuest() != null) { selectedQuest = GameQuestManager.GetActiveQuest(); } ImGui.TextUnformatted("Active Quest:"); ImGui.SameLine(); ImGui.TextUnformatted(selectedQuest!.QuestName); ImGui.TextUnformatted("Current Step:"); ImGui.SameLine(); ImGui.TextUnformatted(selectedQuest.CurrentStep.ToString()); ImGui.Separator(); ImGui.TextUnformatted("Quest Steps:"); ImGui.Separator(); var steps = selectedQuest.QuestSteps; for (var i = 0; i < steps!.Count; i++) { if (i + 1 == selectedQuest.CurrentStep || (selectedQuest.CurrentStep == 0 && i == 0) || (selectedQuest.CurrentStep == 0xFF && i + 1 == steps.Count)) { ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]); } else if (i + 1 < selectedQuest.CurrentStep) { ImGui.TextColored(ImGuiColors.DalamudYellow, steps[i]); } else { ImGui.TextUnformatted("???"); } } } else { ImGui.BeginDisabled(generatePending); if (ImGui.Button("Generate New")) { ApiService.DispatchRegister(); } ImGui.EndDisabled(); } } } private string enteredShareCode = ""; private bool isJoining = false; private bool isLeaving = false; private void OnGroupJoin(object? sender, GroupJoinEventArgs args) { isJoining = false; ApiService.GroupJoined -= OnGroupJoin; } private void DrawJoinTab() { ImGui.TextUnformatted("Enter Share Code:"); ImGui.SameLine(); ImGui.BeginDisabled(isJoining); ImGui.InputText("##ShareCode", ref enteredShareCode, 8); ImGui.SameLine(); var btn = "Join"; if (isJoining) btn = "Joining..."; if (ImGui.Button(btn)) { var payload = new Objects.ShareCode { CharacterId = ClientState.LocalContentId.ToString().SaltedHash(enteredShareCode), Code = enteredShareCode }; isJoining = true; ApiService.GroupJoined += OnGroupJoin; ApiService.DispatchGroup(payload); } ImGui.EndDisabled(); ImGui.Separator(); ImGui.TextUnformatted("Currently Joined Groups"); if (ShareService.Sessions.Count == 0) { ImGui.TextUnformatted("No groups joined."); } else { foreach (var session in ShareService.Sessions) { using var tree = ImRaii.TreeNode($"Session: {session.ShareCode}"); if (tree) { DrawSessionDetails(session); if (ImGui.Button("Leave Group")) { ApiService.DispatchUngroup(session); isLeaving = true; } } } } } private void DrawSessionDetails(Objects.Session session) { ImGui.TextUnformatted($"Owner: {session.OwnerCharacterId}"); var activeQuest = session.ActiveQuestId; var activeStep = session.ActiveQuestStep; if (activeQuest != 0) { var questInfo = GameQuestManager.GetQuestById((uint)activeQuest); var steps = questInfo.QuestSteps; ImGui.TextUnformatted(questInfo.QuestData.Name.ExtractText()); ImGui.TextUnformatted("Current Step:"); ImGui.SameLine(); ImGui.TextUnformatted(activeStep.ToString()); ImGui.Separator(); ImGui.TextUnformatted("Quest Steps:"); ImGui.Separator(); for (var i = 0; i < steps.Count; i++) { if (i + 1 == activeStep || (i + 1 == steps.Count && activeStep == 0xFF)) { ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]); } else { ImGui.TextUnformatted(steps[i]); } } if (ImGui.Button("Get Marker")) { var marker = questInfo.GetMapLink((byte)(activeStep - 1)); if (marker != null) GameGui.OpenMapWithMapLink(marker); else Log.Error("No map link available for this quest."); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Generates a map marker for the current step's destination."); } // ImGui.SameLine(); /*if (ImGui.Button("Teleport")) { // attempt to generate a path to the next step } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Teleports to nearest aetheryte of quest destination."); }*/ } else { ImGui.TextUnformatted("No active quest or host is offline."); } } private void DrawSettingsTab() { var selectedApiServer = ConfigurationManager.Instance.ApiDisplayName; if (ImGui.BeginCombo("API Server", selectedApiServer)) { foreach (var server in ConfigurationManager.Instance.ApiConfigurations) { var isSelected = selectedApiServer == server.DisplayName; if (ImGui.Selectable(server.DisplayName, isSelected)) { var index = Array.FindIndex(ConfigurationManager.Instance.ApiConfigurations, x => x.DisplayName == server.DisplayName); ConfigurationManager.Instance.ApiConfigurations[index].Active = true; foreach (var config in ConfigurationManager.Instance.ApiConfigurations) { if (config.DisplayName != server.DisplayName) { config.Active = false; } } ConfigurationManager.Save(); } if (isSelected) { ImGui.SetItemDefaultFocus(); } } ImGui.EndCombo(); } ImGui.SameLine(); if (ImGui.Button("Add")) { // does nothing yet } var connectOnStartup = ConfigurationManager.Instance.ConnectOnStartup; if (ImGui.Checkbox("Connect on Startup", ref connectOnStartup)) { ConfigurationManager.Instance.ConnectOnStartup = connectOnStartup; ConfigurationManager.Save(); } var autoShareMsq = ConfigurationManager.Instance.AutoShareMsq; if (ImGui.Checkbox("Auto Share MSQ", ref autoShareMsq)) { ConfigurationManager.Instance.AutoShareMsq = autoShareMsq; ConfigurationManager.Save(); } var autoShareNewQuests = ConfigurationManager.Instance.AutoShareNewQuests; if (ImGui.Checkbox("Auto Share New Quests", ref autoShareNewQuests)) { ConfigurationManager.Instance.AutoShareNewQuests = autoShareNewQuests; ConfigurationManager.Save(); } } }