Compare commits

...

6 Commits
v5.5 ... master

18 changed files with 280 additions and 486 deletions

2
LLib

@ -1 +1 @@
Subproject commit b5125d4b3f7cdc0c7514a01764e5b5d4d85f80a7
Subproject commit e4bbc05ede6f6f01e7028b24614ed8cb333e909c

View File

@ -6,13 +6,13 @@ namespace Workshoppa.External;
internal sealed class ExternalPluginHandler
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly IPluginLog _pluginLog;
private readonly PandoraIpc _pandoraIpc;
private bool? _pandoraState;
public ExternalPluginHandler(DalamudPluginInterface pluginInterface, IPluginLog pluginLog)
public ExternalPluginHandler(IDalamudPluginInterface pluginInterface, IPluginLog pluginLog)
{
_pluginInterface = pluginInterface;
_pluginLog = pluginLog;

View File

@ -13,7 +13,7 @@ internal sealed class PandoraIpc
private readonly ICallGateSubscriber<string, bool?> _getEnabled;
private readonly ICallGateSubscriber<string, bool, object?> _setEnabled;
public PandoraIpc(DalamudPluginInterface pluginInterface, IPluginLog pluginLog)
public PandoraIpc(IDalamudPluginInterface pluginInterface, IPluginLog pluginLog)
{
_pluginLog = pluginLog;
_getEnabled = pluginInterface.GetIpcSubscriber<string, bool?>("PandorasBox.GetFeatureEnabled");

View File

@ -4,8 +4,8 @@ using System.Text.RegularExpressions;
using Dalamud.Plugin.Services;
using LLib;
using Lumina.Excel;
using Lumina.Excel.CustomSheets;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;
using Lumina.Text.ReadOnly;
namespace Workshoppa.GameData;
@ -38,7 +38,16 @@ internal sealed class GameStrings
[Sheet("custom/001/CmnDefCompanyManufactory_00150")]
[SuppressMessage("Performance", "CA1812")]
private sealed class WorkshopDialogue : QuestDialogueText
private readonly struct WorkshopDialogue(ExcelPage page, uint offset, uint row)
: IQuestDialogueText, IExcelRow<WorkshopDialogue>
{
public uint RowId => row;
public ReadOnlySeString Key => page.ReadString(offset, offset);
public ReadOnlySeString Value => page.ReadString(offset + 4, offset);
static WorkshopDialogue IExcelRow<WorkshopDialogue>.Create(ExcelPage page, uint offset,
uint row) =>
new(page, offset, row);
}
}

View File

@ -2,18 +2,20 @@
using System.Collections.Generic;
using System.Linq;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;
namespace Workshoppa.GameData;
internal sealed class RecipeTree
{
private readonly IDataManager _dataManager;
private readonly IPluginLog _pluginLog;
private readonly IReadOnlyList<uint> _shopItemsOnly;
public RecipeTree(IDataManager dataManager)
public RecipeTree(IDataManager dataManager, IPluginLog pluginLog)
{
_dataManager = dataManager;
_pluginLog = pluginLog;
// probably incomplete, e.g. different housing districts have different shop types
var shopVendorIds = new uint[]
@ -29,9 +31,10 @@ internal sealed class RecipeTree
262211, // Z'ranmaia, upper decks
};
_shopItemsOnly = _dataManager.GetExcelSheet<GilShopItem>()!
_shopItemsOnly = _dataManager.GetSubrowExcelSheet<GilShopItem>()
.Flatten()
.Where(x => shopVendorIds.Contains(x.RowId))
.Select(x => x.Item.Row)
.Select(x => x.Item.RowId)
.Where(x => x > 0)
.Distinct()
.ToList()
@ -62,6 +65,9 @@ internal sealed class RecipeTree
AmountCrafted = x.First().AmountCrafted,
})
.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
foreach (var ingredient in completeList.Where(x => x is { AmountCrafted: > 1 }))
@ -84,11 +90,18 @@ internal sealed class RecipeTree
List<RecipeInfo> sortedList = new List<RecipeInfo>();
while (sortedList.Count < completeList.Count)
{
_pluginLog.Verbose("Sort round");
var canBeCrafted = completeList.Where(x =>
!sortedList.Contains(x) && x.DependsOn.All(y => sortedList.Any(z => y == z.ItemId)))
.ToList();
foreach (var item in canBeCrafted)
_pluginLog.Verbose($" can craft: {item.TotalQuantity}x {item.Name}");
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");
}
sortedList.AddRange(canBeCrafted.OrderBy(x => x.Name));
}
@ -107,33 +120,34 @@ internal sealed class RecipeTree
if (recipe == null)
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.Value.Ingredient[i];
if (!ingredient.IsValid || ingredient.RowId == 0)
continue;
Item? item = _dataManager.GetExcelSheet<Item>()!.GetRow((uint)ingredient.ItemIngredient);
if (item == null)
Item item = ingredient.Value;
if (!IsValidItem(item.RowId))
continue;
Recipe? ingredientRecipe = GetFirstRecipeForItem((uint)ingredient.ItemIngredient);
Recipe? ingredientRecipe = GetFirstRecipeForItem(ingredient.RowId);
//_pluginLog.Information($"Adding {item.Name}");
ingredients.Add(new RecipeInfo
{
ItemId = (uint)ingredient.ItemIngredient,
Name = item.Name,
TotalQuantity = material.TotalQuantity * ingredient.AmountIngredient,
ItemId = ingredient.RowId,
Name = item.Name.ToString(),
TotalQuantity = material.TotalQuantity * recipe.Value.AmountIngredient[i],
Type =
_shopItemsOnly.Contains((uint)ingredient.ItemIngredient) ? Ingredient.EType.ShopItem :
_shopItemsOnly.Contains(ingredient.RowId) ? Ingredient.EType.ShopItem :
ingredientRecipe != null ? Ingredient.EType.Craftable :
GetGatheringItem((uint)ingredient.ItemIngredient) != null ? Ingredient.EType.Gatherable :
GetVentureItem((uint)ingredient.ItemIngredient) != null ? Ingredient.EType.Gatherable :
GetGatheringItem(ingredient.RowId) != null ? Ingredient.EType.Gatherable :
GetVentureItem(ingredient.RowId) != null ? Ingredient.EType.Gatherable :
Ingredient.EType.Other,
AmountCrafted = ingredientRecipe?.AmountResult ?? 1,
DependsOn = ingredientRecipe?.UnkData5.Take(8).Where(x => x != null && x.ItemIngredient != 0)
.Select(x => (uint)x.ItemIngredient)
DependsOn = ingredientRecipe?.Ingredient.Where(x => x.IsValid && IsValidItem(x.RowId))
.Select(x => x.RowId)
.ToList()
?? new(),
});
@ -157,9 +171,9 @@ internal sealed class RecipeTree
Name = x.Ingredient.Name,
TotalQuantity = x.Ingredient.TotalQuantity,
Type = _shopItemsOnly.Contains(x.Ingredient.ItemId) ? Ingredient.EType.ShopItem : x.Ingredient.Type,
AmountCrafted = x.Recipe!.AmountResult,
DependsOn = x.Recipe.UnkData5.Take(8).Where(y => y != null && y.ItemIngredient != 0)
.Select(y => (uint)y.ItemIngredient)
AmountCrafted = x.Recipe!.Value.AmountResult,
DependsOn = x.Recipe.Value.Ingredient.Where(y => y.IsValid && IsValidItem(y.RowId))
.Select(y => y.RowId)
.ToList(),
})
.ToList();
@ -167,18 +181,23 @@ internal sealed class RecipeTree
private Recipe? GetFirstRecipeForItem(uint itemId)
{
return _dataManager.GetExcelSheet<Recipe>()!.FirstOrDefault(x => x.RowId > 0 && x.ItemResult.Row == itemId);
return _dataManager.GetExcelSheet<Recipe>().FirstOrDefault(x => x.RowId > 0 && x.ItemResult.RowId == 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.RowId == itemId);
}
private RetainerTaskNormal? GetVentureItem(uint itemId)
{
return _dataManager.GetExcelSheet<RetainerTaskNormal>()!
.FirstOrDefault(x => x.RowId > 0 && x.Item.Row == itemId);
return _dataManager.GetExcelSheet<RetainerTaskNormal>()
.FirstOrDefault(x => x.RowId > 0 && x.Item.RowId == itemId);
}
private static bool IsValidItem(uint itemId)
{
return itemId > 19 && itemId != uint.MaxValue;
}
private sealed class RecipeInfo : Ingredient

View File

@ -1,10 +0,0 @@
namespace Workshoppa.GameData.Shops;
internal sealed class ItemForSale
{
public required int Position { get; init; }
public required uint ItemId { get; init; }
public required string? ItemName { get; init; }
public required uint Price { get; init; }
public required uint OwnedItems { get; init; }
}

View File

@ -1,19 +0,0 @@
using System;
namespace Workshoppa.GameData.Shops;
internal sealed class PurchaseState
{
public PurchaseState(int desiredItems, int ownedItems)
{
DesiredItems = desiredItems;
OwnedItems = ownedItems;
}
public int DesiredItems { get; }
public int OwnedItems { get; set; }
public int ItemsLeftToBuy => Math.Max(0, DesiredItems - OwnedItems);
public bool IsComplete => ItemsLeftToBuy == 0;
public bool IsAwaitingYesNo { get; set; }
public DateTime NextStep { get; set; } = DateTime.MinValue;
}

View File

@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;
namespace Workshoppa.GameData;
@ -15,41 +16,45 @@ internal sealed class WorkshopCache
{
try
{
Dictionary<ushort, Item> itemMapping = dataManager.GetExcelSheet<CompanyCraftSupplyItem>()!
Dictionary<uint, Item> itemMapping = dataManager.GetExcelSheet<CompanyCraftSupplyItem>()
.Where(x => x.RowId > 0)
.ToDictionary(x => (ushort)x.RowId, x => x.Item.Value!);
.ToDictionary(x => x.RowId, x => x.Item.Value);
Crafts = dataManager.GetExcelSheet<CompanyCraftSequence>()!
Crafts = dataManager.GetExcelSheet<CompanyCraftSequence>()
.Where(x => x.RowId > 0)
.Select(x => new WorkshopCraft
{
WorkshopItemId = x.RowId,
ResultItem = x.ResultItem.Row,
Name = x.ResultItem.Value!.Name.ToString(),
IconId = x.ResultItem.Value!.Icon,
Category = (WorkshopCraftCategory)x.CompanyCraftDraftCategory.Row,
Type = x.CompanyCraftType.Row,
Phases = x.CompanyCraftPart.Where(part => part.Row != 0)
ResultItem = x.ResultItem.RowId,
Name = x.ResultItem.Value.Name.ToString(),
IconId = x.ResultItem.Value.Icon,
Category = (WorkshopCraftCategory)x.CompanyCraftDraftCategory.RowId,
Type = x.CompanyCraftType.RowId,
Phases = x.CompanyCraftPart.Where(part => part.RowId != 0)
.SelectMany(part =>
part.Value!.CompanyCraftProcess
.Where(y => y.Value!.UnkData0.Any(z => z.SupplyItem > 0))
.Select(y => (Type: part.Value!.CompanyCraftType.Value, Process: y)))
.Select(y => new WorkshopCraftPhase
{
Name = y.Type!.Name.ToString(),
Items = y.Process.Value!.UnkData0
.Where(item => item.SupplyItem > 0)
.Select(item => new WorkshopCraftItem
part.Value.CompanyCraftProcess
.Select(y => new WorkshopCraftPhase
{
ItemId = itemMapping[item.SupplyItem].RowId,
Name = itemMapping[item.SupplyItem].Name.ToString(),
IconId = itemMapping[item.SupplyItem].Icon,
SetQuantity = item.SetQuantity,
SetsRequired = item.SetsRequired,
})
.ToList()
.AsReadOnly(),
})
Name = part.Value.CompanyCraftType.Value.Name.ToString(),
Items = Enumerable.Range(0, y.Value.SupplyItem.Count)
.Select(i => new
{
SupplyItem = y.Value.SupplyItem[i],
SetsRequired = y.Value.SetsRequired[i],
SetQuantity = y.Value.SetQuantity[i],
})
.Where(item => item.SupplyItem.RowId > 0)
.Select(item => new WorkshopCraftItem
{
ItemId = itemMapping[item.SupplyItem.RowId].RowId,
Name = itemMapping[item.SupplyItem.RowId].Name.ToString(),
IconId = itemMapping[item.SupplyItem.RowId].Icon,
SetQuantity = item.SetQuantity,
SetsRequired = item.SetsRequired,
})
.ToList()
.AsReadOnly(),
}))
.ToList()
.AsReadOnly(),
})

