using System.Collections.Generic; using System.Numerics; using Dalamud.Plugin.Services; using LLib.GameData; using LLib.Shop; using LLib.Shop.Model; using Lumina.Excel.Sheets; using Microsoft.Extensions.Logging; namespace Questionable.Controller.CustomDelivery; internal sealed class VendorResolverService(IDataManager dataManager, VendorResolver vendorResolver, NpcPositionCache npcPositionCache, ILogger logger) { internal readonly record struct IngredientInfo(uint ItemId, int CountPerCraft); internal readonly record struct RecipeInfo(uint RecipeId, uint CraftedItemId, IReadOnlyList Ingredients, EClassJob CraftingClass); internal readonly record struct VendorInfo(uint ShopId, uint VendorNpcId, ushort TerritoryId, Vector3 Position, int DialogOptionIndex); public RecipeInfo? ResolveRecipe(uint craftedItemId, EClassJob? preferredJob = null) { if (!dataManager.GetExcelSheet().TryGetRow(craftedItemId, out var row)) { logger.LogDebug("No RecipeLookup row for item {ItemId}", craftedItemId); return null; } (EClassJob, uint)[] array = new(EClassJob, uint)[8] { (EClassJob.Carpenter, row.CRP.RowId), (EClassJob.Blacksmith, row.BSM.RowId), (EClassJob.Armorer, row.ARM.RowId), (EClassJob.Goldsmith, row.GSM.RowId), (EClassJob.Leatherworker, row.LTW.RowId), (EClassJob.Weaver, row.WVR.RowId), (EClassJob.Alchemist, row.ALC.RowId), (EClassJob.Culinarian, row.CUL.RowId) }; (EClassJob, uint)[] array2; if (preferredJob.HasValue) { array2 = array; for (int i = 0; i < array2.Length; i++) { var (eClassJob, num) = array2[i]; if (eClassJob == preferredJob.Value && num != 0) { if (!dataManager.GetExcelSheet().TryGetRow(num, out var row2)) { break; } List list = CollectIngredients(row2); if (list.Count == 0) { break; } logger.LogDebug("Resolved recipe {RecipeId} ({ClassJob}) for item {ItemId} via preferred job: {Count} ingredient(s)", num, eClassJob, craftedItemId, list.Count); return new RecipeInfo(num, craftedItemId, list, eClassJob); } } } array2 = array; for (int i = 0; i < array2.Length; i++) { var (eClassJob2, num2) = array2[i]; if (num2 != 0 && dataManager.GetExcelSheet().TryGetRow(num2, out var row3)) { List list2 = CollectIngredients(row3); if (list2.Count != 0) { logger.LogDebug("Resolved recipe {RecipeId} ({ClassJob}) for item {ItemId}: {Count} ingredient(s)", num2, eClassJob2, craftedItemId, list2.Count); return new RecipeInfo(num2, craftedItemId, list2, eClassJob2); } } } logger.LogDebug("No valid recipe found for item {ItemId}", craftedItemId); return null; } public VendorInfo? ResolveVendor(uint ingredientId) { if (!npcPositionCache.IsBuilt) { logger.LogWarning("NPC position cache not built yet, cannot resolve a vendor for ingredient {IngredientId}", ingredientId); return null; } ShopSearchResult shopSearchResult = vendorResolver.FindGilVendor(ingredientId); if (shopSearchResult == null) { logger.LogDebug("No vendor with known position found for ingredient {IngredientId}", ingredientId); return null; } logger.LogDebug("Found vendor NPC {NpcId} selling ingredient {IngredientId} at shop {ShopId} in territory {TerritoryId} (dialog option {DialogOption})", shopSearchResult.NpcId, ingredientId, shopSearchResult.ShopId, shopSearchResult.TerritoryId, shopSearchResult.DialogOptionIndex); return new VendorInfo(shopSearchResult.ShopId, shopSearchResult.NpcId, shopSearchResult.TerritoryId, shopSearchResult.NpcPosition, shopSearchResult.DialogOptionIndex); } private static List CollectIngredients(Recipe recipe) { List list = new List(); for (int i = 0; i < recipe.Ingredient.Count; i++) { uint rowId = recipe.Ingredient[i].RowId; int num = recipe.AmountIngredient[i]; if (rowId != 0 && num > 0) { list.Add(new IngredientInfo(rowId, num)); } } return list; } }