qstbak/Questionable/Questionable.Windows/QuestSelectionWindow.cs
2026-08-19 13:19:57 +10:00

304 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI;
using LLib.GameUI;
using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.JournalComponents;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;
internal sealed class QuestSelectionWindow : ThemedWindow
{
private const string WindowId = "###QuestionableQuestSelection";
private readonly QuestData _questData;
private readonly IGameGui _gameGui;
private readonly IChatGui _chatGui;
private readonly QuestFunctions _questFunctions;
private readonly QuestController _questController;
private readonly FateController _fateController;
private readonly SeasonalDutyController _seasonalDutyController;
private readonly CustomDeliveryController _customDeliveryController;
private readonly AttunementController _attunementController;
private readonly QuestRegistry _questRegistry;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly TerritoryData _territoryData;
private readonly IClientState _clientState;
private readonly UiUtils _uiUtils;
private readonly QuestTooltipComponent _questTooltipComponent;
private readonly QuestJournalUtils _questJournalUtils;
private List<IQuestInfo> _quests = new List<IQuestInfo>();
private List<IQuestInfo> _offeredQuests = new List<IQuestInfo>();
private bool _onlyAvailableQuests = true;
private string _searchText = string.Empty;
public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, QuestFunctions questFunctions, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestRegistry questRegistry, IDalamudPluginInterface pluginInterface, TerritoryData territoryData, IClientState clientState, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, QuestJournalUtils questJournalUtils)
: base("Quest Selection###QuestionableQuestSelection")
{
_questData = questData;
_gameGui = gameGui;
_chatGui = chatGui;
_questFunctions = questFunctions;
_questController = questController;
_fateController = fateController;
_seasonalDutyController = seasonalDutyController;
_customDeliveryController = customDeliveryController;
_attunementController = attunementController;
_questRegistry = questRegistry;
_pluginInterface = pluginInterface;
_territoryData = territoryData;
_clientState = clientState;
_uiUtils = uiUtils;
_questTooltipComponent = questTooltipComponent;
_questJournalUtils = questJournalUtils;
base.Size = new Vector2(500f, 200f);
base.SizeCondition = ImGuiCond.Once;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(500f, 200f)
};
}
public unsafe void OpenForTarget(IGameObject? gameObject)
{
if (gameObject != null)
{
uint baseId = gameObject.BaseId;
string value = gameObject.Name.ToString();
base.WindowName = $"Quests starting with {value} [{baseId}]{"###QuestionableQuestSelection"}";
_quests = _questData.GetAllByIssuerDataId(baseId);
if (_gameGui.TryGetAddonByName<AddonSelectIconString>("SelectIconString", out var addonPtr))
{
List<string?> answers = InteractionUiController.GetChoices(addonPtr);
_offeredQuests = _quests.Where((IQuestInfo x) => answers.Any((string y) => GameFunctions.GameStringEquals(x.Name, y))).ToList();
}
else
{
_offeredQuests = new List<IQuestInfo>();
}
}
else
{
_quests = new List<IQuestInfo>();
_offeredQuests = new List<IQuestInfo>();
}
base.IsOpenAndUncollapsed = _quests.Count > 0;
}
public unsafe void OpenForCurrentZone()
{
uint territoryId = _clientState.TerritoryType;
string nameAndId = _territoryData.GetNameAndId(territoryId);
base.WindowName = "Quests starting in " + nameAndId + "###QuestionableQuestSelection";
_quests = (from x in _questRegistry.AllQuests
where x.FindSequence(0)?.FindStep(0)?.TerritoryId == territoryId
select _questData.GetQuestInfo(x.Id)).ToList();
foreach (ref MarkerInfo unacceptedQuestMarker in Map.Instance()->UnacceptedQuestMarkers)
{
MarkerInfo current = unacceptedQuestMarker;
QuestId questId = QuestId.FromRowId(current.ObjectiveId);
if (_quests.All((IQuestInfo q) => q.QuestId != questId))
{
_quests.Add(_questData.GetQuestInfo(questId));
}
}
_offeredQuests = new List<IQuestInfo>();
base.IsOpenAndUncollapsed = true;
}
public override void OnClose()
{
_quests = new List<IQuestInfo>();
_offeredQuests = new List<IQuestInfo>();
}
public override void DrawContent()
{
var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard();
UiThemeUtils.SearchInput("##QuestSelectionSearch", ref _searchText, -13f, "Search quests...");
if (_offeredQuests.Count != 0)
{
UiThemeUtils.WrappedCheckbox("Only show quests currently offered", ref _onlyAvailableQuests);
}
using (ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable("QuestSelection", 4, ImGuiTableFlags.ScrollY))
{
if ((bool)tableDisposable)
{
float x;
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
x = ImGui.CalcTextSize(FontAwesomeIcon.Copy.ToIconString()).X;
}
float initWidthOrWeight = 3f * ImGui.GetFrameHeight() + 2f * ImGui.GetStyle().ItemSpacing.X;
ImGui.TableSetupColumn("Id", ImGuiTableColumnFlags.WidthFixed, 50f * ImGui.GetIO().FontGlobalScale);
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, x);
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.None, 200f);
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, initWidthOrWeight);
ImGui.TableHeadersRow();
IEnumerable<IQuestInfo> enumerable = ((_offeredQuests.Count != 0 && _onlyAvailableQuests) ? _offeredQuests : _quests);
if (!string.IsNullOrWhiteSpace(_searchText))
{
enumerable = enumerable.Where((IQuestInfo q) => q.Name.Contains(_searchText, StringComparison.OrdinalIgnoreCase) || q.QuestId.ToString().Contains(_searchText, StringComparison.OrdinalIgnoreCase));
}
foreach (IQuestInfo item in enumerable)
{
ImGui.TableNextRow();
string text = item.QuestId.ToString();
Quest quest;
bool flag = _questRegistry.TryGetQuest(item.QuestId, out quest);
if (ImGui.TableNextColumn())
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(text);
}
if (ImGui.TableNextColumn())
{
ImGui.AlignTextToFramePadding();
var (col, icon, _) = _uiUtils.GetQuestStyle(item.QuestId);
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
if (flag)
{
ImGui.TextColored(in col, icon.ToIconString());
}
else
{
ImGui.TextColored(in UiThemeUtils.StatusUnobtainable, icon.ToIconString());
}
}
if (ImGui.IsItemHovered())
{
_questTooltipComponent.Draw(item);
}
}
if (ImGui.TableNextColumn())
{
ImGui.AlignTextToFramePadding();
if (quest != null && quest.Root.Disabled)
{
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
ImGui.TextColored(in UiThemeUtils.StatusActive, FontAwesomeIcon.Ban.ToIconString());
ImGui.SameLine();
}
}
UiThemeUtils.RowLabel(item.Name, 0f);
_questJournalUtils.ShowContextMenu(item, quest, "QuestSelectionWindow");
}
if (!ImGui.TableNextColumn())
{
continue;
}
using (ImRaii.PushId(text))
{
bool num = UiThemeUtils.IconButton(FontAwesomeIcon.Copy, ImGui.GetFrameHeight());
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Copy as file name");
}
if (num)
{
CopyToClipboard(item, suffix: true);
}
else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
CopyToClipboard(item, suffix: false);
}
ImGui.SameLine();
if (quest == null)
{
continue;
}
EInteractionType? eInteractionType = quest.FindSequence(0)?.LastStep()?.InteractionType;
if (!eInteractionType.HasValue || eInteractionType != EInteractionType.AcceptQuest || !_questFunctions.IsReadyToAcceptQuest(item.QuestId))
{
continue;
}
using (ImRaii.Disabled(_questController.NextQuest != null || _questController.SimulatedQuest != null || _fateController.IsRunning || _seasonalDutyController.IsRunning || _customDeliveryController.IsRunning || _attunementController.IsRunning))
{
bool flag2;
using (ImRaii.PushColor(ImGuiCol.Button, UiThemeUtils.PlayButtonColor))
{
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UiThemeUtils.PlayButtonHover))
{
using (ImRaii.PushColor(ImGuiCol.ButtonActive, UiThemeUtils.PlayButtonActive))
{
using (ImRaii.PushColor(ImGuiCol.Text, UiThemeUtils.PlayButtonText))
{
flag2 = UiThemeUtils.IconButton(FontAwesomeIcon.Play, ImGui.GetFrameHeight());
}
}
}
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Start as next quest");
}
if (flag2)
{
if (!_questController.ManualPriorityQuests.Contains(quest))
{
_questController.ManualPriorityQuests.Insert(0, quest);
}
_questJournalUtils.StartQuestAsNext(quest, "QuestSelectionWindow");
}
ImGui.SameLine();
bool num2 = UiThemeUtils.IconButton(FontAwesomeIcon.AngleDoubleRight, ImGui.GetFrameHeight());
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Set as next quest");
}
if (num2)
{
_questController.SetNextQuest(quest);
}
}
}
}
}
}
UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList);
}
private void CopyToClipboard(IQuestInfo quest, bool suffix)
{
string text = $"{quest.QuestId}_{quest.SimplifiedName}{(suffix ? ".json" : "")}";
ImGui.SetClipboardText(text);
_chatGui.Print("Copied '" + text + "' to clipboard");
}
}