punish v6.8.18.0

This commit is contained in:
alydev 2025-10-09 07:47:19 +10:00
commit cfb4dea47e
316 changed files with 554088 additions and 0 deletions

View file

@ -0,0 +1,64 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using LLib.ImGui;
using Questionable.Windows.ConfigComponents;
namespace Questionable.Windows;
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly GeneralConfigComponent _generalConfigComponent;
private readonly PluginConfigComponent _pluginConfigComponent;
private readonly DutyConfigComponent _dutyConfigComponent;
private readonly SinglePlayerDutyConfigComponent _singlePlayerDutyConfigComponent;
private readonly StopConditionComponent _stopConditionComponent;
private readonly NotificationConfigComponent _notificationConfigComponent;
private readonly DebugConfigComponent _debugConfigComponent;
private readonly Configuration _configuration;
public WindowConfig WindowConfig => _configuration.ConfigWindowConfig;
public ConfigWindow(IDalamudPluginInterface pluginInterface, GeneralConfigComponent generalConfigComponent, PluginConfigComponent pluginConfigComponent, DutyConfigComponent dutyConfigComponent, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent, StopConditionComponent stopConditionComponent, NotificationConfigComponent notificationConfigComponent, DebugConfigComponent debugConfigComponent, Configuration configuration)
: base("Config - Questionable###QuestionableConfig", ImGuiWindowFlags.AlwaysAutoResize)
{
_pluginInterface = pluginInterface;
_generalConfigComponent = generalConfigComponent;
_pluginConfigComponent = pluginConfigComponent;
_dutyConfigComponent = dutyConfigComponent;
_singlePlayerDutyConfigComponent = singlePlayerDutyConfigComponent;
_stopConditionComponent = stopConditionComponent;
_notificationConfigComponent = notificationConfigComponent;
_debugConfigComponent = debugConfigComponent;
_configuration = configuration;
}
public override void DrawContent()
{
using ImRaii.IEndObject endObject = ImRaii.TabBar("QuestionableConfigTabs");
if (!(!endObject))
{
_generalConfigComponent.DrawTab();
_pluginConfigComponent.DrawTab();
_dutyConfigComponent.DrawTab();
_singlePlayerDutyConfigComponent.DrawTab();
_stopConditionComponent.DrawTab();
_notificationConfigComponent.DrawTab();
_debugConfigComponent.DrawTab();
}
}
public void SaveWindowConfig()
{
_pluginInterface.SavePluginConfig(_configuration);
}
}

View file

