Apply csharpier reflow across source tree
Reformats the entire Craftimizer source tree with dotnet csharpier 1.2.6 to match the Hellion Forge house style (matches what HellionChat enforces in its pre-push pipeline). Pure whitespace + using-block sorting; no semantic changes. This is a one-time noisy commit. Future code edits in this fork should land csharpier-clean because the pre-push hook (introduced in the next commit) runs `dotnet csharpier check Craftimizer/` as Block C of the preflight gate. Trade-off acknowledged: this widens the merge gap with upstream Craftimizer should Asriel ever resume maintenance. Given the upstream has been dormant since FFXIV 7.4 and the fork is light-rename only (internal namespaces unchanged), the marginal cost is acceptable.
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Craftimizer.Solver;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Craftimizer.Simulator.Actions;
|
||||||
|
using Craftimizer.Solver;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
@@ -13,7 +13,8 @@ public class StoredActionTypeConverter : JsonConverter<ActionType[]>
|
|||||||
public override ActionType[] Read(
|
public override ActionType[] Read(
|
||||||
ref Utf8JsonReader reader,
|
ref Utf8JsonReader reader,
|
||||||
Type typeToConvert,
|
Type typeToConvert,
|
||||||
JsonSerializerOptions options)
|
JsonSerializerOptions options
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (reader.TokenType != JsonTokenType.StartArray)
|
if (reader.TokenType != JsonTokenType.StartArray)
|
||||||
throw new JsonException();
|
throw new JsonException();
|
||||||
@@ -87,7 +88,8 @@ public class StoredActionTypeConverter : JsonConverter<ActionType[]>
|
|||||||
public override void Write(
|
public override void Write(
|
||||||
Utf8JsonWriter writer,
|
Utf8JsonWriter writer,
|
||||||
ActionType[] value,
|
ActionType[] value,
|
||||||
JsonSerializerOptions options)
|
JsonSerializerOptions options
|
||||||
|
)
|
||||||
{
|
{
|
||||||
writer.WriteStartArray();
|
writer.WriteStartArray();
|
||||||
foreach (var item in value)
|
foreach (var item in value)
|
||||||
@@ -101,14 +103,18 @@ public class Macro
|
|||||||
public static event Action<Macro>? OnMacroChanged;
|
public static event Action<Macro>? OnMacroChanged;
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
[JsonInclude] [JsonPropertyName("Actions")]
|
|
||||||
|
[JsonInclude]
|
||||||
|
[JsonPropertyName("Actions")]
|
||||||
internal ActionType[] actions { get; set; } = [];
|
internal ActionType[] actions { get; set; } = [];
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IReadOnlyList<ActionType> Actions
|
public IReadOnlyList<ActionType> Actions
|
||||||
{
|
{
|
||||||
get => actions;
|
get => actions;
|
||||||
set => ActionEnumerable = value;
|
set => ActionEnumerable = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IEnumerable<ActionType> ActionEnumerable
|
public IEnumerable<ActionType> ActionEnumerable
|
||||||
{
|
{
|
||||||
@@ -127,7 +133,7 @@ public class MacroCopyConfiguration
|
|||||||
OpenWindow, // useful for big macros
|
OpenWindow, // useful for big macros
|
||||||
CopyToMacro, // (add option for down or right) (max macro count; open copy-paste window if too much)
|
CopyToMacro, // (add option for down or right) (max macro count; open copy-paste window if too much)
|
||||||
CopyToClipboard,
|
CopyToClipboard,
|
||||||
CopyToMacroMate
|
CopyToMacroMate,
|
||||||
}
|
}
|
||||||
|
|
||||||
public CopyType Type { get; set; } = CopyType.OpenWindow;
|
public CopyType Type { get; set; } = CopyType.OpenWindow;
|
||||||
@@ -172,13 +178,15 @@ public partial class Configuration
|
|||||||
{
|
{
|
||||||
Colorful,
|
Colorful,
|
||||||
Simple,
|
Simple,
|
||||||
None
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static event Action? OnMacroListChanged;
|
public static event Action? OnMacroListChanged;
|
||||||
|
|
||||||
[JsonInclude] [JsonPropertyName("Macros")]
|
[JsonInclude]
|
||||||
|
[JsonPropertyName("Macros")]
|
||||||
internal List<Macro> macros { get; private set; } = [];
|
internal List<Macro> macros { get; private set; } = [];
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IReadOnlyList<Macro> Macros => macros;
|
public IReadOnlyList<Macro> Macros => macros;
|
||||||
public int ReliabilitySimulationCount { get; set; } = 1000;
|
public int ReliabilitySimulationCount { get; set; } = 1000;
|
||||||
@@ -244,10 +252,8 @@ public partial class Configuration
|
|||||||
[JsonSerializable(typeof(Configuration))]
|
[JsonSerializable(typeof(Configuration))]
|
||||||
internal sealed partial class JsonContext : JsonSerializerContext
|
internal sealed partial class JsonContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
public static JsonSerializerOptions DeserializeOptions { get; } = new()
|
public static JsonSerializerOptions DeserializeOptions { get; } =
|
||||||
{
|
new() { Converters = { new StoredActionTypeConverter() } };
|
||||||
Converters = { new StoredActionTypeConverter() }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
@@ -265,7 +271,8 @@ public partial class Configuration
|
|||||||
using var stream = f.OpenRead();
|
using var stream = f.OpenRead();
|
||||||
|
|
||||||
// System.InvalidOperationException: Setting init-only properties is not supported in source generation mode.
|
// System.InvalidOperationException: Setting init-only properties is not supported in source generation mode.
|
||||||
return JsonSerializer.Deserialize<Configuration>(stream, JsonContext.DeserializeOptions) ?? new();
|
return JsonSerializer.Deserialize<Configuration>(stream, JsonContext.DeserializeOptions)
|
||||||
|
?? new();
|
||||||
}
|
}
|
||||||
return new();
|
return new();
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-20
@@ -1,9 +1,9 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
@@ -11,22 +11,50 @@ internal static unsafe class ImGuiExtras
|
|||||||
{
|
{
|
||||||
// https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/CodeGenerator/definitions/cimgui/definitions.json#L25394
|
// https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/CodeGenerator/definitions/cimgui/definitions.json#L25394
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern unsafe byte igInputTextEx(byte* label, byte* hint, byte* buf, int buf_size, Vector2 size, ImGuiInputTextFlags flags, ImGuiInputTextCallback? callback, void* user_data);
|
private static extern unsafe byte igInputTextEx(
|
||||||
|
byte* label,
|
||||||
|
byte* hint,
|
||||||
|
byte* buf,
|
||||||
|
int buf_size,
|
||||||
|
Vector2 size,
|
||||||
|
ImGuiInputTextFlags flags,
|
||||||
|
ImGuiInputTextCallback? callback,
|
||||||
|
void* user_data
|
||||||
|
);
|
||||||
|
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern bool igItemAdd(Vector4 bb, uint id, Vector4* navBb, uint flags);
|
private static extern bool igItemAdd(Vector4 bb, uint id, Vector4* navBb, uint flags);
|
||||||
|
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern bool igButtonBehavior(Vector4 bb, uint id, bool* outHovered, bool* outHeld, ImGuiButtonFlags flags);
|
private static extern bool igButtonBehavior(
|
||||||
|
Vector4 bb,
|
||||||
|
uint id,
|
||||||
|
bool* outHovered,
|
||||||
|
bool* outHeld,
|
||||||
|
ImGuiButtonFlags flags
|
||||||
|
);
|
||||||
|
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern bool igItemSize_Vec2(Vector2 size, float text_baseline_y = -1.0f);
|
private static extern bool igItemSize_Vec2(Vector2 size, float text_baseline_y = -1.0f);
|
||||||
|
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void igRenderFrame(Vector2 p_min, Vector2 p_max, uint fill_col, bool border = true, float rounding = 0.0f);
|
private static extern void igRenderFrame(
|
||||||
|
Vector2 p_min,
|
||||||
|
Vector2 p_max,
|
||||||
|
uint fill_col,
|
||||||
|
bool border = true,
|
||||||
|
float rounding = 0.0f
|
||||||
|
);
|
||||||
|
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void igRenderRectFilledRangeH(ImDrawList* draw_list, Vector4* rect, uint col, float x_start_norm, float x_end_norm, float rounding);
|
private static extern void igRenderRectFilledRangeH(
|
||||||
|
ImDrawList* draw_list,
|
||||||
|
Vector4* rect,
|
||||||
|
uint col,
|
||||||
|
float x_start_norm,
|
||||||
|
float x_end_norm,
|
||||||
|
float rounding
|
||||||
|
);
|
||||||
|
|
||||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern ImGuiItemFlags igGetItemFlags();
|
private static extern ImGuiItemFlags igGetItemFlags();
|
||||||
@@ -74,7 +102,16 @@ internal static unsafe class ImGuiExtras
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Based off of code from InputTextWithHint: https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/ImGui.NET/ImGui.Manual.cs#L271
|
// Based off of code from InputTextWithHint: https://github.com/ImGuiNET/ImGui.NET/blob/069363672fed940ebdaa02f9b032c282b66467c7/src/ImGui.NET/ImGui.Manual.cs#L271
|
||||||
public static unsafe bool InputTextEx(string label, string hint, ref string input, int maxLength, Vector2 size, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None, ImGuiInputTextCallback? callback = null, IntPtr user_data = default)
|
public static unsafe bool InputTextEx(
|
||||||
|
string label,
|
||||||
|
string hint,
|
||||||
|
ref string input,
|
||||||
|
int maxLength,
|
||||||
|
Vector2 size,
|
||||||
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags.None,
|
||||||
|
ImGuiInputTextCallback? callback = null,
|
||||||
|
IntPtr user_data = default
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var utf8LabelByteCount = Encoding.UTF8.GetByteCount(label);
|
var utf8LabelByteCount = Encoding.UTF8.GetByteCount(label);
|
||||||
byte* utf8LabelBytes;
|
byte* utf8LabelBytes;
|
||||||
@@ -132,7 +169,8 @@ internal static unsafe class ImGuiExtras
|
|||||||
size,
|
size,
|
||||||
flags,
|
flags,
|
||||||
callback,
|
callback,
|
||||||
user_data.ToPointer());
|
user_data.ToPointer()
|
||||||
|
);
|
||||||
if (!AreStringsEqual(originalUtf8InputBytes, inputBufSize, utf8InputBytes))
|
if (!AreStringsEqual(originalUtf8InputBytes, inputBufSize, utf8InputBytes))
|
||||||
{
|
{
|
||||||
input = StringFromPtr(utf8InputBytes);
|
input = StringFromPtr(utf8InputBytes);
|
||||||
@@ -155,8 +193,7 @@ internal static unsafe class ImGuiExtras
|
|||||||
return result != 0;
|
return result != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe bool ItemAdd(Vector4 bb, uint id) =>
|
public static unsafe bool ItemAdd(Vector4 bb, uint id) => ItemAdd(bb, id, out _);
|
||||||
ItemAdd(bb, id, out _);
|
|
||||||
|
|
||||||
public static unsafe bool ItemAdd(Vector4 bb, uint id, out Vector4 navBb, uint flags = 0)
|
public static unsafe bool ItemAdd(Vector4 bb, uint id, out Vector4 navBb, uint flags = 0)
|
||||||
{
|
{
|
||||||
@@ -166,7 +203,13 @@ internal static unsafe class ImGuiExtras
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe bool ButtonBehavior(Vector4 bb, uint id, out bool hovered, out bool held, ImGuiButtonFlags flags)
|
public static unsafe bool ButtonBehavior(
|
||||||
|
Vector4 bb,
|
||||||
|
uint id,
|
||||||
|
out bool hovered,
|
||||||
|
out bool held,
|
||||||
|
ImGuiButtonFlags flags
|
||||||
|
)
|
||||||
{
|
{
|
||||||
fixed (bool* hoveredPtr = &hovered)
|
fixed (bool* hoveredPtr = &hovered)
|
||||||
fixed (bool* heldPtr = &held)
|
fixed (bool* heldPtr = &held)
|
||||||
@@ -175,19 +218,34 @@ internal static unsafe class ImGuiExtras
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void RenderFrame(Vector2 p_min, Vector2 p_max, uint fill_col, bool border = true, float rounding = 0.0f) =>
|
public static unsafe void RenderFrame(
|
||||||
igRenderFrame(p_min, p_max, fill_col, border, rounding);
|
Vector2 p_min,
|
||||||
|
Vector2 p_max,
|
||||||
|
uint fill_col,
|
||||||
|
bool border = true,
|
||||||
|
float rounding = 0.0f
|
||||||
|
) => igRenderFrame(p_min, p_max, fill_col, border, rounding);
|
||||||
|
|
||||||
public static unsafe void RenderRectFilledRangeH(ImDrawListPtr draw_list, Vector4 rect, uint col, float x_start_norm, float x_end_norm, float rounding) =>
|
public static unsafe void RenderRectFilledRangeH(
|
||||||
igRenderRectFilledRangeH(draw_list, &rect, col, x_start_norm, x_end_norm, rounding);
|
ImDrawListPtr draw_list,
|
||||||
|
Vector4 rect,
|
||||||
|
uint col,
|
||||||
|
float x_start_norm,
|
||||||
|
float x_end_norm,
|
||||||
|
float rounding
|
||||||
|
) => igRenderRectFilledRangeH(draw_list, &rect, col, x_start_norm, x_end_norm, rounding);
|
||||||
|
|
||||||
public static unsafe bool ItemSize(Vector2 size, float text_baseline_y = -1.0f) =>
|
public static unsafe bool ItemSize(Vector2 size, float text_baseline_y = -1.0f) =>
|
||||||
igItemSize_Vec2(size, text_baseline_y);
|
igItemSize_Vec2(size, text_baseline_y);
|
||||||
|
|
||||||
public static unsafe ImGuiItemFlags GetItemFlags() =>
|
public static unsafe ImGuiItemFlags GetItemFlags() => igGetItemFlags();
|
||||||
igGetItemFlags();
|
|
||||||
|
|
||||||
public static unsafe int? CalcWordWrapPositionA(this ImFontPtr font, float scale, ReadOnlySpan<char> text, float wrap_width)
|
public static unsafe int? CalcWordWrapPositionA(
|
||||||
|
this ImFontPtr font,
|
||||||
|
float scale,
|
||||||
|
ReadOnlySpan<char> text,
|
||||||
|
float wrap_width
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var utf8TextByteCount = Encoding.UTF8.GetByteCount(text);
|
var utf8TextByteCount = Encoding.UTF8.GetByteCount(text);
|
||||||
byte* utf8TextBytes;
|
byte* utf8TextBytes;
|
||||||
@@ -202,7 +260,13 @@ internal static unsafe class ImGuiExtras
|
|||||||
}
|
}
|
||||||
GetUtf8(text, utf8TextBytes, utf8TextByteCount);
|
GetUtf8(text, utf8TextBytes, utf8TextByteCount);
|
||||||
|
|
||||||
var ret = ImGuiNative.CalcWordWrapPositionA(font, scale, utf8TextBytes, utf8TextBytes + utf8TextByteCount, wrap_width);
|
var ret = ImGuiNative.CalcWordWrapPositionA(
|
||||||
|
font,
|
||||||
|
scale,
|
||||||
|
utf8TextBytes,
|
||||||
|
utf8TextBytes + utf8TextByteCount,
|
||||||
|
wrap_width
|
||||||
|
);
|
||||||
|
|
||||||
int? retVal = null;
|
int? retVal = null;
|
||||||
if (utf8TextBytes <= ret && ret <= utf8TextBytes + utf8TextByteCount)
|
if (utf8TextBytes <= ret && ret <= utf8TextBytes + utf8TextByteCount)
|
||||||
@@ -217,10 +281,12 @@ internal static unsafe class ImGuiExtras
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe bool SetDragDropPayload<T>(string type, T data) where T : unmanaged =>
|
public static unsafe bool SetDragDropPayload<T>(string type, T data)
|
||||||
|
where T : unmanaged =>
|
||||||
ImGui.SetDragDropPayload(type, MemoryMarshal.AsBytes(new ReadOnlySpan<T>(&data, 1)));
|
ImGui.SetDragDropPayload(type, MemoryMarshal.AsBytes(new ReadOnlySpan<T>(&data, 1)));
|
||||||
|
|
||||||
public static unsafe bool AcceptDragDropPayload<T>(string type, out T data) where T : unmanaged
|
public static unsafe bool AcceptDragDropPayload<T>(string type, out T data)
|
||||||
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
var payload = ImGui.AcceptDragDropPayload(type);
|
var payload = ImGui.AcceptDragDropPayload(type);
|
||||||
if (payload.IsNull || payload.DataSize != sizeof(T))
|
if (payload.IsNull || payload.DataSize != sizeof(T))
|
||||||
|
|||||||
+241
-56
@@ -1,11 +1,3 @@
|
|||||||
using Craftimizer.Utils;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.ManagedFontAtlas;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Bindings.ImPlot;
|
|
||||||
using MathNet.Numerics.Statistics;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
@@ -15,12 +7,24 @@ using System.Numerics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Craftimizer.Utils;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Bindings.ImPlot;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.ManagedFontAtlas;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using MathNet.Numerics.Statistics;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
internal static class ImGuiUtils
|
internal static class ImGuiUtils
|
||||||
{
|
{
|
||||||
private static readonly Stack<(Vector2 Min, Vector2 Max, float TopPadding)> GroupPanelLabelStack = new();
|
private static readonly Stack<(
|
||||||
|
Vector2 Min,
|
||||||
|
Vector2 Max,
|
||||||
|
float TopPadding
|
||||||
|
)> GroupPanelLabelStack = new();
|
||||||
|
|
||||||
// Adapted from https://github.com/ocornut/imgui/issues/1496#issuecomment-655048353
|
// Adapted from https://github.com/ocornut/imgui/issues/1496#issuecomment-655048353
|
||||||
// width = -1 -> size to parent
|
// width = -1 -> size to parent
|
||||||
@@ -62,7 +66,9 @@ internal static class ImGuiUtils
|
|||||||
var textFrameHeight = ImGui.GetFrameHeight();
|
var textFrameHeight = ImGui.GetFrameHeight();
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted(name);
|
ImGui.TextUnformatted(name);
|
||||||
GroupPanelLabelStack.Push((ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), textFrameHeight / 2f)); // push rect to stack
|
GroupPanelLabelStack.Push(
|
||||||
|
(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), textFrameHeight / 2f)
|
||||||
|
); // push rect to stack
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
ImGui.Dummy(new Vector2(0f, textFrameHeight + itemSpacing.Y)); // shifts content by fh + is.y
|
ImGui.Dummy(new Vector2(0f, textFrameHeight + itemSpacing.Y)); // shifts content by fh + is.y
|
||||||
}
|
}
|
||||||
@@ -112,18 +118,29 @@ internal static class ImGuiUtils
|
|||||||
{
|
{
|
||||||
var (minClip, maxClip) = i switch
|
var (minClip, maxClip) = i switch
|
||||||
{
|
{
|
||||||
0 => (new Vector2(float.NegativeInfinity), new Vector2(labelMin.X, float.PositiveInfinity)),
|
0 => (
|
||||||
1 => (new Vector2(labelMax.X, float.NegativeInfinity), new Vector2(float.PositiveInfinity)),
|
new Vector2(float.NegativeInfinity),
|
||||||
2 => (new Vector2(labelMin.X, float.NegativeInfinity), new Vector2(labelMax.X, labelMin.Y)),
|
new Vector2(labelMin.X, float.PositiveInfinity)
|
||||||
3 => (new Vector2(labelMin.X, labelMax.Y), new Vector2(labelMax.X, float.PositiveInfinity)),
|
),
|
||||||
_ => (Vector2.Zero, Vector2.Zero)
|
1 => (
|
||||||
|
new Vector2(labelMax.X, float.NegativeInfinity),
|
||||||
|
new Vector2(float.PositiveInfinity)
|
||||||
|
),
|
||||||
|
2 => (
|
||||||
|
new Vector2(labelMin.X, float.NegativeInfinity),
|
||||||
|
new Vector2(labelMax.X, labelMin.Y)
|
||||||
|
),
|
||||||
|
3 => (
|
||||||
|
new Vector2(labelMin.X, labelMax.Y),
|
||||||
|
new Vector2(labelMax.X, float.PositiveInfinity)
|
||||||
|
),
|
||||||
|
_ => (Vector2.Zero, Vector2.Zero),
|
||||||
};
|
};
|
||||||
|
|
||||||
ImGui.PushClipRect(minClip, maxClip, true);
|
ImGui.PushClipRect(minClip, maxClip, true);
|
||||||
ImGui.GetWindowDrawList().AddRect(
|
ImGui
|
||||||
innerMin, innerMax,
|
.GetWindowDrawList()
|
||||||
ImGui.GetColorU32(ImGuiCol.Border),
|
.AddRect(innerMin, innerMax, ImGui.GetColorU32(ImGuiCol.Border), itemSpacing.X);
|
||||||
itemSpacing.X);
|
|
||||||
ImGui.PopClipRect();
|
ImGui.PopClipRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,15 +160,23 @@ internal static class ImGuiUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static float Lerp(float a, float b, float t) =>
|
private static float Lerp(float a, float b, float t) => MathF.FusedMultiplyAdd(b - a, t, a);
|
||||||
MathF.FusedMultiplyAdd(b - a, t, a);
|
|
||||||
|
|
||||||
private readonly record struct ArcEdge(float Angle, Vector2 Point)
|
private readonly record struct ArcEdge(float Angle, Vector2 Point)
|
||||||
{
|
{
|
||||||
public ArcEdge(float angle) : this(angle, UnitCircle(angle)) { }
|
public ArcEdge(float angle)
|
||||||
|
: this(angle, UnitCircle(angle)) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ArcSegment(Vector2 o, ArcEdge prev, ArcEdge cur, ArcEdge? next, float radius, float ratio, uint color)
|
private static void ArcSegment(
|
||||||
|
Vector2 o,
|
||||||
|
ArcEdge prev,
|
||||||
|
ArcEdge cur,
|
||||||
|
ArcEdge? next,
|
||||||
|
float radius,
|
||||||
|
float ratio,
|
||||||
|
uint color
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var d = ImGui.GetWindowDrawList();
|
var d = ImGui.GetWindowDrawList();
|
||||||
|
|
||||||
@@ -164,7 +189,15 @@ internal static class ImGuiUtils
|
|||||||
d.PathFillConvex(color);
|
d.PathFillConvex(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Arc(float startAngle, float endAngle, float radius, float ratio, uint backgroundColor, uint filledColor, bool addDummy = true)
|
public static void Arc(
|
||||||
|
float startAngle,
|
||||||
|
float endAngle,
|
||||||
|
float radius,
|
||||||
|
float ratio,
|
||||||
|
uint backgroundColor,
|
||||||
|
uint filledColor,
|
||||||
|
bool addDummy = true
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Fix normals when drawing (for antialiasing)
|
// Fix normals when drawing (for antialiasing)
|
||||||
if (startAngle > endAngle)
|
if (startAngle > endAngle)
|
||||||
@@ -224,9 +257,16 @@ internal static class ImGuiUtils
|
|||||||
ImGui.Dummy(new Vector2(radius * 2));
|
ImGui.Dummy(new Vector2(radius * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ArcProgress(float value, float radius, float ratio, uint backgroundColor, uint filledColor)
|
public static void ArcProgress(
|
||||||
|
float value,
|
||||||
|
float radius,
|
||||||
|
float ratio,
|
||||||
|
uint backgroundColor,
|
||||||
|
uint filledColor
|
||||||
|
)
|
||||||
{
|
{
|
||||||
float startAngle, endAngle;
|
float startAngle,
|
||||||
|
endAngle;
|
||||||
|
|
||||||
// https://github.com/ocornut/imgui/commit/c895e987adf746a997b655c64a6a8916c549ff6f#diff-d750e175eb584ba76bc560b8e54cf113ccbb31dd33f75078c1588925e197a3afR1304-R1310
|
// https://github.com/ocornut/imgui/commit/c895e987adf746a997b655c64a6a8916c549ff6f#diff-d750e175eb584ba76bc560b8e54cf113ccbb31dd33f75078c1588925e197a3afR1304-R1310
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
@@ -271,19 +311,34 @@ internal static class ImGuiUtils
|
|||||||
bar_end = bar_begin + bar_fraction;
|
bar_end = bar_begin + bar_fraction;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiExtras.RenderFrame(bbMin, bbMax, ImGui.GetColorU32(ImGuiCol.FrameBg), true, style.FrameRounding);
|
ImGuiExtras.RenderFrame(
|
||||||
|
bbMin,
|
||||||
|
bbMax,
|
||||||
|
ImGui.GetColorU32(ImGuiCol.FrameBg),
|
||||||
|
true,
|
||||||
|
style.FrameRounding
|
||||||
|
);
|
||||||
|
|
||||||
bbMin += new Vector2(style.FrameBorderSize);
|
bbMin += new Vector2(style.FrameBorderSize);
|
||||||
bbMax -= new Vector2(style.FrameBorderSize);
|
bbMax -= new Vector2(style.FrameBorderSize);
|
||||||
|
|
||||||
ImGuiExtras.RenderRectFilledRangeH(ImGui.GetWindowDrawList(), new(bbMin.X, bbMin.Y, bbMax.X, bbMax.Y), ImGui.GetColorU32(ImGuiCol.PlotHistogram), bar_begin, bar_end, style.FrameRounding);
|
ImGuiExtras.RenderRectFilledRangeH(
|
||||||
|
ImGui.GetWindowDrawList(),
|
||||||
|
new(bbMin.X, bbMin.Y, bbMax.X, bbMax.Y),
|
||||||
|
ImGui.GetColorU32(ImGuiCol.PlotHistogram),
|
||||||
|
bar_begin,
|
||||||
|
bar_end,
|
||||||
|
style.FrameRounding
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ViolinData
|
public sealed class ViolinData
|
||||||
{
|
{
|
||||||
public struct Point(float x, float y, float y2)
|
public struct Point(float x, float y, float y2)
|
||||||
{
|
{
|
||||||
public float X = x, Y = y, Y2 = y2;
|
public float X = x,
|
||||||
|
Y = y,
|
||||||
|
Y2 = y2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<Point> Data => (DataArray ?? []).AsSpan();
|
public ReadOnlySpan<Point> Data => (DataArray ?? []).AsSpan();
|
||||||
@@ -291,7 +346,13 @@ internal static class ImGuiUtils
|
|||||||
public readonly float Min;
|
public readonly float Min;
|
||||||
public readonly float Max;
|
public readonly float Max;
|
||||||
|
|
||||||
public ViolinData(IEnumerable<int> samples, float min, float max, int resolution, double bandwidth)
|
public ViolinData(
|
||||||
|
IEnumerable<int> samples,
|
||||||
|
float min,
|
||||||
|
float max,
|
||||||
|
int resolution,
|
||||||
|
double bandwidth
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Min = min;
|
Min = min;
|
||||||
Max = max;
|
Max = max;
|
||||||
@@ -300,9 +361,12 @@ internal static class ImGuiUtils
|
|||||||
_ = Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
var s = Stopwatch.StartNew();
|
var s = Stopwatch.StartNew();
|
||||||
var data = ParallelEnumerable.Range(0, resolution + 1)
|
var data = ParallelEnumerable
|
||||||
|
.Range(0, resolution + 1)
|
||||||
.Select(n => Lerp(min, max, n / (float)resolution))
|
.Select(n => Lerp(min, max, n / (float)resolution))
|
||||||
.Select(n => (n, (float)KernelDensity.EstimateGaussian(n, bandwidth, samplesList)))
|
.Select(n =>
|
||||||
|
(n, (float)KernelDensity.EstimateGaussian(n, bandwidth, samplesList))
|
||||||
|
)
|
||||||
.Select(n => new Point(n.n, n.Item2, -n.Item2));
|
.Select(n => new Point(n.n, n.Item2, -n.Item2));
|
||||||
// ParallelQuery doesn't support [.. data] correctly. The plots look very wrong.
|
// ParallelQuery doesn't support [.. data] correctly. The plots look very wrong.
|
||||||
#pragma warning disable IDE0305 // Simplify collection initialization
|
#pragma warning disable IDE0305 // Simplify collection initialization
|
||||||
@@ -320,10 +384,22 @@ internal static class ImGuiUtils
|
|||||||
using var plotBg = ImRaii2.PushColor(ImPlotCol.Bg, Vector4.Zero);
|
using var plotBg = ImRaii2.PushColor(ImPlotCol.Bg, Vector4.Zero);
|
||||||
using var fill = ImRaii2.PushColor(ImPlotCol.Fill, Vector4.One.WithAlpha(.5f));
|
using var fill = ImRaii2.PushColor(ImPlotCol.Fill, Vector4.One.WithAlpha(.5f));
|
||||||
|
|
||||||
using var plot = ImRaii2.Plot("##violin", size, ImPlotFlags.CanvasOnly | ImPlotFlags.NoInputs | ImPlotFlags.NoChild | ImPlotFlags.NoFrame);
|
using var plot = ImRaii2.Plot(
|
||||||
|
"##violin",
|
||||||
|
size,
|
||||||
|
ImPlotFlags.CanvasOnly
|
||||||
|
| ImPlotFlags.NoInputs
|
||||||
|
| ImPlotFlags.NoChild
|
||||||
|
| ImPlotFlags.NoFrame
|
||||||
|
);
|
||||||
if (plot.Success)
|
if (plot.Success)
|
||||||
{
|
{
|
||||||
ImPlot.SetupAxes([], [], ImPlotAxisFlags.NoDecorations, ImPlotAxisFlags.NoDecorations | ImPlotAxisFlags.AutoFit);
|
ImPlot.SetupAxes(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
ImPlotAxisFlags.NoDecorations,
|
||||||
|
ImPlotAxisFlags.NoDecorations | ImPlotAxisFlags.AutoFit
|
||||||
|
);
|
||||||
ImPlot.SetupAxisLimits(ImAxis.X1, data.Min, data.Max, ImPlotCond.Always);
|
ImPlot.SetupAxisLimits(ImAxis.X1, data.Min, data.Max, ImPlotCond.Always);
|
||||||
ImPlot.SetupFinish();
|
ImPlot.SetupFinish();
|
||||||
|
|
||||||
@@ -334,14 +410,24 @@ internal static class ImGuiUtils
|
|||||||
var label_id = stackalloc byte[] { (byte)'\0' };
|
var label_id = stackalloc byte[] { (byte)'\0' };
|
||||||
fixed (ViolinData.Point* p = points)
|
fixed (ViolinData.Point* p = points)
|
||||||
{
|
{
|
||||||
ImPlot.PlotShaded(label_id, &p->X, &p->Y, &p->Y2, points.Length, ImPlotShadedFlags.None, 0, sizeof(ViolinData.Point));
|
ImPlot.PlotShaded(
|
||||||
|
label_id,
|
||||||
|
&p->X,
|
||||||
|
&p->Y,
|
||||||
|
&p->Y2,
|
||||||
|
points.Length,
|
||||||
|
ImPlotShadedFlags.None,
|
||||||
|
0,
|
||||||
|
sizeof(ViolinData.Point)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class SearchableComboData<T> where T : IEquatable<T>
|
private sealed class SearchableComboData<T>
|
||||||
|
where T : IEquatable<T>
|
||||||
{
|
{
|
||||||
public readonly ImmutableArray<T> items;
|
public readonly ImmutableArray<T> items;
|
||||||
public List<T> filteredItems;
|
public List<T> filteredItems;
|
||||||
@@ -381,20 +467,26 @@ internal static class ImGuiUtils
|
|||||||
cts = new();
|
cts = new();
|
||||||
var token = cts.Token;
|
var token = cts.Token;
|
||||||
task = Task.Run(() => FilterTask(inp, token), token)
|
task = Task.Run(() => FilterTask(inp, token), token)
|
||||||
.ContinueWith(t =>
|
.ContinueWith(
|
||||||
|
t =>
|
||||||
{
|
{
|
||||||
if (cts.IsCancellationRequested)
|
if (cts.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
t.Exception!.Flatten().Handle(ex => ex is TaskCanceledException or OperationCanceledException);
|
t.Exception!.Flatten()
|
||||||
|
.Handle(ex =>
|
||||||
|
ex is TaskCanceledException or OperationCanceledException
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e, "Filtering recipes failed");
|
Log.Error(e, "Filtering recipes failed");
|
||||||
}
|
}
|
||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
},
|
||||||
|
TaskContinuationOptions.OnlyOnFaulted
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FilterTask(string input, CancellationToken token)
|
private void FilterTask(string input, CancellationToken token)
|
||||||
@@ -405,7 +497,9 @@ internal static class ImGuiUtils
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var matcher = new FuzzyMatcher(input.ToLowerInvariant(), MatchMode.FuzzyParts);
|
var matcher = new FuzzyMatcher(input.ToLowerInvariant(), MatchMode.FuzzyParts);
|
||||||
var query = items.AsParallel().Select(i => (Item: i, Score: matcher.Matches(getString(i).ToLowerInvariant())))
|
var query = items
|
||||||
|
.AsParallel()
|
||||||
|
.Select(i => (Item: i, Score: matcher.Matches(getString(i).ToLowerInvariant())))
|
||||||
.Where(t => t.Score > 0)
|
.Where(t => t.Score > 0)
|
||||||
.OrderByDescending(t => t.Score)
|
.OrderByDescending(t => t.Score)
|
||||||
.Select(t => t.Item);
|
.Select(t => t.Item);
|
||||||
@@ -413,16 +507,34 @@ internal static class ImGuiUtils
|
|||||||
filteredItems = [.. query];
|
filteredItems = [.. query];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<uint, object> ComboData = [];
|
private static readonly Dictionary<uint, object> ComboData = [];
|
||||||
|
|
||||||
private static SearchableComboData<T> GetComboData<T>(uint comboKey, IEnumerable<T> items, T selectedItem, Func<T, string> getString) where T : IEquatable<T> =>
|
private static SearchableComboData<T> GetComboData<T>(
|
||||||
|
uint comboKey,
|
||||||
|
IEnumerable<T> items,
|
||||||
|
T selectedItem,
|
||||||
|
Func<T, string> getString
|
||||||
|
)
|
||||||
|
where T : IEquatable<T> =>
|
||||||
(SearchableComboData<T>)(
|
(SearchableComboData<T>)(
|
||||||
ComboData.TryGetValue(comboKey, out var data)
|
ComboData.TryGetValue(comboKey, out var data)
|
||||||
? data
|
? data
|
||||||
: ComboData[comboKey] = new SearchableComboData<T>(items, selectedItem, getString));
|
: ComboData[comboKey] = new SearchableComboData<T>(items, selectedItem, getString)
|
||||||
|
);
|
||||||
|
|
||||||
// https://github.com/ocornut/imgui/issues/718#issuecomment-1563162222
|
// https://github.com/ocornut/imgui/issues/718#issuecomment-1563162222
|
||||||
public static bool SearchableCombo<T>(string id, ref T selectedItem, IEnumerable<T> items, ImFontPtr selectableFont, float width, Func<T, string> getString, Func<T, string> getId, Action<T> draw) where T : IEquatable<T>
|
public static bool SearchableCombo<T>(
|
||||||
|
string id,
|
||||||
|
ref T selectedItem,
|
||||||
|
IEnumerable<T> items,
|
||||||
|
ImFontPtr selectableFont,
|
||||||
|
float width,
|
||||||
|
Func<T, string> getString,
|
||||||
|
Func<T, string> getId,
|
||||||
|
Action<T> draw
|
||||||
|
)
|
||||||
|
where T : IEquatable<T>
|
||||||
{
|
{
|
||||||
var comboKey = ImGui.GetID(id);
|
var comboKey = ImGui.GetID(id);
|
||||||
var data = GetComboData(comboKey, items, selectedItem, getString);
|
var data = GetComboData(comboKey, items, selectedItem, getString);
|
||||||
@@ -433,7 +545,12 @@ internal static class ImGuiUtils
|
|||||||
width = width == 0 ? ImGui.GetContentRegionAvail().X : width;
|
width = width == 0 ? ImGui.GetContentRegionAvail().X : width;
|
||||||
var availableSpace = Math.Min(ImGui.GetContentRegionAvail().X, width);
|
var availableSpace = Math.Min(ImGui.GetContentRegionAvail().X, width);
|
||||||
ImGui.SetNextItemWidth(availableSpace);
|
ImGui.SetNextItemWidth(availableSpace);
|
||||||
var isInputTextEnterPressed = ImGui.InputText("##input", ref data.input, 256, ImGuiInputTextFlags.EnterReturnsTrue);
|
var isInputTextEnterPressed = ImGui.InputText(
|
||||||
|
"##input",
|
||||||
|
ref data.input,
|
||||||
|
256,
|
||||||
|
ImGuiInputTextFlags.EnterReturnsTrue
|
||||||
|
);
|
||||||
var min = ImGui.GetItemRectMin();
|
var min = ImGui.GetItemRectMin();
|
||||||
var size = ImGui.GetItemRectSize();
|
var size = ImGui.GetItemRectSize();
|
||||||
size.X = Math.Min(size.X, availableSpace);
|
size.X = Math.Min(size.X, availableSpace);
|
||||||
@@ -447,7 +564,15 @@ internal static class ImGuiUtils
|
|||||||
data.wasTextActive = false;
|
data.wasTextActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var popup = ImRaii.Popup("##popup", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings))
|
using (
|
||||||
|
var popup = ImRaii.Popup(
|
||||||
|
"##popup",
|
||||||
|
ImGuiWindowFlags.NoMove
|
||||||
|
| ImGuiWindowFlags.NoResize
|
||||||
|
| ImGuiWindowFlags.AlwaysAutoResize
|
||||||
|
| ImGuiWindowFlags.NoSavedSettings
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (popup)
|
if (popup)
|
||||||
{
|
{
|
||||||
@@ -466,11 +591,21 @@ internal static class ImGuiUtils
|
|||||||
isInputTextEnterPressed = true;
|
isInputTextEnterPressed = true;
|
||||||
data.wasTextActive = isActive;
|
data.wasTextActive = isActive;
|
||||||
|
|
||||||
using (var scrollingRegion = ImRaii.Child("scrollingRegion", new Vector2(size.X, size.Y * 10), false, ImGuiWindowFlags.HorizontalScrollbar))
|
using (
|
||||||
|
var scrollingRegion = ImRaii.Child(
|
||||||
|
"scrollingRegion",
|
||||||
|
new Vector2(size.X, size.Y * 10),
|
||||||
|
false,
|
||||||
|
ImGuiWindowFlags.HorizontalScrollbar
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
T? _selectedItem = default;
|
T? _selectedItem = default;
|
||||||
var height = ImGui.GetTextLineHeight();
|
var height = ImGui.GetTextLineHeight();
|
||||||
var r = ListClip(data.filteredItems, height, t =>
|
var r = ListClip(
|
||||||
|
data.filteredItems,
|
||||||
|
height,
|
||||||
|
t =>
|
||||||
{
|
{
|
||||||
var name = getString(t);
|
var name = getString(t);
|
||||||
using (var selectFont = ImRaii.PushFont(selectableFont))
|
using (var selectFont = ImRaii.PushFont(selectableFont))
|
||||||
@@ -484,7 +619,8 @@ internal static class ImGuiUtils
|
|||||||
ImGui.SameLine(0, ImGui.GetStyle().ItemSpacing.X / 2f);
|
ImGui.SameLine(0, ImGui.GetStyle().ItemSpacing.X / 2f);
|
||||||
draw(t);
|
draw(t);
|
||||||
return false;
|
return false;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
selectedItem = _selectedItem!;
|
selectedItem = _selectedItem!;
|
||||||
@@ -530,7 +666,11 @@ internal static class ImGuiUtils
|
|||||||
imGuiListClipperPtr.Begin(data.Count, lineHeight);
|
imGuiListClipperPtr.Begin(data.Count, lineHeight);
|
||||||
while (imGuiListClipperPtr.Step())
|
while (imGuiListClipperPtr.Step())
|
||||||
{
|
{
|
||||||
for (var i = imGuiListClipperPtr.DisplayStart; i <= imGuiListClipperPtr.DisplayEnd; i++)
|
for (
|
||||||
|
var i = imGuiListClipperPtr.DisplayStart;
|
||||||
|
i <= imGuiListClipperPtr.DisplayEnd;
|
||||||
|
i++
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (i >= data.Count)
|
if (i >= data.Count)
|
||||||
return false;
|
return false;
|
||||||
@@ -551,10 +691,28 @@ internal static class ImGuiUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool InputTextMultilineWithHint(string label, string hint, ref string input, int maxLength, Vector2 size, ImGuiInputTextFlags flags = ImGuiInputTextFlags.None, ImGuiInputTextCallback? callback = null, IntPtr user_data = default)
|
public static bool InputTextMultilineWithHint(
|
||||||
|
string label,
|
||||||
|
string hint,
|
||||||
|
ref string input,
|
||||||
|
int maxLength,
|
||||||
|
Vector2 size,
|
||||||
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags.None,
|
||||||
|
ImGuiInputTextCallback? callback = null,
|
||||||
|
IntPtr user_data = default
|
||||||
|
)
|
||||||
{
|
{
|
||||||
const ImGuiInputTextFlags Multiline = (ImGuiInputTextFlags)(1 << 26);
|
const ImGuiInputTextFlags Multiline = (ImGuiInputTextFlags)(1 << 26);
|
||||||
return ImGuiExtras.InputTextEx(label, hint, ref input, maxLength, size, flags | Multiline, callback, user_data);
|
return ImGuiExtras.InputTextEx(
|
||||||
|
label,
|
||||||
|
hint,
|
||||||
|
ref input,
|
||||||
|
maxLength,
|
||||||
|
size,
|
||||||
|
flags | Multiline,
|
||||||
|
callback,
|
||||||
|
user_data
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector2 GetIconSize(FontAwesomeIcon icon)
|
private static Vector2 GetIconSize(FontAwesomeIcon icon)
|
||||||
@@ -563,7 +721,12 @@ internal static class ImGuiUtils
|
|||||||
return ImGui.CalcTextSize(icon.ToIconString());
|
return ImGui.CalcTextSize(icon.ToIconString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawCenteredIcon(FontAwesomeIcon icon, Vector2 offset, Vector2 size, bool isDisabled = false)
|
private static void DrawCenteredIcon(
|
||||||
|
FontAwesomeIcon icon,
|
||||||
|
Vector2 offset,
|
||||||
|
Vector2 size,
|
||||||
|
bool isDisabled = false
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var iconSize = GetIconSize(icon);
|
var iconSize = GetIconSize(icon);
|
||||||
|
|
||||||
@@ -585,7 +748,15 @@ internal static class ImGuiUtils
|
|||||||
iconOffset = Vector2.Zero;
|
iconOffset = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.GetWindowDrawList().AddText(UiBuilder.IconFont, UiBuilder.IconFont.FontSize * ImGuiHelpers.GlobalScale * scale, offset + iconOffset, ImGui.GetColorU32(!isDisabled ? ImGuiCol.Text : ImGuiCol.TextDisabled), icon.ToIconString());
|
ImGui
|
||||||
|
.GetWindowDrawList()
|
||||||
|
.AddText(
|
||||||
|
UiBuilder.IconFont,
|
||||||
|
UiBuilder.IconFont.FontSize * ImGuiHelpers.GlobalScale * scale,
|
||||||
|
offset + iconOffset,
|
||||||
|
ImGui.GetColorU32(!isDisabled ? ImGuiCol.Text : ImGuiCol.TextDisabled),
|
||||||
|
icon.ToIconString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IconButtonSquare(FontAwesomeIcon icon, float size = -1)
|
public static bool IconButtonSquare(FontAwesomeIcon icon, float size = -1)
|
||||||
@@ -627,7 +798,13 @@ internal static class ImGuiUtils
|
|||||||
Dalamud.Utility.Util.OpenLink(url);
|
Dalamud.Utility.Util.OpenLink(url);
|
||||||
var urlWithoutScheme = url;
|
var urlWithoutScheme = url;
|
||||||
if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
|
if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
|
||||||
urlWithoutScheme = uri.Host + (string.Equals(uri.PathAndQuery, "/", StringComparison.Ordinal) ? string.Empty : uri.PathAndQuery);
|
urlWithoutScheme =
|
||||||
|
uri.Host
|
||||||
|
+ (
|
||||||
|
string.Equals(uri.PathAndQuery, "/", StringComparison.Ordinal)
|
||||||
|
? string.Empty
|
||||||
|
: uri.PathAndQuery
|
||||||
|
);
|
||||||
Tooltip(urlWithoutScheme);
|
Tooltip(urlWithoutScheme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -647,7 +824,11 @@ internal static class ImGuiUtils
|
|||||||
ImGui.TextUnformatted(text);
|
ImGui.TextUnformatted(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TextWrappedTo(string text, float wrapPosX = default, float basePosX = default)
|
public static void TextWrappedTo(
|
||||||
|
string text,
|
||||||
|
float wrapPosX = default,
|
||||||
|
float basePosX = default
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var font = ImGui.GetFont();
|
var font = ImGui.GetFont();
|
||||||
|
|
||||||
@@ -663,7 +844,11 @@ internal static class ImGuiUtils
|
|||||||
currentWrapWidth = wrapPosX - currentPos;
|
currentWrapWidth = wrapPosX - currentPos;
|
||||||
|
|
||||||
var textBuf = text.AsSpan();
|
var textBuf = text.AsSpan();
|
||||||
var lineSize = font.CalcWordWrapPositionA(ImGuiHelpers.GlobalScale, textBuf, currentWrapWidth);
|
var lineSize = font.CalcWordWrapPositionA(
|
||||||
|
ImGuiHelpers.GlobalScale,
|
||||||
|
textBuf,
|
||||||
|
currentWrapWidth
|
||||||
|
);
|
||||||
if (lineSize == 0)
|
if (lineSize == 0)
|
||||||
lineSize = textBuf.Length;
|
lineSize = textBuf.Length;
|
||||||
var lineBuf = textBuf[..lineSize];
|
var lineBuf = textBuf[..lineSize];
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Bindings.ImPlot;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Bindings.ImPlot;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
@@ -61,7 +61,10 @@ public static class ImRaii2
|
|||||||
|
|
||||||
public static IEndObject Plot(string title_id, Vector2 size, ImPlotFlags flags)
|
public static IEndObject Plot(string title_id, Vector2 size, ImPlotFlags flags)
|
||||||
{
|
{
|
||||||
return new EndConditionally(new Action(ImPlot.EndPlot), ImPlot.BeginPlot(title_id, size, flags));
|
return new EndConditionally(
|
||||||
|
new Action(ImPlot.EndPlot),
|
||||||
|
ImPlot.BeginPlot(title_id, size, flags)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEndObject PushStyle(ImPlotStyleVar idx, Vector2 val)
|
public static IEndObject PushStyle(ImPlotStyleVar idx, Vector2 val)
|
||||||
|
|||||||
@@ -10,18 +10,24 @@ public static class LuminaSheets
|
|||||||
|
|
||||||
public static readonly ExcelSheet<Recipe> RecipeSheet = Module.GetSheet<Recipe>();
|
public static readonly ExcelSheet<Recipe> RecipeSheet = Module.GetSheet<Recipe>();
|
||||||
public static readonly ExcelSheet<Action> ActionSheet = Module.GetSheet<Action>();
|
public static readonly ExcelSheet<Action> ActionSheet = Module.GetSheet<Action>();
|
||||||
public static readonly ExcelSheet<CraftAction> CraftActionSheet = Module.GetSheet<CraftAction>();
|
public static readonly ExcelSheet<CraftAction> CraftActionSheet =
|
||||||
|
Module.GetSheet<CraftAction>();
|
||||||
public static readonly ExcelSheet<Status> StatusSheet = Module.GetSheet<Status>();
|
public static readonly ExcelSheet<Status> StatusSheet = Module.GetSheet<Status>();
|
||||||
public static readonly ExcelSheet<Addon> AddonSheet = Module.GetSheet<Addon>();
|
public static readonly ExcelSheet<Addon> AddonSheet = Module.GetSheet<Addon>();
|
||||||
public static readonly ExcelSheet<ClassJob> ClassJobSheet = Module.GetSheet<ClassJob>();
|
public static readonly ExcelSheet<ClassJob> ClassJobSheet = Module.GetSheet<ClassJob>();
|
||||||
public static readonly ExcelSheet<Item> ItemSheet = Module.GetSheet<Item>();
|
public static readonly ExcelSheet<Item> ItemSheet = Module.GetSheet<Item>();
|
||||||
public static readonly ExcelSheet<Item> ItemSheetEnglish = Module.GetSheet<Item>(Language.English)!;
|
public static readonly ExcelSheet<Item> ItemSheetEnglish = Module.GetSheet<Item>(
|
||||||
|
Language.English
|
||||||
|
)!;
|
||||||
public static readonly ExcelSheet<Level> LevelSheet = Module.GetSheet<Level>();
|
public static readonly ExcelSheet<Level> LevelSheet = Module.GetSheet<Level>();
|
||||||
public static readonly ExcelSheet<Quest> QuestSheet = Module.GetSheet<Quest>();
|
public static readonly ExcelSheet<Quest> QuestSheet = Module.GetSheet<Quest>();
|
||||||
public static readonly ExcelSheet<Materia> MateriaSheet = Module.GetSheet<Materia>();
|
public static readonly ExcelSheet<Materia> MateriaSheet = Module.GetSheet<Materia>();
|
||||||
public static readonly ExcelSheet<BaseParam> BaseParamSheet = Module.GetSheet<BaseParam>();
|
public static readonly ExcelSheet<BaseParam> BaseParamSheet = Module.GetSheet<BaseParam>();
|
||||||
public static readonly ExcelSheet<ItemFood> ItemFoodSheet = Module.GetSheet<ItemFood>();
|
public static readonly ExcelSheet<ItemFood> ItemFoodSheet = Module.GetSheet<ItemFood>();
|
||||||
public static readonly ExcelSheet<WKSMissionToDoEvalutionRefin> WKSMissionToDoEvalutionRefinSheet = Module.GetSheet<WKSMissionToDoEvalutionRefin>();
|
public static readonly ExcelSheet<WKSMissionToDoEvalutionRefin> WKSMissionToDoEvalutionRefinSheet =
|
||||||
public static readonly ExcelSheet<RecipeLevelTable> RecipeLevelTableSheet = Module.GetSheet<RecipeLevelTable>();
|
Module.GetSheet<WKSMissionToDoEvalutionRefin>();
|
||||||
public static readonly ExcelSheet<GathererCrafterLvAdjustTable> GathererCrafterLvAdjustTableSheet = Module.GetSheet<GathererCrafterLvAdjustTable>();
|
public static readonly ExcelSheet<RecipeLevelTable> RecipeLevelTableSheet =
|
||||||
|
Module.GetSheet<RecipeLevelTable>();
|
||||||
|
public static readonly ExcelSheet<GathererCrafterLvAdjustTable> GathererCrafterLvAdjustTableSheet =
|
||||||
|
Module.GetSheet<GathererCrafterLvAdjustTable>();
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-25
@@ -1,3 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
using Craftimizer.Plugin.Windows;
|
using Craftimizer.Plugin.Windows;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
@@ -6,9 +9,6 @@ using Craftimizer.Windows;
|
|||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
@@ -50,9 +50,13 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
AttributeCommandManager = new();
|
AttributeCommandManager = new();
|
||||||
|
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
Version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion.Split('+')[0];
|
Version = assembly
|
||||||
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!
|
||||||
|
.InformationalVersion.Split('+')[0];
|
||||||
Author = assembly.GetCustomAttribute<AssemblyCompanyAttribute>()!.Company;
|
Author = assembly.GetCustomAttribute<AssemblyCompanyAttribute>()!.Company;
|
||||||
BuildConfiguration = assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()!.Configuration;
|
BuildConfiguration = assembly
|
||||||
|
.GetCustomAttribute<AssemblyConfigurationAttribute>()!
|
||||||
|
.Configuration;
|
||||||
if (DateTime.Now is { Day: 1, Month: 4 })
|
if (DateTime.Now is { Day: 1, Month: 4 })
|
||||||
Icon = IconManager.GetAssemblyTexture("Graphics.horse_icon.png");
|
Icon = IconManager.GetAssemblyTexture("Graphics.horse_icon.png");
|
||||||
else
|
else
|
||||||
@@ -72,21 +76,37 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
Service.PluginInterface.UiBuilder.OpenMainUi += OpenCraftingLog;
|
Service.PluginInterface.UiBuilder.OpenMainUi += OpenCraftingLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (CharacterStats? Character, RecipeData? Recipe, MacroEditor.CrafterBuffs? Buffs) GetOpenedStats()
|
public (
|
||||||
|
CharacterStats? Character,
|
||||||
|
RecipeData? Recipe,
|
||||||
|
MacroEditor.CrafterBuffs? Buffs
|
||||||
|
) GetOpenedStats()
|
||||||
{
|
{
|
||||||
var editorWindow = (EditorWindow?.IsOpen ?? false) ? EditorWindow : null;
|
var editorWindow = (EditorWindow?.IsOpen ?? false) ? EditorWindow : null;
|
||||||
var recipeData = editorWindow?.RecipeData ?? Service.Plugin.RecipeNoteWindow.RecipeData;
|
var recipeData = editorWindow?.RecipeData ?? Service.Plugin.RecipeNoteWindow.RecipeData;
|
||||||
var characterStats = editorWindow?.CharacterStats ?? Service.Plugin.RecipeNoteWindow.CharacterStats;
|
var characterStats =
|
||||||
var buffs = editorWindow?.Buffs ?? (RecipeNoteWindow.CharacterStats != null ? new(Service.Objects.LocalPlayer?.StatusList) : null);
|
editorWindow?.CharacterStats ?? Service.Plugin.RecipeNoteWindow.CharacterStats;
|
||||||
|
var buffs =
|
||||||
|
editorWindow?.Buffs
|
||||||
|
?? (
|
||||||
|
RecipeNoteWindow.CharacterStats != null
|
||||||
|
? new(Service.Objects.LocalPlayer?.StatusList)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
return (characterStats, recipeData, buffs);
|
return (characterStats, recipeData, buffs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (CharacterStats Character, RecipeData Recipe, MacroEditor.CrafterBuffs Buffs) GetDefaultStats()
|
public (
|
||||||
|
CharacterStats Character,
|
||||||
|
RecipeData Recipe,
|
||||||
|
MacroEditor.CrafterBuffs Buffs
|
||||||
|
) GetDefaultStats()
|
||||||
{
|
{
|
||||||
var stats = GetOpenedStats();
|
var stats = GetOpenedStats();
|
||||||
return (
|
return (
|
||||||
stats.Character ?? new()
|
stats.Character
|
||||||
|
?? new()
|
||||||
{
|
{
|
||||||
Craftsmanship = 100,
|
Craftsmanship = 100,
|
||||||
Control = 100,
|
Control = 100,
|
||||||
@@ -101,30 +121,48 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(name: "/crafteditor", aliases: "/macroeditor", description: "Open the crafting macro editor.")]
|
[Command(
|
||||||
|
name: "/crafteditor",
|
||||||
|
aliases: "/macroeditor",
|
||||||
|
description: "Open the crafting macro editor."
|
||||||
|
)]
|
||||||
public void OpenEmptyMacroEditor()
|
public void OpenEmptyMacroEditor()
|
||||||
{
|
{
|
||||||
var stats = GetDefaultStats();
|
var stats = GetDefaultStats();
|
||||||
OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, null, [], null);
|
OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, null, [], null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenMacroEditor(CharacterStats characterStats, RecipeData recipeData, MacroEditor.CrafterBuffs buffs, IEnumerable<int>? ingredientHqCounts, IEnumerable<ActionType> actions, Action<IEnumerable<ActionType>>? setter)
|
public void OpenMacroEditor(
|
||||||
|
CharacterStats characterStats,
|
||||||
|
RecipeData recipeData,
|
||||||
|
MacroEditor.CrafterBuffs buffs,
|
||||||
|
IEnumerable<int>? ingredientHqCounts,
|
||||||
|
IEnumerable<ActionType> actions,
|
||||||
|
Action<IEnumerable<ActionType>>? setter
|
||||||
|
)
|
||||||
{
|
{
|
||||||
EditorWindow?.Dispose();
|
EditorWindow?.Dispose();
|
||||||
EditorWindow = new(characterStats, recipeData, buffs, ingredientHqCounts, actions, setter);
|
EditorWindow = new(characterStats, recipeData, buffs, ingredientHqCounts, actions, setter);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(name: "/craftaction", description: "Execute the suggested action in the synthesis helper. Can also be run inside a macro. This command is useful for controller players.")]
|
[Command(
|
||||||
public void ExecuteSuggestedSynthHelperAction() =>
|
name: "/craftaction",
|
||||||
SynthHelperWindow.ExecuteNextAction();
|
description: "Execute the suggested action in the synthesis helper. Can also be run inside a macro. This command is useful for controller players."
|
||||||
|
)]
|
||||||
|
public void ExecuteSuggestedSynthHelperAction() => SynthHelperWindow.ExecuteNextAction();
|
||||||
|
|
||||||
[Command(name: "/craftretry", description: "Clicks \"Retry\" in the synthesis helper. Can also be run inside a macro. This command is useful for controller players.")]
|
[Command(
|
||||||
public void ExecuteRetrySynthHelper() =>
|
name: "/craftretry",
|
||||||
SynthHelperWindow.AttemptRetry();
|
description: "Clicks \"Retry\" in the synthesis helper. Can also be run inside a macro. This command is useful for controller players."
|
||||||
|
)]
|
||||||
|
public void ExecuteRetrySynthHelper() => SynthHelperWindow.AttemptRetry();
|
||||||
|
|
||||||
[Command(name: "/craftimizer", aliases: "/forgeimizer", description: "Open the settings window.")]
|
[Command(
|
||||||
private void OpenSettingsWindowForced() =>
|
name: "/craftimizer",
|
||||||
OpenSettingsWindow(true);
|
aliases: "/forgeimizer",
|
||||||
|
description: "Open the settings window."
|
||||||
|
)]
|
||||||
|
private void OpenSettingsWindowForced() => OpenSettingsWindow(true);
|
||||||
|
|
||||||
public void OpenSettingsWindow(bool force = false)
|
public void OpenSettingsWindow(bool force = false)
|
||||||
{
|
{
|
||||||
@@ -138,7 +176,11 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
SettingsWindow.SelectTab(selectedTabLabel);
|
SettingsWindow.SelectTab(selectedTabLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(name: "/craftmacros", aliases: "/macrolist", description: "Open the crafting macros window.")]
|
[Command(
|
||||||
|
name: "/craftmacros",
|
||||||
|
aliases: "/macrolist",
|
||||||
|
description: "Open the crafting macros window."
|
||||||
|
)]
|
||||||
public void OpenMacroListWindow()
|
public void OpenMacroListWindow()
|
||||||
{
|
{
|
||||||
ListWindow.IsOpen = true;
|
ListWindow.IsOpen = true;
|
||||||
@@ -157,12 +199,14 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static IActiveNotification DisplaySolverWarning(string text) =>
|
public static IActiveNotification DisplaySolverWarning(string text) =>
|
||||||
DisplayNotification(new()
|
DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = text,
|
Content = text,
|
||||||
Title = "Solver Warning",
|
Title = "Solver Warning",
|
||||||
Type = NotificationType.Warning
|
Type = NotificationType.Warning,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
public static IActiveNotification DisplayNotification(Notification notification)
|
public static IActiveNotification DisplayNotification(Notification notification)
|
||||||
{
|
{
|
||||||
|
|||||||
+47
-16
@@ -14,22 +14,53 @@ namespace Craftimizer.Plugin;
|
|||||||
public sealed class Service
|
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 IDalamudPluginInterface PluginInterface { get; private set; }
|
[PluginService]
|
||||||
[PluginService] public static ICommandManager CommandManager { get; private set; }
|
public static IDalamudPluginInterface PluginInterface { get; private set; }
|
||||||
[PluginService] public static IObjectTable Objects { get; private set; }
|
|
||||||
[PluginService] public static ISigScanner SigScanner { get; private set; }
|
[PluginService]
|
||||||
[PluginService] public static IGameGui GameGui { get; private set; }
|
public static ICommandManager CommandManager { get; private set; }
|
||||||
[PluginService] public static IClientState ClientState { get; private set; }
|
|
||||||
[PluginService] public static IDataManager DataManager { get; private set; }
|
[PluginService]
|
||||||
[PluginService] public static ITextureProvider TextureProvider { get; private set; }
|
public static IObjectTable Objects { get; private set; }
|
||||||
[PluginService] public static IDalamudAssetManager DalamudAssetManager { get; private set; }
|
|
||||||
[PluginService] public static ITargetManager TargetManager { get; private set; }
|
[PluginService]
|
||||||
[PluginService] public static ICondition Condition { get; private set; }
|
public static ISigScanner SigScanner { get; private set; }
|
||||||
[PluginService] public static IFramework Framework { get; private set; }
|
|
||||||
[PluginService] public static IPluginLog PluginLog { get; private set; }
|
[PluginService]
|
||||||
[PluginService] public static IGameInteropProvider GameInteropProvider { get; private set; }
|
public static IGameGui GameGui { get; private set; }
|
||||||
[PluginService] public static INotificationManager NotificationManager { get; private set; }
|
|
||||||
[PluginService] public static ISeStringEvaluator SeStringEvaluator { 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 IDalamudAssetManager DalamudAssetManager { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static ITargetManager TargetManager { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static ICondition Condition { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static IFramework Framework { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static IPluginLog PluginLog { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static IGameInteropProvider GameInteropProvider { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static INotificationManager NotificationManager { get; private set; }
|
||||||
|
|
||||||
|
[PluginService]
|
||||||
|
public static ISeStringEvaluator SeStringEvaluator { get; private set; }
|
||||||
|
|
||||||
public static Plugin Plugin { get; private set; }
|
public static Plugin Plugin { get; private set; }
|
||||||
public static Configuration Configuration => Plugin.Configuration;
|
public static Configuration Configuration => Plugin.Configuration;
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Craftimizer.Simulator;
|
||||||
|
using Craftimizer.Simulator.Actions;
|
||||||
|
using Craftimizer.Utils;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Event;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
using Lumina.Text.Payloads;
|
||||||
|
using Lumina.Text.ReadOnly;
|
||||||
using Action = Lumina.Excel.Sheets.Action;
|
using Action = Lumina.Excel.Sheets.Action;
|
||||||
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
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.Sheets.Status;
|
using Status = Lumina.Excel.Sheets.Status;
|
||||||
using Craftimizer.Utils;
|
|
||||||
using Lumina.Text.ReadOnly;
|
|
||||||
using Lumina.Text.Payloads;
|
|
||||||
using Lumina.Excel.Sheets;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Event;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
@@ -27,7 +27,10 @@ internal static class ActionUtils
|
|||||||
{
|
{
|
||||||
var actionTypes = Enum.GetValues<ActionType>();
|
var actionTypes = Enum.GetValues<ActionType>();
|
||||||
var classJobs = Enum.GetValues<ClassJob>();
|
var classJobs = Enum.GetValues<ClassJob>();
|
||||||
ActionRows = new (CraftAction? CraftAction, Action? Action)[actionTypes.Length, classJobs.Length];
|
ActionRows = new (CraftAction? CraftAction, Action? Action)[
|
||||||
|
actionTypes.Length,
|
||||||
|
classJobs.Length
|
||||||
|
];
|
||||||
foreach (var actionType in actionTypes)
|
foreach (var actionType in actionTypes)
|
||||||
{
|
{
|
||||||
var actionId = actionType.Base().ActionId;
|
var actionId = actionType.Base().ActionId;
|
||||||
@@ -35,7 +38,8 @@ internal static class ActionUtils
|
|||||||
{
|
{
|
||||||
foreach (var classJob in classJobs)
|
foreach (var classJob in classJobs)
|
||||||
{
|
{
|
||||||
ActionRows[(int)actionType, (int)classJob] = (classJob switch
|
ActionRows[(int)actionType, (int)classJob] = (
|
||||||
|
classJob switch
|
||||||
{
|
{
|
||||||
ClassJob.Carpenter => baseCraftAction.CRP.Value,
|
ClassJob.Carpenter => baseCraftAction.CRP.Value,
|
||||||
ClassJob.Blacksmith => baseCraftAction.BSM.Value,
|
ClassJob.Blacksmith => baseCraftAction.BSM.Value,
|
||||||
@@ -45,27 +49,37 @@ internal static class ActionUtils
|
|||||||
ClassJob.Weaver => baseCraftAction.WVR.Value,
|
ClassJob.Weaver => baseCraftAction.WVR.Value,
|
||||||
ClassJob.Alchemist => baseCraftAction.ALC.Value,
|
ClassJob.Alchemist => baseCraftAction.ALC.Value,
|
||||||
ClassJob.Culinarian => baseCraftAction.CUL.Value,
|
ClassJob.Culinarian => baseCraftAction.CUL.Value,
|
||||||
_ => baseCraftAction
|
_ => baseCraftAction,
|
||||||
}, null);
|
},
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (LuminaSheets.ActionSheet.GetRowOrDefault(actionId) is { } baseAction)
|
if (LuminaSheets.ActionSheet.GetRowOrDefault(actionId) is { } baseAction)
|
||||||
{
|
{
|
||||||
var possibleActions = LuminaSheets.ActionSheet.Where(r =>
|
var possibleActions = LuminaSheets.ActionSheet.Where(r =>
|
||||||
r.Icon == baseAction.Icon &&
|
r.Icon == baseAction.Icon
|
||||||
r.ActionCategory.RowId == baseAction.ActionCategory.RowId &&
|
&& r.ActionCategory.RowId == baseAction.ActionCategory.RowId
|
||||||
r.Name.Equals(baseAction.Name));
|
&& r.Name.Equals(baseAction.Name)
|
||||||
|
);
|
||||||
|
|
||||||
foreach (var classJob in classJobs)
|
foreach (var classJob in classJobs)
|
||||||
ActionRows[(int)actionType, (int)classJob] = (null, possibleActions.First(r => r.ClassJobCategory.ValueNullable?.IsClassJob(classJob) ?? false));
|
ActionRows[(int)actionType, (int)classJob] = (
|
||||||
|
null,
|
||||||
|
possibleActions.First(r =>
|
||||||
|
r.ClassJobCategory.ValueNullable?.IsClassJob(classJob) ?? false
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Initialize() { }
|
public static void Initialize() { }
|
||||||
|
|
||||||
public static (CraftAction? CraftAction, Action? Action) GetActionRow(this ActionType me, ClassJob classJob) =>
|
public static (CraftAction? CraftAction, Action? Action) GetActionRow(
|
||||||
ActionRows[(int)me, (int)classJob];
|
this ActionType me,
|
||||||
|
ClassJob classJob
|
||||||
|
) => ActionRows[(int)me, (int)classJob];
|
||||||
|
|
||||||
public static uint GetId(this ActionType me, ClassJob classJob)
|
public static uint GetId(this ActionType me, ClassJob classJob)
|
||||||
{
|
{
|
||||||
@@ -86,7 +100,11 @@ internal static class ActionUtils
|
|||||||
return Service.IconManager.GetIconCached(craftAction?.Icon ?? action?.Icon ?? 1953);
|
return Service.IconManager.GetIconCached(craftAction?.Icon ?? action?.Icon ?? 1953);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ActionType? GetActionTypeFromId(uint actionId, ClassJob classJob, bool isCraftAction)
|
public static ActionType? GetActionTypeFromId(
|
||||||
|
uint actionId,
|
||||||
|
ClassJob classJob,
|
||||||
|
bool isCraftAction
|
||||||
|
)
|
||||||
{
|
{
|
||||||
foreach (var action in Enum.GetValues<ActionType>())
|
foreach (var action in Enum.GetValues<ActionType>())
|
||||||
{
|
{
|
||||||
@@ -119,7 +137,7 @@ internal static class ClassJobUtils
|
|||||||
ClassJob.Weaver => 13,
|
ClassJob.Weaver => 13,
|
||||||
ClassJob.Alchemist => 14,
|
ClassJob.Alchemist => 14,
|
||||||
ClassJob.Culinarian => 15,
|
ClassJob.Culinarian => 15,
|
||||||
_ => 0
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static ClassJob? GetClassJobFromIdx(byte classJobIdx) =>
|
public static ClassJob? GetClassJobFromIdx(byte classJobIdx) =>
|
||||||
@@ -133,7 +151,7 @@ internal static class ClassJobUtils
|
|||||||
13 => ClassJob.Weaver,
|
13 => ClassJob.Weaver,
|
||||||
14 => ClassJob.Alchemist,
|
14 => ClassJob.Alchemist,
|
||||||
15 => ClassJob.Culinarian,
|
15 => ClassJob.Culinarian,
|
||||||
_ => null
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static sbyte GetExpArrayIdx(this ClassJob me) =>
|
public static sbyte GetExpArrayIdx(this ClassJob me) =>
|
||||||
@@ -158,7 +176,11 @@ internal static class ClassJobUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe bool CanPlayerUseManipulation(this ClassJob me) =>
|
public static unsafe bool CanPlayerUseManipulation(this ClassJob me) =>
|
||||||
UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(ActionType.Manipulation.GetActionRow(me).Action!.Value.UnlockLink.RowId);
|
UIState
|
||||||
|
.Instance()
|
||||||
|
->IsUnlockLinkUnlockedOrQuestCompleted(
|
||||||
|
ActionType.Manipulation.GetActionRow(me).Action!.Value.UnlockLink.RowId
|
||||||
|
);
|
||||||
|
|
||||||
public static string GetName(this ClassJob me)
|
public static string GetName(this ClassJob me)
|
||||||
{
|
{
|
||||||
@@ -186,8 +208,7 @@ internal static class ClassJobUtils
|
|||||||
public static Quest GetUnlockQuest(this ClassJob me) =>
|
public static Quest GetUnlockQuest(this ClassJob me) =>
|
||||||
LuminaSheets.QuestSheet.GetRow(65720 + (uint)me);
|
LuminaSheets.QuestSheet.GetRow(65720 + (uint)me);
|
||||||
|
|
||||||
public static ushort GetIconId(this ClassJob me) =>
|
public static ushort GetIconId(this ClassJob me) => (ushort)(62000 + me.GetClassJobIndex());
|
||||||
(ushort)(62000 + me.GetClassJobIndex());
|
|
||||||
|
|
||||||
public static bool IsClassJob(this ClassJobCategory me, ClassJob classJob) =>
|
public static bool IsClassJob(this ClassJobCategory me, ClassJob classJob) =>
|
||||||
classJob switch
|
classJob switch
|
||||||
@@ -200,7 +221,7 @@ internal static class ClassJobUtils
|
|||||||
ClassJob.Weaver => me.WVR,
|
ClassJob.Weaver => me.WVR,
|
||||||
ClassJob.Alchemist => me.ALC,
|
ClassJob.Alchemist => me.ALC,
|
||||||
ClassJob.Culinarian => me.CUL,
|
ClassJob.Culinarian => me.CUL,
|
||||||
_ => false
|
_ => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +240,7 @@ internal static class ConditionUtils
|
|||||||
Condition.Malleable => (13455, 14208),
|
Condition.Malleable => (13455, 14208),
|
||||||
Condition.Primed => (13454, 14207),
|
Condition.Primed => (13454, 14207),
|
||||||
Condition.GoodOmen => (14214, 14215),
|
Condition.GoodOmen => (14214, 14215),
|
||||||
_ => (226, 14200) // Unknown
|
_ => (226, 14200), // Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Vector3 AddRGB(this Condition me) =>
|
private static Vector3 AddRGB(this Condition me) =>
|
||||||
@@ -235,10 +256,11 @@ internal static class ConditionUtils
|
|||||||
Condition.Malleable => new(-80, -40, 180),
|
Condition.Malleable => new(-80, -40, 180),
|
||||||
Condition.Primed => new(30, -155, 200),
|
Condition.Primed => new(30, -155, 200),
|
||||||
Condition.GoodOmen => new(100, 20, 0),
|
Condition.GoodOmen => new(100, 20, 0),
|
||||||
_ => Vector3.Zero // Unknown
|
_ => Vector3.Zero, // Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
private const float ConditionCyclePeriod = 19 / 30f;
|
private const float ConditionCyclePeriod = 19 / 30f;
|
||||||
|
|
||||||
// The real period of all condition color cycles are 0.633... (19/30) seconds
|
// The real period of all condition color cycles are 0.633... (19/30) seconds
|
||||||
// Interp accepts 0-1
|
// Interp accepts 0-1
|
||||||
public static Vector4 GetColor(this Condition me, float interp)
|
public static Vector4 GetColor(this Condition me, float interp)
|
||||||
@@ -252,12 +274,32 @@ internal static class ConditionUtils
|
|||||||
addRgb = interp switch
|
addRgb = interp switch
|
||||||
{
|
{
|
||||||
< 0.155f => Vector3.Lerp(new(128, 0, 0), new(128, 80, 0), (interp - 0) / 0.155f),
|
< 0.155f => Vector3.Lerp(new(128, 0, 0), new(128, 80, 0), (interp - 0) / 0.155f),
|
||||||
< 0.315f => Vector3.Lerp(new(128, 80, 0), new(128, 128, 0), (interp - 0.155f) / 0.16f),
|
< 0.315f => Vector3.Lerp(
|
||||||
< 0.475f => Vector3.Lerp(new(128, 128, 0), new(0, 64, 0), (interp - 0.315f) / 0.16f),
|
new(128, 80, 0),
|
||||||
< 0.630f => Vector3.Lerp(new(0, 64, 0), new(0, 128, 128), (interp - 0.475f) / 0.155f),
|
new(128, 128, 0),
|
||||||
< 0.790f => Vector3.Lerp(new(0, 128, 128), new(0, 0, 128), (interp - 0.630f) / 0.16f),
|
(interp - 0.155f) / 0.16f
|
||||||
< 0.945f => Vector3.Lerp(new(0, 0, 128), new(64, 0, 64), (interp - 0.790f) / 0.155f),
|
),
|
||||||
_ => new(64, 0, 64)
|
< 0.475f => Vector3.Lerp(
|
||||||
|
new(128, 128, 0),
|
||||||
|
new(0, 64, 0),
|
||||||
|
(interp - 0.315f) / 0.16f
|
||||||
|
),
|
||||||
|
< 0.630f => Vector3.Lerp(
|
||||||
|
new(0, 64, 0),
|
||||||
|
new(0, 128, 128),
|
||||||
|
(interp - 0.475f) / 0.155f
|
||||||
|
),
|
||||||
|
< 0.790f => Vector3.Lerp(
|
||||||
|
new(0, 128, 128),
|
||||||
|
new(0, 0, 128),
|
||||||
|
(interp - 0.630f) / 0.16f
|
||||||
|
),
|
||||||
|
< 0.945f => Vector3.Lerp(
|
||||||
|
new(0, 0, 128),
|
||||||
|
new(64, 0, 64),
|
||||||
|
(interp - 0.790f) / 0.155f
|
||||||
|
),
|
||||||
|
_ => new(64, 0, 64),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Period is twice as fast so we oscillate at twice that speed
|
// Period is twice as fast so we oscillate at twice that speed
|
||||||
@@ -285,7 +327,7 @@ internal static class ConditionUtils
|
|||||||
Condition.Pliant => Vector3.Lerp(new(0, 150, 0), new(0, 249, 0), interp),
|
Condition.Pliant => Vector3.Lerp(new(0, 150, 0), new(0, 249, 0), interp),
|
||||||
Condition.Primed => Vector3.Lerp(new(-30, -255, 50), new(29, -156, 199), interp),
|
Condition.Primed => Vector3.Lerp(new(-30, -255, 50), new(29, -156, 199), interp),
|
||||||
Condition.GoodOmen => Vector3.Lerp(new(100, 20, 0), new(100, 99, 99), interp),
|
Condition.GoodOmen => Vector3.Lerp(new(100, 20, 0), new(100, 99, 99), interp),
|
||||||
_ => default
|
_ => default,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +336,9 @@ internal static class ConditionUtils
|
|||||||
|
|
||||||
public static Vector4 GetColor(this Condition me, TimeSpan time)
|
public static Vector4 GetColor(this Condition me, TimeSpan time)
|
||||||
{
|
{
|
||||||
return me.GetColor((float)(time.TotalSeconds % ConditionCyclePeriod / ConditionCyclePeriod));
|
return me.GetColor(
|
||||||
|
(float)(time.TotalSeconds % ConditionCyclePeriod / ConditionCyclePeriod)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Name(this Condition me) =>
|
public static string Name(this Condition me) =>
|
||||||
@@ -310,7 +354,11 @@ internal static class ConditionUtils
|
|||||||
foreach (var payload in text)
|
foreach (var payload in text)
|
||||||
{
|
{
|
||||||
if (payload is { Type: ReadOnlySePayloadType.Macro, MacroCode: MacroCode.Float })
|
if (payload is { Type: ReadOnlySePayloadType.Macro, MacroCode: MacroCode.Float })
|
||||||
finalText += new ReadOnlySePayload(ReadOnlySePayloadType.Text, default, Encoding.UTF8.GetBytes(isRelic ? "1.75" : "1.5"));
|
finalText += new ReadOnlySePayload(
|
||||||
|
ReadOnlySePayloadType.Text,
|
||||||
|
default,
|
||||||
|
Encoding.UTF8.GetBytes(isRelic ? "1.75" : "1.5")
|
||||||
|
);
|
||||||
else
|
else
|
||||||
finalText += payload;
|
finalText += payload;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Game.Command;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
public sealed class CommandAttribute(string name, string description, bool hidden = false, params string[] aliases) : Attribute
|
public sealed class CommandAttribute(
|
||||||
|
string name,
|
||||||
|
string description,
|
||||||
|
bool hidden = false,
|
||||||
|
params string[] aliases
|
||||||
|
) : Attribute
|
||||||
{
|
{
|
||||||
public string Name { get; } = name;
|
public string Name { get; } = name;
|
||||||
public string Description { get; } = description;
|
public string Description { get; } = description;
|
||||||
@@ -22,7 +27,11 @@ public sealed class AttributeCommandManager : IDisposable
|
|||||||
public AttributeCommandManager()
|
public AttributeCommandManager()
|
||||||
{
|
{
|
||||||
var target = Service.Plugin;
|
var target = Service.Plugin;
|
||||||
foreach (var method in target.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
foreach (
|
||||||
|
var method in target
|
||||||
|
.GetType()
|
||||||
|
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (method.GetCustomAttribute<CommandAttribute>() is not { } command)
|
if (method.GetCustomAttribute<CommandAttribute>() is not { } command)
|
||||||
continue;
|
continue;
|
||||||
@@ -51,15 +60,21 @@ public sealed class AttributeCommandManager : IDisposable
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!RegisteredCommands.Add(command.Name))
|
if (!RegisteredCommands.Add(command.Name))
|
||||||
throw new InvalidOperationException($"Command '{command.Name}' is already registered.");
|
throw new InvalidOperationException(
|
||||||
|
$"Command '{command.Name}' is already registered."
|
||||||
|
);
|
||||||
|
|
||||||
if (!Service.CommandManager.AddHandler(command.Name, info))
|
if (!Service.CommandManager.AddHandler(command.Name, info))
|
||||||
throw new InvalidOperationException($"Failed to register command '{command.Name}'.");
|
throw new InvalidOperationException(
|
||||||
|
$"Failed to register command '{command.Name}'."
|
||||||
|
);
|
||||||
|
|
||||||
foreach (var alias in command.Aliases)
|
foreach (var alias in command.Aliases)
|
||||||
{
|
{
|
||||||
if (!RegisteredCommands.Add(alias))
|
if (!RegisteredCommands.Add(alias))
|
||||||
throw new InvalidOperationException($"Command '{alias}' is already registered.");
|
throw new InvalidOperationException(
|
||||||
|
$"Command '{alias}' is already registered."
|
||||||
|
);
|
||||||
|
|
||||||
if (!Service.CommandManager.AddHandler(alias, aliasInfo))
|
if (!Service.CommandManager.AddHandler(alias, aliasInfo))
|
||||||
throw new InvalidOperationException($"Failed to register command '{alias}'.");
|
throw new InvalidOperationException($"Failed to register command '{alias}'.");
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
public sealed class BackgroundTask<T>(Func<CancellationToken, T> func) : IDisposable where T : struct
|
public sealed class BackgroundTask<T>(Func<CancellationToken, T> func) : IDisposable
|
||||||
|
where T : struct
|
||||||
{
|
{
|
||||||
public T? Result { get; private set; }
|
public T? Result { get; private set; }
|
||||||
public Exception? Exception { get; private set; }
|
public Exception? Exception { get; private set; }
|
||||||
@@ -19,26 +20,28 @@ public sealed class BackgroundTask<T>(Func<CancellationToken, T> func) : IDispos
|
|||||||
var token = TokenSource.Token;
|
var token = TokenSource.Token;
|
||||||
var task = Task.Run(() => Result = Func(token), token);
|
var task = Task.Run(() => Result = Func(token), token);
|
||||||
_ = task.ContinueWith(t => Completed = true);
|
_ = task.ContinueWith(t => Completed = true);
|
||||||
_ = task.ContinueWith(t =>
|
_ = task.ContinueWith(
|
||||||
|
t =>
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
t.Exception!.Flatten().Handle(ex => ex is TaskCanceledException or OperationCanceledException);
|
t.Exception!.Flatten()
|
||||||
|
.Handle(ex => ex is TaskCanceledException or OperationCanceledException);
|
||||||
}
|
}
|
||||||
catch (AggregateException e)
|
catch (AggregateException e)
|
||||||
{
|
{
|
||||||
Exception = e;
|
Exception = e;
|
||||||
Log.Error(e, "Background task failed");
|
Log.Error(e, "Background task failed");
|
||||||
}
|
}
|
||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
},
|
||||||
|
TaskContinuationOptions.OnlyOnFaulted
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel() =>
|
public void Cancel() => TokenSource.Cancel();
|
||||||
TokenSource.Cancel();
|
|
||||||
|
|
||||||
public void Dispose() =>
|
public void Dispose() => Cancel();
|
||||||
Cancel();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 2880)]
|
[StructLayout(LayoutKind.Explicit, Size = 2880)]
|
||||||
public unsafe struct CSRecipeNote
|
public unsafe struct CSRecipeNote
|
||||||
{
|
{
|
||||||
[FieldOffset(0x118)] public ushort ActiveCraftRecipeId;
|
[FieldOffset(0x118)]
|
||||||
|
public ushort ActiveCraftRecipeId;
|
||||||
|
|
||||||
public static CSRecipeNote* Instance()
|
public static CSRecipeNote* Instance()
|
||||||
{
|
{
|
||||||
|
|||||||
+22
-10
@@ -1,6 +1,6 @@
|
|||||||
|
using System;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -19,16 +19,28 @@ public static unsafe class Chat
|
|||||||
|
|
||||||
var unsanitizedLength = str->Length;
|
var unsanitizedLength = str->Length;
|
||||||
str->SanitizeString(
|
str->SanitizeString(
|
||||||
AllowedEntities.Unknown9 | // 200
|
AllowedEntities.Unknown9
|
||||||
AllowedEntities.Payloads | // 40
|
| // 200
|
||||||
AllowedEntities.OtherCharacters | // 20
|
AllowedEntities.Payloads
|
||||||
AllowedEntities.CharacterList | // 10
|
| // 40
|
||||||
AllowedEntities.SpecialCharacters | // 8
|
AllowedEntities.OtherCharacters
|
||||||
AllowedEntities.Numbers | // 4
|
| // 20
|
||||||
AllowedEntities.LowercaseLetters | // 2
|
AllowedEntities.CharacterList
|
||||||
|
| // 10
|
||||||
|
AllowedEntities.SpecialCharacters
|
||||||
|
| // 8
|
||||||
|
AllowedEntities.Numbers
|
||||||
|
| // 4
|
||||||
|
AllowedEntities.LowercaseLetters
|
||||||
|
| // 2
|
||||||
AllowedEntities.UppercaseLetters, // 1
|
AllowedEntities.UppercaseLetters, // 1
|
||||||
null);
|
null
|
||||||
ArgumentOutOfRangeException.ThrowIfNotEqual(unsanitizedLength, str->Length, nameof(message));
|
);
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNotEqual(
|
||||||
|
unsanitizedLength,
|
||||||
|
str->Length,
|
||||||
|
nameof(message)
|
||||||
|
);
|
||||||
|
|
||||||
UIModule.Instance()->ProcessChatBoxEntry(str);
|
UIModule.Instance()->ProcessChatBoxEntry(str);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -15,7 +15,8 @@ public static class Colors
|
|||||||
public static readonly Vector4 Collectability = new(0.99f, 0.56f, 0.57f, 1f);
|
public static readonly Vector4 Collectability = new(0.99f, 0.56f, 0.57f, 1f);
|
||||||
public static readonly Vector4 CP = new(0.63f, 0.37f, 0.75f, 1f);
|
public static readonly Vector4 CP = new(0.63f, 0.37f, 0.75f, 1f);
|
||||||
|
|
||||||
private static Vector4 SolverProgressBg => ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.TableBorderLight));
|
private static Vector4 SolverProgressBg =>
|
||||||
|
ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.TableBorderLight));
|
||||||
private static Vector4 SolverProgressFgBland => ImGuiColors.DalamudWhite2;
|
private static Vector4 SolverProgressFgBland => ImGuiColors.DalamudWhite2;
|
||||||
|
|
||||||
private static readonly Vector4[] SolverProgressFgColorful =
|
private static readonly Vector4[] SolverProgressFgColorful =
|
||||||
@@ -52,7 +53,7 @@ public static class Colors
|
|||||||
{
|
{
|
||||||
Configuration.ProgressBarType.Colorful => SolverProgressFgColorful,
|
Configuration.ProgressBarType.Colorful => SolverProgressFgColorful,
|
||||||
Configuration.ProgressBarType.Simple => SolverProgressFgMonochromatic,
|
Configuration.ProgressBarType.Simple => SolverProgressFgMonochromatic,
|
||||||
_ => throw new InvalidOperationException("No progress bar should be visible")
|
_ => throw new InvalidOperationException("No progress bar should be visible"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (stageValue is not { } stage)
|
if (stageValue is not { } stage)
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
using Dalamud.Networking.Http;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Craftimizer.Simulator.Actions;
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
|
using Craftimizer.Simulator.Actions;
|
||||||
using Craftimizer.Solver;
|
using Craftimizer.Solver;
|
||||||
|
using Dalamud.Networking.Http;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -40,7 +40,9 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
[JsonPropertyName("integerValue")]
|
[JsonPropertyName("integerValue")]
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
[JsonNumberHandling(
|
||||||
|
JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
|
||||||
|
)]
|
||||||
public required int Value { get; set; }
|
public required int Value { get; set; }
|
||||||
|
|
||||||
public static implicit operator int(IntegerValue v) => v.Value;
|
public static implicit operator int(IntegerValue v) => v.Value;
|
||||||
@@ -59,6 +61,7 @@ public sealed class CommunityMacros
|
|||||||
public required ValueData Data { get; set; }
|
public required ValueData Data { get; set; }
|
||||||
|
|
||||||
public T Value => Data.Fields;
|
public T Value => Data.Fields;
|
||||||
|
|
||||||
public static implicit operator T(MapValue<T> v) => v.Value;
|
public static implicit operator T(MapValue<T> v) => v.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +77,7 @@ public sealed class CommunityMacros
|
|||||||
public required ValueData Data { get; set; }
|
public required ValueData Data { get; set; }
|
||||||
|
|
||||||
public T[] Value => Data.Values ?? [];
|
public T[] Value => Data.Values ?? [];
|
||||||
|
|
||||||
public static implicit operator T[](ArrayValue<T> v) => v.Value;
|
public static implicit operator T[](ArrayValue<T> v) => v.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +92,10 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required List<CollectionSelector> From { get; set; }
|
public required List<CollectionSelector> From { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required Filter Where { get; set; }
|
public required Filter Where { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required List<Order> OrderBy { get; set; }
|
public required List<Order> OrderBy { get; set; }
|
||||||
}
|
}
|
||||||
@@ -109,6 +115,7 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required List<Filter> Filters { get; set; }
|
public required List<Filter> Filters { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required CompositeOperator Op { get; set; }
|
public required CompositeOperator Op { get; set; }
|
||||||
}
|
}
|
||||||
@@ -117,13 +124,14 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
OPERATOR_UNSPECIFIED,
|
OPERATOR_UNSPECIFIED,
|
||||||
AND,
|
AND,
|
||||||
OR
|
OR,
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed record FieldFilter
|
public sealed record FieldFilter
|
||||||
{
|
{
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required FieldReference Field { get; set; }
|
public required FieldReference Field { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required FieldOperator Op { get; set; }
|
public required FieldOperator Op { get; set; }
|
||||||
public object? Value { get; set; }
|
public object? Value { get; set; }
|
||||||
@@ -141,13 +149,14 @@ public sealed class CommunityMacros
|
|||||||
ARRAY_CONTAINS,
|
ARRAY_CONTAINS,
|
||||||
IN,
|
IN,
|
||||||
ARRAY_CONTAINS_ANY,
|
ARRAY_CONTAINS_ANY,
|
||||||
NOT_IN
|
NOT_IN,
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed record Order
|
public sealed record Order
|
||||||
{
|
{
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required FieldReference Field { get; set; }
|
public required FieldReference Field { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required Direction Direction { get; set; }
|
public required Direction Direction { get; set; }
|
||||||
}
|
}
|
||||||
@@ -162,7 +171,7 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
DIRECTION_UNSPECIFIED,
|
DIRECTION_UNSPECIFIED,
|
||||||
ASCENDING,
|
ASCENDING,
|
||||||
DESCENDING
|
DESCENDING,
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record RunQueryRequest
|
private sealed record RunQueryRequest
|
||||||
@@ -178,6 +187,7 @@ public sealed class CommunityMacros
|
|||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
[JsonPropertyName("rlvl")]
|
[JsonPropertyName("rlvl")]
|
||||||
public required IntegerValue RLvl { get; set; }
|
public required IntegerValue RLvl { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required IntegerValue Durability { get; set; }
|
public required IntegerValue Durability { get; set; }
|
||||||
}
|
}
|
||||||
@@ -185,6 +195,7 @@ public sealed class CommunityMacros
|
|||||||
public sealed record FieldData
|
public sealed record FieldData
|
||||||
{
|
{
|
||||||
public StringValue? Name { get; set; }
|
public StringValue? Name { get; set; }
|
||||||
|
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public required ArrayValue<StringValue> Rotation { get; set; }
|
public required ArrayValue<StringValue> Rotation { get; set; }
|
||||||
public MapValue<RecipeFieldData>? Recipe { get; set; }
|
public MapValue<RecipeFieldData>? Recipe { get; set; }
|
||||||
@@ -212,6 +223,7 @@ public sealed class CommunityMacros
|
|||||||
public string? Slug { get; set; }
|
public string? Slug { get; set; }
|
||||||
public string? Version { get; set; }
|
public string? Version { get; set; }
|
||||||
public string? Job { get; set; }
|
public string? Job { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("job_level")]
|
[JsonPropertyName("job_level")]
|
||||||
public int JobLevel { get; set; }
|
public int JobLevel { get; set; }
|
||||||
public int Craftsmanship { get; set; }
|
public int Craftsmanship { get; set; }
|
||||||
@@ -219,11 +231,14 @@ public sealed class CommunityMacros
|
|||||||
public int CP { get; set; }
|
public int CP { get; set; }
|
||||||
public string? Food { get; set; }
|
public string? Food { get; set; }
|
||||||
public string? Potion { get; set; }
|
public string? Potion { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("recipe_job_level")]
|
[JsonPropertyName("recipe_job_level")]
|
||||||
public int RecipeJobLevel { get; set; }
|
public int RecipeJobLevel { get; set; }
|
||||||
public string? Recipe { get; set; }
|
public string? Recipe { get; set; }
|
||||||
|
|
||||||
// HqIngredients
|
// HqIngredients
|
||||||
public string? Actions { get; set; }
|
public string? Actions { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("created_at")]
|
[JsonPropertyName("created_at")]
|
||||||
public long CreatedAt { get; set; }
|
public long CreatedAt { get; set; }
|
||||||
public string? Error { get; set; }
|
public string? Error { get; set; }
|
||||||
@@ -241,10 +256,13 @@ public sealed class CommunityMacros
|
|||||||
throw new Exception($"Internal error; No fields were returned");
|
throw new Exception($"Internal error; No fields were returned");
|
||||||
|
|
||||||
// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/67f453041c6b2b31d32fcf6e1fd53aa38ed7a12b/apps/client/src/app/model/other/crafting-rotation.ts#L49
|
// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/67f453041c6b2b31d32fcf6e1fd53aa38ed7a12b/apps/client/src/app/model/other/crafting-rotation.ts#L49
|
||||||
Name = rotation.Name?.Value ??
|
Name =
|
||||||
(rotation.Recipe is { Value: var recipe } ?
|
rotation.Name?.Value
|
||||||
$"rlvl{recipe.RLvl.Value} - {rotation.Rotation.Value.Length} steps, {recipe.Durability.Value} dur" :
|
?? (
|
||||||
"New Teamcraft Rotation");
|
rotation.Recipe is { Value: var recipe }
|
||||||
|
? $"rlvl{recipe.RLvl.Value} - {rotation.Rotation.Value.Length} steps, {recipe.Durability.Value} dur"
|
||||||
|
: "New Teamcraft Rotation"
|
||||||
|
);
|
||||||
|
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
foreach (var action in rotation.Rotation.Value)
|
foreach (var action in rotation.Rotation.Value)
|
||||||
@@ -290,7 +308,7 @@ public sealed class CommunityMacros
|
|||||||
|
|
||||||
"RemoveFinalAppraisal" => null,
|
"RemoveFinalAppraisal" => null,
|
||||||
// Old actions?
|
// Old actions?
|
||||||
_ => null
|
_ => null,
|
||||||
};
|
};
|
||||||
if (actionType.HasValue)
|
if (actionType.HasValue)
|
||||||
actions.Add(actionType.Value);
|
actions.Add(actionType.Value);
|
||||||
@@ -353,7 +371,7 @@ public sealed class CommunityMacros
|
|||||||
"DelicateSynthesisTraited" => ActionType.DelicateSynthesis,
|
"DelicateSynthesisTraited" => ActionType.DelicateSynthesis,
|
||||||
|
|
||||||
// Old actions?
|
// Old actions?
|
||||||
_ => null
|
_ => null,
|
||||||
};
|
};
|
||||||
if (actionType.HasValue)
|
if (actionType.HasValue)
|
||||||
actions.Add(actionType.Value);
|
actions.Add(actionType.Value);
|
||||||
@@ -361,16 +379,30 @@ public sealed class CommunityMacros
|
|||||||
Actions = actions;
|
Actions = actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (float Score, SimulationState FinalState) CalculateScore(SimulatorNoRandom simulator, in SimulationState startingState, in MCTSConfig mctsConfig)
|
public (float Score, SimulationState FinalState) CalculateScore(
|
||||||
|
SimulatorNoRandom simulator,
|
||||||
|
in SimulationState startingState,
|
||||||
|
in MCTSConfig mctsConfig
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return CalculateScore(Actions, simulator, startingState, mctsConfig);
|
return CalculateScore(Actions, simulator, startingState, mctsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (float Score, SimulationState FinalState) CalculateScore(IReadOnlyCollection<ActionType> actions, SimulatorNoRandom simulator, in SimulationState startingState, in MCTSConfig mctsConfig)
|
public static (float Score, SimulationState FinalState) CalculateScore(
|
||||||
|
IReadOnlyCollection<ActionType> actions,
|
||||||
|
SimulatorNoRandom simulator,
|
||||||
|
in SimulationState startingState,
|
||||||
|
in MCTSConfig mctsConfig
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var (resp, outState, failedIdx) = simulator.ExecuteMultiple(startingState, actions);
|
var (resp, outState, failedIdx) = simulator.ExecuteMultiple(startingState, actions);
|
||||||
outState.ActionCount = actions.Count;
|
outState.ActionCount = actions.Count;
|
||||||
var score = SimulationNode.CalculateScoreForState(outState, simulator.CompletionState, mctsConfig) ?? 0;
|
var score =
|
||||||
|
SimulationNode.CalculateScoreForState(
|
||||||
|
outState,
|
||||||
|
simulator.CompletionState,
|
||||||
|
mctsConfig
|
||||||
|
) ?? 0;
|
||||||
if (resp != ActionResponse.SimulationComplete)
|
if (resp != ActionResponse.SimulationComplete)
|
||||||
{
|
{
|
||||||
if (failedIdx != -1)
|
if (failedIdx != -1)
|
||||||
@@ -382,7 +414,10 @@ public sealed class CommunityMacros
|
|||||||
|
|
||||||
private Dictionary<int, List<CommunityMacro>> CachedRotations { get; } = [];
|
private Dictionary<int, List<CommunityMacro>> CachedRotations { get; } = [];
|
||||||
|
|
||||||
public async Task<IReadOnlyList<CommunityMacro>> RetrieveRotations(int rlvl, CancellationToken token)
|
public async Task<IReadOnlyList<CommunityMacro>> RetrieveRotations(
|
||||||
|
int rlvl,
|
||||||
|
CancellationToken token
|
||||||
|
)
|
||||||
{
|
{
|
||||||
lock (CachedRotations)
|
lock (CachedRotations)
|
||||||
{
|
{
|
||||||
@@ -397,23 +432,25 @@ public sealed class CommunityMacros
|
|||||||
return macros;
|
return macros;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<TeamcraftMacro>> RetrieveRotationsInternal(int rlvl, CancellationToken token)
|
private static async Task<List<TeamcraftMacro>> RetrieveRotationsInternal(
|
||||||
|
int rlvl,
|
||||||
|
CancellationToken token
|
||||||
|
)
|
||||||
{
|
{
|
||||||
using var heCallback = new HappyEyeballsCallback();
|
using var heCallback = new HappyEyeballsCallback();
|
||||||
using var client = new HttpClient(new SocketsHttpHandler
|
using var client = new HttpClient(
|
||||||
|
new SocketsHttpHandler
|
||||||
{
|
{
|
||||||
AutomaticDecompression = DecompressionMethods.All,
|
AutomaticDecompression = DecompressionMethods.All,
|
||||||
ConnectCallback = heCallback.ConnectCallback,
|
ConnectCallback = heCallback.ConnectCallback,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
var request = new RunQueryRequest
|
var request = new RunQueryRequest
|
||||||
{
|
{
|
||||||
StructuredQuery = new StructuredQuery
|
StructuredQuery = new StructuredQuery
|
||||||
{
|
{
|
||||||
From =
|
From = [new() { CollectionId = "rotations" }],
|
||||||
[
|
|
||||||
new() { CollectionId = "rotations" }
|
|
||||||
],
|
|
||||||
Where = new Filter
|
Where = new Filter
|
||||||
{
|
{
|
||||||
CompositeFilter = new CompositeFilter
|
CompositeFilter = new CompositeFilter
|
||||||
@@ -427,8 +464,8 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
Field = new FieldReference { FieldPath = "public" },
|
Field = new FieldReference { FieldPath = "public" },
|
||||||
Op = FieldOperator.EQUAL,
|
Op = FieldOperator.EQUAL,
|
||||||
Value = new BooleanValue { Value = true }
|
Value = new BooleanValue { Value = true },
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
@@ -436,10 +473,10 @@ public sealed class CommunityMacros
|
|||||||
{
|
{
|
||||||
Field = new FieldReference { FieldPath = "community.rlvl" },
|
Field = new FieldReference { FieldPath = "community.rlvl" },
|
||||||
Op = FieldOperator.EQUAL,
|
Op = FieldOperator.EQUAL,
|
||||||
Value = new IntegerValue { Value = rlvl }
|
Value = new IntegerValue { Value = rlvl },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OrderBy =
|
OrderBy =
|
||||||
@@ -447,26 +484,29 @@ public sealed class CommunityMacros
|
|||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Field = new FieldReference { FieldPath = "xivVersion" },
|
Field = new FieldReference { FieldPath = "xivVersion" },
|
||||||
Direction = Direction.DESCENDING
|
Direction = Direction.DESCENDING,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Field = new FieldReference { FieldPath = "__name__" },
|
Field = new FieldReference { FieldPath = "__name__" },
|
||||||
Direction = Direction.DESCENDING
|
Direction = Direction.DESCENDING,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var resp = await PostFromJsonAsync<RunQueryRequest, List<QueriedTeamcraftMacro>>(
|
var resp = await PostFromJsonAsync<RunQueryRequest, List<QueriedTeamcraftMacro>>(
|
||||||
client,
|
client,
|
||||||
$"https://firestore.googleapis.com/v1beta1/projects/ffxivteamcraft/databases/(default)/documents:runQuery",
|
$"https://firestore.googleapis.com/v1beta1/projects/ffxivteamcraft/databases/(default)/documents:runQuery",
|
||||||
request, new JsonSerializerOptions
|
request,
|
||||||
|
new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
}, token).
|
},
|
||||||
ConfigureAwait(false);
|
token
|
||||||
|
)
|
||||||
|
.ConfigureAwait(false);
|
||||||
if (resp is null)
|
if (resp is null)
|
||||||
throw new Exception("Internal server error; failed to retrieve macro");
|
throw new Exception("Internal server error; failed to retrieve macro");
|
||||||
|
|
||||||
@@ -476,10 +516,18 @@ public sealed class CommunityMacros
|
|||||||
throw new Exception($"Internal server error ({error.Status}); {error.Message}");
|
throw new Exception($"Internal server error ({error.Status}); {error.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.Where(macro => macro.Document is not null).Select(macro => macro.Document!).ToList();
|
return resp.Where(macro => macro.Document is not null)
|
||||||
|
.Select(macro => macro.Document!)
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TResponse?> PostFromJsonAsync<TRequest, TResponse>(HttpClient client, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, TRequest value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
|
private static async Task<TResponse?> PostFromJsonAsync<TRequest, TResponse>(
|
||||||
|
HttpClient client,
|
||||||
|
[StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri,
|
||||||
|
TRequest value,
|
||||||
|
JsonSerializerOptions? options = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(client);
|
ArgumentNullException.ThrowIfNull(client);
|
||||||
|
|
||||||
@@ -487,6 +535,8 @@ public sealed class CommunityMacros
|
|||||||
using var message = await resp.ConfigureAwait(false);
|
using var message = await resp.ConfigureAwait(false);
|
||||||
message.EnsureSuccessStatusCode();
|
message.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
return await message.Content!.ReadFromJsonAsync<TResponse>(options, cancellationToken).ConfigureAwait(false);
|
return await message
|
||||||
|
.Content!.ReadFromJsonAsync<TResponse>(options, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,34 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Utility.Numerics;
|
using System.Numerics;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Utility.Numerics;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
internal static class DynamicBars
|
internal static class DynamicBars
|
||||||
{
|
{
|
||||||
public readonly record struct BarData(string Name, Vector4 Color, SimulatedMacro.Reliablity.Param? Reliability, float Value, float Max, IReadOnlyList<int?>? Collectability = null, string? Caption = null, string? DefaultCaptionSizeText = null, Action<DrawerParams>? CustomDrawer = null)
|
public readonly record struct BarData(
|
||||||
{
|
string Name,
|
||||||
public BarData(string name, Action<DrawerParams> customDrawer) : this(name, default, null, 0, 0, null, null, null, customDrawer)
|
Vector4 Color,
|
||||||
|
SimulatedMacro.Reliablity.Param? Reliability,
|
||||||
|
float Value,
|
||||||
|
float Max,
|
||||||
|
IReadOnlyList<int?>? Collectability = null,
|
||||||
|
string? Caption = null,
|
||||||
|
string? DefaultCaptionSizeText = null,
|
||||||
|
Action<DrawerParams>? CustomDrawer = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
|
public BarData(string name, Action<DrawerParams> customDrawer)
|
||||||
|
: this(name, default, null, 0, 0, null, null, null, customDrawer) { }
|
||||||
|
|
||||||
}
|
public BarData(string name, Vector4 color, float value, float max)
|
||||||
|
: this(name, color, null, value, max, null, null, null) { }
|
||||||
public BarData(string name, Vector4 color, float value, float max) : this(name, color, null, value, max, null, null, null)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly record struct DrawerParams(float TotalSize, float Spacing);
|
public readonly record struct DrawerParams(float TotalSize, float Spacing);
|
||||||
@@ -39,13 +45,19 @@ internal static class DynamicBars
|
|||||||
return Math.Max(ImGui.CalcTextSize(caption).X, defaultSize);
|
return Math.Max(ImGui.CalcTextSize(caption).X, defaultSize);
|
||||||
// max (sp/2) "/" (sp/2) max
|
// max (sp/2) "/" (sp/2) max
|
||||||
return Math.Max(
|
return Math.Max(
|
||||||
Math.Max(ImGui.CalcTextSize($"{b.Value:0}").X, ImGui.CalcTextSize($"{b.Max:0}").X) * 2
|
Math.Max(ImGui.CalcTextSize($"{b.Value:0}").X, ImGui.CalcTextSize($"{b.Max:0}").X)
|
||||||
|
* 2
|
||||||
+ ImGui.GetStyle().ItemSpacing.X
|
+ ImGui.GetStyle().ItemSpacing.X
|
||||||
+ ImGui.CalcTextSize("/").X,
|
+ ImGui.CalcTextSize("/").X,
|
||||||
defaultSize);
|
defaultSize
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
private static ImRaii.ColorDisposable? PushCollectableColor(this in BarData bar, float collectability, bool colorUnmetThreshold = true)
|
private static ImRaii.ColorDisposable? PushCollectableColor(
|
||||||
|
this in BarData bar,
|
||||||
|
float collectability,
|
||||||
|
bool colorUnmetThreshold = true
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (bar.Collectability is not { } collectabilities)
|
if (bar.Collectability is not { } collectabilities)
|
||||||
return null;
|
return null;
|
||||||
@@ -88,7 +100,10 @@ internal static class DynamicBars
|
|||||||
var pos = ImGui.GetCursorPos();
|
var pos = ImGui.GetCursorPos();
|
||||||
var screenPos = ImGui.GetCursorScreenPos();
|
var screenPos = ImGui.GetCursorScreenPos();
|
||||||
using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, bar.Color))
|
using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, bar.Color))
|
||||||
ImGuiUtils.ProgressBar(Math.Clamp(bar.Value / bar.Max, 0, 1), new(barSize, ImGui.GetFrameHeight()));
|
ImGuiUtils.ProgressBar(
|
||||||
|
Math.Clamp(bar.Value / bar.Max, 0, 1),
|
||||||
|
new(barSize, ImGui.GetFrameHeight())
|
||||||
|
);
|
||||||
if (bar.Collectability is { } collectability)
|
if (bar.Collectability is { } collectability)
|
||||||
{
|
{
|
||||||
var i = 0;
|
var i = 0;
|
||||||
@@ -101,15 +116,21 @@ internal static class DynamicBars
|
|||||||
continue;
|
continue;
|
||||||
var offset = barSize * threshold / bar.Max;
|
var offset = barSize * threshold / bar.Max;
|
||||||
var isLast = i == collectability.Count;
|
var isLast = i == collectability.Count;
|
||||||
var offsetNext = isLast ? barSize : barSize * collectability[i]!.Value / bar.Max;
|
var offsetNext = isLast
|
||||||
|
? barSize
|
||||||
|
: barSize * collectability[i]!.Value / bar.Max;
|
||||||
var passedThreshold = bar.Value >= threshold;
|
var passedThreshold = bar.Value >= threshold;
|
||||||
ImGui.GetWindowDrawList().AddRectFilled(
|
ImGui
|
||||||
|
.GetWindowDrawList()
|
||||||
|
.AddRectFilled(
|
||||||
screenPos + new Vector2(offset, 0),
|
screenPos + new Vector2(offset, 0),
|
||||||
screenPos + new Vector2(offsetNext, height),
|
screenPos + new Vector2(offsetNext, height),
|
||||||
ImGui.GetColorU32(color.WithW(passedThreshold ? 0.6f : 0.2f)),
|
ImGui.GetColorU32(color.WithW(passedThreshold ? 0.6f : 0.2f)),
|
||||||
isLast ? rounding : 0
|
isLast ? rounding : 0
|
||||||
);
|
);
|
||||||
ImGui.GetWindowDrawList().AddLine(
|
ImGui
|
||||||
|
.GetWindowDrawList()
|
||||||
|
.AddLine(
|
||||||
screenPos + new Vector2(offset, 0),
|
screenPos + new Vector2(offset, 0),
|
||||||
screenPos + new Vector2(offset, height),
|
screenPos + new Vector2(offset, height),
|
||||||
ImGui.GetColorU32(color),
|
ImGui.GetColorU32(color),
|
||||||
@@ -121,7 +142,10 @@ internal static class DynamicBars
|
|||||||
{
|
{
|
||||||
if (bar.Reliability is { } reliability)
|
if (bar.Reliability is { } reliability)
|
||||||
{
|
{
|
||||||
if (reliability.GetViolinData(bar.Max, (int)(barSize / 5), 0.02) is { } violinData)
|
if (
|
||||||
|
reliability.GetViolinData(bar.Max, (int)(barSize / 5), 0.02) is
|
||||||
|
{ } violinData
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPos(pos);
|
ImGui.SetCursorPos(pos);
|
||||||
ImGuiUtils.ViolinPlot(violinData, new(barSize, ImGui.GetFrameHeight()));
|
ImGuiUtils.ViolinPlot(violinData, new(barSize, ImGui.GetFrameHeight()));
|
||||||
@@ -129,7 +153,10 @@ internal static class DynamicBars
|
|||||||
{
|
{
|
||||||
using var _font = ImRaii.PushFont(UiBuilder.DefaultFont);
|
using var _font = ImRaii.PushFont(UiBuilder.DefaultFont);
|
||||||
using var _tooltip = ImRaii.Tooltip();
|
using var _tooltip = ImRaii.Tooltip();
|
||||||
using var _ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
using var _ = ImRaii.PushStyle(
|
||||||
|
ImGuiStyleVar.ItemSpacing,
|
||||||
|
Vector2.Zero
|
||||||
|
);
|
||||||
|
|
||||||
ImGui.TextUnformatted("Min: ");
|
ImGui.TextUnformatted("Min: ");
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
@@ -221,7 +248,8 @@ internal static class DynamicBars
|
|||||||
{
|
{
|
||||||
tooltip = $"Solver Progress: {solver.ProgressValue:N0} / {solver.ProgressMax:N0}";
|
tooltip = $"Solver Progress: {solver.ProgressValue:N0} / {solver.ProgressMax:N0}";
|
||||||
if (solver.ProgressValue > solver.ProgressMax)
|
if (solver.ProgressValue > solver.ProgressMax)
|
||||||
tooltip += $"\n\nThis is taking longer than expected. Check to see if your gear stats are good and the solver settings are adequate.";
|
tooltip +=
|
||||||
|
$"\n\nThis is taking longer than expected. Check to see if your gear stats are good and the solver settings are adequate.";
|
||||||
}
|
}
|
||||||
ImGuiUtils.TooltipWrapped(tooltip);
|
ImGuiUtils.TooltipWrapped(tooltip);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Lumina.Excel.Sheets;
|
|
||||||
using System.Collections.Frozen;
|
using System.Collections.Frozen;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -15,8 +15,20 @@ public static class FoodStatus
|
|||||||
private static readonly ImmutableArray<uint> FoodOrder;
|
private static readonly ImmutableArray<uint> FoodOrder;
|
||||||
private static readonly ImmutableArray<uint> MedicineOrder;
|
private static readonly ImmutableArray<uint> MedicineOrder;
|
||||||
|
|
||||||
public readonly record struct FoodStat(bool IsRelative, int Value, int Max, int ValueHQ, int MaxHQ);
|
public readonly record struct FoodStat(
|
||||||
public readonly record struct Food(Item Item, FoodStat? Craftsmanship, FoodStat? Control, FoodStat? CP);
|
bool IsRelative,
|
||||||
|
int Value,
|
||||||
|
int Max,
|
||||||
|
int ValueHQ,
|
||||||
|
int MaxHQ
|
||||||
|
);
|
||||||
|
|
||||||
|
public readonly record struct Food(
|
||||||
|
Item Item,
|
||||||
|
FoodStat? Craftsmanship,
|
||||||
|
FoodStat? Control,
|
||||||
|
FoodStat? CP
|
||||||
|
);
|
||||||
|
|
||||||
static FoodStatus()
|
static FoodStatus()
|
||||||
{
|
{
|
||||||
@@ -39,18 +51,33 @@ public static class FoodStatus
|
|||||||
if (LuminaSheets.ItemFoodSheet.GetRowOrDefault(itemAction.Data[1]) is not { } itemFood)
|
if (LuminaSheets.ItemFoodSheet.GetRowOrDefault(itemAction.Data[1]) is not { } itemFood)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FoodStat? craftsmanship = null, control = null, cp = null;
|
FoodStat? craftsmanship = null,
|
||||||
|
control = null,
|
||||||
|
cp = null;
|
||||||
foreach (var stat in itemFood.Params)
|
foreach (var stat in itemFood.Params)
|
||||||
{
|
{
|
||||||
if (stat.BaseParam.RowId == 0)
|
if (stat.BaseParam.RowId == 0)
|
||||||
continue;
|
continue;
|
||||||
var foodStat = new FoodStat(stat.IsRelative, stat.Value, stat.Max, stat.ValueHQ, stat.MaxHQ);
|
var foodStat = new FoodStat(
|
||||||
|
stat.IsRelative,
|
||||||
|
stat.Value,
|
||||||
|
stat.Max,
|
||||||
|
stat.ValueHQ,
|
||||||
|
stat.MaxHQ
|
||||||
|
);
|
||||||
switch (stat.BaseParam.RowId)
|
switch (stat.BaseParam.RowId)
|
||||||
{
|
{
|
||||||
case Gearsets.ParamCraftsmanship: craftsmanship = foodStat; break;
|
case Gearsets.ParamCraftsmanship:
|
||||||
case Gearsets.ParamControl: control = foodStat; break;
|
craftsmanship = foodStat;
|
||||||
case Gearsets.ParamCP: cp = foodStat; break;
|
break;
|
||||||
default: continue;
|
case Gearsets.ParamControl:
|
||||||
|
control = foodStat;
|
||||||
|
break;
|
||||||
|
case Gearsets.ParamCP:
|
||||||
|
cp = foodStat;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,14 +97,23 @@ public static class FoodStatus
|
|||||||
FoodItems = foods.ToFrozenDictionary();
|
FoodItems = foods.ToFrozenDictionary();
|
||||||
MedicineItems = medicines.ToFrozenDictionary();
|
MedicineItems = medicines.ToFrozenDictionary();
|
||||||
|
|
||||||
FoodOrder = [.. FoodItems.OrderByDescending(a => a.Value.Item.LevelItem.RowId).Select(a => a.Key)];
|
FoodOrder =
|
||||||
MedicineOrder = [.. MedicineItems.OrderByDescending(a => a.Value.Item.LevelItem.RowId).Select(a => a.Key)];
|
[
|
||||||
|
.. FoodItems.OrderByDescending(a => a.Value.Item.LevelItem.RowId).Select(a => a.Key),
|
||||||
|
];
|
||||||
|
MedicineOrder =
|
||||||
|
[
|
||||||
|
.. MedicineItems
|
||||||
|
.OrderByDescending(a => a.Value.Item.LevelItem.RowId)
|
||||||
|
.Select(a => a.Key),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Initialize() { }
|
public static void Initialize() { }
|
||||||
|
|
||||||
public static IEnumerable<Food> OrderedFoods => FoodOrder.Select(id => FoodItems[id]);
|
public static IEnumerable<Food> OrderedFoods => FoodOrder.Select(id => FoodItems[id]);
|
||||||
public static IEnumerable<Food> OrderedMedicines => MedicineOrder.Select(id => MedicineItems[id]);
|
public static IEnumerable<Food> OrderedMedicines =>
|
||||||
|
MedicineOrder.Select(id => MedicineItems[id]);
|
||||||
|
|
||||||
public static (uint ItemId, bool IsHQ)? ResolveFoodParam(ushort param)
|
public static (uint ItemId, bool IsHQ)? ResolveFoodParam(ushort param)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ internal readonly struct FuzzyMatcher
|
|||||||
{
|
{
|
||||||
MatchMode.FuzzyParts => FindNeedleSegments(needleString),
|
MatchMode.FuzzyParts => FindNeedleSegments(needleString),
|
||||||
MatchMode.Fuzzy or MatchMode.Simple => EmptySegArray,
|
MatchMode.Fuzzy or MatchMode.Simple => EmptySegArray,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(matchMode), matchMode, "Invalid match mode"),
|
_ => throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(matchMode),
|
||||||
|
matchMode,
|
||||||
|
"Invalid match mode"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +63,9 @@ internal readonly struct FuzzyMatcher
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (mode == MatchMode.Simple)
|
if (mode == MatchMode.Simple)
|
||||||
return value.Contains(needleString, StringComparison.InvariantCultureIgnoreCase) ? 1 : 0;
|
return value.Contains(needleString, StringComparison.InvariantCultureIgnoreCase)
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
|
||||||
if (mode == MatchMode.Fuzzy)
|
if (mode == MatchMode.Fuzzy)
|
||||||
return GetRawScore(value, 0, needleFinalPosition);
|
return GetRawScore(value, 0, needleFinalPosition);
|
||||||
@@ -101,7 +107,11 @@ internal readonly struct FuzzyMatcher
|
|||||||
|
|
||||||
private int GetRawScore(ReadOnlySpan<char> haystack, int needleStart, int needleEnd)
|
private int GetRawScore(ReadOnlySpan<char> haystack, int needleStart, int needleEnd)
|
||||||
{
|
{
|
||||||
var (startPos, gaps, consecutive, borderMatches, endPos) = FindForward(haystack, needleStart, needleEnd);
|
var (startPos, gaps, consecutive, borderMatches, endPos) = FindForward(
|
||||||
|
haystack,
|
||||||
|
needleStart,
|
||||||
|
needleEnd
|
||||||
|
);
|
||||||
if (startPos < 0)
|
if (startPos < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -109,28 +119,40 @@ internal readonly struct FuzzyMatcher
|
|||||||
|
|
||||||
var score = CalculateRawScore(needleSize, startPos, gaps, consecutive, borderMatches);
|
var score = CalculateRawScore(needleSize, startPos, gaps, consecutive, borderMatches);
|
||||||
|
|
||||||
(startPos, gaps, consecutive, borderMatches) = FindReverse(haystack, endPos, needleStart, needleEnd);
|
(startPos, gaps, consecutive, borderMatches) = FindReverse(
|
||||||
|
haystack,
|
||||||
|
endPos,
|
||||||
|
needleStart,
|
||||||
|
needleEnd
|
||||||
|
);
|
||||||
var revScore = CalculateRawScore(needleSize, startPos, gaps, consecutive, borderMatches);
|
var revScore = CalculateRawScore(needleSize, startPos, gaps, consecutive, borderMatches);
|
||||||
|
|
||||||
return int.Max(score, revScore);
|
return int.Max(score, revScore);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int CalculateRawScore(int needleSize, int startPos, int gaps, int consecutive, int borderMatches)
|
private static int CalculateRawScore(
|
||||||
|
int needleSize,
|
||||||
|
int startPos,
|
||||||
|
int gaps,
|
||||||
|
int consecutive,
|
||||||
|
int borderMatches
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var score = 100
|
var score =
|
||||||
+ needleSize * 3
|
100 + needleSize * 3 + borderMatches * 3 + consecutive * 5 - startPos - gaps * 2;
|
||||||
+ borderMatches * 3
|
|
||||||
+ consecutive * 5
|
|
||||||
- startPos
|
|
||||||
- gaps * 2;
|
|
||||||
if (startPos == 0)
|
if (startPos == 0)
|
||||||
score += 5;
|
score += 5;
|
||||||
return score < 1 ? 1 : score;
|
return score < 1 ? 1 : score;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (int StartPos, int Gaps, int Consecutive, int BorderMatches, int HaystackIndex) FindForward(
|
private (
|
||||||
ReadOnlySpan<char> haystack, int needleStart, int needleEnd)
|
int StartPos,
|
||||||
|
int Gaps,
|
||||||
|
int Consecutive,
|
||||||
|
int BorderMatches,
|
||||||
|
int HaystackIndex
|
||||||
|
) FindForward(ReadOnlySpan<char> haystack, int needleStart, int needleEnd)
|
||||||
{
|
{
|
||||||
var needleIndex = needleStart;
|
var needleIndex = needleStart;
|
||||||
var lastMatchIndex = -10;
|
var lastMatchIndex = -10;
|
||||||
@@ -176,7 +198,11 @@ internal readonly struct FuzzyMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
private (int StartPos, int Gaps, int Consecutive, int BorderMatches) FindReverse(
|
private (int StartPos, int Gaps, int Consecutive, int BorderMatches) FindReverse(
|
||||||
ReadOnlySpan<char> haystack, int haystackLastMatchIndex, int needleStart, int needleEnd)
|
ReadOnlySpan<char> haystack,
|
||||||
|
int haystackLastMatchIndex,
|
||||||
|
int needleStart,
|
||||||
|
int needleEnd
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var needleIndex = needleEnd;
|
var needleIndex = needleEnd;
|
||||||
var revLastMatchIndex = haystack.Length + 10;
|
var revLastMatchIndex = haystack.Length + 10;
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Craftimizer.Plugin;
|
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
public 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);
|
||||||
|
|
||||||
public record struct GearsetItem(uint ItemId, bool IsHq, GearsetMateria[] Materia);
|
public record struct GearsetItem(uint ItemId, bool IsHq, GearsetMateria[] Materia);
|
||||||
|
|
||||||
private static readonly GearsetStats BaseStats = new(180, 0, 0);
|
private static readonly GearsetStats BaseStats = new(180, 0, 0);
|
||||||
@@ -27,7 +29,11 @@ public static unsafe class Gearsets
|
|||||||
for (var i = 0; i < container->Size; ++i)
|
for (var i = 0; i < container->Size; ++i)
|
||||||
{
|
{
|
||||||
var item = container->Items[i];
|
var item = container->Items[i];
|
||||||
items[i] = new(item.ItemId, item.Flags.HasFlag(InventoryItem.ItemFlags.HighQuality), GetMaterias(item.Materia, item.MateriaGrades));
|
items[i] = new(
|
||||||
|
item.ItemId,
|
||||||
|
item.Flags.HasFlag(InventoryItem.ItemFlags.HighQuality),
|
||||||
|
GetMaterias(item.Materia, item.MateriaGrades)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@@ -39,7 +45,11 @@ public static unsafe class Gearsets
|
|||||||
for (var i = 0; i < 14; ++i)
|
for (var i = 0; i < 14; ++i)
|
||||||
{
|
{
|
||||||
var item = gearsetItems[i];
|
var item = gearsetItems[i];
|
||||||
items[i] = new(item.ItemId % 1000000, item.ItemId > 1000000, GetMaterias(item.Materia, item.MateriaGrades));
|
items[i] = new(
|
||||||
|
item.ItemId % 1000000,
|
||||||
|
item.ItemId > 1000000,
|
||||||
|
GetMaterias(item.Materia, item.MateriaGrades)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@@ -48,7 +58,9 @@ public static unsafe class Gearsets
|
|||||||
{
|
{
|
||||||
var item = LuminaSheets.ItemSheet.GetRow(gearsetItem.ItemId)!;
|
var item = LuminaSheets.ItemSheet.GetRow(gearsetItem.ItemId)!;
|
||||||
|
|
||||||
int cp = 0, craftsmanship = 0, control = 0;
|
int cp = 0,
|
||||||
|
craftsmanship = 0,
|
||||||
|
control = 0;
|
||||||
|
|
||||||
void IncreaseStat(uint baseParam, int amount)
|
void IncreaseStat(uint baseParam, int amount)
|
||||||
{
|
{
|
||||||
@@ -83,7 +95,12 @@ public static unsafe class Gearsets
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static GearsetStats CalculateGearsetStats(GearsetItem[] gearsetItems) =>
|
public static GearsetStats CalculateGearsetStats(GearsetItem[] gearsetItems) =>
|
||||||
gearsetItems.Select(CalculateGearsetItemStats).Aggregate(BaseStats, (a, b) => new(a.CP + b.CP, a.Craftsmanship + b.Craftsmanship, a.Control + b.Control));
|
gearsetItems
|
||||||
|
.Select(CalculateGearsetItemStats)
|
||||||
|
.Aggregate(
|
||||||
|
BaseStats,
|
||||||
|
(a, b) => new(a.CP + b.CP, a.Craftsmanship + b.Craftsmanship, a.Control + b.Control)
|
||||||
|
);
|
||||||
|
|
||||||
public static GearsetStats CalculateGearsetCurrentStats()
|
public static GearsetStats CalculateGearsetCurrentStats()
|
||||||
{
|
{
|
||||||
@@ -97,10 +114,24 @@ public static unsafe class Gearsets
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CharacterStats CalculateCharacterStats(GearsetItem[] gearsetItems, int characterLevel, bool canUseManipulation) =>
|
public static CharacterStats CalculateCharacterStats(
|
||||||
CalculateCharacterStats(CalculateGearsetStats(gearsetItems), gearsetItems, characterLevel, canUseManipulation);
|
GearsetItem[] gearsetItems,
|
||||||
|
int characterLevel,
|
||||||
|
bool canUseManipulation
|
||||||
|
) =>
|
||||||
|
CalculateCharacterStats(
|
||||||
|
CalculateGearsetStats(gearsetItems),
|
||||||
|
gearsetItems,
|
||||||
|
characterLevel,
|
||||||
|
canUseManipulation
|
||||||
|
);
|
||||||
|
|
||||||
public static CharacterStats CalculateCharacterStats(GearsetStats gearsetStats, GearsetItem[] gearsetItems, int characterLevel, bool canUseManipulation) =>
|
public static CharacterStats CalculateCharacterStats(
|
||||||
|
GearsetStats gearsetStats,
|
||||||
|
GearsetItem[] gearsetItems,
|
||||||
|
int characterLevel,
|
||||||
|
bool canUseManipulation
|
||||||
|
) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
CP = gearsetStats.CP,
|
CP = gearsetStats.CP,
|
||||||
@@ -115,8 +146,7 @@ public static unsafe class Gearsets
|
|||||||
public static bool HasDelineations() =>
|
public static bool HasDelineations() =>
|
||||||
InventoryManager.Instance()->GetInventoryItemCount(28724) > 0;
|
InventoryManager.Instance()->GetInventoryItemCount(28724) > 0;
|
||||||
|
|
||||||
public static bool IsItem(GearsetItem item, uint itemId) =>
|
public static bool IsItem(GearsetItem item, uint itemId) => item.ItemId == itemId;
|
||||||
item.ItemId == itemId;
|
|
||||||
|
|
||||||
public static bool IsSpecialistSoulCrystal(GearsetItem item)
|
public static bool IsSpecialistSoulCrystal(GearsetItem item)
|
||||||
{
|
{
|
||||||
@@ -125,11 +155,18 @@ public static unsafe class Gearsets
|
|||||||
|
|
||||||
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.RowId == 62 && luminaItem.ClassJobUse.Value.ClassJobCategory.RowId == 33;
|
return luminaItem.ItemUICategory.RowId == 62
|
||||||
|
&& luminaItem.ClassJobUse.Value.ClassJobCategory.RowId == 33;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsSplendorousTool(GearsetItem item) =>
|
public static bool IsSplendorousTool(GearsetItem item) =>
|
||||||
LuminaSheets.ItemSheetEnglish.GetRow(item.ItemId).Description.ToString().Contains("Increases to quality are 1.75 times higher than normal when material condition is Good.", StringComparison.Ordinal);
|
LuminaSheets
|
||||||
|
.ItemSheetEnglish.GetRow(item.ItemId)
|
||||||
|
.Description.ToString()
|
||||||
|
.Contains(
|
||||||
|
"Increases to quality are 1.75 times higher than normal when material condition is Good.",
|
||||||
|
StringComparison.Ordinal
|
||||||
|
);
|
||||||
|
|
||||||
// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/client/src/app/modules/gearsets/materia.service.ts#L265
|
// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/client/src/app/modules/gearsets/materia.service.ts#L265
|
||||||
private static int CalculateParamCap(Item item, uint paramId)
|
private static int CalculateParamCap(Item item, uint paramId)
|
||||||
@@ -142,7 +179,7 @@ public static unsafe class Gearsets
|
|||||||
ParamCP => ilvl.CP,
|
ParamCP => ilvl.CP,
|
||||||
ParamCraftsmanship => ilvl.Craftsmanship,
|
ParamCraftsmanship => ilvl.Craftsmanship,
|
||||||
ParamControl => ilvl.Control,
|
ParamControl => ilvl.Control,
|
||||||
_ => 0
|
_ => 0,
|
||||||
};
|
};
|
||||||
// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/data-extraction/src/extractors/items.extractor.ts#L6
|
// https://github.com/ffxiv-teamcraft/ffxiv-teamcraft/blob/24d0db2d9676f264edf53651b21005305267c84c/apps/data-extraction/src/extractors/items.extractor.ts#L6
|
||||||
var slotMod = item.EquipSlotCategory.RowId switch
|
var slotMod = item.EquipSlotCategory.RowId switch
|
||||||
@@ -168,16 +205,20 @@ public static unsafe class Gearsets
|
|||||||
19 => param.HeadChestHandsLegsFeetPercent,
|
19 => param.HeadChestHandsLegsFeetPercent,
|
||||||
20 => param.ChestLegsGlovesPercent,
|
20 => param.ChestLegsGlovesPercent,
|
||||||
21 => param.ChestLegsFeetPercent,
|
21 => param.ChestLegsFeetPercent,
|
||||||
_ => 0
|
_ => 0,
|
||||||
};
|
};
|
||||||
var roleMod = param.MeldParam[item.BaseParamModifier];
|
var roleMod = param.MeldParam[item.BaseParamModifier];
|
||||||
|
|
||||||
// https://github.com/Caraxi/SimpleTweaksPlugin/pull/595
|
// https://github.com/Caraxi/SimpleTweaksPlugin/pull/595
|
||||||
var cap = (int)Math.Round((float)baseValue * slotMod / (roleMod * 10f), MidpointRounding.AwayFromZero);
|
var cap = (int)
|
||||||
|
Math.Round((float)baseValue * slotMod / (roleMod * 10f), MidpointRounding.AwayFromZero);
|
||||||
return cap == 0 ? int.MaxValue : cap;
|
return cap == 0 ? int.MaxValue : cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GearsetMateria[] GetMaterias(ReadOnlySpan<ushort> types, ReadOnlySpan<byte> grades)
|
private static GearsetMateria[] GetMaterias(
|
||||||
|
ReadOnlySpan<ushort> types,
|
||||||
|
ReadOnlySpan<byte> grades
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var materia = new GearsetMateria[5];
|
var materia = new GearsetMateria[5];
|
||||||
for (var i = 0; i < 5; ++i)
|
for (var i = 0; i < 5; ++i)
|
||||||
|
|||||||
+63
-11
@@ -1,7 +1,7 @@
|
|||||||
|
using System;
|
||||||
using Craftimizer.Plugin;
|
using Craftimizer.Plugin;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
using System;
|
|
||||||
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
||||||
using ActionUtils = Craftimizer.Plugin.ActionUtils;
|
using ActionUtils = Craftimizer.Plugin.ActionUtils;
|
||||||
using CSActionType = FFXIVClientStructs.FFXIV.Client.Game.ActionType;
|
using CSActionType = FFXIVClientStructs.FFXIV.Client.Game.ActionType;
|
||||||
@@ -14,33 +14,77 @@ public sealed unsafe class Hooks : IDisposable
|
|||||||
|
|
||||||
public event OnActionUsedDelegate? OnActionUsed;
|
public event OnActionUsedDelegate? OnActionUsed;
|
||||||
|
|
||||||
public delegate bool UseActionDelegate(ActionManager* manager, CSActionType actionType, uint actionId, ulong targetId, uint extraParam, ActionManager.UseActionMode mode, uint comboRouteId, bool* outOptAreaTargeted);
|
public delegate bool UseActionDelegate(
|
||||||
|
ActionManager* manager,
|
||||||
|
CSActionType actionType,
|
||||||
|
uint actionId,
|
||||||
|
ulong targetId,
|
||||||
|
uint extraParam,
|
||||||
|
ActionManager.UseActionMode mode,
|
||||||
|
uint comboRouteId,
|
||||||
|
bool* outOptAreaTargeted
|
||||||
|
);
|
||||||
|
|
||||||
public readonly Hook<UseActionDelegate> UseActionHook = null!;
|
public readonly Hook<UseActionDelegate> UseActionHook = null!;
|
||||||
|
|
||||||
public delegate byte IsActionHighlightedDelegate(ActionManager* manager, CSActionType actionType, uint actionId);
|
public delegate byte IsActionHighlightedDelegate(
|
||||||
|
ActionManager* manager,
|
||||||
|
CSActionType actionType,
|
||||||
|
uint actionId
|
||||||
|
);
|
||||||
|
|
||||||
public readonly Hook<IsActionHighlightedDelegate> IsActionHighlightedHook = null!;
|
public readonly Hook<IsActionHighlightedDelegate> IsActionHighlightedHook = null!;
|
||||||
|
|
||||||
public Hooks()
|
public Hooks()
|
||||||
{
|
{
|
||||||
UseActionHook = Service.GameInteropProvider.HookFromAddress<UseActionDelegate>((nint)ActionManager.MemberFunctionPointers.UseAction, UseActionDetour);
|
UseActionHook = Service.GameInteropProvider.HookFromAddress<UseActionDelegate>(
|
||||||
IsActionHighlightedHook = Service.GameInteropProvider.HookFromAddress<IsActionHighlightedDelegate>((nint)ActionManager.MemberFunctionPointers.IsActionHighlighted, IsActionHighlightedDetour);
|
(nint)ActionManager.MemberFunctionPointers.UseAction,
|
||||||
|
UseActionDetour
|
||||||
|
);
|
||||||
|
IsActionHighlightedHook =
|
||||||
|
Service.GameInteropProvider.HookFromAddress<IsActionHighlightedDelegate>(
|
||||||
|
(nint)ActionManager.MemberFunctionPointers.IsActionHighlighted,
|
||||||
|
IsActionHighlightedDetour
|
||||||
|
);
|
||||||
|
|
||||||
UseActionHook.Enable();
|
UseActionHook.Enable();
|
||||||
IsActionHighlightedHook.Enable();
|
IsActionHighlightedHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseActionDetour(ActionManager* manager, CSActionType actionType, uint actionId, ulong targetId, uint extraParam, ActionManager.UseActionMode mode, uint comboRouteId, bool* optOutAreaTargeted)
|
private bool UseActionDetour(
|
||||||
|
ActionManager* manager,
|
||||||
|
CSActionType actionType,
|
||||||
|
uint actionId,
|
||||||
|
ulong targetId,
|
||||||
|
uint extraParam,
|
||||||
|
ActionManager.UseActionMode mode,
|
||||||
|
uint comboRouteId,
|
||||||
|
bool* optOutAreaTargeted
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var canCast = manager->GetActionStatus(actionType, actionId) == 0;
|
var canCast = manager->GetActionStatus(actionType, actionId) == 0;
|
||||||
var ret = UseActionHook.Original(manager, actionType, actionId, targetId, extraParam, mode, comboRouteId, optOutAreaTargeted);
|
var ret = UseActionHook.Original(
|
||||||
|
manager,
|
||||||
|
actionType,
|
||||||
|
actionId,
|
||||||
|
targetId,
|
||||||
|
extraParam,
|
||||||
|
mode,
|
||||||
|
comboRouteId,
|
||||||
|
optOutAreaTargeted
|
||||||
|
);
|
||||||
if (canCast && ret && actionType is CSActionType.CraftAction or CSActionType.Action)
|
if (canCast && ret && actionType is CSActionType.CraftAction or CSActionType.Action)
|
||||||
{
|
{
|
||||||
var classJob = ClassJobUtils.GetClassJobFromIdx((byte)(Service.Objects.LocalPlayer?.ClassJob.RowId ?? 0));
|
var classJob = ClassJobUtils.GetClassJobFromIdx(
|
||||||
|
(byte)(Service.Objects.LocalPlayer?.ClassJob.RowId ?? 0)
|
||||||
|
);
|
||||||
if (classJob != null)
|
if (classJob != null)
|
||||||
{
|
{
|
||||||
var simActionType = ActionUtils.GetActionTypeFromId(actionId, classJob.Value, actionType == CSActionType.CraftAction);
|
var simActionType = ActionUtils.GetActionTypeFromId(
|
||||||
|
actionId,
|
||||||
|
classJob.Value,
|
||||||
|
actionType == CSActionType.CraftAction
|
||||||
|
);
|
||||||
if (simActionType != null)
|
if (simActionType != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -57,7 +101,11 @@ public sealed unsafe class Hooks : IDisposable
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte IsActionHighlightedDetour(ActionManager* manager, CSActionType actionType, uint actionId)
|
private byte IsActionHighlightedDetour(
|
||||||
|
ActionManager* manager,
|
||||||
|
CSActionType actionType,
|
||||||
|
uint actionId
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var ret = IsActionHighlightedHook.Original(manager, actionType, actionId);
|
var ret = IsActionHighlightedHook.Original(manager, actionType, actionId);
|
||||||
|
|
||||||
@@ -83,7 +131,11 @@ public sealed unsafe class Hooks : IDisposable
|
|||||||
if (classJob == null)
|
if (classJob == null)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
var simActionType = ActionUtils.GetActionTypeFromId(actionId, classJob.Value, actionType == CSActionType.CraftAction);
|
var simActionType = ActionUtils.GetActionTypeFromId(
|
||||||
|
actionId,
|
||||||
|
classJob.Value,
|
||||||
|
actionType == CSActionType.CraftAction
|
||||||
|
);
|
||||||
if (simActionType == null)
|
if (simActionType == null)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface.Textures;
|
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Interface.Textures;
|
||||||
|
using Dalamud.Interface.Textures.TextureWraps;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -52,7 +52,8 @@ public sealed class IconManager : IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDalamudTextureWrap GetWrapOrEmpty() => GetWrap() ?? Service.DalamudAssetManager.Empty4X4;
|
public IDalamudTextureWrap GetWrapOrEmpty() =>
|
||||||
|
GetWrap() ?? Service.DalamudAssetManager.Empty4X4;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -85,7 +86,10 @@ public sealed class IconManager : IDisposable
|
|||||||
Service.TextureProvider.GetFromGameIcon(new GameIconLookup(id, itemHq: isHq));
|
Service.TextureProvider.GetFromGameIcon(new GameIconLookup(id, itemHq: isHq));
|
||||||
|
|
||||||
private static ISharedImmediateTexture GetAssemblyTextureInternal(string filename) =>
|
private static ISharedImmediateTexture GetAssemblyTextureInternal(string filename) =>
|
||||||
Service.TextureProvider.GetFromManifestResource(Assembly.GetExecutingAssembly(), $"Craftimizer.{filename}");
|
Service.TextureProvider.GetFromManifestResource(
|
||||||
|
Assembly.GetExecutingAssembly(),
|
||||||
|
$"Craftimizer.{filename}"
|
||||||
|
);
|
||||||
|
|
||||||
public static ILoadedTextureIcon GetIcon(uint id, bool isHq = false) =>
|
public static ILoadedTextureIcon GetIcon(uint id, bool isHq = false) =>
|
||||||
new LoadedIcon(GetIconInternal(id, isHq));
|
new LoadedIcon(GetIconInternal(id, isHq));
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using DotNext.Reflection;
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Plugin;
|
||||||
using DotNext.Collections.Generic;
|
using DotNext.Collections.Generic;
|
||||||
|
using DotNext.Reflection;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -39,16 +39,27 @@ public sealed class Ipc
|
|||||||
|
|
||||||
var returnsVoid = typeMethod.ReturnType == typeof(void);
|
var returnsVoid = typeMethod.ReturnType == typeof(void);
|
||||||
|
|
||||||
var propSubscriber = typeof(IDalamudPluginInterface).GetMethod("GetIpcSubscriber", typeMethod.GetParameters().Length + 1, [typeof(string)]);
|
var propSubscriber = typeof(IDalamudPluginInterface).GetMethod(
|
||||||
|
"GetIpcSubscriber",
|
||||||
|
typeMethod.GetParameters().Length + 1,
|
||||||
|
[typeof(string)]
|
||||||
|
);
|
||||||
if (propSubscriber is null)
|
if (propSubscriber is null)
|
||||||
throw new InvalidOperationException("GetIpcSubscriber method not found");
|
throw new InvalidOperationException("GetIpcSubscriber method not found");
|
||||||
|
|
||||||
var callGateSubscriber = propSubscriber.MakeGenericMethod([.. typeMethod.GetParameterTypes(), returnsVoid ? typeof(int) : typeMethod.ReturnType]).Invoke(Service.PluginInterface, [attr.Name ?? prop.Name]);
|
var callGateSubscriber = propSubscriber
|
||||||
|
.MakeGenericMethod([
|
||||||
|
.. typeMethod.GetParameterTypes(),
|
||||||
|
returnsVoid ? typeof(int) : typeMethod.ReturnType,
|
||||||
|
])
|
||||||
|
.Invoke(Service.PluginInterface, [attr.Name ?? prop.Name]);
|
||||||
|
|
||||||
if (callGateSubscriber is null)
|
if (callGateSubscriber is null)
|
||||||
throw new InvalidOperationException("CallGateSubscriber is null");
|
throw new InvalidOperationException("CallGateSubscriber is null");
|
||||||
|
|
||||||
var invokeFunc = callGateSubscriber.GetType().GetMethod(returnsVoid ? "InvokeAction" : "InvokeFunc");
|
var invokeFunc = callGateSubscriber
|
||||||
|
.GetType()
|
||||||
|
.GetMethod(returnsVoid ? "InvokeAction" : "InvokeFunc");
|
||||||
if (invokeFunc is null)
|
if (invokeFunc is null)
|
||||||
throw new InvalidOperationException("Subscriber Invoke method not found");
|
throw new InvalidOperationException("Subscriber Invoke method not found");
|
||||||
|
|
||||||
@@ -62,7 +73,8 @@ public sealed class Ipc
|
|||||||
public Func<bool> MacroMateIsAvailable { get; private set; } = null!;
|
public Func<bool> MacroMateIsAvailable { get; private set; } = null!;
|
||||||
|
|
||||||
[IPCCall("MacroMate.CreateOrUpdateMacro")]
|
[IPCCall("MacroMate.CreateOrUpdateMacro")]
|
||||||
public Func<string, string, string?, uint?, bool> MacroMateCreateMacro { get; private set; } = null!;
|
public Func<string, string, string?, uint?, bool> MacroMateCreateMacro { get; private set; } =
|
||||||
|
null!;
|
||||||
|
|
||||||
[IPCCall("MacroMate.ValidateGroupPath")]
|
[IPCCall("MacroMate.ValidateGroupPath")]
|
||||||
public Func<string, (bool, string?)> MacroMateValidateGroupPath { get; private set; } = null!;
|
public Func<string, (bool, string?)> MacroMateValidateGroupPath { get; private set; } = null!;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using System;
|
using System;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -8,5 +8,6 @@ public static class Log
|
|||||||
public static void Debug(string line) => Service.PluginLog.Debug(line);
|
public static void Debug(string line) => Service.PluginLog.Debug(line);
|
||||||
|
|
||||||
public static void Error(string line) => Service.PluginLog.Error(line);
|
public static void Error(string line) => Service.PluginLog.Error(line);
|
||||||
|
|
||||||
public static void Error(Exception e, string line) => Service.PluginLog.Error(e, line);
|
public static void Error(Exception e, string line) => Service.PluginLog.Error(e, line);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Craftimizer.Plugin;
|
using Craftimizer.Plugin;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -20,13 +20,15 @@ public static class MacroCopy
|
|||||||
{
|
{
|
||||||
if (actions.Count == 0)
|
if (actions.Count == 0)
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = "Cannot copy an empty macro.",
|
Content = "Cannot copy an empty macro.",
|
||||||
MinimizedText = "Cannot copy empty macro",
|
MinimizedText = "Cannot copy empty macro",
|
||||||
Title = "Macro Not Copied",
|
Title = "Macro Not Copied",
|
||||||
Type = NotificationType.Error
|
Type = NotificationType.Error,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +51,14 @@ public static class MacroCopy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> GetMacros(IReadOnlyList<ActionType> actions, MacroCopyConfiguration config)
|
private static List<string> GetMacros(
|
||||||
|
IReadOnlyList<ActionType> actions,
|
||||||
|
MacroCopyConfiguration config
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var mustSplit = (config.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !config.CombineMacro) && config.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate;
|
var mustSplit =
|
||||||
|
(config.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !config.CombineMacro)
|
||||||
|
&& config.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate;
|
||||||
|
|
||||||
var macros = new List<string>();
|
var macros = new List<string>();
|
||||||
|
|
||||||
@@ -143,41 +150,54 @@ public static class MacroCopy
|
|||||||
{
|
{
|
||||||
var config = Service.Configuration.MacroCopy;
|
var config = Service.Configuration.MacroCopy;
|
||||||
|
|
||||||
int i, macroIdx;
|
int i,
|
||||||
|
macroIdx;
|
||||||
for (
|
for (
|
||||||
i = 0, macroIdx = config.StartMacroIdx;
|
i = 0, macroIdx = config.StartMacroIdx;
|
||||||
i < macros.Count && i < config.MaxMacroCount && macroIdx < 100;
|
i < macros.Count && i < config.MaxMacroCount && macroIdx < 100;
|
||||||
i++, macroIdx += config.CopyDown ? 10 : 1)
|
i++, macroIdx += config.CopyDown ? 10 : 1
|
||||||
|
)
|
||||||
SetMacro(macroIdx, config.SharedMacro, macros[i], i + 1);
|
SetMacro(macroIdx, config.SharedMacro, macros[i], i + 1);
|
||||||
|
|
||||||
if (config.ShowCopiedMessage)
|
if (config.ShowCopiedMessage)
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = i > 1 ? "Copied macro to User Macros." : $"Copied {i} macros to User Macros.",
|
Content =
|
||||||
|
i > 1
|
||||||
|
? "Copied macro to User Macros."
|
||||||
|
: $"Copied {i} macros to User Macros.",
|
||||||
MinimizedText = i > 1 ? "Copied macro" : $"Copied {i} macros",
|
MinimizedText = i > 1 ? "Copied macro" : $"Copied {i} macros",
|
||||||
Title = "Macro Copied",
|
Title = "Macro Copied",
|
||||||
Type = NotificationType.Success
|
Type = NotificationType.Success,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (i < macros.Count)
|
if (i < macros.Count)
|
||||||
{
|
{
|
||||||
Service.Plugin.OpenMacroClipboard(macros);
|
Service.Plugin.OpenMacroClipboard(macros);
|
||||||
var rest = macros.Count - i;
|
var rest = macros.Count - i;
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = $"Couldn't copy {rest} macro{(rest == 1 ? "" : "s")}, so a window was opened with all of them.",
|
Content =
|
||||||
|
$"Couldn't copy {rest} macro{(rest == 1 ? "" : "s")}, so a window was opened with all of them.",
|
||||||
Minimized = false,
|
Minimized = false,
|
||||||
Title = "Macro Copied",
|
Title = "Macro Copied",
|
||||||
Type = NotificationType.Warning
|
Type = NotificationType.Warning,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe void SetMacro(int idx, bool isShared, string macroText, int macroIdx)
|
private static unsafe void SetMacro(int idx, bool isShared, string macroText, int macroIdx)
|
||||||
{
|
{
|
||||||
if (idx >= 100 || idx < 0)
|
if (idx >= 100 || idx < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(idx), "Macro index must be between 0 and 99");
|
throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(idx),
|
||||||
|
"Macro index must be between 0 and 99"
|
||||||
|
);
|
||||||
|
|
||||||
var set = isShared ? 1u : 0u;
|
var set = isShared ? 1u : 0u;
|
||||||
|
|
||||||
@@ -201,13 +221,19 @@ public static class MacroCopy
|
|||||||
ImGui.SetClipboardText(string.Join(Environment.NewLine + Environment.NewLine, macros));
|
ImGui.SetClipboardText(string.Join(Environment.NewLine + Environment.NewLine, macros));
|
||||||
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
|
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = macros.Count == 1 ? "Copied macro to clipboard." : $"Copied {macros.Count} macros to clipboard.",
|
Content =
|
||||||
MinimizedText = macros.Count == 1 ? "Copied macro" : $"Copied {macros.Count} macros",
|
macros.Count == 1
|
||||||
|
? "Copied macro to clipboard."
|
||||||
|
: $"Copied {macros.Count} macros to clipboard.",
|
||||||
|
MinimizedText =
|
||||||
|
macros.Count == 1 ? "Copied macro" : $"Copied {macros.Count} macros",
|
||||||
Title = "Macro Copied",
|
Title = "Macro Copied",
|
||||||
Type = NotificationType.Success
|
Type = NotificationType.Success,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,13 +241,15 @@ public static class MacroCopy
|
|||||||
{
|
{
|
||||||
if (!Service.Ipc.MacroMateIsAvailable())
|
if (!Service.Ipc.MacroMateIsAvailable())
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = "Please check if it installed and enabled.",
|
Content = "Please check if it installed and enabled.",
|
||||||
MinimizedText = "Macro Mate is unavailable",
|
MinimizedText = "Macro Mate is unavailable",
|
||||||
Title = "Macro Mate Unavailable",
|
Title = "Macro Mate Unavailable",
|
||||||
Type = NotificationType.Error
|
Type = NotificationType.Error,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,27 +260,36 @@ public static class MacroCopy
|
|||||||
var (isValidParent, parentError) = Service.Ipc.MacroMateValidateGroupPath(parentPath);
|
var (isValidParent, parentError) = Service.Ipc.MacroMateValidateGroupPath(parentPath);
|
||||||
if (!isValidParent)
|
if (!isValidParent)
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = parentError!,
|
Content = parentError!,
|
||||||
MinimizedText = parentError,
|
MinimizedText = parentError,
|
||||||
Title = "Macro Mate Invalid Parent",
|
Title = "Macro Mate Invalid Parent",
|
||||||
Type = NotificationType.Error
|
Type = NotificationType.Error,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service.Ipc.MacroMateCreateMacro(Service.Configuration.MacroCopy.MacroMateName, macro, parentPath, null);
|
Service.Ipc.MacroMateCreateMacro(
|
||||||
|
Service.Configuration.MacroCopy.MacroMateName,
|
||||||
|
macro,
|
||||||
|
parentPath,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
|
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = "Copied macro to Macro Mate.",
|
Content = "Copied macro to Macro Mate.",
|
||||||
MinimizedText = "Copied macro",
|
MinimizedText = "Copied macro",
|
||||||
Title = "Macro Copied",
|
Title = "Macro Copied",
|
||||||
Type = NotificationType.Success
|
Type = NotificationType.Success,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Dalamud.Networking.Http;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -12,6 +8,10 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Craftimizer.Simulator;
|
||||||
|
using Craftimizer.Simulator.Actions;
|
||||||
|
using Dalamud.Networking.Http;
|
||||||
using static Craftimizer.Utils.CommunityMacros;
|
using static Craftimizer.Utils.CommunityMacros;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
@@ -69,7 +69,10 @@ public static class MacroImport
|
|||||||
if (!Uri.TryCreate(url, UriKind.Absolute, out uri!))
|
if (!Uri.TryCreate(url, UriKind.Absolute, out uri!))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && !string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
if (
|
||||||
|
!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)
|
||||||
|
)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!uri.IsDefaultPort)
|
if (!uri.IsDefaultPort)
|
||||||
@@ -87,34 +90,46 @@ public static class MacroImport
|
|||||||
{
|
{
|
||||||
"ffxivteamcraft.com" => RetrieveTeamcraftUrl(uri, token),
|
"ffxivteamcraft.com" => RetrieveTeamcraftUrl(uri, token),
|
||||||
"craftingway.app" => RetrieveCraftingwayUrl(uri, token),
|
"craftingway.app" => RetrieveCraftingwayUrl(uri, token),
|
||||||
_ => throw new UnreachableException("TryParseUrl should handle miscellaneous edge cases"),
|
_ => throw new UnreachableException(
|
||||||
|
"TryParseUrl should handle miscellaneous edge cases"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<CommunityMacro> RetrieveTeamcraftUrl(Uri uri, CancellationToken token)
|
private static async Task<CommunityMacro> RetrieveTeamcraftUrl(Uri uri, CancellationToken token)
|
||||||
{
|
{
|
||||||
using var heCallback = new HappyEyeballsCallback();
|
using var heCallback = new HappyEyeballsCallback();
|
||||||
using var client = new HttpClient(new SocketsHttpHandler
|
using var client = new HttpClient(
|
||||||
|
new SocketsHttpHandler
|
||||||
{
|
{
|
||||||
AutomaticDecompression = DecompressionMethods.All,
|
AutomaticDecompression = DecompressionMethods.All,
|
||||||
ConnectCallback = heCallback.ConnectCallback,
|
ConnectCallback = heCallback.ConnectCallback,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
var path = uri.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped);
|
var path = uri.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped);
|
||||||
if (!path.StartsWith("simulator/", StringComparison.Ordinal))
|
if (!path.StartsWith("simulator/", StringComparison.Ordinal))
|
||||||
throw new ArgumentException("Teamcraft macro url should start with /simulator", nameof(uri));
|
throw new ArgumentException(
|
||||||
|
"Teamcraft macro url should start with /simulator",
|
||||||
|
nameof(uri)
|
||||||
|
);
|
||||||
path = path[10..];
|
path = path[10..];
|
||||||
|
|
||||||
var lastSlash = path.LastIndexOf('/');
|
var lastSlash = path.LastIndexOf('/');
|
||||||
if (lastSlash == -1)
|
if (lastSlash == -1)
|
||||||
throw new ArgumentException("Teamcraft macro url is not in the right format", nameof(uri));
|
throw new ArgumentException(
|
||||||
|
"Teamcraft macro url is not in the right format",
|
||||||
|
nameof(uri)
|
||||||
|
);
|
||||||
|
|
||||||
var id = path[(lastSlash + 1)..];
|
var id = path[(lastSlash + 1)..];
|
||||||
|
|
||||||
var resp = await client.GetFromJsonAsync<TeamcraftMacro>(
|
var resp = await client
|
||||||
|
.GetFromJsonAsync<TeamcraftMacro>(
|
||||||
$"https://firestore.googleapis.com/v1beta1/projects/ffxivteamcraft/databases/(default)/documents/rotations/{id}",
|
$"https://firestore.googleapis.com/v1beta1/projects/ffxivteamcraft/databases/(default)/documents/rotations/{id}",
|
||||||
token).
|
token
|
||||||
ConfigureAwait(false);
|
)
|
||||||
|
.ConfigureAwait(false);
|
||||||
if (resp is null)
|
if (resp is null)
|
||||||
throw new Exception("Internal error; failed to retrieve macro");
|
throw new Exception("Internal error; failed to retrieve macro");
|
||||||
if (resp.Error is { } error)
|
if (resp.Error is { } error)
|
||||||
@@ -122,31 +137,41 @@ public static class MacroImport
|
|||||||
return new(resp);
|
return new(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<CommunityMacro> RetrieveCraftingwayUrl(Uri uri, CancellationToken token)
|
private static async Task<CommunityMacro> RetrieveCraftingwayUrl(
|
||||||
|
Uri uri,
|
||||||
|
CancellationToken token
|
||||||
|
)
|
||||||
{
|
{
|
||||||
using var heCallback = new HappyEyeballsCallback();
|
using var heCallback = new HappyEyeballsCallback();
|
||||||
using var client = new HttpClient(new SocketsHttpHandler
|
using var client = new HttpClient(
|
||||||
|
new SocketsHttpHandler
|
||||||
{
|
{
|
||||||
AutomaticDecompression = DecompressionMethods.All,
|
AutomaticDecompression = DecompressionMethods.All,
|
||||||
ConnectCallback = heCallback.ConnectCallback,
|
ConnectCallback = heCallback.ConnectCallback,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// https://craftingway.app/rotation/variable-blueprint-KmrvS
|
// https://craftingway.app/rotation/variable-blueprint-KmrvS
|
||||||
|
|
||||||
var path = uri.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped);
|
var path = uri.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped);
|
||||||
if (!path.StartsWith("rotation/", StringComparison.Ordinal))
|
if (!path.StartsWith("rotation/", StringComparison.Ordinal))
|
||||||
throw new ArgumentException("Craftingway macro url should start with /rotation", nameof(uri));
|
throw new ArgumentException(
|
||||||
|
"Craftingway macro url should start with /rotation",
|
||||||
|
nameof(uri)
|
||||||
|
);
|
||||||
path = path[9..];
|
path = path[9..];
|
||||||
|
|
||||||
var lastSlash = path.LastIndexOf('/');
|
var lastSlash = path.LastIndexOf('/');
|
||||||
if (lastSlash != -1)
|
if (lastSlash != -1)
|
||||||
throw new ArgumentException("Craftingway macro url is not in the right format", nameof(uri));
|
throw new ArgumentException(
|
||||||
|
"Craftingway macro url is not in the right format",
|
||||||
|
nameof(uri)
|
||||||
|
);
|
||||||
|
|
||||||
var id = path;
|
var id = path;
|
||||||
|
|
||||||
var resp = await client.GetFromJsonAsync<CraftingwayMacro>(
|
var resp = await client
|
||||||
$"https://servingway.fly.dev/rotation/{id}",
|
.GetFromJsonAsync<CraftingwayMacro>($"https://servingway.fly.dev/rotation/{id}", token)
|
||||||
token)
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
if (resp is null)
|
if (resp is null)
|
||||||
throw new Exception("Internal error; failed to retrieve macro");
|
throw new Exception("Internal error; failed to retrieve macro");
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Craftimizer.Simulator;
|
|
||||||
using Lumina.Excel.Sheets;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Craftimizer.Simulator;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
using ClassJob = Craftimizer.Simulator.ClassJob;
|
using ClassJob = Craftimizer.Simulator.ClassJob;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
@@ -28,16 +28,22 @@ public sealed record RecipeData
|
|||||||
{
|
{
|
||||||
RecipeId = recipeId;
|
RecipeId = recipeId;
|
||||||
|
|
||||||
Recipe = LuminaSheets.RecipeSheet.GetRowOrDefault(recipeId) ??
|
Recipe =
|
||||||
throw new ArgumentException($"Invalid recipe id {recipeId}", nameof(recipeId));
|
LuminaSheets.RecipeSheet.GetRowOrDefault(recipeId)
|
||||||
|
?? throw new ArgumentException($"Invalid recipe id {recipeId}", nameof(recipeId));
|
||||||
|
|
||||||
ClassJob = (ClassJob)Recipe.CraftType.RowId;
|
ClassJob = (ClassJob)Recipe.CraftType.RowId;
|
||||||
|
|
||||||
var resolvedLevelTableRow = Recipe.RecipeLevelTable.RowId;
|
var resolvedLevelTableRow = Recipe.RecipeLevelTable.RowId;
|
||||||
if (Recipe.MaxAdjustableJobLevel.RowId != 0)
|
if (Recipe.MaxAdjustableJobLevel.RowId != 0)
|
||||||
{
|
{
|
||||||
AdjustedJobLevel = Math.Min(explicitlyAdjustedJobLevel ?? ClassJob.GetWKSSyncedLevel(), (ushort)Recipe.MaxAdjustableJobLevel.RowId);
|
AdjustedJobLevel = Math.Min(
|
||||||
resolvedLevelTableRow = LuminaSheets.GathererCrafterLvAdjustTableSheet.GetRow(AdjustedJobLevel.Value).RecipeLevel.RowId;
|
explicitlyAdjustedJobLevel ?? ClassJob.GetWKSSyncedLevel(),
|
||||||
|
(ushort)Recipe.MaxAdjustableJobLevel.RowId
|
||||||
|
);
|
||||||
|
resolvedLevelTableRow = LuminaSheets
|
||||||
|
.GathererCrafterLvAdjustTableSheet.GetRow(AdjustedJobLevel.Value)
|
||||||
|
.RecipeLevel.RowId;
|
||||||
}
|
}
|
||||||
Table = LuminaSheets.RecipeLevelTableSheet.GetRow(resolvedLevelTableRow);
|
Table = LuminaSheets.RecipeLevelTableSheet.GetRow(resolvedLevelTableRow);
|
||||||
|
|
||||||
@@ -46,8 +52,14 @@ public sealed record RecipeData
|
|||||||
IsExpert = Recipe.IsExpert,
|
IsExpert = Recipe.IsExpert,
|
||||||
ClassJobLevel = Table.ClassJobLevel,
|
ClassJobLevel = Table.ClassJobLevel,
|
||||||
ConditionsFlag = Table.ConditionsFlag,
|
ConditionsFlag = Table.ConditionsFlag,
|
||||||
MaxDurability = (Recipe.MaxAdjustableJobLevel.RowId != 0 ? 80 : Table.Durability) * Recipe.DurabilityFactor / 100,
|
MaxDurability =
|
||||||
MaxQuality = (Recipe.CanHq || Recipe.RequiredQuality > 0) ? (int)Table.Quality * Recipe.QualityFactor / 100 : 0,
|
(Recipe.MaxAdjustableJobLevel.RowId != 0 ? 80 : Table.Durability)
|
||||||
|
* Recipe.DurabilityFactor
|
||||||
|
/ 100,
|
||||||
|
MaxQuality =
|
||||||
|
(Recipe.CanHq || Recipe.RequiredQuality > 0)
|
||||||
|
? (int)Table.Quality * Recipe.QualityFactor / 100
|
||||||
|
: 0,
|
||||||
MaxProgress = Table.Difficulty * Recipe.DifficultyFactor / 100,
|
MaxProgress = Table.Difficulty * Recipe.DifficultyFactor / 100,
|
||||||
QualityModifier = Table.QualityModifier,
|
QualityModifier = Table.QualityModifier,
|
||||||
QualityDivider = Table.QualityDivider,
|
QualityDivider = Table.QualityDivider,
|
||||||
@@ -64,23 +76,37 @@ public sealed record RecipeData
|
|||||||
{
|
{
|
||||||
if (entry.ItemTradeIn.RowId == Recipe.ItemResult.RowId)
|
if (entry.ItemTradeIn.RowId == Recipe.ItemResult.RowId)
|
||||||
{
|
{
|
||||||
thresholds = [entry.BaseCollectableRating, entry.MidCollectableRating, entry.HighCollectableRating];
|
thresholds =
|
||||||
|
[
|
||||||
|
entry.BaseCollectableRating,
|
||||||
|
entry.MidCollectableRating,
|
||||||
|
entry.HighCollectableRating,
|
||||||
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Recipe.CollectableMetadata.GetValueOrDefaultSubrow<SatisfactionSupply>() is { } row3)
|
else if (
|
||||||
|
Recipe.CollectableMetadata.GetValueOrDefaultSubrow<SatisfactionSupply>() is { } row3
|
||||||
|
)
|
||||||
{
|
{
|
||||||
foreach (var subrow in row3)
|
foreach (var subrow in row3)
|
||||||
{
|
{
|
||||||
if (subrow.Item.RowId == Recipe.ItemResult.RowId)
|
if (subrow.Item.RowId == Recipe.ItemResult.RowId)
|
||||||
{
|
{
|
||||||
thresholds = [subrow.CollectabilityLow, subrow.CollectabilityMid, subrow.CollectabilityHigh];
|
thresholds =
|
||||||
|
[
|
||||||
|
subrow.CollectabilityLow,
|
||||||
|
subrow.CollectabilityMid,
|
||||||
|
subrow.CollectabilityHigh,
|
||||||
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Recipe.CollectableMetadata.GetValueOrDefault<SharlayanCraftWorksSupply>() is { } row5)
|
else if (
|
||||||
|
Recipe.CollectableMetadata.GetValueOrDefault<SharlayanCraftWorksSupply>() is { } row5
|
||||||
|
)
|
||||||
{
|
{
|
||||||
foreach (var item in row5.Item)
|
foreach (var item in row5.Item)
|
||||||
{
|
{
|
||||||
@@ -93,10 +119,19 @@ public sealed record RecipeData
|
|||||||
}
|
}
|
||||||
else if (Recipe.CollectableMetadata.GetValueOrDefault<CollectablesRefine>() is { } row6)
|
else if (Recipe.CollectableMetadata.GetValueOrDefault<CollectablesRefine>() is { } row6)
|
||||||
thresholds = [row6.CollectabilityLow, row6.CollectabilityMid, row6.CollectabilityHigh];
|
thresholds = [row6.CollectabilityLow, row6.CollectabilityMid, row6.CollectabilityHigh];
|
||||||
else if (Recipe.CollectableMetadataKey == 7 && LuminaSheets.WKSMissionToDoEvalutionRefinSheet.TryGetRow(Recipe.CollectableMetadata.RowId, out var row7))
|
else if (
|
||||||
|
Recipe.CollectableMetadataKey == 7
|
||||||
|
&& LuminaSheets.WKSMissionToDoEvalutionRefinSheet.TryGetRow(
|
||||||
|
Recipe.CollectableMetadata.RowId,
|
||||||
|
out var row7
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
thresholds = [row7.Unknown0, row7.Unknown1, row7.Unknown2];
|
thresholds = [row7.Unknown0, row7.Unknown1, row7.Unknown2];
|
||||||
thresholds = [.. thresholds.Select(percentage => RecipeInfo.MaxQuality * percentage / 1000)];
|
thresholds =
|
||||||
|
[
|
||||||
|
.. thresholds.Select(percentage => RecipeInfo.MaxQuality * percentage / 1000),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thresholds != null)
|
if (thresholds != null)
|
||||||
@@ -106,14 +141,17 @@ public sealed record RecipeData
|
|||||||
CollectableThresholds = t.ToArray();
|
CollectableThresholds = t.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ingredients = Recipe.Ingredient.Zip(Recipe.AmountIngredient)
|
Ingredients = Recipe
|
||||||
|
.Ingredient.Zip(Recipe.AmountIngredient)
|
||||||
.Take(6)
|
.Take(6)
|
||||||
.Where(i => i.First.IsValid)
|
.Where(i => i.First.IsValid)
|
||||||
.Select(i => (i.First.Value, (int)i.Second))
|
.Select(i => (i.First.Value, (int)i.Second))
|
||||||
.ToList();
|
.ToList();
|
||||||
MaxStartingQuality = (int)Math.Floor(Recipe.MaterialQualityFactor * RecipeInfo.MaxQuality / 100f);
|
MaxStartingQuality = (int)
|
||||||
|
Math.Floor(Recipe.MaterialQualityFactor * RecipeInfo.MaxQuality / 100f);
|
||||||
|
|
||||||
TotalHqILvls = (int)Ingredients.Where(i => i.Item.CanBeHq).Sum(i => i.Item.LevelItem.RowId * i.Amount);
|
TotalHqILvls = (int)
|
||||||
|
Ingredients.Where(i => i.Item.CanBeHq).Sum(i => i.Item.LevelItem.RowId * i.Amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateItemStartingQuality(int itemIdx, int amount)
|
public int CalculateItemStartingQuality(int itemIdx, int amount)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Craftimizer.Plugin;
|
using Craftimizer.Plugin;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
using DotNext.Collections.Generic;
|
using DotNext.Collections.Generic;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Sim = Craftimizer.Simulator.Simulator;
|
using Sim = Craftimizer.Simulator.Simulator;
|
||||||
using SimNoRandom = Craftimizer.Simulator.SimulatorNoRandom;
|
using SimNoRandom = Craftimizer.Simulator.SimulatorNoRandom;
|
||||||
|
|
||||||
@@ -51,11 +51,11 @@ internal sealed class SimulatedMacro
|
|||||||
Average = (float)DataList.Average();
|
Average = (float)DataList.Average();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImGuiUtils.ViolinData? GetViolinData(float barMax, int resolution, double bandwidth) =>
|
public ImGuiUtils.ViolinData? GetViolinData(
|
||||||
ViolinData ??=
|
float barMax,
|
||||||
Min != Max ?
|
int resolution,
|
||||||
new(DataList, 0, barMax, resolution, bandwidth) :
|
double bandwidth
|
||||||
null;
|
) => ViolinData ??= Min != Max ? new(DataList, 0, barMax, resolution, bandwidth) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Param Progress = new();
|
public readonly Param Progress = new();
|
||||||
@@ -64,7 +64,12 @@ internal sealed class SimulatedMacro
|
|||||||
// Param is either collectability, quality, or hq%, depending on the recipe
|
// Param is either collectability, quality, or hq%, depending on the recipe
|
||||||
public readonly Param ParamScore = new();
|
public readonly Param ParamScore = new();
|
||||||
|
|
||||||
public Reliablity(in SimulationState startState, IEnumerable<ActionType> actions, int iterCount, RecipeData recipeData)
|
public Reliablity(
|
||||||
|
in SimulationState startState,
|
||||||
|
IEnumerable<ActionType> actions,
|
||||||
|
int iterCount,
|
||||||
|
RecipeData recipeData
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Func<SimulationState, int> getParam;
|
Func<SimulationState, int> getParam;
|
||||||
if (recipeData.IsCollectable)
|
if (recipeData.IsCollectable)
|
||||||
@@ -97,12 +102,18 @@ internal sealed class SimulatedMacro
|
|||||||
{
|
{
|
||||||
public ActionType Action { get; }
|
public ActionType Action { get; }
|
||||||
public bool IsEphemeral { get; }
|
public bool IsEphemeral { get; }
|
||||||
|
|
||||||
// State *after* executing the action
|
// State *after* executing the action
|
||||||
public ActionResponse Response { get; private set; }
|
public ActionResponse Response { get; private set; }
|
||||||
public SimulationState State { get; private set; }
|
public SimulationState State { get; private set; }
|
||||||
private Reliablity? Reliability { get; set; }
|
private Reliablity? Reliability { get; set; }
|
||||||
|
|
||||||
public Step(ActionType action, Sim sim, in SimulationState lastState, out SimulationState newState)
|
public Step(
|
||||||
|
ActionType action,
|
||||||
|
Sim sim,
|
||||||
|
in SimulationState lastState,
|
||||||
|
out SimulationState newState
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Action = action;
|
Action = action;
|
||||||
newState = Recalculate(sim, lastState);
|
newState = Recalculate(sim, lastState);
|
||||||
@@ -122,9 +133,17 @@ internal sealed class SimulatedMacro
|
|||||||
return State;
|
return State;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reliablity GetReliability(in SimulationState initialState, IEnumerable<ActionType> actionSet, RecipeData recipeData) =>
|
public Reliablity GetReliability(
|
||||||
Reliability ??=
|
in SimulationState initialState,
|
||||||
new(initialState, actionSet, Service.Configuration.ReliabilitySimulationCount, recipeData);
|
IEnumerable<ActionType> actionSet,
|
||||||
|
RecipeData recipeData
|
||||||
|
) =>
|
||||||
|
Reliability ??= new(
|
||||||
|
initialState,
|
||||||
|
actionSet,
|
||||||
|
Service.Configuration.ReliabilitySimulationCount,
|
||||||
|
recipeData
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private List<Step> Macro { get; set; } = [];
|
private List<Step> Macro { get; set; } = [];
|
||||||
@@ -162,9 +181,9 @@ internal sealed class SimulatedMacro
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Reliablity GetReliability(RecipeData recipeData, Index? idx = null) =>
|
public Reliablity GetReliability(RecipeData recipeData, Index? idx = null) =>
|
||||||
Macro.Count > 0 ?
|
Macro.Count > 0
|
||||||
Macro[idx ?? ^1].GetReliability(InitialState, Actions.ToArray(), recipeData) :
|
? Macro[idx ?? ^1].GetReliability(InitialState, Actions.ToArray(), recipeData)
|
||||||
new(InitialState, Array.Empty<ActionType>(), 0, recipeData);
|
: new(InitialState, Array.Empty<ActionType>(), 0, recipeData);
|
||||||
|
|
||||||
private void TryRecalculateFrom(int index)
|
private void TryRecalculateFrom(int index)
|
||||||
{
|
{
|
||||||
@@ -177,14 +196,11 @@ internal sealed class SimulatedMacro
|
|||||||
state = Macro[i].Recalculate(sim, state);
|
state = Macro[i].Recalculate(sim, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RecalculateState() =>
|
public void RecalculateState() => TryRecalculateFrom(0);
|
||||||
TryRecalculateFrom(0);
|
|
||||||
|
|
||||||
public void RemoveRange(int index, int count) =>
|
public void RemoveRange(int index, int count) => Macro.RemoveRange(index, count);
|
||||||
Macro.RemoveRange(index, count);
|
|
||||||
|
|
||||||
public void Clear() =>
|
public void Clear() => Macro.Clear();
|
||||||
Macro.Clear();
|
|
||||||
|
|
||||||
public void Add(ActionType action)
|
public void Add(ActionType action)
|
||||||
{
|
{
|
||||||
@@ -247,7 +263,10 @@ internal sealed class SimulatedMacro
|
|||||||
QueuedEphemeralSteps.Clear();
|
QueuedEphemeralSteps.Clear();
|
||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
{
|
{
|
||||||
if (maxSize is { } size && QueuedSteps.Count + QueuedEphemeralSteps.Count + Macro.Count >= size)
|
if (
|
||||||
|
maxSize is { } size
|
||||||
|
&& QueuedSteps.Count + QueuedEphemeralSteps.Count + Macro.Count >= size
|
||||||
|
)
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
QueuedEphemeralSteps.Add(new(action, true));
|
QueuedEphemeralSteps.Add(new(action, true));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Dalamud.Game.Text;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Frozen;
|
using System.Collections.Frozen;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
|
|
||||||
@@ -10,7 +10,10 @@ public static class SqText
|
|||||||
{
|
{
|
||||||
public static SeIconChar LevelPrefix => SeIconChar.LevelEn;
|
public static SeIconChar LevelPrefix => SeIconChar.LevelEn;
|
||||||
|
|
||||||
public static readonly FrozenDictionary<char, SeIconChar> LevelNumReplacements = new Dictionary<char, SeIconChar>
|
public static readonly FrozenDictionary<char, SeIconChar> LevelNumReplacements = new Dictionary<
|
||||||
|
char,
|
||||||
|
SeIconChar
|
||||||
|
>
|
||||||
{
|
{
|
||||||
['0'] = SeIconChar.Number0,
|
['0'] = SeIconChar.Number0,
|
||||||
['1'] = SeIconChar.Number1,
|
['1'] = SeIconChar.Number1,
|
||||||
@@ -24,7 +27,8 @@ public static class SqText
|
|||||||
['9'] = SeIconChar.Number9,
|
['9'] = SeIconChar.Number9,
|
||||||
}.ToFrozenDictionary();
|
}.ToFrozenDictionary();
|
||||||
|
|
||||||
public static string ToLevelString<T>(T value) where T : IBinaryInteger<T>
|
public static string ToLevelString<T>(T value)
|
||||||
|
where T : IBinaryInteger<T>
|
||||||
{
|
{
|
||||||
var str = value.ToString() ?? throw new FormatException("Failed to format value");
|
var str = value.ToString() ?? throw new FormatException("Failed to format value");
|
||||||
foreach (var (k, v) in LevelNumReplacements)
|
foreach (var (k, v) in LevelNumReplacements)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
using System;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using System;
|
|
||||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
||||||
|
|
||||||
namespace Craftimizer.Utils;
|
namespace Craftimizer.Utils;
|
||||||
@@ -12,7 +12,8 @@ internal sealed unsafe class SynthesisValues(AddonSynthesis* addon)
|
|||||||
{
|
{
|
||||||
private AddonSynthesis* Addon { get; } = addon;
|
private AddonSynthesis* Addon { get; } = addon;
|
||||||
|
|
||||||
private ReadOnlySpan<AtkValue> Values => new(Addon->AtkUnitBase.AtkValues, Addon->AtkUnitBase.AtkValuesCount);
|
private ReadOnlySpan<AtkValue> Values =>
|
||||||
|
new(Addon->AtkUnitBase.AtkValues, Addon->AtkUnitBase.AtkValuesCount);
|
||||||
|
|
||||||
// Always 0?
|
// Always 0?
|
||||||
private uint IsInitializing => GetUInt(0);
|
private uint IsInitializing => GetUInt(0);
|
||||||
@@ -51,9 +52,7 @@ internal sealed unsafe class SynthesisValues(AddonSynthesis* addon)
|
|||||||
if (Addon == null)
|
if (Addon == null)
|
||||||
return null;
|
return null;
|
||||||
var value = Values[i];
|
var value = Values[i];
|
||||||
return value.Type == ValueType.UInt ?
|
return value.Type == ValueType.UInt ? value.UInt : null;
|
||||||
value.UInt :
|
|
||||||
null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool? TryGetBool(int i)
|
private bool? TryGetBool(int i)
|
||||||
@@ -61,9 +60,7 @@ internal sealed unsafe class SynthesisValues(AddonSynthesis* addon)
|
|||||||
if (Addon == null)
|
if (Addon == null)
|
||||||
return null;
|
return null;
|
||||||
var value = Values[i];
|
var value = Values[i];
|
||||||
return value.Type == ValueType.Bool ?
|
return value.Type == ValueType.Bool ? value.Byte != 0 : null;
|
||||||
value.Byte != 0 :
|
|
||||||
null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SeString? TryGetString(int i)
|
private SeString? TryGetString(int i)
|
||||||
@@ -73,10 +70,10 @@ internal sealed unsafe class SynthesisValues(AddonSynthesis* addon)
|
|||||||
var value = Values[i];
|
var value = Values[i];
|
||||||
return value.Type switch
|
return value.Type switch
|
||||||
{
|
{
|
||||||
ValueType.ManagedString or
|
ValueType.ManagedString or ValueType.String => MemoryHelper.ReadSeStringNullTerminated(
|
||||||
ValueType.String =>
|
(nint)value.String.Value
|
||||||
MemoryHelper.ReadSeStringNullTerminated((nint)value.String.Value),
|
),
|
||||||
_ => null
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
|
||||||
namespace Craftimizer.Windows;
|
namespace Craftimizer.Windows;
|
||||||
|
|
||||||
@@ -17,7 +17,8 @@ public sealed class MacroClipboard : Window, IDisposable
|
|||||||
|
|
||||||
private List<string> Macros { get; }
|
private List<string> Macros { get; }
|
||||||
|
|
||||||
public MacroClipboard(IEnumerable<string> macros) : base("Macro Clipboard", WindowFlags)
|
public MacroClipboard(IEnumerable<string> macros)
|
||||||
|
: base("Macro Clipboard", WindowFlags)
|
||||||
{
|
{
|
||||||
Macros = [.. macros];
|
Macros = [.. macros];
|
||||||
|
|
||||||
@@ -39,32 +40,49 @@ public sealed class MacroClipboard : Window, IDisposable
|
|||||||
private void DrawMacro(int idx, string macro)
|
private void DrawMacro(int idx, string macro)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(idx);
|
using var id = ImRaii.PushId(idx);
|
||||||
using var panel = ImRaii2.GroupPanel(Macros.Count == 1 ? "Macro" : $"Macro {idx + 1}", -1, out var availWidth);
|
using var panel = ImRaii2.GroupPanel(
|
||||||
|
Macros.Count == 1 ? "Macro" : $"Macro {idx + 1}",
|
||||||
|
-1,
|
||||||
|
out var availWidth
|
||||||
|
);
|
||||||
|
|
||||||
var cursor = ImGui.GetCursorPos();
|
var cursor = ImGui.GetCursorPos();
|
||||||
|
|
||||||
ImGuiUtils.AlignRight(ImGui.GetFrameHeight(), availWidth);
|
ImGuiUtils.AlignRight(ImGui.GetFrameHeight(), availWidth);
|
||||||
var buttonCursor = ImGui.GetCursorPos();
|
var buttonCursor = ImGui.GetCursorPos();
|
||||||
ImGui.InvisibleButton("##copyInvButton", new(ImGui.GetFrameHeight()));
|
ImGui.InvisibleButton("##copyInvButton", new(ImGui.GetFrameHeight()));
|
||||||
var buttonHovered = ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenOverlapped | ImGuiHoveredFlags.AllowWhenBlockedByActiveItem);
|
var buttonHovered = ImGui.IsItemHovered(
|
||||||
|
ImGuiHoveredFlags.AllowWhenOverlapped | ImGuiHoveredFlags.AllowWhenBlockedByActiveItem
|
||||||
|
);
|
||||||
var buttonActive = buttonHovered && ImGui.GetIO().MouseDown[(int)ImGuiMouseButton.Left];
|
var buttonActive = buttonHovered && ImGui.GetIO().MouseDown[(int)ImGuiMouseButton.Left];
|
||||||
var buttonClicked = buttonHovered && ImGui.GetIO().MouseReleased[(int)ImGuiMouseButton.Left];
|
var buttonClicked =
|
||||||
|
buttonHovered && ImGui.GetIO().MouseReleased[(int)ImGuiMouseButton.Left];
|
||||||
ImGui.SetCursorPos(buttonCursor);
|
ImGui.SetCursorPos(buttonCursor);
|
||||||
{
|
{
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(buttonActive ? ImGuiCol.ButtonActive : ImGuiCol.ButtonHovered), buttonHovered);
|
using var color = ImRaii.PushColor(
|
||||||
|
ImGuiCol.Button,
|
||||||
|
ImGui.GetColorU32(buttonActive ? ImGuiCol.ButtonActive : ImGuiCol.ButtonHovered),
|
||||||
|
buttonHovered
|
||||||
|
);
|
||||||
ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste);
|
ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste);
|
||||||
if (buttonClicked)
|
if (buttonClicked)
|
||||||
{
|
{
|
||||||
ImGui.SetClipboardText(macro);
|
ImGui.SetClipboardText(macro);
|
||||||
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
|
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
|
||||||
{
|
{
|
||||||
Plugin.Plugin.DisplayNotification(new()
|
Plugin.Plugin.DisplayNotification(
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
Content = Macros.Count == 1 ? "Copied macro to clipboard." : $"Copied macro {idx + 1} to clipboard.",
|
Content =
|
||||||
MinimizedText = Macros.Count == 1 ? "Copied macro" : $"Copied macro {idx + 1}",
|
Macros.Count == 1
|
||||||
|
? "Copied macro to clipboard."
|
||||||
|
: $"Copied macro {idx + 1} to clipboard.",
|
||||||
|
MinimizedText =
|
||||||
|
Macros.Count == 1 ? "Copied macro" : $"Copied macro {idx + 1}",
|
||||||
Title = "Macro Copied",
|
Title = "Macro Copied",
|
||||||
Type = NotificationType.Success
|
Type = NotificationType.Success,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +95,17 @@ public sealed class MacroClipboard : Window, IDisposable
|
|||||||
using var padding = ImRaii.PushStyle(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
using var padding = ImRaii.PushStyle(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
||||||
using var bg = ImRaii.PushColor(ImGuiCol.FrameBg, Vector4.Zero);
|
using var bg = ImRaii.PushColor(ImGuiCol.FrameBg, Vector4.Zero);
|
||||||
var lineCount = macro.Count(c => c == '\n') + 1;
|
var lineCount = macro.Count(c => c == '\n') + 1;
|
||||||
ImGui.InputTextMultiline("", ref macro, macro.Length + 1, new(availWidth, ImGui.GetTextLineHeight() * Math.Max(15, lineCount) + ImGui.GetStyle().FramePadding.Y), ImGuiInputTextFlags.ReadOnly | ImGuiInputTextFlags.AutoSelectAll);
|
ImGui.InputTextMultiline(
|
||||||
|
"",
|
||||||
|
ref macro,
|
||||||
|
macro.Length + 1,
|
||||||
|
new(
|
||||||
|
availWidth,
|
||||||
|
ImGui.GetTextLineHeight() * Math.Max(15, lineCount)
|
||||||
|
+ ImGui.GetStyle().FramePadding.Y
|
||||||
|
),
|
||||||
|
ImGuiInputTextFlags.ReadOnly | ImGuiInputTextFlags.AutoSelectAll
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonHovered)
|
if (buttonHovered)
|
||||||
|
|||||||
+696
-185
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
|||||||
using Craftimizer.Plugin;
|
|
||||||
using Craftimizer.Utils;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
using System;
|
||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Sim = Craftimizer.Simulator.SimulatorNoRandom;
|
using Craftimizer.Plugin;
|
||||||
|
using Craftimizer.Simulator;
|
||||||
|
using Craftimizer.Simulator.Actions;
|
||||||
|
using Craftimizer.Utils;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
using Sim = Craftimizer.Simulator.SimulatorNoRandom;
|
||||||
|
|
||||||
namespace Craftimizer.Windows;
|
namespace Craftimizer.Windows;
|
||||||
|
|
||||||
@@ -26,7 +26,8 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
private static IReadOnlyList<Macro> Macros => Service.Configuration.Macros;
|
private static IReadOnlyList<Macro> Macros => Service.Configuration.Macros;
|
||||||
private Dictionary<Macro, SimulationState> MacroStateCache { get; } = [];
|
private Dictionary<Macro, SimulationState> MacroStateCache { get; } = [];
|
||||||
|
|
||||||
public MacroList() : base("Craftimizer Macro List", WindowFlags, false)
|
public MacroList()
|
||||||
|
: base("Craftimizer Macro List", WindowFlags, false)
|
||||||
{
|
{
|
||||||
RefreshSearch();
|
RefreshSearch();
|
||||||
|
|
||||||
@@ -36,7 +37,11 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
CollapsedCondition = ImGuiCond.Appearing;
|
CollapsedCondition = ImGuiCond.Appearing;
|
||||||
Collapsed = false;
|
Collapsed = false;
|
||||||
|
|
||||||
SizeConstraints = new() { MinimumSize = new(465, 520), MaximumSize = new(float.PositiveInfinity) };
|
SizeConstraints = new()
|
||||||
|
{
|
||||||
|
MinimumSize = new(465, 520),
|
||||||
|
MaximumSize = new(float.PositiveInfinity),
|
||||||
|
};
|
||||||
|
|
||||||
TitleBarButtons =
|
TitleBarButtons =
|
||||||
[
|
[
|
||||||
@@ -45,14 +50,15 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
Icon = FontAwesomeIcon.Cog,
|
Icon = FontAwesomeIcon.Cog,
|
||||||
IconOffset = new(2, 1),
|
IconOffset = new(2, 1),
|
||||||
Click = _ => Service.Plugin.OpenSettingsTab("General"),
|
Click = _ => Service.Plugin.OpenSettingsTab("General"),
|
||||||
ShowTooltip = () => ImGuiUtils.Tooltip("Open Settings")
|
ShowTooltip = () => ImGuiUtils.Tooltip("Open Settings"),
|
||||||
},
|
},
|
||||||
new() {
|
new()
|
||||||
|
{
|
||||||
Icon = FontAwesomeIcon.Heart,
|
Icon = FontAwesomeIcon.Heart,
|
||||||
IconOffset = new(2, 1),
|
IconOffset = new(2, 1),
|
||||||
Click = _ => Util.OpenLink(Plugin.Plugin.SupportLink),
|
Click = _ => Util.OpenLink(Plugin.Plugin.SupportLink),
|
||||||
ShowTooltip = () => ImGuiUtils.Tooltip("Support me on Ko-fi!")
|
ShowTooltip = () => ImGuiUtils.Tooltip("Support me on Ko-fi!"),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
Service.WindowSystem.AddWindow(this);
|
Service.WindowSystem.AddWindow(this);
|
||||||
@@ -90,7 +96,9 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
ImGui.InvisibleButton($"###macroButton{i}", ImGui.GetItemRectSize());
|
ImGui.InvisibleButton($"###macroButton{i}", ImGui.GetItemRectSize());
|
||||||
if (isUnsorted)
|
if (isUnsorted)
|
||||||
{
|
{
|
||||||
using (var _source = ImRaii.DragDropSource(ImGuiDragDropFlags.SourceNoDisableHover))
|
using (
|
||||||
|
var _source = ImRaii.DragDropSource(ImGuiDragDropFlags.SourceNoDisableHover)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (_source)
|
if (_source)
|
||||||
{
|
{
|
||||||
@@ -115,7 +123,10 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
var text2 = "the Macro Editor here or from the Crafting Log.";
|
var text2 = "the Macro Editor here or from the Crafting Log.";
|
||||||
var text3 = "Open Crafting Log";
|
var text3 = "Open Crafting Log";
|
||||||
var text4 = "Open Macro Editor";
|
var text4 = "Open Macro Editor";
|
||||||
var buttonRowWidth = ImGui.CalcTextSize(text3).X + ImGui.CalcTextSize(text4).X + ImGui.GetStyle().ItemSpacing.X * 5;
|
var buttonRowWidth =
|
||||||
|
ImGui.CalcTextSize(text3).X
|
||||||
|
+ ImGui.CalcTextSize(text4).X
|
||||||
|
+ ImGui.GetStyle().ItemSpacing.X * 5;
|
||||||
var size = new Vector2(
|
var size = new Vector2(
|
||||||
Math.Max(
|
Math.Max(
|
||||||
Math.Max(ImGui.CalcTextSize(text1).X, ImGui.CalcTextSize(text2).X),
|
Math.Max(ImGui.CalcTextSize(text1).X, ImGui.CalcTextSize(text2).X),
|
||||||
@@ -139,6 +150,7 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
private string searchText = string.Empty;
|
private string searchText = string.Empty;
|
||||||
private List<Macro> sortedMacros = null!;
|
private List<Macro> sortedMacros = null!;
|
||||||
private bool isUnsorted = true;
|
private bool isUnsorted = true;
|
||||||
|
|
||||||
private void DrawSearchBar()
|
private void DrawSearchBar()
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||||
@@ -157,12 +169,20 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
|
|
||||||
var stateNullable = GetMacroState(macro);
|
var stateNullable = GetMacroState(macro);
|
||||||
|
|
||||||
using var panel = ImRaii2.GroupPanel(macro.Name, width - ImGui.GetStyle().ItemSpacing.X * 2, out var availWidth);
|
using var panel = ImRaii2.GroupPanel(
|
||||||
|
macro.Name,
|
||||||
|
width - ImGui.GetStyle().ItemSpacing.X * 2,
|
||||||
|
out var availWidth
|
||||||
|
);
|
||||||
var stepsAvailWidthOffset = width - availWidth;
|
var stepsAvailWidthOffset = width - availWidth;
|
||||||
var spacing = ImGui.GetStyle().ItemSpacing.Y;
|
var spacing = ImGui.GetStyle().ItemSpacing.Y;
|
||||||
var miniRowHeight = (windowHeight - spacing) / 2f;
|
var miniRowHeight = (windowHeight - spacing) / 2f;
|
||||||
|
|
||||||
using var table = ImRaii.Table("table", stateNullable.HasValue ? 3 : 2, ImGuiTableFlags.BordersInnerV);
|
using var table = ImRaii.Table(
|
||||||
|
"table",
|
||||||
|
stateNullable.HasValue ? 3 : 2,
|
||||||
|
ImGuiTableFlags.BordersInnerV
|
||||||
|
);
|
||||||
if (table)
|
if (table)
|
||||||
{
|
{
|
||||||
if (stateNullable.HasValue)
|
if (stateNullable.HasValue)
|
||||||
@@ -177,16 +197,22 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
if (Service.Configuration.ShowOptimalMacroStat)
|
if (Service.Configuration.ShowOptimalMacroStat)
|
||||||
{
|
{
|
||||||
var progressHeight = windowHeight;
|
var progressHeight = windowHeight;
|
||||||
if (state.Progress >= state.Input.Recipe.MaxProgress && state.Input.Recipe.MaxQuality > 0)
|
if (
|
||||||
|
state.Progress >= state.Input.Recipe.MaxProgress
|
||||||
|
&& state.Input.Recipe.MaxQuality > 0
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ImGuiUtils.ArcProgress(
|
ImGuiUtils.ArcProgress(
|
||||||
(float)state.Quality / state.Input.Recipe.MaxQuality,
|
(float)state.Quality / state.Input.Recipe.MaxQuality,
|
||||||
progressHeight / 2f,
|
progressHeight / 2f,
|
||||||
.5f,
|
.5f,
|
||||||
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
||||||
ImGui.GetColorU32(Colors.Quality));
|
ImGui.GetColorU32(Colors.Quality)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"Quality: {state.Quality} / {state.Input.Recipe.MaxQuality}");
|
ImGuiUtils.Tooltip(
|
||||||
|
$"Quality: {state.Quality} / {state.Input.Recipe.MaxQuality}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -195,9 +221,12 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
progressHeight / 2f,
|
progressHeight / 2f,
|
||||||
.5f,
|
.5f,
|
||||||
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
||||||
ImGui.GetColorU32(Colors.Progress));
|
ImGui.GetColorU32(Colors.Progress)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"Progress: {state.Progress} / {state.Input.Recipe.MaxProgress}");
|
ImGuiUtils.Tooltip(
|
||||||
|
$"Progress: {state.Progress} / {state.Input.Recipe.MaxProgress}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -207,9 +236,12 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
miniRowHeight / 2f,
|
miniRowHeight / 2f,
|
||||||
.5f,
|
.5f,
|
||||||
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
||||||
ImGui.GetColorU32(Colors.Progress));
|
ImGui.GetColorU32(Colors.Progress)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"Progress: {state.Progress} / {state.Input.Recipe.MaxProgress}");
|
ImGuiUtils.Tooltip(
|
||||||
|
$"Progress: {state.Progress} / {state.Input.Recipe.MaxProgress}"
|
||||||
|
);
|
||||||
|
|
||||||
ImGui.SameLine(0, spacing);
|
ImGui.SameLine(0, spacing);
|
||||||
ImGuiUtils.ArcProgress(
|
ImGuiUtils.ArcProgress(
|
||||||
@@ -217,17 +249,24 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
miniRowHeight / 2f,
|
miniRowHeight / 2f,
|
||||||
.5f,
|
.5f,
|
||||||
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
||||||
ImGui.GetColorU32(Colors.Quality));
|
ImGui.GetColorU32(Colors.Quality)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"Quality: {state.Quality} / {state.Input.Recipe.MaxQuality}");
|
ImGuiUtils.Tooltip(
|
||||||
|
$"Quality: {state.Quality} / {state.Input.Recipe.MaxQuality}"
|
||||||
|
);
|
||||||
|
|
||||||
ImGuiUtils.ArcProgress((float)state.Durability / state.Input.Recipe.MaxDurability,
|
ImGuiUtils.ArcProgress(
|
||||||
|
(float)state.Durability / state.Input.Recipe.MaxDurability,
|
||||||
miniRowHeight / 2f,
|
miniRowHeight / 2f,
|
||||||
.5f,
|
.5f,
|
||||||
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
||||||
ImGui.GetColorU32(Colors.Durability));
|
ImGui.GetColorU32(Colors.Durability)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"Remaining Durability: {state.Durability} / {state.Input.Recipe.MaxDurability}");
|
ImGuiUtils.Tooltip(
|
||||||
|
$"Remaining Durability: {state.Durability} / {state.Input.Recipe.MaxDurability}"
|
||||||
|
);
|
||||||
|
|
||||||
ImGui.SameLine(0, spacing);
|
ImGui.SameLine(0, spacing);
|
||||||
ImGuiUtils.ArcProgress(
|
ImGuiUtils.ArcProgress(
|
||||||
@@ -235,7 +274,8 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
miniRowHeight / 2f,
|
miniRowHeight / 2f,
|
||||||
.5f,
|
.5f,
|
||||||
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
|
||||||
ImGui.GetColorU32(Colors.CP));
|
ImGui.GetColorU32(Colors.CP)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"Remaining CP: {state.CP} / {state.Input.Stats.CP}");
|
ImGuiUtils.Tooltip($"Remaining CP: {state.CP} / {state.Input.Stats.CP}");
|
||||||
}
|
}
|
||||||
@@ -270,7 +310,11 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
{
|
{
|
||||||
var itemsPerRow = (int)MathF.Floor((ImGui.GetContentRegionAvail().X - stepsAvailWidthOffset + spacing * 2) / (miniRowHeight + spacing));
|
var itemsPerRow = (int)
|
||||||
|
MathF.Floor(
|
||||||
|
(ImGui.GetContentRegionAvail().X - stepsAvailWidthOffset + spacing * 2)
|
||||||
|
/ (miniRowHeight + spacing)
|
||||||
|
);
|
||||||
var itemCount = macro.Actions.Count;
|
var itemCount = macro.Actions.Count;
|
||||||
for (var i = 0; i < itemsPerRow * 2; i++)
|
for (var i = 0; i < itemsPerRow * 2; i++)
|
||||||
{
|
{
|
||||||
@@ -281,7 +325,10 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
var shouldShowMore = i + 1 == itemsPerRow * 2 && i + 1 < itemCount;
|
var shouldShowMore = i + 1 == itemsPerRow * 2 && i + 1 < itemCount;
|
||||||
if (!shouldShowMore)
|
if (!shouldShowMore)
|
||||||
{
|
{
|
||||||
ImGui.Image(macro.Actions[i].GetIcon(RecipeData!.ClassJob).Handle, new(miniRowHeight));
|
ImGui.Image(
|
||||||
|
macro.Actions[i].GetIcon(RecipeData!.ClassJob).Handle,
|
||||||
|
new(miniRowHeight)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip(macro.Actions[i].GetName(RecipeData!.ClassJob));
|
ImGuiUtils.Tooltip(macro.Actions[i].GetName(RecipeData!.ClassJob));
|
||||||
}
|
}
|
||||||
@@ -289,12 +336,36 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
{
|
{
|
||||||
var amtMore = itemCount - itemsPerRow * 2;
|
var amtMore = itemCount - itemsPerRow * 2;
|
||||||
var pos = ImGui.GetCursorPos();
|
var pos = ImGui.GetCursorPos();
|
||||||
ImGui.Image(macro.Actions[i].GetIcon(RecipeData!.ClassJob).Handle, new(miniRowHeight), default, Vector2.One, new(1, 1, 1, .5f));
|
ImGui.Image(
|
||||||
|
macro.Actions[i].GetIcon(RecipeData!.ClassJob).Handle,
|
||||||
|
new(miniRowHeight),
|
||||||
|
default,
|
||||||
|
Vector2.One,
|
||||||
|
new(1, 1, 1, .5f)
|
||||||
|
);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.Tooltip($"{macro.Actions[i].GetName(RecipeData!.ClassJob)}\nand {amtMore} more");
|
ImGuiUtils.Tooltip(
|
||||||
|
$"{macro.Actions[i].GetName(RecipeData!.ClassJob)}\nand {amtMore} more"
|
||||||
|
);
|
||||||
ImGui.SetCursorPos(pos);
|
ImGui.SetCursorPos(pos);
|
||||||
ImGui.GetWindowDrawList().AddRectFilled(ImGui.GetCursorScreenPos(), ImGui.GetCursorScreenPos() + new Vector2(miniRowHeight), ImGui.GetColorU32(ImGuiCol.FrameBg), miniRowHeight / 8f);
|
ImGui
|
||||||
ImGui.GetWindowDrawList().AddTextClippedEx(ImGui.GetCursorScreenPos(), ImGui.GetCursorScreenPos() + new Vector2(miniRowHeight), $"+{amtMore}", null, new(.5f), null);
|
.GetWindowDrawList()
|
||||||
|
.AddRectFilled(
|
||||||
|
ImGui.GetCursorScreenPos(),
|
||||||
|
ImGui.GetCursorScreenPos() + new Vector2(miniRowHeight),
|
||||||
|
ImGui.GetColorU32(ImGuiCol.FrameBg),
|
||||||
|
miniRowHeight / 8f
|
||||||
|
);
|
||||||
|
ImGui
|
||||||
|
.GetWindowDrawList()
|
||||||
|
.AddTextClippedEx(
|
||||||
|
ImGui.GetCursorScreenPos(),
|
||||||
|
ImGui.GetCursorScreenPos() + new Vector2(miniRowHeight),
|
||||||
|
$"+{amtMore}",
|
||||||
|
null,
|
||||||
|
new(.5f),
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -306,12 +377,19 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
|
|
||||||
private string popupMacroName = string.Empty;
|
private string popupMacroName = string.Empty;
|
||||||
private Macro? popupMacro;
|
private Macro? popupMacro;
|
||||||
|
|
||||||
private void ShowRenamePopup(Macro macro)
|
private void ShowRenamePopup(Macro macro)
|
||||||
{
|
{
|
||||||
ImGui.OpenPopup($"##renamePopup-{macro.GetHashCode()}");
|
ImGui.OpenPopup($"##renamePopup-{macro.GetHashCode()}");
|
||||||
popupMacro = macro;
|
popupMacro = macro;
|
||||||
popupMacroName = macro.Name;
|
popupMacroName = macro.Name;
|
||||||
ImGui.SetNextWindowPos(ImGui.GetMousePos() - new Vector2(ImGui.CalcItemWidth() * .25f, ImGui.GetFrameHeight() + ImGui.GetStyle().WindowPadding.Y * 2));
|
ImGui.SetNextWindowPos(
|
||||||
|
ImGui.GetMousePos()
|
||||||
|
- new Vector2(
|
||||||
|
ImGui.CalcItemWidth() * .25f,
|
||||||
|
ImGui.GetFrameHeight() + ImGui.GetStyle().WindowPadding.Y * 2
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawRenamePopup(Macro macro)
|
private void DrawRenamePopup(Macro macro)
|
||||||
@@ -322,7 +400,15 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
if (ImGui.IsWindowAppearing())
|
if (ImGui.IsWindowAppearing())
|
||||||
ImGui.SetKeyboardFocusHere();
|
ImGui.SetKeyboardFocusHere();
|
||||||
ImGui.SetNextItemWidth(ImGui.CalcItemWidth());
|
ImGui.SetNextItemWidth(ImGui.CalcItemWidth());
|
||||||
if (ImGui.InputTextWithHint($"##setName", "Name", ref popupMacroName, 100, ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.EnterReturnsTrue))
|
if (
|
||||||
|
ImGui.InputTextWithHint(
|
||||||
|
$"##setName",
|
||||||
|
"Name",
|
||||||
|
ref popupMacroName,
|
||||||
|
100,
|
||||||
|
ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.EnterReturnsTrue
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(popupMacroName))
|
if (!string.IsNullOrWhiteSpace(popupMacroName))
|
||||||
{
|
{
|
||||||
@@ -349,7 +435,9 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
}
|
}
|
||||||
isUnsorted = false;
|
isUnsorted = false;
|
||||||
var matcher = new FuzzyMatcher(searchText.ToLowerInvariant(), MatchMode.FuzzyParts);
|
var matcher = new FuzzyMatcher(searchText.ToLowerInvariant(), MatchMode.FuzzyParts);
|
||||||
var query = Macros.AsParallel().Select(i => (Item: i, Score: matcher.Matches(i.Name.ToLowerInvariant())))
|
var query = Macros
|
||||||
|
.AsParallel()
|
||||||
|
.Select(i => (Item: i, Score: matcher.Matches(i.Name.ToLowerInvariant())))
|
||||||
.Where(t => t.Score > 0)
|
.Where(t => t.Score > 0)
|
||||||
.OrderByDescending(t => t.Score)
|
.OrderByDescending(t => t.Score)
|
||||||
.Select(t => t.Item);
|
.Select(t => t.Item);
|
||||||
@@ -359,7 +447,22 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
private static void OpenEditor(Macro? macro)
|
private static void OpenEditor(Macro? macro)
|
||||||
{
|
{
|
||||||
var stats = Service.Plugin.GetDefaultStats();
|
var stats = Service.Plugin.GetDefaultStats();
|
||||||
Service.Plugin.OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, null, macro?.Actions ?? Enumerable.Empty<ActionType>(), macro != null ? (actions => { macro.ActionEnumerable = actions; Service.Configuration.Save(); }) : null);
|
Service.Plugin.OpenMacroEditor(
|
||||||
|
stats.Character,
|
||||||
|
stats.Recipe,
|
||||||
|
stats.Buffs,
|
||||||
|
null,
|
||||||
|
macro?.Actions ?? Enumerable.Empty<ActionType>(),
|
||||||
|
macro != null
|
||||||
|
? (
|
||||||
|
actions =>
|
||||||
|
{
|
||||||
|
macro.ActionEnumerable = actions;
|
||||||
|
Service.Configuration.Save();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMacroChanged(Macro macro)
|
private void OnMacroChanged(Macro macro)
|
||||||
|
|||||||
+435
-128
File diff suppressed because it is too large
Load Diff
+330
-175
@@ -1,6 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
using Craftimizer.Solver;
|
using Craftimizer.Solver;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.ManagedFontAtlas;
|
using Dalamud.Interface.ManagedFontAtlas;
|
||||||
@@ -8,12 +14,6 @@ using Dalamud.Interface.Utility;
|
|||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
namespace Craftimizer.Plugin.Windows;
|
||||||
|
|
||||||
@@ -31,17 +31,22 @@ public sealed class Settings : Window, IDisposable
|
|||||||
private IFontHandle HeaderFont { get; }
|
private IFontHandle HeaderFont { get; }
|
||||||
private IFontHandle SubheaderFont { get; }
|
private IFontHandle SubheaderFont { get; }
|
||||||
|
|
||||||
public Settings() : base("Craftimizer Settings", WindowFlags)
|
public Settings()
|
||||||
|
: base("Craftimizer Settings", WindowFlags)
|
||||||
{
|
{
|
||||||
Service.WindowSystem.AddWindow(this);
|
Service.WindowSystem.AddWindow(this);
|
||||||
|
|
||||||
HeaderFont = Service.PluginInterface.UiBuilder.FontAtlas.NewDelegateFontHandle(e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(UiBuilder.DefaultFontSizePx * 2f)));
|
HeaderFont = Service.PluginInterface.UiBuilder.FontAtlas.NewDelegateFontHandle(e =>
|
||||||
SubheaderFont = Service.PluginInterface.UiBuilder.FontAtlas.NewDelegateFontHandle(e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(UiBuilder.DefaultFontSizePx * 1.5f)));
|
e.OnPreBuild(tk => tk.AddDalamudDefaultFont(UiBuilder.DefaultFontSizePx * 2f))
|
||||||
|
);
|
||||||
|
SubheaderFont = Service.PluginInterface.UiBuilder.FontAtlas.NewDelegateFontHandle(e =>
|
||||||
|
e.OnPreBuild(tk => tk.AddDalamudDefaultFont(UiBuilder.DefaultFontSizePx * 1.5f))
|
||||||
|
);
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
MinimumSize = new(450, 400),
|
MinimumSize = new(450, 400),
|
||||||
MaximumSize = new(float.PositiveInfinity)
|
MaximumSize = new(float.PositiveInfinity),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +66,13 @@ public sealed class Settings : Window, IDisposable
|
|||||||
return ImRaii.TabItem(label);
|
return ImRaii.TabItem(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawOption(string label, string tooltip, bool val, Action<bool> setter, ref bool isDirty)
|
private static void DrawOption(
|
||||||
|
string label,
|
||||||
|
string tooltip,
|
||||||
|
bool val,
|
||||||
|
Action<bool> setter,
|
||||||
|
ref bool isDirty
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ImGui.Checkbox(label, ref val))
|
if (ImGui.Checkbox(label, ref val))
|
||||||
{
|
{
|
||||||
@@ -72,12 +83,28 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGuiUtils.TooltipWrapped(tooltip);
|
ImGuiUtils.TooltipWrapped(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawOption<T>(string label, string tooltip, T value, T min, T max, Action<T> setter, ref bool isDirty) where T : struct, INumber<T>
|
private static void DrawOption<T>(
|
||||||
|
string label,
|
||||||
|
string tooltip,
|
||||||
|
T value,
|
||||||
|
T min,
|
||||||
|
T max,
|
||||||
|
Action<T> setter,
|
||||||
|
ref bool isDirty
|
||||||
|
)
|
||||||
|
where T : struct, INumber<T>
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(OptionWidth);
|
ImGui.SetNextItemWidth(OptionWidth);
|
||||||
var text = value.ToString();
|
var text = value.ToString();
|
||||||
ArgumentNullException.ThrowIfNull(text, nameof(value));
|
ArgumentNullException.ThrowIfNull(text, nameof(value));
|
||||||
if (ImGui.InputText(label, ref text, 8, ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.CharsDecimal))
|
if (
|
||||||
|
ImGui.InputText(
|
||||||
|
label,
|
||||||
|
ref text,
|
||||||
|
8,
|
||||||
|
ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.CharsDecimal
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (T.TryParse(text, null, out var newValue))
|
if (T.TryParse(text, null, out var newValue))
|
||||||
{
|
{
|
||||||
@@ -102,7 +129,13 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGuiUtils.TooltipWrapped(tooltip);
|
ImGuiUtils.TooltipWrapped(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawOption(string label, string tooltip, string value, Action<string> setter, ref bool isDirty)
|
private static void DrawOption(
|
||||||
|
string label,
|
||||||
|
string tooltip,
|
||||||
|
string value,
|
||||||
|
Action<string> setter,
|
||||||
|
ref bool isDirty
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(OptionWidth);
|
ImGui.SetNextItemWidth(OptionWidth);
|
||||||
var text = value;
|
var text = value;
|
||||||
@@ -118,7 +151,17 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGuiUtils.TooltipWrapped(tooltip);
|
ImGuiUtils.TooltipWrapped(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawOption<T>(string label, string tooltip, Func<T, string> getName, Func<T, string> getTooltip, T value, Action<T> setter, ref bool isDirty, params T[] excludedValues) where T : struct, Enum
|
private static void DrawOption<T>(
|
||||||
|
string label,
|
||||||
|
string tooltip,
|
||||||
|
Func<T, string> getName,
|
||||||
|
Func<T, string> getTooltip,
|
||||||
|
T value,
|
||||||
|
Action<T> setter,
|
||||||
|
ref bool isDirty,
|
||||||
|
params T[] excludedValues
|
||||||
|
)
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(OptionWidth);
|
ImGui.SetNextItemWidth(OptionWidth);
|
||||||
using (var combo = ImRaii.Combo(label, getName(value)))
|
using (var combo = ImRaii.Combo(label, getName(value)))
|
||||||
@@ -160,16 +203,16 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
SolverAlgorithm.Oneshot => "Run through all iterations and pick the best macro",
|
SolverAlgorithm.Oneshot => "Run through all iterations and pick the best macro",
|
||||||
SolverAlgorithm.OneshotForked => "Oneshot, but using multiple solvers simultaneously",
|
SolverAlgorithm.OneshotForked => "Oneshot, but using multiple solvers simultaneously",
|
||||||
SolverAlgorithm.Stepwise => "Run through all iterations and pick the next best step, " +
|
SolverAlgorithm.Stepwise => "Run through all iterations and pick the next best step, "
|
||||||
"and repeat using previous steps as a starting point",
|
+ "and repeat using previous steps as a starting point",
|
||||||
SolverAlgorithm.StepwiseForked => "Stepwise, but using multiple solvers simultaneously",
|
SolverAlgorithm.StepwiseForked => "Stepwise, but using multiple solvers simultaneously",
|
||||||
SolverAlgorithm.StepwiseGenetic => "Stepwise Forked, but the top N next best steps are " +
|
SolverAlgorithm.StepwiseGenetic => "Stepwise Forked, but the top N next best steps are "
|
||||||
"selected from the solvers, and each one is equally " +
|
+ "selected from the solvers, and each one is equally "
|
||||||
"used as a starting point",
|
+ "used as a starting point",
|
||||||
SolverAlgorithm.Raphael => "Finds the best solution, every time. This solver has " +
|
SolverAlgorithm.Raphael => "Finds the best solution, every time. This solver has "
|
||||||
"very different options compared to the rest, as it " +
|
+ "very different options compared to the rest, as it "
|
||||||
"is designed using an entirely different algorithm.",
|
+ "is designed using an entirely different algorithm.",
|
||||||
_ => "Unknown"
|
_ => "Unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string GetCopyTypeName(MacroCopyConfiguration.CopyType type) =>
|
private static string GetCopyTypeName(MacroCopyConfiguration.CopyType type) =>
|
||||||
@@ -185,12 +228,16 @@ public sealed class Settings : Window, IDisposable
|
|||||||
private static string GetCopyTypeTooltip(MacroCopyConfiguration.CopyType type) =>
|
private static string GetCopyTypeTooltip(MacroCopyConfiguration.CopyType type) =>
|
||||||
type switch
|
type switch
|
||||||
{
|
{
|
||||||
MacroCopyConfiguration.CopyType.OpenWindow => "Open a dedicated window with all macros being copied. " +
|
MacroCopyConfiguration.CopyType.OpenWindow =>
|
||||||
"Copy, view, and choose at your own leisure.",
|
"Open a dedicated window with all macros being copied. "
|
||||||
MacroCopyConfiguration.CopyType.CopyToMacro => "Copy directly to the game's macro system.",
|
+ "Copy, view, and choose at your own leisure.",
|
||||||
MacroCopyConfiguration.CopyType.CopyToClipboard => "Copy to your clipboard. Macros are separated by a blank line.",
|
MacroCopyConfiguration.CopyType.CopyToMacro =>
|
||||||
MacroCopyConfiguration.CopyType.CopyToMacroMate => "Copy directly to a Macro Mate macro. Requires the Macro Mate plugin.",
|
"Copy directly to the game's macro system.",
|
||||||
_ => "Unknown"
|
MacroCopyConfiguration.CopyType.CopyToClipboard =>
|
||||||
|
"Copy to your clipboard. Macros are separated by a blank line.",
|
||||||
|
MacroCopyConfiguration.CopyType.CopyToMacroMate =>
|
||||||
|
"Copy directly to a Macro Mate macro. Requires the Macro Mate plugin.",
|
||||||
|
_ => "Unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string GetProgressBarTypeName(Configuration.ProgressBarType type) =>
|
private static string GetProgressBarTypeName(Configuration.ProgressBarType type) =>
|
||||||
@@ -207,8 +254,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
Configuration.ProgressBarType.Colorful => "Colorful, rainbow colors",
|
Configuration.ProgressBarType.Colorful => "Colorful, rainbow colors",
|
||||||
Configuration.ProgressBarType.Simple => "Simple, grayscale colors",
|
Configuration.ProgressBarType.Simple => "Simple, grayscale colors",
|
||||||
Configuration.ProgressBarType.None => "No progress bar; only percent completion is shown",
|
Configuration.ProgressBarType.None =>
|
||||||
_ => "Unknown"
|
"No progress bar; only percent completion is shown",
|
||||||
|
_ => "Unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
@@ -238,9 +286,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Enable Synthesis Helper",
|
"Enable Synthesis Helper",
|
||||||
"Adds a helper next to your synthesis window to help solve for the best craft. " +
|
"Adds a helper next to your synthesis window to help solve for the best craft. "
|
||||||
"Extremely useful for expert recipes, where the condition can greatly affect " +
|
+ "Extremely useful for expert recipes, where the condition can greatly affect "
|
||||||
"which actions you take.",
|
+ "which actions you take.",
|
||||||
Config.EnableSynthHelper,
|
Config.EnableSynthHelper,
|
||||||
v => Config.EnableSynthHelper = v,
|
v => Config.EnableSynthHelper = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -248,9 +296,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Show Only One Macro Stat in Crafting Log",
|
"Show Only One Macro Stat in Crafting Log",
|
||||||
"Only one stat will be shown for a macro. If a craft will be finished, quality " +
|
"Only one stat will be shown for a macro. If a craft will be finished, quality "
|
||||||
"is shown. Otherwise, progress is shown. Durability and remaining CP will be " +
|
+ "is shown. Otherwise, progress is shown. Durability and remaining CP will be "
|
||||||
"hidden.",
|
+ "hidden.",
|
||||||
Config.ShowOptimalMacroStat,
|
Config.ShowOptimalMacroStat,
|
||||||
v => Config.ShowOptimalMacroStat = v,
|
v => Config.ShowOptimalMacroStat = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -258,8 +306,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Check For Delineations",
|
"Check For Delineations",
|
||||||
"Your inventory will be checked to ensure that you have delineations available " +
|
"Your inventory will be checked to ensure that you have delineations available "
|
||||||
"before suggesting any specialist actions.",
|
+ "before suggesting any specialist actions.",
|
||||||
Config.CheckDelineations,
|
Config.CheckDelineations,
|
||||||
v => Config.CheckDelineations = v,
|
v => Config.CheckDelineations = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -267,9 +315,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Reliability Trial Count",
|
"Reliability Trial Count",
|
||||||
"When testing for reliability of a macro in the editor, this many trials will be " +
|
"When testing for reliability of a macro in the editor, this many trials will be "
|
||||||
"run. You should set this value to at least 100 to get a reliable spread of data. " +
|
+ "run. You should set this value to at least 100 to get a reliable spread of data. "
|
||||||
"If it's too low, you may not find an outlier, and the average might be skewed.",
|
+ "If it's too low, you may not find an outlier, and the average might be skewed.",
|
||||||
Config.ReliabilitySimulationCount,
|
Config.ReliabilitySimulationCount,
|
||||||
5,
|
5,
|
||||||
5000,
|
5000,
|
||||||
@@ -301,8 +349,13 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacroMate &&
|
if (
|
||||||
!Service.PluginInterface.InstalledPlugins.Any(p => p.IsLoaded && string.Equals(p.InternalName, "MacroMate", StringComparison.Ordinal)))
|
Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacroMate
|
||||||
|
&& !Service.PluginInterface.InstalledPlugins.Any(p =>
|
||||||
|
p.IsLoaded
|
||||||
|
&& string.Equals(p.InternalName, "MacroMate", StringComparison.Ordinal)
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange))
|
using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange))
|
||||||
@@ -326,8 +379,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Copy to Shared Macros",
|
"Copy to Shared Macros",
|
||||||
"Copy to the shared macros tab. Leaving this unchecked copies to the " +
|
"Copy to the shared macros tab. Leaving this unchecked copies to the "
|
||||||
"individual tab.",
|
+ "individual tab.",
|
||||||
Config.MacroCopy.SharedMacro,
|
Config.MacroCopy.SharedMacro,
|
||||||
v => Config.MacroCopy.SharedMacro = v,
|
v => Config.MacroCopy.SharedMacro = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -335,20 +388,22 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Macro Number",
|
"Macro Number",
|
||||||
"The # of the macro to being copying to. Subsequent macros will be " +
|
"The # of the macro to being copying to. Subsequent macros will be "
|
||||||
"copied relative to this macro.",
|
+ "copied relative to this macro.",
|
||||||
Config.MacroCopy.StartMacroIdx,
|
Config.MacroCopy.StartMacroIdx,
|
||||||
0, 99,
|
0,
|
||||||
|
99,
|
||||||
v => Config.MacroCopy.StartMacroIdx = v,
|
v => Config.MacroCopy.StartMacroIdx = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Max Macro Copy Count",
|
"Max Macro Copy Count",
|
||||||
"The maximum number of macros to be copied. Any more and a window is " +
|
"The maximum number of macros to be copied. Any more and a window is "
|
||||||
"displayed with the rest of them.",
|
+ "displayed with the rest of them.",
|
||||||
Config.MacroCopy.MaxMacroCount,
|
Config.MacroCopy.MaxMacroCount,
|
||||||
1, 99,
|
1,
|
||||||
|
99,
|
||||||
v => Config.MacroCopy.MaxMacroCount = v,
|
v => Config.MacroCopy.MaxMacroCount = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
@@ -384,15 +439,20 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Use Macro Chain",
|
"Use Macro Chain",
|
||||||
"Replaces the last step with /nextmacro to run the next macro " +
|
"Replaces the last step with /nextmacro to run the next macro "
|
||||||
"automatically. Overrides the Intermediate Notification Sound.",
|
+ "automatically. Overrides the Intermediate Notification Sound.",
|
||||||
Config.MacroCopy.UseNextMacro,
|
Config.MacroCopy.UseNextMacro,
|
||||||
v => Config.MacroCopy.UseNextMacro = v,
|
v => Config.MacroCopy.UseNextMacro = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Config.MacroCopy.UseNextMacro &&
|
if (
|
||||||
!Service.PluginInterface.InstalledPlugins.Any(p => p.IsLoaded && string.Equals(p.InternalName, "MacroChain", StringComparison.Ordinal)))
|
Config.MacroCopy.UseNextMacro
|
||||||
|
&& !Service.PluginInterface.InstalledPlugins.Any(p =>
|
||||||
|
p.IsLoaded
|
||||||
|
&& string.Equals(p.InternalName, "MacroChain", StringComparison.Ordinal)
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange))
|
using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange))
|
||||||
@@ -407,8 +467,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Add Macro Lock",
|
"Add Macro Lock",
|
||||||
"Adds /mlock to the beginning of every macro. Prevents other " +
|
"Adds /mlock to the beginning of every macro. Prevents other "
|
||||||
"macros from being run.",
|
+ "macros from being run.",
|
||||||
Config.MacroCopy.UseMacroLock,
|
Config.MacroCopy.UseMacroLock,
|
||||||
v => Config.MacroCopy.UseMacroLock = v,
|
v => Config.MacroCopy.UseMacroLock = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -424,12 +484,18 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
if (Config.MacroCopy.AddNotification)
|
if (Config.MacroCopy.AddNotification)
|
||||||
{
|
{
|
||||||
if ((Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !Config.MacroCopy.CombineMacro) && Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate)
|
if (
|
||||||
|
(
|
||||||
|
Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacro
|
||||||
|
|| !Config.MacroCopy.CombineMacro
|
||||||
|
)
|
||||||
|
&& Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate
|
||||||
|
)
|
||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Force Notification",
|
"Force Notification",
|
||||||
"Prioritize always having a notification sound at the end of " +
|
"Prioritize always having a notification sound at the end of "
|
||||||
"every macro. Keeping this off prevents macros with only 1 action.",
|
+ "every macro. Keeping this off prevents macros with only 1 action.",
|
||||||
Config.MacroCopy.ForceNotification,
|
Config.MacroCopy.ForceNotification,
|
||||||
v => Config.MacroCopy.ForceNotification = v,
|
v => Config.MacroCopy.ForceNotification = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -446,14 +512,18 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
if (Config.MacroCopy.AddNotificationSound)
|
if (Config.MacroCopy.AddNotificationSound)
|
||||||
{
|
{
|
||||||
if (!Config.MacroCopy.UseNextMacro && Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate)
|
if (
|
||||||
|
!Config.MacroCopy.UseNextMacro
|
||||||
|
&& Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate
|
||||||
|
)
|
||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Intermediate Notification Sound",
|
"Intermediate Notification Sound",
|
||||||
"Ending notification sound for an intermediary macro.\n" +
|
"Ending notification sound for an intermediary macro.\n"
|
||||||
"Uses <se.#>",
|
+ "Uses <se.#>",
|
||||||
Config.MacroCopy.IntermediateNotificationSound,
|
Config.MacroCopy.IntermediateNotificationSound,
|
||||||
1, 16,
|
1,
|
||||||
|
16,
|
||||||
v =>
|
v =>
|
||||||
{
|
{
|
||||||
Config.MacroCopy.IntermediateNotificationSound = v;
|
Config.MacroCopy.IntermediateNotificationSound = v;
|
||||||
@@ -465,10 +535,10 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Final Notification Sound",
|
"Final Notification Sound",
|
||||||
"Ending notification sound for the final macro.\n" +
|
"Ending notification sound for the final macro.\n" + "Uses <se.#>",
|
||||||
"Uses <se.#>",
|
|
||||||
Config.MacroCopy.EndNotificationSound,
|
Config.MacroCopy.EndNotificationSound,
|
||||||
1, 16,
|
1,
|
||||||
|
16,
|
||||||
v =>
|
v =>
|
||||||
{
|
{
|
||||||
Config.MacroCopy.EndNotificationSound = v;
|
Config.MacroCopy.EndNotificationSound = v;
|
||||||
@@ -506,7 +576,12 @@ public sealed class Settings : Window, IDisposable
|
|||||||
Config.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawSolverConfig(ref SolverConfig configRef, SolverConfig defaultConfig, bool disableOptimal, out bool isDirty)
|
private static void DrawSolverConfig(
|
||||||
|
ref SolverConfig configRef,
|
||||||
|
SolverConfig defaultConfig,
|
||||||
|
bool disableOptimal,
|
||||||
|
out bool isDirty
|
||||||
|
)
|
||||||
{
|
{
|
||||||
isDirty = false;
|
isDirty = false;
|
||||||
|
|
||||||
@@ -522,10 +597,10 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Algorithm",
|
"Algorithm",
|
||||||
"The algorithm to use when solving for a macro. Different " +
|
"The algorithm to use when solving for a macro. Different "
|
||||||
"algorithms provide different pros and cons for using them. " +
|
+ "algorithms provide different pros and cons for using them. "
|
||||||
"By far, the Optimal and Stepwise Genetic algorithms provide " +
|
+ "By far, the Optimal and Stepwise Genetic algorithms provide "
|
||||||
"the best results, especially for very difficult crafts.",
|
+ "the best results, especially for very difficult crafts.",
|
||||||
GetAlgorithmName,
|
GetAlgorithmName,
|
||||||
GetAlgorithmTooltip,
|
GetAlgorithmTooltip,
|
||||||
config.Algorithm,
|
config.Algorithm,
|
||||||
@@ -534,15 +609,25 @@ public sealed class Settings : Window, IDisposable
|
|||||||
disableOptimal ? [SolverAlgorithm.Raphael] : []
|
disableOptimal ? [SolverAlgorithm.Raphael] : []
|
||||||
);
|
);
|
||||||
|
|
||||||
using (ImRaii.Disabled(config.Algorithm is not (SolverAlgorithm.OneshotForked or SolverAlgorithm.StepwiseForked or SolverAlgorithm.StepwiseGenetic or SolverAlgorithm.Raphael)))
|
using (
|
||||||
|
ImRaii.Disabled(
|
||||||
|
config.Algorithm
|
||||||
|
is not (
|
||||||
|
SolverAlgorithm.OneshotForked
|
||||||
|
or SolverAlgorithm.StepwiseForked
|
||||||
|
or SolverAlgorithm.StepwiseGenetic
|
||||||
|
or SolverAlgorithm.Raphael
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Max Core Count",
|
"Max Core Count",
|
||||||
"The number of cores to use when solving. You should use as many " +
|
"The number of cores to use when solving. You should use as many "
|
||||||
"as you can. If it's too high, it will have an effect on your gameplay " +
|
+ "as you can. If it's too high, it will have an effect on your gameplay "
|
||||||
$"experience. A good estimate would be 1 or 2 cores less than your " +
|
+ $"experience. A good estimate would be 1 or 2 cores less than your "
|
||||||
$"system (FYI, you have {Environment.ProcessorCount} cores), but make sure to accomodate " +
|
+ $"system (FYI, you have {Environment.ProcessorCount} cores), but make sure to accomodate "
|
||||||
$"for any other tasks you have in the background, if you have any.\n" +
|
+ $"for any other tasks you have in the background, if you have any.\n"
|
||||||
"(Only used in the Forked, Genetic, and Optimal algorithms)",
|
+ "(Only used in the Forked, Genetic, and Optimal algorithms)",
|
||||||
config.MaxThreadCount,
|
config.MaxThreadCount,
|
||||||
1,
|
1,
|
||||||
Environment.ProcessorCount,
|
Environment.ProcessorCount,
|
||||||
@@ -554,10 +639,10 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Target Iterations",
|
"Target Iterations",
|
||||||
"The total number of iterations to run per crafting step. " +
|
"The total number of iterations to run per crafting step. "
|
||||||
"Higher values require more computational power. Higher values " +
|
+ "Higher values require more computational power. Higher values "
|
||||||
"also may decrease variance, so other values should be tweaked " +
|
+ "also may decrease variance, so other values should be tweaked "
|
||||||
"as necessary to get a more favorable outcome.",
|
+ "as necessary to get a more favorable outcome.",
|
||||||
config.Iterations,
|
config.Iterations,
|
||||||
1000,
|
1000,
|
||||||
1000000,
|
1000000,
|
||||||
@@ -567,11 +652,11 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Max Iterations",
|
"Max Iterations",
|
||||||
"The solver may go about the target iteration value if the craft " +
|
"The solver may go about the target iteration value if the craft "
|
||||||
"is sufficiently difficult, and it wasn't able to find any way to " +
|
+ "is sufficiently difficult, and it wasn't able to find any way to "
|
||||||
"complete it yet. In rare cases, the solver might go on for a very " +
|
+ "complete it yet. In rare cases, the solver might go on for a very "
|
||||||
"long time. This maximum is here to prevent the solver from stealing " +
|
+ "long time. This maximum is here to prevent the solver from stealing "
|
||||||
"all your RAM.",
|
+ "all your RAM.",
|
||||||
config.MaxIterations,
|
config.MaxIterations,
|
||||||
config.Iterations,
|
config.Iterations,
|
||||||
5000000,
|
5000000,
|
||||||
@@ -581,11 +666,11 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Max Step Count",
|
"Max Step Count",
|
||||||
"The maximum number of crafting steps; this is generally the only " +
|
"The maximum number of crafting steps; this is generally the only "
|
||||||
"setting you should change, and it should be set to around 5 steps " +
|
+ "setting you should change, and it should be set to around 5 steps "
|
||||||
"more than what you'd expect. If this value is too low, the solver " +
|
+ "more than what you'd expect. If this value is too low, the solver "
|
||||||
"won't learn much per iteration; too high and it will waste time " +
|
+ "won't learn much per iteration; too high and it will waste time "
|
||||||
"on useless extra steps.",
|
+ "on useless extra steps.",
|
||||||
config.MaxStepCount,
|
config.MaxStepCount,
|
||||||
1,
|
1,
|
||||||
100,
|
100,
|
||||||
@@ -595,9 +680,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Exploration Constant",
|
"Exploration Constant",
|
||||||
"A constant that decides how often the solver will explore new, " +
|
"A constant that decides how often the solver will explore new, "
|
||||||
"possibly good paths. If this value is too high, " +
|
+ "possibly good paths. If this value is too high, "
|
||||||
"moves will mostly be decided at random.",
|
+ "moves will mostly be decided at random.",
|
||||||
config.ExplorationConstant,
|
config.ExplorationConstant,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
@@ -607,10 +692,10 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Score Weighting Constant",
|
"Score Weighting Constant",
|
||||||
"A constant ranging from 0 to 1 that configures how the solver " +
|
"A constant ranging from 0 to 1 that configures how the solver "
|
||||||
"scores and picks paths to travel to next. A value of 0 means " +
|
+ "scores and picks paths to travel to next. A value of 0 means "
|
||||||
"actions will be chosen based on their average outcome, whereas " +
|
+ "actions will be chosen based on their average outcome, whereas "
|
||||||
"1 uses their best outcome achieved so far.",
|
+ "1 uses their best outcome achieved so far.",
|
||||||
config.MaxScoreWeightingConstant,
|
config.MaxScoreWeightingConstant,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
@@ -618,16 +703,25 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
|
|
||||||
using (ImRaii.Disabled(config.Algorithm is not (SolverAlgorithm.OneshotForked or SolverAlgorithm.StepwiseForked or SolverAlgorithm.StepwiseGenetic)))
|
using (
|
||||||
|
ImRaii.Disabled(
|
||||||
|
config.Algorithm
|
||||||
|
is not (
|
||||||
|
SolverAlgorithm.OneshotForked
|
||||||
|
or SolverAlgorithm.StepwiseForked
|
||||||
|
or SolverAlgorithm.StepwiseGenetic
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Fork Count",
|
"Fork Count",
|
||||||
"Split the number of iterations across different solvers. In general, " +
|
"Split the number of iterations across different solvers. In general, "
|
||||||
"you should increase this value to at least the number of cores in " +
|
+ "you should increase this value to at least the number of cores in "
|
||||||
$"your system (FYI, you have {Environment.ProcessorCount} cores) to attain the most speedup. " +
|
+ $"your system (FYI, you have {Environment.ProcessorCount} cores) to attain the most speedup. "
|
||||||
"The higher the number, the more chance you have of finding a " +
|
+ "The higher the number, the more chance you have of finding a "
|
||||||
"better local maximum; this concept similar but not equivalent " +
|
+ "better local maximum; this concept similar but not equivalent "
|
||||||
"to the exploration constant.\n" +
|
+ "to the exploration constant.\n"
|
||||||
"(Only used in the Forked and Genetic algorithms)",
|
+ "(Only used in the Forked and Genetic algorithms)",
|
||||||
config.ForkCount,
|
config.ForkCount,
|
||||||
1,
|
1,
|
||||||
500,
|
500,
|
||||||
@@ -638,10 +732,10 @@ public sealed class Settings : Window, IDisposable
|
|||||||
using (ImRaii.Disabled(config.Algorithm is not SolverAlgorithm.StepwiseGenetic))
|
using (ImRaii.Disabled(config.Algorithm is not SolverAlgorithm.StepwiseGenetic))
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Elitist Action Count",
|
"Elitist Action Count",
|
||||||
"On every craft step, pick this many top solutions and use them as " +
|
"On every craft step, pick this many top solutions and use them as "
|
||||||
"the input for the next craft step. For best results, use Fork Count / 2 " +
|
+ "the input for the next craft step. For best results, use Fork Count / 2 "
|
||||||
"and add about 1 or 2 more if needed.\n" +
|
+ "and add about 1 or 2 more if needed.\n"
|
||||||
"(Only used in the Stepwise Genetic algorithm)",
|
+ "(Only used in the Stepwise Genetic algorithm)",
|
||||||
config.FurcatedActionCount,
|
config.FurcatedActionCount,
|
||||||
1,
|
1,
|
||||||
500,
|
500,
|
||||||
@@ -653,16 +747,16 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Backload Progress",
|
"Backload Progress",
|
||||||
"Speeds up solve times. Backloads all Progress " +
|
"Speeds up solve times. Backloads all Progress "
|
||||||
"actions to the end of the rotation.",
|
+ "actions to the end of the rotation.",
|
||||||
config.BackloadProgress,
|
config.BackloadProgress,
|
||||||
v => config = config with { BackloadProgress = v },
|
v => config = config with { BackloadProgress = v },
|
||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Ensure Reliability",
|
"Ensure Reliability",
|
||||||
"Find a rotation that can reach the target quality no matter " +
|
"Find a rotation that can reach the target quality no matter "
|
||||||
"how unlucky the random conditions are.",
|
+ "how unlucky the random conditions are.",
|
||||||
config.Adversarial,
|
config.Adversarial,
|
||||||
v => config = config with { Adversarial = v },
|
v => config = config with { Adversarial = v },
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -677,7 +771,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGui.TextUnformatted(FontAwesomeIcon.ExclamationCircle.ToIconString());
|
ImGui.TextUnformatted(FontAwesomeIcon.ExclamationCircle.ToIconString());
|
||||||
}
|
}
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.TooltipWrapped("\"Ensure Reliability\" uses a lot more memory and can significantly increase solve times.");
|
ImGuiUtils.TooltipWrapped(
|
||||||
|
"\"Ensure Reliability\" uses a lot more memory and can significantly increase solve times."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -703,9 +799,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Max Rollout Step Count",
|
"Max Rollout Step Count",
|
||||||
"The maximum number of crafting steps every iteration can consider. " +
|
"The maximum number of crafting steps every iteration can consider. "
|
||||||
"Decreasing this value can have unintended side effects. Only change " +
|
+ "Decreasing this value can have unintended side effects. Only change "
|
||||||
"this value if you absolutely know what you're doing.",
|
+ "this value if you absolutely know what you're doing.",
|
||||||
config.MaxRolloutStepCount,
|
config.MaxRolloutStepCount,
|
||||||
1,
|
1,
|
||||||
50,
|
50,
|
||||||
@@ -715,9 +811,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Strict Actions",
|
"Strict Actions",
|
||||||
"When finding the next possible actions to execute, use a heuristic " +
|
"When finding the next possible actions to execute, use a heuristic "
|
||||||
"to restrict which actions to attempt taking. This results in a much " +
|
+ "to restrict which actions to attempt taking. This results in a much "
|
||||||
"better macro at the cost of not finding an extremely creative one.",
|
+ "better macro at the cost of not finding an extremely creative one.",
|
||||||
config.StrictActions,
|
config.StrictActions,
|
||||||
v => config = config with { StrictActions = v },
|
v => config = config with { StrictActions = v },
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -771,8 +867,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Steps",
|
"Steps",
|
||||||
"Amount of weight to give to the craft's number of steps. The lower " +
|
"Amount of weight to give to the craft's number of steps. The lower "
|
||||||
"the step count, the higher the score.",
|
+ "the step count, the higher the score.",
|
||||||
config.ScoreSteps,
|
config.ScoreSteps,
|
||||||
0,
|
0,
|
||||||
100,
|
100,
|
||||||
@@ -786,7 +882,11 @@ public sealed class Settings : Window, IDisposable
|
|||||||
configRef = config;
|
configRef = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawActionPool(ref ActionType[] actionPool, float poolWidth, out bool isDirty)
|
private static void DrawActionPool(
|
||||||
|
ref ActionType[] actionPool,
|
||||||
|
float poolWidth,
|
||||||
|
out bool isDirty
|
||||||
|
)
|
||||||
{
|
{
|
||||||
isDirty = false;
|
isDirty = false;
|
||||||
|
|
||||||
@@ -799,14 +899,21 @@ public sealed class Settings : Window, IDisposable
|
|||||||
using var _color = ImRaii.PushColor(ImGuiCol.Button, Vector4.Zero);
|
using var _color = ImRaii.PushColor(ImGuiCol.Button, Vector4.Zero);
|
||||||
using var _color3 = ImRaii.PushColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
using var _color3 = ImRaii.PushColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
||||||
using var _color2 = ImRaii.PushColor(ImGuiCol.ButtonActive, Vector4.Zero);
|
using var _color2 = ImRaii.PushColor(ImGuiCol.ButtonActive, Vector4.Zero);
|
||||||
using var _alpha = ImRaii.PushStyle(ImGuiStyleVar.DisabledAlpha, ImGui.GetStyle().DisabledAlpha * .5f);
|
using var _alpha = ImRaii.PushStyle(
|
||||||
|
ImGuiStyleVar.DisabledAlpha,
|
||||||
|
ImGui.GetStyle().DisabledAlpha * .5f
|
||||||
|
);
|
||||||
foreach (var category in Enum.GetValues<ActionCategory>())
|
foreach (var category in Enum.GetValues<ActionCategory>())
|
||||||
{
|
{
|
||||||
if (category == ActionCategory.Combo)
|
if (category == ActionCategory.Combo)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var actions = category.GetActions();
|
var actions = category.GetActions();
|
||||||
using var panel = ImRaii2.GroupPanel(category.GetDisplayName(), poolWidth, out var availSpace);
|
using var panel = ImRaii2.GroupPanel(
|
||||||
|
category.GetDisplayName(),
|
||||||
|
poolWidth,
|
||||||
|
out var availSpace
|
||||||
|
);
|
||||||
var itemsPerRow = (int)MathF.Floor((availSpace + spacing) / (imageSize + spacing));
|
var itemsPerRow = (int)MathF.Floor((availSpace + spacing) / (imageSize + spacing));
|
||||||
var itemCount = actions.Count;
|
var itemCount = actions.Count;
|
||||||
var iterCount = (int)(Math.Ceiling((float)itemCount / itemsPerRow) * itemsPerRow);
|
var iterCount = (int)(Math.Ceiling((float)itemCount / itemsPerRow) * itemsPerRow);
|
||||||
@@ -827,7 +934,17 @@ public sealed class Settings : Window, IDisposable
|
|||||||
iconTint = new(1, 1f, .5f, 1);
|
iconTint = new(1, 1f, .5f, 1);
|
||||||
else if (isRisky)
|
else if (isRisky)
|
||||||
iconTint = new(1, .5f, .5f, 1);
|
iconTint = new(1, .5f, .5f, 1);
|
||||||
if (ImGui.ImageButton(actions[i].GetIcon(recipeData.ClassJob).Handle, new(imageSize), default, Vector2.One, 0, default, iconTint))
|
if (
|
||||||
|
ImGui.ImageButton(
|
||||||
|
actions[i].GetIcon(recipeData.ClassJob).Handle,
|
||||||
|
new(imageSize),
|
||||||
|
default,
|
||||||
|
Vector2.One,
|
||||||
|
0,
|
||||||
|
default,
|
||||||
|
iconTint
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
if (isEnabled)
|
if (isEnabled)
|
||||||
@@ -841,16 +958,18 @@ public sealed class Settings : Window, IDisposable
|
|||||||
s.AppendLine(actions[i].GetName(recipeData.ClassJob));
|
s.AppendLine(actions[i].GetName(recipeData.ClassJob));
|
||||||
if (isInefficient)
|
if (isInefficient)
|
||||||
s.AppendLine(
|
s.AppendLine(
|
||||||
"Not recommended. This action may be randomly used in a " +
|
"Not recommended. This action may be randomly used in a "
|
||||||
"detrimental way to the rest of the craft. Always use " +
|
+ "detrimental way to the rest of the craft. Always use "
|
||||||
"your best judgement if enabling this action.");
|
+ "your best judgement if enabling this action."
|
||||||
|
);
|
||||||
if (isRisky)
|
if (isRisky)
|
||||||
s.AppendLine(
|
s.AppendLine(
|
||||||
"Useless; the solver currently doesn't take any risks in " +
|
"Useless; the solver currently doesn't take any risks in "
|
||||||
"its crafts. It only takes steps that have a 100% chance of " +
|
+ "its crafts. It only takes steps that have a 100% chance of "
|
||||||
"succeeding. If you want have a moment where you want to take " +
|
+ "succeeding. If you want have a moment where you want to take "
|
||||||
"risks in your craft (like in expert recipes), don't rely " +
|
+ "risks in your craft (like in expert recipes), don't rely "
|
||||||
"on the solver during that time.");
|
+ "on the solver during that time."
|
||||||
|
);
|
||||||
ImGuiUtils.TooltipWrapped(s.ToString());
|
ImGuiUtils.TooltipWrapped(s.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -912,8 +1031,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Pin Helper Window",
|
"Pin Helper Window",
|
||||||
"Pins the helper window to the right of your crafting log. Disabling this will " +
|
"Pins the helper window to the right of your crafting log. Disabling this will "
|
||||||
"allow you to move it around.",
|
+ "allow you to move it around.",
|
||||||
Config.PinRecipeNoteToWindow,
|
Config.PinRecipeNoteToWindow,
|
||||||
v => Config.PinRecipeNoteToWindow = v,
|
v => Config.PinRecipeNoteToWindow = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -921,8 +1040,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Always Collapse Helper Window",
|
"Always Collapse Helper Window",
|
||||||
"Enabling this will cause the Helper Window to be collapsed whenever you start " +
|
"Enabling this will cause the Helper Window to be collapsed whenever you start "
|
||||||
"a new craft, preventing the solver from running automatically.",
|
+ "a new craft, preventing the solver from running automatically.",
|
||||||
Config.CollapseSynthHelper,
|
Config.CollapseSynthHelper,
|
||||||
v => Config.CollapseSynthHelper = v,
|
v => Config.CollapseSynthHelper = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -930,11 +1049,11 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Automatically Suggest Macro",
|
"Automatically Suggest Macro",
|
||||||
"(Can cause frame drops!) When navigating to a new recipe or changing your gear " +
|
"(Can cause frame drops!) When navigating to a new recipe or changing your gear "
|
||||||
"stats, automatically suggest a new macro (equivalent to clicking \"Generate\" " +
|
+ "stats, automatically suggest a new macro (equivalent to clicking \"Generate\" "
|
||||||
"in the Macro Editor). This can cause harsh frame drops on some computers or " +
|
+ "in the Macro Editor). This can cause harsh frame drops on some computers or "
|
||||||
"recipes when underleveled while navigating the crafting log. Turning this off " +
|
+ "recipes when underleveled while navigating the crafting log. Turning this off "
|
||||||
"provides a button to allow you to manually suggest a macro only when you need it.",
|
+ "provides a button to allow you to manually suggest a macro only when you need it.",
|
||||||
Config.SuggestMacroAutomatically,
|
Config.SuggestMacroAutomatically,
|
||||||
v => Config.SuggestMacroAutomatically = v,
|
v => Config.SuggestMacroAutomatically = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -942,10 +1061,10 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Enable Community Macros",
|
"Enable Community Macros",
|
||||||
"Use FFXIV Teamcraft's community rotations to search for and find the best possible " +
|
"Use FFXIV Teamcraft's community rotations to search for and find the best possible "
|
||||||
"crowd-sourced macro for your craft. This sends a request to their servers to retrieve " +
|
+ "crowd-sourced macro for your craft. This sends a request to their servers to retrieve "
|
||||||
"a list of macros that apply to your craft's rlvl. Requests are only sent once per rlvl " +
|
+ "a list of macros that apply to your craft's rlvl. Requests are only sent once per rlvl "
|
||||||
"and are always cached to reduce server load.",
|
+ "and are always cached to reduce server load.",
|
||||||
Config.ShowCommunityMacros,
|
Config.ShowCommunityMacros,
|
||||||
v => Config.ShowCommunityMacros = v,
|
v => Config.ShowCommunityMacros = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -955,9 +1074,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Automatically Search for Community Macro",
|
"Automatically Search for Community Macro",
|
||||||
"When navigating to a new recipe or changing your gear stats, automatically search " +
|
"When navigating to a new recipe or changing your gear stats, automatically search "
|
||||||
"online for a new community macro.\n" +
|
+ "online for a new community macro.\n"
|
||||||
"This is turned off by default so you don't hammer their servers :)",
|
+ "This is turned off by default so you don't hammer their servers :)",
|
||||||
Config.SearchCommunityMacroAutomatically,
|
Config.SearchCommunityMacroAutomatically,
|
||||||
v => Config.SearchCommunityMacroAutomatically = v,
|
v => Config.SearchCommunityMacroAutomatically = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -969,7 +1088,12 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
var solverConfig = Config.RecipeNoteSolverConfig;
|
var solverConfig = Config.RecipeNoteSolverConfig;
|
||||||
DrawSolverConfig(ref solverConfig, SolverConfig.RecipeNoteDefault, false, out var isSolverDirty);
|
DrawSolverConfig(
|
||||||
|
ref solverConfig,
|
||||||
|
SolverConfig.RecipeNoteDefault,
|
||||||
|
false,
|
||||||
|
out var isSolverDirty
|
||||||
|
);
|
||||||
if (isSolverDirty)
|
if (isSolverDirty)
|
||||||
{
|
{
|
||||||
Config.RecipeNoteSolverConfig = solverConfig;
|
Config.RecipeNoteSolverConfig = solverConfig;
|
||||||
@@ -991,7 +1115,12 @@ public sealed class Settings : Window, IDisposable
|
|||||||
var isDirty = false;
|
var isDirty = false;
|
||||||
|
|
||||||
var solverConfig = Config.EditorSolverConfig;
|
var solverConfig = Config.EditorSolverConfig;
|
||||||
DrawSolverConfig(ref solverConfig, SolverConfig.EditorDefault, false, out var isSolverDirty);
|
DrawSolverConfig(
|
||||||
|
ref solverConfig,
|
||||||
|
SolverConfig.EditorDefault,
|
||||||
|
false,
|
||||||
|
out var isSolverDirty
|
||||||
|
);
|
||||||
if (isSolverDirty)
|
if (isSolverDirty)
|
||||||
{
|
{
|
||||||
Config.EditorSolverConfig = solverConfig;
|
Config.EditorSolverConfig = solverConfig;
|
||||||
@@ -1014,8 +1143,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Pin Helper Window",
|
"Pin Helper Window",
|
||||||
"Pins the synthesis helper to the right of your synthesis window. Disabling this will " +
|
"Pins the synthesis helper to the right of your synthesis window. Disabling this will "
|
||||||
"allow you to move it around.",
|
+ "allow you to move it around.",
|
||||||
Config.PinSynthHelperToWindow,
|
Config.PinSynthHelperToWindow,
|
||||||
v => Config.PinSynthHelperToWindow = v,
|
v => Config.PinSynthHelperToWindow = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -1031,9 +1160,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Simulate Only First Step",
|
"Simulate Only First Step",
|
||||||
"Only the first step is simulated by default. You can still " +
|
"Only the first step is simulated by default. You can still "
|
||||||
"hover over the other steps to view their outcomes, but the " +
|
+ "hover over the other steps to view their outcomes, but the "
|
||||||
"reliability trials (when hovering over the macro stats) are hidden.",
|
+ "reliability trials (when hovering over the macro stats) are hidden.",
|
||||||
Config.SynthHelperDisplayOnlyFirstStep,
|
Config.SynthHelperDisplayOnlyFirstStep,
|
||||||
v => Config.SynthHelperDisplayOnlyFirstStep = v,
|
v => Config.SynthHelperDisplayOnlyFirstStep = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -1041,9 +1170,9 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Draw Ability Ants",
|
"Draw Ability Ants",
|
||||||
"Turns your hotbar into a whack-a-mole game! Draws ants for " +
|
"Turns your hotbar into a whack-a-mole game! Draws ants for "
|
||||||
"the next action that should be executed. Also disables ants " +
|
+ "the next action that should be executed. Also disables ants "
|
||||||
"for things like combo actions and condition procs.",
|
+ "for things like combo actions and condition procs.",
|
||||||
Config.SynthHelperAbilityAnts,
|
Config.SynthHelperAbilityAnts,
|
||||||
v => Config.SynthHelperAbilityAnts = v,
|
v => Config.SynthHelperAbilityAnts = v,
|
||||||
ref isDirty
|
ref isDirty
|
||||||
@@ -1051,8 +1180,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Solver Step Count",
|
"Solver Step Count",
|
||||||
"The minimum number of future steps to solve for during an in-game craft. " +
|
"The minimum number of future steps to solve for during an in-game craft. "
|
||||||
"The solver may still give more than this amount if it's at no cost to you.",
|
+ "The solver may still give more than this amount if it's at no cost to you.",
|
||||||
Config.SynthHelperStepCount,
|
Config.SynthHelperStepCount,
|
||||||
1,
|
1,
|
||||||
100,
|
100,
|
||||||
@@ -1062,8 +1191,8 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Max Step Display Count",
|
"Max Step Display Count",
|
||||||
"Enforces a maximum number of steps to display in the synth helper to " +
|
"Enforces a maximum number of steps to display in the synth helper to "
|
||||||
"get rid of clutter.",
|
+ "get rid of clutter.",
|
||||||
Config.SynthHelperMaxDisplayCount,
|
Config.SynthHelperMaxDisplayCount,
|
||||||
Config.SynthHelperStepCount,
|
Config.SynthHelperStepCount,
|
||||||
100,
|
100,
|
||||||
@@ -1076,7 +1205,12 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
var solverConfig = Config.SynthHelperSolverConfig;
|
var solverConfig = Config.SynthHelperSolverConfig;
|
||||||
DrawSolverConfig(ref solverConfig, SolverConfig.SynthHelperDefault, true, out var isSolverDirty);
|
DrawSolverConfig(
|
||||||
|
ref solverConfig,
|
||||||
|
SolverConfig.SynthHelperDefault,
|
||||||
|
true,
|
||||||
|
out var isSolverDirty
|
||||||
|
);
|
||||||
if (isSolverDirty)
|
if (isSolverDirty)
|
||||||
{
|
{
|
||||||
Config.SynthHelperSolverConfig = solverConfig;
|
Config.SynthHelperSolverConfig = solverConfig;
|
||||||
@@ -1109,18 +1243,33 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGui.Image(icon.Handle, iconDim);
|
ImGui.Image(icon.Handle, iconDim);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGuiUtils.AlignMiddle(new(float.PositiveInfinity, HeaderFont.GetFontSize() + SubheaderFont.GetFontSize() + ImGui.GetFontSize() * 3 + ImGui.GetStyle().ItemSpacing.Y * 4), new(0, iconDim.Y));
|
ImGuiUtils.AlignMiddle(
|
||||||
|
new(
|
||||||
|
float.PositiveInfinity,
|
||||||
|
HeaderFont.GetFontSize()
|
||||||
|
+ SubheaderFont.GetFontSize()
|
||||||
|
+ ImGui.GetFontSize() * 3
|
||||||
|
+ ImGui.GetStyle().ItemSpacing.Y * 4
|
||||||
|
),
|
||||||
|
new(0, iconDim.Y)
|
||||||
|
);
|
||||||
|
|
||||||
using (HeaderFont.Push())
|
using (HeaderFont.Push())
|
||||||
{
|
{
|
||||||
ImGuiUtils.AlignCentered(ImGui.CalcTextSize("Forgeimizer").X);
|
ImGuiUtils.AlignCentered(ImGui.CalcTextSize("Forgeimizer").X);
|
||||||
ImGuiUtils.Hyperlink("Forgeimizer", "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer", false);
|
ImGuiUtils.Hyperlink(
|
||||||
|
"Forgeimizer",
|
||||||
|
"https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer",
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (SubheaderFont.Push())
|
using (SubheaderFont.Push())
|
||||||
ImGuiUtils.TextCentered($"v{plugin.Version} {plugin.BuildConfiguration}");
|
ImGuiUtils.TextCentered($"v{plugin.Version} {plugin.BuildConfiguration}");
|
||||||
|
|
||||||
ImGuiUtils.AlignCentered(ImGui.CalcTextSize($"By {plugin.Author} (WorkingRobot)").X);
|
ImGuiUtils.AlignCentered(
|
||||||
|
ImGui.CalcTextSize($"By {plugin.Author} (WorkingRobot)").X
|
||||||
|
);
|
||||||
ImGui.TextUnformatted($"By {plugin.Author} (");
|
ImGui.TextUnformatted($"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");
|
||||||
@@ -1178,11 +1327,17 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
ImGuiUtils.TextWrappedTo("Thank you to ");
|
ImGuiUtils.TextWrappedTo("Thank you to ");
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
ImGuiUtils.Hyperlink("this", "https://dke.maastrichtuniversity.nl/m.winands/documents/multithreadedMCTS2.pdf");
|
ImGuiUtils.Hyperlink(
|
||||||
|
"this",
|
||||||
|
"https://dke.maastrichtuniversity.nl/m.winands/documents/multithreadedMCTS2.pdf"
|
||||||
|
);
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
ImGuiUtils.TextWrappedTo(", ");
|
ImGuiUtils.TextWrappedTo(", ");
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
ImGuiUtils.Hyperlink("this", "https://liacs.leidenuniv.nl/~plaata1/papers/paper_ICAART18.pdf");
|
ImGuiUtils.Hyperlink(
|
||||||
|
"this",
|
||||||
|
"https://liacs.leidenuniv.nl/~plaata1/papers/paper_ICAART18.pdf"
|
||||||
|
);
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
ImGuiUtils.TextWrappedTo(", and ");
|
ImGuiUtils.TextWrappedTo(", and ");
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Threading;
|
||||||
using Craftimizer.Plugin;
|
using Craftimizer.Plugin;
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
using Craftimizer.Utils;
|
using Craftimizer.Utils;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
@@ -15,12 +21,6 @@ using FFXIVClientStructs.FFXIV.Client.Game;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Shell;
|
using FFXIVClientStructs.FFXIV.Client.UI.Shell;
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Threading;
|
|
||||||
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
||||||
using Sim = Craftimizer.Simulator.Simulator;
|
using Sim = Craftimizer.Simulator.Simulator;
|
||||||
using SimNoRandom = Craftimizer.Simulator.SimulatorNoRandom;
|
using SimNoRandom = Craftimizer.Simulator.SimulatorNoRandom;
|
||||||
@@ -29,12 +29,11 @@ namespace Craftimizer.Windows;
|
|||||||
|
|
||||||
public sealed unsafe class SynthHelper : Window, IDisposable
|
public sealed unsafe class SynthHelper : Window, IDisposable
|
||||||
{
|
{
|
||||||
private const ImGuiWindowFlags WindowFlagsPinned = WindowFlagsFloating
|
private const ImGuiWindowFlags WindowFlagsPinned =
|
||||||
| ImGuiWindowFlags.NoSavedSettings;
|
WindowFlagsFloating | ImGuiWindowFlags.NoSavedSettings;
|
||||||
|
|
||||||
private const ImGuiWindowFlags WindowFlagsFloating =
|
private const ImGuiWindowFlags WindowFlagsFloating =
|
||||||
ImGuiWindowFlags.AlwaysAutoResize
|
ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoFocusOnAppearing;
|
||||||
| ImGuiWindowFlags.NoFocusOnAppearing;
|
|
||||||
|
|
||||||
private const string WindowNamePinned = "Craftimizer Synthesis Helper###CraftimizerSynthHelper";
|
private const string WindowNamePinned = "Craftimizer Synthesis Helper###CraftimizerSynthHelper";
|
||||||
private const string WindowNameFloating = $"{WindowNamePinned}Floating";
|
private const string WindowNameFloating = $"{WindowNamePinned}Floating";
|
||||||
@@ -69,9 +68,12 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
|
|
||||||
private IFontHandle AxisFont { get; }
|
private IFontHandle AxisFont { get; }
|
||||||
|
|
||||||
public SynthHelper() : base(WindowNamePinned)
|
public SynthHelper()
|
||||||
|
: base(WindowNamePinned)
|
||||||
{
|
{
|
||||||
AxisFont = Service.PluginInterface.UiBuilder.FontAtlas.NewGameFontHandle(new(GameFontFamilyAndSize.Axis14));
|
AxisFont = Service.PluginInterface.UiBuilder.FontAtlas.NewGameFontHandle(
|
||||||
|
new(GameFontFamilyAndSize.Axis14)
|
||||||
|
);
|
||||||
|
|
||||||
Service.Plugin.Hooks.OnActionUsed += OnUseAction;
|
Service.Plugin.Hooks.OnActionUsed += OnUseAction;
|
||||||
|
|
||||||
@@ -83,7 +85,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
SizeConstraints = new WindowSizeConstraints
|
SizeConstraints = new WindowSizeConstraints
|
||||||
{
|
{
|
||||||
MinimumSize = new(494, -1),
|
MinimumSize = new(494, -1),
|
||||||
MaximumSize = new(494, 10000)
|
MaximumSize = new(494, 10000),
|
||||||
};
|
};
|
||||||
|
|
||||||
TitleBarButtons =
|
TitleBarButtons =
|
||||||
@@ -93,14 +95,15 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
Icon = FontAwesomeIcon.Cog,
|
Icon = FontAwesomeIcon.Cog,
|
||||||
IconOffset = new(2, 1),
|
IconOffset = new(2, 1),
|
||||||
Click = _ => Service.Plugin.OpenSettingsTab("Synthesis Helper"),
|
Click = _ => Service.Plugin.OpenSettingsTab("Synthesis Helper"),
|
||||||
ShowTooltip = () => ImGuiUtils.Tooltip("Open Settings")
|
ShowTooltip = () => ImGuiUtils.Tooltip("Open Settings"),
|
||||||
},
|
},
|
||||||
new() {
|
new()
|
||||||
|
{
|
||||||
Icon = FontAwesomeIcon.Heart,
|
Icon = FontAwesomeIcon.Heart,
|
||||||
IconOffset = new(2, 1),
|
IconOffset = new(2, 1),
|
||||||
Click = _ => Util.OpenLink(Plugin.Plugin.SupportLink),
|
Click = _ => Util.OpenLink(Plugin.Plugin.SupportLink),
|
||||||
ShowTooltip = () => ImGuiUtils.Tooltip("Support me on Ko-fi!")
|
ShowTooltip = () => ImGuiUtils.Tooltip("Support me on Ko-fi!"),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
Service.WindowSystem.AddWindow(this);
|
Service.WindowSystem.AddWindow(this);
|
||||||
@@ -153,10 +156,10 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
WasCalculatable = ShouldCalculate;
|
WasCalculatable = ShouldCalculate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool DrawConditions() =>
|
public override bool DrawConditions() => ShouldOpen;
|
||||||
ShouldOpen;
|
|
||||||
|
|
||||||
private bool wasInCraftAction;
|
private bool wasInCraftAction;
|
||||||
|
|
||||||
private bool CalculateShouldOpen()
|
private bool CalculateShouldOpen()
|
||||||
{
|
{
|
||||||
if (Service.Objects.LocalPlayer == null)
|
if (Service.Objects.LocalPlayer == null)
|
||||||
@@ -209,7 +212,8 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
OnStartCrafting(recipeId);
|
OnStartCrafting(recipeId);
|
||||||
OnStateUpdated();
|
OnStateUpdated();
|
||||||
|
|
||||||
if (Service.Configuration.CollapseSynthHelper) ShouldCollapse = true;
|
if (Service.Configuration.CollapseSynthHelper)
|
||||||
|
ShouldCollapse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsRecalculateQueued)
|
if (IsRecalculateQueued)
|
||||||
@@ -228,6 +232,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
private Vector2? LastPosition { get; set; }
|
private Vector2? LastPosition { get; set; }
|
||||||
private byte? StyleAlpha { get; set; }
|
private byte? StyleAlpha { get; set; }
|
||||||
private byte? LastAlpha { get; set; }
|
private byte? LastAlpha { get; set; }
|
||||||
|
|
||||||
public override void PreDraw()
|
public override void PreDraw()
|
||||||
{
|
{
|
||||||
base.PreDraw();
|
base.PreDraw();
|
||||||
@@ -239,7 +244,9 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
ref var unit = ref Addon->AtkUnitBase;
|
ref var unit = ref Addon->AtkUnitBase;
|
||||||
var scale = unit.Scale;
|
var scale = unit.Scale;
|
||||||
var pos = new Vector2(unit.X, unit.Y);
|
var pos = new Vector2(unit.X, unit.Y);
|
||||||
var size = new Vector2(unit.WindowNode->AtkResNode.Width, unit.WindowNode->AtkResNode.Height) * scale;
|
var size =
|
||||||
|
new Vector2(unit.WindowNode->AtkResNode.Width, unit.WindowNode->AtkResNode.Height)
|
||||||
|
* scale;
|
||||||
|
|
||||||
var offset = 5;
|
var offset = 5;
|
||||||
|
|
||||||
@@ -261,7 +268,10 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
WindowName = WindowNameFloating;
|
WindowName = WindowNameFloating;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, StyleAlpha.HasValue ? (StyleAlpha.Value / 255f) : 1);
|
ImGui.PushStyleVar(
|
||||||
|
ImGuiStyleVar.Alpha,
|
||||||
|
StyleAlpha.HasValue ? (StyleAlpha.Value / 255f) : 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PostDraw()
|
public override void PostDraw()
|
||||||
@@ -273,7 +283,6 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ShouldCollapse)
|
if (ShouldCollapse)
|
||||||
{
|
{
|
||||||
ImGui.SetWindowCollapsed(true);
|
ImGui.SetWindowCollapsed(true);
|
||||||
@@ -298,7 +307,9 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SimulationState? hoveredState;
|
private SimulationState? hoveredState;
|
||||||
private SimulationState DisplayedState => hoveredState ?? (Service.Configuration.SynthHelperDisplayOnlyFirstStep ? Macro.FirstState : Macro.State);
|
private SimulationState DisplayedState =>
|
||||||
|
hoveredState
|
||||||
|
?? (Service.Configuration.SynthHelperDisplayOnlyFirstStep ? Macro.FirstState : Macro.State);
|
||||||
|
|
||||||
private void DrawMacro()
|
private void DrawMacro()
|
||||||
{
|
{
|
||||||
@@ -308,7 +319,11 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
var lastState = Macro.InitialState;
|
var lastState = Macro.InitialState;
|
||||||
hoveredState = null;
|
hoveredState = null;
|
||||||
|
|
||||||
var itemsPerRow = (int)Math.Max(1, MathF.Floor((ImGui.GetContentRegionAvail().X + spacing) / (imageSize + spacing)));
|
var itemsPerRow = (int)
|
||||||
|
Math.Max(
|
||||||
|
1,
|
||||||
|
MathF.Floor((ImGui.GetContentRegionAvail().X + spacing) / (imageSize + spacing))
|
||||||
|
);
|
||||||
|
|
||||||
using var _color = ImRaii.PushColor(ImGuiCol.Button, Vector4.Zero);
|
using var _color = ImRaii.PushColor(ImGuiCol.Button, Vector4.Zero);
|
||||||
using var _color3 = ImRaii.PushColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
using var _color3 = ImRaii.PushColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
||||||
@@ -328,9 +343,18 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
var offsetVec2 = ImGui.GetStyle().ItemSpacing / 2;
|
var offsetVec2 = ImGui.GetStyle().ItemSpacing / 2;
|
||||||
var offset = new Vector2((offsetVec2.X + offsetVec2.Y) / 2f);
|
var offset = new Vector2((offsetVec2.X + offsetVec2.Y) / 2f);
|
||||||
var color = canExecute ? ImGuiColors.DalamudWhite2 : ImGuiColors.DalamudGrey3;
|
var color = canExecute ? ImGuiColors.DalamudWhite2 : ImGuiColors.DalamudGrey3;
|
||||||
ImGui.GetWindowDrawList().AddRectFilled(pos - offset, pos + new Vector2(imageSize) + offset, ImGui.GetColorU32(color), 4);
|
ImGui
|
||||||
|
.GetWindowDrawList()
|
||||||
|
.AddRectFilled(
|
||||||
|
pos - offset,
|
||||||
|
pos + new Vector2(imageSize) + offset,
|
||||||
|
ImGui.GetColorU32(color),
|
||||||
|
4
|
||||||
|
);
|
||||||
}
|
}
|
||||||
bool isHovered, isHeld, isPressed;
|
bool isHovered,
|
||||||
|
isHeld,
|
||||||
|
isPressed;
|
||||||
{
|
{
|
||||||
var pos = ImGui.GetCursorScreenPos();
|
var pos = ImGui.GetCursorScreenPos();
|
||||||
var offset = ImGui.GetStyle().ItemSpacing / 2f;
|
var offset = ImGui.GetStyle().ItemSpacing / 2f;
|
||||||
@@ -344,9 +368,23 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
var id = ImGui.GetID($"###ButtonContainer");
|
var id = ImGui.GetID($"###ButtonContainer");
|
||||||
var isClipped = !ImGuiExtras.ItemAdd(bb, id, out _, 0);
|
var isClipped = !ImGuiExtras.ItemAdd(bb, id, out _, 0);
|
||||||
|
|
||||||
isPressed = ImGuiExtras.ButtonBehavior(bb, id, out isHovered, out isHeld, ImGuiButtonFlags.None);
|
isPressed = ImGuiExtras.ButtonBehavior(
|
||||||
|
bb,
|
||||||
|
id,
|
||||||
|
out isHovered,
|
||||||
|
out isHeld,
|
||||||
|
ImGuiButtonFlags.None
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ImGui.ImageButton(action.GetIcon(RecipeData!.ClassJob).Handle, new(imageSize), default, Vector2.One, 0, default, failedAction ? new(1, 1, 1, ImGui.GetStyle().DisabledAlpha) : Vector4.One);
|
ImGui.ImageButton(
|
||||||
|
action.GetIcon(RecipeData!.ClassJob).Handle,
|
||||||
|
new(imageSize),
|
||||||
|
default,
|
||||||
|
Vector2.One,
|
||||||
|
0,
|
||||||
|
default,
|
||||||
|
failedAction ? new(1, 1, 1, ImGui.GetStyle().DisabledAlpha) : Vector4.One
|
||||||
|
);
|
||||||
if (isPressed && i == 0)
|
if (isPressed && i == 0)
|
||||||
{
|
{
|
||||||
if (ExecuteNextAction())
|
if (ExecuteNextAction())
|
||||||
@@ -354,15 +392,21 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
}
|
}
|
||||||
if (isHovered)
|
if (isHovered)
|
||||||
{
|
{
|
||||||
ImGuiUtils.Tooltip($"{action.GetName(RecipeData!.ClassJob)}\n" +
|
ImGuiUtils.Tooltip(
|
||||||
$"{actionBase.GetTooltip(CreateSim(lastState), true)}" +
|
$"{action.GetName(RecipeData!.ClassJob)}\n"
|
||||||
$"{(canExecute && i == 0 ? "Click or run /craftaction to execute" : string.Empty)}");
|
+ $"{actionBase.GetTooltip(CreateSim(lastState), true)}"
|
||||||
|
+ $"{(canExecute && i == 0 ? "Click or run /craftaction to execute" : string.Empty)}"
|
||||||
|
);
|
||||||
hoveredState = state;
|
hoveredState = state;
|
||||||
}
|
}
|
||||||
lastState = state;
|
lastState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rows = (int)Math.Max(1, MathF.Ceiling(Service.Configuration.SynthHelperMaxDisplayCount / itemsPerRow));
|
var rows = (int)
|
||||||
|
Math.Max(
|
||||||
|
1,
|
||||||
|
MathF.Ceiling(Service.Configuration.SynthHelperMaxDisplayCount / itemsPerRow)
|
||||||
|
);
|
||||||
for (var i = 0; i < rows; ++i)
|
for (var i = 0; i < rows; ++i)
|
||||||
{
|
{
|
||||||
if (count <= i * itemsPerRow)
|
if (count <= i * itemsPerRow)
|
||||||
@@ -381,7 +425,15 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
var iconHeight = ImGui.GetFrameHeight() * 1.75f;
|
var iconHeight = ImGui.GetFrameHeight() * 1.75f;
|
||||||
var durationShift = iconHeight * .2f;
|
var durationShift = iconHeight * .2f;
|
||||||
|
|
||||||
ImGui.Dummy(new(0, iconHeight + ImGui.GetStyle().ItemSpacing.Y + ImGui.GetTextLineHeight() - durationShift));
|
ImGui.Dummy(
|
||||||
|
new(
|
||||||
|
0,
|
||||||
|
iconHeight
|
||||||
|
+ ImGui.GetStyle().ItemSpacing.Y
|
||||||
|
+ ImGui.GetTextLineHeight()
|
||||||
|
- durationShift
|
||||||
|
)
|
||||||
|
);
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
|
|
||||||
var effects = state.ActiveEffects;
|
var effects = state.ActiveEffects;
|
||||||
@@ -413,35 +465,92 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var reliability = Macro.GetReliability(RecipeData!, Service.Configuration.SynthHelperDisplayOnlyFirstStep ? 0 : ^1);
|
var reliability = Macro.GetReliability(
|
||||||
|
RecipeData!,
|
||||||
|
Service.Configuration.SynthHelperDisplayOnlyFirstStep ? 0 : ^1
|
||||||
|
);
|
||||||
{
|
{
|
||||||
var mainBars = new List<DynamicBars.BarData>()
|
var mainBars = new List<DynamicBars.BarData>()
|
||||||
{
|
{
|
||||||
new("Progress", Colors.Progress, reliability.Progress, state.Progress, RecipeData!.RecipeInfo.MaxProgress),
|
new(
|
||||||
new("Quality", Colors.Quality, reliability.Quality, state.Quality, RecipeData.RecipeInfo.MaxQuality),
|
"Progress",
|
||||||
|
Colors.Progress,
|
||||||
|
reliability.Progress,
|
||||||
|
state.Progress,
|
||||||
|
RecipeData!.RecipeInfo.MaxProgress
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
"Quality",
|
||||||
|
Colors.Quality,
|
||||||
|
reliability.Quality,
|
||||||
|
state.Quality,
|
||||||
|
RecipeData.RecipeInfo.MaxQuality
|
||||||
|
),
|
||||||
new("CP", Colors.CP, state.CP, CharacterStats!.CP),
|
new("CP", Colors.CP, state.CP, CharacterStats!.CP),
|
||||||
};
|
};
|
||||||
if (RecipeData.RecipeInfo.MaxQuality <= 0)
|
if (RecipeData.RecipeInfo.MaxQuality <= 0)
|
||||||
mainBars.RemoveAt(1);
|
mainBars.RemoveAt(1);
|
||||||
var halfBars = new List<DynamicBars.BarData>()
|
var halfBars = new List<DynamicBars.BarData>()
|
||||||
{
|
{
|
||||||
new("Durability", Colors.Durability, state.Durability, RecipeData.RecipeInfo.MaxDurability),
|
new(
|
||||||
|
"Durability",
|
||||||
|
Colors.Durability,
|
||||||
|
state.Durability,
|
||||||
|
RecipeData.RecipeInfo.MaxDurability
|
||||||
|
),
|
||||||
};
|
};
|
||||||
if (RecipeData.IsCollectable)
|
if (RecipeData.IsCollectable)
|
||||||
halfBars.Add(new("Collectability", Colors.Collectability, reliability.ParamScore, state.Collectability, state.MaxCollectability, RecipeData.CollectableThresholds, $"{state.Collectability}", $"{state.MaxCollectability:0}"));
|
halfBars.Add(
|
||||||
|
new(
|
||||||
|
"Collectability",
|
||||||
|
Colors.Collectability,
|
||||||
|
reliability.ParamScore,
|
||||||
|
state.Collectability,
|
||||||
|
state.MaxCollectability,
|
||||||
|
RecipeData.CollectableThresholds,
|
||||||
|
$"{state.Collectability}",
|
||||||
|
$"{state.MaxCollectability:0}"
|
||||||
|
)
|
||||||
|
);
|
||||||
else if (RecipeData.Recipe.RequiredQuality > 0)
|
else if (RecipeData.Recipe.RequiredQuality > 0)
|
||||||
{
|
{
|
||||||
var qualityPercent = (float)state.Quality / RecipeData.Recipe.RequiredQuality * 100;
|
var qualityPercent = (float)state.Quality / RecipeData.Recipe.RequiredQuality * 100;
|
||||||
halfBars.Add(new("Quality %", Colors.HQ, reliability.ParamScore, qualityPercent, 100, null, $"{qualityPercent:0}%", null));
|
halfBars.Add(
|
||||||
|
new(
|
||||||
|
"Quality %",
|
||||||
|
Colors.HQ,
|
||||||
|
reliability.ParamScore,
|
||||||
|
qualityPercent,
|
||||||
|
100,
|
||||||
|
null,
|
||||||
|
$"{qualityPercent:0}%",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else if (RecipeData.RecipeInfo.MaxQuality > 0)
|
else if (RecipeData.RecipeInfo.MaxQuality > 0)
|
||||||
halfBars.Add(new("HQ %", Colors.HQ, reliability.ParamScore, state.HQPercent, 100, null, $"{state.HQPercent}%", null));
|
halfBars.Add(
|
||||||
|
new(
|
||||||
|
"HQ %",
|
||||||
|
Colors.HQ,
|
||||||
|
reliability.ParamScore,
|
||||||
|
state.HQPercent,
|
||||||
|
100,
|
||||||
|
null,
|
||||||
|
$"{state.HQPercent}%",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (halfBars.Count > 1)
|
if (halfBars.Count > 1)
|
||||||
{
|
{
|
||||||
var textSize = DynamicBars.GetTextSize(mainBars.Concat(halfBars));
|
var textSize = DynamicBars.GetTextSize(mainBars.Concat(halfBars));
|
||||||
DynamicBars.Draw(mainBars, textSize);
|
DynamicBars.Draw(mainBars, textSize);
|
||||||
using var table = ImRaii.Table($"##{nameof(SynthHelper)}_halfbars", halfBars.Count, ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.SizingStretchSame);
|
using var table = ImRaii.Table(
|
||||||
|
$"##{nameof(SynthHelper)}_halfbars",
|
||||||
|
halfBars.Count,
|
||||||
|
ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.SizingStretchSame
|
||||||
|
);
|
||||||
if (table)
|
if (table)
|
||||||
{
|
{
|
||||||
foreach (var bar in halfBars)
|
foreach (var bar in halfBars)
|
||||||
@@ -467,7 +576,9 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
using var _disabled = ImRaii.Disabled();
|
using var _disabled = ImRaii.Disabled();
|
||||||
ImGui.Button("Stopping", new(-1, 0));
|
ImGui.Button("Stopping", new(-1, 0));
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.TooltipWrapped("This might could a while, sorry! Please report if this takes longer than a second.");
|
ImGuiUtils.TooltipWrapped(
|
||||||
|
"This might could a while, sorry! Please report if this takes longer than a second."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -480,13 +591,22 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
if (ImGui.Button("Retry", new(-1, 0)))
|
if (ImGui.Button("Retry", new(-1, 0)))
|
||||||
AttemptRetry();
|
AttemptRetry();
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiUtils.TooltipWrapped("Suggest a way to finish the crafting recipe. " +
|
ImGuiUtils.TooltipWrapped(
|
||||||
"Results aren't perfect, and levels of success " +
|
"Suggest a way to finish the crafting recipe. "
|
||||||
"can vary wildly depending on the solver's settings.");
|
+ "Results aren't perfect, and levels of success "
|
||||||
|
+ "can vary wildly depending on the solver's settings."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.Button("Open in Macro Editor", new(-1, 0)))
|
if (ImGui.Button("Open in Macro Editor", new(-1, 0)))
|
||||||
Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.Objects.LocalPlayer!.StatusList), null, [], null);
|
Service.Plugin.OpenMacroEditor(
|
||||||
|
CharacterStats!,
|
||||||
|
RecipeData!,
|
||||||
|
new(Service.Objects.LocalPlayer!.StatusList),
|
||||||
|
null,
|
||||||
|
[],
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ExecuteNextAction()
|
public bool ExecuteNextAction()
|
||||||
@@ -519,13 +639,20 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
{
|
{
|
||||||
var gearStats = Gearsets.CalculateGearsetCurrentStats();
|
var gearStats = Gearsets.CalculateGearsetCurrentStats();
|
||||||
|
|
||||||
var container = InventoryManager.Instance()->GetInventoryContainer(InventoryType.EquippedItems);
|
var container = InventoryManager
|
||||||
|
.Instance()
|
||||||
|
->GetInventoryContainer(InventoryType.EquippedItems);
|
||||||
if (container == null)
|
if (container == null)
|
||||||
throw new InvalidOperationException("Could not get inventory container");
|
throw new InvalidOperationException("Could not get inventory container");
|
||||||
|
|
||||||
var gearItems = Gearsets.GetGearsetItems(container);
|
var gearItems = Gearsets.GetGearsetItems(container);
|
||||||
|
|
||||||
var characterStats = Gearsets.CalculateCharacterStats(gearStats, gearItems, RecipeData.ClassJob.GetPlayerLevel(), RecipeData.ClassJob.CanPlayerUseManipulation());
|
var characterStats = Gearsets.CalculateCharacterStats(
|
||||||
|
gearStats,
|
||||||
|
gearItems,
|
||||||
|
RecipeData.ClassJob.GetPlayerLevel(),
|
||||||
|
RecipeData.ClassJob.CanPlayerUseManipulation()
|
||||||
|
);
|
||||||
if (characterStats != CharacterStats)
|
if (characterStats != CharacterStats)
|
||||||
{
|
{
|
||||||
CharacterStats = characterStats;
|
CharacterStats = characterStats;
|
||||||
@@ -554,8 +681,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
CurrentActionStates = CurrentState.ActionStates;
|
CurrentActionStates = CurrentState.ActionStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshCurrentState() =>
|
private void RefreshCurrentState() => CurrentState = GetCurrentState();
|
||||||
CurrentState = GetCurrentState();
|
|
||||||
|
|
||||||
private SimulationState GetCurrentState()
|
private SimulationState GetCurrentState()
|
||||||
{
|
{
|
||||||
@@ -602,7 +728,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
TrainedPerfection = HasEffect((ushort)EffectType.TrainedPerfection.StatusId()),
|
TrainedPerfection = HasEffect((ushort)EffectType.TrainedPerfection.StatusId()),
|
||||||
HeartAndSoul = HasEffect((ushort)EffectType.HeartAndSoul.StatusId()),
|
HeartAndSoul = HasEffect((ushort)EffectType.HeartAndSoul.StatusId()),
|
||||||
},
|
},
|
||||||
ActionStates = CurrentActionStates
|
ActionStates = CurrentActionStates,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +764,11 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
SolverTask.Start();
|
SolverTask.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int CalculateBestMacroTask(SimulationState state, CancellationToken token, bool hasDelineations)
|
private int CalculateBestMacroTask(
|
||||||
|
SimulationState state,
|
||||||
|
CancellationToken token,
|
||||||
|
bool hasDelineations
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var config = Service.Configuration.SynthHelperSolverConfig;
|
var config = Service.Configuration.SynthHelperSolverConfig;
|
||||||
var canUseDelineations = !Service.Configuration.CheckDelineations || hasDelineations;
|
var canUseDelineations = !Service.Configuration.CheckDelineations || hasDelineations;
|
||||||
@@ -663,12 +793,17 @@ public sealed unsafe class SynthHelper : Window, IDisposable
|
|||||||
private void EnqueueAction(ActionType action)
|
private void EnqueueAction(ActionType action)
|
||||||
{
|
{
|
||||||
var newSize = Macro.Enqueue(action, Service.Configuration.SynthHelperMaxDisplayCount);
|
var newSize = Macro.Enqueue(action, Service.Configuration.SynthHelperMaxDisplayCount);
|
||||||
if (newSize >= Service.Configuration.SynthHelperStepCount || newSize >= Service.Configuration.SynthHelperMaxDisplayCount)
|
if (
|
||||||
|
newSize >= Service.Configuration.SynthHelperStepCount
|
||||||
|
|| newSize >= Service.Configuration.SynthHelperMaxDisplayCount
|
||||||
|
)
|
||||||
SolverTask?.Cancel();
|
SolverTask?.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Sim CreateSim(in SimulationState state) =>
|
private static Sim CreateSim(in SimulationState state) =>
|
||||||
Service.Configuration.ConditionRandomness ? new Sim() { State = state } : new SimNoRandom() { State = state };
|
Service.Configuration.ConditionRandomness
|
||||||
|
? new Sim() { State = state }
|
||||||
|
: new SimNoRandom() { State = state };
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user