{
public bool DebugOverlay { get; set; }
public bool NeverFly { get; set; }
+ public bool AdditionalStatusInformation { get; set; }
}
}
// New Gridania Navmesh workaround
new BlacklistedPoint(128, new(2f, 40.25f, 36.5f), new(0.25f, 40.25f, 36.5f)),
+ // lotus stand
+ new BlacklistedPoint(205, new(26.75f, 0.5f, 20.75f), new(27.179117f, 0.26728272f, 19.714373f)),
+
new BlacklistedPoint(132, new(45.5f, -8f, 101f), new(50.53978f, -8.046954f, 101.06045f)),
// eastern thanalan
catch (Exception e)
{
_logger.LogError(e, "Failed to start task {TaskName}", upcomingTask.ToString());
- _chatGui.PrintError($"[Questionable] Failed to start task '{upcomingTask}', please check /xllog for details");
+ _chatGui.PrintError(
+ $"[Questionable] Failed to start task '{upcomingTask}', please check /xllog for details");
Stop("Task failed to start");
return;
}
ClearTasksInternal();
_automatic = automatic;
+ if (TryPickPriorityQuest())
+ _logger.LogInformation("Using priority quest over current quest");
+
(QuestSequence? seq, QuestStep? step) = GetNextStep();
if (CurrentQuest == null || seq == null || step == null)
{
_currentTask = null;
}
+ public bool IsInterruptible()
+ {
+ var details = CurrentQuestDetails;
+ if (details == null)
+ return false;
+
+ var (currentQuest, type) = details.Value;
+ if (type != CurrentQuestType.Normal)
+ return false;
+
+ QuestSequence? currentSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
+ QuestStep? currentStep = currentSequence?.FindStep(currentQuest.Step);
+ return currentStep?.AetheryteShortcut != null;
+ }
+
+ public bool TryPickPriorityQuest()
+ {
+ if (!IsInterruptible())
+ return false;
+
+ ushort[] priorityQuests =
+ [
+ 1157, // Garuda (Hard)
+ 1158, // Titan (Hard)
+ ];
+
+ foreach (var questId in priorityQuests)
+ {
+ if (_gameFunctions.IsReadyToAcceptQuest(questId) && _questRegistry.TryGetQuest(questId, out var quest))
+ {
+ SetNextQuest(quest);
+ _chatGui.Print(
+ "[Questionable] Picking up quest '{Name}' as a priority over current main story/side quests",
+ quest.Info.Name);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public sealed record StepProgress(
DateTime StartedAt,
int PointMenuCounter = 0);
if (DateTime.Now <= _continueAt)
return ETaskResult.StillRunning;
- if (ItemId == VesperBayAetheryteTicket)
+ if (ItemId == VesperBayAetheryteTicket && _usedItem)
{
InventoryManager* inventoryManager = InventoryManager.Instance();
if (inventoryManager == null)
}
int itemCount = inventoryManager->GetInventoryItemCount(ItemId);
- if (!_usedItem && itemCount == _itemCount)
+ if (itemCount == _itemCount)
{
// TODO Better handling for game-provided errors, i.e. reacting to the 'Could not use' messages. UseItem() is successful in this case (and returns 0)
logger.LogInformation(
-using System.Collections.Immutable;
+using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using Dalamud.Plugin.Services;
private readonly ImmutableDictionary<uint, string> _territoryNames;
private readonly ImmutableHashSet<ushort> _territoriesWithMount;
private readonly ImmutableHashSet<ushort> _dutyTerritories;
+ private readonly ImmutableDictionary<ushort, string> _instanceNames;
public TerritoryData(IDataManager dataManager)
{
.Where(x => x.RowId > 0 && x.ContentFinderCondition.Row != 0)
.Select(x => (ushort)x.RowId)
.ToImmutableHashSet();
+
+ _instanceNames = dataManager.GetExcelSheet<ContentFinderCondition>()!
+ .Where(x => x.RowId > 0 && x.Content != 0 && x.ContentLinkType == 1 && x.ContentType.Row != 6)
+ .ToImmutableDictionary(x => x.Content, x => x.Name.ToString());
}
public string? GetName(ushort territoryId) => _territoryNames.GetValueOrDefault(territoryId);
public bool CanUseMount(ushort territoryId) => _territoriesWithMount.Contains(territoryId);
public bool IsDutyInstance(ushort territoryId) => _dutyTerritories.Contains(territoryId);
+
+ public string? GetInstanceName(ushort instanceId) => _instanceNames.GetValueOrDefault(instanceId);
}
return false;
}
+ if (IsQuestLocked(questId))
+ return false;
+
// if we're not at a high enough level to continue, we also ignore it
var currentLevel = _clientState.LocalPlayer?.Level ?? 0;
if (currentLevel != 0 && quest != null && quest.Info.Level > currentLevel)
return true;
}
- if (questInfo.PreviousQuests.Count > 0)
- {
- var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x == extraCompletedQuest);
- if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
- questInfo.PreviousQuests.Count == completedQuests)
- return false;
- else if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
- return false;
- else
- return true;
- }
+ return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
+ }
- return false;
+ private bool HasCompletedPreviousQuests(QuestInfo questInfo, ushort? extraCompletedQuest)
+ {
+ if (questInfo.PreviousQuests.Count == 0)
+ return true;
+
+ var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x == extraCompletedQuest);
+ if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
+ questInfo.PreviousQuests.Count == completedQuests)
+ return true;
+ else if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
+ return true;
+ else
+ return false;
+ }
+
+ private static bool HasCompletedPreviousInstances(QuestInfo questInfo)
+ {
+ if (questInfo.PreviousInstanceContent.Count == 0)
+ return true;
+
+ var completedInstances = questInfo.PreviousInstanceContent.Count(x => UIState.IsInstanceContentCompleted(x));
+ if (questInfo.PreviousInstanceContentJoin == QuestInfo.QuestJoin.All &&
+ questInfo.PreviousInstanceContent.Count == completedInstances)
+ return true;
+ else if (questInfo.PreviousInstanceContentJoin == QuestInfo.QuestJoin.AtLeastOne && completedInstances > 0)
+ return true;
+ else
+ return false;
}
public bool IsAetheryteUnlocked(uint aetheryteId, out byte subIndex)
QuestLockJoin = (QuestJoin)quest.QuestLockJoin;
IsMainScenarioQuest = quest.JournalGenre?.Value?.JournalCategory?.Value?.JournalSection?.Row is 0 or 1;
CompletesInstantly = quest.ToDoCompleteSeq[0] == 0;
+ PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
+ PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin;
}
+
public ushort QuestId { get; }
public string Name { get; }
public ushort Level { get; }
public QuestJoin PreviousQuestJoin { get; }
public ImmutableList<ushort> QuestLocks { get; set; }
public QuestJoin QuestLockJoin { get; set; }
+ public List<ushort> PreviousInstanceContent { get; set; }
+ public QuestJoin PreviousInstanceContentJoin { get; set; }
public bool IsMainScenarioQuest { get; }
public bool CompletesInstantly { get; set; }
using Questionable.Validation;
using Questionable.Validation.Validators;
using Questionable.Windows;
+using Questionable.Windows.QuestComponents;
using Action = Questionable.Controller.Steps.Interactions.Action;
namespace Questionable;
private static void AddWindows(ServiceCollection serviceCollection)
{
+ serviceCollection.AddSingleton<UiUtils>();
+
+ serviceCollection.AddSingleton<ActiveQuestComponent>();
+ serviceCollection.AddSingleton<ARealmRebornComponent>();
+ serviceCollection.AddSingleton<CreationUtilsComponent>();
+ serviceCollection.AddSingleton<QuickAccessButtonsComponent>();
+ serviceCollection.AddSingleton<RemainingTasksComponent>();
+
serviceCollection.AddSingleton<QuestWindow>();
serviceCollection.AddSingleton<ConfigWindow>();
serviceCollection.AddSingleton<DebugOverlay>();
Save();
}
+ bool additionalStatusInformation = _configuration.Advanced.AdditionalStatusInformation;
+ if (ImGui.Checkbox("Draw additional status information", ref additionalStatusInformation))
+ {
+ _configuration.Advanced.AdditionalStatusInformation = additionalStatusInformation;
+ Save();
+ }
+
ImGui.EndTabItem();
}
--- /dev/null
+using System.Linq;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Utility.Raii;
+using FFXIVClientStructs.FFXIV.Client.Game.UI;
+using FFXIVClientStructs.FFXIV.Common.Math;
+using ImGuiNET;
+using Questionable.Data;
+
+namespace Questionable.Windows.QuestComponents;
+
+internal sealed class ARealmRebornComponent
+{
+ private const ushort ATimeForEveryPurpose = 425;
+ private const ushort TheUltimateWeapon = 524;
+ private static readonly ushort[] RequiredPrimalInstances = [20004, 20006, 20005];
+ private static readonly ushort[] RequiredAllianceRaidQuests = [1709, 1200, 1201, 1202, 1203, 1474, 494, 495];
+
+ private readonly GameFunctions _gameFunctions;
+ private readonly QuestData _questData;
+ private readonly TerritoryData _territoryData;
+ private readonly UiUtils _uiUtils;
+
+ public ARealmRebornComponent(GameFunctions gameFunctions, QuestData questData, TerritoryData territoryData,
+ UiUtils uiUtils)
+ {
+ _gameFunctions = gameFunctions;
+ _questData = questData;
+ _territoryData = territoryData;
+ _uiUtils = uiUtils;
+ }
+
+ public bool ShouldDraw => !_gameFunctions.IsQuestComplete(ATimeForEveryPurpose) &&
+ _gameFunctions.IsQuestComplete(TheUltimateWeapon);
+
+ public void Draw()
+ {
+ var completedPrimals = UIState.IsInstanceContentCompleted(RequiredPrimalInstances.Last());
+ bool completedRaids = _gameFunctions.IsQuestComplete(RequiredAllianceRaidQuests.Last());
+ bool complete = completedPrimals && completedRaids;
+ bool hover = _uiUtils.ChecklistItem("ARR Primals & Raids",
+ complete ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed,
+ complete ? FontAwesomeIcon.Check : FontAwesomeIcon.Times);
+ if (complete || !hover)
+ return;
+
+ using var tooltip = ImRaii.Tooltip();
+ if (!tooltip)
+ return;
+
+ ImGui.Text("Primals:");
+ foreach (var instanceId in RequiredPrimalInstances)
+ {
+ (Vector4 color, FontAwesomeIcon icon) = UiUtils.GetInstanceStyle(instanceId);
+ _uiUtils.ChecklistItem(_territoryData.GetInstanceName(instanceId) ?? "?", color, icon);
+ }
+
+ ImGui.Text("Alliance Raids:");
+ foreach (var questId in RequiredAllianceRaidQuests)
+ {
+ (Vector4 color, FontAwesomeIcon icon, _) = _uiUtils.GetQuestStyle(questId);
+ _uiUtils.ChecklistItem(_questData.GetQuestInfo(questId).Name, color, icon);
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Globalization;
+using System.Linq;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Components;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Plugin;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
+using ImGuiNET;
+using Questionable.Controller;
+using Questionable.Controller.Steps.Shared;
+using Questionable.Model.V1;
+
+namespace Questionable.Windows.QuestComponents;
+
+internal sealed class ActiveQuestComponent
+{
+ private readonly QuestController _questController;
+ private readonly MovementController _movementController;
+ private readonly CombatController _combatController;
+ private readonly GameFunctions _gameFunctions;
+ private readonly ICommandManager _commandManager;
+ private readonly IDalamudPluginInterface _pluginInterface;
+ private readonly Configuration _configuration;
+ private readonly QuestRegistry _questRegistry;
+
+ public ActiveQuestComponent(QuestController questController, MovementController movementController,
+ CombatController combatController, GameFunctions gameFunctions, ICommandManager commandManager,
+ IDalamudPluginInterface pluginInterface, Configuration configuration, QuestRegistry questRegistry)
+ {
+ _questController = questController;
+ _movementController = movementController;
+ _combatController = combatController;
+ _gameFunctions = gameFunctions;
+ _commandManager = commandManager;
+ _pluginInterface = pluginInterface;
+ _configuration = configuration;
+ _questRegistry = questRegistry;
+ }
+
+ public void Draw()
+ {
+ var currentQuestDetails = _questController.CurrentQuestDetails;
+ QuestController.QuestProgress? currentQuest = currentQuestDetails?.Progress;
+ QuestController.CurrentQuestType? currentQuestType = currentQuestDetails?.Type;
+ if (currentQuest != null)
+ {
+ DrawQuestNames(currentQuest, currentQuestType);
+ var questWork = DrawQuestWork(currentQuest);
+
+ if (_combatController.IsRunning)
+ ImGui.TextColored(ImGuiColors.DalamudOrange, "In Combat");
+ else
+ {
+ ImGui.BeginDisabled();
+ ImGui.TextUnformatted(_questController.DebugState ?? string.Empty);
+ ImGui.EndDisabled();
+ }
+
+ QuestSequence? currentSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
+ QuestStep? currentStep = currentSequence?.FindStep(currentQuest.Step);
+ bool colored = currentStep is
+ { InteractionType: EInteractionType.Instruction or EInteractionType.WaitForManualProgress };
+ if (colored)
+ ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudOrange);
+ ImGui.TextUnformatted(currentStep?.Comment ??
+ currentSequence?.Comment ?? currentQuest.Quest.Root.Comment ?? string.Empty);
+ if (colored)
+ ImGui.PopStyleColor();
+
+ //var nextStep = _questController.GetNextStep();
+ //ImGui.BeginDisabled(nextStep.Step == null);
+ ImGui.Text(_questController.ToStatString());
+ //ImGui.EndDisabled();
+
+ DrawQuestIcons(currentQuest, currentStep, questWork);
+
+ DrawSimulationControls();
+ }
+ else
+ {
+ ImGui.Text("No active quest");
+ ImGui.TextColored(ImGuiColors.DalamudGrey, $"{_questRegistry.Count} quests loaded");
+ }
+ }
+
+ private void DrawQuestNames(QuestController.QuestProgress currentQuest,
+ QuestController.CurrentQuestType? currentQuestType)
+ {
+ if (currentQuestType == QuestController.CurrentQuestType.Simulated)
+ {
+ var simulatedQuest = _questController.SimulatedQuest ?? currentQuest;
+ using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
+ ImGui.TextUnformatted(
+ $"Simulated Quest: {simulatedQuest.Quest.Info.Name} / {simulatedQuest.Sequence} / {simulatedQuest.Step}");
+ }
+ else if (currentQuestType == QuestController.CurrentQuestType.Next)
+ {
+ var startedQuest = _questController.StartedQuest;
+ if (startedQuest != null)
+ DrawCurrentQuest(startedQuest);
+
+ using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
+ ImGui.TextUnformatted(
+ $"Next Quest: {currentQuest.Quest.Info.Name} / {currentQuest.Sequence} / {currentQuest.Step}");
+ }
+ else
+ DrawCurrentQuest(currentQuest);
+ }
+
+ private void DrawCurrentQuest(QuestController.QuestProgress currentQuest)
+ {
+ ImGui.TextUnformatted(
+ $"Quest: {currentQuest.Quest.Info.Name} / {currentQuest.Sequence} / {currentQuest.Step}");
+
+ if (currentQuest.Quest.Root.Disabled)
+ {
+ ImGui.SameLine();
+ ImGui.TextColored(ImGuiColors.DalamudRed, "Disabled");
+ }
+
+ if (_configuration.Advanced.AdditionalStatusInformation && _questController.IsInterruptible())
+ {
+ ImGui.SameLine();
+ ImGui.TextColored(ImGuiColors.DalamudYellow, "Interruptible");
+ }
+ }
+
+ private QuestWork? DrawQuestWork(QuestController.QuestProgress currentQuest)
+ {
+ ImGui.BeginDisabled();
+ var questWork = _gameFunctions.GetQuestEx(currentQuest.Quest.QuestId);
+ if (questWork != null)
+ {
+ var qw = questWork.Value;
+ string vars = "";
+ for (int i = 0; i < 6; ++i)
+ {
+ vars += qw.Variables[i] + " ";
+ if (i % 2 == 1)
+ vars += " ";
+ }
+
+ // For combat quests, a sequence to kill 3 enemies works a bit like this:
+ // Trigger enemies → 0
+ // Kill first enemy → 1
+ // Kill second enemy → 2
+ // Last enemy → increase sequence, reset variable to 0
+ // The order in which enemies are killed doesn't seem to matter.
+ // If multiple waves spawn, this continues to count up (e.g. 1 enemy from wave 1, 2 enemies from wave 2, 1 from wave 3) would count to 3 then 0
+ ImGui.Text($"QW: {vars.Trim()}");
+ }
+ else
+ {
+ if (currentQuest.Quest.QuestId == _questController.NextQuest?.Quest.QuestId)
+ ImGui.TextUnformatted("(Next quest in story line not accepted)");
+ else
+ ImGui.TextUnformatted("(Not accepted)");
+ }
+
+ ImGui.EndDisabled();
+ return questWork;
+ }
+
+ private void DrawQuestIcons(QuestController.QuestProgress currentQuest, QuestStep? currentStep,
+ QuestWork? questWork)
+ {
+ ImGui.BeginDisabled(_questController.IsRunning);
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Play))
+ {
+ // if we haven't accepted this quest, mark it as next quest so that we can optionally use aetherytes to travel
+ if (questWork == null)
+ _questController.SetNextQuest(currentQuest.Quest);
+
+ _questController.ExecuteNextStep(true);
+ }
+
+ ImGui.SameLine();
+
+ if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.StepForward, "Step"))
+ {
+ _questController.ExecuteNextStep(false);
+ }
+
+ ImGui.EndDisabled();
+ ImGui.SameLine();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop))
+ {
+ _movementController.Stop();
+ _questController.Stop("Manual");
+ }
+
+ bool lastStep = currentStep ==
+ currentQuest.Quest.FindSequence(currentQuest.Sequence)?.Steps.LastOrDefault();
+ bool colored = currentStep != null
+ && !lastStep
+ && currentStep.InteractionType == EInteractionType.Instruction
+ && _questController.HasCurrentTaskMatching<WaitAtEnd.WaitNextStepOrSequence>();
+
+ ImGui.BeginDisabled(lastStep);
+ if (colored)
+ ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.ParsedGreen);
+ if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip"))
+ {
+ _movementController.Stop();
+ _questController.Skip(currentQuest.Quest.QuestId, currentQuest.Sequence);
+ }
+
+ if (colored)
+ ImGui.PopStyleColor();
+ ImGui.EndDisabled();
+
+ if (_commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
+ {
+ ImGui.SameLine();
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
+ _commandManager.DispatchCommand("/questinfo",
+ currentQuest.Quest.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
+ }
+
+ bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
+ if (ImGui.Checkbox("Automatically accept next quest", ref autoAcceptNextQuest))
+ {
+ _configuration.General.AutoAcceptNextQuest = autoAcceptNextQuest;
+ _pluginInterface.SavePluginConfig(_configuration);
+ }
+ }
+
+ private void DrawSimulationControls()
+ {
+ if (_questController.SimulatedQuest == null)
+ return;
+
+ var simulatedQuest = _questController.SimulatedQuest;
+
+ ImGui.Separator();
+ ImGui.TextColored(ImGuiColors.DalamudRed, "Quest sim active (experimental)");
+ ImGui.Text($"Sequence: {simulatedQuest.Sequence}");
+
+ ImGui.BeginDisabled(simulatedQuest.Sequence == 0);
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus))
+ {
+ _movementController.Stop();
+ _questController.Stop("Sim-");
+
+ byte oldSequence = simulatedQuest.Sequence;
+ byte newSequence = simulatedQuest.Quest.Root.QuestSequence
+ .Select(x => (byte)x.Sequence)
+ .LastOrDefault(x => x < oldSequence, byte.MinValue);
+
+ _questController.SimulatedQuest.SetSequence(newSequence);
+ }
+
+ ImGui.EndDisabled();
+
+ ImGui.SameLine();
+ ImGui.BeginDisabled(simulatedQuest.Sequence >= 255);
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
+ {
+ _movementController.Stop();
+ _questController.Stop("Sim+");
+
+ byte oldSequence = simulatedQuest.Sequence;
+ byte newSequence = simulatedQuest.Quest.Root.QuestSequence
+ .Select(x => (byte)x.Sequence)
+ .FirstOrDefault(x => x > oldSequence, byte.MaxValue);
+
+ simulatedQuest.SetSequence(newSequence);
+ }
+
+ ImGui.EndDisabled();
+
+ var simulatedSequence = simulatedQuest.Quest.FindSequence(simulatedQuest.Sequence);
+ if (simulatedSequence != null)
+ {
+ using var _ = ImRaii.PushId("SimulatedStep");
+
+ ImGui.Text($"Step: {simulatedQuest.Step} / {simulatedSequence.Steps.Count - 1}");
+
+ ImGui.BeginDisabled(simulatedQuest.Step == 0);
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus))
+ {
+ _movementController.Stop();
+ _questController.Stop("SimStep-");
+
+ simulatedQuest.SetStep(Math.Min(simulatedQuest.Step - 1,
+ simulatedSequence.Steps.Count - 1));
+ }
+
+ ImGui.EndDisabled();
+
+ ImGui.SameLine();
+ ImGui.BeginDisabled(simulatedQuest.Step >= simulatedSequence.Steps.Count);
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
+ {
+ _movementController.Stop();
+ _questController.Stop("SimStep+");
+
+ simulatedQuest.SetStep(
+ simulatedQuest.Step == simulatedSequence.Steps.Count - 1
+ ? 255
+ : (simulatedQuest.Step + 1));
+ }
+
+ ImGui.EndDisabled();
+
+ if (ImGui.Button("Skip current task"))
+ {
+ _questController.SkipSimulatedTask();
+ }
+
+ ImGui.SameLine();
+ if (ImGui.Button("Clear sim"))
+ {
+ _questController.SimulateQuest(null);
+
+ _movementController.Stop();
+ _questController.Stop("ClearSim");
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.Numerics;
+using Dalamud.Game.ClientState.Conditions;
+using Dalamud.Game.ClientState.Objects;
+using Dalamud.Game.Text;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Components;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game.Control;
+using FFXIVClientStructs.FFXIV.Client.Game.Object;
+using ImGuiNET;
+using Microsoft.Extensions.Logging;
+using Questionable.Controller;
+using Questionable.Data;
+using Questionable.Model;
+using Questionable.Model.V1;
+using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
+
+namespace Questionable.Windows.QuestComponents;
+
+internal sealed class CreationUtilsComponent
+{
+ private readonly MovementController _movementController;
+ private readonly GameFunctions _gameFunctions;
+ private readonly TerritoryData _territoryData;
+ private readonly QuestData _questData;
+ private readonly QuestSelectionWindow _questSelectionWindow;
+ private readonly IClientState _clientState;
+ private readonly ITargetManager _targetManager;
+ private readonly ICondition _condition;
+ private readonly IGameGui _gameGui;
+ private readonly ILogger<CreationUtilsComponent> _logger;
+
+ public CreationUtilsComponent(MovementController movementController, GameFunctions gameFunctions,
+ TerritoryData territoryData, QuestData questData, QuestSelectionWindow questSelectionWindow,
+ IClientState clientState, ITargetManager targetManager, ICondition condition, IGameGui gameGui,
+ ILogger<CreationUtilsComponent> logger)
+ {
+ _movementController = movementController;
+ _gameFunctions = gameFunctions;
+ _territoryData = territoryData;
+ _questData = questData;
+ _questSelectionWindow = questSelectionWindow;
+ _clientState = clientState;
+ _targetManager = targetManager;
+ _condition = condition;
+ _gameGui = gameGui;
+ _logger = logger;
+ }
+
+ public unsafe void Draw()
+ {
+ Debug.Assert(_clientState.LocalPlayer != null, "_clientState.LocalPlayer != null");
+
+ string territoryName = _territoryData.GetNameAndId(_clientState.TerritoryType);
+ ImGui.Text(territoryName);
+
+ if (_gameFunctions.IsFlyingUnlockedInCurrentZone())
+ {
+ ImGui.SameLine();
+ ImGui.Text(SeIconChar.BotanistSprout.ToIconString());
+ }
+
+ var q = _gameFunctions.GetCurrentQuest();
+ ImGui.Text($"Current Quest: {q.CurrentQuest} → {q.Sequence}");
+
+#if false
+ var questManager = QuestManager.Instance();
+ if (questManager != null)
+ {
+ for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
+ {
+ var trackedQuest = questManager->TrackedQuests[i];
+ switch (trackedQuest.QuestType)
+ {
+ default:
+ ImGui.Text($"Tracked quest {i}: {trackedQuest.QuestType}, {trackedQuest.Index}");
+ break;
+
+ case 1:
+ _questRegistry.TryGetQuest(questManager->NormalQuests[trackedQuest.Index].QuestId,
+ out var quest);
+ ImGui.Text(
+ $"Tracked quest: {questManager->NormalQuests[trackedQuest.Index].QuestId}, {trackedQuest.Index}: {quest?.Info.Name}");
+ break;
+ }
+ }
+ }
+#endif
+
+ if (_targetManager.Target != null)
+ {
+ ImGui.Separator();
+ ImGui.Text(string.Create(CultureInfo.InvariantCulture,
+ $"Target: {_targetManager.Target.Name} ({_targetManager.Target.ObjectKind}; {_targetManager.Target.DataId})"));
+
+ GameObject* gameObject = (GameObject*)_targetManager.Target.Address;
+ ImGui.Text(string.Create(CultureInfo.InvariantCulture,
+ $"Distance: {(_targetManager.Target.Position - _clientState.LocalPlayer.Position).Length():F2}"));
+ ImGui.SameLine();
+
+ float verticalDistance = _targetManager.Target.Position.Y - _clientState.LocalPlayer.Position.Y;
+ string verticalDistanceText = string.Create(CultureInfo.InvariantCulture, $"Y: {verticalDistance:F2}");
+ if (Math.Abs(verticalDistance) >= MovementController.DefaultVerticalInteractionDistance)
+ ImGui.TextColored(ImGuiColors.DalamudOrange, verticalDistanceText);
+ else
+ ImGui.Text(verticalDistanceText);
+
+ ImGui.SameLine();
+ ImGui.Text($"QM: {gameObject->NamePlateIconId}");
+
+ ImGui.BeginDisabled(!_movementController.IsNavmeshReady);
+ if (!_movementController.IsPathfinding)
+ {
+ if (ImGui.Button("Move to Target"))
+ {
+ _movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId,
+ _targetManager.Target.Position,
+ fly: _condition[ConditionFlag.Mounted] && _gameFunctions.IsFlyingUnlockedInCurrentZone(),
+ sprint: true);
+ }
+ }
+ else
+ {
+ if (ImGui.Button("Cancel pathfinding"))
+ _movementController.ResetPathfinding();
+ }
+
+ ImGui.EndDisabled();
+
+ ImGui.SameLine();
+ ImGui.BeginDisabled(!_questData.IsIssuerOfAnyQuest(_targetManager.Target.DataId));
+ bool showQuests = ImGuiComponents.IconButton(FontAwesomeIcon.MapMarkerAlt);
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Show all Quests starting with your current target.");
+ if (showQuests)
+ _questSelectionWindow.OpenForTarget(_targetManager.Target);
+
+ ImGui.EndDisabled();
+
+ ImGui.SameLine();
+ bool interact = ImGuiComponents.IconButton(FontAwesomeIcon.MousePointer);
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Interact with your current target.");
+ if (interact)
+ {
+ ulong result = TargetSystem.Instance()->InteractWithObject(
+ (GameObject*)_targetManager.Target.Address, false);
+ _logger.LogInformation("XXXXX Interaction Result: {Result}", result);
+ }
+
+ ImGui.SameLine();
+
+ bool copy = ImGuiComponents.IconButton(FontAwesomeIcon.Copy);
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip(
+ "Left click: Copy target position as JSON.\nRight click: Copy target position as C# code.");
+ if (copy)
+ {
+ string interactionType = gameObject->NamePlateIconId switch
+ {
+ 71201 or 71211 or 71221 or 71231 or 71341 or 71351 => "AcceptQuest",
+ 71202 or 71212 or 71222 or 71232 or 71342 or 71352 => "AcceptQuest", // repeatable
+ 71205 or 71215 or 71225 or 71235 or 71345 or 71355 => "CompleteQuest",
+ _ => "Interact",
+ };
+ ImGui.SetClipboardText($$"""
+ "DataId": {{_targetManager.Target.DataId}},
+ "Position": {
+ "X": {{_targetManager.Target.Position.X.ToString(CultureInfo.InvariantCulture)}},
+ "Y": {{_targetManager.Target.Position.Y.ToString(CultureInfo.InvariantCulture)}},
+ "Z": {{_targetManager.Target.Position.Z.ToString(CultureInfo.InvariantCulture)}}
+ },
+ "TerritoryId": {{_clientState.TerritoryType}},
+ "InteractionType": "{{interactionType}}"
+ """);
+ }
+ else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
+ {
+ if (_targetManager.Target.ObjectKind == ObjectKind.Aetheryte)
+ {
+ EAetheryteLocation location = (EAetheryteLocation)_targetManager.Target.DataId;
+ ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
+ $"{{EAetheryteLocation.{location}, new({_targetManager.Target.Position.X}f, {_targetManager.Target.Position.Y}f, {_targetManager.Target.Position.Z}f)}},"));
+ }
+ else
+ ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
+ $"new({_targetManager.Target.Position.X}f, {_targetManager.Target.Position.Y}f, {_targetManager.Target.Position.Z}f)"));
+ }
+ }
+ else
+ {
+ bool copy = ImGuiComponents.IconButton(FontAwesomeIcon.Copy);
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip(
+ "Left click: Copy your position as JSON.\nRight click: Copy your position as C# code.");
+ if (copy)
+ {
+ ImGui.SetClipboardText($$"""
+ "Position": {
+ "X": {{_clientState.LocalPlayer.Position.X.ToString(CultureInfo.InvariantCulture)}},
+ "Y": {{_clientState.LocalPlayer.Position.Y.ToString(CultureInfo.InvariantCulture)}},
+ "Z": {{_clientState.LocalPlayer.Position.Z.ToString(CultureInfo.InvariantCulture)}}
+ },
+ "TerritoryId": {{_clientState.TerritoryType}},
+ "InteractionType": ""
+ """);
+ }
+ else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
+ {
+ Vector3 position = _clientState.LocalPlayer!.Position;
+ ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
+ $"new({position.X}f, {position.Y}f, {position.Z}f)"));
+ }
+ }
+
+ ulong hoveredItemId = _gameGui.HoveredItem;
+ if (hoveredItemId != 0)
+ {
+ ImGui.Separator();
+ ImGui.Text($"Hovered Item: {hoveredItemId}");
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Globalization;
+using System.Numerics;
+using Dalamud.Game.ClientState.Conditions;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Utility;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.UI.Agent;
+using ImGuiNET;
+using Questionable.Controller;
+using Questionable.External;
+
+namespace Questionable.Windows.QuestComponents;
+
+internal sealed class QuickAccessButtonsComponent
+{
+ private readonly QuestController _questController;
+ private readonly MovementController _movementController;
+ private readonly GameUiController _gameUiController;
+ private readonly GameFunctions _gameFunctions;
+ private readonly ChatFunctions _chatFunctions;
+ private readonly QuestRegistry _questRegistry;
+ private readonly NavmeshIpc _navmeshIpc;
+ private readonly QuestValidationWindow _questValidationWindow;
+ private readonly IClientState _clientState;
+ private readonly ICondition _condition;
+ private readonly IFramework _framework;
+
+ public QuickAccessButtonsComponent(QuestController questController, MovementController movementController,
+ GameUiController gameUiController, GameFunctions gameFunctions, ChatFunctions chatFunctions,
+ QuestRegistry questRegistry, NavmeshIpc navmeshIpc, QuestValidationWindow questValidationWindow,
+ IClientState clientState, ICondition condition, IFramework framework)
+ {
+ _questController = questController;
+ _movementController = movementController;
+ _gameUiController = gameUiController;
+ _gameFunctions = gameFunctions;
+ _chatFunctions = chatFunctions;
+ _questRegistry = questRegistry;
+ _navmeshIpc = navmeshIpc;
+ _questValidationWindow = questValidationWindow;
+ _clientState = clientState;
+ _condition = condition;
+ _framework = framework;
+ }
+
+ public unsafe void Draw()
+ {
+ var map = AgentMap.Instance();
+ ImGui.BeginDisabled(map == null || map->IsFlagMarkerSet == 0 ||
+ map->FlagMapMarker.TerritoryId != _clientState.TerritoryType ||
+ !_navmeshIpc.IsReady);
+ if (ImGui.Button("Move to Flag"))
+ {
+ _movementController.Destination = null;
+ _chatFunctions.ExecuteCommand(
+ $"/vnav {(_condition[ConditionFlag.Mounted] && _gameFunctions.IsFlyingUnlockedInCurrentZone() ? "flyflag" : "moveflag")}");
+ }
+
+ ImGui.EndDisabled();
+
+ ImGui.SameLine();
+
+ ImGui.BeginDisabled(!_movementController.IsPathRunning);
+ if (ImGui.Button("Stop Nav"))
+ {
+ _movementController.Stop();
+ _questController.Stop("Manual");
+ }
+
+ ImGui.EndDisabled();
+
+ if (ImGui.Button("Reload Data"))
+ {
+ _questController.Reload();
+ _framework.RunOnTick(() => _gameUiController.HandleCurrentDialogueChoices(),
+ TimeSpan.FromMilliseconds(200));
+ }
+
+ if (_questRegistry.ValidationIssueCount > 0)
+ {
+ ImGui.SameLine();
+ if (DrawValidationIssuesButton())
+ _questValidationWindow.IsOpen = true;
+ }
+ }
+
+ private bool DrawValidationIssuesButton()
+ {
+ int errorCount = _questRegistry.ValidationErrorCount;
+ int infoCount = _questRegistry.ValidationIssueCount - _questRegistry.ValidationErrorCount;
+ if (errorCount == 0 && infoCount == 0)
+ return false;
+
+ int partsToRender = errorCount == 0 || infoCount == 0 ? 1 : 2;
+ using var id = ImRaii.PushId("validationissues");
+
+ ImGui.PushFont(UiBuilder.IconFont);
+ var icon1 = FontAwesomeIcon.TimesCircle;
+ var icon2 = FontAwesomeIcon.InfoCircle;
+ Vector2 iconSize1 = errorCount > 0 ? ImGui.CalcTextSize(icon1.ToIconString()) : Vector2.Zero;
+ Vector2 iconSize2 = infoCount > 0 ? ImGui.CalcTextSize(icon2.ToIconString()) : Vector2.Zero;
+ ImGui.PopFont();
+
+ string text1 = errorCount > 0 ? errorCount.ToString(CultureInfo.InvariantCulture) : string.Empty;
+ string text2 = infoCount > 0 ? infoCount.ToString(CultureInfo.InvariantCulture) : string.Empty;
+ Vector2 textSize1 = errorCount > 0 ? ImGui.CalcTextSize(text1) : Vector2.Zero;
+ Vector2 textSize2 = infoCount > 0 ? ImGui.CalcTextSize(text2) : Vector2.Zero;
+ var dl = ImGui.GetWindowDrawList();
+ var cursor = ImGui.GetCursorScreenPos();
+
+ var iconPadding = 3 * ImGuiHelpers.GlobalScale;
+
+ // Draw an ImGui button with the icon and text
+ var buttonWidth = iconSize1.X + iconSize2.X + textSize1.X + textSize2.X +
+ (ImGui.GetStyle().FramePadding.X * 2) + iconPadding * 2 * partsToRender;
+ var buttonHeight = ImGui.GetFrameHeight();
+ var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight));
+
+ // Draw the icon on the window drawlist
+ Vector2 position = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X,
+ cursor.Y + ImGui.GetStyle().FramePadding.Y);
+ if (errorCount > 0)
+ {
+ ImGui.PushFont(UiBuilder.IconFont);
+ dl.AddText(position, ImGui.GetColorU32(ImGuiColors.DalamudRed), icon1.ToIconString());
+ ImGui.PopFont();
+ position = position with { X = position.X + iconSize1.X + iconPadding };
+
+ // Draw the text on the window drawlist
+ dl.AddText(position, ImGui.GetColorU32(ImGuiCol.Text), text1);
+ position = position with { X = position.X + textSize1.X + 2 * iconPadding };
+ }
+
+ if (infoCount > 0)
+ {
+ ImGui.PushFont(UiBuilder.IconFont);
+ dl.AddText(position, ImGui.GetColorU32(ImGuiColors.ParsedBlue), icon2.ToIconString());
+ ImGui.PopFont();
+ position = position with { X = position.X + iconSize2.X + iconPadding };
+
+ // Draw the text on the window drawlist
+ dl.AddText(position, ImGui.GetColorU32(ImGuiCol.Text), text2);
+ }
+
+ return button;
+ }
+}
--- /dev/null
+using ImGuiNET;
+using Questionable.Controller;
+
+namespace Questionable.Windows.QuestComponents;
+
+internal sealed class RemainingTasksComponent
+{
+ private readonly QuestController _questController;
+
+ public RemainingTasksComponent(QuestController questController)
+ {
+ _questController = questController;
+ }
+
+ public void Draw()
+ {
+ var remainingTasks = _questController.GetRemainingTaskNames();
+ if (remainingTasks.Count > 0)
+ {
+ ImGui.Separator();
+ ImGui.BeginDisabled();
+ foreach (var task in remainingTasks)
+ ImGui.TextUnformatted(task);
+ ImGui.EndDisabled();
+ }
+ }
+}
private readonly IDalamudPluginInterface _pluginInterface;
private readonly TerritoryData _territoryData;
private readonly IClientState _clientState;
+ private readonly UiUtils _uiUtils;
private List<QuestInfo> _quests = [];
private List<QuestInfo> _offeredQuests = [];
public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, GameFunctions gameFunctions,
QuestController questController, QuestRegistry questRegistry, IDalamudPluginInterface pluginInterface,
- TerritoryData territoryData, IClientState clientState)
+ TerritoryData territoryData, IClientState clientState, UiUtils uiUtils)
: base($"Quest Selection{WindowId}")
{
_questData = questData;
_pluginInterface = pluginInterface;
_territoryData = territoryData;
_clientState = clientState;
+ _uiUtils = uiUtils;
Size = new Vector2(500, 200);
SizeCondition = ImGuiCond.Once;
if (ImGui.TableNextColumn())
{
ImGui.AlignTextToFramePadding();
- var (color, icon, tooltipText) = GetQuestStyle(quest.QuestId);
+ var (color, icon, tooltipText) = _uiUtils.GetQuestStyle(quest.QuestId);
using (var _ = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
if (isKnownQuest)
_chatGui.Print($"Copied '{fileName}' to clipboard");
}
- private (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ushort questId)
- {
- if (_gameFunctions.IsQuestAccepted(questId))
- return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
- else if (_gameFunctions.IsQuestAcceptedOrComplete(questId))
- return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check, "Complete");
- else if (_gameFunctions.IsQuestLocked(questId))
- return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times, "Locked");
- else
- return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight, "Available");
- }
-
private void DrawQuestUnlocks(QuestInfo quest, int counter)
{
if (counter >= 10)
foreach (var q in quest.PreviousQuests)
{
var qInfo = _questData.GetQuestInfo(q);
- var (iconColor, icon, _) = GetQuestStyle(q);
+ var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
// ReSharper disable once UnusedVariable
using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
foreach (var q in quest.QuestLocks)
{
var qInfo = _questData.GetQuestInfo(q);
- var (iconColor, icon, _) = GetQuestStyle(q);
+ var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
// ReSharper disable once UnusedVariable
using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
}
}
+ if (counter == 0 && quest.PreviousInstanceContent.Count > 0)
+ {
+ if (quest.PreviousInstanceContent.Count > 1)
+ {
+ if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.All)
+ ImGui.Text("Requires all:");
+ else if (quest.PreviousQuestJoin == QuestInfo.QuestJoin.AtLeastOne)
+ ImGui.Text("Requires one:");
+ }
+ else
+ ImGui.Text("Requires:");
+
+ foreach (var instanceId in quest.PreviousInstanceContent)
+ {
+ string instanceName = _territoryData.GetInstanceName(instanceId) ?? "?";
+ var (iconColor, icon) = UiUtils.GetInstanceStyle(instanceId);
+
+ // ReSharper disable once UnusedVariable
+ using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
+ {
+ ImGui.TextColored(iconColor, icon.ToIconString());
+ }
+
+ ImGui.SameLine();
+ ImGui.TextUnformatted(instanceName);
+ }
+ }
+
if (counter > 0)
ImGui.Unindent();
}
-using System;
-using System.Diagnostics;
-using System.Globalization;
-using System.Linq;
-using System.Numerics;
-using Dalamud.Game.ClientState.Conditions;
-using Dalamud.Game.ClientState.Objects;
-using Dalamud.Game.Text;
-using Dalamud.Interface;
-using Dalamud.Interface.Colors;
-using Dalamud.Interface.Components;
-using Dalamud.Interface.Utility;
-using Dalamud.Interface.Utility.Raii;
+using System.Numerics;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
-using FFXIVClientStructs.FFXIV.Client.Game.Control;
-using FFXIVClientStructs.FFXIV.Client.Game.Object;
-using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET;
using LLib.ImGui;
-using Microsoft.Extensions.Logging;
using Questionable.Controller;
-using Questionable.Controller.Steps.Shared;
using Questionable.Data;
-using Questionable.External;
-using Questionable.Model;
-using Questionable.Model.V1;
-using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
+using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;
internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
{
+
private readonly IDalamudPluginInterface _pluginInterface;
- private readonly MovementController _movementController;
private readonly QuestController _questController;
- private readonly GameFunctions _gameFunctions;
- private readonly ChatFunctions _chatFunctions;
private readonly IClientState _clientState;
- private readonly IFramework _framework;
- private readonly ITargetManager _targetManager;
- private readonly GameUiController _gameUiController;
- private readonly CombatController _combatController;
private readonly Configuration _configuration;
- private readonly NavmeshIpc _navmeshIpc;
- private readonly QuestRegistry _questRegistry;
- private readonly QuestData _questData;
private readonly TerritoryData _territoryData;
- private readonly ICondition _condition;
- private readonly IGameGui _gameGui;
- private readonly QuestSelectionWindow _questSelectionWindow;
- private readonly QuestValidationWindow _questValidationWindow;
- private readonly ICommandManager _commandManager;
- private readonly ILogger<QuestWindow> _logger;
+ private readonly ActiveQuestComponent _activeQuestComponent;
+ private readonly ARealmRebornComponent _aRealmRebornComponent;
+ private readonly CreationUtilsComponent _creationUtilsComponent;
+ private readonly QuickAccessButtonsComponent _quickAccessButtonsComponent;
+ private readonly RemainingTasksComponent _remainingTasksComponent;
public QuestWindow(IDalamudPluginInterface pluginInterface,
- MovementController movementController,
QuestController questController,
- GameFunctions gameFunctions,
- ChatFunctions chatFunctions,
IClientState clientState,
- IFramework framework,
- ITargetManager targetManager,
- GameUiController gameUiController,
- CombatController combatController,
Configuration configuration,
- NavmeshIpc navmeshIpc,
- QuestRegistry questRegistry,
- QuestData questData,
TerritoryData territoryData,
- ICondition condition,
- IGameGui gameGui,
- QuestSelectionWindow questSelectionWindow,
- QuestValidationWindow questValidationWindow,
- ICommandManager commandManager,
- ILogger<QuestWindow> logger)
+ ActiveQuestComponent activeQuestComponent,
+ ARealmRebornComponent aRealmRebornComponent,
+ CreationUtilsComponent creationUtilsComponent,
+ QuickAccessButtonsComponent quickAccessButtonsComponent,
+ RemainingTasksComponent remainingTasksComponent)
: base("Questionable###Questionable", ImGuiWindowFlags.AlwaysAutoResize)
{
_pluginInterface = pluginInterface;
- _movementController = movementController;
_questController = questController;
- _gameFunctions = gameFunctions;
- _chatFunctions = chatFunctions;
_clientState = clientState;
- _framework = framework;
- _targetManager = targetManager;
- _gameUiController = gameUiController;
- _combatController = combatController;
_configuration = configuration;
- _navmeshIpc = navmeshIpc;
- _questRegistry = questRegistry;
- _questData = questData;
_territoryData = territoryData;
- _condition = condition;
- _gameGui = gameGui;
- _questSelectionWindow = questSelectionWindow;
- _questValidationWindow = questValidationWindow;
- _commandManager = commandManager;
- _logger = logger;
+ _activeQuestComponent = activeQuestComponent;
+ _aRealmRebornComponent = aRealmRebornComponent;
+ _creationUtilsComponent = creationUtilsComponent;
+ _quickAccessButtonsComponent = quickAccessButtonsComponent;
+ _remainingTasksComponent = remainingTasksComponent;
#if DEBUG
IsOpen = true;
public override void Draw()
{
- DrawQuest();
- ImGui.Separator();
-
- DrawCreationUtils();
+ _activeQuestComponent.Draw();
ImGui.Separator();
- DrawQuickAccessButtons();
- DrawRemainingTasks();
- }
-
- private void DrawQuest()
- {
- var currentQuestDetails = _questController.CurrentQuestDetails;
- QuestController.QuestProgress? currentQuest = currentQuestDetails?.Progress;
- QuestController.CurrentQuestType? currentQuestType = currentQuestDetails?.Type;
- if (currentQuest != null)
- {
- if (currentQuestType == QuestController.CurrentQuestType.Simulated)
- {
- var simulatedQuest = _questController.SimulatedQuest ?? currentQuest;
- using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
- ImGui.TextUnformatted(
- $"Simulated Quest: {simulatedQuest.Quest.Info.Name} / {simulatedQuest.Sequence} / {simulatedQuest.Step}");
- }
- else if (currentQuestType == QuestController.CurrentQuestType.Next)
- {
- var startedQuest = _questController.StartedQuest;
- if (startedQuest != null)
- {
- ImGui.TextUnformatted(
- $"Quest: {startedQuest.Quest.Info.Name} / {startedQuest.Sequence} / {startedQuest.Step}");
-
- if (startedQuest.Quest.Root.Disabled)
- {
- ImGui.SameLine();
- ImGui.TextColored(ImGuiColors.DalamudRed, "Disabled");
- }
- }
-
- using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
- ImGui.TextUnformatted(
- $"Next Quest: {currentQuest.Quest.Info.Name} / {currentQuest.Sequence} / {currentQuest.Step}");
- }
- else
- {
- ImGui.TextUnformatted(
- $"Quest: {currentQuest.Quest.Info.Name} / {currentQuest.Sequence} / {currentQuest.Step}");
-
- if (currentQuest.Quest.Root.Disabled)
- {
- ImGui.SameLine();
- ImGui.TextColored(ImGuiColors.DalamudRed, "Disabled");
- }
- }
-
-
- ImGui.BeginDisabled();
- var questWork = _gameFunctions.GetQuestEx(currentQuest.Quest.QuestId);
- if (questWork != null)
- {
- var qw = questWork.Value;
- string vars = "";
- for (int i = 0; i < 6; ++i)
- {
- vars += qw.Variables[i] + " ";
- if (i % 2 == 1)
- vars += " ";
- }
-
- // For combat quests, a sequence to kill 3 enemies works a bit like this:
- // Trigger enemies → 0
- // Kill first enemy → 1
- // Kill second enemy → 2
- // Last enemy → increase sequence, reset variable to 0
- // The order in which enemies are killed doesn't seem to matter.
- // If multiple waves spawn, this continues to count up (e.g. 1 enemy from wave 1, 2 enemies from wave 2, 1 from wave 3) would count to 3 then 0
- ImGui.Text($"QW: {vars.Trim()}");
- }
- else
- {
- if (currentQuest.Quest.QuestId == _questController.NextQuest?.Quest.QuestId)
- ImGui.TextUnformatted("(Next quest in story line not accepted)");
- else
- ImGui.TextUnformatted("(Not accepted)");
- }
-
- ImGui.EndDisabled();
-
- if (_combatController.IsRunning)
- ImGui.TextColored(ImGuiColors.DalamudOrange, "In Combat");
- else
- {
- ImGui.BeginDisabled();
- ImGui.TextUnformatted(_questController.DebugState ?? string.Empty);
- ImGui.EndDisabled();
- }
-
- QuestSequence? currentSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
- QuestStep? currentStep = currentSequence?.FindStep(currentQuest.Step);
- bool colored = currentStep is
- { InteractionType: EInteractionType.Instruction or EInteractionType.WaitForManualProgress };
- if (colored)
- ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudOrange);
- ImGui.TextUnformatted(currentStep?.Comment ?? currentSequence?.Comment ?? currentQuest.Quest.Root.Comment ?? string.Empty);
- if (colored)
- ImGui.PopStyleColor();
-
- //var nextStep = _questController.GetNextStep();
- //ImGui.BeginDisabled(nextStep.Step == null);
- ImGui.Text(_questController.ToStatString());
- //ImGui.EndDisabled();
-
- ImGui.BeginDisabled(_questController.IsRunning);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Play))
- {
- // if we haven't accepted this quest, mark it as next quest so that we can optionally use aetherytes to travel
- if (questWork == null)
- _questController.SetNextQuest(currentQuest.Quest);
-
- _questController.ExecuteNextStep(true);
- }
-
- ImGui.SameLine();
-
- if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.StepForward, "Step"))
- {
- _questController.ExecuteNextStep(false);
- }
-
- ImGui.EndDisabled();
- ImGui.SameLine();
-
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop))
- {
- _movementController.Stop();
- _questController.Stop("Manual");
- }
-
- bool lastStep = currentStep ==
- currentQuest.Quest.FindSequence(currentQuest.Sequence)?.Steps.LastOrDefault();
- colored = currentStep != null
- && !lastStep
- && currentStep.InteractionType == EInteractionType.Instruction
- && _questController.HasCurrentTaskMatching<WaitAtEnd.WaitNextStepOrSequence>();
-
- ImGui.BeginDisabled(lastStep);
- if (colored)
- ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
- if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip"))
- {
- _movementController.Stop();
- _questController.Skip(currentQuest.Quest.QuestId, currentQuest.Sequence);
- }
-
- if (colored)
- ImGui.PopStyleColor();
- ImGui.EndDisabled();
-
- if (_commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
- {
- ImGui.SameLine();
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
- _commandManager.DispatchCommand("/questinfo",
- currentQuest.Quest.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
- }
-
- bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
- if (ImGui.Checkbox("Automatically accept next quest", ref autoAcceptNextQuest))
- {
- _configuration.General.AutoAcceptNextQuest = autoAcceptNextQuest;
- _pluginInterface.SavePluginConfig(_configuration);
- }
-
-
- if (_questController.SimulatedQuest != null)
- {
- var simulatedQuest = _questController.SimulatedQuest;
-
- ImGui.Separator();
- ImGui.TextColored(ImGuiColors.DalamudRed, "Quest sim active (experimental)");
- ImGui.Text($"Sequence: {simulatedQuest.Sequence}");
-
- ImGui.BeginDisabled(simulatedQuest.Sequence == 0);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus))
- {
- _movementController.Stop();
- _questController.Stop("Sim-");
-
- byte oldSequence = simulatedQuest.Sequence;
- byte newSequence = simulatedQuest.Quest.Root.QuestSequence
- .Select(x => (byte)x.Sequence)
- .LastOrDefault(x => x < oldSequence, byte.MinValue);
-
- _questController.SimulatedQuest.SetSequence(newSequence);
- }
-
- ImGui.EndDisabled();
-
- ImGui.SameLine();
- ImGui.BeginDisabled(simulatedQuest.Sequence >= 255);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
- {
- _movementController.Stop();
- _questController.Stop("Sim+");
-
- byte oldSequence = simulatedQuest.Sequence;
- byte newSequence = simulatedQuest.Quest.Root.QuestSequence
- .Select(x => (byte)x.Sequence)
- .FirstOrDefault(x => x > oldSequence, byte.MaxValue);
-
- simulatedQuest.SetSequence(newSequence);
- }
-
- ImGui.EndDisabled();
-
- var simulatedSequence = simulatedQuest.Quest.FindSequence(simulatedQuest.Sequence);
- if (simulatedSequence != null)
- {
- using var _ = ImRaii.PushId("SimulatedStep");
-
- ImGui.Text($"Step: {simulatedQuest.Step} / {simulatedSequence.Steps.Count - 1}");
-
- ImGui.BeginDisabled(simulatedQuest.Step == 0);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus))
- {
- _movementController.Stop();
- _questController.Stop("SimStep-");
-
- simulatedQuest.SetStep(Math.Min(simulatedQuest.Step - 1,
- simulatedSequence.Steps.Count - 1));
- }
-
- ImGui.EndDisabled();
-
- ImGui.SameLine();
- ImGui.BeginDisabled(simulatedQuest.Step >= simulatedSequence.Steps.Count);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
- {
- _movementController.Stop();
- _questController.Stop("SimStep+");
-
- simulatedQuest.SetStep(
- simulatedQuest.Step == simulatedSequence.Steps.Count - 1
- ? 255
- : (simulatedQuest.Step + 1));
- }
-
- ImGui.EndDisabled();
-
- if (ImGui.Button("Skip current task"))
- {
- _questController.SkipSimulatedTask();
- }
-
- ImGui.SameLine();
- if (ImGui.Button("Clear sim"))
- {
- _questController.SimulateQuest(null);
-
- _movementController.Stop();
- _questController.Stop("ClearSim");
- }
- }
- }
- }
- else
- {
- ImGui.Text("No active quest");
- ImGui.TextColored(ImGuiColors.DalamudGrey, $"{_questRegistry.Count} quests loaded");
- }
- }
-
- private unsafe void DrawCreationUtils()
- {
- Debug.Assert(_clientState.LocalPlayer != null, "_clientState.LocalPlayer != null");
-
- string territoryName = _territoryData.GetNameAndId(_clientState.TerritoryType);
- ImGui.Text(territoryName);
-
- if (_gameFunctions.IsFlyingUnlockedInCurrentZone())
- {
- ImGui.SameLine();
- ImGui.Text(SeIconChar.BotanistSprout.ToIconString());
- }
-
- var q = _gameFunctions.GetCurrentQuest();
- ImGui.Text($"Current Quest: {q.CurrentQuest} → {q.Sequence}");
-
-#if false
- var questManager = QuestManager.Instance();
- if (questManager != null)
- {
- for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
- {
- var trackedQuest = questManager->TrackedQuests[i];
- switch (trackedQuest.QuestType)
- {
- default:
- ImGui.Text($"Tracked quest {i}: {trackedQuest.QuestType}, {trackedQuest.Index}");
- break;
-
- case 1:
- _questRegistry.TryGetQuest(questManager->NormalQuests[trackedQuest.Index].QuestId,
- out var quest);
- ImGui.Text(
- $"Tracked quest: {questManager->NormalQuests[trackedQuest.Index].QuestId}, {trackedQuest.Index}: {quest?.Info.Name}");
- break;
- }
- }
- }
-#endif
-
- if (_targetManager.Target != null)
+ if (_aRealmRebornComponent.ShouldDraw)
{
+ _aRealmRebornComponent.Draw();
ImGui.Separator();
- ImGui.Text(string.Create(CultureInfo.InvariantCulture,
- $"Target: {_targetManager.Target.Name} ({_targetManager.Target.ObjectKind}; {_targetManager.Target.DataId})"));
-
- GameObject* gameObject = (GameObject*)_targetManager.Target.Address;
- ImGui.Text(string.Create(CultureInfo.InvariantCulture,
- $"Distance: {(_targetManager.Target.Position - _clientState.LocalPlayer.Position).Length():F2}"));
- ImGui.SameLine();
-
- float verticalDistance = _targetManager.Target.Position.Y - _clientState.LocalPlayer.Position.Y;
- string verticalDistanceText = string.Create(CultureInfo.InvariantCulture, $"Y: {verticalDistance:F2}");
- if (Math.Abs(verticalDistance) >= MovementController.DefaultVerticalInteractionDistance)
- ImGui.TextColored(ImGuiColors.DalamudOrange, verticalDistanceText);
- else
- ImGui.Text(verticalDistanceText);
-
- ImGui.SameLine();
- ImGui.Text($"QM: {gameObject->NamePlateIconId}");
-
- ImGui.BeginDisabled(!_movementController.IsNavmeshReady);
- if (!_movementController.IsPathfinding)
- {
- if (ImGui.Button("Move to Target"))
- {
- _movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId,
- _targetManager.Target.Position,
- fly: _condition[ConditionFlag.Mounted] && _gameFunctions.IsFlyingUnlockedInCurrentZone(),
- sprint: true);
- }
- }
- else
- {
- if (ImGui.Button("Cancel pathfinding"))
- _movementController.ResetPathfinding();
- }
-
- ImGui.EndDisabled();
-
- ImGui.SameLine();
- ImGui.BeginDisabled(!_questData.IsIssuerOfAnyQuest(_targetManager.Target.DataId));
- bool showQuests = ImGuiComponents.IconButton(FontAwesomeIcon.MapMarkerAlt);
- if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Show all Quests starting with your current target.");
- if (showQuests)
- _questSelectionWindow.OpenForTarget(_targetManager.Target);
-
- ImGui.EndDisabled();
-
- ImGui.SameLine();
- bool interact = ImGuiComponents.IconButton(FontAwesomeIcon.MousePointer);
- if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Interact with your current target.");
- if (interact)
- {
- ulong result = TargetSystem.Instance()->InteractWithObject(
- (GameObject*)_targetManager.Target.Address, false);
- _logger.LogInformation("XXXXX Interaction Result: {Result}", result);
- }
-
- ImGui.SameLine();
-
- bool copy = ImGuiComponents.IconButton(FontAwesomeIcon.Copy);
- if (ImGui.IsItemHovered())
- ImGui.SetTooltip(
- "Left click: Copy target position as JSON.\nRight click: Copy target position as C# code.");
- if (copy)
- {
- string interactionType = gameObject->NamePlateIconId switch
- {
- 71201 or 71211 or 71221 or 71231 or 71341 or 71351 => "AcceptQuest",
- 71202 or 71212 or 71222 or 71232 or 71342 or 71352 => "AcceptQuest", // repeatable
- 71205 or 71215 or 71225 or 71235 or 71345 or 71355 => "CompleteQuest",
- _ => "Interact",
- };
- ImGui.SetClipboardText($$"""
- "DataId": {{_targetManager.Target.DataId}},
- "Position": {
- "X": {{_targetManager.Target.Position.X.ToString(CultureInfo.InvariantCulture)}},
- "Y": {{_targetManager.Target.Position.Y.ToString(CultureInfo.InvariantCulture)}},
- "Z": {{_targetManager.Target.Position.Z.ToString(CultureInfo.InvariantCulture)}}
- },
- "TerritoryId": {{_clientState.TerritoryType}},
- "InteractionType": "{{interactionType}}"
- """);
- }
- else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
- {
- if (_targetManager.Target.ObjectKind == ObjectKind.Aetheryte)
- {
- EAetheryteLocation location = (EAetheryteLocation)_targetManager.Target.DataId;
- ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
- $"{{EAetheryteLocation.{location}, new({_targetManager.Target.Position.X}f, {_targetManager.Target.Position.Y}f, {_targetManager.Target.Position.Z}f)}},"));
- }
- else
- ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
- $"new({_targetManager.Target.Position.X}f, {_targetManager.Target.Position.Y}f, {_targetManager.Target.Position.Z}f)"));
- }
- }
- else
- {
- bool copy = ImGuiComponents.IconButton(FontAwesomeIcon.Copy);
- if (ImGui.IsItemHovered())
- ImGui.SetTooltip(
- "Left click: Copy your position as JSON.\nRight click: Copy your position as C# code.");
- if (copy)
- {
- ImGui.SetClipboardText($$"""
- "Position": {
- "X": {{_clientState.LocalPlayer.Position.X.ToString(CultureInfo.InvariantCulture)}},
- "Y": {{_clientState.LocalPlayer.Position.Y.ToString(CultureInfo.InvariantCulture)}},
- "Z": {{_clientState.LocalPlayer.Position.Z.ToString(CultureInfo.InvariantCulture)}}
- },
- "TerritoryId": {{_clientState.TerritoryType}},
- "InteractionType": ""
- """);
- }
- else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
- {
- Vector3 position = _clientState.LocalPlayer!.Position;
- ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
- $"new({position.X}f, {position.Y}f, {position.Z}f)"));
- }
- }
-
- ulong hoveredItemId = _gameGui.HoveredItem;
- if (hoveredItemId != 0)
- {
- ImGui.Separator();
- ImGui.Text($"Hovered Item: {hoveredItemId}");
- }
- }
-
- private unsafe void DrawQuickAccessButtons()
- {
- var map = AgentMap.Instance();
- ImGui.BeginDisabled(map == null || map->IsFlagMarkerSet == 0 ||
- map->FlagMapMarker.TerritoryId != _clientState.TerritoryType ||
- !_navmeshIpc.IsReady);
- if (ImGui.Button("Move to Flag"))
- {
- _movementController.Destination = null;
- _chatFunctions.ExecuteCommand(
- $"/vnav {(_condition[ConditionFlag.Mounted] && _gameFunctions.IsFlyingUnlockedInCurrentZone() ? "flyflag" : "moveflag")}");
- }
-
- ImGui.EndDisabled();
-
- ImGui.SameLine();
-
- ImGui.BeginDisabled(!_movementController.IsPathRunning);
- if (ImGui.Button("Stop Nav"))
- {
- _movementController.Stop();
- _questController.Stop("Manual");
}
- ImGui.EndDisabled();
-
- if (ImGui.Button("Reload Data"))
- {
- _questController.Reload();
- _framework.RunOnTick(() => _gameUiController.HandleCurrentDialogueChoices(),
- TimeSpan.FromMilliseconds(200));
- }
-
- if (_questRegistry.ValidationIssueCount > 0)
- {
- ImGui.SameLine();
- if (DrawValidationIssuesButton())
- _questValidationWindow.IsOpen = true;
- }
- }
-
- private bool DrawValidationIssuesButton()
- {
- int errorCount = _questRegistry.ValidationErrorCount;
- int infoCount = _questRegistry.ValidationIssueCount - _questRegistry.ValidationErrorCount;
- if (errorCount == 0 && infoCount == 0)
- return false;
-
- int partsToRender = errorCount == 0 || infoCount == 0 ? 1 : 2;
- using var id = ImRaii.PushId("validationissues");
-
- ImGui.PushFont(UiBuilder.IconFont);
- var icon1 = FontAwesomeIcon.TimesCircle;
- var icon2 = FontAwesomeIcon.InfoCircle;
- Vector2 iconSize1 = errorCount > 0 ? ImGui.CalcTextSize(icon1.ToIconString()) : Vector2.Zero;
- Vector2 iconSize2 = infoCount > 0 ? ImGui.CalcTextSize(icon2.ToIconString()) : Vector2.Zero;
- ImGui.PopFont();
-
- string text1 = errorCount > 0 ? errorCount.ToString(CultureInfo.InvariantCulture) : string.Empty;
- string text2 = infoCount > 0 ? infoCount.ToString(CultureInfo.InvariantCulture) : string.Empty;
- Vector2 textSize1 = errorCount > 0 ? ImGui.CalcTextSize(text1) : Vector2.Zero;
- Vector2 textSize2 = infoCount > 0 ? ImGui.CalcTextSize(text2) : Vector2.Zero;
- var dl = ImGui.GetWindowDrawList();
- var cursor = ImGui.GetCursorScreenPos();
-
- var iconPadding = 3 * ImGuiHelpers.GlobalScale;
-
- // Draw an ImGui button with the icon and text
- var buttonWidth = iconSize1.X + iconSize2.X + textSize1.X + textSize2.X +
- (ImGui.GetStyle().FramePadding.X * 2) + iconPadding * 2 * partsToRender;
- var buttonHeight = ImGui.GetFrameHeight();
- var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight));
-
- // Draw the icon on the window drawlist
- Vector2 position = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X,
- cursor.Y + ImGui.GetStyle().FramePadding.Y);
- if (errorCount > 0)
- {
- ImGui.PushFont(UiBuilder.IconFont);
- dl.AddText(position, ImGui.GetColorU32(ImGuiColors.DalamudRed), icon1.ToIconString());
- ImGui.PopFont();
- position = position with { X = position.X + iconSize1.X + iconPadding };
-
- // Draw the text on the window drawlist
- dl.AddText(position, ImGui.GetColorU32(ImGuiCol.Text), text1);
- position = position with { X = position.X + textSize1.X + 2 * iconPadding };
- }
-
- if (infoCount > 0)
- {
- ImGui.PushFont(UiBuilder.IconFont);
- dl.AddText(position, ImGui.GetColorU32(ImGuiColors.ParsedBlue), icon2.ToIconString());
- ImGui.PopFont();
- position = position with { X = position.X + iconSize2.X + iconPadding };
-
- // Draw the text on the window drawlist
- dl.AddText(position, ImGui.GetColorU32(ImGuiCol.Text), text2);
- }
-
- return button;
- }
+ _creationUtilsComponent.Draw();
+ ImGui.Separator();
- private void DrawRemainingTasks()
- {
- var remainingTasks = _questController.GetRemainingTaskNames();
- if (remainingTasks.Count > 0)
- {
- ImGui.Separator();
- ImGui.BeginDisabled();
- foreach (var task in remainingTasks)
- ImGui.TextUnformatted(task);
- ImGui.EndDisabled();
- }
+ _quickAccessButtonsComponent.Draw();
+ _remainingTasksComponent.Draw();
}
}
--- /dev/null
+using System.Numerics;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Plugin;
+using FFXIVClientStructs.FFXIV.Client.Game.UI;
+using ImGuiNET;
+
+namespace Questionable.Windows;
+
+internal sealed class UiUtils
+{
+ private readonly GameFunctions _gameFunctions;
+ private readonly IDalamudPluginInterface _pluginInterface;
+
+ public UiUtils(GameFunctions gameFunctions, IDalamudPluginInterface pluginInterface)
+ {
+ _gameFunctions = gameFunctions;
+ _pluginInterface = pluginInterface;
+ }
+
+ public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ushort questId)
+ {
+ if (_gameFunctions.IsQuestAccepted(questId))
+ return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
+ else if (_gameFunctions.IsQuestAcceptedOrComplete(questId))
+ return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check, "Complete");
+ else if (_gameFunctions.IsQuestLocked(questId))
+ return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times, "Locked");
+ else
+ return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight, "Available");
+ }
+
+ public static (Vector4 color, FontAwesomeIcon icon) GetInstanceStyle(ushort instanceId)
+ {
+ if (UIState.IsInstanceContentCompleted(instanceId))
+ return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check);
+ else if (UIState.IsInstanceContentUnlocked(instanceId))
+ return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight);
+ else
+ return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times);
+ }
+
+ public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon)
+ {
+ // ReSharper disable once UnusedVariable
+ using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
+ {
+ ImGui.TextColored(color, icon.ToIconString());
+ }
+
+ bool hover = ImGui.IsItemHovered();
+
+ ImGui.SameLine();
+ ImGui.TextUnformatted(text);
+ return hover;
+ }
+}