@ -0,0 +1,230 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
using Questionable.Model.Common;
using Questionable.Model.Questing;
namespace Questionable.Windows;
internal sealed class DebugOverlay : Window
{
private readonly QuestController _questController;
private readonly QuestRegistry _questRegistry;
private readonly IGameGui _gameGui;
private readonly IClientState _clientState;
private readonly ICondition _condition;
private readonly AetheryteData _aetheryteData;
private readonly IObjectTable _objectTable;
private readonly CombatController _combatController;
private readonly Configuration _configuration;
public ElementId? HighlightedQuest { get; set; }
public DebugOverlay(QuestController questController, QuestRegistry questRegistry, IGameGui gameGui, IClientState clientState, ICondition condition, AetheryteData aetheryteData, IObjectTable objectTable, CombatController combatController, Configuration configuration)
: base("Questionable Debug Overlay###QuestionableDebugOverlay", ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoSavedSettings, forceMainWindow: true)
{
_questController = questController;
_questRegistry = questRegistry;
_gameGui = gameGui;
_clientState = clientState;
_condition = condition;
_aetheryteData = aetheryteData;
_objectTable = objectTable;
_combatController = combatController;
_configuration = configuration;
base.Position = Vector2.Zero;
base.PositionCondition = ImGuiCond.Always;
base.Size = ImGui.GetIO().DisplaySize;
base.SizeCondition = ImGuiCond.Always;
base.IsOpen = true;
base.ShowCloseButton = false;
base.RespectCloseHotkey = false;
}
public override bool DrawConditions()
{
return _configuration.Advanced.DebugOverlay;
}
public override void PreDraw()
{
base.Size = ImGui.GetIO().DisplaySize;
}
public override void Draw()
{
if (_condition[ConditionFlag.OccupiedInCutSceneEvent])
{
return;
}
IClientState clientState = _clientState;
if (clientState != null && clientState.IsLoggedIn && clientState.LocalPlayer != null && !clientState.IsPvPExcludingDen && _questController.IsQuestWindowOpen)
{
DrawCurrentQuest();
DrawHighlightedQuest();
if (_configuration.Advanced.CombatDataOverlay)
{
DrawCombatTargets();
}
}
}
private void DrawCurrentQuest()
{
QuestController.QuestProgress currentQuest = _questController.CurrentQuest;
if (currentQuest == null)
{
return;
}
QuestSequence questSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
if (questSequence == null)
{
return;
}
for (int i = currentQuest.Step; i <= questSequence.Steps.Count; i++)
{
QuestStep questStep = questSequence.FindStep(i);
if (questStep != null && TryGetPosition(questStep, out var position))
{
DrawStep(i.ToString(CultureInfo.InvariantCulture), questStep, position.Value, (Vector3.Distance(_clientState.LocalPlayer.Position, position.Value) < questStep.CalculateActualStopDistance()) ? 4278255360u : 4278190335u);
}
}
}
private void DrawHighlightedQuest()
{
if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest, out Quest quest))
{
return;
}
foreach (QuestSequence item in quest.Root.QuestSequence)
{
for (int i = 0; i < item.Steps.Count; i++)
{
QuestStep questStep = item.FindStep(i);
if (questStep != null && TryGetPosition(questStep, out var position))
{
DrawStep($"{quest.Id} / {item.Sequence} / {i}", questStep, position.Value, uint.MaxValue);
}
}
}
}
private void DrawStep(string counter, QuestStep step, Vector3 position, uint color)
{
if (!step.Disabled && step.TerritoryId == _clientState.TerritoryType && _gameGui.WorldToScreen(position, out var screenPos))
{
ImGui.GetWindowDrawList().AddCircleFilled(screenPos, 3f, color);
ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList();
Vector2 pos = screenPos + new Vector2(10f, -8f);
ImU8String text = new ImU8String(7, 5);
text.AppendFormatted(counter);
text.AppendLiteral(": ");
text.AppendFormatted(step.InteractionType);
text.AppendLiteral("\n");
text.AppendFormatted(position.ToString("G", CultureInfo.InvariantCulture));
text.AppendLiteral(" [");
text.AppendFormatted((position - _clientState.LocalPlayer.Position).Length(), "N2");
text.AppendLiteral("]\n");
text.AppendFormatted(step.Comment);
windowDrawList.AddText(pos, color, text);
}
}
private void DrawCombatTargets()
{
if (!_combatController.IsRunning)
{
return;
}
foreach (IGameObject item3 in _objectTable.Skip(1))
{
if (item3 is IBattleNpc && _gameGui.WorldToScreen(item3.Position, out var screenPos))
{
(int Priority, string Reason) killPriority = _combatController.GetKillPriority(item3);
int item = killPriority.Priority;
string item2 = killPriority.Reason;
ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList();
Vector2 pos = screenPos + new Vector2(10f, -8f);
int col = ((item > 0) ? (-16711936) : (-1));
ImU8String text = new ImU8String(12, 7);
text.AppendFormatted(item3.Name);
text.AppendLiteral("/");
text.AppendFormatted(item3.GameObjectId, "X");
text.AppendLiteral(", ");
text.AppendFormatted(item3.DataId);
text.AppendLiteral(", ");
text.AppendFormatted(item);
text.AppendLiteral(" - ");
text.AppendFormatted(item2);
text.AppendLiteral(", ");
text.AppendFormatted(Vector3.Distance(item3.Position, _clientState.LocalPlayer.Position), "N2");
text.AppendLiteral(", ");
text.AppendFormatted(item3.IsTargetable);
windowDrawList.AddText(pos, (uint)col, text);
}
}
}
private bool TryGetPosition(QuestStep step, [NotNullWhen(true)] out Vector3? position)
{
if (step.Position.HasValue)
{
position = step.Position;
return true;
}
EAetheryteLocation valueOrDefault = default(EAetheryteLocation);
bool flag;
if (step != null)
{
EInteractionType interactionType = step.InteractionType;
if ((uint)(interactionType - 4) <= 1u)
{
EAetheryteLocation? aetheryte = step.Aetheryte;
if (aetheryte.HasValue)
{
valueOrDefault = aetheryte.GetValueOrDefault();
flag = true;
goto IL_004e;
}
}
}
flag = false;
goto IL_004e;
IL_004e:
if (flag)
{
position = _aetheryteData.Locations[valueOrDefault];
return true;
}
if (step != null && step.InteractionType == EInteractionType.AttuneAethernetShard)
{
EAetheryteLocation? aetheryte = step.AethernetShard;
if (aetheryte.HasValue)
{
EAetheryteLocation valueOrDefault2 = aetheryte.GetValueOrDefault();
position = _aetheryteData.Locations[valueOrDefault2];
return true;
}
}
position = null;
return false;
}
}

View file

@ -0,0 +1,80 @@
using System;
using System.Numerics;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using LLib.ImGui;
using Questionable.Controller;
using Questionable.Windows.JournalComponents;
namespace Questionable.Windows;
internal sealed class JournalProgressWindow : LWindow, IDisposable
{
private readonly QuestJournalComponent _questJournalComponent;
private readonly AlliedSocietyJournalComponent _alliedSocietyJournalComponent;
private readonly QuestRewardComponent _questRewardComponent;
private readonly GatheringJournalComponent _gatheringJournalComponent;
private readonly QuestRegistry _questRegistry;
private readonly IClientState _clientState;
public JournalProgressWindow(QuestJournalComponent questJournalComponent, QuestRewardComponent questRewardComponent, AlliedSocietyJournalComponent alliedSocietyJournalComponent, GatheringJournalComponent gatheringJournalComponent, QuestRegistry questRegistry, IClientState clientState)
: base("Journal Progress###QuestionableJournalProgress")
{
_questJournalComponent = questJournalComponent;
_alliedSocietyJournalComponent = alliedSocietyJournalComponent;
_questRewardComponent = questRewardComponent;
_gatheringJournalComponent = gatheringJournalComponent;
_questRegistry = questRegistry;
_clientState = clientState;
_clientState.Login += _questJournalComponent.RefreshCounts;
_clientState.Login += _gatheringJournalComponent.RefreshCounts;
_clientState.Logout += _questJournalComponent.ClearCounts;
_clientState.Logout += _gatheringJournalComponent.ClearCounts;
_questRegistry.Reloaded += OnQuestsReloaded;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(700f, 500f)
};
}
private void OnQuestsReloaded(object? sender, EventArgs e)
{
_questJournalComponent.RefreshCounts();
_gatheringJournalComponent.RefreshCounts();
}
public override void OnOpen()
{
_questJournalComponent.UpdateFilter();
_questJournalComponent.RefreshCounts();
_gatheringJournalComponent.UpdateFilter();
_gatheringJournalComponent.RefreshCounts();
}
public override void DrawContent()
{
using ImRaii.IEndObject endObject = ImRaii.TabBar("Journal");
if (!(!endObject))
{
_questJournalComponent.DrawQuests();
_alliedSocietyJournalComponent.DrawAlliedSocietyQuests();
_questRewardComponent.DrawItemRewards();
_gatheringJournalComponent.DrawGatheringItems();
}
}
public void Dispose()
{
_questRegistry.Reloaded -= OnQuestsReloaded;
_clientState.Logout -= _gatheringJournalComponent.ClearCounts;
_clientState.Logout -= _questJournalComponent.ClearCounts;
_clientState.Login -= _gatheringJournalComponent.RefreshCounts;
_clientState.Login -= _questJournalComponent.RefreshCounts;
}
}

View file

@ -0,0 +1,74 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using LLib.ImGui;
using Microsoft.Extensions.Logging;
using Questionable.Windows.ConfigComponents;
namespace Questionable.Windows;
internal sealed class OneTimeSetupWindow : LWindow
{
private readonly PluginConfigComponent _pluginConfigComponent;
private readonly Configuration _configuration;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly ILogger<OneTimeSetupWindow> _logger;
public OneTimeSetupWindow(PluginConfigComponent pluginConfigComponent, Configuration configuration, IDalamudPluginInterface pluginInterface, ILogger<OneTimeSetupWindow> logger)
: base("Questionable Setup###QuestionableOneTimeSetup", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings, forceMainWindow: true)
{
_pluginConfigComponent = pluginConfigComponent;
_configuration = configuration;
_pluginInterface = pluginInterface;
_logger = logger;
base.RespectCloseHotkey = false;
base.ShowCloseButton = false;
base.AllowPinning = false;
base.AllowClickthrough = false;
base.IsOpen = !_configuration.IsPluginSetupComplete();
_logger.LogInformation("One-time setup needed: {IsOpen}", base.IsOpen);
}
public override void DrawContent()
{
_pluginConfigComponent.Draw(out var allRequiredInstalled);
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
if (allRequiredInstalled)
{
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedGreen))
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Finish Setup"))
{
_logger.LogInformation("Marking setup as complete");
_configuration.MarkPluginSetupComplete();
_pluginInterface.SavePluginConfig(_configuration);
base.IsOpen = false;
}
}
}
else
{
using (ImRaii.Disabled())
{
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed))
{
ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Missing required plugins");
}
}
}
ImGui.SameLine();
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Close window & don't enable Questionable"))
{
_logger.LogWarning("Closing window without all required plugins installed");
base.IsOpen = false;
}
}
}

View file

