From d9e78166d974eccf59506da9b9a939226493cd85 Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Sun, 1 Oct 2023 03:43:11 -0700 Subject: [PATCH] Updates for v9, begin ui rework --- Craftimizer/Craftimizer.csproj | 10 + Craftimizer/Graphics/collectible_badge.png | Bin 0 -> 816 bytes Craftimizer/Graphics/expert.png | Bin 0 -> 1963 bytes Craftimizer/Graphics/expert_badge.png | Bin 0 -> 896 bytes Craftimizer/Graphics/no_manip.png | Bin 0 -> 9422 bytes Craftimizer/Graphics/specialist.png | Bin 0 -> 1992 bytes Craftimizer/Graphics/splendorous.png | Bin 0 -> 1270 bytes Craftimizer/Icons.cs | 19 - Craftimizer/ImGuiUtils.cs | 21 +- Craftimizer/LuminaSheets.cs | 3 + Craftimizer/Plugin.cs | 34 +- Craftimizer/Service.cs | 31 +- Craftimizer/SimulatorUtils.cs | 29 +- Craftimizer/Utils/Gearsets.cs | 9 +- Craftimizer/Utils/IconManager.cs | 66 +++ Craftimizer/Utils/RecipeNote.cs | 46 ++ Craftimizer/Utils/SqText.cs | 33 ++ Craftimizer/Windows/RecipeNote.cs | 542 +++++++++++++++++++++ Craftimizer/Windows/Settings.cs | 2 +- Craftimizer/Windows/SimulatorDrawer.cs | 2 +- 20 files changed, 785 insertions(+), 62 deletions(-) create mode 100644 Craftimizer/Graphics/collectible_badge.png create mode 100644 Craftimizer/Graphics/expert.png create mode 100644 Craftimizer/Graphics/expert_badge.png create mode 100644 Craftimizer/Graphics/no_manip.png create mode 100644 Craftimizer/Graphics/specialist.png create mode 100644 Craftimizer/Graphics/splendorous.png delete mode 100644 Craftimizer/Icons.cs create mode 100644 Craftimizer/Utils/IconManager.cs create mode 100644 Craftimizer/Utils/SqText.cs create mode 100644 Craftimizer/Windows/RecipeNote.cs diff --git a/Craftimizer/Craftimizer.csproj b/Craftimizer/Craftimizer.csproj index 1b2e168..c021083 100644 --- a/Craftimizer/Craftimizer.csproj +++ b/Craftimizer/Craftimizer.csproj @@ -28,6 +28,12 @@ + + + + + + @@ -50,6 +56,10 @@ $(DalamudLibPath)Dalamud.dll false + + $(DalamudLibPath)Dalamud.Interface.dll + false + $(DalamudLibPath)ImGui.NET.dll false diff --git a/Craftimizer/Graphics/collectible_badge.png b/Craftimizer/Graphics/collectible_badge.png new file mode 100644 index 0000000000000000000000000000000000000000..5b3b890f6fd6b5b3425a6708cc80bfd0dac358a0 GIT binary patch literal 816 zcmV-01JC@4P) z20%M#AF_cxP~2biN~sT8>CajEjS+}#kALlV{H?QRue}xDKixSZa$-#gac~?1JbnHY z6BumUqDqSJO$dNe3avC+>2VB$_x_{r`V%7BTD&U|^I1D=HCqI00f7lnB}Hw%20*b; z#8`u|25T+TN2e)Q%2X=O8UBKoqpndR*#fv*LWPn5ZM_2QoLVxgGa8*6AjYZ9~q z%9V0v5%2v+v9ope9dLNqMxChB3nnm_z!0v7NF}LWs{X9dyEx~1H~HR0&d8onv509K zj4@baky0XOE2S3<8i70IO}kM!@YC^+e#^^&)1V@(>4UH0OuT1N~#yC zlNVT6SirU|b64ij<=qd`Sw~~JF;pLOwK;UD*S6E?;9fWy4;#M;4kM6DQ+s=R8{4+P zZVJUnC6P-0u@9wH5Ckx8`0Y@W99Vu`$ui79PUKFB)3dO;dVhAu?<@+$G17_*5QuTk uL4Nl*2?Lhm^a5Mn`!Ju&Uq5r^%99^&QD#T%#d%YDB9 literal 0 HcmV?d00001 diff --git a/Craftimizer/Graphics/expert.png b/Craftimizer/Graphics/expert.png new file mode 100644 index 0000000000000000000000000000000000000000..db26b79292ac4e7246b98389524528065f9fdc18 GIT binary patch literal 1963 zcmZ`(YgAKL7Cu*mBq%sSTOJ~T+z>(%G~k1WBI()fr0`OAdm=($T)bYq!3Cd1O*i*LlIb&lPu)!KtcpkSn|a6@mUZhCnIoMLM2RB zinaM{86?Np5Csju93x@i6cnCF0)t)vkY zG(7APkU^2hDxHj@q~H}4ypl?gkpK*J7%E2*I2Yzc=;~tB5~{++q2NL+9it@yRVXS& z0gnpg9smMi!2bUPkb1&Sh((|tsFUjit@^JRSi-6lAftlmYOxe16rkRR_U6Vy223rA znI@+qB@6$J2%KZEFh{j*nR4(SV_Zd6hlsT z?Bb0f6o`ekK(&xoO2j-=(D8Ctq))VeuA!)efrr+?5)=tTwZ|Q9e^VDhdmFIk%ZKRy2DOLUVwt;NE-AO@?!#sNK-gfNhiS0e8>#F!uYYT z0(L?Ji%?Lv1ike4Q+zPr{l}NxHJbp;|brFOLP? zxGRhqKgI9jheKOhq8WGjF{XwS`hw)UqL?Sj)Rnf1H_gR5dBV!dN>|8(C1|%s!j6px zt8ML8t#P6GZzmI+y*O#f;qO@B(crAImQ&n6Z*r7&k3Q_I8Z|BCXjN9Eq@&dX<0kW{ zzP`O{mI@%4zFUGa-~?6~{3+*xy*f z7=AzufSm+;kmp>Y3?AYn5iy~-32Vf{%5wcHds_f3Y$8JTaQwCPkDesk#kur~j=04! zYP`W=TlYz&}Fi^x;HWyq^D|V05vb5pUJIn{+KE-k4dKiI4Kx z9OyUka&GzMnu(n!?-?8KPAvA2@W(tSBM6^7N08>OCbzwe$vu{f>*s9G4@{o<_~T3Gf3Uu6FKH*U{@Gmi>6MDo4VFVX>G^S% z4{Q5V*A7RUiBZpl%JiAqt7E8&f9(}-GPLZyB)gPl8)ho9%JDrow5xc1 zB=U>oktaF&dJ5`c5*luT^+4F-i#lZb+oc|TiHs;nNRSUTzr?YA{YVdwk zUE|brGRQb|tBI3c!TpI;FY+0#S$kvk>2s~osNP1zcQ|`$;D}Jc%y53PYsbO&)Rh~) z+WPDY_!O>c;TWPid5)%>9F#?B;h8$_4UhrJhhGIbkRrSwo+A}nphRl zn!H$-I+#lxaom@)cVn=>U+$bU+3&q~`TZ+BvP3K6OkxRrh~@MJFXuIjd+o1fZT?AhY|=}|xNI*y z@p#(6=VM>+UZ=CTs*C4iIZ2UsMpk<5*Ol!c-0On zg^{H-4$pEH*DXZ1w{beQ9?dE)C+&KF)B1vufUmW}#21}Ev)aGw5U|FY`ZVuMcXp?o z({i{pt)C5!fxUH#@kJeJByYEH0>8Qd+V|Z@|2VygKpUQn-=2%P5tD? zzqCAXp4Pl;w7%G%PWtR|Qu6Te-@dKBe)#GKPsiF-=$S#U>9+v^EAy0{9{buek~PZKtyP4Nax-|$NmkV<_&`Y literal 0 HcmV?d00001 diff --git a/Craftimizer/Graphics/expert_badge.png b/Craftimizer/Graphics/expert_badge.png new file mode 100644 index 0000000000000000000000000000000000000000..5186387b33864c011989f8bbb953926245ba66db GIT binary patch literal 896 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 g Cache = new(); - - public static TextureWrap GetIconFromPath(string path) - { - if (!Cache.TryGetValue(path, out var ret)) - Cache.Add(path, ret = Service.DataManager.GetImGuiTexture(path)!); - return ret; - } - - public static TextureWrap GetIconFromId(ushort id) => - GetIconFromPath($"ui/icon/{id / 1000 * 1000:000000}/{id:000000}_hr1.tex"); -} diff --git a/Craftimizer/ImGuiUtils.cs b/Craftimizer/ImGuiUtils.cs index c895934..0e34dc6 100644 --- a/Craftimizer/ImGuiUtils.cs +++ b/Craftimizer/ImGuiUtils.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; - namespace Craftimizer.Plugin; internal static class ImGuiUtils @@ -158,4 +157,24 @@ internal static class ImGuiUtils ImGui.SetTooltip("Open in Browser"); } } + + public static void AlignCentered(float width) + { + var availWidth = ImGui.GetContentRegionAvail().X; + if (availWidth > width) + ImGui.SetCursorPosX(ImGui.GetCursorPos().X + (availWidth - width) / 2); + } + + // https://stackoverflow.com/a/67855985 + public static void TextCentered(string text) + { + AlignCentered(ImGui.CalcTextSize(text).X); + ImGui.TextUnformatted(text); + } + + public static bool ButtonCentered(string text) + { + AlignCentered(ImGui.CalcTextSize(text).X + ImGui.GetStyle().FramePadding.Y * 2); + return ImGui.Button(text); + } } diff --git a/Craftimizer/LuminaSheets.cs b/Craftimizer/LuminaSheets.cs index 67ba2e9..18301f2 100644 --- a/Craftimizer/LuminaSheets.cs +++ b/Craftimizer/LuminaSheets.cs @@ -16,6 +16,9 @@ public static class LuminaSheets 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()!; diff --git a/Craftimizer/Plugin.cs b/Craftimizer/Plugin.cs index cb31ff4..c62db6e 100644 --- a/Craftimizer/Plugin.cs +++ b/Craftimizer/Plugin.cs @@ -1,6 +1,7 @@ using Craftimizer.Plugin.Windows; using Craftimizer.Simulator; using Craftimizer.Utils; +using Craftimizer.Windows; using Dalamud.Interface.Windowing; using Dalamud.IoC; using Dalamud.Plugin; @@ -16,39 +17,35 @@ public sealed class Plugin : IDalamudPlugin public string Name => "Craftimizer"; public string Version { get; } public string Author { get; } - public string Configuration { get; } + public string BuildConfiguration { get; } public TextureWrap Icon { get; } public WindowSystem WindowSystem { get; } public Settings SettingsWindow { get; } - public CraftingLog RecipeNoteWindow { get; } + public Craftimizer.Windows.RecipeNote RecipeNoteWindow { get; } public Craft SynthesisWindow { get; } public Windows.Simulator? SimulatorWindow { get; set; } + public Configuration Configuration { get; } public Hooks Hooks { get; } - public RecipeNote RecipeNote { get; } + public Craftimizer.Utils.RecipeNote RecipeNote { get; } + public IconManager IconManager { get; } public Plugin([RequiredVersion("1.0")] DalamudPluginInterface pluginInterface) { - Service.Plugin = this; - pluginInterface.Create(); - Service.Configuration = pluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); + Service.Initialize(this, pluginInterface); + + Configuration = pluginInterface.GetPluginConfig() as Configuration ?? new(); + Hooks = new(); + RecipeNote = new(); + IconManager = new(); + WindowSystem = new(Name); var assembly = Assembly.GetExecutingAssembly(); Version = assembly.GetCustomAttribute()!.InformationalVersion; Author = assembly.GetCustomAttribute()!.Company; - Configuration = assembly.GetCustomAttribute()!.Configuration; - byte[] iconData; - using (var stream = assembly.GetManifestResourceStream("Craftimizer.icon.png")!) - { - iconData = new byte[stream.Length]; - _ = stream.Read(iconData); - } - Icon = Service.PluginInterface.UiBuilder.LoadImage(iconData); - - Hooks = new(); - RecipeNote = new(); - WindowSystem = new(Name); + BuildConfiguration = assembly.GetCustomAttribute()!.Configuration; + Icon = IconManager.GetAssemblyTexture("icon.png"); SettingsWindow = new(); RecipeNoteWindow = new(); @@ -86,5 +83,6 @@ public sealed class Plugin : IDalamudPlugin SynthesisWindow.Dispose(); RecipeNote.Dispose(); Hooks.Dispose(); + IconManager.Dispose(); } } diff --git a/Craftimizer/Service.cs b/Craftimizer/Service.cs index 0c02ad0..e09304e 100644 --- a/Craftimizer/Service.cs +++ b/Craftimizer/Service.cs @@ -1,13 +1,11 @@ -using Dalamud.Data; +using Craftimizer.Utils; using Dalamud.Game; -using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects; -using Dalamud.Game.Command; -using Dalamud.Game.Gui; using Dalamud.Interface.Windowing; using Dalamud.IoC; using Dalamud.Plugin; +using Dalamud.Plugin.Services; namespace Craftimizer.Plugin; @@ -15,19 +13,26 @@ public sealed class Service { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. [PluginService] public static DalamudPluginInterface PluginInterface { get; private set; } - [PluginService] public static CommandManager CommandManager { get; private set; } - [PluginService] public static ObjectTable Objects { get; private set; } - [PluginService] public static SigScanner SigScanner { get; private set; } - [PluginService] public static GameGui GameGui { get; private set; } - [PluginService] public static ClientState ClientState { get; private set; } - [PluginService] public static DataManager DataManager { get; private set; } - [PluginService] public static TargetManager TargetManager { get; private set; } + [PluginService] public static ICommandManager CommandManager { get; private set; } + [PluginService] public static IObjectTable Objects { get; private set; } + [PluginService] public static ISigScanner SigScanner { get; private set; } + [PluginService] public static IGameGui GameGui { get; private set; } + [PluginService] public static IClientState ClientState { get; private set; } + [PluginService] public static IDataManager DataManager { get; private set; } + [PluginService] public static ITextureProvider TextureProvider { get; private set; } + [PluginService] public static ITargetManager TargetManager { get; private set; } [PluginService] public static Condition Condition { get; private set; } [PluginService] public static Framework Framework { get; private set; } - public static Plugin Plugin { get; internal set; } - public static Configuration Configuration { get; internal set; } + public static Plugin Plugin { get; private set; } + public static Configuration Configuration => Plugin.Configuration; public static WindowSystem WindowSystem => Plugin.WindowSystem; + public static IconManager IconManager => Plugin.IconManager; #pragma warning restore CS8618 + internal static void Initialize(Plugin plugin, DalamudPluginInterface iface) + { + Plugin = plugin; + iface.Create(); + } } diff --git a/Craftimizer/SimulatorUtils.cs b/Craftimizer/SimulatorUtils.cs index 27eeb87..4d30def 100644 --- a/Craftimizer/SimulatorUtils.cs +++ b/Craftimizer/SimulatorUtils.cs @@ -2,6 +2,9 @@ using Craftimizer.Simulator; using Craftimizer.Simulator.Actions; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.Game.Object; +using FFXIVClientStructs.FFXIV.Client.Game.UI; using ImGuiScene; using Lumina.Excel.GeneratedSheets; using System; @@ -10,8 +13,10 @@ using System.Linq; using System.Numerics; using System.Text; using Action = Lumina.Excel.GeneratedSheets.Action; +using ActionType = Craftimizer.Simulator.Actions.ActionType; using ClassJob = Craftimizer.Simulator.ClassJob; using Condition = Craftimizer.Simulator.Condition; +using Status = Lumina.Excel.GeneratedSheets.Status; namespace Craftimizer.Plugin; @@ -85,11 +90,11 @@ internal static class ActionUtils { var (craftAction, action) = GetActionRow(me, classJob); if (craftAction != null) - return Icons.GetIconFromId(craftAction.Icon); + return Service.IconManager.GetIcon(craftAction.Icon); if (action != null) - return Icons.GetIconFromId(action.Icon); + return Service.IconManager.GetIcon(action.Icon); // Old "Steady Hand" action icon - return Icons.GetIconFromId(1953); + return Service.IconManager.GetIcon(1953); } public static ActionType? GetActionTypeFromId(uint actionId, ClassJob classJob, bool isCraftAction) @@ -145,12 +150,24 @@ internal static class ClassJobUtils public static sbyte GetExpArrayIdx(this ClassJob me) => LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex())!.ExpArrayIndex; - public static string GetName(this ClassJob classJob) + public static unsafe short GetPlayerLevel(this ClassJob me) => + PlayerState.Instance()->ClassJobLevelArray[me.GetExpArrayIdx()]; + + public static unsafe bool CanPlayerUseManipulation(this ClassJob me) => + ActionManager.CanUseActionOnTarget(ActionType.Manipulation.GetId(me), (GameObject*)Service.ClientState.LocalPlayer!.Address); + + public static string GetName(this ClassJob me) { - var job = LuminaSheets.ClassJobSheet.GetRow(classJob.GetClassJobIndex())!; + var job = LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex())!; return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(job.Name.ToDalamudString().TextValue); } + 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)); + + public static ushort GetIconId(this ClassJob me) => + (ushort)(62100 + me.GetClassJobIndex()); + public static bool IsClassJob(this ClassJobCategory me, ClassJob classJob) => classJob switch { @@ -303,7 +320,7 @@ internal static class EffectUtils } public static TextureWrap GetIcon(this EffectType me, int strength) => - Icons.GetIconFromId(me.GetIconId(strength)); + Service.IconManager.GetIcon(me.GetIconId(strength)); public static string GetTooltip(this EffectType me, int strength, int duration) { diff --git a/Craftimizer/Utils/Gearsets.cs b/Craftimizer/Utils/Gearsets.cs index 86e16c5..0a6dd19 100644 --- a/Craftimizer/Utils/Gearsets.cs +++ b/Craftimizer/Utils/Gearsets.cs @@ -8,7 +8,7 @@ using System; using System.Linq; namespace Craftimizer.Plugin.Utils; -internal static unsafe class Gearsets +public static unsafe class Gearsets { public record struct GearsetStats(int CP, int Craftsmanship, int Control); public record struct GearsetMateria(ushort Type, ushort Grade); @@ -117,9 +117,12 @@ internal static unsafe class Gearsets public static bool IsSpecialistSoulCrystal(GearsetItem item) { + if (item.itemId == 0) + 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.Row == 62 && luminaItem.ClassJobUse.Value!.ClassJobCategory.Row == 33; } public static bool IsSplendorousTool(GearsetItem item) => diff --git a/Craftimizer/Utils/IconManager.cs b/Craftimizer/Utils/IconManager.cs new file mode 100644 index 0000000..abefac2 --- /dev/null +++ b/Craftimizer/Utils/IconManager.cs @@ -0,0 +1,66 @@ +using Craftimizer.Plugin; +using Dalamud.Interface.Internal; +using ImGuiScene; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace Craftimizer.Utils; + +public sealed class IconManager : IDisposable +{ + private readonly Dictionary iconCache = new(); + private readonly Dictionary textureCache = new(); + private readonly Dictionary assemblyCache = new(); + + public IDalamudTextureWrap GetIcon(uint id) + { + if (!iconCache.TryGetValue(id, out var ret)) + iconCache.Add(id, ret = Service.TextureProvider.GetIcon(id) ?? + throw new ArgumentException($"Invalid icon id {id}", nameof(id))); + return ret; + } + + public IDalamudTextureWrap GetTexture(string path) + { + if (!textureCache.TryGetValue(path, out var ret)) + textureCache.Add(path, ret = Service.TextureProvider.GetTextureFromGame(path) ?? + throw new ArgumentException($"Invalid texture {path}", nameof(path))); + return ret; + } + + public TextureWrap GetAssemblyTexture(string filename) + { + if (!assemblyCache.TryGetValue(filename, out var ret)) + assemblyCache.Add(filename, ret = GetAssemblyTextureInternal(filename)); + return ret; + } + + private static TextureWrap GetAssemblyTextureInternal(string filename) + { + var assembly = Assembly.GetExecutingAssembly(); + byte[] iconData; + using (var stream = assembly.GetManifestResourceStream($"Craftimizer.{filename}") ?? throw new InvalidDataException($"Could not load resource {filename}")) + { + iconData = new byte[stream.Length]; + _ = stream.Read(iconData); + } + return Service.PluginInterface.UiBuilder.LoadImage(iconData); + } + + public void Dispose() + { + foreach (var image in iconCache.Values) + image.Dispose(); + iconCache.Clear(); + + foreach (var image in textureCache.Values) + image.Dispose(); + textureCache.Clear(); + + foreach (var image in assemblyCache.Values) + image.Dispose(); + assemblyCache.Clear(); + } +} diff --git a/Craftimizer/Utils/RecipeNote.cs b/Craftimizer/Utils/RecipeNote.cs index 9178682..d27154b 100644 --- a/Craftimizer/Utils/RecipeNote.cs +++ b/Craftimizer/Utils/RecipeNote.cs @@ -16,6 +16,52 @@ using CSRecipeNote = FFXIVClientStructs.FFXIV.Client.Game.UI.RecipeNote; namespace Craftimizer.Utils; +public record RecipeData +{ + public ushort RecipeId { get; } + + public Recipe Recipe { get; } + public RecipeLevelTable Table { get; } + + public ClassJob ClassJob { get; } + public RecipeInfo RecipeInfo { get; } + public int HQIngredientCount { get; } + public int MaxStartingQuality { get; } + + public RecipeData(ushort recipeId) + { + RecipeId = recipeId; + + Recipe = LuminaSheets.RecipeSheet.GetRow(recipeId) ?? + throw new ArgumentException($"Invalid recipe id {recipeId}", nameof(recipeId)); + + Table = Recipe.RecipeLevelTable.Value!; + ClassJob = (ClassJob)Recipe.CraftType.Row; + RecipeInfo = new() + { + IsExpert = Recipe.IsExpert, + ClassJobLevel = Table.ClassJobLevel, + RLvl = (int)Table.RowId, + ConditionsFlag = Table.ConditionsFlag, + MaxDurability = Table.Durability * Recipe.DurabilityFactor / 100, + MaxQuality = (int)Table.Quality * Recipe.QualityFactor / 100, + MaxProgress = Table.Difficulty * Recipe.DifficultyFactor / 100, + QualityModifier = Table.QualityModifier, + QualityDivider = Table.QualityDivider, + ProgressModifier = Table.ProgressModifier, + ProgressDivider = Table.ProgressDivider, + }; + + HQIngredientCount = Recipe.UnkData5 + .Where(i => + i != null && + i.ItemIngredient != 0 && + (LuminaSheets.ItemSheet.GetRow((uint)i.ItemIngredient)?.CanBeHq ?? false) + ).Sum(i => i.AmountIngredient); + MaxStartingQuality = (int)Math.Floor(Recipe.MaterialQualityFactor * RecipeInfo.MaxQuality / 100f); + } +} + public sealed unsafe class RecipeNote : IDisposable { public AddonRecipeNote* AddonRecipe { get; private set; } diff --git a/Craftimizer/Utils/SqText.cs b/Craftimizer/Utils/SqText.cs new file mode 100644 index 0000000..b8ab740 --- /dev/null +++ b/Craftimizer/Utils/SqText.cs @@ -0,0 +1,33 @@ +using Dalamud.Game.Text; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Craftimizer.Utils; + +public static class SqText +{ + private static ReadOnlyDictionary levelNumReplacements = new(new Dictionary + { + ['0'] = SeIconChar.Number0, + ['1'] = SeIconChar.Number1, + ['2'] = SeIconChar.Number2, + ['3'] = SeIconChar.Number3, + ['4'] = SeIconChar.Number4, + ['5'] = SeIconChar.Number5, + ['6'] = SeIconChar.Number6, + ['7'] = SeIconChar.Number7, + ['8'] = SeIconChar.Number8, + ['9'] = SeIconChar.Number9, + }); + + public static string ToLevelString(T value) where T : IBinaryInteger + { + var str = value.ToString() ?? throw new FormatException("Failed to format value"); + foreach(var (k, v) in levelNumReplacements) + str = str.Replace(k, v.ToIconChar()); + return SeIconChar.LevelEn.ToIconChar() + str; + } +} diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs new file mode 100644 index 0000000..4685ded --- /dev/null +++ b/Craftimizer/Windows/RecipeNote.cs @@ -0,0 +1,542 @@ +using Craftimizer.Plugin; +using Craftimizer.Plugin.Utils; +using Craftimizer.Simulator; +using Craftimizer.Simulator.Actions; +using Craftimizer.Utils; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Interface; +using Dalamud.Interface.Components; +using Dalamud.Interface.GameFonts; +using Dalamud.Interface.Raii; +using Dalamud.Interface.Windowing; +using Dalamud.Logging; +using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Client.UI.Misc; +using FFXIVClientStructs.FFXIV.Component.GUI; +using ImGuiNET; +using ImGuiScene; +using System; +using System.Linq; +using System.Numerics; +using ActionType = Craftimizer.Simulator.Actions.ActionType; +using ClassJob = Craftimizer.Simulator.ClassJob; +using CSRecipeNote = FFXIVClientStructs.FFXIV.Client.Game.UI.RecipeNote; + +namespace Craftimizer.Windows; + +public sealed unsafe class RecipeNote : Window, IDisposable +{ + private const ImGuiWindowFlags WindowFlags = ImGuiWindowFlags.NoDecoration + | ImGuiWindowFlags.AlwaysAutoResize + | ImGuiWindowFlags.NoSavedSettings + | ImGuiWindowFlags.NoFocusOnAppearing + | ImGuiWindowFlags.NoNavFocus; + + public enum CraftableStatus + { + OK, + LockedClassJob, + WrongClassJob, + SpecialistRequired, + RequiredItem, + RequiredStatus, + CraftsmanshipTooLow, + ControlTooLow, + } + + public AddonRecipeNote* Addon { get; private set; } + public RecipeData? RecipeData { get; private set; } + public CharacterStats? CharacterStats { get; private set; } + public CraftableStatus CraftStatus { get; private set; } + + private TextureWrap ExpertBadge { get; } + private TextureWrap CollectibleBadge { get; } + private TextureWrap SplendorousBadge { get; } + private TextureWrap SpecialistBadge { get; } + private TextureWrap NoManipulationBadge { get; } + private GameFontHandle AxisFont { get; } + + public RecipeNote() : base("Craftimizer RecipeNode", WindowFlags, false) + { + ExpertBadge = Service.IconManager.GetAssemblyTexture("Graphics.expert_badge.png"); + CollectibleBadge = Service.IconManager.GetAssemblyTexture("Graphics.collectible_badge.png"); + SplendorousBadge = Service.IconManager.GetAssemblyTexture("Graphics.splendorous.png"); + SpecialistBadge = Service.IconManager.GetAssemblyTexture("Graphics.specialist.png"); + NoManipulationBadge = Service.IconManager.GetAssemblyTexture("Graphics.no_manip.png"); + AxisFont = Service.PluginInterface.UiBuilder.GetGameFontHandle(new(GameFontFamilyAndSize.Axis14)); + + Service.WindowSystem.AddWindow(this); + + IsOpen = true; + } + + public override bool DrawConditions() + { + if (Service.ClientState.LocalPlayer == null) + return false; + + { + Addon = (AddonRecipeNote*)Service.GameGui.GetAddonByName("RecipeNote"); + if (Addon == null) + return false; + + // Check if RecipeNote addon is visible + if (Addon->AtkUnitBase.WindowNode == null) + return false; + + // Check if RecipeNote has a visible selected recipe + if (!Addon->Unk258->IsVisible) + return false; + } + + { + var instance = CSRecipeNote.Instance(); + + var list = instance->RecipeList; + if (list == null) + return false; + + var recipeEntry = list->SelectedRecipe; + if (recipeEntry == null) + return false; + + var recipeId = recipeEntry->RecipeId; + RecipeData = new(recipeId); + } + + Gearsets.GearsetItem[] gearItems; + { + var gearStats = Gearsets.CalculateGearsetCurrentStats(); + + var container = InventoryManager.Instance()->GetInventoryContainer(InventoryType.EquippedItems); + if (container == null) + return false; + + gearItems = Gearsets.GetGearsetItems(container); + + CharacterStats = Gearsets.CalculateCharacterStats(gearStats, gearItems, RecipeData.ClassJob.GetPlayerLevel(), RecipeData.ClassJob.CanPlayerUseManipulation()); + } + + CraftStatus = CalculateCraftStatus(gearItems); + + return true; + } + + public override void PreDraw() + { + ref var unit = ref Addon->AtkUnitBase; + var scale = unit.Scale; + var pos = new Vector2(unit.X, unit.Y); + var size = new Vector2(unit.WindowNode->AtkResNode.Width, unit.WindowNode->AtkResNode.Height) * scale; + + var node = (AtkResNode*)Addon->Unk458; // unit.GetNodeById(59); + var nodeParent = Addon->Unk258; // unit.GetNodeById(57); + + Position = pos + new Vector2(size.X, (nodeParent->Y + node->Y) * scale); + SizeConstraints = new WindowSizeConstraints + { + MinimumSize = new(-1), + MaximumSize = new(10000, 10000) + }; + } + + public override void Draw() + { + using var table = ImRaii.Table("stats", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchSame); + if (table) + { + ImGui.TableSetupColumn("col1", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("col2", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableNextColumn(); + DrawCharacterStats(); + ImGui.TableNextColumn(); + DrawRecipeStats(); + } + } + + private void DrawCharacterStats() + { + ImGuiUtils.TextCentered("Crafter"); + + var level = RecipeData!.ClassJob.GetPlayerLevel(); + { + var className = RecipeData.ClassJob.GetName(); + var levelText = string.Empty; + if (level != 0) + levelText = SqText.ToLevelString(level); + var imageSize = ImGuiUtils.ButtonHeight; + bool hasSplendorous = false, hasSpecialist = false, shouldHaveManip = false; + if (CraftStatus is not (CraftableStatus.LockedClassJob or CraftableStatus.WrongClassJob)) + { + hasSplendorous = CharacterStats!.HasSplendorousBuff; + hasSpecialist = CharacterStats!.IsSpecialist; + shouldHaveManip = !CharacterStats.CanUseManipulation && CharacterStats.Level >= ActionType.Manipulation.Level(); + } + + ImGuiUtils.AlignCentered( + imageSize + 5 + + ImGui.CalcTextSize(className).X + + (level == 0 ? 0 : (3 + ImGui.CalcTextSize(levelText).X)) + + (hasSplendorous ? (3 + imageSize) : 0) + + (hasSpecialist ? (3 + imageSize) : 0) + + (shouldHaveManip ? (3 + imageSize) : 0) + ); + ImGui.AlignTextToFramePadding(); + + ImGui.Image(Service.IconManager.GetIcon(RecipeData.ClassJob.GetIconId()).ImGuiHandle, new Vector2(imageSize)); + ImGui.SameLine(0, 5); + + if (level != 0) + { + ImGui.Text(levelText); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"CLvl {Gearsets.CalculateCLvl(level)}"); + ImGui.SameLine(0, 3); + } + + ImGui.Text(className); + + if (hasSplendorous) + { + ImGui.SameLine(0, 3); + ImGui.Image(SplendorousBadge.ImGuiHandle, new Vector2(imageSize)); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"Splendorous Tool"); + } + + if (hasSpecialist) + { + ImGui.SameLine(0, 3); + ImGui.Image(SpecialistBadge.ImGuiHandle, new Vector2(imageSize), Vector2.Zero, Vector2.One, new(0.99f, 0.97f, 0.62f, 1f)); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"Specialist"); + } + + if (shouldHaveManip) + { + ImGui.SameLine(0, 3); + ImGui.Image(NoManipulationBadge.ImGuiHandle, new Vector2(imageSize)); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"No Manipulation (Missing Job Quest)"); + } + } + + ImGui.Separator(); + + switch (CraftStatus) + { + case CraftableStatus.LockedClassJob: + { + ImGuiUtils.TextCentered($"You do not have {RecipeData.ClassJob.GetName().ToLowerInvariant()} unlocked."); + ImGui.Separator(); + var unlockQuest = RecipeData.ClassJob.GetUnlockQuest(); + var (questGiver, questTerritory, questLocation, mapPayload) = ResolveLevelData(unlockQuest.IssuerLocation.Row); + + var unlockText = $"Unlock it from {questGiver}"; + ImGuiUtils.AlignCentered(ImGui.CalcTextSize(unlockText).X + 5 + ImGuiUtils.ButtonHeight); + ImGui.AlignTextToFramePadding(); + ImGui.Text(unlockText); + ImGui.SameLine(0, 5); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Flag)) + Service.GameGui.OpenMapWithMapLink(mapPayload); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("Open in map"); + + ImGuiUtils.TextCentered($"{questTerritory} ({questLocation.X:0.0}, {questLocation.Y:0.0})"); + } + break; + case CraftableStatus.WrongClassJob: + { + ImGuiUtils.TextCentered($"You are not a {RecipeData.ClassJob.GetName().ToLowerInvariant()}."); + var gearsetId = GetGearsetForJob(RecipeData.ClassJob); + if (gearsetId.HasValue) + { + if (ImGuiUtils.ButtonCentered("Switch Job")) + Chat.SendMessage($"/gearset change {gearsetId + 1}"); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"Swap to gearset {gearsetId + 1}"); + } + else + ImGuiUtils.TextCentered($"You do not have any {RecipeData.ClassJob.GetName().ToLowerInvariant()} gearsets."); + } + break; + case CraftableStatus.SpecialistRequired: + { + ImGuiUtils.TextCentered($"You need to be a specialist to craft this recipe."); + + var (vendorName, vendorTerritory, vendorLoation, mapPayload) = ResolveLevelData(5891399); + + var unlockText = $"Trade a Soul of the Crafter to {vendorName}"; + ImGuiUtils.AlignCentered(ImGui.CalcTextSize(unlockText).X + 5 + ImGuiUtils.ButtonHeight); + ImGui.AlignTextToFramePadding(); + ImGui.Text(unlockText); + ImGui.SameLine(0, 5); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Flag)) + Service.GameGui.OpenMapWithMapLink(mapPayload); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("Open in map"); + + ImGuiUtils.TextCentered($"{vendorTerritory} ({vendorLoation.X:0.0}, {vendorLoation.Y:0.0})"); + } + break; + case CraftableStatus.RequiredItem: + { + var item = RecipeData.Recipe.ItemRequired.Value!; + var itemName = item.Name.ToDalamudString().ToString(); + var imageSize = ImGuiUtils.ButtonHeight; + + ImGuiUtils.TextCentered($"You are missing the required equipment."); + ImGuiUtils.AlignCentered(imageSize + 5 + ImGui.CalcTextSize(itemName).X); + ImGui.AlignTextToFramePadding(); + ImGui.Image(Service.IconManager.GetIcon(item.Icon).ImGuiHandle, new(imageSize)); + ImGui.SameLine(0, 5); + ImGui.Text(itemName); + } + break; + case CraftableStatus.RequiredStatus: + { + var status = RecipeData.Recipe.StatusRequired.Value!; + var statusName = status.Name.ToDalamudString().ToString(); + var statusIcon = Service.IconManager.GetIcon(status.Icon); + var imageSize = new Vector2(ImGuiUtils.ButtonHeight * statusIcon.Width / statusIcon.Height, ImGuiUtils.ButtonHeight); + + ImGuiUtils.TextCentered($"You are missing the required status effect."); + ImGuiUtils.AlignCentered(imageSize.X + 5 + ImGui.CalcTextSize(statusName).X); + ImGui.AlignTextToFramePadding(); + ImGui.Image(statusIcon.ImGuiHandle, imageSize); + ImGui.SameLine(0, 5); + ImGui.Text(statusName); + } + break; + case CraftableStatus.CraftsmanshipTooLow: + { + ImGuiUtils.TextCentered("Your Craftsmanship is too low."); + + DrawRequiredStatsTable(CharacterStats!.Craftsmanship, RecipeData.Recipe.RequiredCraftsmanship); + } + break; + case CraftableStatus.ControlTooLow: + { + ImGuiUtils.TextCentered("Your Control is too low."); + + DrawRequiredStatsTable(CharacterStats!.Control, RecipeData.Recipe.RequiredControl); + } + break; + case CraftableStatus.OK: + { + using var table = ImRaii.Table("characterStats", 2, ImGuiTableFlags.NoHostExtendX); + if (table) + { + ImGui.TableSetupColumn("ccol1", ImGuiTableColumnFlags.WidthFixed, 100); + ImGui.TableSetupColumn("ccol2", ImGuiTableColumnFlags.WidthStretch); + + ImGui.TableNextColumn(); + ImGui.Text("Craftsmanship"); + ImGui.TableNextColumn(); + ImGui.Text($"{CharacterStats!.Craftsmanship}"); + + ImGui.TableNextColumn(); + ImGui.Text("Control"); + ImGui.TableNextColumn(); + ImGui.Text($"{CharacterStats.Control}"); + + ImGui.TableNextColumn(); + ImGui.Text("CP"); + ImGui.TableNextColumn(); + ImGui.Text($"{CharacterStats.CP}"); + } + } + break; + } + } + + private void DrawRecipeStats() + { + ImGuiUtils.TextCentered("Recipe"); + + { + var textStars = new string('★', RecipeData!.Table.Stars); + var textStarsSize = Vector2.Zero; + if (!string.IsNullOrEmpty(textStars)) { + var layout = AxisFont.LayoutBuilder(textStars).Build(); + textStarsSize = new(layout.Width + 3, layout.Height); + } + var textLevel = SqText.ToLevelString(RecipeData.RecipeInfo.ClassJobLevel); + var isExpert = RecipeData.RecipeInfo.IsExpert; + var isCollectable = RecipeData.Recipe.ItemResult.Value!.IsCollectable; + var imageSize = ImGuiUtils.ButtonHeight; + var textSize = ImGui.CalcTextSize("A").Y; + var badgeSize = new Vector2(textSize * ExpertBadge.Width / ExpertBadge.Height, textSize); + var badgeOffset = (imageSize - badgeSize.Y) / 2; + + ImGuiUtils.AlignCentered( + imageSize + 5 + + ImGui.CalcTextSize(textLevel).X + + textStarsSize.X + + (isCollectable ? badgeSize.X + 3 : 0) + + (isExpert ? badgeSize.X + 3 : 0) + ); + ImGui.AlignTextToFramePadding(); + + ImGui.Image(Service.IconManager.GetIcon(RecipeData.Recipe.ItemResult.Value!.Icon).ImGuiHandle, new Vector2(imageSize)); + + ImGui.SameLine(0, 5); + ImGui.Text(textLevel); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"RLvl {RecipeData.RecipeInfo.RLvl}"); + + if (textStarsSize != Vector2.Zero) + { + ImGui.SameLine(0, 3); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (textStarsSize.Y - textSize) / 2); + AxisFont.Text(textStars); + } + + if (isCollectable) + { + ImGui.SameLine(0, 3); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + badgeOffset); + ImGui.Image(CollectibleBadge.ImGuiHandle, badgeSize); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"Collectible"); + } + + if (isExpert) + { + ImGui.SameLine(0, 3); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + badgeOffset); + ImGui.Image(ExpertBadge.ImGuiHandle, badgeSize); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"Expert Recipe"); + } + } + + using var table = ImRaii.Table("recipeStats", 2); + if (table) + { + ImGui.TableSetupColumn("rcol1", ImGuiTableColumnFlags.WidthFixed, 100); + ImGui.TableSetupColumn("rcol2", ImGuiTableColumnFlags.WidthStretch); + + ImGui.TableNextColumn(); + ImGui.Text("Progress"); + ImGui.TableNextColumn(); + ImGui.Text($"{RecipeData.RecipeInfo.MaxProgress}"); + + ImGui.TableNextColumn(); + ImGui.Text("Quality"); + ImGui.TableNextColumn(); + ImGui.Text($"{RecipeData.RecipeInfo.MaxQuality}"); + + ImGui.TableNextColumn(); + ImGui.Text("Durability"); + ImGui.TableNextColumn(); + ImGui.Text($"{RecipeData.RecipeInfo.MaxDurability}"); + } + } + + private static void DrawRequiredStatsTable(int current, int required) + { + if (current >= required) + throw new ArgumentOutOfRangeException(nameof(current)); + + using var table = ImRaii.Table("requiredStats", 2, ImGuiTableFlags.NoHostExtendX); + if (table) + { + ImGui.TableSetupColumn("ccol1", ImGuiTableColumnFlags.WidthFixed, 100); + ImGui.TableSetupColumn("ccol2", ImGuiTableColumnFlags.WidthStretch); + + ImGui.TableNextColumn(); + ImGui.Text("Current"); + ImGui.TableNextColumn(); + ImGui.TextColored(new(0, 1, 0, 1), $"{current}"); + + ImGui.TableNextColumn(); + ImGui.Text("Required"); + ImGui.TableNextColumn(); + ImGui.TextColored(new(1, 0, 0, 1), $"{required}"); + + ImGui.TableNextColumn(); + ImGui.Text("You need"); + ImGui.TableNextColumn(); + ImGui.Text($"{required - current}"); + } + } + + private CraftableStatus CalculateCraftStatus(Gearsets.GearsetItem[] gearItems) + { + if (RecipeData!.ClassJob.GetPlayerLevel() == 0) + return CraftableStatus.LockedClassJob; + + if (PlayerState.Instance()->CurrentClassJobId != RecipeData.ClassJob.GetClassJobIndex()) + return CraftableStatus.WrongClassJob; + + if (RecipeData.Recipe.IsSpecializationRequired && !(CharacterStats!.IsSpecialist)) + return CraftableStatus.SpecialistRequired; + + var itemRequired = RecipeData.Recipe.ItemRequired; + if (itemRequired.Row != 0 && itemRequired.Value != null) + { + if (!gearItems.Any(i => Gearsets.IsItem(i, itemRequired.Row))) + return CraftableStatus.RequiredItem; + } + + var statusRequired = RecipeData.Recipe.StatusRequired; + if (statusRequired.Row != 0 && statusRequired.Value != null) + { + if (!Service.ClientState.LocalPlayer!.StatusList.Any(s => s.StatusId == statusRequired.Row)) + return CraftableStatus.RequiredStatus; + } + + if (RecipeData.Recipe.RequiredCraftsmanship > CharacterStats!.Craftsmanship) + return CraftableStatus.CraftsmanshipTooLow; + + if (RecipeData.Recipe.RequiredControl > CharacterStats.Control) + return CraftableStatus.ControlTooLow; + + return CraftableStatus.OK; + } + + private static (string NpcName, string Territory, Vector2 MapLocation, MapLinkPayload Payload) ResolveLevelData(uint levelRowId) + { + var level = LuminaSheets.LevelSheet.GetRow(levelRowId) ?? + throw new ArgumentNullException(nameof(levelRowId), $"Invalid level row {levelRowId}"); + var territory = level.Territory.Value!.PlaceName.Value!.Name.ToDalamudString().ToString(); + var location = MapUtil.WorldToMap(new(level.X, level.Z), level.Map.Value!); + + return (ResolveNpcResidentName(level.Object), territory, location, new(level.Territory.Row, level.Map.Row, location.X, location.Y)); + } + + private static string ResolveNpcResidentName(uint npcRowId) + { + var resident = LuminaSheets.ENpcResidentSheet.GetRow(npcRowId) ?? + throw new ArgumentNullException(nameof(npcRowId), $"Invalid npc row {npcRowId}"); + return resident.Singular.ToDalamudString().ToString(); + } + + private static int? GetGearsetForJob(ClassJob job) + { + var gearsetModule = RaptureGearsetModule.Instance(); + for (var i = 0; i < 100; i++) + { + var gearset = gearsetModule->Gearset[i]; + if (gearset == null) + continue; + if (!gearset->Flags.HasFlag(RaptureGearsetModule.GearsetFlag.Exists)) + continue; + if (gearset->ID != i) + continue; + if (gearset->ClassJob != job.GetClassJobIndex()) + continue; + return i; + } + return null; + } + + public void Dispose() + { + AxisFont?.Dispose(); + } +} diff --git a/Craftimizer/Windows/Settings.cs b/Craftimizer/Windows/Settings.cs index e55ada3..1088b3a 100644 --- a/Craftimizer/Windows/Settings.cs +++ b/Craftimizer/Windows/Settings.cs @@ -473,7 +473,7 @@ public class Settings : Window ImGui.Image(icon.ImGuiHandle, new(icon.Width, icon.Height)); ImGui.TableNextColumn(); - ImGui.Text($"{plugin.Name} v{plugin.Version} {plugin.Configuration}"); + ImGui.Text($"{plugin.Name} v{plugin.Version} {plugin.BuildConfiguration}"); ImGui.Text($"By {plugin.Author} ("); ImGui.SameLine(0, 0); ImGuiUtils.Hyperlink("WorkingRobot", "https://github.com/WorkingRobot"); diff --git a/Craftimizer/Windows/SimulatorDrawer.cs b/Craftimizer/Windows/SimulatorDrawer.cs index 9ad426e..acc0d57 100644 --- a/Craftimizer/Windows/SimulatorDrawer.cs +++ b/Craftimizer/Windows/SimulatorDrawer.cs @@ -133,7 +133,7 @@ public sealed partial class Simulator : Window, IDisposable { var imageSize = new Vector2(ImGui.GetFontSize() * 2.25f); - ImGui.Image(Icons.GetIconFromId(Item.Icon).ImGuiHandle, imageSize); + ImGui.Image(Service.IconManager.GetIcon(Item.Icon).ImGuiHandle, imageSize); ImGui.SameLine(); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f); ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString());