diff --git a/RotationSolver.Basic/Configuration/PluginConfiguration.cs b/RotationSolver.Basic/Configuration/PluginConfiguration.cs index 5c11fd712..b7752290c 100644 --- a/RotationSolver.Basic/Configuration/PluginConfiguration.cs +++ b/RotationSolver.Basic/Configuration/PluginConfiguration.cs @@ -90,6 +90,8 @@ public class PluginConfiguration : IPluginConfiguration public float AlphaInFill = 0.15f; public float MinLastAbilityAdvanced = 0.1f; + public float HealWhenNothingTodoBelow = 0.8f; + public Dictionary HealingOfTimeSubtractSingles { get; set; } = new Dictionary(); public Dictionary HealingOfTimeSubtractAreas { get; set; } = new Dictionary(); diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index 10fa857c5..448c9c6ee 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -43,6 +43,12 @@ private IAction GCD(bool helpDefenseAOE, bool helpDefenseSingle) if (GeneralGCD(out var action)) return action; + if(DataCenter.PartyMembersMinHP < Service.Config.HealWhenNothingTodoBelow && DataCenter.InCombat) + { + if (DataCenter.PartyMembersDifferHP < DataCenter.PartyMembersDifferHP && HealAreaGCD(out act)) return act; + if (HealSingleGCD(out act)) return act; + } + if (Service.Config.RaisePlayerByCasting && RaiseSpell(specialType, out act, true)) return act; return null; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs b/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs index f76d8c3d9..59910499a 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs @@ -1,6 +1,4 @@ using Dalamud.Logging; -using ECommons.ExcelServices; -using static FFXIVClientStructs.FFXIV.Client.UI.Misc.ConfigModule; namespace RotationSolver.Basic.Rotations; @@ -80,7 +78,6 @@ private void UpdateActions(JobRole role) ActionDefenseSingleAbility = DefenseSingleAbility(out act) ? act : null; - EsunaStanceNorthAbility = role switch { JobRole.Melee => TrueNorth.CanUse(out act) ? act : null, diff --git a/RotationSolver/Localization/Strings.cs b/RotationSolver/Localization/Strings.cs index 030c985b6..61205d34b 100644 --- a/RotationSolver/Localization/Strings.cs +++ b/RotationSolver/Localization/Strings.cs @@ -211,6 +211,9 @@ internal partial class Strings public string ConfigWindow_Param_HealthTankRatio { get; set; } = "Heal tank first if its HP threshold is lower than this."; public string ConfigWindow_Param_DistanceForMoving { get; set; } = "If the distance between Melee or Tank to target is less than this, using moving ability as attack ability."; + + public string ConfigWindow_Param_HealWhenNothingTodoBelow { get; set; } = "Healing the members with GCD if there is nothing to do in combat."; + public string ConfigWindow_Param_HealingOfTimeSubtractSingle { get; set; } = "Set the HP threshold reduce with hot effect(single)"; public string ConfigWindow_Param_HealthForDyingTank { get; set; } = "Set the HP threshold for tank to use invincibility"; diff --git a/RotationSolver/UI/OverlayWindow.cs b/RotationSolver/UI/OverlayWindow.cs index 7c3fe57b2..07d5b2f64 100644 --- a/RotationSolver/UI/OverlayWindow.cs +++ b/RotationSolver/UI/OverlayWindow.cs @@ -1,7 +1,11 @@ -using ECommons.DalamudServices; +using Dalamud.Logging; +using ECommons.DalamudServices; using ECommons.GameHelpers; using FFXIVClientStructs.FFXIV.Client.Game.Event; +using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using RotationSolver.Updaters; using System.Drawing; @@ -127,26 +131,27 @@ private static void DrawTarget(BattleChara tar, uint color, float radius, out Ve const int COUNT = 20; private static void DrawPositional() { - if (!Player.Object.IsJobCategory(JobRole.Tank) - && !Player.Object.IsJobCategory(JobRole.Melee)) return; + //if (!Player.Object.IsJobCategory(JobRole.Tank) + // && !Player.Object.IsJobCategory(JobRole.Melee)) return; - var target = ActionUpdater.NextGCDAction?.Target?.IsNPCEnemy() ?? false - ? ActionUpdater.NextGCDAction.Target - : Svc.Targets.Target?.IsNPCEnemy() ?? false - ? Svc.Targets.Target - : null; + //var target = ActionUpdater.NextGCDAction?.Target?.IsNPCEnemy() ?? false + // ? ActionUpdater.NextGCDAction.Target + // : Svc.Targets.Target?.IsNPCEnemy() ?? false + // ? Svc.Targets.Target + // : null; + var target = Player.Object; if (target == null) return; - if (ActionUpdater.NextGCDAction != null - && !ActionUpdater.NextGCDAction.IsSingleTarget) return; + //if (ActionUpdater.NextGCDAction != null + // && !ActionUpdater.NextGCDAction.IsSingleTarget) return; Vector3 pPosition = target.Position; float radius = target.HitboxRadius + Player.Object.HitboxRadius + 3; float rotation = target.Rotation; - if (Service.Config.DrawMeleeOffset && DataCenter.StateType != StateCommandType.Cancel) + //if (Service.Config.DrawMeleeOffset && DataCenter.StateType != StateCommandType.Cancel) { var offsetColor = new Vector3(0.8f, 0.3f, 0.2f); var pts1 = SectorPlots(pPosition, radius, 0, 4 * COUNT, 2 * Math.PI); @@ -265,14 +270,24 @@ public static IEnumerable ClosePoints(IEnumerable pts) public static IEnumerable GetPtsOnScreen(IEnumerable pts) { - //pts = ProjectPts(pts, 3); - var cameraPts = pts.Select(WorldToCamera).ToArray(); + var cameraPts = ProjectPtsOnGround(pts, 3).Select(WorldToCamera).ToArray(); + var changedPts = ChangePtsBehindCamera(cameraPts); + + return changedPts.Select(p => + { + Vector2? result = CameraToScreenCanSee(p, out var screenPos) ? screenPos : null; + return result; + }).Where(p => p.HasValue).Select(p => p.Value); + } + + private static IEnumerable ChangePtsBehindCamera(Vector3[] cameraPts) + { var changedPts = new List(cameraPts.Length * 2); for (int i = 0; i < cameraPts.Length; i++) { - var pt1 = cameraPts[i]; - var pt2 = cameraPts[(i + 1) % cameraPts.Length]; + var pt1 = cameraPts[(i - 1 + cameraPts.Length) % cameraPts.Length]; + var pt2 = cameraPts[i]; if (pt1.Z > 0 && pt2.Z <= 0) { @@ -291,16 +306,11 @@ public static IEnumerable GetPtsOnScreen(IEnumerable pts) changedPts.Add(pt2); } - return changedPts.Where(p => p.Z > 0).Select(p => - { - CameraToScreen(p, out var screenPos, out _); - return screenPos; - }); + return changedPts.Where(p => p.Z > 0); } - private static IEnumerable ProjectPts(IEnumerable pts, float height) - { - return pts.Select(pt => + private static IEnumerable ProjectPtsOnGround(IEnumerable pts, float height) + => pts.Select(pt => { var pUp = pt + Vector3.UnitY * height; if (BGCollisionModule.Raycast(pUp, -Vector3.UnitY, out var hit)) @@ -309,7 +319,6 @@ private static IEnumerable ProjectPts(IEnumerable pts, float h } return pt; }); - } const float PLANE_Z = 0.001f; public static void GetPointOnPlane(Vector3 front, ref Vector3 back) @@ -323,19 +332,45 @@ public static void GetPointOnPlane(Vector3 front, ref Vector3 back) back.Z = PLANE_Z; } - static readonly FieldInfo _matrix = Svc.GameGui.GetType().GetRuntimeFields().FirstOrDefault(f => f.Name == "getMatrixSingleton"); - public static unsafe Vector3 WorldToCamera(Vector3 worldPos) + static unsafe Matrix4x4 worldToCamera + { + get + { + var camera = CameraManager.Instance()->CurrentCamera; + return camera->ViewMatrix * camera->RenderCamera->ProjectionMatrix; + } + } + + public static Vector3 WorldToCamera(Vector3 worldPos) + { + return Vector3.Transform(worldPos, worldToCamera); + } + + private static unsafe bool CameraToScreenCanSee(Vector3 cameraPos, out Vector2 screenPos) { - var matrix = (MulticastDelegate)_matrix.GetValue(Svc.GameGui); - var matrixSingleton = (IntPtr)matrix.DynamicInvoke(); + screenPos = CameraToScreen(cameraPos); + try + { + var camera = CameraManager.Instance()->CurrentCamera; + Ray ray = camera->ScreenPointToRay(screenPos); - var viewProjectionMatrix = *(Matrix4x4*)(matrixSingleton + 0x1b4); - return Vector3.Transform(worldPos, viewProjectionMatrix); + if (BGCollisionModule.Raycast(ray.Origin, ray.Direction, out var hit) + && Math.Abs(cameraPos.Length() - Vector3.Distance(hit.Point, camera->Object.Position)) > 0.1) + { + return false; + } + return true; + } + catch(Exception e) + { + PluginLog.Warning(e, e.Message); + return false; + } } - public static unsafe bool CameraToScreen(Vector3 cameraPos, out Vector2 screenPos, out bool inView) + public static unsafe Vector2 CameraToScreen(Vector3 cameraPos) { - screenPos = new Vector2(cameraPos.X / MathF.Abs(cameraPos.Z), cameraPos.Y / MathF.Abs(cameraPos.Z)); + var screenPos = new Vector2(cameraPos.X / MathF.Abs(cameraPos.Z), cameraPos.Y / MathF.Abs(cameraPos.Z)); var windowPos = ImGuiHelpers.MainViewport.Pos; var device = Device.Instance(); @@ -345,11 +380,6 @@ public static unsafe bool CameraToScreen(Vector3 cameraPos, out Vector2 screenPo screenPos.X = (0.5f * width * (screenPos.X + 1f)) + windowPos.X; screenPos.Y = (0.5f * height * (1f - screenPos.Y)) + windowPos.Y; - var inFront = cameraPos.Z > 0; - inView = inFront && - screenPos.X > windowPos.X && screenPos.X < windowPos.X + width && - screenPos.Y > windowPos.Y && screenPos.Y < windowPos.Y + height; - - return inFront; + return screenPos; } } diff --git a/RotationSolver/UI/RotationConfigWindow_Param.cs b/RotationSolver/UI/RotationConfigWindow_Param.cs index 8dedd0cd5..1e363f023 100644 --- a/RotationSolver/UI/RotationConfigWindow_Param.cs +++ b/RotationSolver/UI/RotationConfigWindow_Param.cs @@ -361,6 +361,11 @@ private void DrawParamCondition() DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_HealOutOfCombat, ref Service.Config.HealOutOfCombat, Service.Default.HealOutOfCombat); + const float speed = 0.005f; + + DrawFloatNumber(LocalizationManager.RightLang.ConfigWindow_Param_HealWhenNothingTodoBelow, + ref Service.Config.HealWhenNothingTodoBelow, Service.Default.HealWhenNothingTodoBelow, speed); + DrawCheckBox(LocalizationManager.RightLang.ConfigWindow_Param_OnlyHotOnTanks, ref Service.Config.OnlyHotOnTanks, Service.Default.OnlyHotOnTanks); @@ -370,8 +375,6 @@ private void DrawParamCondition() ref Service.Config.BeneficialAreaOnTarget, Service.Default.BeneficialAreaOnTarget); } - const float speed = 0.005f; - DrawFloatNumber(LocalizationManager.RightLang.ConfigWindow_Param_DistanceForMoving, ref Service.Config.DistanceForMoving, Service.Default.DistanceForMoving, speed * 3); diff --git a/RotationSolver/Updaters/MajorUpdater.cs b/RotationSolver/Updaters/MajorUpdater.cs index 4aed157b4..b5627b56e 100644 --- a/RotationSolver/Updaters/MajorUpdater.cs +++ b/RotationSolver/Updaters/MajorUpdater.cs @@ -13,6 +13,7 @@ internal static class MajorUpdater && !Svc.Condition[ConditionFlag.BetweenAreas] && !Svc.Condition[ConditionFlag.BetweenAreas51] && Player.Available && !SocialUpdater.InPvp; + public static bool ShouldPreventActions => Basic.Configuration.PluginConfiguration.GetValue(SettingsCommand.PreventActions) && Basic.Configuration.PluginConfiguration.GetValue(SettingsCommand.PreventActionsDuty) && Svc.Condition[ConditionFlag.BoundByDuty] @@ -54,9 +55,9 @@ private static void FrameworkUpdate(Framework framework) bool newValue = Svc.Condition[(ConditionFlag)indexs[i]]; if (_values.TryGetValue(i, out bool value) && value != newValue && indexs[i] != 48 && indexs[i] != 27) { - var str = indexs[i].ToString() + " " + key + ": " + newValue.ToString(); - Svc.Chat.Print(str); - Svc.Toasts.ShowQuest(str); + //var str = indexs[i].ToString() + " " + key + ": " + newValue.ToString(); + //Svc.Chat.Print(str); + //Svc.Toasts.ShowQuest(str); } _values[i] = newValue; }