@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using LLib.ImGui;
using Questionable.Controller;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
using Questionable.Windows.Utils;
namespace Questionable.Windows;
internal sealed class PriorityWindow : LWindow
{
private const string ClipboardPrefix = "qst:priority:";
private const string LegacyClipboardPrefix = "qst:v1:";
private const char ClipboardSeparator = ';';
private readonly QuestController _questController;
private readonly QuestFunctions _questFunctions;
private readonly QuestSelector _questSelector;
private readonly QuestTooltipComponent _questTooltipComponent;
private readonly UiUtils _uiUtils;
private readonly IChatGui _chatGui;
private readonly IDalamudPluginInterface _pluginInterface;
private ElementId? _draggedItem;
public PriorityWindow(QuestController questController, QuestFunctions questFunctions, QuestSelector questSelector, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IChatGui chatGui, IDalamudPluginInterface pluginInterface)
: base("Quest Priority###QuestionableQuestPriority")
{
PriorityWindow priorityWindow = this;
_questController = questController;
_questFunctions = questFunctions;
_questSelector = questSelector;
_questTooltipComponent = questTooltipComponent;
_uiUtils = uiUtils;
_chatGui = chatGui;
_pluginInterface = pluginInterface;
_questSelector.SuggestionPredicate = (Quest quest) => !quest.Info.IsMainScenarioQuest && !questFunctions.IsQuestUnobtainable(quest.Id) && questController.ManualPriorityQuests.All((Quest x) => x.Id != quest.Id);
_questSelector.DefaultPredicate = (Quest quest) => questFunctions.IsQuestAccepted(quest.Id);
_questSelector.QuestSelected = delegate(Quest quest)
{
priorityWindow._questController.ManualPriorityQuests.Add(quest);
};
base.Size = new Vector2(400f, 400f);
base.SizeCondition = ImGuiCond.Once;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(400f, 400f),
MaximumSize = new Vector2(400f, 999f)
};
}
public override void DrawContent()
{
ImGui.Text("Quests to do first:");
_questSelector.DrawSelection();
DrawQuestList();
List<ElementId> list = ParseClipboardItems();
ImGui.BeginDisabled(list.Count == 0);
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Download, "Import from Clipboard"))
{
ImportFromClipboard(list);
}
ImGui.EndDisabled();
ImGui.SameLine();
ImGui.BeginDisabled(_questController.ManualPriorityQuests.Count == 0);
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Upload, "Export to Clipboard"))
{
ExportToClipboard();
}
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Remove finished Quests"))
{
_questController.ManualPriorityQuests.RemoveAll((Quest q) => _questFunctions.IsQuestComplete(q.Id));
}
ImGui.SameLine();
using (ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl)))
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Trash, "Clear All"))
{
_questController.ClearQuestPriority();
}
}
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Hold CTRL to enable this button.");
}
ImGui.EndDisabled();
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
ImGui.TextWrapped("If you have an active MSQ quest, Questionable will generally try to do:");
ImGui.BulletText("'Priority' quests: class quests, ARR primals, ARR raids");
ImGui.BulletText("Supported quests in your 'To-Do list'\n(quests from your Journal that are always on-screen)");
ImGui.BulletText("MSQ quest (if available, unless it is marked as 'ignored'\nin your Journal)");
ImGui.TextWrapped("If you don't have any active MSQ quest, it will always try to pick up the next quest in the MSQ first.");
}
private void DrawQuestList()
{
List<Quest> manualPriorityQuests = _questController.ManualPriorityQuests;
Quest quest = null;
Quest quest2 = null;
int index = 0;
float x = ImGui.GetContentRegionAvail().X;
List<(Vector2, Vector2)> list = new List<(Vector2, Vector2)>();
for (int i = 0; i < manualPriorityQuests.Count; i++)
{
Vector2 item = ImGui.GetCursorScreenPos() + new Vector2(0f, (0f - ImGui.GetStyle().ItemSpacing.Y) / 2f);
Quest quest3 = manualPriorityQuests[i];
ImU8String id = new ImU8String(5, 1);
id.AppendLiteral("Quest");
id.AppendFormatted(quest3.Id);
using (ImRaii.PushId(id))
{
(Vector4, FontAwesomeIcon, string) questStyle = _uiUtils.GetQuestStyle(quest3.Id);
bool flag;
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
ImGui.AlignTextToFramePadding();
ImGui.TextColored(in questStyle.Item1, questStyle.Item2.ToIconString());
flag = ImGui.IsItemHovered();
}
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.Text(quest3.Info.Name);
flag |= ImGui.IsItemHovered();
if (flag)
{
_questTooltipComponent.Draw(quest3.Info);
}
if (manualPriorityQuests.Count > 1)
{
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.SameLine(ImGui.GetContentRegionAvail().X + ImGui.GetStyle().WindowPadding.X - ImGui.CalcTextSize(FontAwesomeIcon.ArrowsUpDown.ToIconString()).X - ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X - ImGui.GetStyle().FramePadding.X * 4f - ImGui.GetStyle().ItemSpacing.X);
}
if (_draggedItem == quest3.Id)
{
ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown, ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.ButtonActive)));
}
else
{
ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown);
}
if (_draggedItem == null && ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
{
_draggedItem = quest3.Id;
}
ImGui.SameLine();
}
else
{
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.SameLine(ImGui.GetContentRegionAvail().X + ImGui.GetStyle().WindowPadding.X - ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X - ImGui.GetStyle().FramePadding.X * 2f);
}
}
if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times))
{
quest = quest3;
}
}
Vector2 item2 = new Vector2(item.X + x, ImGui.GetCursorScreenPos().Y - ImGui.GetStyle().ItemSpacing.Y + 2f);
list.Add((item, item2));
}
if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left))
{
_draggedItem = null;
}
else if (_draggedItem != null)
{
Quest item3 = manualPriorityQuests.Single((Quest quest4) => quest4.Id == _draggedItem);
int num = manualPriorityQuests.IndexOf(item3);
var (pMin, pMax) = list[num];
ImGui.GetWindowDrawList().AddRect(pMin, pMax, ImGui.GetColorU32(ImGuiColors.DalamudGrey), 3f, ImDrawFlags.RoundCornersAll);
int num2 = list.FindIndex(((Vector2 TopLeft, Vector2 BottomRight) tuple2) => ImGui.IsMouseHoveringRect(tuple2.TopLeft, tuple2.BottomRight, clip: true));
if (num2 >= 0 && num != num2)
{
quest2 = manualPriorityQuests.Single((Quest quest4) => quest4.Id == _draggedItem);
index = num2;
}
}
if (quest != null)
{
manualPriorityQuests.Remove(quest);
}
if (quest2 != null)
{
manualPriorityQuests.Remove(quest2);
manualPriorityQuests.Insert(index, quest2);
}
}
private static List<ElementId> ParseClipboardItems()
{
return DecodeQuestPriority(ImGui.GetClipboardText().Trim());
}
public static List<ElementId> DecodeQuestPriority(string clipboardText)
{
List<ElementId> list = new List<ElementId>();
try
{
if (!string.IsNullOrEmpty(clipboardText))
{
string text = null;
if (clipboardText.StartsWith("qst:priority:", StringComparison.InvariantCulture))
{
text = "qst:priority:";
}
else if (clipboardText.StartsWith("qst:v1:", StringComparison.InvariantCulture))
{
text = "qst:v1:";
}
if (text != null)
{
clipboardText = clipboardText.Substring(text.Length);
string[] array = Encoding.UTF8.GetString(Convert.FromBase64String(clipboardText)).Split(';');
for (int i = 0; i < array.Length; i++)
{
ElementId item = ElementId.FromString(array[i]);
list.Add(item);
}
}
}
}
catch (Exception)
{
list.Clear();
}
return list;
}
public string EncodeQuestPriority()
{
return "qst:priority:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(';', _questController.ManualPriorityQuests.Select((Quest x) => x.Id.ToString()))));
}
private void ExportToClipboard()
{
ImGui.SetClipboardText(EncodeQuestPriority());
_chatGui.Print("Copied quests to clipboard.", "Questionable", 576);
}
private void ImportFromClipboard(List<ElementId> questElements)
{
_questController.ImportQuestPriority(questElements);
}
}

View file

@ -0,0 +1,263 @@
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.Colors;
using Dalamud.Interface.Components;
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 LLib.ImGui;
using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;
internal sealed class QuestSelectionWindow : LWindow
{
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 QuestRegistry _questRegistry;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly TerritoryData _territoryData;
private readonly IClientState _clientState;
private readonly UiUtils _uiUtils;
private readonly QuestTooltipComponent _questTooltipComponent;
private List<IQuestInfo> _quests = new List<IQuestInfo>();
private List<IQuestInfo> _offeredQuests = new List<IQuestInfo>();
private bool _onlyAvailableQuests = true;
public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, QuestFunctions questFunctions, QuestController questController, QuestRegistry questRegistry, IDalamudPluginInterface pluginInterface, TerritoryData territoryData, IClientState clientState, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent)
: base("Quest Selection###QuestionableQuestSelection")
{
_questData = questData;
_gameGui = gameGui;
_chatGui = chatGui;
_questFunctions = questFunctions;
_questController = questController;
_questRegistry = questRegistry;
_pluginInterface = pluginInterface;
_territoryData = territoryData;
_clientState = clientState;
_uiUtils = uiUtils;
_questTooltipComponent = questTooltipComponent;
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 dataId = gameObject.DataId;
string value = gameObject.Name.ToString();
base.WindowName = $"Quests starting with {value} [{dataId}]{"###QuestionableQuestSelection"}";
_quests = _questData.GetAllByIssuerDataId(dataId);
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()
{
ushort 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()
{
if (_offeredQuests.Count != 0)
{
ImGui.Checkbox("Only show quests currently offered", ref _onlyAvailableQuests);
}
using ImRaii.IEndObject endObject = ImRaii.Table("QuestSelection", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY);
if (!endObject)
{
ImGui.Text("Not table");
return;
}
float x;
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
x = ImGui.CalcTextSize(FontAwesomeIcon.Copy.ToIconString()).X;
}
ImGui.PushFont(UiBuilder.IconFont);
float initWidthOrWeight = ImGui.CalcTextSize(FontAwesomeIcon.Copy.ToIconString()).X + ImGui.CalcTextSize(FontAwesomeIcon.Copy.ToIconString()).X + ImGui.CalcTextSize(FontAwesomeIcon.Copy.ToIconString()).X + 6f * ImGui.GetStyle().FramePadding.X + 2f * ImGui.GetStyle().ItemSpacing.X;
ImGui.PopFont();
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();
foreach (IQuestInfo item in (_offeredQuests.Count != 0 && _onlyAvailableQuests) ? _offeredQuests : _quests)
{
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(ImGuiColors.DalamudGrey, 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(ImGuiColors.DalamudOrange, FontAwesomeIcon.Ban.ToIconString());
ImGui.SameLine();
}
}
ImGui.TextUnformatted(item.Name);
}
if (!ImGui.TableNextColumn())
{
continue;
}
using (ImRaii.PushId(text))
{
bool num = ImGuiComponents.IconButton(FontAwesomeIcon.Copy);
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))
{
ImGui.BeginDisabled(_questController.NextQuest != null || _questController.SimulatedQuest != null);
bool num2 = ImGuiComponents.IconButton(FontAwesomeIcon.Play);
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Start as next quest");
}
if (num2)
{
_questController.SetNextQuest(quest);
_questController.Start("QuestSelectionWindow");
}
ImGui.SameLine();
bool num3 = ImGuiComponents.IconButton(FontAwesomeIcon.AngleDoubleRight);
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Set as next quest");
}
if (num3)
{
_questController.SetNextQuest(quest);
}
ImGui.EndDisabled();
}
}
}
}
private void CopyToClipboard(IQuestInfo quest, bool suffix)
{
string text = $"{quest.QuestId}_{quest.SimplifiedName}{(suffix ? ".json" : "")}";
ImGui.SetClipboardText(text);
_chatGui.Print("Copied '" + text + "' to clipboard");
}
}

View file

@ -0,0 +1,95 @@
using System.Globalization;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using FFXIVClientStructs.FFXIV.Common.Math;
using LLib.ImGui;
using Questionable.Data;
using Questionable.Validation;
namespace Questionable.Windows;
internal sealed class QuestValidationWindow : LWindow
{
private readonly QuestValidator _questValidator;
private readonly QuestData _questData;
private readonly IDalamudPluginInterface _pluginInterface;
public QuestValidationWindow(QuestValidator questValidator, QuestData questData, IDalamudPluginInterface pluginInterface)
: base("Quest Validation###QuestionableValidator")
{
_questValidator = questValidator;
_questData = questData;
_pluginInterface = pluginInterface;
base.Size = new Vector2(600f, 200f);
base.SizeCondition = ImGuiCond.Once;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(600f, 200f)
};
}
public override void DrawContent()
{
using ImRaii.IEndObject endObject = ImRaii.Table("QuestSelection", 5, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY);
if (!endObject)
{
ImGui.Text("Not table");
return;
}
ImGui.TableSetupColumn("Quest", ImGuiTableColumnFlags.WidthFixed, 50f);
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 200f);
ImGui.TableSetupColumn("Seq", ImGuiTableColumnFlags.WidthFixed, 30f);
ImGui.TableSetupColumn("Step", ImGuiTableColumnFlags.WidthFixed, 30f);
ImGui.TableSetupColumn("Issue", ImGuiTableColumnFlags.None, 200f);
ImGui.TableHeadersRow();
foreach (ValidationIssue issue in _questValidator.Issues)
{
ImGui.TableNextRow();
if (ImGui.TableNextColumn())
{
ImGui.TextUnformatted(issue.ElementId?.ToString() ?? string.Empty);
}
if (ImGui.TableNextColumn())
{
ImGui.TextUnformatted((issue.ElementId != null) ? _questData.GetQuestInfo(issue.ElementId).Name : issue.AlliedSociety.ToString());
}
if (ImGui.TableNextColumn())
{
ImGui.TextUnformatted(issue.Sequence?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
}
if (ImGui.TableNextColumn())
{
ImGui.TextUnformatted(issue.Step?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
}
if (!ImGui.TableNextColumn())
{
continue;
}
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
if (issue.Severity == EIssueSeverity.Error)
{
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed))
{
ImGui.TextUnformatted(FontAwesomeIcon.ExclamationTriangle.ToIconString());
}
}
else
{
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedBlue))
{
ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString());
}
}
}
ImGui.SameLine();
ImGui.TextUnformatted(issue.Description);
}
}
}

