Add config option to switch between VBM/RSR
This commit is contained in:
parent
471cc563f4
commit
118a5948a5
@ -7,9 +7,9 @@ namespace Questionable;
|
|||||||
|
|
||||||
internal sealed class Configuration : IPluginConfiguration
|
internal sealed class Configuration : IPluginConfiguration
|
||||||
{
|
{
|
||||||
public const int PluginSetupVersion = 2;
|
public const int PluginSetupVersion = 3;
|
||||||
|
|
||||||
public int Version { get; set; } =1 ;
|
public int Version { get; set; } = 1;
|
||||||
public int PluginSetupCompleteVersion { get; set; }
|
public int PluginSetupCompleteVersion { get; set; }
|
||||||
public GeneralConfiguration General { get; } = new();
|
public GeneralConfiguration General { get; } = new();
|
||||||
public NotificationConfiguration Notifications { get; } = new();
|
public NotificationConfiguration Notifications { get; } = new();
|
||||||
@ -23,6 +23,7 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
|
|
||||||
internal sealed class GeneralConfiguration
|
internal sealed class GeneralConfiguration
|
||||||
{
|
{
|
||||||
|
public ECombatModule CombatModule { get; set; } = ECombatModule.BossMod;
|
||||||
public uint MountId { get; set; } = 71;
|
public uint MountId { get; set; } = 71;
|
||||||
public GrandCompany GrandCompany { get; set; } = GrandCompany.None;
|
public GrandCompany GrandCompany { get; set; } = GrandCompany.None;
|
||||||
public bool HideInAllInstances { get; set; } = true;
|
public bool HideInAllInstances { get; set; } = true;
|
||||||
@ -45,4 +46,11 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
public bool NeverFly { get; set; }
|
public bool NeverFly { get; set; }
|
||||||
public bool AdditionalStatusInformation { get; set; }
|
public bool AdditionalStatusInformation { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum ECombatModule
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
BossMod,
|
||||||
|
RotationSolverReborn,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,22 +12,44 @@ using System.Numerics;
|
|||||||
|
|
||||||
namespace Questionable.Controller.CombatModules;
|
namespace Questionable.Controller.CombatModules;
|
||||||
|
|
||||||
internal sealed class BossModModule(ILogger<BossModModule> logger, MovementController movementController, IClientState clientState, IDalamudPluginInterface pluginInterface) : ICombatModule, IDisposable
|
internal sealed class BossModModule : ICombatModule, IDisposable
|
||||||
{
|
{
|
||||||
private const string Name = "BossMod";
|
private const string Name = "BossMod";
|
||||||
private readonly ILogger<BossModModule> _logger = logger;
|
private readonly ILogger<BossModModule> _logger;
|
||||||
private readonly MovementController _movementController = movementController;
|
private readonly MovementController _movementController;
|
||||||
private readonly IClientState _clientState = clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly ICallGateSubscriber<string, string?> _getPreset = pluginInterface.GetIpcSubscriber<string, string?>($"{Name}.Presets.Get");
|
private readonly Configuration _configuration;
|
||||||
private readonly ICallGateSubscriber<string, bool, bool> _createPreset = pluginInterface.GetIpcSubscriber<string, bool, bool>($"{Name}.Presets.Create");
|
private readonly ICallGateSubscriber<string, string?> _getPreset;
|
||||||
private readonly ICallGateSubscriber<string, bool> _setPreset = pluginInterface.GetIpcSubscriber<string, bool>($"{Name}.Presets.SetActive");
|
private readonly ICallGateSubscriber<string, bool, bool> _createPreset;
|
||||||
private readonly ICallGateSubscriber<bool> _clearPreset = pluginInterface.GetIpcSubscriber<bool>($"{Name}.Presets.ClearActive");
|
private readonly ICallGateSubscriber<string, bool> _setPreset;
|
||||||
|
private readonly ICallGateSubscriber<bool> _clearPreset;
|
||||||
|
|
||||||
private static Stream Preset => typeof(BossModModule).Assembly.GetManifestResourceStream("Questionable.Controller.CombatModules.BossModPreset")!;
|
private static Stream Preset => typeof(BossModModule).Assembly.GetManifestResourceStream("Questionable.Controller.CombatModules.BossModPreset")!;
|
||||||
private DateTime _lastDistanceCheck = DateTime.MinValue;
|
private DateTime _lastDistanceCheck = DateTime.MinValue;
|
||||||
|
|
||||||
|
public BossModModule(
|
||||||
|
ILogger<BossModModule> logger,
|
||||||
|
MovementController movementController,
|
||||||
|
IClientState clientState,
|
||||||
|
IDalamudPluginInterface pluginInterface,
|
||||||
|
Configuration configuration)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_movementController = movementController;
|
||||||
|
_clientState = clientState;
|
||||||
|
_configuration = configuration;
|
||||||
|
|
||||||
|
_getPreset = pluginInterface.GetIpcSubscriber<string, string?>($"{Name}.Presets.Get");
|
||||||
|
_createPreset = pluginInterface.GetIpcSubscriber<string, bool, bool>($"{Name}.Presets.Create");
|
||||||
|
_setPreset = pluginInterface.GetIpcSubscriber<string, bool>($"{Name}.Presets.SetActive");
|
||||||
|
_clearPreset = pluginInterface.GetIpcSubscriber<bool>($"{Name}.Presets.ClearActive");
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanHandleFight(CombatController.CombatData combatData)
|
public bool CanHandleFight(CombatController.CombatData combatData)
|
||||||
{
|
{
|
||||||
|
if (_configuration.General.CombatModule != Configuration.ECombatModule.BossMod)
|
||||||
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _getPreset.HasFunction;
|
return _getPreset.HasFunction;
|
||||||
|
@ -16,17 +16,19 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
|
|||||||
private readonly ILogger<RotationSolverRebornModule> _logger;
|
private readonly ILogger<RotationSolverRebornModule> _logger;
|
||||||
private readonly MovementController _movementController;
|
private readonly MovementController _movementController;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
|
private readonly Configuration _configuration;
|
||||||
private readonly ICallGateSubscriber<string, object> _test;
|
private readonly ICallGateSubscriber<string, object> _test;
|
||||||
private readonly ICallGateSubscriber<StateCommandType, object> _changeOperationMode;
|
private readonly ICallGateSubscriber<StateCommandType, object> _changeOperationMode;
|
||||||
|
|
||||||
private DateTime _lastDistanceCheck = DateTime.MinValue;
|
private DateTime _lastDistanceCheck = DateTime.MinValue;
|
||||||
|
|
||||||
public RotationSolverRebornModule(ILogger<RotationSolverRebornModule> logger, MovementController movementController,
|
public RotationSolverRebornModule(ILogger<RotationSolverRebornModule> logger, MovementController movementController,
|
||||||
IClientState clientState, IDalamudPluginInterface pluginInterface)
|
IClientState clientState, IDalamudPluginInterface pluginInterface, Configuration configuration)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_movementController = movementController;
|
_movementController = movementController;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
|
_configuration = configuration;
|
||||||
_test = pluginInterface.GetIpcSubscriber<string, object>("RotationSolverReborn.Test");
|
_test = pluginInterface.GetIpcSubscriber<string, object>("RotationSolverReborn.Test");
|
||||||
_changeOperationMode =
|
_changeOperationMode =
|
||||||
pluginInterface.GetIpcSubscriber<StateCommandType, object>("RotationSolverReborn.ChangeOperatingMode");
|
pluginInterface.GetIpcSubscriber<StateCommandType, object>("RotationSolverReborn.ChangeOperatingMode");
|
||||||
@ -34,6 +36,9 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
|
|||||||
|
|
||||||
public bool CanHandleFight(CombatController.CombatData combatData)
|
public bool CanHandleFight(CombatController.CombatData combatData)
|
||||||
{
|
{
|
||||||
|
if (_configuration.General.CombatModule != Configuration.ECombatModule.RotationSolverReborn)
|
||||||
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_test.InvokeAction("Validate RSR is callable from Questionable");
|
_test.InvokeAction("Validate RSR is callable from Questionable");
|
||||||
|
@ -26,6 +26,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
private readonly uint[] _mountIds;
|
private readonly uint[] _mountIds;
|
||||||
private readonly string[] _mountNames;
|
private readonly string[] _mountNames;
|
||||||
|
|
||||||
|
private readonly string[] _combatModuleNames = ["None", "Boss Mod (VBM)", "Rotation Solver Reborn"];
|
||||||
private readonly string[] _grandCompanyNames =
|
private readonly string[] _grandCompanyNames =
|
||||||
["None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames"];
|
["None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames"];
|
||||||
|
|
||||||
@ -65,6 +66,14 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
using var tab = ImRaii.TabItem("General");
|
using var tab = ImRaii.TabItem("General");
|
||||||
if (!tab)
|
if (!tab)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int selectedCombatModule = (int)_configuration.General.CombatModule;
|
||||||
|
if (ImGui.Combo("Preferred Combat Module", ref selectedCombatModule, _combatModuleNames, _combatModuleNames.Length))
|
||||||
|
{
|
||||||
|
_configuration.General.CombatModule = (Configuration.ECombatModule)selectedCombatModule;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
int selectedMount = Array.FindIndex(_mountIds, x => x == _configuration.General.MountId);
|
int selectedMount = Array.FindIndex(_mountIds, x => x == _configuration.General.MountId);
|
||||||
if (selectedMount == -1)
|
if (selectedMount == -1)
|
||||||
{
|
{
|
||||||
|
@ -44,6 +44,27 @@ internal sealed class OneTimeSetupWindow : LWindow
|
|||||||
new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
|
new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<Configuration.ECombatModule, PluginInfo> CombatPlugins = new Dictionary<Configuration.ECombatModule, PluginInfo>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Configuration.ECombatModule.BossMod,
|
||||||
|
new("Boss Mod (VBM)",
|
||||||
|
"BossMod",
|
||||||
|
string.Empty,
|
||||||
|
new Uri("https://github.com/awgil/ffxiv_bossmod"),
|
||||||
|
new Uri("https://puni.sh/api/repository/veyn"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Configuration.ECombatModule.RotationSolverReborn,
|
||||||
|
new("Rotation Solver Reborn",
|
||||||
|
"RotationSolver",
|
||||||
|
string.Empty,
|
||||||
|
new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"),
|
||||||
|
new Uri(
|
||||||
|
"https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json"))
|
||||||
|
},
|
||||||
|
}.AsReadOnly();
|
||||||
|
|
||||||
private readonly IReadOnlyList<PluginInfo> _recommendedPlugins;
|
private readonly IReadOnlyList<PluginInfo> _recommendedPlugins;
|
||||||
|
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
@ -60,18 +81,8 @@ internal sealed class OneTimeSetupWindow : LWindow
|
|||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_uiUtils = uiUtils;
|
_uiUtils = uiUtils;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
_recommendedPlugins =
|
_recommendedPlugins =
|
||||||
[
|
[
|
||||||
new("Rotation Solver Reborn",
|
|
||||||
"RotationSolver",
|
|
||||||
"""
|
|
||||||
Automatically handles most combat interactions you encounter
|
|
||||||
during quests, including being interrupted by mobs.
|
|
||||||
""",
|
|
||||||
new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"),
|
|
||||||
new Uri(
|
|
||||||
"https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json")),
|
|
||||||
new PluginInfo("CBT (formerly known as Automaton)",
|
new PluginInfo("CBT (formerly known as Automaton)",
|
||||||
"Automaton",
|
"Automaton",
|
||||||
"""
|
"""
|
||||||
@ -120,6 +131,25 @@ internal sealed class OneTimeSetupWindow : LWindow
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGui.Text("Questionable supports multiple rotation/combat plugins, please pick the one\nyou want to use:");
|
||||||
|
|
||||||
|
using (ImRaii.PushIndent())
|
||||||
|
{
|
||||||
|
if (ImGui.RadioButton("No rotation/combat plugin (combat must be done manually)",
|
||||||
|
_configuration.General.CombatModule == Configuration.ECombatModule.None))
|
||||||
|
{
|
||||||
|
_configuration.General.CombatModule = Configuration.ECombatModule.None;
|
||||||
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawCombatPlugin(Configuration.ECombatModule.BossMod, checklistPadding);
|
||||||
|
DrawCombatPlugin(Configuration.ECombatModule.RotationSolverReborn, checklistPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Spacing();
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
ImGui.Text("The following plugins are recommended, but not required:");
|
ImGui.Text("The following plugins are recommended, but not required:");
|
||||||
using (ImRaii.PushIndent())
|
using (ImRaii.PushIndent())
|
||||||
{
|
{
|
||||||
@ -164,39 +194,75 @@ internal sealed class OneTimeSetupWindow : LWindow
|
|||||||
|
|
||||||
private bool DrawPlugin(PluginInfo plugin, float checklistPadding)
|
private bool DrawPlugin(PluginInfo plugin, float checklistPadding)
|
||||||
{
|
{
|
||||||
bool isInstalled = IsPluginInstalled(plugin);
|
|
||||||
using (ImRaii.PushId("plugin_" + plugin.DisplayName))
|
using (ImRaii.PushId("plugin_" + plugin.DisplayName))
|
||||||
{
|
{
|
||||||
|
bool isInstalled = IsPluginInstalled(plugin);
|
||||||
_uiUtils.ChecklistItem(plugin.DisplayName, isInstalled);
|
_uiUtils.ChecklistItem(plugin.DisplayName, isInstalled);
|
||||||
using (ImRaii.PushIndent(checklistPadding))
|
|
||||||
|
DrawPluginDetails(plugin, checklistPadding, isInstalled);
|
||||||
|
return isInstalled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawCombatPlugin(Configuration.ECombatModule combatModule, float checklistPadding)
|
||||||
|
{
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
PluginInfo plugin = CombatPlugins[combatModule];
|
||||||
|
using (ImRaii.PushId("plugin_" + plugin.DisplayName))
|
||||||
|
{
|
||||||
|
bool isInstalled = IsPluginInstalled(plugin);
|
||||||
|
if (ImGui.RadioButton(plugin.DisplayName, _configuration.General.CombatModule == combatModule))
|
||||||
{
|
{
|
||||||
|
_configuration.General.CombatModule = combatModule;
|
||||||
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine(0);
|
||||||
|
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
|
||||||
|
{
|
||||||
|
var iconColor = isInstalled ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||||
|
var icon = isInstalled ? FontAwesomeIcon.Check : FontAwesomeIcon.Times;
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextColored(iconColor, icon.ToIconString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DrawPluginDetails(plugin, checklistPadding, isInstalled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawPluginDetails(PluginInfo plugin, float checklistPadding, bool isInstalled)
|
||||||
|
{
|
||||||
|
using (ImRaii.PushIndent(checklistPadding))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(plugin.Details))
|
||||||
ImGui.TextUnformatted(plugin.Details);
|
ImGui.TextUnformatted(plugin.Details);
|
||||||
if (plugin.DetailsToCheck != null)
|
|
||||||
{
|
|
||||||
foreach (var detail in plugin.DetailsToCheck)
|
|
||||||
_uiUtils.ChecklistItem(detail.DisplayName, isInstalled && detail.Predicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
if (plugin.DetailsToCheck != null)
|
||||||
|
{
|
||||||
|
foreach (var detail in plugin.DetailsToCheck)
|
||||||
|
_uiUtils.ChecklistItem(detail.DisplayName, isInstalled && detail.Predicate());
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Globe, "Open Website"))
|
ImGui.Spacing();
|
||||||
Util.OpenLink(plugin.WebsiteUri.ToString());
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Globe, "Open Website"))
|
||||||
if (plugin.DalamudRepositoryUri != null)
|
Util.OpenLink(plugin.WebsiteUri.ToString());
|
||||||
{
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository"))
|
ImGui.SameLine();
|
||||||
Util.OpenLink(plugin.DalamudRepositoryUri.ToString());
|
if (plugin.DalamudRepositoryUri != null)
|
||||||
}
|
{
|
||||||
else
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository"))
|
||||||
{
|
Util.OpenLink(plugin.DalamudRepositoryUri.ToString());
|
||||||
ImGui.AlignTextToFramePadding();
|
}
|
||||||
ImGuiComponents.HelpMarker("Available on official Dalamud Repository");
|
else
|
||||||
}
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGuiComponents.HelpMarker("Available on official Dalamud Repository");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isInstalled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPluginInstalled(PluginInfo pluginInfo)
|
private bool IsPluginInstalled(PluginInfo pluginInfo)
|
||||||
|
@ -44,7 +44,8 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
|
|||||||
QuickAccessButtonsComponent quickAccessButtonsComponent,
|
QuickAccessButtonsComponent quickAccessButtonsComponent,
|
||||||
RemainingTasksComponent remainingTasksComponent,
|
RemainingTasksComponent remainingTasksComponent,
|
||||||
IFramework framework,
|
IFramework framework,
|
||||||
InteractionUiController interactionUiController)
|
InteractionUiController interactionUiController,
|
||||||
|
ConfigWindow configWindow)
|
||||||
: base($"Questionable v{PluginVersion.ToString(2)}###Questionable",
|
: base($"Questionable v{PluginVersion.ToString(2)}###Questionable",
|
||||||
ImGuiWindowFlags.AlwaysAutoResize)
|
ImGuiWindowFlags.AlwaysAutoResize)
|
||||||
{
|
{
|
||||||
@ -67,7 +68,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
|
|||||||
#endif
|
#endif
|
||||||
SizeConstraints = new WindowSizeConstraints
|
SizeConstraints = new WindowSizeConstraints
|
||||||
{
|
{
|
||||||
MinimumSize = new Vector2(230, 30),
|
MinimumSize = new Vector2(240, 30),
|
||||||
MaximumSize = default
|
MaximumSize = default
|
||||||
};
|
};
|
||||||
RespectCloseHotkey = false;
|
RespectCloseHotkey = false;
|
||||||
@ -87,6 +88,20 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
|
|||||||
};
|
};
|
||||||
TitleBarButtons.Insert(0, _minimizeButton);
|
TitleBarButtons.Insert(0, _minimizeButton);
|
||||||
|
|
||||||
|
TitleBarButtons.Add(new TitleBarButton
|
||||||
|
{
|
||||||
|
Icon = FontAwesomeIcon.Cog,
|
||||||
|
IconOffset = new Vector2(1.5f, 1),
|
||||||
|
Click = _ => configWindow.IsOpen = true,
|
||||||
|
Priority = int.MinValue,
|
||||||
|
ShowTooltip = () =>
|
||||||
|
{
|
||||||
|
ImGui.BeginTooltip();
|
||||||
|
ImGui.Text("Open Configuration");
|
||||||
|
ImGui.EndTooltip();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
_activeQuestComponent.Reload += OnReload;
|
_activeQuestComponent.Reload += OnReload;
|
||||||
_quickAccessButtonsComponent.Reload += OnReload;
|
_quickAccessButtonsComponent.Reload += OnReload;
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,7 @@ internal sealed class UiUtils
|
|||||||
|
|
||||||
public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon, float extraPadding = 0)
|
public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon, float extraPadding = 0)
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedVariable
|
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
|
||||||
using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
|
|
||||||
{
|
{
|
||||||
ImGui.TextColored(color, icon.ToIconString());
|
ImGui.TextColored(color, icon.ToIconString());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user