qstbak/Questionable/Questionable.Windows/FateSelectionWindow.cs
2026-08-17 20:29:32 +10:00

400 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
using Questionable.Windows.Utils;
namespace Questionable.Windows;
internal sealed class FateSelectionWindow : ThemedWindow
{
private readonly FateController _fateController;
private readonly FateDefinitionRegistry _fateDefinitionRegistry;
private readonly QuestController _questController;
private readonly SeasonalDutyController _seasonalDutyController;
private readonly CustomDeliveryController _customDeliveryController;
private readonly AttunementController _attunementController;
private readonly MovementController _movementController;
private readonly QuestFunctions _questFunctions;
private readonly QuestData _questData;
private readonly TerritoryData _territoryData;
private int _cycleLimit;
private string _searchText = string.Empty;
public FateSelectionWindow(FateController fateController, FateDefinitionRegistry fateDefinitionRegistry, QuestController questController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, MovementController movementController, QuestFunctions questFunctions, QuestData questData, TerritoryData territoryData)
: base("FATE Farming###QuestionableFateFarming")
{
_fateController = fateController;
_fateDefinitionRegistry = fateDefinitionRegistry;
_questController = questController;
_seasonalDutyController = seasonalDutyController;
_customDeliveryController = customDeliveryController;
_attunementController = attunementController;
_movementController = movementController;
_questFunctions = questFunctions;
_questData = questData;
_territoryData = territoryData;
base.Size = new Vector2(600f, 400f);
base.SizeCondition = ImGuiCond.FirstUseEver;
base.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(500f, 300f),
MaximumSize = new Vector2(900f, 700f)
};
}
public override void DrawContent()
{
if (_fateController.IsRunning)
{
if (CycleSelectionUi.DrawRunningBanner(BuildRunningStatusText(), UiThemeUtils.StatusActive, FontAwesomeIcon.Play))
{
_fateController.Stop("UI stop");
_movementController.Stop();
}
}
else
{
var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard();
UiThemeUtils.SectionHeader("Cycle Configuration");
DrawControlsStrip();
UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList);
}
if (_fateDefinitionRegistry.Definitions.Count == 0)
{
UiThemeUtils.EmptyState(FontAwesomeIcon.Star, "No FATE Definitions", "Add FATE definitions to the FatePaths folder to get started.");
return;
}
UiThemeUtils.SectionSpacing();
var (contentStartPos2, availableWidth2, drawList2) = UiThemeUtils.BeginCard();
UiThemeUtils.SectionHeader("FATE List");
UiThemeUtils.SearchInput("##FateSearch", ref _searchText, -13f, "Search FATEs...");
ImGui.Spacing();
DrawFateTable();
UiThemeUtils.EndCard(contentStartPos2, availableWidth2, drawList2);
}
private string BuildRunningStatusText()
{
string value = (_fateController.CycleLimit.HasValue ? $"Cycle {_fateController.CompletedCycles + 1} / {_fateController.CycleLimit}" : $"Cycle {_fateController.CompletedCycles + 1}");
string value2 = FormatElapsed(_fateController.ElapsedTime);
IList<string> remainingTaskNames = _fateController.GetRemainingTaskNames();
string text = ((remainingTaskNames.Count > 0) ? remainingTaskNames[0] : "");
string name = _fateController.CurrentFate.Name;
if (text.Length > 0)
{
return $"{name} · {value} · {value2} · {text}";
}
return $"{name} · {value} · {value2}";
}
private void DrawControlsStrip()
{
using (ImRaii.Disabled(_fateController.IsRunning))
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Cycles:");
ImGui.SameLine();
ImGui.SetNextItemWidth(100f * ImGui.GetIO().FontGlobalScale);
ImGui.InputInt("##CycleLimit", ref _cycleLimit, 1, 5);
if (_cycleLimit < 0)
{
_cycleLimit = 0;
}
ImGui.SameLine();
ImGui.TextColored(in UiThemeUtils.MutedTextColor, (_cycleLimit == 0) ? "(unlimited)" : "");
}
ImGui.Spacing();
}
private void DrawFateTable()
{
IEnumerable<FateDefinition> source = _fateDefinitionRegistry.Definitions.Values;
if (!string.IsNullOrWhiteSpace(_searchText))
{
source = source.Where((FateDefinition f) => f.Name.Contains(_searchText, StringComparison.OrdinalIgnoreCase) || (_territoryData.GetName(f.TerritoryId) ?? f.Description).Contains(_searchText, StringComparison.OrdinalIgnoreCase));
}
List<FateDefinition> list = source.OrderBy(delegate(FateDefinition f)
{
DateTime? eventExpiry = f.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
if (EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow > TimeSpan.Zero)
{
return 0;
}
}
return 1;
}).ThenBy(delegate(FateDefinition f)
{
DateTime? eventExpiry = f.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow;
if (timeSpan > TimeSpan.Zero)
{
return timeSpan.TotalSeconds;
}
}
return double.MaxValue;
}).ThenBy<FateDefinition, string>((FateDefinition f) => f.Name, StringComparer.OrdinalIgnoreCase)
.ToList();
if (list.Count == 0)
{
UiThemeUtils.EmptyState(FontAwesomeIcon.Search, "No FATEs match your search.");
return;
}
using ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable("FateTable", 4, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY);
if (!tableDisposable)
{
return;
}
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.None, 3f);
ImGui.TableSetupColumn("Zone", ImGuiTableColumnFlags.None, 2f);
ImGui.TableSetupColumn("Expiry", ImGuiTableColumnFlags.WidthFixed, 80f);
ImGui.TableSetupColumn("##Actions", ImGuiTableColumnFlags.WidthFixed, 30f);
ImGui.TableHeadersRow();
foreach (FateDefinition item in list)
{
ImGui.TableNextRow();
bool flag = _fateController.IsRunning && _fateController.CurrentFate == item;
if (flag)
{
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.ColorConvertFloat4ToU32(UiThemeUtils.AccentDimColor));
}
ImGui.TableNextColumn();
DrawFateRowName(item, flag);
ImGui.TableNextColumn();
ImGui.TextUnformatted(_territoryData.GetName(item.TerritoryId) ?? item.Description);
ImGui.TableNextColumn();
DrawFateRowExpiry(item);
ImGui.TableNextColumn();
bool disabled = _fateController.IsRunning || _seasonalDutyController.IsRunning || _customDeliveryController.IsRunning || _attunementController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual;
DrawFateRowActions(item, disabled);
}
}
private void DrawFateRowName(FateDefinition fate, bool isActive)
{
if (isActive)
{
ImGui.TextColored(in UiThemeUtils.AccentColor, fate.Name);
}
else
{
ImGui.TextUnformatted(fate.Name);
}
if (!ImGui.IsItemHovered())
{
return;
}
using (ImRaii.Tooltip())
{
ImGui.TextColored(in UiThemeUtils.PrimaryTextColor, fate.Name);
ImGui.Separator();
ImGui.PushTextWrapPos(300f);
ImGui.TextUnformatted(fate.Description);
ImGui.PopTextWrapPos();
ImGui.Spacing();
string nameAndId = _territoryData.GetNameAndId(fate.TerritoryId);
ImU8String text = new ImU8String(6, 1);
text.AppendLiteral("Zone: ");
text.AppendFormatted(nameAndId);
ImGui.TextColored(in UiThemeUtils.DimTextColor, text);
ImU8String text2 = new ImU8String(9, 1);
text2.AppendLiteral("Targets: ");
text2.AppendFormatted(fate.Targets.Count);
ImGui.TextColored(in UiThemeUtils.DimTextColor, text2);
if (fate.RequiredStatusId.HasValue)
{
ImGui.TextColored(in UiThemeUtils.StatusActive, "Requires transformation");
}
DateTime? eventExpiry = fate.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow;
if (timeSpan > TimeSpan.Zero)
{
string text3 = EventInfoComponent.FormatRemainingFull(timeSpan);
ImGui.TextColored(in UiThemeUtils.StatusActive, text3);
}
}
}
}
private static void DrawFateRowExpiry(FateDefinition fate)
{
DateTime? eventExpiry = fate.EventExpiry;
if (eventExpiry.HasValue)
{
DateTime valueOrDefault = eventExpiry.GetValueOrDefault();
TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow;
if (timeSpan > TimeSpan.Zero)
{
string text = EventInfoComponent.FormatRemainingDays(timeSpan);
ImGui.TextColored((timeSpan.TotalDays < 3.0) ? UiThemeUtils.StatusActive : UiThemeUtils.DimTextColor, text);
}
else
{
ImGui.TextColored(in UiThemeUtils.MutedTextColor, "Expired");
}
}
else
{
ImGui.TextColored(in UiThemeUtils.MutedTextColor, "--");
}
}
private void DrawFateRowActions(FateDefinition fate, bool disabled)
{
uint? num = fate.RequiredQuestId;
object obj;
if (num.HasValue)
{
uint valueOrDefault = num.GetValueOrDefault();
if (valueOrDefault <= 65535)
{
obj = new QuestId((ushort)valueOrDefault);
goto IL_004d;
}
}
obj = null;
goto IL_004d;
IL_004d:
QuestId questId = (QuestId)obj;
bool flag = questId != null && !_questFunctions.IsQuestComplete(questId);
ImU8String id = new ImU8String(5, 1);
id.AppendLiteral("fate_");
id.AppendFormatted(fate.Name);
using (ImRaii.PushId(id))
{
if (flag)
{
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextColored(in UiThemeUtils.StatusLocked, FontAwesomeIcon.Times.ToIconString());
}
if (ImGui.IsItemHovered())
{
IQuestInfo questInfo;
string value = (_questData.TryGetQuestInfo(new QuestId(fate.RequiredQuestId.Value), out questInfo) ? questInfo.Name : (num?.ToString(CultureInfo.InvariantCulture) ?? "Unknown"));
ImU8String tooltip = new ImU8String(33, 1);
tooltip.AppendLiteral("Requires \"");
tooltip.AppendFormatted(value);
tooltip.AppendLiteral("\" to be completed first");
ImGui.SetTooltip(tooltip);
}
return;
}
using (ImRaii.Disabled(disabled))
{
using (ImRaii.PushColor(ImGuiCol.Button, UiThemeUtils.PlayButtonColor))
{
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UiThemeUtils.PlayButtonHover))
{
using (ImRaii.PushColor(ImGuiCol.ButtonActive, UiThemeUtils.PlayButtonActive))
{
using (ImRaii.PushColor(ImGuiCol.Text, UiThemeUtils.PlayButtonText))
{
if (UiThemeUtils.IconButton(FontAwesomeIcon.Play, ImGui.GetFrameHeight()))
{
_movementController.Stop();
_questController.Stop("FATE farming start");
_seasonalDutyController.Stop("FATE farming start");
_customDeliveryController.Stop("FATE farming start");
_attunementController.Stop("FATE farming start");
int? cycleLimit = ((_cycleLimit > 0) ? new int?(_cycleLimit) : ((int?)null));
_fateController.Start(fate, cycleLimit);
}
}
}
}
}
}
if (disabled && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
if (_fateController.IsRunning)
{
ImGui.SetTooltip("Already farming a FATE");
}
else if (_seasonalDutyController.IsRunning)
{
ImGui.SetTooltip("Stop seasonal duty farming first");
}
else if (_customDeliveryController.IsRunning)
{
ImGui.SetTooltip("Stop custom delivery first");
}
else if (_attunementController.IsRunning)
{
ImGui.SetTooltip("Stop attunement first");
}
else
{
ImGui.SetTooltip("Stop quest automation first");
}
}
}
}
private static string FormatElapsed(TimeSpan elapsed)
{
IFormatProvider invariantCulture;
if (elapsed.TotalHours >= 1.0)
{
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider = invariantCulture;
DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(5, 3, invariantCulture);
handler.AppendFormatted((int)elapsed.TotalHours);
handler.AppendLiteral("h ");
handler.AppendFormatted(elapsed.Minutes, "D2");
handler.AppendLiteral("m ");
handler.AppendFormatted(elapsed.Seconds, "D2");
handler.AppendLiteral("s");
return string.Create(provider, ref handler);
}
if (elapsed.TotalMinutes >= 1.0)
{
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider2 = invariantCulture;
DefaultInterpolatedStringHandler handler2 = new DefaultInterpolatedStringHandler(3, 2, invariantCulture);
handler2.AppendFormatted((int)elapsed.TotalMinutes);
handler2.AppendLiteral("m ");
handler2.AppendFormatted(elapsed.Seconds, "D2");
handler2.AppendLiteral("s");
return string.Create(provider2, ref handler2);
}
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider3 = invariantCulture;
DefaultInterpolatedStringHandler handler3 = new DefaultInterpolatedStringHandler(1, 1, invariantCulture);
handler3.AppendFormatted(elapsed.Seconds);
handler3.AppendLiteral("s");
return string.Create(provider3, ref handler3);
}
}