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.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using LLib.ImGui; using Questionable.Controller; using Questionable.Data; using Questionable.Model.Questing; using Questionable.Windows.QuestComponents; namespace Questionable.Windows; internal sealed class FateSelectionWindow : LWindow { private readonly FateController _fateController; private readonly FateDefinitionRegistry _fateDefinitionRegistry; private readonly QuestController _questController; private readonly MovementController _movementController; private readonly TerritoryData _territoryData; private int _cycleLimit; public FateSelectionWindow(FateController fateController, FateDefinitionRegistry fateDefinitionRegistry, QuestController questController, MovementController movementController, TerritoryData territoryData) : base("FATE Farming###QuestionableFateFarming") { _fateController = fateController; _fateDefinitionRegistry = fateDefinitionRegistry; _questController = questController; _movementController = movementController; _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) { DrawRunningHeader(); } else { DrawControlsStrip(); } if (_fateDefinitionRegistry.Definitions.Count == 0) { DrawEmptyState(); } else { DrawFateTable(); } } private void DrawRunningHeader() { Vector2 cursorScreenPos = ImGui.GetCursorScreenPos(); float x = ImGui.GetContentRegionAvail().X; float num = ImGui.GetTextLineHeightWithSpacing() * 3f + ImGui.GetStyle().ItemSpacing.Y * 3f; ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList(); windowDrawList.AddRectFilledMultiColor(cursorScreenPos, cursorScreenPos + new Vector2(x, num), ImGui.ColorConvertFloat4ToU32(new Vector4(0.22f, 0.2f, 0.28f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.18f, 0.16f, 0.24f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.18f, 0.16f, 0.24f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.22f, 0.2f, 0.28f, 0.85f))); windowDrawList.AddLine(cursorScreenPos + new Vector2(0f, num), cursorScreenPos + new Vector2(x, num), ImGui.ColorConvertFloat4ToU32(new Vector4(0.7f, 0.6f, 0.9f, 0.5f)), 2f); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetStyle().ItemSpacing.Y); ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f); float num2 = (float)ImGui.GetTime(); float num3 = 0.85f + MathF.Sin(num2 * 3f) * 0.15f; Vector4 col = new Vector4(1f * num3, 0.85f * num3, 0f, 1f); using (ImRaii.PushFont(UiBuilder.IconFont)) { ImGui.TextColored(in col, FontAwesomeIcon.Star.ToIconString()); } ImGui.SameLine(); ImGui.TextColored(ImGuiColors.ParsedGold, _fateController.CurrentFate.Name); ImGui.SameLine(x - 35f); using (ImRaii.PushColor(ImGuiCol.Button, Vector4.Zero)) { using (ImRaii.PushColor(ImGuiCol.ButtonHovered, new Vector4(0.8f, 0.3f, 0.3f, 0.4f))) { using (ImRaii.PushColor(ImGuiCol.ButtonActive, new Vector4(0.9f, 0.2f, 0.2f, 0.6f))) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) { _fateController.Stop("UI stop"); _movementController.Stop(); } } } } ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f); string value = (_fateController.CycleLimit.HasValue ? $"Cycle {_fateController.CompletedCycles + 1} / {_fateController.CycleLimit}" : $"Cycle {_fateController.CompletedCycles + 1}"); string value2 = FormatElapsed(_fateController.ElapsedTime); IList remainingTaskNames = _fateController.GetRemainingTaskNames(); string text = ((remainingTaskNames.Count > 0) ? remainingTaskNames.First() : ""); if (text.Length > 0) { Vector4 col2 = ImGuiColors.DalamudGrey3; ImU8String text2 = new ImU8String(10, 3); text2.AppendFormatted(value); text2.AppendLiteral(" · "); text2.AppendFormatted(value2); text2.AppendLiteral(" · "); text2.AppendFormatted(text); ImGui.TextColored(in col2, text2); } else { Vector4 col2 = ImGuiColors.DalamudGrey3; ImU8String text3 = new ImU8String(5, 2); text3.AppendFormatted(value); text3.AppendLiteral(" · "); text3.AppendFormatted(value2); ImGui.TextColored(in col2, text3); } ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f); using (ImRaii.Disabled(disabled: true)) { ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Cycles:"); ImGui.SameLine(); ImGui.SetNextItemWidth(100f); ImGui.InputInt("##CycleLimitRunning", ref _cycleLimit, 1, 5); } ImGui.SetCursorPosY(cursorScreenPos.Y + num - ImGui.GetCursorScreenPos().Y + ImGui.GetCursorPosY() + 4f); ImGui.Spacing(); } private void DrawControlsStrip() { using (ImRaii.Disabled(_fateController.IsRunning)) { ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Cycles:"); ImGui.SameLine(); ImGui.SetNextItemWidth(100f); ImGui.InputInt("##CycleLimit", ref _cycleLimit, 1, 5); if (_cycleLimit < 0) { _cycleLimit = 0; } ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudGrey, (_cycleLimit == 0) ? "(unlimited)" : ""); } ImGui.Spacing(); } private static void DrawEmptyState() { float y = ImGui.GetContentRegionAvail().Y; float x = ImGui.GetContentRegionAvail().X; ImGui.SetCursorPosY(ImGui.GetCursorPosY() + y * 0.25f); Vector4 col = new Vector4(0.6f, 0.5f, 0.8f, 0.7f); Vector2 cursorScreenPos = ImGui.GetCursorScreenPos(); ImGui.PushFont(UiBuilder.IconFont); string text = FontAwesomeIcon.Search.ToIconString(); Vector2 vector = ImGui.CalcTextSize(text); vector *= 2.5f; ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList(); Vector2 vector2 = new Vector2(x * 0.5f, 0f) + cursorScreenPos + new Vector2(0f, vector.Y * 0.5f); for (int num = 3; num > 0; num--) { Vector4 col2 = new Vector4(col.X, col.Y, col.Z, col.W * 0.1f * (float)num); ImGui.SetCursorScreenPos(vector2 - vector * 0.5f - new Vector2(num * 2, num * 2)); ImGui.TextColored(in col2, text); } ImGui.SetCursorScreenPos(vector2 - vector * 0.5f); ImGui.TextColored(in col, text); ImGui.PopFont(); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + vector.Y * 0.5f + 20f); string text2 = "No FATE Definitions"; Vector2 vector3 = ImGui.CalcTextSize(text2); Vector2 vector4 = new Vector2((x - vector3.X) * 0.5f, 0f) + ImGui.GetCursorScreenPos(); windowDrawList.AddText(vector4 + new Vector2(1f, 1f), ImGui.ColorConvertFloat4ToU32(new Vector4(0f, 0f, 0f, 0.5f)), text2); ImGui.SetCursorPosX((x - vector3.X) * 0.5f); ImGui.TextColored(new Vector4(0.95f, 0.95f, 1f, 1f), text2); ImGui.Spacing(); ImGui.Spacing(); string text3 = "Add FATE definitions to the FatePaths folder to get started."; ImGui.SetCursorPosX((x - ImGui.CalcTextSize(text3).X) * 0.5f); ImGui.TextColored(new Vector4(0.7f, 0.7f, 0.8f, 1f), text3); } private void DrawFateTable() { List list = _fateDefinitionRegistry.Definitions.Values.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 f) => f.Name, StringComparer.OrdinalIgnoreCase) .ToList(); using ImRaii.IEndObject endObject = ImRaii.Table("FateTable", 4, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerH | ImGuiTableFlags.ScrollY); if (!endObject) { 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) { float num = (float)ImGui.GetTime(); float w = 0.1f + MathF.Sin(num * 2.5f) * 0.05f; ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, ImGui.GetColorU32(new Vector4(1f, 0.85f, 0f, w))); } 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 || _questController.AutomationType != QuestController.EAutomationType.Manual; DrawFateRowActions(item, disabled); } } private void DrawFateRowName(FateDefinition fate, bool isActive) { if (isActive) { float num = (float)ImGui.GetTime(); float num2 = 0.85f + MathF.Sin(num * 3f) * 0.15f; ImGui.TextColored(new Vector4(1f * num2, 0.85f * num2, 0f, 1f), fate.Name); } else { ImGui.TextUnformatted(fate.Name); } if (!ImGui.IsItemHovered()) { return; } using (ImRaii.Tooltip()) { ImGui.TextColored(ImGuiColors.DalamudWhite, fate.Name); ImGui.Separator(); ImGui.PushTextWrapPos(300f); ImGui.TextUnformatted(fate.Description); ImGui.PopTextWrapPos(); ImGui.Spacing(); string nameAndId = _territoryData.GetNameAndId(fate.TerritoryId); Vector4 col = ImGuiColors.DalamudGrey; ImU8String text = new ImU8String(6, 1); text.AppendLiteral("Zone: "); text.AppendFormatted(nameAndId); ImGui.TextColored(in col, text); col = ImGuiColors.DalamudGrey; ImU8String text2 = new ImU8String(9, 1); text2.AppendLiteral("Targets: "); text2.AppendFormatted(fate.Targets.Count); ImGui.TextColored(in col, text2); if (fate.RequiredStatusId.HasValue) { ImGui.TextColored(ImGuiColors.DalamudYellow, "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(ImGuiColors.DalamudOrange, 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) ? ImGuiColors.DalamudOrange : ImGuiColors.DalamudGrey, text); } else { ImGui.TextColored(ImGuiColors.DalamudGrey3, "Expired"); } } else { ImGui.TextColored(ImGuiColors.DalamudGrey3, "--"); } } private void DrawFateRowActions(FateDefinition fate, bool disabled) { using (ImRaii.Disabled(disabled)) { ImU8String id = new ImU8String(5, 1); id.AppendLiteral("fate_"); id.AppendFormatted(fate.Name); using (ImRaii.PushId(id)) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { _questController.Stop("FATE farming start"); _movementController.Stop(); 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 { 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); } }