Skip to content

Commit

Permalink
- Items that can be crossbred will now show as a source/use
Browse files Browse the repository at this point in the history
 - Added basic shop highlighting(only gil shops so far)
 - The required column now has a icon that when hovered will give you a breakdown of that particular item in relation to the craft(required, missing, etc)
 - Add to curated list context menu feature
 - New installs now come with a housing list
 - The inventory data now saves on it's own thread to stop hitching when a user has a lot of items

 - Exterior house storage should now be scanned properly
 - Items that are considered currency will no longer be grouped as currency if they are to be purchased
 - Fixed some bugs related to how craft numbers are calculated
 - Fixed a few imgui asserts
 - The can be equipped filter and favourites should work again
 - The monster drop tooltip was showing a monster instead the map
 - NPCs with no shop locations will now longer show up in the action menu in the craft overlay
 - Window positions should save properly(they weren't saving if the window was open)
 - Grand company shops had the wrong seal counts
 - Spectacles will now be considered acquirable and will show in the acquired tooltip
 - Added missing gathering points
 - When crafting an item that relied on inspection, the subcrafts were not being generated properly
  • Loading branch information
Critical-Impact committed Jan 14, 2025
1 parent 6ba0539 commit afc7ec9
Show file tree
Hide file tree
Showing 39 changed files with 1,418 additions and 297 deletions.
10 changes: 10 additions & 0 deletions InventoryTools/Enums/ShopType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace InventoryTools.Enums;

public enum ShopType
{
Gil,
SpecialShop,
Collectable,
InclusionShop,
FreeCompanyShop,
}
189 changes: 189 additions & 0 deletions InventoryTools/Highlighting/ShopHighlighting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;

namespace InventoryTools.Highlighting;

public class ShopHighlighting : IDisposable
{
private readonly IGameGui gameGui;
private readonly IAddonLifecycle addonLifecycle;
private readonly IPluginLog pluginLog;
private uint shopItemsAtkIndex = 441;
private uint shopCountAtkIndex = 2;
private HashSet<uint> highlightedItems = new HashSet<uint>();
private Dictionary<int, uint>? itemIndexMap = null;

public ShopHighlighting(IGameGui gameGui, IAddonLifecycle addonLifecycle, IPluginLog pluginLog)
{
this.gameGui = gameGui;
this.addonLifecycle = addonLifecycle;
this.pluginLog = pluginLog;
addonLifecycle.RegisterListener(AddonEvent.PostSetup, "Shop", AddonSetup);
addonLifecycle.RegisterListener(AddonEvent.PostDraw, "Shop", AddonPostDraw);
}

public void AddItem(uint itemId)
{
highlightedItems.Add(itemId);
}

public void RemoveItem(uint itemId)
{
highlightedItems.Remove(itemId);
}

public void SetItems(List<uint> items)
{
highlightedItems = [..items];
}

public void SetItems(HashSet<uint> items)
{
highlightedItems = items;
}

public void ClearItems()
{
highlightedItems.Clear();
}

private string itemIdString = "";
private uint itemId;

public unsafe void DrawDebug()
{
var addon = gameGui.GetAddonByName("Shop");
if (addon != IntPtr.Zero)
{
var atkUnitBase = (AtkUnitBase*)addon;
var atkComponentBase = atkUnitBase->GetComponentByNodeId(16);
if (atkComponentBase != null)
{
var listNode = (AtkComponentList*)atkComponentBase;
var listItemIndex = listNode->ItemRendererList->AtkComponentListItemRenderer->ListItemIndex;
ImGui.TextUnformatted($"List Item Index: {listItemIndex}");
if (itemIndexMap != null)
{
foreach (var item in itemIndexMap)
{
ImGui.TextUnformatted(item.Key + ": " + item.Value);
}
}

if (ImGui.InputText("Item", ref itemIdString, 128))
{
if (uint.TryParse(itemIdString, out itemId))
{
itemId = itemId;
}
itemIdString = itemId.ToString();
}
if (ImGui.Button("Add Item"))
{
if (uint.TryParse(itemIdString, out itemId))
{
highlightedItems.Add(itemId);
}
}
if (ImGui.Button("Remove Item"))
{
if (uint.TryParse(itemIdString, out itemId))
{
highlightedItems.Remove(itemId);
}
}
}
}
}


private unsafe void AddonPostDraw(AddonEvent type, AddonArgs args)
{
if (args.Addon != IntPtr.Zero)
{
var atkUnitBase = (AtkUnitBase*)args.Addon;
var atkComponentBase = atkUnitBase->GetComponentByNodeId(16);
if (atkComponentBase != null)
{
var listNode = (AtkComponentList*)atkComponentBase;
if (this.itemIndexMap == null)
{
CalculateItemIndexMap(atkUnitBase);
}

for (int i = 0; i < listNode->ListLength; i++)
{
if (!highlightedItems.Any())
{
listNode->SetItemDisabledState(i, false);
listNode->SetItemHighlightedState(i, false);
}
else if (itemIndexMap!.ContainsKey(i))
{
if (highlightedItems.Contains(itemIndexMap[i]))
{
if (!listNode->GetItemHighlightedState(i))
{
listNode->SetItemHighlightedState(i, true);
}
if (listNode->GetItemDisabledState(i))
{
listNode->SetItemDisabledState(i, false);
}
}
else
{
if (!listNode->GetItemDisabledState(i))
{
listNode->SetItemDisabledState(i, true);
}
if (listNode->GetItemHighlightedState(i))
{
listNode->SetItemHighlightedState(i, false);
}
}
}
}
}
}
}

private unsafe void CalculateItemIndexMap(AtkUnitBase* atkUnitBase)
{
var itemIndexMap = new Dictionary<int, uint>();
var shopLength = atkUnitBase->AtkValues[shopCountAtkIndex].UInt;
for (var i = shopItemsAtkIndex; i < shopItemsAtkIndex + shopLength; i++)
{
var atkValue = atkUnitBase->AtkValues[i];
if (atkValue.Type != ValueType.UInt)
{
break;
}

itemIndexMap[(int)(i - shopItemsAtkIndex)] = atkValue.UInt;
}
this.itemIndexMap = itemIndexMap;
}

private unsafe void AddonSetup(AddonEvent type, AddonArgs args)
{
if (args.Addon != IntPtr.Zero)
{
var atkUnitBase = (AtkUnitBase*)args.Addon;
CalculateItemIndexMap(atkUnitBase);
}
}

public void Dispose()
{
addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "Shop", AddonSetup);
addonLifecycle.UnregisterListener(AddonEvent.PostDraw, "Shop", AddonPostDraw);
}
}
3 changes: 1 addition & 2 deletions InventoryTools/InventoryTools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Autofac" Version="8.0.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="DalaMock.Host" Version="2.1.5" />
<PackageReference Include="DalaMock.Host" Version="2.1.6" />
<PackageReference Include="Humanizer.Core" Version="3.0.0-beta.54" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.11" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.11" />
Expand Down Expand Up @@ -155,7 +155,6 @@
</ItemGroup>

<ItemGroup>
<Folder Include="Enums\" />
<Folder Include="Logic\Columns\Tools\" />
</ItemGroup>
</Project>
5 changes: 4 additions & 1 deletion InventoryTools/InventoryToolsPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using InventoryTools.Commands;
using InventoryTools.Highlighting;
using InventoryTools.Host;
using InventoryTools.Hotkeys;
using InventoryTools.IPC;
Expand Down Expand Up @@ -122,7 +123,6 @@ public InventoryToolsPlugin(IDalamudPluginInterface pluginInterface, IPluginLog
typeof(IPCService),
typeof(HostedCraftMonitor),
typeof(ItemSearchService),

};

public List<Type> GetHostedServices()
Expand Down Expand Up @@ -288,6 +288,7 @@ public override void PreBuild(IHostBuilder hostBuilder)
builder.RegisterType<AtkRetainerLarge>().As<IAtkOverlay>().As<AtkRetainerLarge>();
builder.RegisterType<AtkRetainerList>().As<IAtkOverlay>().As<AtkRetainerList>();
builder.RegisterType<AtkSelectIconString>().As<IAtkOverlay>().As<AtkSelectIconString>();
builder.RegisterType<AtkShop>().As<IAtkOverlay>().As<AtkShop>();
});

//Hosted service registrations
Expand Down Expand Up @@ -336,6 +337,8 @@ public override void PreBuild(IHostBuilder hostBuilder)
builder.RegisterType<ExcelCache>().SingleInstance();
builder.RegisterType<CraftingCache>().SingleInstance();
builder.RegisterType<ItemInfoRenderService>().SingleInstance();
builder.RegisterType<ShopHighlighting>().SingleInstance();
builder.RegisterType<ShopTrackerService>().SingleInstance();
builder.Register<GameData>(c => c.Resolve<IDataManager>().GameData).SingleInstance().ExternallyOwned();
builder.RegisterGameSheetManager(new SheetManagerStartupOptions()
{
Expand Down
1 change: 1 addition & 0 deletions InventoryTools/Lists/ListFilterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ private List<SearchResult> GenerateFilterResult(FilterConfiguration filter, List
var displayDestinationCrossCharacter = filter.DestinationIncludeCrossCharacter ?? _configuration.DisplayCrossCharacter;

Logger.LogTrace("Filter Information:");
Logger.LogTrace("Filter Name:" + filter.NameFilter);
Logger.LogTrace("Filter Type: " + filter.FilterType);

if (filter.FilterType == FilterType.SortingFilter || filter.FilterType == FilterType.CraftFilter)
Expand Down
14 changes: 14 additions & 0 deletions InventoryTools/Lists/ListService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public ListService(ILogger<ListService> logger, MediatorService mediatorService,
_mediatorService.Subscribe<ListUpdatedMessage>(this, message => ListUpdated(message.FilterConfiguration) );
_mediatorService.Subscribe<AddToCraftListMessage>(this, AddToCraftListMessageRecv );
_mediatorService.Subscribe<AddToNewCraftListMessage>(this, AddToNewCraftListMessageRecv );
_mediatorService.Subscribe<AddToNewCuratedListMessage>(this, AddToNewCuratedListMessageRecv );
_mediatorService.Subscribe<AddToCuratedListMessage>(this, AddToCuratedListMessageRecv );
_framework.Update += OnUpdate;
}

Expand All @@ -88,12 +90,24 @@ private void AddToCraftListMessageRecv(AddToCraftListMessage obj)
}
}

private void AddToCuratedListMessageRecv(AddToCuratedListMessage obj)
{
var filter = GetListByKey(obj.FilterKey);
filter?.AddCuratedItem(new CuratedItem(obj.ItemId, obj.Quantity, obj.Flags));
}

private void AddToNewCraftListMessageRecv(AddToNewCraftListMessage obj)
{
var craftList = AddNewCraftList(null, obj.IsEphemeral);
craftList.CraftList.AddCraftItem(obj.ItemId, obj.Quantity, obj.Flags);
}

private void AddToNewCuratedListMessageRecv(AddToNewCuratedListMessage obj)
{
var craftList = AddNewCuratedList();
craftList.AddCuratedItem(new CuratedItem(obj.ItemId, obj.Quantity, obj.Flags));
}

private ConcurrentDictionary<string, FilterConfiguration> LoadListsFromConfiguration()
{
var savedLists = _configuration.GetSavedFilters();
Expand Down
Loading

0 comments on commit afc7ec9

Please sign in to comment.