Refactoring, allow unique/untradeable items that can be purchased after completing quests to be discarded
This commit is contained in:
parent
5817f8c976
commit
ce37130390
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0-windows</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<Version>3.8</Version>
|
<Version>4.0</Version>
|
||||||
<LangVersion>11.0</LangVersion>
|
<LangVersion>11.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using ARDiscard.GameData;
|
using ARDiscard.GameData;
|
||||||
using ARDiscard.GameData.Agents;
|
|
||||||
using ARDiscard.Windows;
|
using ARDiscard.Windows;
|
||||||
using Dalamud.ContextMenu;
|
using Dalamud.ContextMenu;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
||||||
|
|
||||||
namespace ARDiscard;
|
namespace ARDiscard;
|
||||||
|
|
||||||
@ -52,18 +49,7 @@ internal sealed class ContextMenuIntegration : IDisposable
|
|||||||
if (_configuration.ContextMenu.OnlyWhenConfigIsOpen && !_configWindow.IsOpen)
|
if (_configuration.ContextMenu.OnlyWhenConfigIsOpen && !_configWindow.IsOpen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.ParentAddonName == "ArmouryBoard")
|
if (!(args.ParentAddonName is "Inventory" or "InventoryExpansion" or "InventoryLarge" or "ArmouryBoard"))
|
||||||
{
|
|
||||||
var agent = AgentModule.Instance()->GetAgentByInternalId(AgentId.ArmouryBoard);
|
|
||||||
if (agent == null || !agent->IsAgentActive())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// don't add it in the main/off hand weapon tabs, as we don't use these for discarding
|
|
||||||
var agentArmouryBoard = (AgentArmouryBoard*)agent;
|
|
||||||
if (agentArmouryBoard->CurrentTab is 0 or 6)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (!(args.ParentAddonName is "Inventory" or "InventoryExpansion" or "InventoryLarge"))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_configWindow.CanItemBeConfigured(args.ItemId))
|
if (!_configWindow.CanItemBeConfigured(args.ItemId))
|
||||||
@ -71,7 +57,8 @@ internal sealed class ContextMenuIntegration : IDisposable
|
|||||||
|
|
||||||
if (_configuration.DiscardingItems.Contains(args.ItemId))
|
if (_configuration.DiscardingItems.Contains(args.ItemId))
|
||||||
args.AddCustomItem(_removeItem);
|
args.AddCustomItem(_removeItem);
|
||||||
else if (!InternalConfiguration.BlacklistedItems.Contains(args.ItemId))
|
else if (_itemCache.TryGetItem(args.ItemId, out ItemCache.CachedItemInfo? cachedItemInfo) &&
|
||||||
|
cachedItemInfo.CanBeDiscarded())
|
||||||
args.AddCustomItem(_addItem);
|
args.AddCustomItem(_addItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using FFXIVClientStructs.Attributes;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
||||||
|
|
||||||
namespace ARDiscard.GameData.Agents;
|
|
||||||
|
|
||||||
[Agent(AgentId.ArmouryBoard)]
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x2E)]
|
|
||||||
public struct AgentArmouryBoard
|
|
||||||
{
|
|
||||||
[FieldOffset(0x2C)] public byte CurrentTab;
|
|
||||||
}
|
|
@ -42,6 +42,9 @@ internal static class InternalConfiguration
|
|||||||
9390, // Antique Breeches
|
9390, // Antique Breeches
|
||||||
9391, // Antique Sollerets
|
9391, // Antique Sollerets
|
||||||
|
|
||||||
|
6223, // Mended Imperial Pot Helm
|
||||||
|
6224, // Mended Imperial Short Robe
|
||||||
|
|
||||||
#region Fate drops used in tribal quests
|
#region Fate drops used in tribal quests
|
||||||
|
|
||||||
7001, // Flamefang Choker (Amalj'aa)
|
7001, // Flamefang Choker (Amalj'aa)
|
||||||
|
@ -35,6 +35,8 @@ internal sealed class InventoryUtils
|
|||||||
InventoryType.ArmoryRings
|
InventoryType.ArmoryRings
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly IReadOnlyList<uint> NoGearsetItems = new List<uint>();
|
||||||
|
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly ItemCache _itemCache;
|
private readonly ItemCache _itemCache;
|
||||||
private readonly IPluginLog _pluginLog;
|
private readonly IPluginLog _pluginLog;
|
||||||
@ -53,7 +55,7 @@ internal sealed class InventoryUtils
|
|||||||
|
|
||||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||||
foreach (InventoryType inventoryType in DefaultInventoryTypes)
|
foreach (InventoryType inventoryType in DefaultInventoryTypes)
|
||||||
toDiscard.AddRange(GetItemsToDiscard(inventoryManager, inventoryType, itemCounts, false, null));
|
toDiscard.AddRange(GetItemsToDiscard(inventoryManager, inventoryType, itemCounts, NoGearsetItems));
|
||||||
|
|
||||||
if (_configuration.Armoury.DiscardFromArmouryChest)
|
if (_configuration.Armoury.DiscardFromArmouryChest)
|
||||||
{
|
{
|
||||||
@ -62,14 +64,14 @@ internal sealed class InventoryUtils
|
|||||||
if (_configuration.Armoury.CheckLeftSideGear)
|
if (_configuration.Armoury.CheckLeftSideGear)
|
||||||
{
|
{
|
||||||
foreach (InventoryType inventoryType in LeftSideGearInventoryTypes)
|
foreach (InventoryType inventoryType in LeftSideGearInventoryTypes)
|
||||||
toDiscard.AddRange(GetItemsToDiscard(inventoryManager, inventoryType, itemCounts, true,
|
toDiscard.AddRange(GetItemsToDiscard(inventoryManager, inventoryType, itemCounts,
|
||||||
gearsetItems));
|
gearsetItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_configuration.Armoury.CheckRightSideGear)
|
if (_configuration.Armoury.CheckRightSideGear)
|
||||||
{
|
{
|
||||||
foreach (InventoryType inventoryType in RightSideGearInventoryTypes)
|
foreach (InventoryType inventoryType in RightSideGearInventoryTypes)
|
||||||
toDiscard.AddRange(GetItemsToDiscard(inventoryManager, inventoryType, itemCounts, true,
|
toDiscard.AddRange(GetItemsToDiscard(inventoryManager, inventoryType, itemCounts,
|
||||||
gearsetItems));
|
gearsetItems));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +90,7 @@ internal sealed class InventoryUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
private unsafe IReadOnlyList<ItemWrapper> GetItemsToDiscard(InventoryManager* inventoryManager,
|
private unsafe IReadOnlyList<ItemWrapper> GetItemsToDiscard(InventoryManager* inventoryManager,
|
||||||
InventoryType inventoryType, Dictionary<uint, uint> itemCounts, bool doGearChecks,
|
InventoryType inventoryType, Dictionary<uint, uint> itemCounts,
|
||||||
IReadOnlyList<uint>? gearsetItems)
|
IReadOnlyList<uint>? gearsetItems)
|
||||||
{
|
{
|
||||||
List<ItemWrapper> toDiscard = new List<ItemWrapper>();
|
List<ItemWrapper> toDiscard = new List<ItemWrapper>();
|
||||||
@ -107,18 +109,16 @@ internal sealed class InventoryUtils
|
|||||||
if (InternalConfiguration.BlacklistedItems.Contains(item->ItemID))
|
if (InternalConfiguration.BlacklistedItems.Contains(item->ItemID))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (doGearChecks)
|
if (!_itemCache.TryGetItem(item->ItemID, out ItemCache.CachedItemInfo? itemInfo) || !itemInfo.CanBeDiscarded())
|
||||||
{
|
continue; // no info, who knows what that item is
|
||||||
if (gearsetItems == null || gearsetItems.Contains(item->ItemID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ItemCache.CachedItemInfo? itemInfo = _itemCache.GetItem(item->ItemID);
|
// skip gear if we're unable to load gearsets or it is used in a gearset
|
||||||
if (itemInfo == null)
|
if (itemInfo.EquipSlotCategory > 0 && (gearsetItems == null || gearsetItems.Contains(item->ItemID)))
|
||||||
continue; // no info, who knows what that item is
|
continue;
|
||||||
|
|
||||||
if (itemInfo.ILvl >= _configuration.Armoury.MaximumGearItemLevel)
|
if (itemInfo is { EquipSlotCategory: > 0, CanBeBoughtFromCalamitySalvager: false } &&
|
||||||
continue;
|
itemInfo.ILvl >= _configuration.Armoury.MaximumGearItemLevel)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
//PluginLog.Verbose($"{i} → {item->ItemID}");
|
//PluginLog.Verbose($"{i} → {item->ItemID}");
|
||||||
if (_configuration.DiscardingItems.Contains(item->ItemID))
|
if (_configuration.DiscardingItems.Contains(item->ItemID))
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
namespace ARDiscard.GameData;
|
namespace ARDiscard.GameData;
|
||||||
@ -28,12 +31,45 @@ internal sealed class ItemCache
|
|||||||
Level = item.LevelEquip,
|
Level = item.LevelEquip,
|
||||||
UiCategory = item.ItemUICategory.Row,
|
UiCategory = item.ItemUICategory.Row,
|
||||||
UiCategoryName = item.ItemUICategory.Value!.Name.ToString(),
|
UiCategoryName = item.ItemUICategory.Value!.Name.ToString(),
|
||||||
|
EquipSlotCategory = item.EquipSlotCategory.Row,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var shopItem in dataManager.GetExcelSheet<GilShopItem>()!)
|
||||||
|
{
|
||||||
|
// exclude base ARR relics, not strictly necessary since we don't allow discarding weapons anyway
|
||||||
|
if (shopItem.Item.Value!.Rarity == 4)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// the item can be discarded already
|
||||||
|
if (!_items.TryGetValue(shopItem.Item.Row, out CachedItemInfo? cachedItemInfo) ||
|
||||||
|
cachedItemInfo.CanBeDiscarded())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (shopItem.AchievementRequired.Row != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// has a quest required to unlock from the shop
|
||||||
|
if (!shopItem.QuestRequired.Any(CanDiscardItemsFromQuest))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cachedItemInfo.CanBeBoughtFromCalamitySalvager = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanDiscardItemsFromQuest(LazyRow<Quest> quest)
|
||||||
|
{
|
||||||
|
return quest.Row > 0 &&
|
||||||
|
quest.Value?.JournalGenre.Value?.JournalCategory.Value?.JournalSection
|
||||||
|
.Row is 0 or 1 or 6; // pre-EW MSQ, EW MSQ or Job/Class quest
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<CachedItemInfo> AllItems => _items.Values;
|
public IEnumerable<CachedItemInfo> AllItems => _items.Values;
|
||||||
|
|
||||||
|
|
||||||
|
public bool TryGetItem(uint itemId, [NotNullWhen(true)] out CachedItemInfo? item)
|
||||||
|
=> _items.TryGetValue(itemId, out item);
|
||||||
|
|
||||||
public CachedItemInfo? GetItem(uint itemId)
|
public CachedItemInfo? GetItem(uint itemId)
|
||||||
{
|
{
|
||||||
if (_items.TryGetValue(itemId, out var item))
|
if (_items.TryGetValue(itemId, out var item))
|
||||||
@ -71,7 +107,28 @@ internal sealed class ItemCache
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public required bool IsIndisposable { get; init; }
|
public required bool IsIndisposable { get; init; }
|
||||||
|
|
||||||
|
public bool CanBeBoughtFromCalamitySalvager { get; set; }
|
||||||
|
|
||||||
public required uint UiCategory { get; init; }
|
public required uint UiCategory { get; init; }
|
||||||
public required string UiCategoryName { get; init; }
|
public required string UiCategoryName { get; init; }
|
||||||
|
public required uint EquipSlotCategory { get; init; }
|
||||||
|
|
||||||
|
public bool CanBeDiscarded()
|
||||||
|
{
|
||||||
|
if (InternalConfiguration.BlacklistedItems.Contains(ItemId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (UiCategory is UiCategories.Currency or UiCategories.Crystals or UiCategories.Unobtainable)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (EquipSlotCategory is 1 or 2 or 13 or 14)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (InternalConfiguration.WhitelistedItems.Contains(ItemId))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return CanBeBoughtFromCalamitySalvager ||
|
||||||
|
this is { IsUnique: false, IsUntradable: false, IsIndisposable: false };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,11 +380,7 @@ internal sealed class ConfigWindow : LImGui.LWindow
|
|||||||
if (_allItems == null)
|
if (_allItems == null)
|
||||||
{
|
{
|
||||||
_allItems = _itemCache.AllItems
|
_allItems = _itemCache.AllItems
|
||||||
.Where(x => !InternalConfiguration.BlacklistedItems.Contains(x.ItemId))
|
.Where(x => x.CanBeDiscarded())
|
||||||
.Where(x => InternalConfiguration.WhitelistedItems.Contains(x.ItemId) ||
|
|
||||||
x is { IsUnique: false, IsUntradable: false, IsIndisposable: false })
|
|
||||||
.Where(x => x.UiCategory != UiCategories.Currency && x.UiCategory != UiCategories.Crystals &&
|
|
||||||
x.UiCategory != UiCategories.Unobtainable)
|
|
||||||
.Select(x => (x.ItemId, x.Name.ToString()))
|
.Select(x => (x.ItemId, x.Name.ToString()))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user