View File

@ -5,10 +5,9 @@ using Dalamud.Interface.Components;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using LLib;
using LLib.GameUI;
using LLib.Shop.Model;
using Workshoppa.External;
using Workshoppa.GameData.Shops;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Workshoppa.Windows;
@ -17,7 +16,6 @@ internal sealed class CeruleumTankWindow : ShopWindow
{
private const int CeruleumTankItemId = 10155;
private readonly WorkshopPlugin _plugin;
private readonly IPluginLog _pluginLog;
private readonly Configuration _configuration;
private readonly IChatGui _chatGui;
@ -26,28 +24,31 @@ internal sealed class CeruleumTankWindow : ShopWindow
private int _buyStackCount;
private bool _buyPartialStacks = true;
public CeruleumTankWindow(WorkshopPlugin plugin, IPluginLog pluginLog, IGameGui gameGui,
IAddonLifecycle addonLifecycle, Configuration configuration, ExternalPluginHandler externalPluginHandler,
public CeruleumTankWindow(
IPluginLog pluginLog,
IGameGui gameGui,
IAddonLifecycle addonLifecycle,
Configuration configuration,
ExternalPluginHandler externalPluginHandler,
IChatGui chatGui)
: base("Ceruleum Tanks###WorkshoppaCeruleumTankWindow", "FreeCompanyCreditShop", plugin, pluginLog, gameGui,
: base("Ceruleum Tanks###WorkshoppaCeruleumTankWindow", "FreeCompanyCreditShop", pluginLog, gameGui,
addonLifecycle, externalPluginHandler)
{
_plugin = plugin;
_pluginLog = pluginLog;
_chatGui = chatGui;
_configuration = configuration;
}
protected override bool Enabled => _configuration.EnableCeruleumTankCalculator;
public override bool IsEnabled => _configuration.EnableCeruleumTankCalculator;
protected override unsafe void UpdateShopStock(AtkUnitBase* addon)
public override unsafe void UpdateShopStock(AtkUnitBase* addon)
{
if (addon->AtkValuesCount != 170)
{
_pluginLog.Error(
$"Unexpected amount of atkvalues for FreeCompanyCreditShop addon ({addon->AtkValuesCount})");
_companyCredits = 0;
ItemForSale = null;
Shop.ItemForSale = null;
return;
}
@ -57,11 +58,11 @@ internal sealed class CeruleumTankWindow : ShopWindow
uint itemCount = atkValues[9].UInt;
if (itemCount == 0)
{
ItemForSale = null;
Shop.ItemForSale = null;
return;
}
ItemForSale = Enumerable.Range(0, (int)itemCount)
Shop.ItemForSale = Enumerable.Range(0, (int)itemCount)
.Select(i => new ItemForSale
{
Position = i,
@ -73,18 +74,18 @@ internal sealed class CeruleumTankWindow : ShopWindow
.FirstOrDefault(x => x.ItemId == CeruleumTankItemId);
}
protected override int GetCurrencyCount() => _companyCredits;
public override int GetCurrencyCount() => _companyCredits;
public override void Draw()
{
if (ItemForSale == null)
if (Shop.ItemForSale == null)
{
IsOpen = false;
return;
}
int ceruleumTanks = GetItemCount(CeruleumTankItemId);
int freeInventorySlots = _plugin.CountFreeInventorySlots();
int ceruleumTanks = Shop.GetItemCount(CeruleumTankItemId);
int freeInventorySlots = Shop.CountFreeInventorySlots();
ImGui.Text("Inventory");
ImGui.Indent();
@ -94,7 +95,7 @@ internal sealed class CeruleumTankWindow : ShopWindow
ImGui.Separator();
if (PurchaseState == null)
if (Shop.PurchaseState == null)
{
ImGui.SetNextItemWidth(100);
ImGui.InputInt("Stacks to Buy", ref _buyStackCount);
@ -108,27 +109,27 @@ internal sealed class CeruleumTankWindow : ShopWindow
if (_buyPartialStacks && ceruleumTanks % 999 > 0)
missingItems += (999 - ceruleumTanks % 999);
if (PurchaseState != null)
if (Shop.PurchaseState != null)
{
HandleNextPurchaseStep();
if (PurchaseState != null)
Shop.HandleNextPurchaseStep();
if (Shop.PurchaseState != null)
{
ImGui.Text($"Buying {FormatStackCount(PurchaseState.ItemsLeftToBuy)}...");
ImGui.Text($"Buying {FormatStackCount(Shop.PurchaseState.ItemsLeftToBuy)}...");
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Cancel Auto-Buy"))
CancelAutoPurchase();
Shop.CancelAutoPurchase();
}
}
else
{
int toPurchase = Math.Min(GetMaxItemsToPurchase(), missingItems);
int toPurchase = Math.Min(Shop.GetMaxItemsToPurchase(), missingItems);
if (toPurchase > 0)
{
ImGui.Spacing();
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.DollarSign,
$"Auto-Buy {FormatStackCount(toPurchase)} for {ItemForSale.Price * toPurchase:N0} CC"))
$"Auto-Buy {FormatStackCount(toPurchase)} for {Shop.ItemForSale.Price * toPurchase:N0} CC"))
{
StartAutoPurchase(toPurchase);
HandleNextPurchaseStep();
Shop.StartAutoPurchase(toPurchase);
Shop.HandleNextPurchaseStep();
}
}
}
@ -144,12 +145,12 @@ internal sealed class CeruleumTankWindow : ShopWindow
return $"{fullStacks:N0} {stacks}";
}
protected override unsafe void FirePurchaseCallback(AtkUnitBase* addonShop, int buyNow)
public override unsafe void TriggerPurchase(AtkUnitBase* addonShop, int buyNow)
{
var buyItem = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 0 },
new() { Type = ValueType.UInt, UInt = (uint)ItemForSale!.Position },
new() { Type = ValueType.UInt, UInt = (uint)Shop.ItemForSale!.Position },
new() { Type = ValueType.UInt, UInt = (uint)buyNow },
};
addonShop->FireCallback(3, buyItem);
@ -163,9 +164,9 @@ internal sealed class CeruleumTankWindow : ShopWindow
return false;
}
int freeInventorySlots = _plugin.CountFreeInventorySlots();
int freeInventorySlots = Shop.CountFreeInventorySlots();
stackCount = Math.Min(freeInventorySlots, stackCount);
missingQuantity = Math.Min(GetMaxItemsToPurchase(), stackCount * 999);
missingQuantity = Math.Min(Shop.GetMaxItemsToPurchase(), stackCount * 999);
return true;
}
@ -177,25 +178,25 @@ internal sealed class CeruleumTankWindow : ShopWindow
return false;
}
int freeInventorySlots = _plugin.CountFreeInventorySlots();
int partialStacks = _plugin.CountInventorySlotsWithCondition(CeruleumTankItemId, q => q < 999);
int fullStacks = _plugin.CountInventorySlotsWithCondition(CeruleumTankItemId, q => q == 999);
int freeInventorySlots = Shop.CountFreeInventorySlots();
int partialStacks = Shop.CountInventorySlotsWithCondition(CeruleumTankItemId, q => q < 999);
int fullStacks = Shop.CountInventorySlotsWithCondition(CeruleumTankItemId, q => q == 999);
int tanks = Math.Min((fullStacks + partialStacks + freeInventorySlots) * 999,
Math.Max(stackCount * 999, (fullStacks + partialStacks) * 999));
_pluginLog.Information("T: " + tanks);
int owned = GetItemCount(CeruleumTankItemId);
int owned = Shop.GetItemCount(CeruleumTankItemId);
if (tanks <= owned)
missingQuantity = 0;
else
missingQuantity = Math.Min(GetMaxItemsToPurchase(), tanks - owned);
missingQuantity = Math.Min(Shop.GetMaxItemsToPurchase(), tanks - owned);
return true;
}
public void StartPurchase(int quantity)
{
if (!IsOpen || ItemForSale == null)
if (!IsOpen || Shop.ItemForSale == null)
{
_chatGui.PrintError("Could not start purchase, shop window is not open.");
return;
@ -208,7 +209,7 @@ internal sealed class CeruleumTankWindow : ShopWindow
}
_chatGui.Print($"Starting purchase of {FormatStackCount(quantity)} ceruleum tanks.");
StartAutoPurchase(quantity);
HandleNextPurchaseStep();
Shop.StartAutoPurchase(quantity);
Shop.HandleNextPurchaseStep();
}
}

