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

267 lines
8.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows.JournalComponents;
internal sealed class QuestRewardComponent
{
private enum RowType : byte
{
Group,
Item
}
private readonly record struct FlatRow(RowType Type, int GroupIndex, ItemReward? Item);
private sealed record ItemGroup(string Label, EItemRewardType Type, List<ItemReward> Items);
private readonly QuestRegistry _questRegistry;
private readonly QuestData _questData;
private readonly QuestTooltipComponent _questTooltipComponent;
private readonly UiUtils _uiUtils;
private readonly IDalamudPluginInterface _pluginInterface;
private bool _showEventRewards;
private string _searchText = string.Empty;
private List<ItemGroup> _filteredGroups = new List<ItemGroup>();
private List<FlatRow> _flatRows = new List<FlatRow>();
private readonly HashSet<string> _expandedGroups = new HashSet<string>();
private bool _initialized;
private static readonly int CountPadWidth = 9999.ToString(CultureInfo.CurrentCulture).Length;
public QuestRewardComponent(QuestRegistry questRegistry, QuestData questData, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IDalamudPluginInterface pluginInterface)
{
_questRegistry = questRegistry;
_questData = questData;
_questTooltipComponent = questTooltipComponent;
_uiUtils = uiUtils;
_pluginInterface = pluginInterface;
}
public unsafe void DrawItemRewards()
{
if (!_initialized)
{
UpdateFilter();
_initialized = true;
}
float x = ImGui.GetStyle().ItemSpacing.X;
float num = ImGui.GetFrameHeight() + ImGui.CalcTextSize("Show seasonal event rewards").X + 3f * x;
if (UiThemeUtils.SearchInput("##RewardSearch", ref _searchText, 0f - num, "Search items..."))
{
UpdateFilter();
}
ImGui.SameLine();
if (UiThemeUtils.WrappedCheckbox("Show seasonal event rewards", ref _showEventRewards))
{
UpdateFilter();
}
ImGui.Spacing();
ImGui.TextColored(in UiThemeUtils.MutedTextColor, "Only untradeable items are listed (e.g. the Wind-up Airship can be sold on the market board).");
ImGui.Separator();
if (_flatRows.Count == 0)
{
UiThemeUtils.EmptyState(FontAwesomeIcon.Gift, "No items match your search.");
return;
}
float x2;
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
x2 = ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X;
}
float fontGlobalScale = ImGui.GetIO().FontGlobalScale;
using ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable("ItemRewards", 2, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.ScrollY);
if (!tableDisposable)
{
return;
}
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Obtained", ImGuiTableColumnFlags.WidthFixed, 120f * fontGlobalScale);
ImGui.TableHeadersRow();
float textLineHeightWithSpacing = ImGui.GetTextLineHeightWithSpacing();
bool flag = false;
ImGuiListClipper* intPtr = ImGuiNative.ImGuiListClipper();
ImGuiNative.Begin(intPtr, _flatRows.Count, textLineHeightWithSpacing);
ImGuiListClipperPtr imGuiListClipperPtr = intPtr;
try
{
while (imGuiListClipperPtr.Step())
{
for (int i = imGuiListClipperPtr.DisplayStart; i < imGuiListClipperPtr.DisplayEnd; i++)
{
if (DrawFlatRow(_flatRows[i], x2))
{
flag = true;
}
}
}
}
finally
{
imGuiListClipperPtr.Destroy();
}
if (flag)
{
RebuildFlatRows();
}
}
private bool DrawFlatRow(FlatRow row, float statusIconSpacing)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
if (row.Type == RowType.Group)
{
ItemGroup itemGroup = _filteredGroups[row.GroupIndex];
bool flag = _expandedGroups.Contains(itemGroup.Label);
UiThemeUtils.DrawTrackerGroupCaret(_pluginInterface.UiBuilder.IconFontFixedWidthHandle, flag);
ImGui.SameLine();
bool result = false;
ImU8String label = new ImU8String(5, 2);
label.AppendFormatted(itemGroup.Label);
label.AppendLiteral("##grp");
label.AppendFormatted(row.GroupIndex);
if (ImGui.Selectable(label))
{
if (flag)
{
_expandedGroups.Remove(itemGroup.Label);
}
else
{
_expandedGroups.Add(itemGroup.Label);
}
result = true;
}
int num = itemGroup.Items.Count((ItemReward x) => x.IsUnlocked());
int count = itemGroup.Items.Count;
ImGui.TableNextColumn();
UiThemeUtils.DrawTrackerCount(FormatCount(num, count), num == count && count > 0, count == 0);
return result;
}
ItemReward item = row.Item;
float indentSpacing = ImGui.GetStyle().IndentSpacing;
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + indentSpacing);
bool flag2 = item.IsUnlocked();
Vector4 color = ((!_questRegistry.IsKnownQuest(item.ElementId)) ? UiThemeUtils.StatusUnobtainable : (flag2 ? UiThemeUtils.StatusComplete : UiThemeUtils.StatusLocked));
IQuestInfo questInfo;
bool flag3 = _questData.TryGetQuestInfo(item.ElementId, out questInfo);
string text = item.Name;
if (questInfo is QuestInfo { IsSeasonalEvent: not false })
{
text = text + " " + SeIconChar.Clock.ToIconString();
}
ImGui.TreeNodeEx(text, ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.SpanFullWidth);
bool num2 = ImGui.IsItemHovered();
ImGui.TableNextColumn();
float num3 = ImGui.GetColumnWidth() / 2f - statusIconSpacing;
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + num3);
FontAwesomeIcon icon = (flag2 ? FontAwesomeIcon.Check : FontAwesomeIcon.Times);
if ((num2 | _uiUtils.ChecklistItem(string.Empty, color, icon)) && flag3)
{
using ImRaii.TooltipDisposable tooltipDisposable = ImRaii.Tooltip();
if (tooltipDisposable.Alive)
{
ImU8String text2 = new ImU8String(15, 1);
text2.AppendLiteral("Obtained from: ");
text2.AppendFormatted(questInfo.Name);
ImGui.Text(text2);
using (ImRaii.PushIndent())
{
_questTooltipComponent.DrawInner(questInfo, showItemRewards: false);
}
}
}
return false;
}
private static string FormatCount(int n, int d)
{
if (d != 0)
{
return n.ToString(CultureInfo.CurrentCulture).PadLeft(CountPadWidth) + " / " + d.ToString(CultureInfo.CurrentCulture).PadLeft(CountPadWidth);
}
return "-".PadLeft(CountPadWidth) + " / " + "-".PadLeft(CountPadWidth);
}
private void RebuildFlatRows()
{
List<FlatRow> list = new List<FlatRow>();
for (int i = 0; i < _filteredGroups.Count; i++)
{
ItemGroup itemGroup = _filteredGroups[i];
list.Add(new FlatRow(RowType.Group, i, null));
if (!_expandedGroups.Contains(itemGroup.Label))
{
continue;
}
foreach (ItemReward item in itemGroup.Items)
{
if (_questData.TryGetQuestInfo(item.ElementId, out IQuestInfo _))
{
list.Add(new FlatRow(RowType.Item, i, item));
}
}
}
_flatRows = list;
}
private void UpdateFilter()
{
List<ItemGroup> list = new List<ItemGroup>();
AddGroup(list, "Mounts", EItemRewardType.Mount);
AddGroup(list, "Minions", EItemRewardType.Minion);
AddGroup(list, "Orchestrion Rolls", EItemRewardType.OrchestrionRoll);
AddGroup(list, "Triple Triad Cards", EItemRewardType.TripleTriadCard);
AddGroup(list, "Fashion Accessories", EItemRewardType.FashionAccessory);
AddGroup(list, "Unlock Links", EItemRewardType.UnlockLink);
_filteredGroups = list;
RebuildFlatRows();
}
private void AddGroup(List<ItemGroup> groups, string label, EItemRewardType type)
{
List<ItemReward> filteredItems = GetFilteredItems(type);
if (filteredItems.Count > 0)
{
groups.Add(new ItemGroup(label, type, filteredItems));
}
}
private List<ItemReward> GetFilteredItems(EItemRewardType type)
{
return (from x in _questData.RedeemableItems
where x.Type == type
where x.IsUntradable
select x).Where(delegate(ItemReward x)
{
if (!_showEventRewards && _questData.TryGetQuestInfo(x.ElementId, out IQuestInfo questInfo) && questInfo is QuestInfo { IsSeasonalEvent: not false })
{
return false;
}
return (string.IsNullOrEmpty(_searchText) || x.Name.Contains(_searchText, StringComparison.OrdinalIgnoreCase)) ? true : false;
}).OrderBy<ItemReward, string>((ItemReward x) => x.Name, StringComparer.CurrentCultureIgnoreCase).ToList();
}
}