diff --git a/Questionable/Controller/CombatModules/BossModModule.cs b/Questionable/Controller/CombatModules/BossModModule.cs new file mode 100644 index 00000000..a26d1c74 --- /dev/null +++ b/Questionable/Controller/CombatModules/BossModModule.cs @@ -0,0 +1,120 @@ +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc.Exceptions; +using Dalamud.Plugin.Services; +using Json.Schema; +using Microsoft.Extensions.Logging; +using Questionable.Model; +using System; +using System.IO; +using System.Numerics; + +namespace Questionable.Controller.CombatModules; + +internal sealed class BossModModule(ILogger logger, MovementController movementController, IClientState clientState, IDalamudPluginInterface pluginInterface) : ICombatModule, IDisposable +{ + private const string Name = "BossMod"; + private readonly ILogger _logger = logger; + private readonly MovementController _movementController = movementController; + private readonly IClientState _clientState = clientState; + private readonly ICallGateSubscriber _getPreset = pluginInterface.GetIpcSubscriber($"{Name}.Presets.Get"); + private readonly ICallGateSubscriber _createPreset = pluginInterface.GetIpcSubscriber($"{Name}.Presets.Create"); + private readonly ICallGateSubscriber _setPreset = pluginInterface.GetIpcSubscriber($"{Name}.Presets.SetActive"); + private readonly ICallGateSubscriber _clearPreset = pluginInterface.GetIpcSubscriber($"{Name}.Presets.ClearActive"); + + private static Stream Preset => typeof(BossModModule).Assembly.GetManifestResourceStream("Questionable.Controller.CombatModules.BossModPreset")!; + private DateTime _lastDistanceCheck = DateTime.MinValue; + + public bool CanHandleFight(CombatController.CombatData combatData) + { + try + { + return _getPreset.HasFunction; + } + catch (IpcError) + { + return false; + } + } + + public bool Start(CombatController.CombatData combatData) + { + try + { + if (_getPreset.InvokeFunc("Questionable") == null) + { + using var reader = new StreamReader(Preset); + _logger.LogInformation("Loading Questionable BossMod Preset: {LoadedState}", _createPreset.InvokeFunc(reader.ReadToEnd(), true)); + } + _setPreset.InvokeFunc("Questionable"); + _lastDistanceCheck = DateTime.Now; + return true; + } + catch (IpcError e) + { + _logger.LogWarning(e, "Could not start combat"); + return false; + } + } + + public bool Stop() + { + try + { + _clearPreset.InvokeFunc(); + return true; + } + catch (IpcError e) + { + _logger.LogWarning(e, "Could not turn off combat"); + return false; + } + } + + public void MoveToTarget(IGameObject gameObject) + { + var player = _clientState.LocalPlayer; + if (player == null) + return; // uh oh + + float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius; + float actualDistance = Vector3.Distance(player.Position, gameObject.Position); + float maxDistance = player.ClassJob.ValueNullable?.Role is 3 or 4 ? 20f : 2.9f; + if (actualDistance - hitboxOffset >= maxDistance) + { + if (actualDistance - hitboxOffset <= 5) + { + _logger.LogInformation("Moving to {TargetName} ({DataId}) to attack", gameObject.Name, + gameObject.DataId); + _movementController.NavigateTo(EMovementType.Combat, null, [gameObject.Position], false, false, + maxDistance + hitboxOffset - 0.25f, true); + } + else + { + _logger.LogInformation("Moving to {TargetName} ({DataId}) to attack (with navmesh)", gameObject.Name, + gameObject.DataId); + _movementController.NavigateTo(EMovementType.Combat, null, gameObject.Position, false, false, + maxDistance + hitboxOffset - 0.25f, true); + } + } + + _lastDistanceCheck = DateTime.Now; + } + + public void Update(IGameObject gameObject) + { + if (_movementController.IsPathfinding || _movementController.IsPathRunning) + return; + + if (DateTime.Now > _lastDistanceCheck.AddSeconds(10)) + { + MoveToTarget(gameObject); + _lastDistanceCheck = DateTime.Now; + } + } + + public bool CanAttack(IBattleNpc target) => true; + + public void Dispose() => Stop(); +} diff --git a/Questionable/Controller/CombatModules/BossModPreset.json b/Questionable/Controller/CombatModules/BossModPreset.json new file mode 100644 index 00000000..b6c6a28a --- /dev/null +++ b/Questionable/Controller/CombatModules/BossModPreset.json @@ -0,0 +1,377 @@ +{ + "Name": "Questionable", + "Modules": { + "BossMod.Autorotation.xan.DNC": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.MCH": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.MNK": [ + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.PCT": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + }, + { + "Track": "Motifs", + "Option": "Downtime" + } + ], + "BossMod.Autorotation.xan.PLD": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.SAM": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.SGE": [ + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.VPR": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.NIN": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.GNB": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.SMN": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.DRK": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.RPR": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.WHM": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.AST": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.BRD": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.SCH": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.BLM": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.StandardWAR": [ + { + "Track": "AOE", + "Option": "AutoFinishCombo" + } + ], + "BossMod.Autorotation.xan.RDM": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ], + "BossMod.Autorotation.xan.DRG": [ + { + "Track": "Buffs", + "Option": "Auto" + }, + { + "Track": "Buffs", + "Option": "Delay", + "Mod": "Shift, Ctrl" + }, + { + "Track": "AOE", + "Option": "AOE" + }, + { + "Track": "Targeting", + "Option": "Manual" + } + ] + } +} \ No newline at end of file diff --git a/Questionable/Questionable.csproj b/Questionable/Questionable.csproj index 6d44ba06..544ba74e 100644 --- a/Questionable/Questionable.csproj +++ b/Questionable/Questionable.csproj @@ -23,4 +23,11 @@ + + + + + Questionable.Controller.CombatModules.BossModPreset + + diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs index 8e4ad410..73af794f 100644 --- a/Questionable/QuestionablePlugin.cs +++ b/Questionable/QuestionablePlugin.cs @@ -247,6 +247,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); }