Fix 'Export Material List to Clipboard'

This commit is contained in:
Liza 2024-07-12 18:33:10 +02:00
parent ccfb6b7423
commit 3a0bb492fa
Signed by: liza
GPG Key ID: 7199F8D727D55F67
3 changed files with 36 additions and 18 deletions

View File

@ -2,18 +2,20 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets2;
namespace Workshoppa.GameData; namespace Workshoppa.GameData;
internal sealed class RecipeTree internal sealed class RecipeTree
{ {
private readonly IDataManager _dataManager; private readonly IDataManager _dataManager;
private readonly IPluginLog _pluginLog;
private readonly IReadOnlyList<uint> _shopItemsOnly; private readonly IReadOnlyList<uint> _shopItemsOnly;
public RecipeTree(IDataManager dataManager) public RecipeTree(IDataManager dataManager, IPluginLog pluginLog)
{ {
_dataManager = dataManager; _dataManager = dataManager;
_pluginLog = pluginLog;
// probably incomplete, e.g. different housing districts have different shop types // probably incomplete, e.g. different housing districts have different shop types
var shopVendorIds = new uint[] var shopVendorIds = new uint[]
@ -62,6 +64,9 @@ internal sealed class RecipeTree
AmountCrafted = x.First().AmountCrafted, AmountCrafted = x.First().AmountCrafted,
}) })
.ToList(); .ToList();
_pluginLog.Verbose("Complete craft list:");
foreach (var item in completeList)
_pluginLog.Verbose($" {item.TotalQuantity}x {item.Name}");
// if a recipe has a specific amount crafted, divide the gathered amount by it // if a recipe has a specific amount crafted, divide the gathered amount by it
foreach (var ingredient in completeList.Where(x => x is { AmountCrafted: > 1 })) foreach (var ingredient in completeList.Where(x => x is { AmountCrafted: > 1 }))
@ -84,11 +89,18 @@ internal sealed class RecipeTree
List<RecipeInfo> sortedList = new List<RecipeInfo>(); List<RecipeInfo> sortedList = new List<RecipeInfo>();
while (sortedList.Count < completeList.Count) while (sortedList.Count < completeList.Count)
{ {
_pluginLog.Verbose("Sort round");
var canBeCrafted = completeList.Where(x => var canBeCrafted = completeList.Where(x =>
!sortedList.Contains(x) && x.DependsOn.All(y => sortedList.Any(z => y == z.ItemId))) !sortedList.Contains(x) && x.DependsOn.All(y => sortedList.Any(z => y == z.ItemId)))
.ToList(); .ToList();
foreach (var item in canBeCrafted)
_pluginLog.Verbose($" can craft: {item.TotalQuantity}x {item.Name}");
if (canBeCrafted.Count == 0) if (canBeCrafted.Count == 0)
{
foreach (var item in completeList.Where(x => !sortedList.Contains(x)))
_pluginLog.Warning($" can't craft: {item.TotalQuantity}x {item.Name} → ({string.Join(", ", item.DependsOn.Where(y => sortedList.All(z => y != z.ItemId)))})");
throw new InvalidOperationException("Unable to sort items"); throw new InvalidOperationException("Unable to sort items");
}
sortedList.AddRange(canBeCrafted.OrderBy(x => x.Name)); sortedList.AddRange(canBeCrafted.OrderBy(x => x.Name));
} }
@ -107,33 +119,34 @@ internal sealed class RecipeTree
if (recipe == null) if (recipe == null)
continue; continue;
foreach (var ingredient in recipe.UnkData5.Take(8)) for (int i = 0; i < 8; ++ i)
{ {
if (ingredient == null || ingredient.ItemIngredient == 0) var ingredient = recipe.Ingredient[i];
if (ingredient == null || ingredient.Row == 0)
continue; continue;
Item? item = _dataManager.GetExcelSheet<Item>()!.GetRow((uint)ingredient.ItemIngredient); Item? item = ingredient.Value;
if (item == null) if (item == null)
continue; continue;
Recipe? ingredientRecipe = GetFirstRecipeForItem((uint)ingredient.ItemIngredient); Recipe? ingredientRecipe = GetFirstRecipeForItem(ingredient.Row);
//_pluginLog.Information($"Adding {item.Name}"); //_pluginLog.Information($"Adding {item.Name}");
ingredients.Add(new RecipeInfo ingredients.Add(new RecipeInfo
{ {
ItemId = (uint)ingredient.ItemIngredient, ItemId = ingredient.Row,
Name = item.Name, Name = item.Name,
TotalQuantity = material.TotalQuantity * ingredient.AmountIngredient, TotalQuantity = material.TotalQuantity * recipe.AmountIngredient[i],
Type = Type =
_shopItemsOnly.Contains((uint)ingredient.ItemIngredient) ? Ingredient.EType.ShopItem : _shopItemsOnly.Contains(ingredient.Row) ? Ingredient.EType.ShopItem :
ingredientRecipe != null ? Ingredient.EType.Craftable : ingredientRecipe != null ? Ingredient.EType.Craftable :
GetGatheringItem((uint)ingredient.ItemIngredient) != null ? Ingredient.EType.Gatherable : GetGatheringItem(ingredient.Row) != null ? Ingredient.EType.Gatherable :
GetVentureItem((uint)ingredient.ItemIngredient) != null ? Ingredient.EType.Gatherable : GetVentureItem(ingredient.Row) != null ? Ingredient.EType.Gatherable :
Ingredient.EType.Other, Ingredient.EType.Other,
AmountCrafted = ingredientRecipe?.AmountResult ?? 1, AmountCrafted = ingredientRecipe?.AmountResult ?? 1,
DependsOn = ingredientRecipe?.UnkData5.Take(8).Where(x => x != null && x.ItemIngredient != 0) DependsOn = ingredientRecipe?.Ingredient.Where(x => x != null && IsValidItem(x.Row))
.Select(x => (uint)x.ItemIngredient) .Select(x => x.Row)
.ToList() .ToList()
?? new(), ?? new(),
}); });
@ -158,8 +171,8 @@ internal sealed class RecipeTree
TotalQuantity = x.Ingredient.TotalQuantity, TotalQuantity = x.Ingredient.TotalQuantity,
Type = _shopItemsOnly.Contains(x.Ingredient.ItemId) ? Ingredient.EType.ShopItem : x.Ingredient.Type, Type = _shopItemsOnly.Contains(x.Ingredient.ItemId) ? Ingredient.EType.ShopItem : x.Ingredient.Type,
AmountCrafted = x.Recipe!.AmountResult, AmountCrafted = x.Recipe!.AmountResult,
DependsOn = x.Recipe.UnkData5.Take(8).Where(y => y != null && y.ItemIngredient != 0) DependsOn = x.Recipe.Ingredient.Where(y => y != null && IsValidItem(y.Row))
.Select(y => (uint)y.ItemIngredient) .Select(y => y.Row)
.ToList(), .ToList(),
}) })
.ToList(); .ToList();
@ -172,7 +185,7 @@ internal sealed class RecipeTree
private GatheringItem? GetGatheringItem(uint itemId) private GatheringItem? GetGatheringItem(uint itemId)
{ {
return _dataManager.GetExcelSheet<GatheringItem>()!.FirstOrDefault(x => x.RowId > 0 && (uint)x.Item == itemId); return _dataManager.GetExcelSheet<GatheringItem>()!.FirstOrDefault(x => x.RowId > 0 && x.Item.Row == itemId);
} }
private RetainerTaskNormal? GetVentureItem(uint itemId) private RetainerTaskNormal? GetVentureItem(uint itemId)
@ -181,6 +194,11 @@ internal sealed class RecipeTree
.FirstOrDefault(x => x.RowId > 0 && x.Item.Row == itemId); .FirstOrDefault(x => x.RowId > 0 && x.Item.Row == itemId);
} }
public bool IsValidItem(uint itemId)
{
return itemId != 0 && itemId != uint.MaxValue;
}
private sealed class RecipeInfo : Ingredient private sealed class RecipeInfo : Ingredient
{ {
public required uint AmountCrafted { get; init; } public required uint AmountCrafted { get; init; }

View File

@ -72,7 +72,7 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
_gameStrings = new(dataManager, _pluginLog); _gameStrings = new(dataManager, _pluginLog);
_mainWindow = new(this, _pluginInterface, _clientState, _configuration, _workshopCache, _mainWindow = new(this, _pluginInterface, _clientState, _configuration, _workshopCache,
new IconCache(textureProvider), _chatGui, new RecipeTree(dataManager), _pluginLog); new IconCache(textureProvider), _chatGui, new RecipeTree(dataManager, _pluginLog), _pluginLog);
_windowSystem.AddWindow(_mainWindow); _windowSystem.AddWindow(_mainWindow);
_configWindow = new(_pluginInterface, _configuration); _configWindow = new(_pluginInterface, _configuration);
_windowSystem.AddWindow(_configWindow); _windowSystem.AddWindow(_configWindow);

View File

@ -1,6 +1,6 @@
<Project Sdk="Dalamud.NET.Sdk/9.0.2"> <Project Sdk="Dalamud.NET.Sdk/9.0.2">
<PropertyGroup> <PropertyGroup>
<Version>6.0</Version> <Version>6.1</Version>
<OutputPath>dist</OutputPath> <OutputPath>dist</OutputPath>
</PropertyGroup> </PropertyGroup>