View file

@ -0,0 +1,191 @@
using System;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using LLib.ImGui;
using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Data;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;
internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
{
private static readonly Version PluginVersion = typeof(QuestionablePlugin).Assembly.GetName().Version;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly QuestController _questController;
private readonly IClientState _clientState;
private readonly Configuration _configuration;
private readonly TerritoryData _territoryData;
private readonly ActiveQuestComponent _activeQuestComponent;
private readonly ARealmRebornComponent _aRealmRebornComponent;
private readonly CreationUtilsComponent _creationUtilsComponent;
private readonly EventInfoComponent _eventInfoComponent;
private readonly QuickAccessButtonsComponent _quickAccessButtonsComponent;
private readonly RemainingTasksComponent _remainingTasksComponent;
private readonly IFramework _framework;
private readonly InteractionUiController _interactionUiController;
private readonly TitleBarButton _minimizeButton;
public WindowConfig WindowConfig => _configuration.DebugWindowConfig;
public bool IsMinimized { get; set; }
public QuestWindow(IDalamudPluginInterface pluginInterface, QuestController questController, IClientState clientState, Configuration configuration, TerritoryData territoryData, ActiveQuestComponent activeQuestComponent, ARealmRebornComponent aRealmRebornComponent, EventInfoComponent eventInfoComponent, CreationUtilsComponent creationUtilsComponent, QuickAccessButtonsComponent quickAccessButtonsComponent, RemainingTasksComponent remainingTasksComponent, IFramework framework, InteractionUiController interactionUiController, ConfigWindow configWindow)
: base("Questionable v" + PluginVersion.ToString(4) + "###Questionable", ImGuiWindowFlags.AlwaysAutoResize)
{
QuestWindow questWindow = this;
_pluginInterface = pluginInterface;
_questController = questController;
_clientState = clientState;
_configuration = configuration;
_territoryData = territoryData;
_activeQuestComponent = activeQuestComponent;
_aRealmRebornComponent = aRealmRebornComponent;
_eventInfoComponent = eventInfoComponent;
_creationUtilsComponent = creationUtilsComponent;
_quickAccessButtonsComponent = quickAccessButtonsComponent;
_remainingTasksComponent = remainingTasksComponent;
_framework = framework;
_interactionUiController = interactionUiController;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(240f, 30f),
MaximumSize = default(Vector2)
};
base.RespectCloseHotkey = false;
base.AllowClickthrough = false;
_minimizeButton = new TitleBarButton
{
Icon = FontAwesomeIcon.Minus,
Priority = int.MinValue,
IconOffset = new Vector2(1.5f, 1f),
Click = delegate
{
questWindow.IsMinimized = !questWindow.IsMinimized;
questWindow._minimizeButton.Icon = (questWindow.IsMinimized ? FontAwesomeIcon.WindowMaximize : FontAwesomeIcon.Minus);
},
AvailableClickthrough = true
};
base.TitleBarButtons.Insert(0, _minimizeButton);
base.TitleBarButtons.Add(new TitleBarButton
{
Icon = FontAwesomeIcon.Cog,
IconOffset = new Vector2(1.5f, 1f),
Click = delegate
{
configWindow.IsOpenAndUncollapsed = true;
},
Priority = int.MinValue,
ShowTooltip = delegate
{
ImGui.BeginTooltip();
ImGui.Text("Open Configuration");
ImGui.EndTooltip();
}
});
_activeQuestComponent.Reload += OnReload;
_quickAccessButtonsComponent.Reload += OnReload;
_questController.IsQuestWindowOpenFunction = () => questWindow.IsOpen;
}
public void SaveWindowConfig()
{
_pluginInterface.SavePluginConfig(_configuration);
}
public override void PreOpenCheck()
{
if (_questController.IsRunning)
{
base.IsOpen = true;
base.Flags |= ImGuiWindowFlags.NoCollapse;
base.ShowCloseButton = false;
}
else
{
base.Flags &= ~ImGuiWindowFlags.NoCollapse;
base.ShowCloseButton = true;
}
}
public override bool DrawConditions()
{
if (!_configuration.IsPluginSetupComplete())
{
return false;
}
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null || _clientState.IsPvPExcludingDen)
{
return false;
}
if (_configuration.General.HideInAllInstances && _territoryData.IsDutyInstance(_clientState.TerritoryType))
{
return false;
}
return true;
}
public override void DrawContent()
{
try
{
_activeQuestComponent.Draw(IsMinimized);
if (!IsMinimized)
{
ImGui.Separator();
if (_aRealmRebornComponent.ShouldDraw)
{
_aRealmRebornComponent.Draw();
ImGui.Separator();
}
if (_eventInfoComponent.ShouldDraw)
{
_eventInfoComponent.Draw();
ImGui.Separator();
}
_creationUtilsComponent.Draw();
ImGui.Separator();
_quickAccessButtonsComponent.Draw();
_remainingTasksComponent.Draw();
}
}
catch (Exception ex)
{
ImGui.TextColored(ImGuiColors.DalamudRed, ex.ToString());
}
}
private void OnReload(object? sender, EventArgs e)
{
Reload();
}
internal void Reload()
{
_questController.Reload();
_framework.RunOnTick(delegate
{
_interactionUiController.HandleCurrentDialogueChoices();
}, TimeSpan.FromMilliseconds(200L, 0L));
}
}

View file

@ -0,0 +1,94 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Plugin;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Questionable.Functions;
using Questionable.Model.Questing;
namespace Questionable.Windows;
internal sealed class UiUtils
{
private readonly QuestFunctions _questFunctions;
private readonly IDalamudPluginInterface _pluginInterface;
public UiUtils(QuestFunctions questFunctions, IDalamudPluginInterface pluginInterface)
{
_questFunctions = questFunctions;
_pluginInterface = pluginInterface;
}
public (Vector4 Color, FontAwesomeIcon Icon, string Status) GetQuestStyle(ElementId elementId)
{
if (_questFunctions.IsQuestAccepted(elementId))
{
return (Color: ImGuiColors.DalamudYellow, Icon: FontAwesomeIcon.PersonWalkingArrowRight, Status: "Active");
}
if (elementId is QuestId questId && _questFunctions.IsDailyAlliedSocietyQuestAndAvailableToday(questId))
{
if (!_questFunctions.IsReadyToAcceptQuest(questId))
{
return (Color: ImGuiColors.ParsedGreen, Icon: FontAwesomeIcon.Check, Status: "Complete");
}
if (_questFunctions.IsQuestComplete(questId))
{
return (Color: ImGuiColors.ParsedBlue, Icon: FontAwesomeIcon.Running, Status: "Available (Complete)");
}
return (Color: ImGuiColors.DalamudYellow, Icon: FontAwesomeIcon.Running, Status: "Available");
}
if (_questFunctions.IsQuestAcceptedOrComplete(elementId))
{
return (Color: ImGuiColors.ParsedGreen, Icon: FontAwesomeIcon.Check, Status: "Complete");
}
if (_questFunctions.IsQuestUnobtainable(elementId))
{
return (Color: ImGuiColors.DalamudGrey, Icon: FontAwesomeIcon.Minus, Status: "Unobtainable");
}
if (_questFunctions.IsQuestLocked(elementId))
{
return (Color: ImGuiColors.DalamudRed, Icon: FontAwesomeIcon.Times, Status: "Locked");
}
return (Color: ImGuiColors.DalamudYellow, Icon: FontAwesomeIcon.Running, Status: "Available");
}
public static (Vector4 color, FontAwesomeIcon icon) GetInstanceStyle(ushort instanceId)
{
if (UIState.IsInstanceContentCompleted(instanceId))
{
return (color: ImGuiColors.ParsedGreen, icon: FontAwesomeIcon.Check);
}
if (UIState.IsInstanceContentUnlocked(instanceId))
{
return (color: ImGuiColors.DalamudYellow, icon: FontAwesomeIcon.Running);
}
return (color: ImGuiColors.DalamudRed, icon: FontAwesomeIcon.Times);
}
public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon, float extraPadding = 0f)
{
if (extraPadding > 0f)
{
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + extraPadding);
}
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
ImGui.TextColored(in color, icon.ToIconString());
}
bool num = ImGui.IsItemHovered();
ImGui.SameLine();
if (extraPadding > 0f)
{
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + extraPadding);
}
ImGui.TextUnformatted(text);
return num | ImGui.IsItemHovered();
}
public bool ChecklistItem(string text, bool complete, Vector4? colorOverride = null)
{
return ChecklistItem(text, colorOverride ?? (complete ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed), complete ? FontAwesomeIcon.Check : FontAwesomeIcon.Times);
}
}