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.Functions; using Questionable.Model; using Questionable.Model.Questing; using Questionable.Windows.QuestComponents; namespace Questionable.Windows; internal sealed class SeasonalDutySelectionWindow : LWindow { private readonly SeasonalDutyController _seasonalDutyController; private readonly SeasonalDutyDefinitionRegistry _seasonalDutyDefinitionRegistry; private readonly QuestController _questController; private readonly FateController _fateController; private readonly MovementController _movementController; private readonly QuestFunctions _questFunctions; private readonly QuestData _questData; private readonly TerritoryData _territoryData; private int _cycleLimit; public SeasonalDutySelectionWindow(SeasonalDutyController seasonalDutyController, SeasonalDutyDefinitionRegistry seasonalDutyDefinitionRegistry, QuestController questController, FateController fateController, MovementController movementController, QuestFunctions questFunctions, QuestData questData, TerritoryData territoryData) : base("Seasonal Duty Farming###QuestionableSeasonalDutyFarming") { _seasonalDutyController = seasonalDutyController; _seasonalDutyDefinitionRegistry = seasonalDutyDefinitionRegistry; _questController = questController; _fateController = fateController; _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 (_seasonalDutyController.IsRunning) { DrawRunningHeader(); } else { DrawControlsStrip(); } if (_seasonalDutyDefinitionRegistry.Definitions.Count == 0) { DrawEmptyState(); } else { DrawDutyTable(); } } 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.18f, 0.26f, 0.2f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.14f, 0.22f, 0.16f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.14f, 0.22f, 0.16f, 0.85f)), ImGui.ColorConvertFloat4ToU32(new Vector4(0.18f, 0.26f, 0.2f, 0.85f))); windowDrawList.AddLine(cursorScreenPos + new Vector2(0f, num), cursorScreenPos + new Vector2(x, num), ImGui.ColorConvertFloat4ToU32(new Vector4(0.3f, 0.8f, 0.4f, 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(0f, 1f * num3, 0.3f * num3, 1f); using (ImRaii.PushFont(UiBuilder.IconFont)) { ImGui.TextColored(in col, FontAwesomeIcon.Leaf.ToIconString()); } ImGui.SameLine(); ImGui.TextColored(ImGuiColors.ParsedGreen, _seasonalDutyController.CurrentDuty.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)) { _seasonalDutyController.Stop("UI stop"); _movementController.Stop(); } } } } ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 8f); string value = (_seasonalDutyController.CycleLimit.HasValue ? $"Cycle {_seasonalDutyController.CompletedCycles + 1} / {_seasonalDutyController.CycleLimit}" : $"Cycle {_seasonalDutyController.CompletedCycles + 1}"); string value2 = FormatElapsed(_seasonalDutyController.ElapsedTime); IList remainingTaskNames = _seasonalDutyController.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(_seasonalDutyController.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.3f, 0.65f, 0.4f, 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 Seasonal Duty 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 definitions to the SeasonalDutyPaths 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 DrawDutyTable() { List list = _seasonalDutyDefinitionRegistry.Definitions.Values.OrderBy(delegate(SeasonalDutyDefinition d) { DateTime? eventExpiry = d.EventExpiry; if (eventExpiry.HasValue) { DateTime valueOrDefault = eventExpiry.GetValueOrDefault(); if (EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow > TimeSpan.Zero) { return 0; } } return 1; }).ThenBy(delegate(SeasonalDutyDefinition d) { DateTime? eventExpiry = d.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((SeasonalDutyDefinition d) => d.Name, StringComparer.OrdinalIgnoreCase) .ToList(); using ImRaii.IEndObject endObject = ImRaii.Table("SeasonalDutyTable", 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 (SeasonalDutyDefinition item in list) { ImGui.TableNextRow(); bool flag = _seasonalDutyController.IsRunning && _seasonalDutyController.CurrentDuty == 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(0f, 0.85f, 0.3f, w))); } ImGui.TableNextColumn(); DrawDutyRowName(item, flag); ImGui.TableNextColumn(); ImGui.TextUnformatted(_territoryData.GetName(item.TerritoryId) ?? item.Name); ImGui.TableNextColumn(); DrawDutyRowExpiry(item); ImGui.TableNextColumn(); bool disabled = _seasonalDutyController.IsRunning || _fateController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual; DrawDutyRowActions(item, disabled); } } private void DrawDutyRowName(SeasonalDutyDefinition duty, bool isActive) { if (isActive) { float num = (float)ImGui.GetTime(); float num2 = 0.85f + MathF.Sin(num * 3f) * 0.15f; ImGui.TextColored(new Vector4(0f, 1f * num2, 0.3f * num2, 1f), duty.Name); } else { ImGui.TextUnformatted(duty.Name); } if (!ImGui.IsItemHovered()) { return; } using (ImRaii.Tooltip()) { ImGui.TextColored(ImGuiColors.DalamudWhite, duty.Name); ImGui.Separator(); ImGui.Spacing(); string nameAndId = _territoryData.GetNameAndId(duty.TerritoryId); Vector4 col = ImGuiColors.DalamudGrey; ImU8String text = new ImU8String(6, 1); text.AppendLiteral("Zone: "); text.AppendFormatted(nameAndId); ImGui.TextColored(in col, text); DateTime? eventExpiry = duty.EventExpiry; if (eventExpiry.HasValue) { DateTime valueOrDefault = eventExpiry.GetValueOrDefault(); TimeSpan timeSpan = EventInfoComponent.NormalizeExpiry(valueOrDefault) - DateTime.UtcNow; if (timeSpan > TimeSpan.Zero) { string text2 = EventInfoComponent.FormatRemainingFull(timeSpan); ImGui.TextColored(ImGuiColors.DalamudOrange, text2); } } } } private static void DrawDutyRowExpiry(SeasonalDutyDefinition duty) { DateTime? eventExpiry = duty.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 DrawDutyRowActions(SeasonalDutyDefinition duty, bool disabled) { bool flag = duty.RequiredQuestId.HasValue && !_questFunctions.IsQuestComplete(new QuestId(duty.RequiredQuestId.Value)); ImU8String id = new ImU8String(5, 1); id.AppendLiteral("duty_"); id.AppendFormatted(duty.Name); using (ImRaii.PushId(id)) { if (flag) { using (ImRaii.PushFont(UiBuilder.IconFont)) { ImGui.TextColored(ImGuiColors.DalamudRed, FontAwesomeIcon.Times.ToIconString()); } if (ImGui.IsItemHovered()) { IQuestInfo questInfo; string value = (_questData.TryGetQuestInfo(new QuestId(duty.RequiredQuestId.Value), out questInfo) ? questInfo.Name : duty.RequiredQuestId.Value.ToString(CultureInfo.InvariantCulture)); 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)) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { _questController.Stop("Seasonal duty farming start"); _fateController.Stop("Seasonal duty farming start"); _movementController.Stop(); int? cycleLimit = ((_cycleLimit > 0) ? new int?(_cycleLimit) : ((int?)null)); _seasonalDutyController.Start(duty, cycleLimit); } } if (disabled && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { if (_seasonalDutyController.IsRunning) { ImGui.SetTooltip("Already farming a seasonal duty"); } else if (_fateController.IsRunning) { ImGui.SetTooltip("Stop FATE farming 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); } }