Remove old windows
This commit is contained in:
@@ -25,7 +25,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
public Settings SettingsWindow { get; }
|
public Settings SettingsWindow { get; }
|
||||||
public Craftimizer.Windows.RecipeNote RecipeNoteWindow { get; }
|
public Craftimizer.Windows.RecipeNote RecipeNoteWindow { get; }
|
||||||
public Craft SynthesisWindow { get; }
|
public Craft SynthesisWindow { get; }
|
||||||
public Windows.Simulator? SimulatorWindow { get; set; }
|
|
||||||
|
|
||||||
public Configuration Configuration { get; }
|
public Configuration Configuration { get; }
|
||||||
public Hooks Hooks { get; }
|
public Hooks Hooks { get; }
|
||||||
@@ -56,16 +55,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
Service.PluginInterface.UiBuilder.OpenConfigUi += OpenSettingsWindow;
|
Service.PluginInterface.UiBuilder.OpenConfigUi += OpenSettingsWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenSimulatorWindow(Item item, bool isExpert, SimulationInput input, ClassJob classJob, Macro? macro)
|
|
||||||
{
|
|
||||||
if (SimulatorWindow != null)
|
|
||||||
{
|
|
||||||
SimulatorWindow.IsOpen = false;
|
|
||||||
WindowSystem.RemoveWindow(SimulatorWindow);
|
|
||||||
}
|
|
||||||
SimulatorWindow = new(item, isExpert, input, classJob, macro);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenSettingsWindow()
|
public void OpenSettingsWindow()
|
||||||
{
|
{
|
||||||
SettingsWindow.IsOpen = true;
|
SettingsWindow.IsOpen = true;
|
||||||
|
|||||||
@@ -1,578 +0,0 @@
|
|||||||
using Craftimizer.Plugin.Utils;
|
|
||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Dalamud;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
||||||
using ImGuiNET;
|
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using ActionType = Craftimizer.Simulator.Actions.ActionType;
|
|
||||||
using RecipeNote = Craftimizer.Utils.RecipeNote;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
|
||||||
|
|
||||||
public unsafe class CraftingLog : Window
|
|
||||||
{
|
|
||||||
private const ImGuiWindowFlags WindowFlags = ImGuiWindowFlags.NoDecoration
|
|
||||||
| ImGuiWindowFlags.AlwaysAutoResize
|
|
||||||
| ImGuiWindowFlags.NoSavedSettings
|
|
||||||
| ImGuiWindowFlags.NoFocusOnAppearing
|
|
||||||
| ImGuiWindowFlags.NoNavFocus;
|
|
||||||
|
|
||||||
private static Configuration Config => Service.Configuration;
|
|
||||||
|
|
||||||
private const int LeftSideWidth = 350;
|
|
||||||
|
|
||||||
// If relative, increase stat by Value's % (rounded down), and cap increase to Max
|
|
||||||
// If not relative, increase stat by Value, and ignore Max
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
private record struct FoodStat(bool IsRelative, sbyte Value, short Max, sbyte ValueHQ, short MaxHQ);
|
|
||||||
private sealed record Food(Item Item, string Name, string NameHQ, FoodStat? Craftsmanship, FoodStat? Control, FoodStat? CP);
|
|
||||||
|
|
||||||
private static Food[] FoodItems { get; }
|
|
||||||
private static Food[] MedicineItems { get; }
|
|
||||||
private static Random Random { get; }
|
|
||||||
|
|
||||||
private static RecipeNote RecipeUtils => Service.Plugin.RecipeNote;
|
|
||||||
private ushort OldRecipeId { get; set; }
|
|
||||||
|
|
||||||
// Set in CalculateCharacterStats (in PreDraw)
|
|
||||||
private Gearsets.GearsetItem[] CharacterEquipment { get; set; } = null!;
|
|
||||||
private CharacterStats CharacterStatsNoConsumable { get; set; } = null!;
|
|
||||||
private Gearsets.GearsetStats CharacterConsumableBonus { get; set; }
|
|
||||||
private CharacterStats CharacterStatsConsumable { get; set; } = null!;
|
|
||||||
private CannotCraftReason CharacterCannotCraftReason { get; set; }
|
|
||||||
private SimulationInput CharacterSimulationInput { get; set; } = null!;
|
|
||||||
|
|
||||||
// Set in UI
|
|
||||||
private int QualityNotches { get; set; }
|
|
||||||
private int StartingQuality =>
|
|
||||||
RecipeUtils.HQIngredientCount == 0 ?
|
|
||||||
0 :
|
|
||||||
(int)((float)QualityNotches * RecipeUtils.MaxStartingQuality / RecipeUtils.HQIngredientCount);
|
|
||||||
|
|
||||||
private Food? SelectedFood { get; set; }
|
|
||||||
private bool SelectedFoodHQ { get; set; }
|
|
||||||
|
|
||||||
private Food? SelectedMedicine { get; set; }
|
|
||||||
private bool SelectedMedicineHQ { get; set; }
|
|
||||||
|
|
||||||
static CraftingLog()
|
|
||||||
{
|
|
||||||
var foods = new List<Food>();
|
|
||||||
var medicines = new List<Food>();
|
|
||||||
foreach (var item in LuminaSheets.ItemSheet)
|
|
||||||
{
|
|
||||||
var isFood = item.ItemUICategory.Row == 46;
|
|
||||||
var isMedicine = item.ItemUICategory.Row == 44;
|
|
||||||
if (!isFood && !isMedicine)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (item.ItemAction.Value == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!(item.ItemAction.Value.Type is 844 or 845 or 846))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var itemFood = LuminaSheets.ItemFoodSheet.GetRow(item.ItemAction.Value.Data[1]);
|
|
||||||
if (itemFood == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
FoodStat? craftsmanship = null, control = null, cp = null;
|
|
||||||
foreach (var stat in itemFood.UnkData1)
|
|
||||||
{
|
|
||||||
if (stat.BaseParam == 0)
|
|
||||||
continue;
|
|
||||||
var foodStat = new FoodStat(stat.IsRelative, stat.Value, stat.Max, stat.ValueHQ, stat.MaxHQ);
|
|
||||||
switch (stat.BaseParam)
|
|
||||||
{
|
|
||||||
case Gearsets.ParamCraftsmanship: craftsmanship = foodStat; break;
|
|
||||||
case Gearsets.ParamControl: control = foodStat; break;
|
|
||||||
case Gearsets.ParamCP: cp = foodStat; break;
|
|
||||||
default: continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (craftsmanship != null || control != null || cp != null)
|
|
||||||
{
|
|
||||||
var name = item.Name.ToDalamudString().TextValue ?? $"Unknown ({item.RowId})";
|
|
||||||
var food = new Food(item, name, $"{name} (HQ)", craftsmanship, control, cp);
|
|
||||||
if (isFood)
|
|
||||||
foods.Add(food);
|
|
||||||
if (isMedicine)
|
|
||||||
medicines.Add(food);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foods.Sort((a, b) => b.Item.LevelItem.Row.CompareTo(a.Item.LevelItem.Row));
|
|
||||||
medicines.Sort((a, b) => b.Item.LevelItem.Row.CompareTo(a.Item.LevelItem.Row));
|
|
||||||
FoodItems = foods.ToArray();
|
|
||||||
MedicineItems = medicines.ToArray();
|
|
||||||
|
|
||||||
Random = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CraftingLog() : base("Craftimizer RecipeNoteHelper", WindowFlags, true)
|
|
||||||
{
|
|
||||||
Service.WindowSystem.AddWindow(this);
|
|
||||||
|
|
||||||
IsOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateCharacterStats()
|
|
||||||
{
|
|
||||||
var container = InventoryManager.Instance()->GetInventoryContainer(InventoryType.EquippedItems);
|
|
||||||
if (container == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CharacterEquipment = Gearsets.GetGearsetItems(container);
|
|
||||||
CharacterStatsNoConsumable = Gearsets.CalculateCharacterStats(CharacterEquipment, RecipeUtils.CharacterLevel, RecipeUtils.CanUseManipulation);
|
|
||||||
CharacterConsumableBonus = CalculateConsumableBonus(CharacterStatsNoConsumable);
|
|
||||||
CharacterStatsConsumable = CharacterStatsNoConsumable with
|
|
||||||
{
|
|
||||||
Craftsmanship = CharacterStatsNoConsumable.Craftsmanship + CharacterConsumableBonus.Craftsmanship,
|
|
||||||
Control = CharacterStatsNoConsumable.Control + CharacterConsumableBonus.Control,
|
|
||||||
CP = CharacterStatsNoConsumable.CP + CharacterConsumableBonus.CP,
|
|
||||||
};
|
|
||||||
CharacterCannotCraftReason = Config.OverrideUncraftability ? CannotCraftReason.OK : CanCraftRecipe(CharacterEquipment, CharacterStatsConsumable);
|
|
||||||
|
|
||||||
CharacterSimulationInput = new(CharacterStatsConsumable, RecipeUtils.Info, StartingQuality, Random);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
ImGui.BeginTable("craftlog", 2, ImGuiTableFlags.BordersInnerV);
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, LeftSideWidth);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawCraftInfo();
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawGearsets();
|
|
||||||
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCraftInfo()
|
|
||||||
{
|
|
||||||
ImGui.BeginTable("craftinfo", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchSame);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawRecipeInfo();
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawCharacterInfo();
|
|
||||||
ImGui.EndTable();
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
DrawCraftParameters();
|
|
||||||
DrawMacros();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawRecipeInfo()
|
|
||||||
{
|
|
||||||
var s = new StringBuilder();
|
|
||||||
s.AppendLine($"{RecipeUtils.ClassJob.GetName()} {new string('★', RecipeUtils.Table.Stars)}");
|
|
||||||
s.AppendLine($"Level {RecipeUtils.Table.ClassJobLevel} (RLvl {RecipeUtils.Info.RLvl})");
|
|
||||||
s.AppendLine($"Durability: {RecipeUtils.Info.MaxDurability}");
|
|
||||||
s.AppendLine($"Progress: {RecipeUtils.Info.MaxProgress}");
|
|
||||||
s.AppendLine($"Quality: {RecipeUtils.Info.MaxQuality}");
|
|
||||||
ImGui.Text(s.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCharacterInfo()
|
|
||||||
{
|
|
||||||
if (CharacterCannotCraftReason != CannotCraftReason.OK)
|
|
||||||
{
|
|
||||||
ImGui.TextWrapped(GetCannotCraftReasonText(CharacterCannotCraftReason));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Text(GetCharacterStatsText(CharacterStatsConsumable));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCraftParameters()
|
|
||||||
{
|
|
||||||
ImGui.BeginDisabled(RecipeUtils.HQIngredientCount == 0);
|
|
||||||
var qualityNotches = QualityNotches;
|
|
||||||
ImGui.SetNextItemWidth(LeftSideWidth - 115);
|
|
||||||
if (ImGui.SliderInt("Starting Quality", ref qualityNotches, 0, RecipeUtils.HQIngredientCount, StartingQuality.ToString(), ImGuiSliderFlags.NoInput | ImGuiSliderFlags.AlwaysClamp))
|
|
||||||
QualityNotches = qualityNotches;
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
ImGui.BeginTable("craftfood", 2, ImGuiTableFlags.BordersInnerV);
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, LeftSideWidth - 120);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
|
|
||||||
if (ImGui.BeginCombo("Food", SelectedFood != null ? (SelectedFoodHQ ? SelectedFood.NameHQ : SelectedFood.Name) : "None"))
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable("None", SelectedFood == null))
|
|
||||||
{
|
|
||||||
SelectedFood = null;
|
|
||||||
SelectedFoodHQ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var food in FoodItems)
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(food.Name, food == SelectedFood && !SelectedFoodHQ))
|
|
||||||
{
|
|
||||||
SelectedFood = food;
|
|
||||||
SelectedFoodHQ = false;
|
|
||||||
}
|
|
||||||
else if (ImGui.Selectable($"{food.Name} (HQ)", food == SelectedFood && SelectedFoodHQ))
|
|
||||||
{
|
|
||||||
SelectedFood = food;
|
|
||||||
SelectedFoodHQ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.BeginCombo("Medicine", SelectedMedicine != null ? (SelectedMedicineHQ ? SelectedMedicine.NameHQ : SelectedMedicine.Name) : "None"))
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable("None", SelectedMedicine == null))
|
|
||||||
{
|
|
||||||
SelectedMedicine = null;
|
|
||||||
SelectedMedicineHQ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var food in MedicineItems)
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(food.Name, food == SelectedMedicine && !SelectedMedicineHQ))
|
|
||||||
{
|
|
||||||
SelectedMedicine = food;
|
|
||||||
SelectedMedicineHQ = false;
|
|
||||||
}
|
|
||||||
else if (ImGui.Selectable($"{food.Name} (HQ)", food == SelectedMedicine && SelectedMedicineHQ))
|
|
||||||
{
|
|
||||||
SelectedMedicine = food;
|
|
||||||
SelectedMedicineHQ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
|
|
||||||
var s = new StringBuilder();
|
|
||||||
s.AppendLine($"+{CharacterConsumableBonus.Craftsmanship} Craftsmanship");
|
|
||||||
s.AppendLine($"+{CharacterConsumableBonus.Control} Control");
|
|
||||||
s.AppendLine($"+{CharacterConsumableBonus.CP} CP");
|
|
||||||
ImGui.Text(s.ToString());
|
|
||||||
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawMacros()
|
|
||||||
{
|
|
||||||
var padding = ImGui.GetStyle().FramePadding;
|
|
||||||
|
|
||||||
var fontSize = ImGui.GetFontSize();
|
|
||||||
var height = fontSize + (padding.Y * 2);
|
|
||||||
var width = ImGui.GetContentRegionAvail().X;
|
|
||||||
var size = new Vector2(width, height);
|
|
||||||
var infoColWidth = Simulator.TooltipProgressBarSize.X;
|
|
||||||
var infoButtonCount = 3;
|
|
||||||
var infoButtonWidth = (infoColWidth - ImGui.GetStyle().ItemSpacing.X * (infoButtonCount - 1)) / infoButtonCount;
|
|
||||||
var infoButtonSize = new Vector2(infoButtonWidth, height);
|
|
||||||
var actionColWidth = width - infoColWidth - ImGui.GetStyle().FramePadding.X * 2;
|
|
||||||
var actionCount = 6;
|
|
||||||
var actionSize = new Vector2((actionColWidth - (ImGui.GetStyle().ItemSpacing.X * (actionCount - 1))) / actionCount);
|
|
||||||
|
|
||||||
if (ImGui.Button("Open Simulator", size))
|
|
||||||
OpenSimulatorWindow(null);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.Button("Generate a new macro", size);
|
|
||||||
|
|
||||||
ImGui.BeginTable("macrotable", 2, ImGuiTableFlags.BordersInner);
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, infoColWidth);
|
|
||||||
ImGui.TableSetupColumn("");
|
|
||||||
var simulation = new SimulatorNoRandom(new(CharacterSimulationInput));
|
|
||||||
for (var i = 0; i < Config.Macros.Count; ++i)
|
|
||||||
{
|
|
||||||
var macro = Config.Macros[i];
|
|
||||||
ImGui.PushID(i);
|
|
||||||
ImGui.TableNextRow();
|
|
||||||
|
|
||||||
SimulationState? state = null;
|
|
||||||
if (CharacterCannotCraftReason == CannotCraftReason.OK)
|
|
||||||
{
|
|
||||||
state = new(CharacterSimulationInput);
|
|
||||||
foreach (var action in macro.Actions)
|
|
||||||
(_, state) = simulation.Execute(state.Value, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextWrapped(macro.Name);
|
|
||||||
if (state.HasValue)
|
|
||||||
Simulator.DrawAllProgressTooltips(state!.Value);
|
|
||||||
|
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Copy, infoButtonSize))
|
|
||||||
CopyMacroToClipboard(macro);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip("Copy macro to clipboard\nHold Shift to exclude wait modifiers");
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.ShareSquare, infoButtonSize))
|
|
||||||
OpenSimulatorWindow(macro);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip("Open macro in simulator");
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Trash, infoButtonSize))
|
|
||||||
Config.Macros.RemoveAt(i);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip("Delete macro");
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var j = 0;
|
|
||||||
foreach (var action in macro.Actions)
|
|
||||||
{
|
|
||||||
ImGui.Image(action.GetIcon(RecipeUtils.ClassJob).ImGuiHandle, actionSize);
|
|
||||||
if (j++ % actionCount != actionCount - 1)
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (j == actionCount * 2)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ImGui.Dummy(Vector2.Zero);
|
|
||||||
ImGui.PopID();
|
|
||||||
}
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenSimulatorWindow(Macro? macro)
|
|
||||||
{
|
|
||||||
Service.Plugin.OpenSimulatorWindow(RecipeUtils.Recipe.ItemResult.Value!, RecipeUtils.Recipe.IsExpert, CharacterSimulationInput, RecipeUtils.ClassJob, macro);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetMacroCommand(ActionType action, bool addWaitTimes)
|
|
||||||
{
|
|
||||||
var actionBase = action.Base();
|
|
||||||
if (actionBase is BaseComboAction comboActionBase)
|
|
||||||
return $"{GetMacroCommand(comboActionBase.ActionTypeA, addWaitTimes)}\n{GetMacroCommand(comboActionBase.ActionTypeB, addWaitTimes)}";
|
|
||||||
if (addWaitTimes)
|
|
||||||
return $"/ac \"{action.GetName(RecipeUtils.ClassJob)}\" <wait.{actionBase.MacroWaitTime}>";
|
|
||||||
else
|
|
||||||
return $"/ac \"{action.GetName(RecipeUtils.ClassJob)}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CopyMacroToClipboard(Macro macro)
|
|
||||||
{
|
|
||||||
var s = new StringBuilder();
|
|
||||||
if (ImGui.IsKeyDown(ImGuiKey.ModShift))
|
|
||||||
{
|
|
||||||
foreach (var action in macro.Actions)
|
|
||||||
s.AppendLine(GetMacroCommand(action, false));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var action in macro.Actions)
|
|
||||||
s.AppendLine(GetMacroCommand(action, true));
|
|
||||||
s.AppendLine($"/echo Macro Complete! <se.1>");
|
|
||||||
}
|
|
||||||
ImGui.SetClipboardText(s.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGearsets()
|
|
||||||
{
|
|
||||||
ImGui.Text("Available Gearsets");
|
|
||||||
|
|
||||||
var inst = RaptureGearsetModule.Instance();
|
|
||||||
|
|
||||||
for (var i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
var gearset = inst->EntriesSpan[i];
|
|
||||||
if (gearset.ID != i)
|
|
||||||
continue;
|
|
||||||
if (!gearset.Flags.HasFlag(RaptureGearsetModule.GearsetFlag.Exists))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ClassJobUtils.GetClassJobFromIdx(gearset.ClassJob) != RecipeUtils.ClassJob)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var items = Gearsets.GetGearsetItems(&gearset);
|
|
||||||
var stats = Gearsets.CalculateCharacterStats(items, RecipeUtils.CharacterLevel, RecipeUtils.CanUseManipulation);
|
|
||||||
var gearsetId = gearset.ID + 1;
|
|
||||||
|
|
||||||
ImGuiUtils.BeginGroupPanel($"{SafeMemory.ReadString((nint)gearset.Name, 47)} ({gearsetId})");
|
|
||||||
ImGui.Text(GetCharacterStatsText(stats));
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiComponents.IconButton($"SwapGearset{gearsetId}", FontAwesomeIcon.SyncAlt))
|
|
||||||
Chat.SendMessage($"/gearset change {gearsetId}");
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip($"Swap to gearset {gearsetId}");
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool DrawConditions()
|
|
||||||
{
|
|
||||||
if (!RecipeUtils.HasValidRecipe)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (OldRecipeId != RecipeUtils.RecipeId)
|
|
||||||
QualityNotches = 0;
|
|
||||||
OldRecipeId = RecipeUtils.RecipeId;
|
|
||||||
|
|
||||||
if (RecipeUtils.AddonRecipe == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if RecipeNote addon is visible
|
|
||||||
if (RecipeUtils.AddonRecipe->AtkUnitBase.WindowNode == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if RecipeNote has a visible selected recipe
|
|
||||||
if (!RecipeUtils.AddonRecipe->Unk258->IsVisible)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.DrawConditions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override unsafe void PreDraw()
|
|
||||||
{
|
|
||||||
var addon = RecipeUtils.AddonRecipe;
|
|
||||||
ref var unit = ref addon->AtkUnitBase;
|
|
||||||
var scale = unit.Scale;
|
|
||||||
var pos = new Vector2(unit.X, unit.Y);
|
|
||||||
var size = new Vector2(unit.WindowNode->AtkResNode.Width, unit.WindowNode->AtkResNode.Height) * scale;
|
|
||||||
|
|
||||||
var node = (AtkResNode*)addon->Unk458; // unit.GetNodeById(59);
|
|
||||||
var nodeParent = addon->Unk258; // unit.GetNodeById(57);
|
|
||||||
|
|
||||||
Position = pos + new Vector2(size.X, (nodeParent->Y + node->Y) * scale);
|
|
||||||
SizeConstraints = new WindowSizeConstraints
|
|
||||||
{
|
|
||||||
MinimumSize = new(-1),
|
|
||||||
MaximumSize = new(10000, 10000)
|
|
||||||
};
|
|
||||||
|
|
||||||
CalculateCharacterStats();
|
|
||||||
|
|
||||||
base.PreDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Gearsets.GearsetStats CalculateConsumableBonus(CharacterStats stats)
|
|
||||||
{
|
|
||||||
static int CalculateBonus(int param, bool isHq, FoodStat? stat)
|
|
||||||
{
|
|
||||||
if (stat == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var foodStat = stat.Value;
|
|
||||||
var (value, max) = isHq ? (foodStat.ValueHQ, foodStat.MaxHQ) : (foodStat.Value, foodStat.Max);
|
|
||||||
|
|
||||||
if (!foodStat.IsRelative)
|
|
||||||
return value;
|
|
||||||
|
|
||||||
return Math.Min((int)MathF.Floor((float)value * param), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gearsets.GearsetStats ret = new();
|
|
||||||
|
|
||||||
if (SelectedFood != null)
|
|
||||||
{
|
|
||||||
ret.CP += CalculateBonus(stats.CP, SelectedFoodHQ, SelectedFood.CP);
|
|
||||||
ret.Craftsmanship += CalculateBonus(stats.Craftsmanship, SelectedFoodHQ, SelectedFood.Craftsmanship);
|
|
||||||
ret.Control += CalculateBonus(stats.Control, SelectedFoodHQ, SelectedFood.Control);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedMedicine != null)
|
|
||||||
{
|
|
||||||
ret.CP += CalculateBonus(stats.CP, SelectedMedicineHQ, SelectedMedicine.CP);
|
|
||||||
ret.Craftsmanship += CalculateBonus(stats.Craftsmanship, SelectedMedicineHQ, SelectedMedicine.Craftsmanship);
|
|
||||||
ret.Control += CalculateBonus(stats.Control, SelectedMedicineHQ, SelectedMedicine.Control);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum CannotCraftReason
|
|
||||||
{
|
|
||||||
OK,
|
|
||||||
WrongClassJob,
|
|
||||||
SpecialistRequired,
|
|
||||||
RequiredItem,
|
|
||||||
RequiredStatus,
|
|
||||||
CraftsmanshipTooLow,
|
|
||||||
ControlTooLow,
|
|
||||||
}
|
|
||||||
|
|
||||||
private CannotCraftReason CanCraftRecipe(Gearsets.GearsetItem[] items, CharacterStats stats)
|
|
||||||
{
|
|
||||||
if (ClassJobUtils.GetClassJobFromIdx((byte)Service.ClientState.LocalPlayer!.ClassJob.Id) != RecipeUtils.ClassJob)
|
|
||||||
return CannotCraftReason.WrongClassJob;
|
|
||||||
|
|
||||||
var recipe = RecipeUtils.Recipe;
|
|
||||||
|
|
||||||
if (recipe.IsSpecializationRequired && !stats.IsSpecialist)
|
|
||||||
return CannotCraftReason.SpecialistRequired;
|
|
||||||
|
|
||||||
if (recipe.ItemRequired.Row != 0)
|
|
||||||
{
|
|
||||||
if (recipe.ItemRequired.Value != null)
|
|
||||||
{
|
|
||||||
if (!items.Any(i => Gearsets.IsItem(i, recipe.ItemRequired.Row)))
|
|
||||||
{
|
|
||||||
return CannotCraftReason.RequiredItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipe.StatusRequired.Row != 0)
|
|
||||||
{
|
|
||||||
if (recipe.StatusRequired.Value != null)
|
|
||||||
{
|
|
||||||
if (!Service.ClientState.LocalPlayer.StatusList.Any(s => s.StatusId == recipe.StatusRequired.Row))
|
|
||||||
return CannotCraftReason.RequiredStatus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipe.RequiredCraftsmanship > stats.Craftsmanship)
|
|
||||||
return CannotCraftReason.CraftsmanshipTooLow;
|
|
||||||
|
|
||||||
if (recipe.RequiredControl > stats.Control)
|
|
||||||
return CannotCraftReason.ControlTooLow;
|
|
||||||
|
|
||||||
return CannotCraftReason.OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetCannotCraftReasonText(CannotCraftReason reason) =>
|
|
||||||
reason switch
|
|
||||||
{
|
|
||||||
CannotCraftReason.OK => "You can craft this recipe.",
|
|
||||||
CannotCraftReason.WrongClassJob => "Your current class cannot craft this recipe.",
|
|
||||||
CannotCraftReason.SpecialistRequired => "You must be a specialist to craft this recipe.",
|
|
||||||
CannotCraftReason.RequiredItem => "You do not have the required item to craft this recipe.",
|
|
||||||
CannotCraftReason.RequiredStatus => "You do not have the required status effect to craft this recipe.",
|
|
||||||
CannotCraftReason.CraftsmanshipTooLow => "Your craftsmanship is too low to craft this recipe.",
|
|
||||||
CannotCraftReason.ControlTooLow => "Your control is too low to craft this recipe.",
|
|
||||||
_ => "Unknown reason.",
|
|
||||||
};
|
|
||||||
|
|
||||||
private static string GetCharacterStatsText(CharacterStats stats)
|
|
||||||
{
|
|
||||||
var s = new StringBuilder();
|
|
||||||
s.AppendLine($"Level {stats.Level} (CLvl {stats.CLvl})");
|
|
||||||
s.AppendLine($"Craftsmanship {stats.Craftsmanship}");
|
|
||||||
s.AppendLine($"Control {stats.Control}");
|
|
||||||
s.AppendLine($"CP {stats.CP}");
|
|
||||||
if (stats.IsSpecialist)
|
|
||||||
s.AppendLine($" + Specialist");
|
|
||||||
if (stats.HasSplendorousBuff)
|
|
||||||
s.AppendLine($" + Splendorous Tool");
|
|
||||||
return s.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable
|
|||||||
private IDalamudTextureWrap NoManipulationBadge { get; }
|
private IDalamudTextureWrap NoManipulationBadge { get; }
|
||||||
private GameFontHandle AxisFont { get; }
|
private GameFontHandle AxisFont { get; }
|
||||||
|
|
||||||
public RecipeNote() : base("Craftimizer RecipeNode", WindowFlags, false)
|
public RecipeNote() : base("Craftimizer RecipeNote", WindowFlags, false)
|
||||||
{
|
{
|
||||||
ExpertBadge = Service.IconManager.GetAssemblyTexture("Graphics.expert_badge.png");
|
ExpertBadge = Service.IconManager.GetAssemblyTexture("Graphics.expert_badge.png");
|
||||||
CollectibleBadge = Service.IconManager.GetAssemblyTexture("Graphics.collectible_badge.png");
|
CollectibleBadge = Service.IconManager.GetAssemblyTexture("Graphics.collectible_badge.png");
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using ImGuiNET;
|
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using ClassJob = Craftimizer.Simulator.ClassJob;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
|
||||||
|
|
||||||
public sealed partial class Simulator : Window, IDisposable
|
|
||||||
{
|
|
||||||
private const ImGuiWindowFlags WindowFlags = ImGuiWindowFlags.AlwaysAutoResize;
|
|
||||||
|
|
||||||
private static Configuration Config => Service.Configuration;
|
|
||||||
|
|
||||||
private Item Item { get; }
|
|
||||||
private bool IsExpert { get; }
|
|
||||||
private SimulationInput Input { get; }
|
|
||||||
private ClassJob ClassJob { get; }
|
|
||||||
private Macro? Macro { get; set; }
|
|
||||||
private string MacroName { get; set; }
|
|
||||||
// State is the state of the simulation *after* its corresponding action is executed.
|
|
||||||
private List<(ActionType Action, string Tooltip, ActionResponse Response, SimulationState State)> Actions { get; }
|
|
||||||
private Craftimizer.Simulator.Simulator Sim { get; set; }
|
|
||||||
|
|
||||||
private SimulationState LatestState => Actions.Count == 0 ? new(Input) : Actions[^1].State;
|
|
||||||
|
|
||||||
// Simulator is set by ResetSimulator()
|
|
||||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
||||||
public Simulator(Item item, bool isExpert, SimulationInput input, ClassJob classJob, Macro? macro) : base("Craftimizer Simulator", WindowFlags)
|
|
||||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
||||||
{
|
|
||||||
Service.WindowSystem.AddWindow(this);
|
|
||||||
|
|
||||||
Item = item;
|
|
||||||
IsExpert = isExpert;
|
|
||||||
Input = input;
|
|
||||||
ClassJob = classJob;
|
|
||||||
Macro = macro;
|
|
||||||
MacroName = Macro?.Name ?? $"Macro {Config.Macros.Count + 1}";
|
|
||||||
Actions = new();
|
|
||||||
ResetSimulator();
|
|
||||||
|
|
||||||
IsOpen = true;
|
|
||||||
|
|
||||||
CollapsedCondition = ImGuiCond.Appearing;
|
|
||||||
Collapsed = false;
|
|
||||||
|
|
||||||
SizeCondition = ImGuiCond.Appearing;
|
|
||||||
Size = SizeConstraints?.MinimumSize ?? new(10);
|
|
||||||
|
|
||||||
if (Macro != null)
|
|
||||||
foreach (var action in Macro.Actions)
|
|
||||||
AppendAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResetSimulator()
|
|
||||||
{
|
|
||||||
Sim = Config.CreateSimulator(LatestState);
|
|
||||||
ReexecuteAllActions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
|
||||||
|
|
||||||
public sealed partial class Simulator : Window, IDisposable
|
|
||||||
{
|
|
||||||
private void AppendAction(ActionType action)
|
|
||||||
{
|
|
||||||
OnActionsChanged();
|
|
||||||
|
|
||||||
AppendGeneratedAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AppendGeneratedAction(ActionType action)
|
|
||||||
{
|
|
||||||
var actionBase = action.Base();
|
|
||||||
if (actionBase is BaseComboAction comboActionBase)
|
|
||||||
{
|
|
||||||
AppendGeneratedAction(comboActionBase.ActionTypeA);
|
|
||||||
AppendGeneratedAction(comboActionBase.ActionTypeB);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var tooltip = actionBase.GetTooltip(Sim, false);
|
|
||||||
var (response, state) = Sim.Execute(LatestState, action);
|
|
||||||
Actions.Add((action, tooltip, response, state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveAction(int actionIndex)
|
|
||||||
{
|
|
||||||
OnActionsChanged();
|
|
||||||
|
|
||||||
// Remove action
|
|
||||||
Actions.RemoveAt(actionIndex);
|
|
||||||
|
|
||||||
// Take note of all actions afterwards
|
|
||||||
Span<ActionType> succeedingActions = stackalloc ActionType[Actions.Count - actionIndex];
|
|
||||||
for (var i = 0; i < succeedingActions.Length; i++)
|
|
||||||
succeedingActions[i] = Actions[i + actionIndex].Action;
|
|
||||||
|
|
||||||
// Remove all future actions
|
|
||||||
Actions.RemoveRange(actionIndex, succeedingActions.Length);
|
|
||||||
|
|
||||||
// Re-execute all future actions
|
|
||||||
foreach (var action in succeedingActions)
|
|
||||||
AppendAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InsertAction(int actionIndex, ActionType action)
|
|
||||||
{
|
|
||||||
OnActionsChanged();
|
|
||||||
|
|
||||||
// Take note of all actions afterwards
|
|
||||||
Span<ActionType> succeedingActions = stackalloc ActionType[Actions.Count - actionIndex];
|
|
||||||
for (var i = 0; i < succeedingActions.Length; i++)
|
|
||||||
succeedingActions[i] = Actions[i + actionIndex].Action;
|
|
||||||
|
|
||||||
// Remove all future actions
|
|
||||||
Actions.RemoveRange(actionIndex, succeedingActions.Length);
|
|
||||||
|
|
||||||
// Execute new action
|
|
||||||
AppendAction(action);
|
|
||||||
|
|
||||||
// Re-execute all future actions
|
|
||||||
foreach (var succeededAction in succeedingActions)
|
|
||||||
AppendAction(succeededAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearAllActions()
|
|
||||||
{
|
|
||||||
OnActionsChanged();
|
|
||||||
|
|
||||||
Actions.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReexecuteAllActions()
|
|
||||||
{
|
|
||||||
Span<ActionType> actions = stackalloc ActionType[Actions.Count];
|
|
||||||
for (var i = 0; i < actions.Length; i++)
|
|
||||||
actions[i] = Actions[i].Action;
|
|
||||||
|
|
||||||
Actions.Clear();
|
|
||||||
foreach (var action in actions)
|
|
||||||
AppendAction(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,440 +0,0 @@
|
|||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Internal;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using ImGuiNET;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
|
||||||
|
|
||||||
public sealed partial class Simulator : Window, IDisposable
|
|
||||||
{
|
|
||||||
private const int ActionColumnSize = 260;
|
|
||||||
|
|
||||||
private static readonly Vector2 ProgressBarSize = new(200, 20);
|
|
||||||
private static readonly Vector2 DurabilityBarSize = new(100, 20);
|
|
||||||
private static readonly Vector2 ConditionBarSize = new(20, 20);
|
|
||||||
private static readonly Vector2 ProgressBarSizeOld = new(200, 20);
|
|
||||||
public static readonly Vector2 TooltipProgressBarSize = new(100, 5);
|
|
||||||
|
|
||||||
public static readonly Vector4 ProgressColor = new(0.44f, 0.65f, 0.18f, 1f);
|
|
||||||
public static readonly Vector4 QualityColor = new(0.26f, 0.71f, 0.69f, 1f);
|
|
||||||
public static readonly Vector4 DurabilityColor = new(0.13f, 0.52f, 0.93f, 1f);
|
|
||||||
public static readonly Vector4 HQColor = new(0.592f, 0.863f, 0.376f, 1f);
|
|
||||||
public static readonly Vector4 CPColor = new(0.63f, 0.37f, 0.75f, 1f);
|
|
||||||
|
|
||||||
private static readonly Vector4 BadActionImageTint = new(1f, .5f, .5f, 1f);
|
|
||||||
private static readonly Vector4 BadActionImageColor = new(1f, .3f, .3f, 1f);
|
|
||||||
|
|
||||||
private static readonly Vector4 BadActionTextColor = new(1f, .2f, .2f, 1f);
|
|
||||||
|
|
||||||
private static readonly (ActionCategory Category, ActionType[] Actions)[] SortedActions;
|
|
||||||
|
|
||||||
static Simulator()
|
|
||||||
{
|
|
||||||
SortedActions = Enum.GetValues<ActionType>()
|
|
||||||
.Where(a => a.Category() != ActionCategory.Combo)
|
|
||||||
.GroupBy(a => a.Category())
|
|
||||||
.Select(g => (g.Key, g.OrderBy(a => a.Level()).ToArray()))
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
while (SolverActionQueue.TryDequeue(out var poppedAction))
|
|
||||||
AppendGeneratedAction(poppedAction);
|
|
||||||
|
|
||||||
ImGui.BeginTable("simulatorWindow", 2, ImGuiTableFlags.BordersInnerV);
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, ActionColumnSize);
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthStretch);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawActions();
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawSimulation();
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawActions()
|
|
||||||
{
|
|
||||||
var hideUnlearnedActions = Config.HideUnlearnedActions;
|
|
||||||
if (ImGui.Checkbox("Show only learned actions", ref hideUnlearnedActions))
|
|
||||||
{
|
|
||||||
Config.HideUnlearnedActions = hideUnlearnedActions;
|
|
||||||
Config.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
Sim.SetState(LatestState);
|
|
||||||
|
|
||||||
var actionSize = new Vector2((ActionColumnSize / 5) - ImGui.GetStyle().ItemSpacing.X * (6f / 5));
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero);
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
|
||||||
ImGui.BeginDisabled(!CanModifyActions);
|
|
||||||
|
|
||||||
foreach (var (category, actions) in SortedActions)
|
|
||||||
{
|
|
||||||
var i = 0;
|
|
||||||
ImGuiUtils.BeginGroupPanel(category.GetDisplayName(), ActionColumnSize);
|
|
||||||
foreach (var action in actions)
|
|
||||||
{
|
|
||||||
var baseAction = action.Base();
|
|
||||||
|
|
||||||
var cannotUse = action.Level() > Input.Stats.Level || (action == ActionType.Manipulation && !Input.Stats.CanUseManipulation);
|
|
||||||
if (cannotUse && Config.HideUnlearnedActions)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var shouldNotUse = !baseAction.CanUse(Sim) || Sim.IsComplete;
|
|
||||||
|
|
||||||
ImGui.BeginDisabled(cannotUse);
|
|
||||||
|
|
||||||
if (ImGui.ImageButton(action.GetIcon(ClassJob).ImGuiHandle, actionSize, Vector2.Zero, Vector2.One, 0, default, shouldNotUse ? BadActionImageTint : Vector4.One))
|
|
||||||
AppendAction(action);
|
|
||||||
|
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
||||||
ImGui.SetTooltip($"{action.GetName(ClassJob)}\n{baseAction.GetTooltip(Sim, true)}");
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (++i % 5 != 0)
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
if (i == 0)
|
|
||||||
ImGui.Dummy(actionSize);
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.PopStyleColor(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulation()
|
|
||||||
{
|
|
||||||
var drawParams = CalculateSynthDrawParams();
|
|
||||||
|
|
||||||
DrawSimulationHeader();
|
|
||||||
DrawSimulationBars(drawParams);
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
|
||||||
DrawSimulationEffects(drawParams);
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
|
||||||
DrawSimulationActions(drawParams);
|
|
||||||
var bottom = ImGui.GetContentRegionAvail().Y - ImGui.GetStyle().FramePadding.Y * 2;
|
|
||||||
var buttonHeight = ImGui.GetFrameHeightWithSpacing() * 2 + ImGui.GetFrameHeight();
|
|
||||||
ImGuiHelpers.ScaledDummy(bottom - buttonHeight);
|
|
||||||
DrawSimulationButtons(drawParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulationHeader()
|
|
||||||
{
|
|
||||||
var imageSize = new Vector2(ImGui.GetFontSize() * 2.25f);
|
|
||||||
|
|
||||||
ImGui.Image(Service.IconManager.GetIcon(Item.Icon).ImGuiHandle, imageSize);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString());
|
|
||||||
if (Item.IsCollectable)
|
|
||||||
{
|
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.TextColored(new(0.98f, 0.98f, 0.61f, 1), SeIconChar.Collectible.ToIconString());
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip("Collectable");
|
|
||||||
}
|
|
||||||
if (IsExpert)
|
|
||||||
{
|
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f);
|
|
||||||
// Using ItemLevel icon instead of '◈' because the game fonts hate
|
|
||||||
// me and I can't bother to include a font just for this one icon.
|
|
||||||
ImGui.TextColored(new(0.93f, 0.59f, 0.45f, 1), SeIconChar.ItemLevel.ToIconString());
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip("Expert Recipe");
|
|
||||||
}
|
|
||||||
var availWidth = ImGui.GetContentRegionAvail().X;
|
|
||||||
var text = $"Step {LatestState.StepCount + 1}";
|
|
||||||
var textWidth = ImGui.CalcTextSize(text).X;
|
|
||||||
ImGui.SameLine(availWidth - textWidth);
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.TextUnformatted(text);
|
|
||||||
ImGui.Separator();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulationBars(SynthDrawParams drawParams)
|
|
||||||
{
|
|
||||||
var state = LatestState;
|
|
||||||
|
|
||||||
var (leftColumn, rightColumn, leftText, rightText) = (drawParams.LeftColumn, drawParams.RightColumn, drawParams.LeftText, drawParams.RightText);
|
|
||||||
|
|
||||||
ImGui.BeginTable("simSynth", 2);
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, leftColumn);
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, rightColumn);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
|
|
||||||
DrawSynthProgress("Durability", state.Durability, Input.Recipe.MaxDurability, DurabilityBarSize, DurabilityColor, leftText);
|
|
||||||
|
|
||||||
DrawSynthCircle("Condition", state.Condition.Name(), ConditionBarSize, new Vector4(.35f, .35f, .35f, 0) + state.Condition.GetColor(DateTime.UtcNow.TimeOfDay), DurabilityBarSize, leftText);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip(state.Condition.Description(state.Input.Stats.HasSplendorousBuff));
|
|
||||||
|
|
||||||
if (Item.IsCollectable)
|
|
||||||
{
|
|
||||||
var collectibility = Math.Max(state.Quality / 10, 1);
|
|
||||||
DrawSynthBar("Collectability", collectibility, Input.Recipe.MaxQuality / 10, $"{collectibility}", DurabilityBarSize, HQColor, leftText);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
DrawSynthBar("HQ %", state.HQPercent, 100, $"{state.HQPercent}%", DurabilityBarSize, HQColor, leftText);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
|
|
||||||
DrawSynthProgress("Progress", state.Progress, Input.Recipe.MaxProgress, ProgressBarSize, ProgressColor, rightText);
|
|
||||||
DrawSynthProgress("Quality", state.Quality, Input.Recipe.MaxQuality, ProgressBarSize, QualityColor, rightText);
|
|
||||||
DrawSynthProgress("CP", state.CP, Input.Stats.CP, ProgressBarSize, CPColor, rightText);
|
|
||||||
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulationEffects(SynthDrawParams drawParams)
|
|
||||||
{
|
|
||||||
ImGuiUtils.BeginGroupPanel("Effects", drawParams.Total);
|
|
||||||
|
|
||||||
var effectHeight = ImGui.GetFontSize() * 2f;
|
|
||||||
Vector2 GetEffectSize(IDalamudTextureWrap icon) => new(icon.Width * effectHeight / icon.Height, effectHeight);
|
|
||||||
|
|
||||||
ImGui.Dummy(new(0, effectHeight));
|
|
||||||
ImGui.SameLine(0, 0);
|
|
||||||
foreach (var effect in Enum.GetValues<EffectType>())
|
|
||||||
{
|
|
||||||
var duration = Sim.GetEffectDuration(effect);
|
|
||||||
if (duration == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var strength = Sim.GetEffectStrength(effect);
|
|
||||||
var icon = effect.GetIcon(strength);
|
|
||||||
var iconSize = GetEffectSize(icon);
|
|
||||||
|
|
||||||
ImGui.Image(icon.ImGuiHandle, iconSize);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip(effect.GetTooltip(strength, duration));
|
|
||||||
if (duration != 0)
|
|
||||||
{
|
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (effectHeight - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.Text($"{duration}");
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
ImGui.Dummy(Vector2.Zero);
|
|
||||||
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulationActions(SynthDrawParams drawParams)
|
|
||||||
{
|
|
||||||
ImGuiUtils.BeginGroupPanel("Actions", drawParams.Total);
|
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero);
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
|
||||||
var actionSize = new Vector2((drawParams.Total / 10) - ImGui.GetStyle().ItemSpacing.X * (11f / 10));
|
|
||||||
ImGui.Dummy(new(0, actionSize.Y));
|
|
||||||
ImGui.SameLine(0, 0);
|
|
||||||
for (var i = 0; i < Actions.Count; ++i)
|
|
||||||
{
|
|
||||||
var (action, tooltip, response, state) = Actions[i];
|
|
||||||
ImGui.PushID(i);
|
|
||||||
if (ImGui.ImageButton(action.GetIcon(ClassJob).ImGuiHandle, actionSize, Vector2.Zero, Vector2.One, 0, default, response != ActionResponse.UsedAction ? BadActionImageTint : Vector4.One))
|
|
||||||
if (CanModifyActions)
|
|
||||||
RemoveAction(i);
|
|
||||||
if (CanModifyActions)
|
|
||||||
{
|
|
||||||
if (ImGui.BeginDragDropSource())
|
|
||||||
{
|
|
||||||
unsafe { ImGui.SetDragDropPayload("simulationAction", (nint)(&i), sizeof(int)); }
|
|
||||||
ImGui.ImageButton(Actions[i].Action.GetIcon(ClassJob).ImGuiHandle, actionSize);
|
|
||||||
ImGui.EndDragDropSource();
|
|
||||||
}
|
|
||||||
if (ImGui.BeginDragDropTarget())
|
|
||||||
{
|
|
||||||
var payload = ImGui.AcceptDragDropPayload("simulationAction");
|
|
||||||
bool isValidPayload;
|
|
||||||
unsafe { isValidPayload = payload.NativePtr != null; }
|
|
||||||
if (isValidPayload)
|
|
||||||
{
|
|
||||||
int draggedIdx;
|
|
||||||
unsafe { draggedIdx = *(int*)payload.Data; }
|
|
||||||
var draggedAction = Actions[draggedIdx].Action;
|
|
||||||
RemoveAction(draggedIdx);
|
|
||||||
InsertAction(i, draggedAction);
|
|
||||||
}
|
|
||||||
ImGui.EndDragDropTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
var responseText = response switch
|
|
||||||
{
|
|
||||||
ActionResponse.SimulationComplete => "Recipe Complete",
|
|
||||||
ActionResponse.ActionNotUnlocked => "Action Not Unlocked",
|
|
||||||
ActionResponse.NotEnoughCP => "Not Enough CP",
|
|
||||||
ActionResponse.NoDurability => "No More Durability",
|
|
||||||
ActionResponse.CannotUseAction => "Cannot Use",
|
|
||||||
_ => string.Empty,
|
|
||||||
};
|
|
||||||
if (response != ActionResponse.UsedAction)
|
|
||||||
ImGui.TextColored(BadActionTextColor, responseText);
|
|
||||||
ImGui.Text($"{action.GetName(ClassJob)}\n{tooltip}");
|
|
||||||
DrawAllProgressTooltips(state);
|
|
||||||
if (CanModifyActions)
|
|
||||||
ImGui.Text("Click to Remove\nDrag to Move");
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
ImGui.PopID();
|
|
||||||
if (i % 10 != 9)
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
ImGui.PopStyleColor(3);
|
|
||||||
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulationButtons(SynthDrawParams drawParams)
|
|
||||||
{
|
|
||||||
var totalWidth = drawParams.Total;
|
|
||||||
var halfWidth = (totalWidth - ImGui.GetStyle().ItemSpacing.X) / 2f;
|
|
||||||
var quarterWidth = (halfWidth - ImGui.GetStyle().ItemSpacing.X) / 2f;
|
|
||||||
var halfButtonSize = new Vector2(halfWidth, ImGui.GetFrameHeight());
|
|
||||||
var quarterButtonSize = new Vector2(quarterWidth, ImGui.GetFrameHeight());
|
|
||||||
|
|
||||||
var conditionRandomnessText = "Condition Randomness";
|
|
||||||
var conditionRandomness = Config.ConditionRandomness;
|
|
||||||
ImGui.BeginDisabled(!CanModifyActions);
|
|
||||||
if (ImGui.Checkbox(conditionRandomnessText, ref conditionRandomness))
|
|
||||||
{
|
|
||||||
Config.ConditionRandomness = conditionRandomness;
|
|
||||||
Config.Save();
|
|
||||||
ResetSimulator();
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
||||||
ImGui.SetTooltip("Allows the condition to fluctuate randomly like a real craft.\nTurns off when generating a macro.");
|
|
||||||
|
|
||||||
var labelSize = ImGui.CalcTextSize(conditionRandomnessText);
|
|
||||||
var checkboxWidth = ImGui.GetFrameHeight() + (labelSize.X > 0 ? ImGui.GetStyle().ItemInnerSpacing.X + labelSize.X : 0);
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
var cogWidth = ImGui.CalcTextSize(FontAwesomeIcon.Cog.ToIconString()).X;
|
|
||||||
ImGui.PopFont();
|
|
||||||
ImGui.SameLine(0, totalWidth - ImGui.GetStyle().ItemSpacing.X - checkboxWidth - cogWidth);
|
|
||||||
if (ImGuiComponents.IconButton("simSettingsButton", FontAwesomeIcon.Cog))
|
|
||||||
Service.Plugin.OpenSettingsTab(Settings.TabSimulator);
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
var macroName = MacroName;
|
|
||||||
ImGui.SetNextItemWidth(halfWidth);
|
|
||||||
if (ImGui.InputTextWithHint("", "Macro Name", ref macroName, 64))
|
|
||||||
MacroName = macroName;
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
DrawSimulationGenerateButton(halfButtonSize);
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
ImGui.BeginDisabled(!CanModifyActions);
|
|
||||||
if (Macro != null)
|
|
||||||
{
|
|
||||||
if (ImGui.Button("Save", quarterButtonSize))
|
|
||||||
{
|
|
||||||
Macro.Name = MacroName;
|
|
||||||
Macro.Actions = Actions.Select(a => a.Action).ToList();
|
|
||||||
Config.Save();
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
if (ImGui.Button("Save New", Macro == null ? halfButtonSize : quarterButtonSize))
|
|
||||||
{
|
|
||||||
Macro = new() { Name = MacroName, Actions = Actions.Select(a => a.Action).ToList() };
|
|
||||||
Config.Macros.Add(Macro);
|
|
||||||
Config.Save();
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Reset", halfButtonSize))
|
|
||||||
ClearAllActions();
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSimulationGenerateButton(Vector2 buttonSize)
|
|
||||||
{
|
|
||||||
var state = GenerateSolverState();
|
|
||||||
string buttonText;
|
|
||||||
string tooltipText;
|
|
||||||
bool isEnabled;
|
|
||||||
var taskCompleted = SolverTask?.IsCompleted ?? true;
|
|
||||||
var taskCancelled = SolverTaskToken?.IsCancellationRequested ?? false;
|
|
||||||
if (!taskCompleted)
|
|
||||||
{
|
|
||||||
if (taskCancelled)
|
|
||||||
{
|
|
||||||
buttonText = "Cancelling...";
|
|
||||||
tooltipText = "Cancelling macro generation. This shouldn't take long.";
|
|
||||||
isEnabled = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buttonText = "Cancel";
|
|
||||||
tooltipText = "Cancel macro generation";
|
|
||||||
isEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (SolverActionsChanged)
|
|
||||||
{
|
|
||||||
buttonText = "Generate";
|
|
||||||
tooltipText = "Generate a set of actions to finish the macro.";
|
|
||||||
isEnabled = state.HasValue;
|
|
||||||
if (!isEnabled)
|
|
||||||
tooltipText += "\nMake sure your craft so far is valid (without random condition changes)";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buttonText = "Regenerate";
|
|
||||||
tooltipText = "Retry and regenerate a new set of actions to finish the macro.";
|
|
||||||
isEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.BeginDisabled(!isEnabled);
|
|
||||||
if (ImGui.Button(buttonText, buttonSize))
|
|
||||||
{
|
|
||||||
if (!taskCompleted)
|
|
||||||
{
|
|
||||||
if (!taskCancelled)
|
|
||||||
SolverTaskToken?.Cancel();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (SolverActionsChanged)
|
|
||||||
{
|
|
||||||
if (state.HasValue)
|
|
||||||
SolveMacro(state.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Actions.RemoveRange(SolverInitialActionCount, Actions.Count - SolverInitialActionCount);
|
|
||||||
SolveMacro(GenerateSolverState()!.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
||||||
ImGui.SetTooltip(tooltipText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
using Craftimizer.Simulator;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using ImGuiNET;
|
|
||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
|
||||||
|
|
||||||
public sealed partial class Simulator : Window, IDisposable
|
|
||||||
{
|
|
||||||
private readonly record struct SynthDrawParams
|
|
||||||
{
|
|
||||||
public float LeftColumn { get; init; }
|
|
||||||
public float RightColumn { get; init; }
|
|
||||||
public float LeftText { get; init; }
|
|
||||||
public float RightText { get; init; }
|
|
||||||
public float Total { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private SynthDrawParams CalculateSynthDrawParams()
|
|
||||||
{
|
|
||||||
var sidePadding = ImGui.GetFrameHeight() / 2;
|
|
||||||
var separatorTextWidth = ImGui.CalcTextSize(" / ").X;
|
|
||||||
var itemSpacing = ImGui.GetStyle().ItemSpacing.X;
|
|
||||||
|
|
||||||
var leftDigits = (int)MathF.Floor(MathF.Log10(Input.Recipe.MaxDurability) + 1);
|
|
||||||
var leftTextWidth = ImGui.CalcTextSize(new string('0', leftDigits)).X;
|
|
||||||
var leftWidth = DurabilityBarSize.X + sidePadding + itemSpacing * 2 + separatorTextWidth + leftTextWidth * 2;
|
|
||||||
|
|
||||||
|
|
||||||
var rightDigits = (int)MathF.Floor(MathF.Log10(Math.Max(Math.Max(Input.Recipe.MaxProgress, Input.Recipe.MaxQuality), Input.Stats.CP)) + 1);
|
|
||||||
var rightTextWidth = ImGui.CalcTextSize(new string('0', rightDigits)).X;
|
|
||||||
var rightWidth = ProgressBarSize.X + sidePadding + itemSpacing * 2 + separatorTextWidth + rightTextWidth * 2;
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
LeftColumn = leftWidth,
|
|
||||||
LeftText = leftTextWidth,
|
|
||||||
RightColumn = rightWidth,
|
|
||||||
RightText = rightTextWidth,
|
|
||||||
Total = leftWidth + rightWidth + itemSpacing
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic Progress Bar
|
|
||||||
private static void DrawSynthProgress(string name, int current, int max, Vector2 size, Vector4 color, float textWidth)
|
|
||||||
{
|
|
||||||
ImGuiUtils.BeginGroupPanel(name);
|
|
||||||
|
|
||||||
DrawProgressBar(current, max, size, color);
|
|
||||||
|
|
||||||
var w = ImGui.GetStyle().ItemSpacing.X;
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle().ItemSpacing.Y));
|
|
||||||
|
|
||||||
ImGui.SameLine(0, textWidth - ImGui.CalcTextSize($"{current}").X + w);
|
|
||||||
var adjustedHeight = ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.SetCursorPosY(adjustedHeight);
|
|
||||||
ImGui.TextUnformatted($"{current}");
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.SetCursorPosY(adjustedHeight);
|
|
||||||
ImGui.TextUnformatted(" / ");
|
|
||||||
|
|
||||||
ImGui.SameLine(0, textWidth - ImGui.CalcTextSize($"{max}").X);
|
|
||||||
ImGui.SetCursorPosY(adjustedHeight);
|
|
||||||
ImGui.TextUnformatted($"{max}");
|
|
||||||
|
|
||||||
ImGui.PopStyleVar();
|
|
||||||
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// HQ% / Collectability Bar (has no fractional bar to indicate max)
|
|
||||||
private static void DrawSynthBar(string name, int current, int max, string text, Vector2 size, Vector4 color, float textWidth)
|
|
||||||
{
|
|
||||||
ImGuiUtils.BeginGroupPanel(name);
|
|
||||||
|
|
||||||
DrawProgressBar(current, max, size, color);
|
|
||||||
|
|
||||||
var w = ImGui.GetStyle().ItemSpacing.X;
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle().ItemSpacing.Y));
|
|
||||||
|
|
||||||
var totalWidth = textWidth * 2 + ImGui.CalcTextSize(" / ").X;
|
|
||||||
|
|
||||||
ImGui.SameLine(0, totalWidth - ImGui.CalcTextSize(text).X + w);
|
|
||||||
var adjustedHeight = ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.SetCursorPosY(adjustedHeight);
|
|
||||||
ImGui.TextUnformatted(text);
|
|
||||||
|
|
||||||
ImGui.PopStyleVar();
|
|
||||||
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Condition "Bar" Circle (always 100%, is a circle)
|
|
||||||
private static void DrawSynthCircle(string name, string text, Vector2 size, Vector4 color, Vector2 otherProgressSize, float textWidth)
|
|
||||||
{
|
|
||||||
ImGuiUtils.BeginGroupPanel(name);
|
|
||||||
|
|
||||||
var w = ImGui.GetStyle().ItemSpacing.X;
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle().ItemSpacing.Y));
|
|
||||||
|
|
||||||
var contentWidth = size.X + w + ImGui.CalcTextSize(text).X;
|
|
||||||
var totalWidth = otherProgressSize.X + w + textWidth * 2 + ImGui.CalcTextSize(" / ").X;
|
|
||||||
|
|
||||||
ImGui.Dummy(default);
|
|
||||||
ImGui.SameLine(0, (totalWidth - contentWidth) / 2);
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, Math.Max(size.X, size.Y));
|
|
||||||
DrawProgressBar(1, 1, size, color);
|
|
||||||
ImGui.PopStyleVar();
|
|
||||||
ImGui.SameLine(0, w);
|
|
||||||
var adjustedHeight = ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f);
|
|
||||||
ImGui.SetCursorPosY(adjustedHeight);
|
|
||||||
ImGui.TextUnformatted(text);
|
|
||||||
|
|
||||||
ImGui.PopStyleVar();
|
|
||||||
|
|
||||||
ImGuiUtils.EndGroupPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DrawAllProgressBars(SimulationState state, Vector2 progressBarSize)
|
|
||||||
{
|
|
||||||
DrawProgressBar(state.Progress, state.Input.Recipe.MaxProgress, progressBarSize, ProgressColor);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip($"Progress: {state.Progress} / {state.Input.Recipe.MaxProgress}");
|
|
||||||
DrawProgressBar(state.Quality, state.Input.Recipe.MaxQuality, progressBarSize, QualityColor);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip($"Quality: {state.Quality} / {state.Input.Recipe.MaxQuality}");
|
|
||||||
DrawProgressBar(state.Durability, state.Input.Recipe.MaxDurability, progressBarSize, DurabilityColor);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip($"Durability: {state.Durability} / {state.Input.Recipe.MaxDurability}");
|
|
||||||
DrawProgressBar(state.CP, state.Input.Stats.CP, progressBarSize, CPColor);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip($"CP: {state.CP} / {state.Input.Stats.CP}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DrawAllProgressTooltips(SimulationState state) =>
|
|
||||||
DrawAllProgressBars(state, TooltipProgressBarSize);
|
|
||||||
|
|
||||||
private static void DrawProgressBar(int progress, int maxProgress, Vector2 size, Vector4 color, string overlay = "")
|
|
||||||
{
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.PlotHistogram, color);
|
|
||||||
ImGui.ProgressBar(Math.Clamp((float)progress / maxProgress, 0f, 1f), size, overlay);
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
using Craftimizer.Simulator;
|
|
||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Craftimizer.Utils;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Craftimizer.Plugin.Windows;
|
|
||||||
|
|
||||||
public sealed partial class Simulator : Window, IDisposable
|
|
||||||
{
|
|
||||||
private Solver.Solver? SolverTask { get; set; }
|
|
||||||
private CancellationTokenSource? SolverTaskToken { get; set; }
|
|
||||||
private ConcurrentQueue<ActionType> SolverActionQueue { get; } = new();
|
|
||||||
private int SolverInitialActionCount { get; set; }
|
|
||||||
private bool SolverActionsChanged { get; set; } = true;
|
|
||||||
|
|
||||||
private bool CanModifyActions => SolverTask?.IsCompleted ?? true;
|
|
||||||
|
|
||||||
private void OnActionsChanged()
|
|
||||||
{
|
|
||||||
SolverActionsChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SimulationState? GenerateSolverState()
|
|
||||||
{
|
|
||||||
if (Sim is SimulatorNoRandom)
|
|
||||||
{
|
|
||||||
if (!Actions.Exists(a => a.Response != ActionResponse.UsedAction))
|
|
||||||
return LatestState;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = new SimulationState(Input);
|
|
||||||
if (Actions.Count != 0)
|
|
||||||
{
|
|
||||||
var tmpSim = new SimulatorNoRandom(ret);
|
|
||||||
foreach (var action in Actions)
|
|
||||||
{
|
|
||||||
(var resp, ret) = tmpSim.Execute(ret, action.Action);
|
|
||||||
if (resp != ActionResponse.UsedAction)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopSolveMacro()
|
|
||||||
{
|
|
||||||
if (SolverTask == null || SolverTaskToken == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!SolverTask.IsCompleted)
|
|
||||||
SolverTaskToken.Cancel();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SolverTaskToken.Dispose();
|
|
||||||
SolverTask.Dispose();
|
|
||||||
|
|
||||||
SolverTask = null;
|
|
||||||
SolverTaskToken = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SolveMacro(SimulationState solverState)
|
|
||||||
{
|
|
||||||
StopSolveMacro();
|
|
||||||
|
|
||||||
// Prevents the quality bar from being unfair between solves
|
|
||||||
if (Config.ConditionRandomness)
|
|
||||||
{
|
|
||||||
Config.ConditionRandomness = false;
|
|
||||||
Config.Save();
|
|
||||||
|
|
||||||
ResetSimulator();
|
|
||||||
}
|
|
||||||
|
|
||||||
SolverActionsChanged = false;
|
|
||||||
|
|
||||||
SolverActionQueue.Clear();
|
|
||||||
|
|
||||||
SolverInitialActionCount = Actions.Count;
|
|
||||||
SolverTaskToken = new();
|
|
||||||
SolverTask = new(Config.SimulatorSolverConfig, solverState) { Token = SolverTaskToken.Token };
|
|
||||||
SolverTask.OnLog += s => Log.Debug(s);
|
|
||||||
SolverTask.OnNewAction += SolverActionQueue.Enqueue;
|
|
||||||
SolverTask.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
StopSolveMacro();
|
|
||||||
SolverTaskToken?.Cancel();
|
|
||||||
SolverTask?.TryWait();
|
|
||||||
SolverTask?.Dispose();
|
|
||||||
SolverTaskToken?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user