using System; using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps; namespace Questionable.Controller.CombatModules; internal sealed class WrathComboModule : ICombatModule, IDisposable { public enum ESetResult { Okay = 0, OkayWorking = 1, IpcDisabled = 10, InvalidLease = 11, BlacklistedLease = 12, Duplicate = 13, PlayerNotAvailable = 14, InvalidConfiguration = 15, InvalidValue = 16 } public enum AutoRotationConfigOption { InCombatOnly, DPSRotationMode, HealerRotationMode, FATEPriority, QuestPriority, SingleTargetHPP, AoETargetHPP, SingleTargetRegenHPP, ManageKardia, AutoRez, AutoRezDPSJobs, AutoCleanse, IncludeNPCs, OnlyAttackInCombat, OrbwalkerIntegration, AutoRezOutOfParty, DPSAoETargets, SingleTargetExcogHPP, AutoRezDPSJobsHealersOnly, DPSAlwaysHardTarget, HealerAlwaysHardTarget, BypassQuest, BypassFATE, IgnoreRangeInBoss, UnTargetAndDisableForPenalty } public enum HealerRotationMode { Manual, Highest_Current, Lowest_Current } public enum DPSRotationMode { Manual, Highest_Max, Lowest_Max, Highest_Current, Lowest_Current, Tank_Target, Nearest, Furthest } private const string CallbackPrefix = "Questionable$Wrath"; private readonly ILogger _logger; private readonly Configuration _configuration; private readonly IObjectTable _objectTable; private readonly ICallGateSubscriber _test; private readonly ICallGateSubscriber _registerForLeaseWithCallback; private readonly ICallGateSubscriber _releaseControl; private readonly ICallGateSubscriber _setAutoRotationState; private readonly ICallGateSubscriber _setAutoRotationConfigState; private readonly ICallGateSubscriber _setCurrentJobAutoRotationReady; private readonly ICallGateSubscriber?> _getComboNamesForJob; private readonly ICallGateSubscriber _setComboState; private readonly ICallGateProvider _callback; private Guid? _lease; public WrathComboModule(ILogger logger, Configuration configuration, IObjectTable objectTable, IDalamudPluginInterface pluginInterface) { _logger = logger; _configuration = configuration; _objectTable = objectTable; _test = pluginInterface.GetIpcSubscriber("WrathCombo.Test"); _registerForLeaseWithCallback = pluginInterface.GetIpcSubscriber("WrathCombo.RegisterForLeaseWithCallback"); _releaseControl = pluginInterface.GetIpcSubscriber("WrathCombo.ReleaseControl"); _setAutoRotationState = pluginInterface.GetIpcSubscriber("WrathCombo.SetAutoRotationState"); _setAutoRotationConfigState = pluginInterface.GetIpcSubscriber("WrathCombo.SetAutoRotationConfigState"); _setCurrentJobAutoRotationReady = pluginInterface.GetIpcSubscriber("WrathCombo.SetCurrentJobAutoRotationReady"); _getComboNamesForJob = pluginInterface.GetIpcSubscriber>("WrathCombo.GetComboNamesForJob"); _setComboState = pluginInterface.GetIpcSubscriber("WrathCombo.SetComboState"); _callback = pluginInterface.GetIpcProvider("Questionable$Wrath.WrathComboCallback"); _callback.RegisterAction(Callback); } public bool IsAvailable() { if (_configuration.General.CombatModule != Configuration.ECombatModule.WrathCombo) { return false; } try { _test.InvokeAction(); return true; } catch (IpcError) { return false; } } public bool CanHandleFight(CombatController.CombatData combatData) { return IsAvailable(); } public bool Start(CombatController.CombatData combatData) { try { _lease = _registerForLeaseWithCallback.InvokeFunc("Questionable", "Questionable", "Questionable$Wrath"); if (_lease.HasValue) { _logger.LogDebug("Wrath combo lease: {Lease}", _lease.Value); ESetResult eSetResult = _setAutoRotationState.InvokeFunc(_lease.Value, arg2: true); _logger.LogDebug("SetAutoRotationState={Result}", eSetResult); if (!eSetResult.IsSuccess()) { _logger.LogError("Unable to set autorotation state"); Stop(); return false; } ESetResult eSetResult2 = _setCurrentJobAutoRotationReady.InvokeFunc(_lease.Value); _logger.LogDebug("SetCurrentJobAutoRotationReady={Result}", eSetResult2); if (!eSetResult2.IsSuccess()) { _logger.LogError("Unable to set current job for autorotation"); Stop(); return false; } EnableCombosForCurrentJob(); SetConfig(AutoRotationConfigOption.HealerRotationMode, HealerRotationMode.Lowest_Current); SetConfig(AutoRotationConfigOption.DPSRotationMode, DPSRotationMode.Manual); SetConfig(AutoRotationConfigOption.InCombatOnly, 0); SetConfig(AutoRotationConfigOption.IncludeNPCs, 1); SetConfig(AutoRotationConfigOption.OnlyAttackInCombat, 0); SetConfig(AutoRotationConfigOption.AutoCleanse, 1); SetConfig(AutoRotationConfigOption.AutoRez, 1); SetConfig(AutoRotationConfigOption.AutoRezDPSJobs, 1); SetConfig(AutoRotationConfigOption.ManageKardia, 1); SetConfig(AutoRotationConfigOption.DPSAoETargets, 3); SetConfig(AutoRotationConfigOption.AutoRezOutOfParty, 0); SetConfig(AutoRotationConfigOption.DPSAlwaysHardTarget, 1); SetConfig(AutoRotationConfigOption.BypassQuest, 1); return true; } _logger.LogError("Wrath combo did not return a lease"); return false; } catch (IpcError exception) { _logger.LogError(exception, "Unable to use wrath combo for combat"); return false; } } public bool Stop() { try { if (_lease.HasValue) { _releaseControl.InvokeAction(_lease.Value); _lease = null; } return true; } catch (IpcError exception) { _logger.LogWarning(exception, "Could not turn off wrath combo"); return false; } } public void Update(IGameObject nextTarget) { if (!_lease.HasValue) { throw new TaskException("Wrath Combo Lease is cancelled"); } } public bool CanAttack(IBattleNpc target) { return true; } private void EnableCombosForCurrentJob() { if (!_lease.HasValue) { return; } uint num = _objectTable.LocalPlayer?.ClassJob.RowId ?? 0; if (num == 0) { return; } try { List list = _getComboNamesForJob.InvokeFunc(num); if (list == null || list.Count == 0) { _logger.LogWarning("No combos found for job {JobId}", num); return; } _logger.LogDebug("Found {Count} combos for job {JobId}: {Combos}", list.Count, num, string.Join(", ", list)); int num2 = 0; foreach (string item in list) { ESetResult eSetResult = _setComboState.InvokeFunc(_lease.Value, item, arg3: true, arg4: true); if (eSetResult.IsSuccess()) { num2++; continue; } _logger.LogWarning("SetComboState({Combo})={Result}", item, eSetResult); } _logger.LogDebug("Enabled {Count}/{Total} combos for job {JobId}", num2, list.Count, num); } catch (IpcError exception) { _logger.LogWarning(exception, "Failed to enable combos for job {JobId}", num); } } private void SetConfig(AutoRotationConfigOption configKey, object value) { if (!_lease.HasValue) { return; } try { ESetResult eSetResult = _setAutoRotationConfigState.InvokeFunc(_lease.Value, configKey, value); if (!eSetResult.IsSuccess()) { _logger.LogWarning("WrathCombo config {ConfigKey}={Value} returned {Result}", configKey, value, eSetResult); } } catch (IpcError exception) { _logger.LogWarning(exception, "Failed to set WrathCombo config {ConfigKey}={Value}", configKey, value); } } private void Callback(int reason, string additionalInfo) { _logger.LogWarning("WrathCombo callback: {Reason} ({Info})", reason, additionalInfo); _lease = null; } public void Dispose() { _lease = null; _callback.UnregisterAction(); } }