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