Updates for v9, begin ui rework
This commit is contained in:
@@ -28,6 +28,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
@@ -50,6 +56,10 @@
|
||||
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>$(DalamudLibPath)Dalamud.Interface.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImGui.NET">
|
||||
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
||||
<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.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ public static class LuminaSheets
|
||||
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> 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<BaseParam> BaseParamSheet = Service.DataManager.GetExcelSheet<BaseParam>()!;
|
||||
public static readonly ExcelSheet<ItemFood> ItemFoodSheet = Service.DataManager.GetExcelSheet<ItemFood>()!;
|
||||
|
||||
+16
-18
@@ -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>();
|
||||
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<AssemblyInformationalVersionAttribute>()!.InformationalVersion;
|
||||
Author = assembly.GetCustomAttribute<AssemblyCompanyAttribute>()!.Company;
|
||||
Configuration = assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()!.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<AssemblyConfigurationAttribute>()!.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();
|
||||
}
|
||||
}
|
||||
|
||||
+18
-13
@@ -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<Service>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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;
|
||||
|
||||
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; }
|
||||
|
||||
@@ -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.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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user