From fa45ac072e2c461876680071b156ddb15b22bc3f Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Tue, 6 Aug 2024 11:53:03 -0700 Subject: [PATCH] Change to ExdSheets 2 --- Benchmark/Craftimizer.Benchmark.csproj | 2 +- Craftimizer/Craftimizer.csproj | 7 +- Craftimizer/ImGuiUtils.cs | 8 +-- Craftimizer/LuminaSheets.cs | 48 +++++-------- Craftimizer/Plugin.cs | 2 +- Craftimizer/SimulatorUtils.cs | 96 ++++++++++++-------------- Craftimizer/Utils/FoodStatus.cs | 21 +++--- Craftimizer/Utils/Gearsets.cs | 18 ++--- Craftimizer/Utils/RecipeData.cs | 52 ++++++-------- Craftimizer/Windows/MacroEditor.cs | 40 +++++++---- Craftimizer/Windows/RecipeNote.cs | 26 +++---- Craftimizer/Windows/SynthHelper.cs | 3 +- Craftimizer/packages.lock.json | 35 ++++++---- Simulator/Craftimizer.Simulator.csproj | 2 +- Solver/Craftimizer.Solver.csproj | 4 +- Test/Craftimizer.Test.csproj | 2 +- 16 files changed, 181 insertions(+), 185 deletions(-) diff --git a/Benchmark/Craftimizer.Benchmark.csproj b/Benchmark/Craftimizer.Benchmark.csproj index 615a83e..9d2fb8a 100644 --- a/Benchmark/Craftimizer.Benchmark.csproj +++ b/Benchmark/Craftimizer.Benchmark.csproj @@ -20,7 +20,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Craftimizer/Craftimizer.csproj b/Craftimizer/Craftimizer.csproj index 7bc840a..733561f 100644 --- a/Craftimizer/Craftimizer.csproj +++ b/Craftimizer/Craftimizer.csproj @@ -39,10 +39,11 @@ - - + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Craftimizer/ImGuiUtils.cs b/Craftimizer/ImGuiUtils.cs index 5145ff2..0211d54 100644 --- a/Craftimizer/ImGuiUtils.cs +++ b/Craftimizer/ImGuiUtils.cs @@ -290,7 +290,7 @@ internal static class ImGuiUtils } } - private sealed class SearchableComboData where T : class + private sealed class SearchableComboData where T : IEquatable { public readonly ImmutableArray items; public List filteredItems; @@ -314,7 +314,7 @@ internal static class ImGuiUtils public void SetItem(T selectedItem) { - if (this.selectedItem != selectedItem) + if (!this.selectedItem.Equals(selectedItem)) { input = GetString(selectedItem); this.selectedItem = selectedItem; @@ -364,14 +364,14 @@ internal static class ImGuiUtils } private static readonly Dictionary ComboData = []; - private static SearchableComboData GetComboData(uint comboKey, IEnumerable items, T selectedItem, Func getString) where T : class => + private static SearchableComboData GetComboData(uint comboKey, IEnumerable items, T selectedItem, Func getString) where T : IEquatable => (SearchableComboData)( ComboData.TryGetValue(comboKey, out var data) ? data : ComboData[comboKey] = new SearchableComboData(items, selectedItem, getString)); // https://github.com/ocornut/imgui/issues/718#issuecomment-1563162222 - public static bool SearchableCombo(string id, ref T selectedItem, IEnumerable items, ImFontPtr selectableFont, float width, Func getString, Func getId, Action draw) where T : class + public static bool SearchableCombo(string id, ref T selectedItem, IEnumerable items, ImFontPtr selectableFont, float width, Func getString, Func getId, Action draw) where T : IEquatable { var comboKey = ImGui.GetID(id); var data = GetComboData(comboKey, items, selectedItem, getString); diff --git a/Craftimizer/LuminaSheets.cs b/Craftimizer/LuminaSheets.cs index 17b8f69..110bb6e 100644 --- a/Craftimizer/LuminaSheets.cs +++ b/Craftimizer/LuminaSheets.cs @@ -1,37 +1,27 @@ -using Dalamud.Game; +using Dalamud.Utility; using ExdSheets; -using Lumina.Excel; -using System.Collections.Concurrent; +using ExdSheets.Sheets; +using Lumina.Data; namespace Craftimizer.Plugin; public static class LuminaSheets { - public static readonly ExcelSheet RecipeSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet ActionSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet CraftActionSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet StatusSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet AddonSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet ClassJobSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet ItemSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet ItemSheetEnglish = Service.DataManager.GetExcelSheet(ClientLanguage.English)!; - public static readonly ExcelSheet ENpcResidentSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet LevelSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet QuestSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet MateriaSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet BaseParamSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet ItemFoodSheet = Service.DataManager.GetExcelSheet()!; - public static readonly ExcelSheet SatisfactionSupplySheet = Service.DataManager.GetExcelSheet()!; + private static readonly Module Module = new(Service.DataManager.GameData, Service.DataManager.Language.ToLumina()); - private static ConcurrentDictionary<(ExcelSheetImpl, uint), uint> SubRowCountCache { get; } = new(); - public static uint? GetSubRowCount(this ExcelSheet sheet, uint row) where T : ExcelRow - { - if (SubRowCountCache.TryGetValue((sheet, row), out var count)) - return count; - var parser = sheet.GetRowParser(row); - if (parser == null) - return null; - SubRowCountCache.TryAdd((sheet, row), parser.RowCount); - return parser.RowCount; - } + public static readonly Sheet RecipeSheet = Module.GetSheet(); + public static readonly Sheet ActionSheet = Module.GetSheet(); + public static readonly Sheet CraftActionSheet = Module.GetSheet(); + public static readonly Sheet StatusSheet = Module.GetSheet(); + public static readonly Sheet AddonSheet = Module.GetSheet(); + public static readonly Sheet ClassJobSheet = Module.GetSheet(); + public static readonly Sheet ItemSheet = Module.GetSheet(); + public static readonly Sheet ItemSheetEnglish = Module.GetSheet(Language.English)!; + public static readonly Sheet ENpcResidentSheet = Module.GetSheet(); + public static readonly Sheet LevelSheet = Module.GetSheet(); + public static readonly Sheet QuestSheet = Module.GetSheet(); + public static readonly Sheet MateriaSheet = Module.GetSheet(); + public static readonly Sheet BaseParamSheet = Module.GetSheet(); + public static readonly Sheet ItemFoodSheet = Module.GetSheet(); + public static readonly Sheet SatisfactionSupplySheet = Module.GetSheet(); } diff --git a/Craftimizer/Plugin.cs b/Craftimizer/Plugin.cs index 7798f5b..178333c 100644 --- a/Craftimizer/Plugin.cs +++ b/Craftimizer/Plugin.cs @@ -62,7 +62,7 @@ public sealed class Plugin : IDalamudPlugin SynthHelperWindow = new(); ListWindow = new(); - // Trigger static constructors so a huge hitch doesn't occur on first RecipeNote frame. + // Trigger static constructors so a hitch doesn't occur on first RecipeNote frame. FoodStatus.Initialize(); ActionUtils.Initialize(); diff --git a/Craftimizer/SimulatorUtils.cs b/Craftimizer/SimulatorUtils.cs index 2ef4d1d..729bde4 100644 --- a/Craftimizer/SimulatorUtils.cs +++ b/Craftimizer/SimulatorUtils.cs @@ -1,21 +1,19 @@ using Craftimizer.Simulator; using Craftimizer.Simulator.Actions; -using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game.UI; -using ExdSheets; using System; -using System.Globalization; using System.Linq; using System.Numerics; using System.Text; -using Action = ExdSheets.Action; +using Action = ExdSheets.Sheets.Action; using ActionType = Craftimizer.Simulator.Actions.ActionType; using ClassJob = Craftimizer.Simulator.ClassJob; using Condition = Craftimizer.Simulator.Condition; -using Status = ExdSheets.Status; -using Dalamud.Interface.Textures; +using Status = ExdSheets.Sheets.Status; using Craftimizer.Utils; +using ExdSheets.Sheets; +using Lumina.Text.ReadOnly; +using Lumina.Text.Payloads; namespace Craftimizer.Plugin; @@ -31,33 +29,33 @@ internal static class ActionUtils foreach (var actionType in actionTypes) { var actionId = actionType.Base().ActionId; - if (LuminaSheets.CraftActionSheet.GetRow(actionId) is CraftAction baseCraftAction) + if (LuminaSheets.CraftActionSheet.TryGetRow(actionId) is { } baseCraftAction) { foreach (var classJob in classJobs) { ActionRows[(int)actionType, (int)classJob] = (classJob switch { - ClassJob.Carpenter => baseCraftAction.CRP.Value!, - ClassJob.Blacksmith => baseCraftAction.BSM.Value!, - ClassJob.Armorer => baseCraftAction.ARM.Value!, - ClassJob.Goldsmith => baseCraftAction.GSM.Value!, - ClassJob.Leatherworker => baseCraftAction.LTW.Value!, - ClassJob.Weaver => baseCraftAction.WVR.Value!, - ClassJob.Alchemist => baseCraftAction.ALC.Value!, - ClassJob.Culinarian => baseCraftAction.CUL.Value!, + ClassJob.Carpenter => baseCraftAction.CRP.Value, + ClassJob.Blacksmith => baseCraftAction.BSM.Value, + ClassJob.Armorer => baseCraftAction.ARM.Value, + ClassJob.Goldsmith => baseCraftAction.GSM.Value, + ClassJob.Leatherworker => baseCraftAction.LTW.Value, + ClassJob.Weaver => baseCraftAction.WVR.Value, + ClassJob.Alchemist => baseCraftAction.ALC.Value, + ClassJob.Culinarian => baseCraftAction.CUL.Value, _ => baseCraftAction }, null); } } - if (LuminaSheets.ActionSheet.GetRow(actionId) is Action baseAction) + if (LuminaSheets.ActionSheet.TryGetRow(actionId) is { } baseAction) { var possibleActions = LuminaSheets.ActionSheet.Where(r => r.Icon == baseAction.Icon && - r.ActionCategory.Row == baseAction.ActionCategory.Row && - r.Name.RawString.Equals(baseAction.Name.RawString, StringComparison.Ordinal)); + r.ActionCategory.RowId == baseAction.ActionCategory.RowId && + r.Name.Equals(baseAction.Name)); foreach (var classJob in classJobs) - ActionRows[(int)actionType, (int)classJob] = (null, possibleActions.First(r => r.ClassJobCategory.Value?.IsClassJob(classJob) ?? false)); + ActionRows[(int)actionType, (int)classJob] = (null, possibleActions.First(r => r.ClassJobCategory.ValueNullable?.IsClassJob(classJob) ?? false)); } } } @@ -70,32 +68,20 @@ internal static class ActionUtils public static uint GetId(this ActionType me, ClassJob classJob) { var (craftAction, action) = GetActionRow(me, classJob); - if (craftAction != null) - return craftAction.RowId; - if (action != null) - return action.RowId; - return 0; + return craftAction?.RowId ?? action?.RowId ?? 0; } public static string GetName(this ActionType me, ClassJob classJob) { var (craftAction, action) = GetActionRow(me, classJob); - if (craftAction != null) - return craftAction.Name.ToDalamudString().TextValue; - if (action != null) - return action.Name.ToDalamudString().TextValue; - return "Unknown"; + return (craftAction?.Name ?? action?.Name)?.AsSpan().ExtractText() ?? "Unknown"; } public static ITextureIcon GetIcon(this ActionType me, ClassJob classJob) { var (craftAction, action) = GetActionRow(me, classJob); - if (craftAction != null) - return Service.IconManager.GetIconCached(craftAction.Icon); - if (action != null) - return Service.IconManager.GetIconCached(action.Icon); - // Old "Steady Hand" action icon - return Service.IconManager.GetIconCached(1953); + // 1953 = Old "Steady Hand" action icon + return Service.IconManager.GetIconCached(craftAction?.Icon ?? action?.Icon ?? 1953); } public static ActionType? GetActionTypeFromId(uint actionId, ClassJob classJob, bool isCraftAction) @@ -155,18 +141,18 @@ internal static class ClassJobUtils PlayerState.Instance()->ClassJobLevels[me.GetExpArrayIdx()]; public static unsafe bool CanPlayerUseManipulation(this ClassJob me) => - UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(ActionType.Manipulation.GetActionRow(me).Action!.UnlockLink.Row); + UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(ActionType.Manipulation.GetActionRow(me).Action!.Value.UnlockLink.RowId); public static string GetName(this ClassJob me) { - var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex())!; - return job.Name.ToDalamudString().TextValue.ToLowerInvariant(); + var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex()); + return job.Name.ExtractText().ToLowerInvariant(); } public static string GetNameArticle(this ClassJob me) { - var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex())!; - if (job.SheetLanguage == Lumina.Data.Language.English) + var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex()); + if (LuminaSheets.ClassJobSheet.Language == Lumina.Data.Language.English) { if (me is ClassJob.Alchemist or ClassJob.Armorer) return "an"; @@ -176,12 +162,12 @@ internal static class ClassJobUtils public static string GetAbbreviation(this ClassJob me) { - var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex())!; - return job.Abbreviation.ToDalamudString().TextValue; + var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex()); + return job.Abbreviation.ExtractText(); } public static Quest GetUnlockQuest(this ClassJob me) => - LuminaSheets.QuestSheet.GetRow(65720 + (uint)me) ?? throw new ArgumentException($"Could not get unlock quest for {me}", nameof(me)); + LuminaSheets.QuestSheet.GetRow(65720 + (uint)me); public static ushort GetIconId(this ClassJob me) => (ushort)(62000 + me.GetClassJobIndex()); @@ -295,15 +281,23 @@ internal static class ConditionUtils } public static string Name(this Condition me) => - LuminaSheets.AddonSheet.GetRow(me.AddonIds().Name)!.Text.ToDalamudString().TextValue; + LuminaSheets.AddonSheet.GetRow(me.AddonIds().Name).Text.ExtractText(); public static string Description(this Condition me, bool isRelic) { - var text = LuminaSheets.AddonSheet.GetRow(me.AddonIds().Description)!.Text.ToDalamudString(); - for (var i = 0; i < text.Payloads.Count; ++i) - if (text.Payloads[i] is RawPayload) - text.Payloads[i] = new TextPayload(isRelic ? "1.75" : "1.5"); - return text.TextValue; + var text = LuminaSheets.AddonSheet.GetRow(me.AddonIds().Description).Text; + if (!text.Any(p => p is { Type: ReadOnlySePayloadType.Macro, MacroCode: MacroCode.Float })) + return text.ExtractText(); + + ReadOnlySeString finalText = new(); + foreach (var payload in text) + { + if (payload is { Type: ReadOnlySePayloadType.Macro, MacroCode: MacroCode.Float }) + finalText += new ReadOnlySePayload(ReadOnlySePayloadType.Text, default, Encoding.UTF8.GetBytes(isRelic ? "1.75" : "1.5")); + else + finalText += payload; + } + return finalText.ExtractText(); } } @@ -349,7 +343,7 @@ internal static class EffectUtils { var status = me.Status(); var name = new StringBuilder(); - name.Append(status.Name.ToDalamudString().TextValue); + name.Append(status.Name.ExtractText()); if (status.MaxStacks != 0) name.Append($" {strength}"); if (!status.IsPermanent) diff --git a/Craftimizer/Utils/FoodStatus.cs b/Craftimizer/Utils/FoodStatus.cs index 442244c..8629651 100644 --- a/Craftimizer/Utils/FoodStatus.cs +++ b/Craftimizer/Utils/FoodStatus.cs @@ -1,5 +1,5 @@ using Craftimizer.Plugin; -using ExdSheets; +using ExdSheets.Sheets; using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; @@ -25,28 +25,27 @@ public static class FoodStatus var medicines = new Dictionary(); foreach (var item in LuminaSheets.ItemSheet) { - var isFood = item.ItemUICategory.Row == 46; - var isMedicine = item.ItemUICategory.Row == 44; + var isFood = item.ItemUICategory.RowId == 46; + var isMedicine = item.ItemUICategory.RowId == 44; if (!isFood && !isMedicine) continue; - if (item.ItemAction.Value == null) + if (item.ItemAction.ValueNullable is not { } itemAction) continue; - if (!(item.ItemAction.Value.Type is 844 or 845 or 846)) + if (itemAction.Type is not (844 or 845 or 846)) continue; - var itemFood = LuminaSheets.ItemFoodSheet.GetRow(item.ItemAction.Value.Data[1]); - if (itemFood == null) + if (LuminaSheets.ItemFoodSheet.TryGetRow(itemAction.Data[1]) is not { } itemFood) continue; FoodStat? craftsmanship = null, control = null, cp = null; foreach (var stat in itemFood.Params) { - if (stat.BaseParam.Row == 0) + if (stat.BaseParam.RowId == 0) continue; var foodStat = new FoodStat(stat.IsRelative, stat.Value, stat.Max, stat.ValueHQ, stat.MaxHQ); - switch (stat.BaseParam.Row) + switch (stat.BaseParam.RowId) { case Gearsets.ParamCraftsmanship: craftsmanship = foodStat; break; case Gearsets.ParamControl: control = foodStat; break; @@ -71,8 +70,8 @@ public static class FoodStatus FoodItems = foods.ToFrozenDictionary(); MedicineItems = medicines.ToFrozenDictionary(); - FoodOrder = FoodItems.OrderByDescending(a => a.Value.Item.LevelItem.Row).Select(a => a.Key).ToImmutableArray(); - MedicineOrder = MedicineItems.OrderByDescending(a => a.Value.Item.LevelItem.Row).Select(a => a.Key).ToImmutableArray(); + FoodOrder = FoodItems.OrderByDescending(a => a.Value.Item.LevelItem.RowId).Select(a => a.Key).ToImmutableArray(); + MedicineOrder = MedicineItems.OrderByDescending(a => a.Value.Item.LevelItem.RowId).Select(a => a.Key).ToImmutableArray(); } public static void Initialize() { } diff --git a/Craftimizer/Utils/Gearsets.cs b/Craftimizer/Utils/Gearsets.cs index 449886f..6bed176 100644 --- a/Craftimizer/Utils/Gearsets.cs +++ b/Craftimizer/Utils/Gearsets.cs @@ -3,7 +3,7 @@ using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using ExdSheets; +using ExdSheets.Sheets; using System; using System.Linq; using Craftimizer.Plugin; @@ -62,10 +62,10 @@ public static unsafe class Gearsets } foreach (var statIncrease in item.BaseParam.Zip(item.BaseParamValue)) - IncreaseStat(statIncrease.First.Row, statIncrease.Second); + IncreaseStat(statIncrease.First.RowId, statIncrease.Second); if (gearsetItem.IsHq) foreach (var statIncrease in item.BaseParamSpecial.Zip(item.BaseParamValueSpecial)) - IncreaseStat(statIncrease.First.Row, statIncrease.Second); + IncreaseStat(statIncrease.First.RowId, statIncrease.Second); foreach (var gearsetMateria in gearsetItem.Materia) { @@ -73,7 +73,7 @@ public static unsafe class Gearsets continue; var materia = LuminaSheets.MateriaSheet.GetRow(gearsetMateria.Type)!; - IncreaseStat(materia.BaseParam.Row, materia.Value[gearsetMateria.Grade]); + IncreaseStat(materia.BaseParam.RowId, materia.Value[gearsetMateria.Grade]); } cp = Math.Min(cp, CalculateParamCap(item, ParamCP)); @@ -125,17 +125,17 @@ public static unsafe class Gearsets return false; var luminaItem = LuminaSheets.ItemSheet.GetRow(item.ItemId)!; - // Soul Crystal ItemUICategory DoH Category - return luminaItem.ItemUICategory.Row == 62 && luminaItem.ClassJobUse.Value!.ClassJobCategory.Row == 33; + // Soul Crystal ItemUICategory DoH Category + return luminaItem.ItemUICategory.RowId == 62 && luminaItem.ClassJobUse.Value.ClassJobCategory.RowId == 33; } public static bool IsSplendorousTool(GearsetItem item) => - LuminaSheets.ItemSheetEnglish.GetRow(item.ItemId)!.Description.ToDalamudString().TextValue.Contains("Increases to quality are 1.75 times higher than normal when material condition is Good.", StringComparison.Ordinal); + LuminaSheets.ItemSheetEnglish.GetRow(item.ItemId).Description.ExtractText().Contains("Increases to quality are 1.75 times higher than normal when material condition is Good.", StringComparison.Ordinal); // https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/client/src/app/modules/gearsets/materia.service.ts#L265 private static int CalculateParamCap(Item item, uint paramId) { - var ilvl = item.LevelItem.Value!; + var ilvl = item.LevelItem.Value; var param = LuminaSheets.BaseParamSheet.GetRow(paramId)!; var baseValue = paramId switch @@ -146,7 +146,7 @@ public static unsafe class Gearsets _ => 0 }; // https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/data-extraction/src/extractors/items.extractor.ts#L6 - var slotMod = item.EquipSlotCategory.Row switch + var slotMod = item.EquipSlotCategory.RowId switch { 1 => param.OneHandWeaponPercent, // column 4 2 => param.OffHandPercent, // column 5 diff --git a/Craftimizer/Utils/RecipeData.cs b/Craftimizer/Utils/RecipeData.cs index f7bf9fb..8d8f7c4 100644 --- a/Craftimizer/Utils/RecipeData.cs +++ b/Craftimizer/Utils/RecipeData.cs @@ -1,7 +1,6 @@ using Craftimizer.Plugin; using Craftimizer.Simulator; -using ExdSheets; -using Lumina.Excel; +using ExdSheets.Sheets; using System; using System.Collections.Generic; using System.Linq; @@ -18,7 +17,7 @@ public sealed record RecipeData public ClassJob ClassJob { get; } public RecipeInfo RecipeInfo { get; } - public bool IsCollectable => Recipe.ItemResult.Value?.AlwaysCollectable ?? false; + public bool IsCollectable => Recipe.ItemResult.ValueNullable?.AlwaysCollectable ?? false; public IReadOnlyList? CollectableThresholds { get; } public IReadOnlyList<(Item Item, int Amount)> Ingredients { get; } public int MaxStartingQuality { get; } @@ -28,11 +27,11 @@ public sealed record RecipeData { RecipeId = recipeId; - Recipe = LuminaSheets.RecipeSheet.GetRow(recipeId) ?? + Recipe = LuminaSheets.RecipeSheet.TryGetRow(recipeId) ?? throw new ArgumentException($"Invalid recipe id {recipeId}", nameof(recipeId)); - Table = Recipe.RecipeLevelTable.Value!; - ClassJob = (ClassJob)Recipe.CraftType.Row; + Table = Recipe.RecipeLevelTable.Value; + ClassJob = (ClassJob)Recipe.CraftType.RowId; RecipeInfo = new() { IsExpert = Recipe.IsExpert, @@ -48,49 +47,44 @@ public sealed record RecipeData }; int[]? thresholds = null; - if (Recipe.CollectableMetadata is LazyRow { Value: { } row }) + if (Recipe.CollectableMetadata.TryGetValue() is { } row) thresholds = [row.LowCollectability, row.MidCollectability, row.HighCollectability]; - else if (Recipe.CollectableMetadata is LazyRow { Value: { } row2 }) + else if (Recipe.CollectableMetadata.TryGetValue() is { } row2) { foreach (var entry in row2.HWDCrafterSupplyParams) { - if (entry.ItemTradeIn.Row == Recipe.ItemResult.Row) + if (entry.ItemTradeIn.RowId == Recipe.ItemResult.RowId) { thresholds = [entry.BaseCollectableRating, entry.MidCollectableRating, entry.HighCollectableRating]; break; } } } - else if (Recipe.CollectableMetadata is LazyRow { } row4) + else if (Recipe.CollectableMetadata.TryGetValue() is { } row3) { - var subRowCount = LuminaSheets.SatisfactionSupplySheet.GetSubRowCount(row4.Row); - if (subRowCount is { } subRowValue) + var subrowCount = LuminaSheets.SatisfactionSupplySheet.GetSubrowCount(row3.RowId); + for (ushort i = 0; i < subrowCount; ++i) { - for (uint i = 0; i < subRowValue; ++i) + var subrow = LuminaSheets.SatisfactionSupplySheet.GetRow(row3.RowId, i); + if (subrow.Item.RowId == Recipe.ItemResult.RowId) { - var subRow = LuminaSheets.SatisfactionSupplySheet.GetRow(row4.Row, i); - if (subRow == null) - continue; - if (subRow.Item.Row == Recipe.ItemResult.Row) - { - thresholds = [subRow.CollectabilityLow, subRow.CollectabilityMid, subRow.CollectabilityHigh]; - break; - } + thresholds = [subrow.CollectabilityLow, subrow.CollectabilityMid, subrow.CollectabilityHigh]; + break; } } } - else if (Recipe.CollectableMetadata is LazyRow { Value: { } row5 }) + else if (Recipe.CollectableMetadata.TryGetValue() is { } row5) { foreach (var item in row5.Item) { - if (item.ItemId.Row == Recipe.ItemResult.Row) + if (item.ItemId.RowId == Recipe.ItemResult.RowId) { thresholds = [0, item.CollectabilityMid, item.CollectabilityHigh]; break; } } } - else if (Recipe.CollectableMetadata is LazyRow { Value: { } row6 }) + else if (Recipe.CollectableMetadata.TryGetValue() is { } row6) { if (row6.CollectabilityHigh != 0) thresholds = [row6.CollectabilityLow, row6.CollectabilityMid, row6.CollectabilityHigh]; @@ -102,12 +96,12 @@ public sealed record RecipeData Ingredients = Recipe.Ingredient.Zip(Recipe.AmountIngredient) .Take(6) - .Where(i => i.First.Value != null) - .Select(i => (i.First.Value!, (int)i.Second)) + .Where(i => i.First.IsValid) + .Select(i => (i.First.Value, (int)i.Second)) .ToList(); MaxStartingQuality = (int)Math.Floor(Recipe.MaterialQualityFactor * RecipeInfo.MaxQuality / 100f); - TotalHqILvls = (int)Ingredients.Where(i => i.Item.CanBeHq).Sum(i => i.Item.LevelItem.Row * i.Amount); + TotalHqILvls = (int)Ingredients.Where(i => i.Item.CanBeHq).Sum(i => i.Item.LevelItem.RowId * i.Amount); } public int CalculateItemStartingQuality(int itemIdx, int amount) @@ -120,7 +114,7 @@ public sealed record RecipeData if (!ingredient.Item.CanBeHq) return 0; - var iLvls = ingredient.Item.LevelItem.Row * amount; + var iLvls = ingredient.Item.LevelItem.RowId * amount; return (int)Math.Floor((float)iLvls / TotalHqILvls * MaxStartingQuality); } @@ -129,7 +123,7 @@ public sealed record RecipeData if (TotalHqILvls == 0) return 0; - var iLvls = Ingredients.Zip(hqQuantities).Sum(i => i.First.Item.LevelItem.Row * i.Second); + var iLvls = Ingredients.Zip(hqQuantities).Sum(i => i.First.Item.LevelItem.RowId * i.Second); return (int)Math.Floor((float)iLvls / TotalHqILvls * MaxStartingQuality); } diff --git a/Craftimizer/Windows/MacroEditor.cs b/Craftimizer/Windows/MacroEditor.cs index 01732aa..ffc2927 100644 --- a/Craftimizer/Windows/MacroEditor.cs +++ b/Craftimizer/Windows/MacroEditor.cs @@ -12,7 +12,6 @@ using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; -using Dalamud.Utility; using ImGuiNET; using System; using System.Collections.Generic; @@ -23,6 +22,7 @@ using System.Threading; using System.Threading.Tasks; using Sim = Craftimizer.Simulator.Simulator; using SimNoRandom = Craftimizer.Simulator.SimulatorNoRandom; +using Recipe = ExdSheets.Sheets.Recipe; namespace Craftimizer.Windows; @@ -376,7 +376,7 @@ public sealed class MacroEditor : Window, IDisposable } ImGui.SameLine(0, 5); { - var manipLevel = ActionType.Manipulation.GetActionRow(RecipeData.ClassJob).Action!.ClassJobLevel; + var manipLevel = ActionType.Manipulation.GetActionRow(RecipeData.ClassJob).Action!.Value.ClassJobLevel; using (var d = ImRaii.Disabled(manipLevel > CharacterStats.Level)) { var v = CharacterStats.CanUseManipulation && manipLevel <= CharacterStats.Level; @@ -586,7 +586,7 @@ public sealed class MacroEditor : Window, IDisposable if (input.ItemId == 0) return "None"; - var name = LuminaSheets.ItemSheet.GetRow(input.ItemId)?.Name.ToDalamudString().ToString() ?? $"Unknown ({input.ItemId})"; + var name = LuminaSheets.ItemSheet.TryGetRow(input.ItemId)?.Name.ExtractText() ?? $"Unknown ({input.ItemId})"; return input.IsHQ ? $"{name} (HQ)" : name; } @@ -727,6 +727,16 @@ public sealed class MacroEditor : Window, IDisposable return (int)Math.Ceiling((y - b) / (c + 1)); } + private readonly struct RecipeWrapper(Recipe recipe) : IEquatable + { + public readonly Recipe Recipe = recipe; + + public bool Equals(RecipeWrapper other) => + Recipe.RowId == other.Recipe.RowId; + } + + private readonly List searchableRecipes = LuminaSheets.RecipeSheet.Where(r => r.RecipeLevelTable.RowId != 0 && r.ItemResult.RowId != 0).Select(r => new RecipeWrapper(r)).ToList(); + private bool DrawRecipeParams() { var oldStartingQuality = StartingQuality; @@ -752,29 +762,29 @@ public sealed class MacroEditor : Window, IDisposable (isExpert ? badgeSize.X + 3 : 0); ImGui.AlignTextToFramePadding(); - ImGui.Image(Service.IconManager.GetIconCached(RecipeData.Recipe.ItemResult.Value!.Icon).ImGuiHandle, new Vector2(imageSize)); + ImGui.Image(Service.IconManager.GetIconCached(RecipeData.Recipe.ItemResult.Value.Icon).ImGuiHandle, new Vector2(imageSize)); ImGui.SameLine(0, 5); ushort? newRecipe = null; { - var recipe = RecipeData.Recipe; + var recipe = new RecipeWrapper(RecipeData.Recipe); using var lockedFontHandle = AxisFont.Available ? AxisFont.Lock() : null; var fontHandle = lockedFontHandle?.ImFont ?? ImGui.GetFont(); if (ImGuiUtils.SearchableCombo( "combo", ref recipe, - LuminaSheets.RecipeSheet.Where(r => r.RecipeLevelTable.Row != 0 && r.ItemResult.Row != 0), + searchableRecipes, fontHandle, ImGui.GetContentRegionAvail().X - rightSideWidth, - r => r.ItemResult.Value!.Name.ToDalamudString().ToString(), - r => r.RowId.ToString(), + r => r.Recipe.ItemResult.Value.Name.ExtractText(), + r => r.Recipe.RowId.ToString(), r => { - ImGui.TextUnformatted($"{r.ItemResult.Value!.Name.ToDalamudString()}"); + ImGui.TextUnformatted($"{r.Recipe.ItemResult.Value.Name.ExtractText()}"); - var classJob = (ClassJob)r.CraftType.Row; - var textLevel = SqText.LevelPrefix.ToIconChar() + SqText.ToLevelString(r.RecipeLevelTable.Value!.ClassJobLevel); + var classJob = (ClassJob)r.Recipe.CraftType.RowId; + var textLevel = SqText.LevelPrefix.ToIconChar() + SqText.ToLevelString(r.Recipe.RecipeLevelTable.Value!.ClassJobLevel); var textLevelSize = ImGui.CalcTextSize(textLevel); ImGui.SameLine(); @@ -796,7 +806,7 @@ public sealed class MacroEditor : Window, IDisposable ImGui.TextUnformatted(textLevel); })) { - newRecipe = (ushort)recipe.RowId; + newRecipe = (ushort)recipe.Recipe.RowId; } } @@ -923,10 +933,10 @@ public sealed class MacroEditor : Window, IDisposable { var perItem = RecipeData.CalculateItemStartingQuality(idx, 1); var total = RecipeData.CalculateItemStartingQuality(idx, hqCount); - ImGuiUtils.Tooltip($"{ingredient.Item.Name.ToDalamudString()} {SeIconChar.HighQuality.ToIconString()}\n+{perItem} Quality/Item{(total > 0 ? $"\n+{total} Quality" : "")}"); + ImGuiUtils.Tooltip($"{ingredient.Item.Name.ExtractText()} {SeIconChar.HighQuality.ToIconString()}\n+{perItem} Quality/Item{(total > 0 ? $"\n+{total} Quality" : "")}"); } else if (ingredient.Amount != 0) - ImGuiUtils.Tooltip($"{ingredient.Item.Name.ToDalamudString()}"); + ImGuiUtils.Tooltip($"{ingredient.Item.Name.ExtractText()}"); } ImGui.SameLine(0, 5); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - (5 + ImGui.CalcTextSize("/").X + 5 + ImGui.CalcTextSize($"99").X)); @@ -1137,7 +1147,7 @@ public sealed class MacroEditor : Window, IDisposable { var status = effect.Status(); using var _reset = ImRaii.DefaultFont(); - ImGuiUtils.Tooltip($"{status.Name.ToDalamudString()}\n{status.Description.ToDalamudString()}"); + ImGuiUtils.Tooltip($"{status.Name.ExtractText()}\n{status.Description.ExtractText()}"); } ImGui.SameLine(); } diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs index 961e409..5872d75 100644 --- a/Craftimizer/Windows/RecipeNote.cs +++ b/Craftimizer/Windows/RecipeNote.cs @@ -527,7 +527,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable ImGuiUtils.TextCentered($"You do not have {RecipeData.ClassJob.GetName()} unlocked."); ImGui.Separator(); var unlockQuest = RecipeData.ClassJob.GetUnlockQuest(); - var (questGiver, questTerritory, questLocation, mapPayload) = ResolveLevelData(unlockQuest.IssuerLocation.Row); + var (questGiver, questTerritory, questLocation, mapPayload) = ResolveLevelData(unlockQuest.IssuerLocation.RowId); var unlockText = $"Unlock it from {questGiver}"; ImGuiUtils.AlignCentered(ImGui.CalcTextSize(unlockText).X + 5 + ImGui.GetFrameHeight()); @@ -580,7 +580,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable case CraftableStatus.RequiredItem: { var item = RecipeData.Recipe.ItemRequired.Value!; - var itemName = item.Name.ToDalamudString().ToString(); + var itemName = item.Name.ExtractText(); var imageSize = ImGui.GetFrameHeight(); ImGuiUtils.TextCentered($"You are missing the required equipment."); @@ -594,7 +594,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable case CraftableStatus.RequiredStatus: { var status = RecipeData.Recipe.StatusRequired.Value!; - var statusName = status.Name.ToDalamudString().ToString(); + var statusName = status.Name.ExtractText(); var statusIcon = Service.IconManager.GetIconCached(status.Icon); var imageSize = new Vector2(ImGui.GetFrameHeight() * (statusIcon.AspectRatio ?? 1), ImGui.GetFrameHeight()); @@ -1073,16 +1073,16 @@ public sealed unsafe class RecipeNote : Window, IDisposable return CraftableStatus.SpecialistRequired; var itemRequired = RecipeData.Recipe.ItemRequired; - if (itemRequired.Row != 0 && itemRequired.Value != null) + if (itemRequired.RowId != 0 && itemRequired.IsValid) { - if (!gearItems.Any(i => Gearsets.IsItem(i, itemRequired.Row))) + if (!gearItems.Any(i => Gearsets.IsItem(i, itemRequired.RowId))) return CraftableStatus.RequiredItem; } var statusRequired = RecipeData.Recipe.StatusRequired; - if (statusRequired.Row != 0 && statusRequired.Value != null) + if (statusRequired.RowId != 0 && statusRequired.IsValid) { - if (!Service.ClientState.LocalPlayer!.StatusList.Any(s => s.StatusId == statusRequired.Row)) + if (!Service.ClientState.LocalPlayer!.StatusList.Any(s => s.StatusId == statusRequired.RowId)) return CraftableStatus.RequiredStatus; } @@ -1097,24 +1097,24 @@ public sealed unsafe class RecipeNote : Window, IDisposable private static (string NpcName, string Territory, Vector2 MapLocation, MapLinkPayload Payload) ResolveLevelData(uint levelRowId) { - var level = LuminaSheets.LevelSheet.GetRow(levelRowId) ?? + var level = LuminaSheets.LevelSheet.TryGetRow(levelRowId) ?? throw new ArgumentNullException(nameof(levelRowId), $"Invalid level row {levelRowId}"); - var territory = level.Territory.Value!.PlaceName.Value!.Name.ToDalamudString().ToString(); + var territory = level.Territory.Value.PlaceName.Value.Name.ExtractText(); var location = WorldToMap2(new(level.X, level.Z), level.Map.Value!); - return (ResolveNpcResidentName(level.Object.Row), territory, location, new(level.Territory.Row, level.Map.Row, location.X, location.Y)); + return (ResolveNpcResidentName(level.Object.RowId), territory, location, new(level.Territory.RowId, level.Map.RowId, location.X, location.Y)); } - private static Vector2 WorldToMap2(Vector2 worldCoordinates, ExdSheets.Map map) + private static Vector2 WorldToMap2(Vector2 worldCoordinates, ExdSheets.Sheets.Map map) { return MapUtil.WorldToMap(worldCoordinates, map.OffsetX, map.OffsetY, map.SizeFactor); } private static string ResolveNpcResidentName(uint npcRowId) { - var resident = LuminaSheets.ENpcResidentSheet.GetRow(npcRowId) ?? + var resident = LuminaSheets.ENpcResidentSheet.TryGetRow(npcRowId) ?? throw new ArgumentNullException(nameof(npcRowId), $"Invalid npc row {npcRowId}"); - return resident.Singular.ToDalamudString().ToString(); + return resident.Singular.ExtractText(); } private static int? GetGearsetForJob(ClassJob job) diff --git a/Craftimizer/Windows/SynthHelper.cs b/Craftimizer/Windows/SynthHelper.cs index f474faf..43338f6 100644 --- a/Craftimizer/Windows/SynthHelper.cs +++ b/Craftimizer/Windows/SynthHelper.cs @@ -10,7 +10,6 @@ using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; -using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.UI; @@ -382,7 +381,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable { var status = effect.Status(); using var _reset = ImRaii.DefaultFont(); - ImGuiUtils.Tooltip($"{status.Name.ToDalamudString()}\n{status.Description.ToDalamudString()}"); + ImGuiUtils.Tooltip($"{status.Name.ExtractText()}\n{status.Description.ExtractText()}"); } ImGui.SameLine(); } diff --git a/Craftimizer/packages.lock.json b/Craftimizer/packages.lock.json index f147428..a0cedaa 100644 --- a/Craftimizer/packages.lock.json +++ b/Craftimizer/packages.lock.json @@ -10,18 +10,21 @@ }, "ExdSheets": { "type": "Direct", - "requested": "[1.2.2, )", - "resolved": "1.2.2", - "contentHash": "oS1oUBhlOpUWY1Wc3SwOLzfAMuSRzBn54I8p84euVUHrUEOzRZwA9vuNKNMAIY66eT1dpc/DxT+C7LNH0nK0jg==", + "requested": "[2.1.0, )", + "resolved": "2.1.0", + "contentHash": "SfvLFyL8LYsW4AzPLFVfFwtKPl9xegGraIe9oFar3IlWW8Z4kf5dkM+1CiSr/J+PQt4xt2QZc/ydZ4ibs3RosA==", "dependencies": { - "Lumina": "3.15.2" + "Lumina": "4.1.1" } }, "Lumina": { "type": "Direct", - "requested": "[3.15.2, )", - "resolved": "3.15.2", - "contentHash": "EnoxYEYMepcvAoXdZhaFJiv2aiDBIPjgkgzxR/+ArOxlrALzCgheTsb5yD39a9sxNIi2tNECT93ulZvYjx8fZg==" + "requested": "[4.1.1, )", + "resolved": "4.1.1", + "contentHash": "Hd+iTtdaPACTpViXaJ/+sQ3ZQ8MEpApCSD+mxTXnUTYDSZOAipuC4LsZqhAkLMeQHSK/R/jiXEXaXS7e0mTXyA==", + "dependencies": { + "Microsoft.Extensions.ObjectPool": "9.0.0-preview.1.24081.5" + } }, "MathNet.Numerics": { "type": "Direct", @@ -31,14 +34,20 @@ }, "Meziantou.Analyzer": { "type": "Direct", - "requested": "[2.0.162, )", - "resolved": "2.0.162", - "contentHash": "lU5ZkiNx6Jj++yYhkUkn2qsgl+PSfAy+BDFc0IkoVVeSCnmUOGBa0Eb1XzGLAeyAuIoz6vYN4J97Q/uiGdIkhw==" + "requested": "[2.0.163, )", + "resolved": "2.0.163", + "contentHash": "vN0YmOkuvPLukMsefLHYCWYaLPzpc6SfoR74aPwuLeK8QdxgxkavywQ1q/II0shLEEaN1RhlMjmL1RxMPW853w==" + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Direct", + "requested": "[9.0.0-preview.1.24081.5, )", + "resolved": "9.0.0-preview.1.24081.5", + "contentHash": "aAR7YW+pUUdvHk3vj7GtAi71dWGDIuY9270lsmQ6lKw23zzY+r8pLP3cGNbJdlnA9VWl+S+gnIVkBCqj2ROlEg==" }, "DotNext": { "type": "Transitive", - "resolved": "5.8.0", - "contentHash": "5PwF7lUwgJKMcQRrMnFo8ilqrFq9OyaQ5Lq/zFHn0LahhLriNeySCPs08Zd15FdBV0G0KpZvPHBC8Rp1LIsrhg==", + "resolved": "5.11.0", + "contentHash": "aLWtURne05gwWFFsOg9/X1g4V5yHyuWHX5paQIN7pJIDLCbaCDHcQlLeRRvzvksjAflZJ7tTHkowN/Xi1NXPrA==", "dependencies": { "System.IO.Hashing": "8.0.0" } @@ -55,7 +64,7 @@ "type": "Project", "dependencies": { "Craftimizer.Simulator": "[1.0.0, )", - "DotNext": "[5.8.0, )" + "DotNext": "[5.11.0, )" } } } diff --git a/Simulator/Craftimizer.Simulator.csproj b/Simulator/Craftimizer.Simulator.csproj index c1eaf9e..35c55fa 100644 --- a/Simulator/Craftimizer.Simulator.csproj +++ b/Simulator/Craftimizer.Simulator.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Solver/Craftimizer.Solver.csproj b/Solver/Craftimizer.Solver.csproj index 07e0afc..29c8a39 100644 --- a/Solver/Craftimizer.Solver.csproj +++ b/Solver/Craftimizer.Solver.csproj @@ -10,8 +10,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Test/Craftimizer.Test.csproj b/Test/Craftimizer.Test.csproj index 7bb36ba..8b0dd57 100644 --- a/Test/Craftimizer.Test.csproj +++ b/Test/Craftimizer.Test.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive