diff --git a/Resources/AnimationLockTime.json b/Resources/AnimationLockTime.json index 439ab56f5..c373aec92 100644 --- a/Resources/AnimationLockTime.json +++ b/Resources/AnimationLockTime.json @@ -160,6 +160,7 @@ "7381": 0.6, "7383": 0.6, "7384": 0.1, + "7385": 0.6, "7386": 0.6, "7387": 0.6, "7388": 0.6, @@ -181,6 +182,7 @@ "7412": 0.6, "7413": 0.6, "7414": 0.6, + "7418": 0.6, "7421": 0.6, "7426": 0.6, "7427": 0.6, diff --git a/Resources/HostileCastingArea.json b/Resources/HostileCastingArea.json index 96035ff28..faed26376 100644 --- a/Resources/HostileCastingArea.json +++ b/Resources/HostileCastingArea.json @@ -7,6 +7,7 @@ 1385, 1688, 2225, + 2279, 2317, 2375, 2398, @@ -35,9 +36,11 @@ 5317, 5329, 5331, + 5823, 5919, 5928, 5959, + 6020, 6107, 6469, 6547, @@ -237,6 +240,7 @@ 32116, 32117, 32122, + 32132, 32785, 32868, 32944, diff --git a/RotationSolver.Basic/Actions/BaseAction_ActionInfo.cs b/RotationSolver.Basic/Actions/BaseAction_ActionInfo.cs index e4aa89182..5dc4c60dd 100644 --- a/RotationSolver.Basic/Actions/BaseAction_ActionInfo.cs +++ b/RotationSolver.Basic/Actions/BaseAction_ActionInfo.cs @@ -103,8 +103,9 @@ public unsafe virtual bool CanUse(out IAction act, CanUseOption option = CanUseO } } - if (!option.HasFlag(CanUseOption.IgnoreCastCheck) && CastTime > 0 && DataCenter.IsMoving && - !player.HasStatus(true, CustomRotation.Swiftcast.StatusProvide)) return false; + if (DataCenter.NoPoslock && DataCenter.IsMoving + && !option.HasFlag(CanUseOption.IgnoreCastCheck) && CastTime > 0 + && !player.HasStatus(true, CustomRotation.Swiftcast.StatusProvide)) return false; if (IsGeneralGCD && IsEot && IsFriendly && IActionHelper.IsLastGCD(true, this) && DataCenter.TimeSinceLastAction.TotalSeconds < 3) return false; diff --git a/RotationSolver.Basic/Configuration/PluginConfiguration.cs b/RotationSolver.Basic/Configuration/PluginConfiguration.cs index 34b253f17..51e299b80 100644 --- a/RotationSolver.Basic/Configuration/PluginConfiguration.cs +++ b/RotationSolver.Basic/Configuration/PluginConfiguration.cs @@ -51,8 +51,13 @@ public class PluginConfiguration : IPluginConfiguration public bool ShowInfoOnToast = true; public bool RaiseAll = false; public bool CastingDisplay = true; - public bool PoslockCasting = false; + public bool PoslockCasting = true; public int PoslockModifier = 0; + public bool PosPassageOfArms = false; + public bool PosTenChiJin = false; + public bool PosFlameThrower = false; + public bool PosImprovisation = false; + public bool RaisePlayerByCasting = true; public bool RaiseBrinkOfDeath = true; public int LessMPNoRaise = 0; @@ -85,6 +90,7 @@ public class PluginConfiguration : IPluginConfiguration public float KeyBoardNoiseTimeMax = 0.2f; public bool MoveAreaActionFarthest = true; public bool StartOnCountdown = true; + public bool StartOnAttackedBySomeone = false; public bool NoNewHostiles = false; public bool UseHealWhenNotAHealer = true; public float HealthDifference = 0.25f; diff --git a/RotationSolver.Basic/Data/ActionID.cs b/RotationSolver.Basic/Data/ActionID.cs index 69194e264..2830a867b 100644 --- a/RotationSolver.Basic/Data/ActionID.cs +++ b/RotationSolver.Basic/Data/ActionID.cs @@ -138,7 +138,7 @@ public enum ActionID : uint #region BlueMage WaterCannon = 11385, - FlameThrower = 11402, + FlameThrower = 7418, AquaBreath = 11390, diff --git a/RotationSolver.Basic/Data/IconSet.cs b/RotationSolver.Basic/Data/IconSet.cs index 503f61b63..454316af6 100644 --- a/RotationSolver.Basic/Data/IconSet.cs +++ b/RotationSolver.Basic/Data/IconSet.cs @@ -50,6 +50,21 @@ public static TextureWrap GetTexture(this IAction action, bool isAdjust = true) return GetTexture(iconId); } + public static TextureWrap GetTexture(this ActionID actionID, bool isAction = true) + { + var id = (uint)actionID; + + if (!_actionIcons.TryGetValue(id, out var iconId)) + { + iconId = isAction + ? Service.GetSheet().GetRow(id).Icon + : Service.GetSheet().GetRow(id).Icon; + + _actionIcons[id] = iconId; + } + return GetTexture(iconId); + } + private static readonly Dictionary _icons = new() { { IconType.Gold, new uint[40] diff --git a/RotationSolver.Basic/Data/StatusID.cs b/RotationSolver.Basic/Data/StatusID.cs index 1bf8031fb..68bff16ba 100644 --- a/RotationSolver.Basic/Data/StatusID.cs +++ b/RotationSolver.Basic/Data/StatusID.cs @@ -501,4 +501,8 @@ public enum StatusID : ushort LivingDead = WillDead, Overheated = 2688, + + Flamethrower = 1205, + + PassageOfArms = 1175, } diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index ad0c82c7f..d9840f553 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -1,19 +1,26 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Conditions; +using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Logging; using ECommons.DalamudServices; using ECommons.ExcelServices; using ECommons.GameHelpers; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Fate; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Lumina.Excel.GeneratedSheets; using Action = Lumina.Excel.GeneratedSheets.Action; using CharacterManager = FFXIVClientStructs.FFXIV.Client.Game.Character.CharacterManager; namespace RotationSolver.Basic; -public static class DataCenter +internal static class DataCenter { + internal static bool NoPoslock => Svc.Condition[ConditionFlag.OccupiedInEvent] + || !Service.Config.PoslockCasting + //Key cancel. + || Svc.KeyState[ConfigurationHelper.Keys[Service.Config.PoslockModifier]] + //Gamepad cancel. + || Svc.GamepadState.Raw(Dalamud.Game.ClientState.GamePad.GamepadButtons.L2) >= 0.5f; + internal static DateTime EffectTime { private get; set; } = DateTime.Now; internal static DateTime EffectEndTime { private get; set; } = DateTime.Now; diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index 17248b7d2..4b1bd1294 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -160,6 +160,14 @@ internal static void UpdateRotationState() _lastCountdownTime = 0; CancelState(); } + //Auto manual on being attacked by someone. + else if (Service.Config.StartOnAttackedBySomeone && DataCenter.AllHostileTargets.Any(t => t.TargetObjectId == Player.Object.ObjectId)) + { + if(DataCenter.StateType == StateCommandType.Cancel) + { + DoStateCommandType(StateCommandType.Manual); + } + } //Auto start at count Down. else if (Service.Config.StartOnCountdown && Service.CountDownTime > 0) { diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 0ca39652e..54ee10c58 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -79,7 +79,7 @@ "ConfigWindow_Param_ClickMistake": "How likely is it that RS will click the wrong action.", "ConfigWindow_Param_Display": "Display", "ConfigWindow_Param_Advanced": "Advanced", - "ConfigWindow_Param_PoslockCasting": "Lock the movement when casting or has TenChiJin or PhantomFlurry status.", + "ConfigWindow_Param_PoslockCasting": "Lock the movement when casting or some actions.", "ConfigWindow_Param_UseStopCasting": "Use stopping casting when target is dead.", "ConfigWindow_Param_ShowTooltips": "Show tooltips", "ConfigWindow_Param_InDebug": "Debug Mode", @@ -132,8 +132,9 @@ "ConfigWindow_Param_LessMPNoRaise": "Never raise player if MP is less than the set value", "ConfigWindow_Param_UseTinctures": "Use Tinctures", "ConfigWindow_Param_UseHealPotions": "Use Heal Potions", - "ConfigWindow_Param_Conditon": "Condition", + "ConfigWindow_Param_Condition": "Condition", "ConfigWindow_Param_StartOnCountdown": "Auto turn smart on countdown", + "ConfigWindow_Param_StartOnAttackedBySomeone": "Auto turn manual on being attacked by someone.", "ConfigWindow_Param_EsunaAll": "Esuna All Statuses.", "ConfigWindow_Param_InterruptibleMoreCheck": "Interrupt the action with action type check.", "ConfigWindow_Param_HealOutOfCombat": "Heal party members outside of combat.", diff --git a/RotationSolver/Localization/Strings.cs b/RotationSolver/Localization/Strings.cs index 82946253b..f03c3296d 100644 --- a/RotationSolver/Localization/Strings.cs +++ b/RotationSolver/Localization/Strings.cs @@ -133,7 +133,7 @@ internal partial class Strings public string ConfigWindow_Param_ClickMistake { get; set; } = "How likely is it that RS will click the wrong action."; public string ConfigWindow_Param_Display { get; set; } = "Display"; public string ConfigWindow_Param_Advanced { get; set; } = "Advanced"; - public string ConfigWindow_Param_PoslockCasting { get; set; } = "Lock the movement when casting or has TenChiJin or PhantomFlurry status."; + public string ConfigWindow_Param_PoslockCasting { get; set; } = "Lock the movement when casting or some actions."; public string ConfigWindow_Param_UseStopCasting { get; set; } = "Use stopping casting when target is dead."; public string ConfigWindow_Param_ShowTooltips { get; set; } = "Show tooltips"; public string ConfigWindow_Param_InDebug { get; set; } = "Debug Mode"; @@ -190,8 +190,10 @@ internal partial class Strings public string ConfigWindow_Param_LessMPNoRaise { get; set; } = "Never raise player if MP is less than the set value"; public string ConfigWindow_Param_UseTinctures { get; set; } = "Use Tinctures"; public string ConfigWindow_Param_UseHealPotions { get; set; } = "Use Heal Potions"; - public string ConfigWindow_Param_Conditon { get; set; } = "Condition"; + public string ConfigWindow_Param_Condition { get; set; } = "Condition"; public string ConfigWindow_Param_StartOnCountdown { get; set; } = "Auto turn smart on countdown"; + + public string ConfigWindow_Param_StartOnAttackedBySomeone { get; set; } = "Auto turn manual on being attacked by someone."; public string ConfigWindow_Param_EsunaAll { get; set; } = "Esuna All Statuses."; public string ConfigWindow_Param_InterruptibleMoreCheck { get; set; } = "Interrupt the action with action type check."; diff --git a/RotationSolver/RotationSolverPlugin.cs b/RotationSolver/RotationSolverPlugin.cs index 2b956b85d..e72f41d41 100644 --- a/RotationSolver/RotationSolverPlugin.cs +++ b/RotationSolver/RotationSolverPlugin.cs @@ -70,7 +70,6 @@ public RotationSolverPlugin(DalamudPluginInterface pluginInterface) MajorUpdater.Enable(); Watcher.Enable(); OtherConfiguration.Init(); - _dis.Add(new MovingController()); _dis.Add(new LocalizationManager()); #if DEBUG LocalizationManager.ExportLocalization(); diff --git a/RotationSolver/UI/ControlWindow.cs b/RotationSolver/UI/ControlWindow.cs index 7344cfc56..9c32e1620 100644 --- a/RotationSolver/UI/ControlWindow.cs +++ b/RotationSolver/UI/ControlWindow.cs @@ -472,7 +472,7 @@ internal static (Vector2, Vector2) DrawIAction(IAction action, float width, floa return result; } - static (Vector2, Vector2) DrawIAction(nint handle, float width, float percent) + internal static (Vector2, Vector2) DrawIAction(nint handle, float width, float percent) { var cursor = ImGui.GetCursorPos(); ImGui.BeginGroup(); diff --git a/RotationSolver/UI/RotationConfigWindow_Debug.cs b/RotationSolver/UI/RotationConfigWindow_Debug.cs index abae8f7a6..526871066 100644 --- a/RotationSolver/UI/RotationConfigWindow_Debug.cs +++ b/RotationSolver/UI/RotationConfigWindow_Debug.cs @@ -162,19 +162,6 @@ private void DrawLastAction() private unsafe void DrawIcon() { - var ptr = (IntPtr)AgentMap.Instance(); - for (var i = 23064; i < 23079; i++) - { - try - { - var value = *(byte*)(ptr + i); - ImGui.Text($"{i}: {value}"); - } - catch - { - - } - } } private static void DrawAction(ActionID id, string type) diff --git a/RotationSolver/UI/RotationConfigWindow_Major.cs b/RotationSolver/UI/RotationConfigWindow_Major.cs index 97a99b211..5922d7c58 100644 --- a/RotationSolver/UI/RotationConfigWindow_Major.cs +++ b/RotationSolver/UI/RotationConfigWindow_Major.cs @@ -2,6 +2,7 @@ using Dalamud.Logging; using Newtonsoft.Json.Linq; using RotationSolver.Localization; +using System.Xml.Linq; namespace RotationSolver.UI; internal partial class RotationConfigWindow : Window @@ -85,6 +86,27 @@ internal static void DrawCheckBox(string name, SettingsCommand command, string d ImGuiHelper.DisplayCommandHelp(OtherCommandType.Settings, command.ToString()); } + internal static void DrawIconCheckBox(ActionID actionID, ref bool value, bool @default, string description = "", Action otherThing = null) + { + var name = $"##{actionID}"; + description = actionID.ToString() + "\n" + description; + + ControlWindow.DrawIAction(IconSet.GetTexture(actionID).ImGuiHandle, 40, 1); + + ImGuiHelper.HoveredString(description); + + ImGui.SameLine(); + ImGuiHelper.Spacing(); + + DrawCheckBox(name, ref value, description, otherThing); + + if (value != @default) + { + ImGuiHelper.UndoValue(name, ref value, @default, otherThing); + } + } + + internal static void DrawCheckBox(string name, ref bool value, bool @default, string description = "", Action otherThing = null) { DrawCheckBox(name, ref value, description, otherThing); diff --git a/RotationSolver/UI/RotationConfigWindow_Param.cs b/RotationSolver/UI/RotationConfigWindow_Param.cs index a4ef05c46..ad5f11bba 100644 --- a/RotationSolver/UI/RotationConfigWindow_Param.cs +++ b/RotationSolver/UI/RotationConfigWindow_Param.cs @@ -16,7 +16,7 @@ private void DrawParamTab() DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Delay, DrawParamDelay); DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Display, DrawParamDisplay); DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Action, DrawParamAction); - DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Conditon, DrawParamCondition); + DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Condition, DrawParamCondition); DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Target, DrawParamTarget); DrawParamTabItem(LocalizationManager.RightLang.ConfigWindow_Param_Advanced, DrawParamAdvanced); @@ -62,6 +62,9 @@ private void DrawParamBasic() DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_StartOnCountdown, ref Service.Config.StartOnCountdown, Service.Default.StartOnCountdown); + DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_StartOnAttackedBySomeone, + ref Service.Config.StartOnAttackedBySomeone, Service.Default.StartOnAttackedBySomeone); + DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_UseWorkTask, ref Service.Config.UseWorkTask, Service.Default.UseWorkTask); @@ -119,6 +122,7 @@ private void DrawParamDelay() DrawFloatNumber(LocalizationManager.RightLang.ConfigWindow_Param_ClickMistake, ref Service.Config.MistakeRatio, Service.Default.MistakeRatio); } + private void DrawParamAdvanced() { DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_SayOutStateChanged, @@ -134,13 +138,31 @@ private void DrawParamAdvanced() if (Service.Config.PoslockCasting) { - ImGui.SameLine(); - ImGuiHelper.Spacing(); + ImGui.Indent(); DrawCombo(LocalizationManager.RightLang.ConfigWindow_Param_PoslockModifier, ref Service.Config.PoslockModifier, EnumTranslations.ToName, ConfigurationHelper.Keys, LocalizationManager.RightLang.ConfigWindow_Param_PoslockDescription); + + DrawIconCheckBox(ActionID.PassageOfArms, ref Service.Config.PosPassageOfArms, Service.Default.PosPassageOfArms); + + ImGui.SameLine(); + ImGuiHelper.Spacing(); + + DrawIconCheckBox(ActionID.TenChiJin, ref Service.Config.PosTenChiJin, Service.Default.PosTenChiJin); + + ImGui.SameLine(); + ImGuiHelper.Spacing(); + + DrawIconCheckBox(ActionID.FlameThrower, ref Service.Config.PosFlameThrower, Service.Default.PosFlameThrower); + + ImGui.SameLine(); + ImGuiHelper.Spacing(); + + DrawIconCheckBox(ActionID.Improvisation, ref Service.Config.PosImprovisation, Service.Default.PosImprovisation); + + ImGui.Unindent(); } DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_UseStopCasting, diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs index cbd997649..c099c4d87 100644 --- a/RotationSolver/Updaters/ActionUpdater.cs +++ b/RotationSolver/Updaters/ActionUpdater.cs @@ -220,7 +220,7 @@ private static void UpdateMPTimer() _lastMP = player.CurrentMp; } - internal unsafe static void DoAction() + internal unsafe static bool DoAction() { if (Svc.Condition[ConditionFlag.OccupiedInQuestEvent] || Svc.Condition[ConditionFlag.OccupiedInCutSceneEvent] @@ -236,39 +236,47 @@ internal unsafe static void DoAction() || Svc.Condition[ConditionFlag.InFlight] || ActionManager.Instance()->ActionQueued && NextAction != null && ActionManager.Instance()->QueuedActionId != NextAction.AdjustedID - || Player.Object.CurrentHp == 0) return; + || Player.Object.CurrentHp == 0) return false; var maxAhead = Math.Max(DataCenter.MinAnimationLock - DataCenter.Ping, 0.08f); var ahead = Math.Min(maxAhead, Service.Config.ActionAhead); //GCD var canUseGCD = DataCenter.WeaponRemain <= ahead; - if (_GCDDelay.Delay(canUseGCD)) RSCommands.DoAnAction(true); - if (canUseGCD) return; + if (_GCDDelay.Delay(canUseGCD)) + { + RSCommands.DoAnAction(true); + return true; + } + if (canUseGCD) return false; var nextAction = NextAction; - if (nextAction == null) return; + if (nextAction == null) return false; var timeToNext = DataCenter.ActionRemain; //No time to use 0gcd if (timeToNext + nextAction.AnimationLockTime - > DataCenter.WeaponRemain) return; + > DataCenter.WeaponRemain) return false; //Skip when casting - if (DataCenter.WeaponElapsed <= DataCenter.CastingTotal) return; + if (DataCenter.WeaponElapsed <= DataCenter.CastingTotal) return false; //The last one. if (timeToNext + nextAction.AnimationLockTime + DataCenter.Ping + DataCenter.MinAnimationLock > DataCenter.WeaponRemain) { if (DataCenter.WeaponRemain > nextAction.AnimationLockTime + ahead + - Math.Max(DataCenter.Ping, Service.Config.MinLastAbilityAdvanced)) return; + Math.Max(DataCenter.Ping, Service.Config.MinLastAbilityAdvanced)) return false; RSCommands.DoAnAction(false); + return true; } else if (timeToNext < ahead) { RSCommands.DoAnAction(false); + return true; } + + return false; } } diff --git a/RotationSolver/Updaters/MajorUpdater.cs b/RotationSolver/Updaters/MajorUpdater.cs index 068b541e8..63a6e7d18 100644 --- a/RotationSolver/Updaters/MajorUpdater.cs +++ b/RotationSolver/Updaters/MajorUpdater.cs @@ -81,11 +81,15 @@ private static void FrameworkUpdate(Framework framework) } ActionUpdater.UpdateActionInfo(); + var nextAction = false; if (!ShouldPreventActions) { - ActionUpdater.DoAction(); + nextAction = ActionUpdater.DoAction(); } + MovingUpdater.UpdateCanMove(nextAction); + + MacroUpdater.UpdateMacro(); } catch (Exception ex) @@ -116,10 +120,11 @@ private static void FrameworkUpdate(Framework framework) public static void Enable() { - Svc.Framework.Update += FrameworkUpdate; ActionSequencerUpdater.Enable(Svc.PluginInterface.ConfigDirectory.FullName + "\\Conditions"); - SocialUpdater.Enable(); + new MovingUpdater(); + + Svc.Framework.Update += FrameworkUpdate; } static bool _work; @@ -137,7 +142,6 @@ private static void UpdateWork() try { - PreviewUpdater.UpdateCastBarState(); TargetUpdater.UpdateTarget(); if (Service.Config.AutoLoadCustomRotations) diff --git a/RotationSolver/Updaters/MovingController.cs b/RotationSolver/Updaters/MovingController.cs deleted file mode 100644 index c07a34147..000000000 --- a/RotationSolver/Updaters/MovingController.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Dalamud.Game.ClientState.Conditions; -using Dalamud.Hooking; -using Dalamud.Utility.Signatures; -using ECommons.DalamudServices; - -namespace RotationSolver.Updaters; - -internal class MovingController : IDisposable -{ - private static bool _posLocker = false; - private delegate bool MovingControllerDelegate(IntPtr ptr); - - [Signature("40 55 53 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 83 79 ?? ??", DetourName = nameof(MovingDetour))] - private static Hook movingHook = null; - internal static unsafe bool IsMoving - { - set => _posLocker = !value; - } - public MovingController() - { - SignatureHelper.Initialise(this); - movingHook?.Enable(); - } - public void Dispose() - { - movingHook.Disable(); - } - - private static bool MovingDetour(IntPtr ptr) - { - if (Svc.Condition[ConditionFlag.OccupiedInEvent]) - return movingHook.Original(ptr); - - if (Service.Config.PoslockCasting && _posLocker && DataCenter.InCombat) - { - //没有键盘取消 - if (!Svc.KeyState[ConfigurationHelper.Keys[Service.Config.PoslockModifier]] - //也没有手柄取消 - && Svc.GamepadState.Raw(Dalamud.Game.ClientState.GamePad.GamepadButtons.L2) <= 0.5f) return false; - } - return movingHook.Original(ptr); - } -} diff --git a/RotationSolver/Updaters/MovingUpdater.cs b/RotationSolver/Updaters/MovingUpdater.cs new file mode 100644 index 000000000..faced294f --- /dev/null +++ b/RotationSolver/Updaters/MovingUpdater.cs @@ -0,0 +1,66 @@ +using Dalamud.Game.ClientState.Conditions; +using Dalamud.Utility.Signatures; +using ECommons.DalamudServices; +using ECommons.GameHelpers; +using FFXIVClientStructs.FFXIV.Client.Game; + +namespace RotationSolver.Updaters; + +internal class MovingUpdater +{ + // From https://github.com/PunishXIV/Orbwalker/blame/master/Orbwalker/Memory.cs#L85-L87 + [Signature("F3 0F 10 05 ?? ?? ?? ?? 0F 2E C6 0F 8A", ScanType = ScanType.StaticAddress, Fallibility = Fallibility.Infallible)] + static IntPtr forceDisableMovementPtr; + private static unsafe ref int ForceDisableMovement => ref *(int*)(forceDisableMovementPtr + 4); + + internal static unsafe bool CanMove + { + get => ForceDisableMovement == 0; + set => ForceDisableMovement = value || DataCenter.NoPoslock ? 0 : 1; + } + + public MovingUpdater() + { + SignatureHelper.Initialise(this); + } + + internal unsafe static void UpdateCanMove(bool nextAction) + { + bool canMove = !Svc.Condition[ConditionFlag.OccupiedInEvent] + && !Svc.Condition[ConditionFlag.Casting]; + + var statusList = new List(4); + var actionList = new List(4); + + if (Service.Config.PosFlameThrower) + { + statusList.Add(StatusID.Flamethrower); + actionList.Add(ActionID.FlameThrower); + } + if (Service.Config.PosTenChiJin) + { + statusList.Add(StatusID.TenChiJin); + actionList.Add(ActionID.TenChiJin); + } + if (Service.Config.PosPassageOfArms) + { + statusList.Add(StatusID.PassageOfArms); + actionList.Add(ActionID.PassageOfArms); + } + if (Service.Config.PosImprovisation) + { + statusList.Add(StatusID.Improvisation); + actionList.Add(ActionID.Improvisation); + } + + //Action + var action = nextAction ? (ActionID)(ActionUpdater.NextAction?.AdjustedID ?? 0) : 0; + var specialActions = ActionManager.GetAdjustedCastTime(ActionType.Spell, (uint)action) > 0 + || actionList.Any(id => Service.GetAdjustedActionId(id) == action); + + //Status + var specialStatus = Player.Object.HasStatus(true, statusList.ToArray()); + + CanMove = !specialStatus && !specialActions && canMove; + } +} diff --git a/RotationSolver/Updaters/PreviewUpdater.cs b/RotationSolver/Updaters/PreviewUpdater.cs index fc4ae218c..330b4178c 100644 --- a/RotationSolver/Updaters/PreviewUpdater.cs +++ b/RotationSolver/Updaters/PreviewUpdater.cs @@ -50,37 +50,23 @@ private static void UpdateEntry() } } - static bool _canMove; - static bool _isTarNoNeedCast; static RandomDelay _tarStopCastDelay = new(() => (Service.Config.StopCastingDelayMin, Service.Config.StopCastingDelayMax)); - internal static void UpdateCastBarState() - { - var tardead = Service.Config.UseStopCasting && Svc.Objects.SearchById(Player.Object.CastTargetObjectId) is BattleChara b - && (b is PlayerCharacter ? b.HasStatus(false, StatusID.Raise) : b.CurrentHp == 0); - _isTarNoNeedCast = _tarStopCastDelay.Delay(tardead); - - bool canMove = !Svc.Condition[Dalamud.Game.ClientState.Conditions.ConditionFlag.OccupiedInEvent] - && !Svc.Condition[Dalamud.Game.ClientState.Conditions.ConditionFlag.Casting]; - - //For lock - var specialStatus = Player.Object.HasStatus(true, StatusID.PhantomFlurry, StatusID.TenChiJin); - - _canMove = !specialStatus && canMove; - MovingController.IsMoving = _canMove; - } - static bool _showCanMove; static readonly ByteColor _redColor = new() { A = 255, R = 120, G = 0, B = 60 }; static readonly ByteColor _greenColor = new() { A = 255, R = 60, G = 120, B = 30 }; private static unsafe void UpdateCastBar() { - if (_isTarNoNeedCast) + var tardead = Service.Config.UseStopCasting && Svc.Objects.SearchById(Player.Object.CastTargetObjectId) is BattleChara b + && (b is PlayerCharacter ? b.HasStatus(false, StatusID.Raise) : b.CurrentHp == 0); + + + if (_tarStopCastDelay.Delay(tardead)) { UIState.Instance()->Hotbar.CancelCast(); } - var nowMove = _canMove && Service.Config.CastingDisplay; + var nowMove = MovingUpdater.CanMove && Service.Config.CastingDisplay; if (nowMove == _showCanMove) return; _showCanMove = nowMove;