Updates for v9, begin ui rework
This commit is contained in:
@@ -28,6 +28,12 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="../icon.png" />
|
<EmbeddedResource Include="../icon.png" />
|
||||||
|
<EmbeddedResource Include="Graphics\collectible_badge.png" />
|
||||||
|
<EmbeddedResource Include="Graphics\expert.png" />
|
||||||
|
<EmbeddedResource Include="Graphics\expert_badge.png" />
|
||||||
|
<EmbeddedResource Include="Graphics\no_manip.png" />
|
||||||
|
<EmbeddedResource Include="Graphics\specialist.png" />
|
||||||
|
<EmbeddedResource Include="Graphics\splendorous.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -50,6 +56,10 @@
|
|||||||
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private>false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Dalamud">
|
||||||
|
<HintPath>$(DalamudLibPath)Dalamud.Interface.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="ImGui.NET">
|
<Reference Include="ImGui.NET">
|
||||||
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private>false</Private>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 816 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 896 B |
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,19 +0,0 @@
|
|||||||
using ImGuiScene;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
|
||||||
|
|
||||||
internal static class Icons
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<string, TextureWrap> 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");
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
internal static class ImGuiUtils
|
internal static class ImGuiUtils
|
||||||
@@ -158,4 +157,24 @@ internal static class ImGuiUtils
|
|||||||
ImGui.SetTooltip("Open in Browser");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public static class LuminaSheets
|
|||||||
public static readonly ExcelSheet<ClassJob> ClassJobSheet = Service.DataManager.GetExcelSheet<ClassJob>()!;
|
public static readonly ExcelSheet<ClassJob> ClassJobSheet = Service.DataManager.GetExcelSheet<ClassJob>()!;
|
||||||
public static readonly ExcelSheet<Item> ItemSheet = Service.DataManager.GetExcelSheet<Item>()!;
|
public static readonly ExcelSheet<Item> ItemSheet = Service.DataManager.GetExcelSheet<Item>()!;
|
||||||
public static readonly ExcelSheet<Item> ItemSheetEnglish = Service.DataManager.GetExcelSheet<Item>(ClientLanguage.English)!;
|
public static readonly ExcelSheet<Item> ItemSheetEnglish = Service.DataManager.GetExcelSheet<Item>(ClientLanguage.English)!;
|
||||||
|
public static readonly ExcelSheet<ENpcResident> ENpcResidentSheet = Service.DataManager.GetExcelSheet<ENpcResident>()!;
|
||||||
|
public static readonly ExcelSheet<Level> LevelSheet = Service.DataManager.GetExcelSheet<Level>()!;
|
||||||
|
public static readonly ExcelSheet<Quest> QuestSheet = Service.DataManager.GetExcelSheet<Quest>()!;
|
||||||
public static readonly ExcelSheet<Materia> MateriaSheet = Service.DataManager.GetExcelSheet<Materia>()!;
|
public static readonly ExcelSheet<Materia> MateriaSheet = Service.DataManager.GetExcelSheet<Materia>()!;
|
||||||
public static readonly ExcelSheet<BaseParam> BaseParamSheet = Service.DataManager.GetExcelSheet<BaseParam>()!;
|
public static readonly ExcelSheet<BaseParam> BaseParamSheet = Service.DataManager.GetExcelSheet<BaseParam>()!;
|
||||||
public static readonly ExcelSheet<ItemFood> ItemFoodSheet = Service.DataManager.GetExcelSheet<ItemFood>()!;
|
public static readonly ExcelSheet<ItemFood> ItemFoodSheet = Service.DataManager.GetExcelSheet<ItemFood>()!;
|
||||||
|
|||||||
+16
-18
@@ -1,6 +1,7 @@
|
|||||||
using Craftimizer.Plugin.Windows;
|
using Craftimizer.Plugin.Windows;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Craftimizer.Utils;
|
using Craftimizer.Utils;
|
||||||
|
using Craftimizer.Windows;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
@@ -16,39 +17,35 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
public string Name => "Craftimizer";
|
public string Name => "Craftimizer";
|
||||||
public string Version { get; }
|
public string Version { get; }
|
||||||
public string Author { get; }
|
public string Author { get; }
|
||||||
public string Configuration { get; }
|
public string BuildConfiguration { get; }
|
||||||
public TextureWrap Icon { get; }
|
public TextureWrap Icon { get; }
|
||||||
|
|
||||||
public WindowSystem WindowSystem { get; }
|
public WindowSystem WindowSystem { get; }
|
||||||
public Settings SettingsWindow { get; }
|
public Settings SettingsWindow { get; }
|
||||||
public CraftingLog RecipeNoteWindow { get; }
|
public Craftimizer.Windows.RecipeNote RecipeNoteWindow { get; }
|
||||||
public Craft SynthesisWindow { get; }
|
public Craft SynthesisWindow { get; }
|
||||||
public Windows.Simulator? SimulatorWindow { get; set; }
|
public Windows.Simulator? SimulatorWindow { get; set; }
|
||||||
|
|
||||||
|
public Configuration Configuration { get; }
|
||||||
public Hooks Hooks { 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)
|
public Plugin([RequiredVersion("1.0")] DalamudPluginInterface pluginInterface)
|
||||||
{
|
{
|
||||||
Service.Plugin = this;
|
Service.Initialize(this, pluginInterface);
|
||||||
pluginInterface.Create<Service>();
|
|
||||||
Service.Configuration = pluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
|
Configuration = pluginInterface.GetPluginConfig() as Configuration ?? new();
|
||||||
|
Hooks = new();
|
||||||
|
RecipeNote = new();
|
||||||
|
IconManager = new();
|
||||||
|
WindowSystem = new(Name);
|
||||||
|
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
Version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion;
|
Version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion;
|
||||||
Author = assembly.GetCustomAttribute<AssemblyCompanyAttribute>()!.Company;
|
Author = assembly.GetCustomAttribute<AssemblyCompanyAttribute>()!.Company;
|
||||||
Configuration = assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()!.Configuration;
|
BuildConfiguration = assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()!.Configuration;
|
||||||
byte[] iconData;
|
Icon = IconManager.GetAssemblyTexture("icon.png");
|
||||||
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);
|
|
||||||
|
|
||||||
SettingsWindow = new();
|
SettingsWindow = new();
|
||||||
RecipeNoteWindow = new();
|
RecipeNoteWindow = new();
|
||||||
@@ -86,5 +83,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
SynthesisWindow.Dispose();
|
SynthesisWindow.Dispose();
|
||||||
RecipeNote.Dispose();
|
RecipeNote.Dispose();
|
||||||
Hooks.Dispose();
|
Hooks.Dispose();
|
||||||
|
IconManager.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-13
@@ -1,13 +1,11 @@
|
|||||||
using Dalamud.Data;
|
using Craftimizer.Utils;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState;
|
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using Dalamud.Game.Gui;
|
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
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.
|
#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 DalamudPluginInterface PluginInterface { get; private set; }
|
||||||
[PluginService] public static CommandManager CommandManager { get; private set; }
|
[PluginService] public static ICommandManager CommandManager { get; private set; }
|
||||||
[PluginService] public static ObjectTable Objects { get; private set; }
|
[PluginService] public static IObjectTable Objects { get; private set; }
|
||||||
[PluginService] public static SigScanner SigScanner { get; private set; }
|
[PluginService] public static ISigScanner SigScanner { get; private set; }
|
||||||
[PluginService] public static GameGui GameGui { get; private set; }
|
[PluginService] public static IGameGui GameGui { get; private set; }
|
||||||
[PluginService] public static ClientState ClientState { get; private set; }
|
[PluginService] public static IClientState ClientState { get; private set; }
|
||||||
[PluginService] public static DataManager DataManager { get; private set; }
|
[PluginService] public static IDataManager DataManager { get; private set; }
|
||||||
[PluginService] public static TargetManager TargetManager { 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 Condition Condition { get; private set; }
|
||||||
[PluginService] public static Framework Framework { get; private set; }
|
[PluginService] public static Framework Framework { get; private set; }
|
||||||
|
|
||||||
public static Plugin Plugin { get; internal set; }
|
public static Plugin Plugin { get; private set; }
|
||||||
public static Configuration Configuration { get; internal set; }
|
public static Configuration Configuration => Plugin.Configuration;
|
||||||
public static WindowSystem WindowSystem => Plugin.WindowSystem;
|
public static WindowSystem WindowSystem => Plugin.WindowSystem;
|
||||||
|
public static IconManager IconManager => Plugin.IconManager;
|
||||||
#pragma warning restore CS8618
|
#pragma warning restore CS8618
|
||||||
|
|
||||||
|
internal static void Initialize(Plugin plugin, DalamudPluginInterface iface)
|
||||||
|
{
|
||||||
|
Plugin = plugin;
|
||||||
|
iface.Create<Service>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ using Craftimizer.Simulator;
|
|||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using System;
|
using System;
|
||||||
@@ -10,8 +13,10 @@ using System.Linq;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Action = Lumina.Excel.GeneratedSheets.Action;
|
using Action = Lumina.Excel.GeneratedSheets.Action;
|
||||||
|
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
||||||
using ClassJob = Craftimizer.Simulator.ClassJob;
|
using ClassJob = Craftimizer.Simulator.ClassJob;
|
||||||
using Condition = Craftimizer.Simulator.Condition;
|
using Condition = Craftimizer.Simulator.Condition;
|
||||||
|
using Status = Lumina.Excel.GeneratedSheets.Status;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
@@ -85,11 +90,11 @@ internal static class ActionUtils
|
|||||||
{
|
{
|
||||||
var (craftAction, action) = GetActionRow(me, classJob);
|
var (craftAction, action) = GetActionRow(me, classJob);
|
||||||
if (craftAction != null)
|
if (craftAction != null)
|
||||||
return Icons.GetIconFromId(craftAction.Icon);
|
return Service.IconManager.GetIcon(craftAction.Icon);
|
||||||
if (action != null)
|
if (action != null)
|
||||||
return Icons.GetIconFromId(action.Icon);
|
return Service.IconManager.GetIcon(action.Icon);
|
||||||
// Old "Steady Hand" 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)
|
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) =>
|
public static sbyte GetExpArrayIdx(this ClassJob me) =>
|
||||||
LuminaSheets.ClassJobSheet.GetRow(me.GetClassJobIndex())!.ExpArrayIndex;
|
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);
|
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) =>
|
public static bool IsClassJob(this ClassJobCategory me, ClassJob classJob) =>
|
||||||
classJob switch
|
classJob switch
|
||||||
{
|
{
|
||||||
@@ -303,7 +320,7 @@ internal static class EffectUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static TextureWrap GetIcon(this EffectType me, int strength) =>
|
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)
|
public static string GetTooltip(this EffectType me, int strength, int duration)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Utils;
|
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 GearsetStats(int CP, int Craftsmanship, int Control);
|
||||||
public record struct GearsetMateria(ushort Type, ushort Grade);
|
public record struct GearsetMateria(ushort Type, ushort Grade);
|
||||||
@@ -117,9 +117,12 @@ internal static unsafe class Gearsets
|
|||||||
|
|
||||||
public static bool IsSpecialistSoulCrystal(GearsetItem item)
|
public static bool IsSpecialistSoulCrystal(GearsetItem item)
|
||||||
{
|
{
|
||||||
|
if (item.itemId == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
var luminaItem = LuminaSheets.ItemSheet.GetRow(item.itemId)!;
|
var luminaItem = LuminaSheets.ItemSheet.GetRow(item.itemId)!;
|
||||||
// Soul Crystal ItemUICategory DoH Category
|
// Soul Crystal ItemUICategory DoH Category
|
||||||
return luminaItem.ItemUICategory.Row != 62 && luminaItem.ClassJobUse.Value!.ClassJobCategory.Row == 33;
|
return luminaItem.ItemUICategory.Row == 62 && luminaItem.ClassJobUse.Value!.ClassJobCategory.Row == 33;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsSplendorousTool(GearsetItem item) =>
|
public static bool IsSplendorousTool(GearsetItem item) =>
|
||||||
|
|||||||
@@ -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<uint, IDalamudTextureWrap> iconCache = new();
|
||||||
|
private readonly Dictionary<string, IDalamudTextureWrap> textureCache = new();
|
||||||
|
private readonly Dictionary<string, TextureWrap> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,52 @@ using CSRecipeNote = FFXIVClientStructs.FFXIV.Client.Game.UI.RecipeNote;
|
|||||||
|
|
||||||
namespace Craftimizer.Utils;
|
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 sealed unsafe class RecipeNote : IDisposable
|
||||||
{
|
{
|
||||||
public AddonRecipeNote* AddonRecipe { get; private set; }
|
public AddonRecipeNote* AddonRecipe { get; private set; }
|
||||||
|
|||||||
@@ -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<char, SeIconChar> levelNumReplacements = new(new Dictionary<char, SeIconChar>
|
||||||
|
{
|
||||||
|
['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>(T value) where T : IBinaryInteger<T>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -473,7 +473,7 @@ public class Settings : Window
|
|||||||
ImGui.Image(icon.ImGuiHandle, new(icon.Width, icon.Height));
|
ImGui.Image(icon.ImGuiHandle, new(icon.Width, icon.Height));
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
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.Text($"By {plugin.Author} (");
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
ImGuiUtils.Hyperlink("WorkingRobot", "https://github.com/WorkingRobot");
|
ImGuiUtils.Hyperlink("WorkingRobot", "https://github.com/WorkingRobot");
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public sealed partial class Simulator : Window, IDisposable
|
|||||||
{
|
{
|
||||||
var imageSize = new Vector2(ImGui.GetFontSize() * 2.25f);
|
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.SameLine();
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f);
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f);
|
||||||
ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString());
|
ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString());
|
||||||
|
|||||||
Reference in New Issue
Block a user