View File

@ -7,10 +7,10 @@ namespace Workshoppa.Windows;
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly Configuration _configuration;
public ConfigWindow(DalamudPluginInterface pluginInterface, Configuration configuration)
public ConfigWindow(IDalamudPluginInterface pluginInterface, Configuration configuration)
: base("Workshoppa - Configuration###WorkshoppaConfigWindow")
{

View File

@ -7,7 +7,7 @@ using System.Text.RegularExpressions;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
@ -24,7 +24,7 @@ internal sealed class MainWindow : LWindow, IPersistableWindowConfig
private static readonly Regex CountAndName = new(@"^(\d{1,5})x?\s+(.*)$", RegexOptions.Compiled);
private readonly WorkshopPlugin _plugin;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState;
private readonly Configuration _configuration;
private readonly WorkshopCache _workshopCache;
@ -37,7 +37,7 @@ internal sealed class MainWindow : LWindow, IPersistableWindowConfig
private bool _checkInventory;
private string _newPresetName = string.Empty;
public MainWindow(WorkshopPlugin plugin, DalamudPluginInterface pluginInterface, IClientState clientState,
public MainWindow(WorkshopPlugin plugin, IDalamudPluginInterface pluginInterface, IClientState clientState,
Configuration configuration, WorkshopCache workshopCache, IconCache iconCache, IChatGui chatGui,
RecipeTree recipeTree, IPluginLog pluginLog)
: base("Workshoppa###WorkshoppaMainWindow")
@ -70,7 +70,7 @@ internal sealed class MainWindow : LWindow, IPersistableWindowConfig
public ButtonState State { get; set; } = ButtonState.None;
private bool IsDiscipleOfHand =>
_clientState.LocalPlayer != null && _clientState.LocalPlayer.ClassJob.Id is >= 8 and <= 15;
_clientState.LocalPlayer != null && _clientState.LocalPlayer.ClassJob.RowId is >= 8 and <= 15;
public WindowConfig WindowConfig => _configuration.MainWindowConfig;
@ -547,6 +547,8 @@ internal sealed class MainWindow : LWindow, IPersistableWindowConfig
ImGui.Image(icon.ImGuiHandle, new Vector2(ImGui.GetFrameHeight()));
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (ImGui.GetFrameHeight() - ImGui.GetTextLineHeight()) / 2);
icon.Dispose();
}
ImGui.TextColored(inInventory >= item.TotalQuantity ? ImGuiColors.HealerGreen : ImGuiColors.DalamudRed,

View File

@ -8,8 +8,8 @@ using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using LLib.GameUI;
using LLib.Shop.Model;
using Workshoppa.External;
using Workshoppa.GameData.Shops;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Workshoppa.Windows;
@ -21,29 +21,32 @@ internal sealed class RepairKitWindow : ShopWindow
private readonly IPluginLog _pluginLog;
private readonly Configuration _configuration;
public RepairKitWindow(WorkshopPlugin plugin, IPluginLog pluginLog,
IGameGui gameGui, IAddonLifecycle addonLifecycle, Configuration configuration,
public RepairKitWindow(
IPluginLog pluginLog,
IGameGui gameGui,
IAddonLifecycle addonLifecycle,
Configuration configuration,
ExternalPluginHandler externalPluginHandler)
: base("Repair Kits###WorkshoppaRepairKitWindow", "Shop", plugin, pluginLog, gameGui, addonLifecycle, externalPluginHandler)
: base("Repair Kits###WorkshoppaRepairKitWindow", "Shop", pluginLog, gameGui, addonLifecycle, externalPluginHandler)
{
_pluginLog = pluginLog;
_configuration = configuration;
}
protected override bool Enabled => _configuration.EnableRepairKitCalculator;
public override bool IsEnabled => _configuration.EnableRepairKitCalculator;
protected override unsafe void UpdateShopStock(AtkUnitBase* addon)
public override unsafe void UpdateShopStock(AtkUnitBase* addon)
{
if (GetDarkMatterClusterCount() == 0)
{
ItemForSale = null;
Shop.ItemForSale = null;
return;
}
if (addon->AtkValuesCount != 625)
{
_pluginLog.Error($"Unexpected amount of atkvalues for Shop addon ({addon->AtkValuesCount})");
ItemForSale = null;
Shop.ItemForSale = null;
return;
}
@ -52,18 +55,18 @@ internal sealed class RepairKitWindow : ShopWindow
// Check if on 'Current Stock' tab?
if (atkValues[0].UInt != 0)
{
ItemForSale = null;
Shop.ItemForSale = null;
return;
}
uint itemCount = atkValues[2].UInt;
if (itemCount == 0)
{
ItemForSale = null;
Shop.ItemForSale = null;
return;
}
ItemForSale = Enumerable.Range(0, (int)itemCount)
Shop.ItemForSale = Enumerable.Range(0, (int)itemCount)
.Select(i => new ItemForSale
{
Position = i,
@ -75,14 +78,14 @@ internal sealed class RepairKitWindow : ShopWindow
.FirstOrDefault(x => x.ItemId == DarkMatterCluster6ItemId);
}
private int GetDarkMatterClusterCount() => GetItemCount(10335);
private int GetDarkMatterClusterCount() => Shop.GetItemCount(10335);
protected override int GetCurrencyCount() => GetItemCount(1);
public override int GetCurrencyCount() => Shop.GetItemCount(1);
public override void Draw()
{
int darkMatterClusters = GetDarkMatterClusterCount();
if (ItemForSale == null || darkMatterClusters == 0)
if (Shop.ItemForSale == null || darkMatterClusters == 0)
{
IsOpen = false;
return;
@ -91,43 +94,43 @@ internal sealed class RepairKitWindow : ShopWindow
ImGui.Text("Inventory");
ImGui.Indent();
ImGui.Text($"Dark Matter Clusters: {darkMatterClusters:N0}");
ImGui.Text($"Grade 6 Dark Matter: {ItemForSale.OwnedItems:N0}");
ImGui.Text($"Grade 6 Dark Matter: {Shop.ItemForSale.OwnedItems:N0}");
ImGui.Unindent();
int missingItems = Math.Max(0, darkMatterClusters * 5 - (int)ItemForSale.OwnedItems);
int missingItems = Math.Max(0, darkMatterClusters * 5 - (int)Shop.ItemForSale.OwnedItems);
ImGui.TextColored(missingItems == 0 ? ImGuiColors.HealerGreen : ImGuiColors.DalamudRed,
$"Missing Grade 6 Dark Matter: {missingItems:N0}");
if (PurchaseState != null)
if (Shop.PurchaseState != null)
{
HandleNextPurchaseStep();
if (PurchaseState != null)
Shop.HandleNextPurchaseStep();
if (Shop.PurchaseState != null)
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Cancel Auto-Buy"))
CancelAutoPurchase();
Shop.CancelAutoPurchase();
}
}
else
{
int toPurchase = Math.Min(GetMaxItemsToPurchase(), missingItems);
int toPurchase = Math.Min(Shop.GetMaxItemsToPurchase(), missingItems);
if (toPurchase > 0)
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.DollarSign,
$"Auto-Buy missing Dark Matter for {ItemForSale.Price * toPurchase:N0}{SeIconChar.Gil.ToIconString()}"))
$"Auto-Buy missing Dark Matter for {Shop.ItemForSale.Price * toPurchase:N0}{SeIconChar.Gil.ToIconString()}"))
{
StartAutoPurchase(toPurchase);
HandleNextPurchaseStep();
Shop.StartAutoPurchase(toPurchase);
Shop.HandleNextPurchaseStep();
}
}
}
}
protected override unsafe void FirePurchaseCallback(AtkUnitBase* addonShop, int buyNow)
public override unsafe void TriggerPurchase(AtkUnitBase* addonShop, int buyNow)
{
var buyItem = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 0 },
new() { Type = ValueType.Int, Int = ItemForSale!.Position },
new() { Type = ValueType.Int, Int = Shop.ItemForSale!.Position },
new() { Type = ValueType.Int, Int = buyNow },
new() { Type = 0, Int = 0 }
};

View File

@ -1,198 +1,49 @@
using System;
using System.Numerics;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using LLib.GameUI;
using LLib.ImGui;
using LLib.Shop;
using Workshoppa.External;
using Workshoppa.GameData.Shops;
namespace Workshoppa.Windows;
internal abstract class ShopWindow : LWindow, IDisposable
internal abstract class ShopWindow : LWindow, IShopWindow, IDisposable
{
private readonly string _addonName;
private readonly WorkshopPlugin _plugin;
private readonly IPluginLog _pluginLog;
private readonly IGameGui _gameGui;
private readonly IAddonLifecycle _addonLifecycle;
private readonly ExternalPluginHandler _externalPluginHandler;
protected ItemForSale? ItemForSale;
protected PurchaseState? PurchaseState;
protected ShopWindow(string name, string addonName, WorkshopPlugin plugin, IPluginLog pluginLog,
IGameGui gameGui, IAddonLifecycle addonLifecycle, ExternalPluginHandler externalPluginHandler)
: base(name)
protected ShopWindow(
string windowName,
string addonName,
IPluginLog pluginLog,
IGameGui gameGui,
IAddonLifecycle addonLifecycle,
ExternalPluginHandler externalPluginHandler)
: base(windowName)
{
_addonName = addonName;
_plugin = plugin;
_pluginLog = pluginLog;
_gameGui = gameGui;
_addonLifecycle = addonLifecycle;
_externalPluginHandler = externalPluginHandler;
Shop = new RegularShopBase(this, addonName, pluginLog, gameGui, addonLifecycle);
Position = new Vector2(100, 100);
PositionCondition = ImGuiCond.Always;
Flags = ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoNav | ImGuiWindowFlags.NoCollapse;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, _addonName, ShopPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PreFinalize, _addonName, ShopPreFinalize);
_addonLifecycle.RegisterListener(AddonEvent.PostUpdate, _addonName, ShopPostUpdate);
}
public bool AutoBuyEnabled => PurchaseState != null;
protected abstract bool Enabled { get; }
public void Dispose() => Shop.Dispose();
public bool AutoBuyEnabled => Shop.AutoBuyEnabled;
public bool IsAwaitingYesNo
{
get => PurchaseState?.IsAwaitingYesNo ?? false;
set => PurchaseState!.IsAwaitingYesNo = value;
get { return Shop.IsAwaitingYesNo; }
set { Shop.IsAwaitingYesNo = value; }
}
private unsafe void ShopPostSetup(AddonEvent type, AddonArgs args)
{
if (!Enabled)
{
ItemForSale = null;
IsOpen = false;
return;
}
UpdateShopStock((AtkUnitBase*)args.Addon);
PostUpdateShopStock();
if (ItemForSale != null)
IsOpen = true;
}
private void ShopPreFinalize(AddonEvent type, AddonArgs args)
{
PurchaseState = null;
_externalPluginHandler.Restore();
IsOpen = false;
}
private unsafe void ShopPostUpdate(AddonEvent type, AddonArgs args)
{
if (!Enabled)
{
ItemForSale = null;
IsOpen = false;
return;
}
UpdateShopStock((AtkUnitBase*)args.Addon);
PostUpdateShopStock();
if (ItemForSale != null)
{
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
short x = 0, y = 0;
addon->GetPosition(&x, &y);
short width = 0, height = 0;
addon->GetSize(&width, &height, true);
x += width;
if ((short)Position!.Value.X != x || (short)Position!.Value.Y != y)
Position = new Vector2(x, y);
IsOpen = true;
}
else
IsOpen = false;
}
protected abstract unsafe void UpdateShopStock(AtkUnitBase* addon);
private void PostUpdateShopStock()
{
if (ItemForSale != null && PurchaseState != null)
{
int ownedItems = (int)ItemForSale.OwnedItems;
if (PurchaseState.OwnedItems != ownedItems)
{
PurchaseState.OwnedItems = ownedItems;
PurchaseState.NextStep = DateTime.Now.AddSeconds(0.25);
}
}
}
protected unsafe int GetItemCount(uint itemId)
{
InventoryManager* inventoryManager = InventoryManager.Instance();
return inventoryManager->GetInventoryItemCount(itemId, checkEquipped: false, checkArmory: false);
}
protected abstract int GetCurrencyCount();
protected int GetMaxItemsToPurchase()
{
if (ItemForSale == null)
return 0;
int currency = GetCurrencyCount();
return (int)(currency / ItemForSale!.Price);
}
protected void CancelAutoPurchase()
{
PurchaseState = null;
_externalPluginHandler.Restore();
}
protected void StartAutoPurchase(int toPurchase)
{
PurchaseState = new((int)ItemForSale!.OwnedItems + toPurchase, (int)ItemForSale.OwnedItems);
_externalPluginHandler.Save();
}
protected unsafe void HandleNextPurchaseStep()
{
if (ItemForSale == null || PurchaseState == null)
return;
int maxStackSize = _plugin.DetermineMaxStackSize(ItemForSale.ItemId);
if (maxStackSize == 0 && !_plugin.HasFreeInventorySlot())
{
_pluginLog.Warning($"No free inventory slots, can't buy more {ItemForSale.ItemName}");
PurchaseState = null;
_externalPluginHandler.Restore();
}
else if (!PurchaseState.IsComplete)
{
if (PurchaseState.NextStep <= DateTime.Now &&
_gameGui.TryGetAddonByName(_addonName, out AtkUnitBase* addonShop))
{
int buyNow = Math.Min(PurchaseState.ItemsLeftToBuy, maxStackSize);
_pluginLog.Information($"Buying {buyNow}x {ItemForSale.ItemName}");
FirePurchaseCallback(addonShop, buyNow);
PurchaseState.NextStep = DateTime.MaxValue;
PurchaseState.IsAwaitingYesNo = true;
}
}
else
{
_pluginLog.Information(
$"Stopping item purchase (desired = {PurchaseState.DesiredItems}, owned = {PurchaseState.OwnedItems})");
PurchaseState = null;
_externalPluginHandler.Restore();
}
}
protected abstract unsafe void FirePurchaseCallback(AtkUnitBase* addonShop, int buyNow);
public void Dispose()
{
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, _addonName, ShopPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PreFinalize, _addonName, ShopPreFinalize);
_addonLifecycle.UnregisterListener(AddonEvent.PostUpdate, _addonName, ShopPostUpdate);
}
protected RegularShopBase Shop { get; }
public abstract bool IsEnabled { get; }
public abstract int GetCurrencyCount();
public abstract unsafe void UpdateShopStock(AtkUnitBase* addon);
public abstract unsafe void TriggerPurchase(AtkUnitBase* addonShop, int buyNow);
public void SaveExternalPluginState() => _externalPluginHandler.Save();
public void RestoreExternalPluginState() => _externalPluginHandler.Restore();
}

View File

@ -9,7 +9,7 @@ namespace Workshoppa;
partial class WorkshopPlugin
{
private void InteractWithFabricationStation(GameObject fabricationStation)
private void InteractWithFabricationStation(IGameObject fabricationStation)
=> InteractWithTarget(fabricationStation);
private void TakeItemFromQueue()

View File

@ -18,7 +18,7 @@ namespace Workshoppa;
partial class WorkshopPlugin
{
private unsafe void InteractWithTarget(GameObject obj)
private unsafe void InteractWithTarget(IGameObject obj)
{
_pluginLog.Information($"Setting target to {obj}");
/*
@ -31,7 +31,7 @@ partial class WorkshopPlugin
(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)obj.Address, false);
}
private float GetDistanceToEventObject(IReadOnlyList<uint> npcIds, out GameObject? o)
private float GetDistanceToEventObject(IReadOnlyList<uint> npcIds, out IGameObject? o)
{
Vector3? localPlayerPosition = _clientState.LocalPlayer?.Position;
if (localPlayerPosition != null)
@ -40,7 +40,7 @@ partial class WorkshopPlugin
{
if (obj.ObjectKind == ObjectKind.EventObj)
{
if (npcIds.Contains(GetNpcId(obj)))
if (npcIds.Contains(obj.DataId))
{
o = obj;
float distance = Vector3.Distance(localPlayerPosition.Value,
@ -56,12 +56,6 @@ partial class WorkshopPlugin
return float.MaxValue;
}
private unsafe uint GetNpcId(GameObject obj)
{
return ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)obj.Address)->GetNpcID();
}
private unsafe AtkUnitBase* GetCompanyCraftingLogAddon()
{
if (_gameGui.TryGetAddonByName<AtkUnitBase>("CompanyCraftRecipeNoteBook", out var addon) &&
@ -80,7 +74,7 @@ partial class WorkshopPlugin
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.CompanyCraftMaterial);
if (agentInterface != null && agentInterface->IsAgentActive())
{
var addonId = agentInterface->GetAddonID();
var addonId = agentInterface->GetAddonId();
if (addonId == 0)
return null;
@ -224,88 +218,11 @@ partial class WorkshopPlugin
if (item == null)
continue;
if (item->ItemID == itemId && item->Quantity >= count)
if (item->ItemId == itemId && item->Quantity >= count)
return true;
}
}
return false;
}
public bool HasFreeInventorySlot() => CountFreeInventorySlots() > 0;
public unsafe int CountFreeInventorySlots()
{
var inventoryManger = InventoryManager.Instance();
if (inventoryManger == null)
return 0;
int count = 0;
for (InventoryType t = InventoryType.Inventory1; t <= InventoryType.Inventory4; ++t)
{
var container = inventoryManger->GetInventoryContainer(t);
for (int i = 0; i < container->Size; ++i)
{
var item = container->GetInventorySlot(i);
if (item == null || item->ItemID == 0)
++count;
}
}
return count;
}
public unsafe int CountInventorySlotsWithCondition(uint itemId, Predicate<int> predicate)
{
ArgumentNullException.ThrowIfNull(predicate);
var inventoryManager = InventoryManager.Instance();
if (inventoryManager == null)
return 0;
int count = 0;
for (InventoryType t = InventoryType.Inventory1; t <= InventoryType.Inventory4; ++t)
{
var container = inventoryManager->GetInventoryContainer(t);
for (int i = 0; i < container->Size; ++i)
{
var item = container->GetInventorySlot(i);
if (item == null || item->ItemID == 0)
continue;
if (item->ItemID == itemId && predicate((int)item->Quantity))
++count;
}
}
return count;
}
public unsafe int DetermineMaxStackSize(uint itemId)
{
var inventoryManger = InventoryManager.Instance();
if (inventoryManger == null)
return 0;
int max = 0;
for (InventoryType t = InventoryType.Inventory1; t <= InventoryType.Inventory4; ++t)
{
var container = inventoryManger->GetInventoryContainer(t);
for (int i = 0; i < container->Size; ++i)
{
var item = container->GetInventorySlot(i);
if (item == null || item->ItemID == 0)
return 99;
if (item->ItemID == itemId)
{
max += (999 - (int)item->Quantity);
if (max >= 99)
break;
}
}
}
return Math.Min(99, max);
}
}

View File

@ -25,7 +25,7 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
internal readonly IReadOnlyList<ushort> WorkshopTerritories = new ushort[] { 423, 424, 425, 653, 984 }.AsReadOnly();
private readonly WindowSystem _windowSystem = new WindowSystem(nameof(WorkshopPlugin));
private readonly DalamudPluginInterface _pluginInterface;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly IGameGui _gameGui;
private readonly IFramework _framework;
private readonly ICondition _condition;
@ -50,7 +50,7 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
private DateTime _continueAt = DateTime.MinValue;
private DateTime _fallbackAt = DateTime.MaxValue;
public WorkshopPlugin(DalamudPluginInterface pluginInterface, IGameGui gameGui, IFramework framework,
public WorkshopPlugin(IDalamudPluginInterface pluginInterface, IGameGui gameGui, IFramework framework,
ICondition condition, IClientState clientState, IObjectTable objectTable, IDataManager dataManager,
ICommandManager commandManager, IPluginLog pluginLog, IAddonLifecycle addonLifecycle, IChatGui chatGui,
ITextureProvider textureProvider)
@ -72,14 +72,14 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
_gameStrings = new(dataManager, _pluginLog);
_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);
_configWindow = new(_pluginInterface, _configuration);
_windowSystem.AddWindow(_configWindow);
_repairKitWindow = new(this, _pluginLog, _gameGui, addonLifecycle, _configuration,
_repairKitWindow = new(_pluginLog, _gameGui, addonLifecycle, _configuration,
_externalPluginHandler);
_windowSystem.AddWindow(_repairKitWindow);
_ceruleumTankWindow = new(this, _pluginLog, _gameGui, addonLifecycle, _configuration,
_ceruleumTankWindow = new(_pluginLog, _gameGui, addonLifecycle, _configuration,
_externalPluginHandler, _chatGui);
_windowSystem.AddWindow(_ceruleumTankWindow);

View File

@ -1,68 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Dalamud.NET.Sdk/11.0.0">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Version>5.5</Version>
<LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Version>7.0</Version>
<OutputPath>dist</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>portable</DebugType>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup>
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
</PropertyGroup>
<Import Project="..\LLib\LLib.targets"/>
<Import Project="..\LLib\RenameZip.targets"/>
<ItemGroup>
<ProjectReference Include="..\LLib\LLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
</ItemGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin" Condition="'$(Configuration)' == 'Release'">
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
</Target>
</Project>

View File

@ -4,12 +4,83 @@
"net8.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[2.1.12, )",
"resolved": "2.1.12",
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
"requested": "[11.0.0, )",
"resolved": "11.0.0",
"contentHash": "bjT7XUlhIJSmsE/O76b7weUX+evvGQctbQB8aKXt94o+oPWxHpCepxAGMs7Thow3AzCyqWs7cOpp9/2wcgRRQA=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",
"requested": "[1.1.1, )",
"resolved": "1.1.1",
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
"dependencies": {
"Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
"Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
"Microsoft.SourceLink.GitHub": "1.1.1",
"Microsoft.SourceLink.GitLab": "1.1.1"
}
},
"Microsoft.SourceLink.Gitea": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.SourceLink.AzureRepos.Git": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.Bitbucket.Git": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.GitLab": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"llib": {
"type": "Project"
"type": "Project",
"dependencies": {
"DalamudPackager": "[11.0.0, )"
}
}
}
}