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 GeneralConfiguration General { get; } = new();
public NotificationConfiguration Notifications { get; } = new();
internal sealed class GeneralConfiguration
{
+ public ECombatModule CombatModule { get; set; } = ECombatModule.BossMod;
public uint MountId { get; set; } = 71;
public GrandCompany GrandCompany { get; set; } = GrandCompany.None;
public bool HideInAllInstances { get; set; } = true;
public bool NeverFly { get; set; }
public bool AdditionalStatusInformation { get; set; }
}
+
+ internal enum ECombatModule
+ {
+ None,
+ BossMod,
+ RotationSolverReborn,
+ }
}
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 readonly ILogger<BossModModule> _logger = logger;
- private readonly MovementController _movementController = movementController;
- private readonly IClientState _clientState = clientState;
- private readonly ICallGateSubscriber<string, string?> _getPreset = pluginInterface.GetIpcSubscriber<string, string?>($"{Name}.Presets.Get");
- private readonly ICallGateSubscriber<string, bool, bool> _createPreset = pluginInterface.GetIpcSubscriber<string, bool, bool>($"{Name}.Presets.Create");
- private readonly ICallGateSubscriber<string, bool> _setPreset = pluginInterface.GetIpcSubscriber<string, bool>($"{Name}.Presets.SetActive");
- private readonly ICallGateSubscriber<bool> _clearPreset = pluginInterface.GetIpcSubscriber<bool>($"{Name}.Presets.ClearActive");
+ private readonly ILogger<BossModModule> _logger;
+ private readonly MovementController _movementController;
+ private readonly IClientState _clientState;
+ private readonly Configuration _configuration;
+ private readonly ICallGateSubscriber<string, string?> _getPreset;
+ private readonly ICallGateSubscriber<string, bool, bool> _createPreset;
+ private readonly ICallGateSubscriber<string, bool> _setPreset;
+ private readonly ICallGateSubscriber<bool> _clearPreset;
private static Stream Preset => typeof(BossModModule).Assembly.GetManifestResourceStream("Questionable.Controller.CombatModules.BossModPreset")!;
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)
{
+ if (_configuration.General.CombatModule != Configuration.ECombatModule.BossMod)
+ return false;
+
try
{
return _getPreset.HasFunction;
private readonly ILogger<RotationSolverRebornModule> _logger;
private readonly MovementController _movementController;
private readonly IClientState _clientState;
+ private readonly Configuration _configuration;
private readonly ICallGateSubscriber<string, object> _test;
private readonly ICallGateSubscriber<StateCommandType, object> _changeOperationMode;
private DateTime _lastDistanceCheck = DateTime.MinValue;
public RotationSolverRebornModule(ILogger<RotationSolverRebornModule> logger, MovementController movementController,
- IClientState clientState, IDalamudPluginInterface pluginInterface)
+ IClientState clientState, IDalamudPluginInterface pluginInterface, Configuration configuration)
{
_logger = logger;
_movementController = movementController;
_clientState = clientState;
+ _configuration = configuration;
_test = pluginInterface.GetIpcSubscriber<string, object>("RotationSolverReborn.Test");
_changeOperationMode =
pluginInterface.GetIpcSubscriber<StateCommandType, object>("RotationSolverReborn.ChangeOperatingMode");
public bool CanHandleFight(CombatController.CombatData combatData)
{
+ if (_configuration.General.CombatModule != Configuration.ECombatModule.RotationSolverReborn)
+ return false;
+
try
{
_test.InvokeAction("Validate RSR is callable from Questionable");
private readonly uint[] _mountIds;
private readonly string[] _mountNames;
+ private readonly string[] _combatModuleNames = ["None", "Boss Mod (VBM)", "Rotation Solver Reborn"];
private readonly string[] _grandCompanyNames =
["None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames"];
using var tab = ImRaii.TabItem("General");
if (!tab)
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);
if (selectedMount == -1)
{
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 Configuration _configuration;
_pluginInterface = pluginInterface;
_uiUtils = uiUtils;
_logger = logger;
-
_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)",
"Automaton",
"""
ImGui.Separator();
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:");
using (ImRaii.PushIndent())
{
private bool DrawPlugin(PluginInfo plugin, float checklistPadding)
{
- bool isInstalled = IsPluginInstalled(plugin);
using (ImRaii.PushId("plugin_" + plugin.DisplayName))
{
+ bool isInstalled = IsPluginInstalled(plugin);
_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);
- 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"))
- Util.OpenLink(plugin.WebsiteUri.ToString());
+ ImGui.Spacing();
- ImGui.SameLine();
- if (plugin.DalamudRepositoryUri != null)
- {
- if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository"))
- Util.OpenLink(plugin.DalamudRepositoryUri.ToString());
- }
- else
- {
- ImGui.AlignTextToFramePadding();
- ImGuiComponents.HelpMarker("Available on official Dalamud Repository");
- }
+ if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Globe, "Open Website"))
+ Util.OpenLink(plugin.WebsiteUri.ToString());
+
+ ImGui.SameLine();
+ if (plugin.DalamudRepositoryUri != null)
+ {
+ if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository"))
+ Util.OpenLink(plugin.DalamudRepositoryUri.ToString());
+ }
+ else
+ {
+ ImGui.AlignTextToFramePadding();
+ ImGuiComponents.HelpMarker("Available on official Dalamud Repository");
}
}
-
- return isInstalled;
}
private bool IsPluginInstalled(PluginInfo pluginInfo)
QuickAccessButtonsComponent quickAccessButtonsComponent,
RemainingTasksComponent remainingTasksComponent,
IFramework framework,
- InteractionUiController interactionUiController)
+ InteractionUiController interactionUiController,
+ ConfigWindow configWindow)
: base($"Questionable v{PluginVersion.ToString(2)}###Questionable",
ImGuiWindowFlags.AlwaysAutoResize)
{
#endif
SizeConstraints = new WindowSizeConstraints
{
- MinimumSize = new Vector2(230, 30),
+ MinimumSize = new Vector2(240, 30),
MaximumSize = default
};
RespectCloseHotkey = false;
};
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;
_quickAccessButtonsComponent.Reload += OnReload;
}
public bool ChecklistItem(string text, Vector4 color, FontAwesomeIcon icon, float extraPadding = 0)
{
- // ReSharper disable once UnusedVariable
- using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
+ using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
ImGui.TextColored(color, icon.ToIconString());
}