From e76e9687b2eb2e1bee2513ca692574330e759dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Fri, 23 Feb 2024 08:45:19 +0800 Subject: [PATCH 01/48] fix: delay bug. --- ECommons | 2 +- RotationSolver.Basic/Data/ObjectListDelay.cs | 2 +- RotationSolver.Basic/Rotations/CustomRotation_Actions.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ECommons b/ECommons index 05b5c0ff7..2227b472f 160000 --- a/ECommons +++ b/ECommons @@ -1 +1 @@ -Subproject commit 05b5c0ff790e793a9a27a52e80573834a1189635 +Subproject commit 2227b472fcc492b376aab5abb2fa947a82bd0931 diff --git a/RotationSolver.Basic/Data/ObjectListDelay.cs b/RotationSolver.Basic/Data/ObjectListDelay.cs index c5ddfbcf5..e0d943088 100644 --- a/RotationSolver.Basic/Data/ObjectListDelay.cs +++ b/RotationSolver.Basic/Data/ObjectListDelay.cs @@ -47,7 +47,7 @@ public void Delay(IEnumerable originData) var delaySecond = min + (float)_ran.NextDouble() * (max - min); time = now + new TimeSpan(0, 0, 0, 0, (int)(delaySecond * 1000)); } - revealTime.Add(item.ObjectId, time); + revealTime[item.ObjectId] = time; if (now > time) { diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs index 3b7438430..9b7dea3b3 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs @@ -7,7 +7,7 @@ partial class CustomRotation internal static void LoadActionSetting(ref IBaseAction action) { var a = action.Action; - if(a.CanTargetFriendly || a.CanTargetParty) + if (a.CanTargetFriendly || a.CanTargetParty) { action.Setting.IsFriendly = true; } From 731f4ba12a6c8ace6bb9cea1d1cd4e55998637f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Fri, 23 Feb 2024 22:16:17 +0800 Subject: [PATCH 02/48] fix: manual aoe check fixed. --- Resources/RotationSolverRecord.json | 2 +- RotationSolver.Basic/Actions/ActionTargetInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 00108b32f..05f9caf15 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 70096, + "ClickingCount": 70222, "SayingHelloCount": 60, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index d5a3095f1..f5e76eb97 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -222,7 +222,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) else { var effects = GetAffects(t, canAffects).ToArray(); - if(effects.Length >= _action.Config.AoeCount) + if (effects.Length >= _action.Config.AoeCount || skipAoeCheck) { return new(t, effects, t.Position); } From 1a2b0603a121438af1af18b40685d1f1dc103bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:40:18 +0800 Subject: [PATCH 03/48] fix: icon drawing. --- Resources/AnimationLockTime.json | 1 + Resources/HostileCastingArea.json | 3 ++- Resources/RotationSolverRecord.json | 4 ++-- RotationSolver.Basic/Actions/IBaseAction.cs | 5 ++++- RotationSolver.Basic/Data/IconSet.cs | 4 +++- RotationSolver/Localization/Localization.json | 4 +++- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Resources/AnimationLockTime.json b/Resources/AnimationLockTime.json index 7cd74d4b9..05cc93145 100644 --- a/Resources/AnimationLockTime.json +++ b/Resources/AnimationLockTime.json @@ -360,6 +360,7 @@ "16470": 0.6, "16472": 0.6, "16473": 0.6, + "16474": 0.6, "16478": 0.8, "16479": 0.6, "16480": 1.5, diff --git a/Resources/HostileCastingArea.json b/Resources/HostileCastingArea.json index f69bb1d0b..0a55a07bf 100644 --- a/Resources/HostileCastingArea.json +++ b/Resources/HostileCastingArea.json @@ -494,5 +494,6 @@ 17435, 35384, 35386, - 36001 + 36001, + 20052 ] \ No newline at end of file diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 05f9caf15..32dd843af 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 70222, - "SayingHelloCount": 60, + "ClickingCount": 73576, + "SayingHelloCount": 61, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/IBaseAction.cs b/RotationSolver.Basic/Actions/IBaseAction.cs index 7e3a45e84..6cfe3cde2 100644 --- a/RotationSolver.Basic/Actions/IBaseAction.cs +++ b/RotationSolver.Basic/Actions/IBaseAction.cs @@ -4,7 +4,10 @@ namespace RotationSolver.Basic.Actions; public interface IBaseAction : IAction { - internal static TargetType? TargetOverride { get; set; } = null; + /// + /// The target override. + /// + public static TargetType? TargetOverride { get; set; } = null; internal static bool ForceEnable { get; set; } = false; internal static bool AutoHealCheck { get; set; } = false; internal static bool ActionPreview { get; set; } = false; diff --git a/RotationSolver.Basic/Data/IconSet.cs b/RotationSolver.Basic/Data/IconSet.cs index 53fa66160..fcf992596 100644 --- a/RotationSolver.Basic/Data/IconSet.cs +++ b/RotationSolver.Basic/Data/IconSet.cs @@ -115,7 +115,9 @@ private static byte[] SvgToPng(byte[] data) /// public static bool GetTexture(uint id, out IDalamudTextureWrap texture, uint @default = 0) => ThreadLoadImageHandler.TryGetIconTextureWrap(id, true, out texture) + || ThreadLoadImageHandler.TryGetIconTextureWrap(id, false, out texture) || ThreadLoadImageHandler.TryGetIconTextureWrap(@default, true, out texture) + || ThreadLoadImageHandler.TryGetIconTextureWrap(@default, false, out texture) || ThreadLoadImageHandler.TryGetIconTextureWrap(0, true, out texture); /// @@ -141,7 +143,7 @@ public static bool GetTexture(string path, out IDalamudTextureWrap texture, bool /// public static bool GetTexture(this IAction? action, out IDalamudTextureWrap texture, bool isAdjust = true) { - if (isAdjust) + if (isAdjust && action is IBaseAction) { return GetTexture((ActionID)(action?.AdjustedID ?? 0), out texture); } diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 9373a156c..d8ddef886 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -229,5 +229,7 @@ "PosFlameThrowerName": "", "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "" + "PoslockModifierDescription": "", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled." } \ No newline at end of file From b3c648a1d3a7744df9cfc70a8d97ca24e71090f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:44:17 +0800 Subject: [PATCH 04/48] fix: heal when nothing todo aoe fix. --- .../Actions/ActionTargetInfo.cs | 18 +++++++++++++----- .../Rotations/CustomRotation_GCD.cs | 8 +++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index f5e76eb97..256e800cb 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -41,13 +41,15 @@ private static bool NoAOE #region Target Finder. //The delay of finding the targets. private readonly ObjectListDelay _canTargets = new (() => Service.Config.TargetDelay); - public readonly IEnumerable GetCanTargets(bool skipStatusProvideCheck) + public readonly IEnumerable GetCanTargets(bool skipStatusProvideCheck, TargetType type) { var items = TargetFilter.GetObjectInRadius(DataCenter.AllTargets, Range); var objs = new List(items.Count()); foreach (var obj in items) { + if (type == TargetType.Heal && obj.GetHealthRatio() == 1) continue; + if (!GeneralCheck(obj, skipStatusProvideCheck)) continue; objs.Add(obj); } @@ -56,7 +58,7 @@ public readonly IEnumerable GetCanTargets(bool skipStatusProvideChe return _canTargets; } - public readonly IEnumerable GetCanAffects(bool skipStatusProvideCheck) + public readonly IEnumerable GetCanAffects(bool skipStatusProvideCheck, TargetType type) { if (EffectRange == 0) return []; @@ -64,6 +66,12 @@ public readonly IEnumerable GetCanAffects(bool skipStatusProvideChe ? DataCenter.PartyMembers : DataCenter.AllHostileTargets, Range + EffectRange); + + if (type == TargetType.Heal) + { + items = items.Where(i => i.GetHealthRatio() < 1); + } + var objs = new List(items.Count()); foreach (var obj in items) @@ -198,9 +206,10 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { return new(player, [], player.Position); } + var type = _action.Setting.TargetType; - var canTargets = GetCanTargets(skipStatusProvideCheck); - var canAffects = GetCanAffects(skipStatusProvideCheck); + var canTargets = GetCanTargets(skipStatusProvideCheck, type); + var canAffects = GetCanAffects(skipStatusProvideCheck, type); if (IsTargetArea) { @@ -232,7 +241,6 @@ private readonly bool CheckTimeToKill(GameObject gameObject) var targets = GetMostCanTargetObjects(canTargets, canAffects, skipAoeCheck ? 0 : _action.Config.AoeCount); - var type = _action.Setting.TargetType; if (type == TargetType.BeAttacked && !_action.Setting.IsFriendly) { type = TargetType.Big; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index 96c98fa63..aee1de0b1 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -94,8 +94,14 @@ partial class CustomRotation if (PartyMembersMinHP < Service.Config.HealWhenNothingTodoBelow) { - if (DataCenter.PartyMembersDifferHP < Service.Config.HealthDifference && HealAreaGCD(out act)) return act; + IBaseAction.TargetOverride = TargetType.Heal; + + if (DataCenter.PartyMembersDifferHP < Service.Config.HealthDifference + && DataCenter.PartyMembersHP.Count(i => i < 1) > 2 + && HealAreaGCD(out act)) return act; if (HealSingleGCD(out act)) return act; + + IBaseAction.TargetOverride = null; } } } From 083b25b17aa089efbd5c426596bf3ae050a55ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:34:56 +0800 Subject: [PATCH 05/48] fix: add some default configs. --- Resources/ActionAOECounts.json | 26 ------ Resources/ActionHealRatio.json | 1 - Resources/ActionTTK.json | 14 --- .../Configuration/OtherConfiguration.cs | 89 ++----------------- .../Rotations/Basic/AstrologianRotation.cs | 24 +++++ .../Rotations/Basic/BardRotation.cs | 36 ++++++++ .../Rotations/Basic/BlueMageRotation.cs | 16 ++++ .../Rotations/Basic/DancerRotation.cs | 12 +++ .../Rotations/Basic/DarkKnightRotation.cs | 12 +++ .../Rotations/Basic/GunbreakerRotation.cs | 16 ++++ .../Rotations/Basic/MachinistRotation.cs | 12 +++ .../Rotations/Basic/NinjaRotation.cs | 12 +++ .../Rotations/Basic/ReaperRotation.cs | 9 +- .../Rotations/Basic/RedMageRotation.cs | 11 ++- .../Rotations/Basic/SageRotation.cs | 8 ++ .../Rotations/Basic/SamuraiRotation.cs | 5 +- .../Rotations/Basic/ScholarRotation.cs | 14 ++- .../Rotations/Basic/SummonerRotation.cs | 6 +- .../Rotations/Basic/WarriorRotation.cs | 37 +++++++- .../Rotations/Basic/WhiteMageRotation.cs | 6 +- RotationSolver/Localization/Localization.json | 36 +++++++- 21 files changed, 271 insertions(+), 131 deletions(-) delete mode 100644 Resources/ActionAOECounts.json delete mode 100644 Resources/ActionHealRatio.json delete mode 100644 Resources/ActionTTK.json diff --git a/Resources/ActionAOECounts.json b/Resources/ActionAOECounts.json deleted file mode 100644 index e664b03ea..000000000 --- a/Resources/ActionAOECounts.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "3615": 2, - "11426": 1, - "11427": 1, - "106": 2, - "16494": 2, - "117": 2, - "15994": 2, - "15995": 2, - "15996": 2, - "16008": 2, - "3621": 2, - "16468": 2, - "16141": 2, - "16149": 2, - "2870": 2, - "16497": 2, - "2266": 2, - "7509": 2, - "24379": 2, - "16539": 2, - "24297": 2, - "41": 2, - "16462": 2, - "51": 2 -} \ No newline at end of file diff --git a/Resources/ActionHealRatio.json b/Resources/ActionHealRatio.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/Resources/ActionHealRatio.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/Resources/ActionTTK.json b/Resources/ActionTTK.json deleted file mode 100644 index 7a5b4a09e..000000000 --- a/Resources/ActionTTK.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "3599": 20.0, - "100": 30.0, - "113": 30.0, - "3560": 30.0, - "16499": 10.0, - "61": 10.0, - "66": 12.0, - "24378": 10.0, - "7489": 40.0, - "17864": 20.0, - "24283": 20.0, - "121": 20.0 -} \ No newline at end of file diff --git a/RotationSolver.Basic/Configuration/OtherConfiguration.cs b/RotationSolver.Basic/Configuration/OtherConfiguration.cs index a7b980145..857a67426 100644 --- a/RotationSolver.Basic/Configuration/OtherConfiguration.cs +++ b/RotationSolver.Basic/Configuration/OtherConfiguration.cs @@ -6,67 +6,19 @@ namespace RotationSolver.Basic.Configuration; #pragma warning disable CA2211 public class OtherConfiguration { - public static HashSet HostileCastingArea = new(); - public static HashSet HostileCastingTank = new(); + public static HashSet HostileCastingArea = []; + public static HashSet HostileCastingTank = []; - public static SortedList AnimationLockTime = new(); + public static SortedList AnimationLockTime = []; - public static Dictionary NoHostileNames = new(); - public static Dictionary NoProvokeNames = new(); - public static Dictionary BeneficialPositions = new(); + public static Dictionary NoHostileNames = []; + public static Dictionary NoProvokeNames = []; + public static Dictionary BeneficialPositions = []; - public static HashSet DangerousStatus = new(); - public static HashSet PriorityStatus = new(); + public static HashSet DangerousStatus = []; + public static HashSet PriorityStatus = []; - public static HashSet InvincibleStatus = new(); - - public static Dictionary ActionAOECounts = new() - { - //{ (uint) ActionID.Gravity, 2}, - //{ (uint) ActionID.FeatherRain, 1}, - //{ (uint) ActionID.Eruption, 1}, - //{ (uint) ActionID.QuickNock, 2}, - ////{ (uint) ActionID.ShadowBite, 2}, - //{ (uint) ActionID.RainOfDeath, 2}, - ////{ (uint) ActionID.BladeShower, 2}, - //{ (uint) ActionID.RisingWindmill, 2}, - ////{ (uint) ActionID.BloodShower, 2}, - ////{ (uint) ActionID.FanDance2, 2}, - //{ (uint) ActionID.Unleash, 2}, - //{ (uint) ActionID.StalwartSoul, 2}, - //{ (uint) ActionID.DemonSlice, 2}, - //{ (uint) ActionID.DemonSlaughter, 2}, - //{ (uint) ActionID.SpreadShot, 2}, - //{ (uint) ActionID.AutoCrossbow, 2}, - //{ (uint) ActionID.Katon, 2}, - //{ (uint) ActionID.Scatter, 2}, - //{ (uint) ActionID.WhorlOfDeath, 2}, - //{ (uint) ActionID.ArtOfWar, 2}, - //{ (uint) ActionID.Dyskrasia, 2}, - //{ (uint) ActionID.Overpower, 2}, - //{ (uint) ActionID.MythrilTempest, 2}, - //{ (uint) ActionID.SteelCyclone, 2}, - //{ (uint) ActionID.VariantSpiritDart, 1 }, - ////{ (uint) ActionID.VariantSpiritDart2, 1 }, - }; - - public static Dictionary ActionTTK = new() - { - //{ (uint) ActionID.Combust, 20}, - //{ (uint) ActionID.VenomousBite, 30}, - ////{ (uint) ActionID.WindBite, 30}, - //{ (uint) ActionID.IronJaws, 30}, - ////{ (uint) ActionID.BioBlaster, 10}, - //{ (uint) ActionID.TwinSnakes, 10}, - //{ (uint) ActionID.Demolish, 12}, - //{ (uint) ActionID.ShadowOfDeath, 10}, - //{ (uint) ActionID.Higanbana, 40}, - //{ (uint) ActionID.Bio, 20}, - //{ (uint) ActionID.EukrasianDosis, 20}, - //{ (uint) ActionID.Aero, 20}, - }; - - public static Dictionary ActionHealRatio = new(); + public static HashSet InvincibleStatus = []; public static RotationSolverRecord RotationSolverRecord = new(); @@ -95,12 +47,6 @@ public static void Init() Task.Run(() => InitOne(ref BeneficialPositions, nameof(BeneficialPositions))); - Task.Run(() => InitOne(ref ActionAOECounts, nameof(ActionAOECounts))); - - Task.Run(() => InitOne(ref ActionTTK, nameof(ActionTTK))); - - Task.Run(() => InitOne(ref ActionHealRatio, nameof(ActionHealRatio))); - Task.Run(() => InitOne(ref RotationSolverRecord, nameof(RotationSolverRecord), false)); } @@ -118,9 +64,6 @@ public static Task Save() await SaveBeneficialPositions(); await SaveRotationSolverRecord(); await SaveNoProvokeNames(); - await SaveActionAOECounts(); - await SaveActionTTK(); - await SaveActionHealRatio(); }); } public static Task SavePriorityStatus() @@ -128,20 +71,6 @@ public static Task SavePriorityStatus() return Task.Run(() => Save(PriorityStatus, nameof(PriorityStatus))); } - public static Task SaveActionHealRatio() - { - return Task.Run(() => Save(ActionHealRatio, nameof(ActionHealRatio))); - } - - public static Task SaveActionTTK() - { - return Task.Run(() => Save(ActionTTK, nameof(ActionTTK))); - } - - public static Task SaveActionAOECounts() - { - return Task.Run(() => Save(ActionAOECounts, nameof(ActionAOECounts))); - } public static Task SaveRotationSolverRecord() { return Task.Run(() => Save(RotationSolverRecord, nameof(RotationSolverRecord))); diff --git a/RotationSolver.Basic/Rotations/Basic/AstrologianRotation.cs b/RotationSolver.Basic/Rotations/Basic/AstrologianRotation.cs index ab26e5924..41836d76c 100644 --- a/RotationSolver.Basic/Rotations/Basic/AstrologianRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/AstrologianRotation.cs @@ -113,6 +113,14 @@ static partial void ModifyEarthlyStarPvE(ref ActionSetting setting) }; } + static partial void ModifyGravityPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + static partial void ModifyTheArrowPvE(ref ActionSetting setting) { setting.TargetStatusProvide = StatusHelper.AstCardStatus; @@ -161,6 +169,22 @@ static partial void ModifyTheSpirePvE(ref ActionSetting setting) setting.ActionCheck = () => DrawnCard == CardType.SPIRE; } + static partial void ModifyLightspeedPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; + } + + static partial void ModifyNeutralSectPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + TimeToKill = 15, + }; + } + /// /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/BardRotation.cs b/RotationSolver.Basic/Rotations/Basic/BardRotation.cs index 8898a358d..ed42ad70c 100644 --- a/RotationSolver.Basic/Rotations/Basic/BardRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/BardRotation.cs @@ -88,11 +88,19 @@ static partial void ModifyPitchPerfectPvE(ref ActionSetting setting) static partial void ModifyQuickNockPvE(ref ActionSetting setting) { setting.StatusProvide = [StatusID.ShadowbiteReady]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyShadowbitePvE(ref ActionSetting setting) { setting.StatusNeed = [StatusID.ShadowbiteReady]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyApexArrowPvE(ref ActionSetting setting) @@ -106,9 +114,29 @@ static partial void ModifyBlastArrowPvE(ref ActionSetting setting) setting.StatusNeed = [StatusID.BlastArrowReady]; } + static partial void ModifyRainOfDeathPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + static partial void ModifyRadiantFinalePvE(ref ActionSetting setting) { setting.ActionCheck = () => JobGauge.Coda.Any(s => s != Song.NONE); + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; + } + + static partial void ModifyRagingStrikesPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; } static partial void ModifyTroubadourPvE(ref ActionSetting setting) @@ -117,6 +145,14 @@ static partial void ModifyTroubadourPvE(ref ActionSetting setting) setting.StatusProvide = StatusHelper.RangePhysicalDefense; } + static partial void ModifyBattleVoicePvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; + } + /// [RotationDesc(ActionID.TheWardensPaeanPvE)] protected override bool DispelGCD(out IAction? act) diff --git a/RotationSolver.Basic/Rotations/Basic/BlueMageRotation.cs b/RotationSolver.Basic/Rotations/Basic/BlueMageRotation.cs index 594188723..2ed5c0520 100644 --- a/RotationSolver.Basic/Rotations/Basic/BlueMageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/BlueMageRotation.cs @@ -34,6 +34,22 @@ public enum BLUID : byte protected BLUID BlueId { get; set; } = BLUID.DPS; private protected sealed override IBaseAction Raise => AngelWhisperPvE; + + static partial void ModifyFeatherRainPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 1, + }; + } + + static partial void ModifyEruptionPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 1, + }; + } } ///// diff --git a/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs b/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs index ff9177d64..5d2ba0250 100644 --- a/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs @@ -61,11 +61,19 @@ static partial void ModifyWindmillPvE(ref ActionSetting setting) static partial void ModifyBladeshowerPvE(ref ActionSetting setting) { setting.StatusProvide = [StatusID.SilkenFlow]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyRisingWindmillPvE(ref ActionSetting setting) { setting.StatusNeed = [StatusID.SilkenSymmetry, StatusID.FlourishingSymmetry]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyBloodshowerPvE(ref ActionSetting setting) @@ -77,6 +85,10 @@ static partial void ModifyFanDanceIiPvE(ref ActionSetting setting) { setting.ActionCheck = () => Feathers > 0; setting.StatusProvide = [StatusID.ThreefoldFanDance]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyFanDanceIiiPvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/Basic/DarkKnightRotation.cs b/RotationSolver.Basic/Rotations/Basic/DarkKnightRotation.cs index 8645c0908..e23132f6b 100644 --- a/RotationSolver.Basic/Rotations/Basic/DarkKnightRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DarkKnightRotation.cs @@ -89,6 +89,10 @@ static partial void ModifyQuietusPvE(ref ActionSetting setting) static partial void ModifyStalwartSoulPvE(ref ActionSetting setting) { setting.StatusNeed = [StatusID.SaltedEarth]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifySaltAndDarknessPvE(ref ActionSetting setting) @@ -135,6 +139,14 @@ static partial void ModifyDeliriumPvE(ref ActionSetting setting) }; } + static partial void ModifyUnleashPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + /// protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) { diff --git a/RotationSolver.Basic/Rotations/Basic/GunbreakerRotation.cs b/RotationSolver.Basic/Rotations/Basic/GunbreakerRotation.cs index 1fae085a1..44ce288a1 100644 --- a/RotationSolver.Basic/Rotations/Basic/GunbreakerRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/GunbreakerRotation.cs @@ -119,6 +119,22 @@ static partial void ModifyBloodfestPvE(ref ActionSetting setting) setting.ActionCheck = () => MaxAmmo - Ammo > 1; } + static partial void ModifyDemonSlicePvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + + static partial void ModifyDemonSlaughterPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + /// protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) { diff --git a/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs b/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs index ca2401b3a..7a22740be 100644 --- a/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs @@ -63,6 +63,10 @@ static partial void ModifyHeatBlastPvE(ref ActionSetting setting) static partial void ModifyAutoCrossbowPvE(ref ActionSetting setting) { setting.ActionCheck = () => IsOverheated && !OverheatedEndAfterGCD(); + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyRookAutoturretPvE(ref ActionSetting setting) @@ -101,6 +105,14 @@ static partial void ModifyQueenOverdrivePvE(ref ActionSetting setting) }; } + static partial void ModifySpreadShotPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + static partial void ModifyBarrelStabilizerPvE(ref ActionSetting setting) { setting.ActionCheck = () => Heat <= 50 && InCombat; diff --git a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs index 187dd63d2..a8879548f 100644 --- a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs @@ -74,6 +74,10 @@ static partial void ModifyMeisuiPvE(ref ActionSetting setting) static partial void ModifyMugPvE(ref ActionSetting setting) { setting.ActionCheck = () => JobGauge.Ninki <= 60 && IsLongerThan(10); + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; } static partial void ModifyTrickAttackPvE(ref ActionSetting setting) @@ -124,6 +128,14 @@ public NinjaRotation() HyoshoRanryuPvE.Setting.Ninjutsu = [TenPvE, JinPvE]; } + static partial void ModifyKatonPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + static partial void ModifyShukuchiPvE(ref ActionSetting setting) { setting.TargetType = TargetType.Move; diff --git a/RotationSolver.Basic/Rotations/Basic/ReaperRotation.cs b/RotationSolver.Basic/Rotations/Basic/ReaperRotation.cs index d79a5dd3d..b556528bc 100644 --- a/RotationSolver.Basic/Rotations/Basic/ReaperRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/ReaperRotation.cs @@ -77,6 +77,10 @@ static partial void ModifyWhorlOfDeathPvE(ref ActionSetting setting) { setting.ActionCheck = () => !HasSoulReaver; setting.TargetStatusProvide = [StatusID.DeathsDesign]; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifySoulScythePvE(ref ActionSetting setting) @@ -120,7 +124,10 @@ static partial void ModifyGluttonyPvE(ref ActionSetting setting) static partial void ModifyArcaneCirclePvE(ref ActionSetting setting) { setting.StatusProvide = [StatusID.BloodsownCircle_2972]; - setting.ActionCheck = () => IsLongerThan(10); + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; } static partial void ModifyPlentifulHarvestPvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/Basic/RedMageRotation.cs b/RotationSolver.Basic/Rotations/Basic/RedMageRotation.cs index cd3b990ab..6de795467 100644 --- a/RotationSolver.Basic/Rotations/Basic/RedMageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/RedMageRotation.cs @@ -76,6 +76,10 @@ static partial void ModifyRedoublementPvE(ref ActionSetting setting) static partial void ModifyScatterPvE(ref ActionSetting setting) { setting.StatusNeed = SwiftcastStatus; + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyVerthunderIiPvE(ref ActionSetting setting) @@ -120,8 +124,13 @@ static partial void ModifyEmboldenPvE(ref ActionSetting setting) static partial void ModifyManaficationPvE(ref ActionSetting setting) { - setting.ActionCheck = () => WhiteMana <= 50 && BlackMana <= 50 && InCombat && ManaStacks == 0 && IsLongerThan(10); + setting.ActionCheck = () => WhiteMana <= 50 && BlackMana <= 50 && InCombat && ManaStacks == 0; setting.ComboIdsNot = [ActionID.RipostePvE, ActionID.ZwerchhauPvE, ActionID.ScorchPvE, ActionID.VerflarePvE, ActionID.VerholyPvE]; + + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; } /// diff --git a/RotationSolver.Basic/Rotations/Basic/SageRotation.cs b/RotationSolver.Basic/Rotations/Basic/SageRotation.cs index 8c644e2b2..4539eca0e 100644 --- a/RotationSolver.Basic/Rotations/Basic/SageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/SageRotation.cs @@ -61,6 +61,14 @@ static partial void ModifyEukrasianDosisPvE(ref ActionSetting setting) ]; } + static partial void ModifyDyskrasiaPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + static partial void ModifyToxikonPvE(ref ActionSetting setting) { setting.ActionCheck = () => Addersting > 0; diff --git a/RotationSolver.Basic/Rotations/Basic/SamuraiRotation.cs b/RotationSolver.Basic/Rotations/Basic/SamuraiRotation.cs index 664d4c857..3a2d40b61 100644 --- a/RotationSolver.Basic/Rotations/Basic/SamuraiRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/SamuraiRotation.cs @@ -124,7 +124,10 @@ static partial void ModifyEnpiPvE(ref ActionSetting setting) static partial void ModifyMeikyoShisuiPvE(ref ActionSetting setting) { setting.StatusProvide = [StatusID.MeikyoShisui]; - setting.ActionCheck = () => IsLongerThan(8); + setting.CreateConfig = () => new() + { + TimeToKill = 8, + }; } static partial void ModifyHagakurePvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs index 9085123d0..b0203b1a1 100644 --- a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs @@ -121,7 +121,19 @@ static partial void ModifyAetherflowPvE(ref ActionSetting setting) static partial void ModifyChainStratagemPvE(ref ActionSetting setting) { - setting.ActionCheck = () => InCombat && IsLongerThan(10); + setting.ActionCheck = () => InCombat; + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; + } + + static partial void ModifyArtOfWarPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyDeploymentTacticsPvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/Basic/SummonerRotation.cs b/RotationSolver.Basic/Rotations/Basic/SummonerRotation.cs index fae6b7cf6..2c84851ba 100644 --- a/RotationSolver.Basic/Rotations/Basic/SummonerRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/SummonerRotation.cs @@ -203,7 +203,11 @@ static partial void ModifyRuinIvPvE(ref ActionSetting setting) static partial void ModifySearingLightPvE(ref ActionSetting setting) { setting.StatusProvide = [StatusID.SearingLight]; - setting.ActionCheck = () => InCombat && IsLongerThan(15); + setting.ActionCheck = () => InCombat; + setting.CreateConfig = () => new() + { + TimeToKill = 15, + }; } static partial void ModifyRadiantAegisPvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/Basic/WarriorRotation.cs b/RotationSolver.Basic/Rotations/Basic/WarriorRotation.cs index 825d047ba..18499ae07 100644 --- a/RotationSolver.Basic/Rotations/Basic/WarriorRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/WarriorRotation.cs @@ -39,6 +39,10 @@ static partial void ModifyUpheavalPvE(ref ActionSetting setting) static partial void ModifySteelCyclonePvE(ref ActionSetting setting) { setting.ActionCheck = () => BeastGauge >= 50 || Player.HasStatus(true, StatusID.InnerRelease); + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyPrimalRendPvE(ref ActionSetting setting) @@ -50,17 +54,44 @@ static partial void ModifyPrimalRendPvE(ref ActionSetting setting) static partial void ModifyInfuriatePvE(ref ActionSetting setting) { setting.StatusProvide = [StatusID.NascentChaos]; - setting.ActionCheck = () => HasHostilesInRange && BeastGauge <= 50 && InCombat && IsLongerThan(5); + setting.ActionCheck = () => HasHostilesInRange && BeastGauge <= 50 && InCombat; + setting.CreateConfig = () => new() + { + TimeToKill = 5, + }; } static partial void ModifyInnerReleasePvE(ref ActionSetting setting) { - setting.ActionCheck = () => IsLongerThan(10); + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; } static partial void ModifyBerserkPvE(ref ActionSetting setting) { - setting.ActionCheck = () => HasHostilesInRange && !ActionID.InnerReleasePvE.IsCoolingDown() && IsLongerThan(10); + setting.ActionCheck = () => HasHostilesInRange && !ActionID.InnerReleasePvE.IsCoolingDown(); + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; + } + + static partial void ModifyOverpowerPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; + } + + static partial void ModifyMythrilTempestPvE(ref ActionSetting setting) + { + setting.CreateConfig = () => new() + { + AoeCount = 2, + }; } static partial void ModifyVengeancePvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs b/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs index 26f866b4d..5ac33553c 100644 --- a/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs @@ -98,7 +98,11 @@ static partial void ModifyAfflatusMiseryPvE(ref ActionSetting setting) static partial void ModifyPresenceOfMindPvE(ref ActionSetting setting) { - setting.ActionCheck = () => !IsMoving && IsLongerThan(10); + setting.ActionCheck = () => !IsMoving; + setting.CreateConfig = () => new() + { + TimeToKill = 10, + }; } static partial void ModifyCureIiiPvP(ref ActionSetting setting) diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index d8ddef886..333158add 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -231,5 +231,39 @@ "PoslockModifierName": "The modifier key to unlock the movement temporary", "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", - "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled." + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", + "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", + "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", + "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", + "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", + "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", + "ChangeTargetForFateName": "Select only Fate targets in Fate", + "OnlyAttackInViewName": "Only attack the target in view.", + "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", + "TargetFatePriorityName": "Target Fate priority", + "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", + "TargetQuestPriorityName": "Target quest priority.", + "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", + "ChooseAttackMarkName": "Priority attack targets with attack markers", + "FilterStopMarkName": "Never attack targets with stop markers", + "HostileTypeName": "Engage settings", + "SwitchTargetFriendlyName": "Target allies for friendly actions.", + "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", + "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", + "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", + "BossTimeToKillDescription": "", + "AddEnemyListToHostileDescription": "", + "ChooseAttackMarkDescription": "", + "FilterStopMarkDescription": "", + "HostileTypeDescription": "", + "SwitchTargetFriendlyDescription": "", + "TargetHuntingRelicLevePriorityDescription": "", + "TargetQuestPriorityDescription": "", + "DyingTimeToKillDescription": "", + "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", + "ChangeTargetForFateDescription": "", + "OnlyAttackInViewDescription": "", + "OnlyAttackInVisionConeDescription": "" } \ No newline at end of file From 1c317a3dd67cc57ea75de03caee2d06a67dc0f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:36:07 +0800 Subject: [PATCH 06/48] New translations localization.json (Spanish) --- RotationSolver/Localization/es.json | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/es.json b/RotationSolver/Localization/es.json index 9373a156c..333158add 100644 --- a/RotationSolver/Localization/es.json +++ b/RotationSolver/Localization/es.json @@ -229,5 +229,41 @@ "PosFlameThrowerName": "", "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "" + "PoslockModifierDescription": "", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", + "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", + "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", + "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", + "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", + "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", + "ChangeTargetForFateName": "Select only Fate targets in Fate", + "OnlyAttackInViewName": "Only attack the target in view.", + "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", + "TargetFatePriorityName": "Target Fate priority", + "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", + "TargetQuestPriorityName": "Target quest priority.", + "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", + "ChooseAttackMarkName": "Priority attack targets with attack markers", + "FilterStopMarkName": "Never attack targets with stop markers", + "HostileTypeName": "Engage settings", + "SwitchTargetFriendlyName": "Target allies for friendly actions.", + "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", + "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", + "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", + "BossTimeToKillDescription": "", + "AddEnemyListToHostileDescription": "", + "ChooseAttackMarkDescription": "", + "FilterStopMarkDescription": "", + "HostileTypeDescription": "", + "SwitchTargetFriendlyDescription": "", + "TargetHuntingRelicLevePriorityDescription": "", + "TargetQuestPriorityDescription": "", + "DyingTimeToKillDescription": "", + "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", + "ChangeTargetForFateDescription": "", + "OnlyAttackInViewDescription": "", + "OnlyAttackInVisionConeDescription": "" } \ No newline at end of file From 571e2a6977ba88fdab4c55ad1c38b713049f979d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:36:08 +0800 Subject: [PATCH 07/48] New translations localization.json (German) --- RotationSolver/Localization/de.json | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/de.json b/RotationSolver/Localization/de.json index 9373a156c..333158add 100644 --- a/RotationSolver/Localization/de.json +++ b/RotationSolver/Localization/de.json @@ -229,5 +229,41 @@ "PosFlameThrowerName": "", "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "" + "PoslockModifierDescription": "", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", + "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", + "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", + "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", + "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", + "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", + "ChangeTargetForFateName": "Select only Fate targets in Fate", + "OnlyAttackInViewName": "Only attack the target in view.", + "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", + "TargetFatePriorityName": "Target Fate priority", + "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", + "TargetQuestPriorityName": "Target quest priority.", + "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", + "ChooseAttackMarkName": "Priority attack targets with attack markers", + "FilterStopMarkName": "Never attack targets with stop markers", + "HostileTypeName": "Engage settings", + "SwitchTargetFriendlyName": "Target allies for friendly actions.", + "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", + "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", + "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", + "BossTimeToKillDescription": "", + "AddEnemyListToHostileDescription": "", + "ChooseAttackMarkDescription": "", + "FilterStopMarkDescription": "", + "HostileTypeDescription": "", + "SwitchTargetFriendlyDescription": "", + "TargetHuntingRelicLevePriorityDescription": "", + "TargetQuestPriorityDescription": "", + "DyingTimeToKillDescription": "", + "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", + "ChangeTargetForFateDescription": "", + "OnlyAttackInViewDescription": "", + "OnlyAttackInVisionConeDescription": "" } \ No newline at end of file From af87665df0309ed80bd765d8bdb2e77b9c6646e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:36:09 +0800 Subject: [PATCH 08/48] New translations localization.json (Japanese) --- RotationSolver/Localization/ja.json | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/ja.json b/RotationSolver/Localization/ja.json index 9373a156c..333158add 100644 --- a/RotationSolver/Localization/ja.json +++ b/RotationSolver/Localization/ja.json @@ -229,5 +229,41 @@ "PosFlameThrowerName": "", "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "" + "PoslockModifierDescription": "", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", + "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", + "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", + "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", + "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", + "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", + "ChangeTargetForFateName": "Select only Fate targets in Fate", + "OnlyAttackInViewName": "Only attack the target in view.", + "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", + "TargetFatePriorityName": "Target Fate priority", + "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", + "TargetQuestPriorityName": "Target quest priority.", + "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", + "ChooseAttackMarkName": "Priority attack targets with attack markers", + "FilterStopMarkName": "Never attack targets with stop markers", + "HostileTypeName": "Engage settings", + "SwitchTargetFriendlyName": "Target allies for friendly actions.", + "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", + "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", + "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", + "BossTimeToKillDescription": "", + "AddEnemyListToHostileDescription": "", + "ChooseAttackMarkDescription": "", + "FilterStopMarkDescription": "", + "HostileTypeDescription": "", + "SwitchTargetFriendlyDescription": "", + "TargetHuntingRelicLevePriorityDescription": "", + "TargetQuestPriorityDescription": "", + "DyingTimeToKillDescription": "", + "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", + "ChangeTargetForFateDescription": "", + "OnlyAttackInViewDescription": "", + "OnlyAttackInVisionConeDescription": "" } \ No newline at end of file From 9679fb77fb33d2196440fb1f8b116342ac6ced2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:36:10 +0800 Subject: [PATCH 09/48] New translations localization.json (Chinese Simplified) --- RotationSolver/Localization/zh.json | 74 +++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/RotationSolver/Localization/zh.json b/RotationSolver/Localization/zh.json index 6cece41f2..4d7edb995 100644 --- a/RotationSolver/Localization/zh.json +++ b/RotationSolver/Localization/zh.json @@ -76,7 +76,7 @@ "RotationSolver.Basic.Data.TargetingType.Big": "大型目标", "RotationSolver.Data.UiString.SpecialCommandType_DefenseArea": "群体减伤", "RotationSolver.Basic.Data.OtherCommandType.NextAction": "执行下一个技能", - "RotationSolver.Basic.Data.SpecialCommandType.LimitBreak": "开启一个自动使用群体治疗的窗口期。", + "RotationSolver.Basic.Data.SpecialCommandType.LimitBreak": "开启一个自动使用极限技的窗口期。", "RotationSolver.Data.UiString.ConfigWindow_Helper_RunCommand": "单击左键以执行命令", "RotationSolver.Data.UiString.ConfigWindow_Helper_CopyCommand": "单击右键以复制这条命令", "RotationSolver.Data.UiString.ConfigWindow_Auto_Description": "更改Rotation Solver 自动使用技能的方式。", @@ -87,13 +87,13 @@ "RotationSolver.Data.UiString.ConfigWindow_Auto_HealSingleConditionSet": "强制单体治疗条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_DefenseAreaConditionSet": "强制群体减伤条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_DefenseSingleConditionSet": "强制单体减伤条件", - "RotationSolver.Data.UiString.ConfigWindow_Auto_DispelStancePositionalConditionSet": "Esuna Stance North Forced Condition", + "RotationSolver.Data.UiString.ConfigWindow_Auto_DispelStancePositionalConditionSet": "强制真北条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_RaiseShirkConditionSet": "强制复活退避条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_MoveForwardConditionSet": "强制前移条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_MoveBackConditionSet": "强制后移条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_AntiKnockbackConditionSet": "强制防击退条件", "RotationSolver.Data.UiString.ConfigWindow_Auto_SpeedConditionSet": "强制疾跑条件", - "RotationSolver.Data.UiString.ConfigWindow_Auto_LimitBreakConditionSet": "Limit Break Condition", + "RotationSolver.Data.UiString.ConfigWindow_Auto_LimitBreakConditionSet": "强制极限技条件\n", "RotationSolver.Data.UiString.ActionSequencer_NotDescription": "点击以反转条件。\n是否反转:{0}", "RotationSolver.Data.UiString.ActionSequencer_Delay_Description": "延迟该条件转为True。", "RotationSolver.Basic.Configuration.Conditions.LogicalType.And": "&&", @@ -115,7 +115,7 @@ "HealthForDyingTanksName": "坦克使用无敌技能的血量阈值HP%%", "HealthTankRatioName": "如果坦克职业的HP低于此值,则首先治疗治疗职业。", "HealthHealerRatioName": "如果治疗职业的HP低于此值,则首先治疗治疗职业。", - "HealOutOfCombatName": "Heal party members outside of combat.", + "HealOutOfCombatName": "在战斗外治疗小队成员", "UseHealWhenNotAHealerName": "在玩非治疗者角色时使用治疗能力", "OnlyHotOnTanksName": "只对坦克职业使用单体HOT技能", "HealWhenNothingTodoName": "如果战斗中没有要做的事情,使用GCD来治疗成员。", @@ -205,22 +205,22 @@ "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "技能", "RotationSolver.Data.UiString.ConfigWindow_List_Territories": "地图特定设置", "RotationSolver.Data.UiString.ConfigWindow_Rotation_BetaRotation": "Beta测试循环!", - "RotationSolver.Data.UiString.SpecialCommandType_HealSingle": "Heal Single", - "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "Heal Area", - "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "Defense Single", - "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "How many gcds are needed to add the status.", + "RotationSolver.Data.UiString.SpecialCommandType_HealSingle": "单体治疗", + "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "群体治疗", + "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "单体减伤", + "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "需要多少gcd才能添加状态", "InDebugDescription": "", - "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "How many targets are needed to use this action.", - "RotationSolver.Data.UiString.SpecialCommandType_Speed": "Speed", - "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "Add Events", + "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "需要多少目标才能使用此操作", + "RotationSolver.Data.UiString.SpecialCommandType_Speed": "速度", + "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "添加事件", "RotationSolver.Data.UiString.ConfigWindow_Events_Description": "In this window, you can set what macro will be trigger after using an action.", - "RotationSolver.Data.UiString.ConfigWindow_Events_DutyStart": "Duty Start: ", - "RotationSolver.Data.UiString.ConfigWindow_Events_MacroIndex": "Macro No.", + "RotationSolver.Data.UiString.ConfigWindow_Events_DutyStart": "任务开始 ", + "RotationSolver.Data.UiString.ConfigWindow_Events_MacroIndex": "宏编号", "RotationSolver.Data.UiString.ConfigWindow_Events_ShareMacro": "Is Shared", - "RotationSolver.Data.UiString.ConfigWindow_Events_DutyEnd": "Duty End: ", - "PoslockCastingName": "Lock the movement when casting or when doing some actions.", - "UseStopCastingName": "Stops casting when the target is dead.", - "AutoOpenChestName": "Auto Open the treasure chest", + "RotationSolver.Data.UiString.ConfigWindow_Events_DutyEnd": "任务结束 ", + "PoslockCastingName": "在施法或执行某些操作时锁定移动", + "UseStopCastingName": "在目标死亡时停止施法", + "AutoOpenChestName": "自动开启宝箱", "PoslockCastingDescription": "LT is for gamepad player", "UseStopCastingDescription": "", "AutoOpenChestDescription": "", @@ -228,6 +228,42 @@ "PosTenChiJinName": "", "PosFlameThrowerName": "", "PosImprovisationName": "", - "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "" + "PoslockModifierName": "自定义键位临时取消移动锁定", + "PoslockModifierDescription": "", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", + "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", + "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", + "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", + "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", + "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", + "ChangeTargetForFateName": "Select only Fate targets in Fate", + "OnlyAttackInViewName": "Only attack the target in view.", + "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", + "TargetFatePriorityName": "Target Fate priority", + "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", + "TargetQuestPriorityName": "Target quest priority.", + "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", + "ChooseAttackMarkName": "Priority attack targets with attack markers", + "FilterStopMarkName": "Never attack targets with stop markers", + "HostileTypeName": "Engage settings", + "SwitchTargetFriendlyName": "Target allies for friendly actions.", + "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", + "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", + "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", + "BossTimeToKillDescription": "", + "AddEnemyListToHostileDescription": "", + "ChooseAttackMarkDescription": "", + "FilterStopMarkDescription": "", + "HostileTypeDescription": "", + "SwitchTargetFriendlyDescription": "", + "TargetHuntingRelicLevePriorityDescription": "", + "TargetQuestPriorityDescription": "", + "DyingTimeToKillDescription": "", + "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", + "ChangeTargetForFateDescription": "", + "OnlyAttackInViewDescription": "", + "OnlyAttackInVisionConeDescription": "" } \ No newline at end of file From a2de41ef022656ea91bce804676cedab2a827e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:36:10 +0800 Subject: [PATCH 10/48] New translations localization.json (French) --- RotationSolver/Localization/fr.json | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/fr.json b/RotationSolver/Localization/fr.json index 9373a156c..333158add 100644 --- a/RotationSolver/Localization/fr.json +++ b/RotationSolver/Localization/fr.json @@ -229,5 +229,41 @@ "PosFlameThrowerName": "", "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "" + "PoslockModifierDescription": "", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", + "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", + "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", + "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", + "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", + "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", + "ChangeTargetForFateName": "Select only Fate targets in Fate", + "OnlyAttackInViewName": "Only attack the target in view.", + "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", + "TargetFatePriorityName": "Target Fate priority", + "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", + "TargetQuestPriorityName": "Target quest priority.", + "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", + "ChooseAttackMarkName": "Priority attack targets with attack markers", + "FilterStopMarkName": "Never attack targets with stop markers", + "HostileTypeName": "Engage settings", + "SwitchTargetFriendlyName": "Target allies for friendly actions.", + "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", + "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", + "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", + "BossTimeToKillDescription": "", + "AddEnemyListToHostileDescription": "", + "ChooseAttackMarkDescription": "", + "FilterStopMarkDescription": "", + "HostileTypeDescription": "", + "SwitchTargetFriendlyDescription": "", + "TargetHuntingRelicLevePriorityDescription": "", + "TargetQuestPriorityDescription": "", + "DyingTimeToKillDescription": "", + "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", + "ChangeTargetForFateDescription": "", + "OnlyAttackInViewDescription": "", + "OnlyAttackInVisionConeDescription": "" } \ No newline at end of file From 405242f53dbcb46344b4d7596c1d45b4309862d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:44:35 +0800 Subject: [PATCH 11/48] fix: desc fix. --- RotationSolver/Localization/Localization.json | 27 ++++--------------- .../UI/SearchableConfigs/Searchable.cs | 2 +- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 333158add..db7682ff4 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -198,8 +198,6 @@ "ActionAheadName": "Action Ahead", "SpecialDurationName": "The duration of special windows set by commands", "CountDownAheadName": "The starting when abilities will be used before finishing the countdown", - "WeaponDelayDescription": "", - "ClickingDelayDescription": "", "RotationSolver.Data.UiString.ConfigWindow_List_Description": "In this window, you can set the parameters that can be customised using lists.", "RotationSolver.Data.UiString.ConfigWindow_List_Statuses": "Statuses", "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "Actions", @@ -209,7 +207,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "Heal Area", "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "Defense Single", "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "How many gcds are needed to add the status.", - "InDebugDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "How many targets are needed to use this action.", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "Speed", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "Add Events", @@ -222,14 +219,7 @@ "UseStopCastingName": "Stops casting when the target is dead.", "AutoOpenChestName": "Auto Open the treasure chest", "PoslockCastingDescription": "LT is for gamepad player", - "UseStopCastingDescription": "", - "AutoOpenChestDescription": "", - "PosPassageOfArmsName": "", - "PosTenChiJinName": "", - "PosFlameThrowerName": "", - "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", @@ -253,17 +243,10 @@ "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "BossTimeToKillDescription": "", - "AddEnemyListToHostileDescription": "", - "ChooseAttackMarkDescription": "", - "FilterStopMarkDescription": "", - "HostileTypeDescription": "", - "SwitchTargetFriendlyDescription": "", - "TargetHuntingRelicLevePriorityDescription": "", - "TargetQuestPriorityDescription": "", - "DyingTimeToKillDescription": "", "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "ChangeTargetForFateDescription": "", - "OnlyAttackInViewDescription": "", - "OnlyAttackInVisionConeDescription": "" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition" } \ No newline at end of file diff --git a/RotationSolver/UI/SearchableConfigs/Searchable.cs b/RotationSolver/UI/SearchableConfigs/Searchable.cs index f93daf0b3..b8836c060 100644 --- a/RotationSolver/UI/SearchableConfigs/Searchable.cs +++ b/RotationSolver/UI/SearchableConfigs/Searchable.cs @@ -153,7 +153,7 @@ public virtual string Description get { var ui = _property.GetCustomAttribute(); - if (ui == null) return string.Empty; + if (ui == null || string.IsNullOrEmpty(ui.Description)) return string.Empty; return (_property.Name + "Description").Local(ui.Description); } From 8aa60a29da10eb31a762f6f5945ae53302615804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:36:24 +0800 Subject: [PATCH 12/48] fix: fixed some rotations. --- Directory.Build.props | 2 +- Resources/HostileCastingArea.json | 13 ++++++++++- Resources/RotationSolverRecord.json | 4 ++-- .../Actions/ActionTargetInfo.cs | 6 ++++- RotationSolver.Basic/Actions/IBaseAction.cs | 5 +---- .../Rotations/Basic/BardRotation.cs | 22 ++++++++++++++++++- .../Rotations/Basic/DancerRotation.cs | 5 ----- .../Rotations/Basic/NinjaRotation.cs | 8 +++---- .../Rotations/CustomRotation_Actions.cs | 4 ++++ .../Rotations/CustomRotation_Invoke.cs | 4 +++- RotationSolver/Commands/RSCommands_Actions.cs | 4 ++-- RotationSolver/Localization/Localization.json | 8 ++++++- XIVPainter | 2 +- 13 files changed, 63 insertions(+), 24 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 606eb3b7a..cbae7cbed 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net7.0-windows enable ArchiTed - 4.0.3.1 + 4.0.3.2 x64 AnyCPU latest diff --git a/Resources/HostileCastingArea.json b/Resources/HostileCastingArea.json index 0a55a07bf..1770c2fe8 100644 --- a/Resources/HostileCastingArea.json +++ b/Resources/HostileCastingArea.json @@ -495,5 +495,16 @@ 35384, 35386, 36001, - 20052 + 20052, + 20386, + 9239, + 11325, + 11344, + 11349, + 11484, + 11550, + 11464, + 11612, + 11306, + 11308 ] \ No newline at end of file diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 32dd843af..23514e8b5 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 73576, - "SayingHelloCount": 61, + "ClickingCount": 76809, + "SayingHelloCount": 66, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index 256e800cb..d6606a007 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -221,7 +221,11 @@ private readonly bool CheckTimeToKill(GameObject gameObject) if (t == null) return null; - if (IsSingleTarget) + if (type == TargetType.Move) + { + return null; + } + else if (IsSingleTarget) { if (CanUseTo(t) && CheckStatus(t, skipStatusProvideCheck) && t.DistanceToPlayer() <= range) { diff --git a/RotationSolver.Basic/Actions/IBaseAction.cs b/RotationSolver.Basic/Actions/IBaseAction.cs index 6cfe3cde2..7e3a45e84 100644 --- a/RotationSolver.Basic/Actions/IBaseAction.cs +++ b/RotationSolver.Basic/Actions/IBaseAction.cs @@ -4,10 +4,7 @@ namespace RotationSolver.Basic.Actions; public interface IBaseAction : IAction { - /// - /// The target override. - /// - public static TargetType? TargetOverride { get; set; } = null; + internal static TargetType? TargetOverride { get; set; } = null; internal static bool ForceEnable { get; set; } = false; internal static bool AutoHealCheck { get; set; } = false; internal static bool ActionPreview { get; set; } = false; diff --git a/RotationSolver.Basic/Rotations/Basic/BardRotation.cs b/RotationSolver.Basic/Rotations/Basic/BardRotation.cs index ed42ad70c..85b94f73b 100644 --- a/RotationSolver.Basic/Rotations/Basic/BardRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/BardRotation.cs @@ -79,7 +79,27 @@ static partial void ModifyIronJawsPvE(ref ActionSetting setting) return true; }; } - + + static partial void ModifyPitchPerfectPvP(ref ActionSetting setting) + { + setting.StatusNeed = [StatusID.Repertoire]; + } + + static partial void ModifySilentNocturnePvP(ref ActionSetting setting) + { + setting.StatusProvide = [StatusID.Repertoire]; + } + + static partial void ModifyTheWardensPaeanPvP(ref ActionSetting setting) + { + setting.StatusProvide = [StatusID.Repertoire]; + } + + static partial void ModifyBlastArrowPvP(ref ActionSetting setting) + { + setting.StatusNeed = [StatusID.BlastArrowReady_3142]; + } + static partial void ModifyPitchPerfectPvE(ref ActionSetting setting) { setting.ActionCheck = () => Song == Song.WANDERER && Repertoire > 0; diff --git a/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs b/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs index 5d2ba0250..b75604136 100644 --- a/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs @@ -148,11 +148,6 @@ static partial void ModifyFlourishPvE(ref ActionSetting setting) setting.ActionCheck = () => InCombat; } - static partial void ModifyStandardStepPvE(ref ActionSetting setting) - { - setting.StatusProvide = [StatusID.StandardStep, StatusID.TechnicalStep]; - } - static partial void ModifyTechnicalStepPvE(ref ActionSetting setting) { setting.StatusNeed = [StatusID.StandardFinish]; diff --git a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs index a8879548f..fe78e9253 100644 --- a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs @@ -165,10 +165,10 @@ protected override bool DefenseSingleAbility(out IAction? act) return base.DefenseSingleAbility(out act); } - static partial void ModifySuitonPvE(ref ActionSetting setting) - { - setting.StatusProvide = [StatusID.Suiton]; - } + //static partial void ModifySuitonPvE(ref ActionSetting setting) + //{ + // setting.StatusProvide = [StatusID.Suiton]; + //} static partial void ModifyFleetingRaijuPvE(ref ActionSetting setting) { diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs index 9b7dea3b3..d90df5231 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs @@ -102,21 +102,25 @@ static partial void ModifyStandardissueElixirPvP(ref ActionSetting setting) setting.ActionCheck = () => !HasHostilesInMaxRange && (Player.CurrentMp <= Player.MaxMp / 3 || Player.CurrentHp <= Player.MaxHp / 3) && !IsLastAction(ActionID.StandardissueElixirPvP); + setting.IsFriendly = true; } static partial void ModifyRecuperatePvP(ref ActionSetting setting) { setting.ActionCheck = () => Player.MaxHp - Player.CurrentHp > 15000; + setting.IsFriendly = true; } static partial void ModifyPurifyPvP(ref ActionSetting setting) { setting.TargetType = TargetType.Dispel; + setting.IsFriendly = true; } static partial void ModifySprintPvP(ref ActionSetting setting) { setting.StatusProvide = [StatusID.Sprint_1342]; + setting.IsFriendly = true; } #endregion diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs b/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs index 2275eddcb..f1eb830fa 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Invoke.cs @@ -156,8 +156,10 @@ private void UpdateActions(JobRole role) { IBaseAction.ShouldEndSpecial = false; IBaseAction.IgnoreClipping = true; - IBaseAction.TargetOverride = TargetType.BeAttacked; + var countDown = Service.CountDownTime; + IBaseAction.TargetOverride = countDown < 1 + ? TargetType.Move : TargetType.BeAttacked; if (countDown > 0) { gcdAction = null; diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index b893607b4..2e994f01f 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -58,8 +58,8 @@ public static void DoAction() var hash = SocialUpdater.EncryptString(p); //Don't attack authors and contributors!! - if (RotationUpdater.AuthorHashes.ContainsKey(hash) - || DownloadHelper.ContributorsHash.Contains(hash)) + if (!act1.Setting.IsFriendly && (RotationUpdater.AuthorHashes.ContainsKey(hash) + || DownloadHelper.ContributorsHash.Contains(hash))) { Svc.Chat.PrintError($"Please don't attack RS developers with RS by {act1}!"); return; diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index db7682ff4..6ecfdca68 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -248,5 +248,11 @@ "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", - "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", + "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", + "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", + "DownloadRotationsName": "Auto Download Rotations", + "AutoLoadCustomRotationsName": "Auto load rotations", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib." } \ No newline at end of file diff --git a/XIVPainter b/XIVPainter index f3e605492..03263805d 160000 --- a/XIVPainter +++ b/XIVPainter @@ -1 +1 @@ -Subproject commit f3e6054923f064739095516cf6127ea53bac99bd +Subproject commit 03263805d94b4764ca0efc86613485c263f1715b From 86353061eccbf3ca23296ae0355983f8397b7c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 02:13:51 +0800 Subject: [PATCH 13/48] fix: add lb things. --- Resources/RotationSolverRecord.json | 2 +- .../Actions/ActionBasicInfo.cs | 2 + RotationSolver.Basic/Actions/BaseAction.cs | 2 + .../Rotations/CustomRotation_Actions.cs | 1 + .../Rotations/CustomRotation_GCD.cs | 8 +- .../Getters/RotationGetter.cs | 35 +- .../Properties/Resources.resx | 849 ++++++++++++++++++ RotationSolver/Commands/RSCommands_Actions.cs | 4 +- RotationSolver/Localization/Localization.json | 3 +- 9 files changed, 896 insertions(+), 10 deletions(-) diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 23514e8b5..2992a46cb 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 76809, + "ClickingCount": 77120, "SayingHelloCount": 66, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionBasicInfo.cs b/RotationSolver.Basic/Actions/ActionBasicInfo.cs index dda035000..4ef37d51b 100644 --- a/RotationSolver.Basic/Actions/ActionBasicInfo.cs +++ b/RotationSolver.Basic/Actions/ActionBasicInfo.cs @@ -85,6 +85,8 @@ internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipCombo, b { if (!_action.Config.IsEnabled || !IsOnSlot) return false; + if (IsLimitBreak) return true; + //Disabled. if (DataCenter.DisabledActionSequencer?.Contains(ID) ?? false) return false; diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index f92190197..60629faf1 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -95,8 +95,10 @@ public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool sk } if (!Info.BasicCheck(skipStatusProvideCheck, skipCombo, ignoreCastingCheck)) return false; + if (!Cooldown.CooldownCheck(isEmpty, onLastAbility, ignoreClippingCheck, gcdCountForAbility)) return false; + if (Setting.IsMeleeRange && IActionHelper.IsLastAction(IActionHelper.MovingActions)) return false; //No range actions after moving. if (Setting.IsFriendly && DataCenter.AverageTimeToKill < Config.TimeToKill) return false; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs index d90df5231..ad7132cf6 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs @@ -130,6 +130,7 @@ static partial void ModifySprintPvP(ref ActionSetting setting) private protected virtual IBaseAction? LimitBreak1 => null; private protected virtual IBaseAction? LimitBreak2 => null; private protected virtual IBaseAction? LimitBreak3 => null; + private protected virtual IBaseAction? LimitBreakPvP => null; public virtual IAction[] AllActions => [ diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index aee1de0b1..b6257690d 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -1,4 +1,6 @@ -namespace RotationSolver.Basic.Rotations; +using ECommons.DalamudServices; + +namespace RotationSolver.Basic.Rotations; partial class CustomRotation { @@ -121,7 +123,9 @@ private bool UseLimitBreak(out IAction? act) return LimitBreakLevel switch { - 1 => LimitBreak1?.CanUse(out act, skipAoeCheck: true) ?? false, + 1 => ((DataCenter.Territory?.IsPvpZone ?? false) + ? LimitBreakPvP?.CanUse(out act, skipAoeCheck: true) + : LimitBreak1?.CanUse(out act, skipAoeCheck: true)) ?? false, 2 => LimitBreak2?.CanUse(out act, skipAoeCheck: true) ?? false, 3 => LimitBreak3?.CanUse(out act, skipAoeCheck: true) ?? false, _ => false, diff --git a/RotationSolver.GameData/Getters/RotationGetter.cs b/RotationSolver.GameData/Getters/RotationGetter.cs index 9e388b5b2..50784cc57 100644 --- a/RotationSolver.GameData/Getters/RotationGetter.cs +++ b/RotationSolver.GameData/Getters/RotationGetter.cs @@ -1,5 +1,7 @@ -using Lumina.Excel.GeneratedSheets; +using Lumina.Data; +using Lumina.Excel.GeneratedSheets; using RotationSolver.GameData.Getters.Actions; +using System.Xml.Linq; namespace RotationSolver.GameData.Getters; @@ -48,10 +50,13 @@ public abstract partial class {{GetName()}} : CustomRotation {{GetLBInRotation(job.LimitBreak1.Value, 1)}} {{GetLBInRotation(job.LimitBreak2.Value, 2)}} {{GetLBInRotation(job.LimitBreak3.Value, 3)}} + {{GetLBInRotationPvP(gameData.GetExcelSheet()?.FirstOrDefault(i => i.ActionCategory.Row is 15 + && ((bool?)i.ClassJobCategory.Value?.GetType().GetRuntimeProperty(job.Abbreviation)?.GetValue(i.ClassJobCategory.Value) ?? false)))}} #endregion #region Traits + {{traitsCode.Table()}} {{Util.ArrayNames("AllTraits", "IBaseTrait", @@ -66,7 +71,7 @@ private string GetLBInRotation(Lumina.Excel.GeneratedSheets.Action? action, int if (action == null) return string.Empty; if (action.RowId == 0) return string.Empty; - var code = GetLB(action, out var name); + var code = GetLBPvE(action, out var name); return code + "\n" + $""" /// @@ -76,14 +81,36 @@ private string GetLBInRotation(Lumina.Excel.GeneratedSheets.Action? action, int private sealed protected override IBaseAction LimitBreak{index} => {name}; """; } - - private string GetLB(Lumina.Excel.GeneratedSheets.Action action, out string name) + private string GetLBPvE(Lumina.Excel.GeneratedSheets.Action action, out string name) { name = action.Name.RawString.ToPascalCase() + $"PvE"; var descName = action.GetDescName(); return action.ToCode(name, descName, GetDesc(action), false); } + private string GetLBInRotationPvP(Lumina.Excel.GeneratedSheets.Action? action) + { + if (action == null) return string.Empty; + if (action.RowId == 0) return string.Empty; + + var code = GetLBPvP(action, out var name); + + return code + "\n" + $""" + /// + /// {action.GetDescName()} + /// {GetDesc(action)} + /// + private sealed protected override IBaseAction LimitBreakPvP => {name}; + """; + } + + private string GetLBPvP(Lumina.Excel.GeneratedSheets.Action action, out string name) + { + name = action.Name.RawString.ToPascalCase() + $"PvP"; + var descName = action.GetDescName(); + + return action.ToCode(name, descName, GetDesc(action), false); + } private string GetDesc(Lumina.Excel.GeneratedSheets.Action item) { diff --git a/RotationSolver.SourceGenerators/Properties/Resources.resx b/RotationSolver.SourceGenerators/Properties/Resources.resx index a537e5407..b67af275e 100644 --- a/RotationSolver.SourceGenerators/Properties/Resources.resx +++ b/RotationSolver.SourceGenerators/Properties/Resources.resx @@ -42016,10 +42016,59 @@ private IBaseAction LastBastionPvE => _LastBastionPvECreator.Value; /// <para>Duration: 12s</para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => LastBastionPvE; +private readonly Lazy<IBaseAction> _PhalanxPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29069, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyPhalanxPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29069"><strong>Phalanx</strong></see> <i>PvP</i> (PLD) [29069] [Limit Break] +/// </summary> +static partial void ModifyPhalanxPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29069"><strong>Phalanx</strong></see> <i>PvP</i> (PLD) [29069] [Limit Break] +/// <para>Grants the effect of Hallowed Ground to self and Phalanx to nearby party members.</para> +/// <para>Hallowed Ground Effect: Renders you impervious to most attacks</para> +/// <para>Duration: 10s</para> +/// <para>Phalanx Effect: Reduces damage taken by 33%</para> +/// <para>Duration: 10s</para> +/// <para>Additional Effect: Grants Blade of Faith Ready</para> +/// <para>Duration: 10s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// <para></para> +/// <para>※Action changes to Blade of Faith upon execution.</para> +/// </summary> + +private IBaseAction PhalanxPvP => _PhalanxPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29069"><strong>Phalanx</strong></see> <i>PvP</i> (PLD) [29069] [Limit Break] +/// <para>Grants the effect of Hallowed Ground to self and Phalanx to nearby party members.</para> +/// <para>Hallowed Ground Effect: Renders you impervious to most attacks</para> +/// <para>Duration: 10s</para> +/// <para>Phalanx Effect: Reduces damage taken by 33%</para> +/// <para>Duration: 10s</para> +/// <para>Additional Effect: Grants Blade of Faith Ready</para> +/// <para>Duration: 10s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// <para></para> +/// <para>※Action changes to Blade of Faith upon execution.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => PhalanxPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50207"><strong>Divine Magic Mastery</strong></see> (PLD) [207] /// <para>Halves MP cost for all spells while preventing casting interruptions via damage taken. Grants the following effects after successfully completing a combo with Royal Authority:</para> @@ -43449,10 +43498,55 @@ private IBaseAction FinalHeavenPvE => _FinalHeavenPvECreator.Value; /// <para>Delivers an attack with a potency of 9,000.</para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => FinalHeavenPvE; +private readonly Lazy<IBaseAction> _MeteodrivePvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29485, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyMeteodrivePvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29485"><strong>Meteodrive</strong></see> <i>PvP</i> (MNK) [29485] [Limit Break] +/// </summary> +static partial void ModifyMeteodrivePvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29485"><strong>Meteodrive</strong></see> <i>PvP</i> (MNK) [29485] [Limit Break] +/// <para>Rushes target and delivers an attack with a potency of 12,000.</para> +/// <para>Additional Effect: Removes target's Guard</para> +/// <para>Additional Effect: Afflicts target with Meteodrive, rendering them unable to move</para> +/// <para>Duration: 3s</para> +/// <para>If target does not have an active Guard effect, deals additional damage with a potency of 12,000.</para> +/// <para>Effect cannot be applied to players riding machina or non-player combatants.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 75s</para> +/// <para>Cannot be executed while bound.</para> +/// </summary> + +private IBaseAction MeteodrivePvP => _MeteodrivePvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29485"><strong>Meteodrive</strong></see> <i>PvP</i> (MNK) [29485] [Limit Break] +/// <para>Rushes target and delivers an attack with a potency of 12,000.</para> +/// <para>Additional Effect: Removes target's Guard</para> +/// <para>Additional Effect: Afflicts target with Meteodrive, rendering them unable to move</para> +/// <para>Duration: 3s</para> +/// <para>If target does not have an active Guard effect, deals additional damage with a potency of 12,000.</para> +/// <para>Effect cannot be applied to players riding machina or non-player combatants.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 75s</para> +/// <para>Cannot be executed while bound.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => MeteodrivePvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50160"><strong>Deep Meditation</strong></see> (MNK) [160] /// <para>Grants an 80% chance that a chakra will open upon dealing critical damage with a weaponskill.</para> @@ -44711,10 +44805,61 @@ private IBaseAction LandWakerPvE => _LandWakerPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => LandWakerPvE; +private readonly Lazy<IBaseAction> _PrimalScreamPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29083, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyPrimalScreamPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29083"><strong>Primal Scream</strong></see> <i>PvP</i> (WAR) [29083] [Limit Break] +/// </summary> +static partial void ModifyPrimalScreamPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29083"><strong>Primal Scream</strong></see> <i>PvP</i> (WAR) [29083] [Limit Break] +/// <para>Let out a bloodcurdling cry in a cone before you, rendering enemies unable to use Guard.</para> +/// <para>Duration: 15s</para> +/// <para>Additional Effect: Grants Inner Release and Thrill of Battle</para> +/// <para>Inner Release Effect: Increases the potency of Primal Rend and Chaotic Cyclone, while also increasing movement speed by 25%, and granting immunity to Stun, Heavy, Bind, Silence, Half-asleep, Sleep, Deep Freeze, and knockback and draw-in effects</para> +/// <para>Duration: 15s</para> +/// <para>Cannot execute Guard while under the effect of Inner Release.</para> +/// <para>Thrill of Battle Effect: Increases maximum HP by 20% and restores the amount increased</para> +/// <para>Duration: 30s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Storm's Path Combo changes to Fell Cleave while under the effect of Inner Release.</para> +/// </summary> + +private IBaseAction PrimalScreamPvP => _PrimalScreamPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29083"><strong>Primal Scream</strong></see> <i>PvP</i> (WAR) [29083] [Limit Break] +/// <para>Let out a bloodcurdling cry in a cone before you, rendering enemies unable to use Guard.</para> +/// <para>Duration: 15s</para> +/// <para>Additional Effect: Grants Inner Release and Thrill of Battle</para> +/// <para>Inner Release Effect: Increases the potency of Primal Rend and Chaotic Cyclone, while also increasing movement speed by 25%, and granting immunity to Stun, Heavy, Bind, Silence, Half-asleep, Sleep, Deep Freeze, and knockback and draw-in effects</para> +/// <para>Duration: 15s</para> +/// <para>Cannot execute Guard while under the effect of Inner Release.</para> +/// <para>Thrill of Battle Effect: Increases maximum HP by 20% and restores the amount increased</para> +/// <para>Duration: 30s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Storm's Path Combo changes to Fell Cleave while under the effect of Inner Release.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => PrimalScreamPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50157"><strong>Enhanced Infuriate</strong></see> (WAR) [157] /// <para>Reduces Infuriate recast time by 5 seconds upon landing Inner Beast, Steel Cyclone, Fell Cleave, or Decimate on most targets.</para> @@ -45963,10 +46108,53 @@ private IBaseAction DragonsongDivePvE => _DragonsongDivePvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => DragonsongDivePvE; +private readonly Lazy<IBaseAction> _SkyHighPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29497, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifySkyHighPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29497"><strong>Sky High</strong></see> <i>PvP</i> (DRG) [29497] [Limit Break] +/// </summary> +static partial void ModifySkyHighPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29497"><strong>Sky High</strong></see> <i>PvP</i> (DRG) [29497] [Limit Break] +/// <para>Jump high into the air, preventing enemies from targeting you. Movement is still possible before landing.</para> +/// <para>Duration: 5s</para> +/// <para>Additional Effect: Removes Heavy, Bind, and Half-asleep</para> +/// <para>Executes Sky Shatter automatically when effect duration expires.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Action changes to Sky Shatter upon execution.</para> +/// </summary> + +private IBaseAction SkyHighPvP => _SkyHighPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29497"><strong>Sky High</strong></see> <i>PvP</i> (DRG) [29497] [Limit Break] +/// <para>Jump high into the air, preventing enemies from targeting you. Movement is still possible before landing.</para> +/// <para>Duration: 5s</para> +/// <para>Additional Effect: Removes Heavy, Bind, and Half-asleep</para> +/// <para>Executes Sky Shatter automatically when effect duration expires.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Action changes to Sky Shatter upon execution.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => SkyHighPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50162"><strong>Lance Mastery</strong></see> (DRG) [162] /// <para>Allows for immediate execution of Wheeling Thrust after landing Fang and Claw, or Fang and Claw after landing Wheeling Thrust, increasing potency by 100.</para> @@ -47201,10 +47389,49 @@ private IBaseAction SagittariusArrowPvE => _SagittariusArrowPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => SagittariusArrowPvE; +private readonly Lazy<IBaseAction> _FinalFantasiaPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29401, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyFinalFantasiaPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29401"><strong>Final Fantasia</strong></see> <i>PvP</i> (BRD) [29401] [Limit Break] +/// </summary> +static partial void ModifyFinalFantasiaPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29401"><strong>Final Fantasia</strong></see> <i>PvP</i> (BRD) [29401] [Limit Break] +/// <para>Reduces your weaponskill cast time and recast time by 10% while also increasing movement speed by 25%.</para> +/// <para>Additional Effect: Increases damage dealt by self and all party members within a radius of 30 yalms by 10% while also gradually filling the limit gauge</para> +/// <para>Duration: 30s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// <para>Can only be executed while in combat.</para> +/// </summary> + +private IBaseAction FinalFantasiaPvP => _FinalFantasiaPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29401"><strong>Final Fantasia</strong></see> <i>PvP</i> (BRD) [29401] [Limit Break] +/// <para>Reduces your weaponskill cast time and recast time by 10% while also increasing movement speed by 25%.</para> +/// <para>Additional Effect: Increases damage dealt by self and all party members within a radius of 30 yalms by 10% while also gradually filling the limit gauge</para> +/// <para>Duration: 30s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// <para>Can only be executed while in combat.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => FinalFantasiaPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50017"><strong>Heavier Shot</strong></see> (ARC BRD) [17] /// <para>Adds to Heavy Shot a 20% chance that you will become Straight Shot Ready.</para> @@ -48421,10 +48648,55 @@ private IBaseAction PulseOfLifePvE => _PulseOfLifePvECreator.Value; /// <para>Restores 100% of own HP and the HP of all nearby party members, including ones KO'd.</para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => PulseOfLifePvE; +private readonly Lazy<IBaseAction> _AfflatusPurgationPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29230, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyAfflatusPurgationPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29230"><strong>Afflatus Purgation</strong></see> <i>PvP</i> (WHM) [29230] [Limit Break] +/// </summary> +static partial void ModifyAfflatusPurgationPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29230"><strong>Afflatus Purgation</strong></see> <i>PvP</i> (WHM) [29230] [Limit Break] +/// <para>Deals unaspected damage with a potency of 18,000 to all enemies in a straight line before you.</para> +/// <para>Additional Effect: Stun</para> +/// <para>Duration: 2s</para> +/// <para>Additional Effect: Grants Temperance</para> +/// <para>Temperance Effect: Increases damage dealt and healing potency by 10%, and grants Regen to self and nearby party members within 30 yalms</para> +/// <para>Cure Potency: 3,000</para> +/// <para>Duration: 15s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 60s</para> +/// </summary> + +private IBaseAction AfflatusPurgationPvP => _AfflatusPurgationPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29230"><strong>Afflatus Purgation</strong></see> <i>PvP</i> (WHM) [29230] [Limit Break] +/// <para>Deals unaspected damage with a potency of 18,000 to all enemies in a straight line before you.</para> +/// <para>Additional Effect: Stun</para> +/// <para>Duration: 2s</para> +/// <para>Additional Effect: Grants Temperance</para> +/// <para>Temperance Effect: Increases damage dealt and healing potency by 10%, and grants Regen to self and nearby party members within 30 yalms</para> +/// <para>Cure Potency: 3,000</para> +/// <para>Duration: 15s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 60s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => AfflatusPurgationPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50023"><strong>Maim and Mend</strong></see> (CNJ WHM) [23] /// <para>Increases base action damage and HP restoration by 10%.</para> @@ -49849,10 +50121,57 @@ private IBaseAction MeteorPvE => _MeteorPvECreator.Value; /// <para>Deals unaspected damage with a potency of 6,150 to all enemies near point of impact.</para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => MeteorPvE; +private readonly Lazy<IBaseAction> _SoulResonancePvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29662, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifySoulResonancePvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29662"><strong>Soul Resonance</strong></see> <i>PvP</i> (BLM) [29662] [Limit Break] +/// </summary> +static partial void ModifySoulResonancePvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29662"><strong>Soul Resonance</strong></see> <i>PvP</i> (BLM) [29662] [Limit Break] +/// <para>Grants 6 stacks of Soul Resonance, upgrading Fire to Flare and Blizzard to Freeze.</para> +/// <para>Duration: 30s</para> +/// <para>Additional Effect: Grants Polyglot and Apocatastasis</para> +/// <para>Polyglot Effect: Action changes to Foul</para> +/// <para>Duration: 60s</para> +/// <para>Apocatastasis Effect: Reduces damage taken by 10%</para> +/// <para>Duration: 15s</para> +/// <para>Additional Effect: Reduces the recast time of Superflare by 15 seconds</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 60s</para> +/// </summary> + +private IBaseAction SoulResonancePvP => _SoulResonancePvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29662"><strong>Soul Resonance</strong></see> <i>PvP</i> (BLM) [29662] [Limit Break] +/// <para>Grants 6 stacks of Soul Resonance, upgrading Fire to Flare and Blizzard to Freeze.</para> +/// <para>Duration: 30s</para> +/// <para>Additional Effect: Grants Polyglot and Apocatastasis</para> +/// <para>Polyglot Effect: Action changes to Foul</para> +/// <para>Duration: 60s</para> +/// <para>Apocatastasis Effect: Reduces damage taken by 10%</para> +/// <para>Duration: 15s</para> +/// <para>Additional Effect: Reduces the recast time of Superflare by 15 seconds</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 60s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => SoulResonancePvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50029"><strong>Maim and Mend</strong></see> (THM BLM) [29] /// <para>Increases base action damage and HP restoration by 10%.</para> @@ -52399,10 +52718,53 @@ private IBaseAction TeraflarePvE => _TeraflarePvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => TeraflarePvE; +private readonly Lazy<IBaseAction> _SummonBahamutPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29673, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifySummonBahamutPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29673"><strong>Summon Bahamut</strong></see> <i>PvP</i> (SMN) [29673] [Limit Break] +/// </summary> +static partial void ModifySummonBahamutPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29673"><strong>Summon Bahamut</strong></see> <i>PvP</i> (SMN) [29673] [Limit Break] +/// <para>Enters Dreadwyrm Trance and summons Demi-Bahamut to fight at a designated location.</para> +/// <para>Duration: 20s</para> +/// <para>Demi-Bahamut will execute Megaflare upon being summoned, then execute Wyrmwave automatically, prioritizing targets with the lowest HP within 30 yalms.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Action changes to Enkindle Bahamut upon execution.</para> +/// <para>※Ruin III changes to Astral Impulse while under the effect of Dreadwyrm Trance.</para> +/// </summary> + +private IBaseAction SummonBahamutPvP => _SummonBahamutPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29673"><strong>Summon Bahamut</strong></see> <i>PvP</i> (SMN) [29673] [Limit Break] +/// <para>Enters Dreadwyrm Trance and summons Demi-Bahamut to fight at a designated location.</para> +/// <para>Duration: 20s</para> +/// <para>Demi-Bahamut will execute Megaflare upon being summoned, then execute Wyrmwave automatically, prioritizing targets with the lowest HP within 30 yalms.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Action changes to Enkindle Bahamut upon execution.</para> +/// <para>※Ruin III changes to Astral Impulse while under the effect of Dreadwyrm Trance.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => SummonBahamutPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50066"><strong>Maim and Mend</strong></see> (ACN SMN SCH) [66] /// <para>Increases base action damage and HP restoration by 10%. Also increases base damage and HP restoration of your pet by 10%.</para> @@ -54062,10 +54424,57 @@ private IBaseAction AngelFeathersPvE => _AngelFeathersPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => AngelFeathersPvE; +private readonly Lazy<IBaseAction> _SummonSeraphPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29237, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifySummonSeraphPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29237"><strong>Summon Seraph</strong></see> <i>PvP</i> (SCH) [29237] [Limit Break] +/// </summary> +static partial void ModifySummonSeraphPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29237"><strong>Summon Seraph</strong></see> <i>PvP</i> (SCH) [29237] [Limit Break] +/// <para>Summons Seraph to fight at a designated location.</para> +/// <para>Seraph will execute Seraph Flight immediately after being summoned.</para> +/// <para>Automatically casts Seraphic Veil on party members who suffer damage.</para> +/// <para>Additional Effect: Grants Recitation</para> +/// <para>Recitation Effect: Increases the potency of Galvanize, Biolysis, and Biolytic effects</para> +/// <para>Duration: 15s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Action changes to Consolation upon execution.</para> +/// </summary> + +private IBaseAction SummonSeraphPvP => _SummonSeraphPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29237"><strong>Summon Seraph</strong></see> <i>PvP</i> (SCH) [29237] [Limit Break] +/// <para>Summons Seraph to fight at a designated location.</para> +/// <para>Seraph will execute Seraph Flight immediately after being summoned.</para> +/// <para>Automatically casts Seraphic Veil on party members who suffer damage.</para> +/// <para>Additional Effect: Grants Recitation</para> +/// <para>Recitation Effect: Increases the potency of Galvanize, Biolysis, and Biolytic effects</para> +/// <para>Duration: 15s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// <para></para> +/// <para>※Action changes to Consolation upon execution.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => SummonSeraphPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50066"><strong>Maim and Mend</strong></see> (ACN SMN SCH) [66] /// <para>Increases base action damage and HP restoration by 10%. Also increases base damage and HP restoration of your pet by 10%.</para> @@ -56063,10 +56472,57 @@ private IBaseAction ChimatsuriPvE => _ChimatsuriPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => ChimatsuriPvE; +private readonly Lazy<IBaseAction> _SeitonTenchuPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29515, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifySeitonTenchuPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29515"><strong>Seiton Tenchu</strong></see> <i>PvP</i> (NIN) [29515] [Limit Break] +/// </summary> +static partial void ModifySeitonTenchuPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29515"><strong>Seiton Tenchu</strong></see> <i>PvP</i> (NIN) [29515] [Limit Break] +/// <para>Rushes target and delivers an attack with a potency of 12,000, incapacitating foes whose HP is below 50%.</para> +/// <para>Additional Effect: Afflicts target with Death Link</para> +/// <para>Duration: 4s</para> +/// <para>Defeating an enemy with this action, or one under the effect of Death Link, will grant Unsealed Seiton Tenchu, allowing a second execution of this action.</para> +/// <para>Duration: 8s</para> +/// <para>Effect duration is reduced by 1s each time it is applied to a target with a minimum duration of 4s.</para> +/// <para>Effect cannot be applied to players riding machina or non-player combatants.</para> +/// <para>Can only be executed when the limit gauge is full or while under the effect of Unsealed Seiton Tenchu.</para> +/// <para>Gauge Charge Time: 105s</para> +/// <para>Cannot be executed while bound.</para> +/// </summary> + +private IBaseAction SeitonTenchuPvP => _SeitonTenchuPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29515"><strong>Seiton Tenchu</strong></see> <i>PvP</i> (NIN) [29515] [Limit Break] +/// <para>Rushes target and delivers an attack with a potency of 12,000, incapacitating foes whose HP is below 50%.</para> +/// <para>Additional Effect: Afflicts target with Death Link</para> +/// <para>Duration: 4s</para> +/// <para>Defeating an enemy with this action, or one under the effect of Death Link, will grant Unsealed Seiton Tenchu, allowing a second execution of this action.</para> +/// <para>Duration: 8s</para> +/// <para>Effect duration is reduced by 1s each time it is applied to a target with a minimum duration of 4s.</para> +/// <para>Effect cannot be applied to players riding machina or non-player combatants.</para> +/// <para>Can only be executed when the limit gauge is full or while under the effect of Unsealed Seiton Tenchu.</para> +/// <para>Gauge Charge Time: 105s</para> +/// <para>Cannot be executed while bound.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => SeitonTenchuPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50090"><strong>All Fours</strong></see> (ROG NIN) [90] /// <para>Reduces damage taken when falling.</para> @@ -57462,10 +57918,43 @@ private IBaseAction SatelliteBeamPvE => _SatelliteBeamPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => SatelliteBeamPvE; +private readonly Lazy<IBaseAction> _MarksmansSpitePvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29415, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyMarksmansSpitePvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29415"><strong>Marksman's Spite</strong></see> <i>PvP</i> (MCH) [29415] [Limit Break] +/// </summary> +static partial void ModifyMarksmansSpitePvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29415"><strong>Marksman's Spite</strong></see> <i>PvP</i> (MCH) [29415] [Limit Break] +/// <para>Delivers a ranged attack with a potency of 36,000.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// </summary> + +private IBaseAction MarksmansSpitePvP => _MarksmansSpitePvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29415"><strong>Marksman's Spite</strong></see> <i>PvP</i> (MCH) [29415] [Limit Break] +/// <para>Delivers a ranged attack with a potency of 36,000.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => MarksmansSpitePvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50117"><strong>Increased Action Damage</strong></see> (MCH) [117] /// <para>Increases base action damage and autoturret damage by 10%.</para> @@ -58693,10 +59182,61 @@ private IBaseAction DarkForcePvE => _DarkForcePvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => DarkForcePvE; +private readonly Lazy<IBaseAction> _EventidePvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29097, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyEventidePvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29097"><strong>Eventide</strong></see> <i>PvP</i> (DRK) [29097] [Limit Break] +/// </summary> +static partial void ModifyEventidePvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29097"><strong>Eventide</strong></see> <i>PvP</i> (DRK) [29097] [Limit Break] +/// <para>Delivers an attack with a potency of 6,000 to all enemies in a straight line before and behind you.</para> +/// <para>Potency increases up to 24,000 as HP nears maximum.</para> +/// <para>HP is reduced to 1 upon execution.</para> +/// <para>Additional Effect: Grants Undead Redemption</para> +/// <para>Undead Redemption Effect: Most attacks cannot reduce your HP to less than 1, and you absorb 100% of weaponskill damage dealt as HP</para> +/// <para>Duration: 10s</para> +/// <para>Additional Effect: Grants Blackblood</para> +/// <para>Duration: 10s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 105s</para> +/// <para></para> +/// <para>※Souleater Combo changes to Bloodspiller while under the effect of Blackblood.</para> +/// </summary> + +private IBaseAction EventidePvP => _EventidePvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29097"><strong>Eventide</strong></see> <i>PvP</i> (DRK) [29097] [Limit Break] +/// <para>Delivers an attack with a potency of 6,000 to all enemies in a straight line before and behind you.</para> +/// <para>Potency increases up to 24,000 as HP nears maximum.</para> +/// <para>HP is reduced to 1 upon execution.</para> +/// <para>Additional Effect: Grants Undead Redemption</para> +/// <para>Undead Redemption Effect: Most attacks cannot reduce your HP to less than 1, and you absorb 100% of weaponskill damage dealt as HP</para> +/// <para>Duration: 10s</para> +/// <para>Additional Effect: Grants Blackblood</para> +/// <para>Duration: 10s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 105s</para> +/// <para></para> +/// <para>※Souleater Combo changes to Bloodspiller while under the effect of Blackblood.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => EventidePvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50158"><strong>Blackblood</strong></see> (DRK) [158] /// <para>Allows for Blood Gauge accumulation upon the landing of certain actions.</para> @@ -60367,10 +60907,49 @@ private IBaseAction AstralStasisPvE => _AstralStasisPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => AstralStasisPvE; +private readonly Lazy<IBaseAction> _CelestialRiverPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29255, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyCelestialRiverPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29255"><strong>Celestial River</strong></see> <i>PvP</i> (AST) [29255] [Limit Break] +/// </summary> +static partial void ModifyCelestialRiverPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29255"><strong>Celestial River</strong></see> <i>PvP</i> (AST) [29255] [Limit Break] +/// <para>Reduces damage dealt by nearby enemies by 30%. Effect is reduced by 10% every 5 seconds.</para> +/// <para>Duration: 15s</para> +/// <para>Additional Effect: Increases damage dealt by self and nearby party members by 30%. Effect is reduced by 10% every 5s.</para> +/// <para>Duration: 15s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 105s</para> +/// </summary> + +private IBaseAction CelestialRiverPvP => _CelestialRiverPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29255"><strong>Celestial River</strong></see> <i>PvP</i> (AST) [29255] [Limit Break] +/// <para>Reduces damage dealt by nearby enemies by 30%. Effect is reduced by 10% every 5 seconds.</para> +/// <para>Duration: 15s</para> +/// <para>Additional Effect: Increases damage dealt by self and nearby party members by 30%. Effect is reduced by 10% every 5s.</para> +/// <para>Duration: 15s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 105s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => CelestialRiverPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50122"><strong>Maim and Mend</strong></see> (AST) [122] /// <para>Increases base action damage and HP restoration by 10%.</para> @@ -61839,10 +62418,51 @@ private IBaseAction DoomOfTheLivingPvE => _DoomOfTheLivingPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => DoomOfTheLivingPvE; +private readonly Lazy<IBaseAction> _ZantetsukenPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29537, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyZantetsukenPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29537"><strong>Zantetsuken</strong></see> <i>PvP</i> (SAM) [29537] [Limit Break] +/// </summary> +static partial void ModifyZantetsukenPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29537"><strong>Zantetsuken</strong></see> <i>PvP</i> (SAM) [29537] [Limit Break] +/// <para>Swiftly charges forward, dealing damage to target and all enemies nearby it with a potency of 24,000.</para> +/// <para>If target is afflicted with Kuzushi, deals damage equal to 100% of their maximum HP.</para> +/// <para>Ignores the effects of Guard when dealing damage.</para> +/// <para>The additional effect of Kuzushi cannot be applied to players riding machina and non-player combatants.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// <para>Cannot be executed while bound.</para> +/// </summary> + +private IBaseAction ZantetsukenPvP => _ZantetsukenPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29537"><strong>Zantetsuken</strong></see> <i>PvP</i> (SAM) [29537] [Limit Break] +/// <para>Swiftly charges forward, dealing damage to target and all enemies nearby it with a potency of 24,000.</para> +/// <para>If target is afflicted with Kuzushi, deals damage equal to 100% of their maximum HP.</para> +/// <para>Ignores the effects of Guard when dealing damage.</para> +/// <para>The additional effect of Kuzushi cannot be applied to players riding machina and non-player combatants.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// <para>Cannot be executed while bound.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => ZantetsukenPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50208"><strong>Kenki Mastery II</strong></see> (SAM) [208] /// <para>Increases Kenki Gauge when landing all weaponskills excluding Iaijutsu.</para> @@ -63556,10 +64176,53 @@ private IBaseAction VermilionScourgePvE => _VermilionScourgePvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => VermilionScourgePvE; +private readonly Lazy<IBaseAction> _SouthernCrossPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29704, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifySouthernCrossPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29704"><strong>Southern Cross</strong></see> <i>PvP</i> (RDM) [29704] [Limit Break] +/// </summary> +static partial void ModifySouthernCrossPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29704"><strong>Southern Cross</strong></see> <i>PvP</i> (RDM) [29704] [Limit Break] +/// <para>Emblazons a grand cross on the ground beneath a party member or enemy, restoring HP of party members within range while damaging enemies.</para> +/// <para>Cure Potency: 8,000</para> +/// <para>Damage Potency: 8,000</para> +/// <para>White Shift Effect: Cure potency is increased by 50%</para> +/// <para>Black Shift Effect: Damage potency is increased by 50%</para> +/// <para>Targets standing at the cross's center will receive the effects of this action twice.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// </summary> + +private IBaseAction SouthernCrossPvP => _SouthernCrossPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29704"><strong>Southern Cross</strong></see> <i>PvP</i> (RDM) [29704] [Limit Break] +/// <para>Emblazons a grand cross on the ground beneath a party member or enemy, restoring HP of party members within range while damaging enemies.</para> +/// <para>Cure Potency: 8,000</para> +/// <para>Damage Potency: 8,000</para> +/// <para>White Shift Effect: Cure potency is increased by 50%</para> +/// <para>Black Shift Effect: Damage potency is increased by 50%</para> +/// <para>Targets standing at the cross's center will receive the effects of this action twice.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => SouthernCrossPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50195"><strong>Enhanced Jolt</strong></see> (RDM) [195] /// <para>Upgrades Jolt to Jolt II. Also increases the potency of Verthunder and Veraero to 360, and the potency of Verfire and Verstone to 300.</para> @@ -66984,9 +67647,11 @@ public abstract partial class BlueMageRotation : CustomRotation + #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50219"><strong>Learning</strong></see> (BLU) [219] /// <para>Infrequently grants the ability to discern the secrets of enemy actions wielded in battle and make them your own.</para> @@ -68886,10 +69551,59 @@ private IBaseAction GunmetalSoulPvE => _GunmetalSoulPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => GunmetalSoulPvE; +private readonly Lazy<IBaseAction> _RelentlessRushPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29130, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyRelentlessRushPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29130"><strong>Relentless Rush</strong></see> <i>PvP</i> (GNB) [29130] [Limit Break] +/// </summary> +static partial void ModifyRelentlessRushPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29130"><strong>Relentless Rush</strong></see> <i>PvP</i> (GNB) [29130] [Limit Break] +/// <para>Unleashes a flurry of blade strikes on nearby enemies, dealing damage over time with a potency of 4,000 and reducing damage taken by 25% until effect expires.</para> +/// <para>Duration: 4s</para> +/// <para>Activates Terminal Trigger when effect duration expires.</para> +/// <para>Additional Effect: Afflicts target with Relentless Shrapnel, reducing damage dealt by 4% while increasing damage taken by 4% per stack</para> +/// <para>Duration: 5s</para> +/// <para>Maximum Stacks: 5</para> +/// <para>Can be executed while moving.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 60s</para> +/// <para></para> +/// <para>※Action changes to Terminal Trigger upon execution.</para> +/// </summary> + +private IBaseAction RelentlessRushPvP => _RelentlessRushPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29130"><strong>Relentless Rush</strong></see> <i>PvP</i> (GNB) [29130] [Limit Break] +/// <para>Unleashes a flurry of blade strikes on nearby enemies, dealing damage over time with a potency of 4,000 and reducing damage taken by 25% until effect expires.</para> +/// <para>Duration: 4s</para> +/// <para>Activates Terminal Trigger when effect duration expires.</para> +/// <para>Additional Effect: Afflicts target with Relentless Shrapnel, reducing damage dealt by 4% while increasing damage taken by 4% per stack</para> +/// <para>Duration: 5s</para> +/// <para>Maximum Stacks: 5</para> +/// <para>Can be executed while moving.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 60s</para> +/// <para></para> +/// <para>※Action changes to Terminal Trigger upon execution.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => RelentlessRushPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50257"><strong>Cartridge Charge</strong></see> (GNB) [257] /// <para>Stores a Cartridge in your Powder Gauge after successfully completing a combo with Solid Barrel.</para> @@ -70666,10 +71380,47 @@ private IBaseAction CrimsonLotusPvE => _CrimsonLotusPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => CrimsonLotusPvE; +private readonly Lazy<IBaseAction> _ContradancePvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29432, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyContradancePvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29432"><strong>Contradance</strong></see> <i>PvP</i> (DNC) [29432] [Limit Break] +/// </summary> +static partial void ModifyContradancePvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29432"><strong>Contradance</strong></see> <i>PvP</i> (DNC) [29432] [Limit Break] +/// <para>Charms nearby enemies.</para> +/// <para>Duration: 2s</para> +/// <para>Damage you deal to charmed enemies will extend the effect duration by 2s. This effect can only be extended once.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// </summary> + +private IBaseAction ContradancePvP => _ContradancePvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29432"><strong>Contradance</strong></see> <i>PvP</i> (DNC) [29432] [Limit Break] +/// <para>Charms nearby enemies.</para> +/// <para>Duration: 2s</para> +/// <para>Damage you deal to charmed enemies will extend the effect duration by 2s. This effect can only be extended once.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 90s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => ContradancePvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50251"><strong>Increased Action Damage</strong></see> (DNC) [251] /// <para>Increases base action damage by 10%.</para> @@ -72210,10 +72961,61 @@ private IBaseAction TheEndPvE => _TheEndPvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => TheEndPvE; +private readonly Lazy<IBaseAction> _TenebraeLemurumPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29553, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyTenebraeLemurumPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29553"><strong>Tenebrae Lemurum</strong></see> <i>PvP</i> (RPR) [29553] [Limit Break] +/// </summary> +static partial void ModifyTenebraeLemurumPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29553"><strong>Tenebrae Lemurum</strong></see> <i>PvP</i> (RPR) [29553] [Limit Break] +/// <para>Offers your flesh as a vessel to your avatar, gaining 5 stacks of Enshrouded.</para> +/// <para>Duration: 20s</para> +/// <para>Additional Effect: Afflicts nearby enemies with Hysteria</para> +/// <para>Hysteria Effect: Afflicted enemies are unable to act of their own free will</para> +/// <para>Duration: 2s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 75s</para> +/// <para>Has no effect on players riding machina or non-player combatants.</para> +/// <para></para> +/// <para>※Action changes to Communio while under the effect of Enshrouded.</para> +/// <para>※Grim Swathe changes to Lemure's Slice while under the effect of Enshrouded.</para> +/// <para>※Infernal Slice Combo changes to Void Reaping while under the effect of Enshrouded.</para> +/// </summary> + +private IBaseAction TenebraeLemurumPvP => _TenebraeLemurumPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29553"><strong>Tenebrae Lemurum</strong></see> <i>PvP</i> (RPR) [29553] [Limit Break] +/// <para>Offers your flesh as a vessel to your avatar, gaining 5 stacks of Enshrouded.</para> +/// <para>Duration: 20s</para> +/// <para>Additional Effect: Afflicts nearby enemies with Hysteria</para> +/// <para>Hysteria Effect: Afflicted enemies are unable to act of their own free will</para> +/// <para>Duration: 2s</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 75s</para> +/// <para>Has no effect on players riding machina or non-player combatants.</para> +/// <para></para> +/// <para>※Action changes to Communio while under the effect of Enshrouded.</para> +/// <para>※Grim Swathe changes to Lemure's Slice while under the effect of Enshrouded.</para> +/// <para>※Infernal Slice Combo changes to Void Reaping while under the effect of Enshrouded.</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => TenebraeLemurumPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50379"><strong>Soul Gauge</strong></see> (RPR) [379] /// <para>Allows for Soul Gauge accumulation upon the landing of certain actions, or defeating enemies under the effect of Death's Design.</para> @@ -73579,10 +74381,57 @@ private IBaseAction TechneMakrePvE => _TechneMakrePvECreator.Value; /// <para></para> /// </summary> private sealed protected override IBaseAction LimitBreak3 => TechneMakrePvE; +private readonly Lazy<IBaseAction> _MesotesPvPCreator = new(() => +{ + IBaseAction action = new BaseAction((ActionID)29266, false); + CustomRotation.LoadActionSetting(ref action); + + var setting = action.Setting; + ModifyMesotesPvP(ref setting); + action.Setting = setting; + + return action; +}); + +/// <summary> +/// Modify <see href="https://garlandtools.org/db/#action/29266"><strong>Mesotes</strong></see> <i>PvP</i> (SGE) [29266] [Limit Break] +/// </summary> +static partial void ModifyMesotesPvP(ref ActionSetting setting); + +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29266"><strong>Mesotes</strong></see> <i>PvP</i> (SGE) [29266] [Limit Break] +/// <para>Erects a magicked barrier at a select location, granting party members the effect of Mesotes and afflicting enemies with the effect of Lype when standing inside.</para> +/// <para>Duration: 15s</para> +/// <para>Mesotes Effect: Negates all damage from enemies who are not under the effect of Lype</para> +/// <para>Duration: 5s</para> +/// <para>Lype Effect: Damage over time</para> +/// <para>Potency: 8,000</para> +/// <para>Duration: 5s</para> +/// <para>The barrier can be relocated once before its effect expires, but the effect duration will not be reset.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// </summary> + +private IBaseAction MesotesPvP => _MesotesPvPCreator.Value; +/// <summary> +/// <see href="https://garlandtools.org/db/#action/29266"><strong>Mesotes</strong></see> <i>PvP</i> (SGE) [29266] [Limit Break] +/// <para>Erects a magicked barrier at a select location, granting party members the effect of Mesotes and afflicting enemies with the effect of Lype when standing inside.</para> +/// <para>Duration: 15s</para> +/// <para>Mesotes Effect: Negates all damage from enemies who are not under the effect of Lype</para> +/// <para>Duration: 5s</para> +/// <para>Lype Effect: Damage over time</para> +/// <para>Potency: 8,000</para> +/// <para>Duration: 5s</para> +/// <para>The barrier can be relocated once before its effect expires, but the effect duration will not be reset.</para> +/// <para>Can only be executed when the limit gauge is full.</para> +/// <para>Gauge Charge Time: 120s</para> +/// </summary> +private sealed protected override IBaseAction LimitBreakPvP => MesotesPvP; #endregion #region Traits + /// <summary> /// <see href="https://garlandtools.org/db/#action/50368"><strong>Maim and Mend</strong></see> (SGE) [368] /// <para>Increases base action damage and HP restoration by 10%.</para> diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index 2e994f01f..e901d3b01 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -53,12 +53,12 @@ public static void DoAction() if (nextAction is BaseAction act1 && act1.Info.IsPvP && !act1.Setting.IsFriendly && act1.TargetInfo.IsSingleTarget - && act1.Target?.Target is PlayerCharacter p/* && p != Player.Object*/) + && act1.Target?.Target is PlayerCharacter p && p != Player.Object) { var hash = SocialUpdater.EncryptString(p); //Don't attack authors and contributors!! - if (!act1.Setting.IsFriendly && (RotationUpdater.AuthorHashes.ContainsKey(hash) + if ((RotationUpdater.AuthorHashes.ContainsKey(hash) || DownloadHelper.ContributorsHash.Contains(hash))) { Svc.Chat.PrintError($"Please don't attack RS developers with RS by {act1}!"); diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 6ecfdca68..6c3d53656 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -254,5 +254,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", - "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib." + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" } \ No newline at end of file From 6a5998c6b351fe9128ad3f0e30a634d228509d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 15:08:17 +0800 Subject: [PATCH 14/48] fix: fixed manual target thing. --- Resources/RotationSolverRecord.json | 4 ++-- RotationSolver.Basic/Actions/ActionTargetInfo.cs | 2 +- RotationSolver.Basic/Actions/BaseAction.cs | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 2992a46cb..102d5e144 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 77120, - "SayingHelloCount": 66, + "ClickingCount": 77828, + "SayingHelloCount": 67, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index d6606a007..330c29930 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -219,7 +219,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { var t = Svc.Targets.Target as BattleChara; - if (t == null) return null; + if (t == null || !_action.Setting.CanTarget(t)) return null; if (type == TargetType.Move) { diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index 60629faf1..884bed93b 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -59,6 +59,10 @@ public ActionConfig Config { Service.Config.RotationActionConfig[ID] = value = Setting.CreateConfig?.Invoke() ?? new(); + if (Setting.TargetStatusProvide != null) + { + value.TimeToKill = 10; + } } return value; } @@ -100,7 +104,7 @@ public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool sk if (Setting.IsMeleeRange && IActionHelper.IsLastAction(IActionHelper.MovingActions)) return false; //No range actions after moving. - if (Setting.IsFriendly && DataCenter.AverageTimeToKill < Config.TimeToKill) return false; + if (DataCenter.AverageTimeToKill < Config.TimeToKill) return false; PreviewTarget = TargetInfo.FindTarget(skipAoeCheck, skipStatusProvideCheck); if (PreviewTarget == null) return false; From f82d01c05eed816fb716fa02574818eeea440696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:14:40 +0800 Subject: [PATCH 15/48] New translations localization.json (Spanish) --- RotationSolver/Localization/es.json | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/RotationSolver/Localization/es.json b/RotationSolver/Localization/es.json index 333158add..6c3d53656 100644 --- a/RotationSolver/Localization/es.json +++ b/RotationSolver/Localization/es.json @@ -198,8 +198,6 @@ "ActionAheadName": "Action Ahead", "SpecialDurationName": "The duration of special windows set by commands", "CountDownAheadName": "The starting when abilities will be used before finishing the countdown", - "WeaponDelayDescription": "", - "ClickingDelayDescription": "", "RotationSolver.Data.UiString.ConfigWindow_List_Description": "In this window, you can set the parameters that can be customised using lists.", "RotationSolver.Data.UiString.ConfigWindow_List_Statuses": "Statuses", "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "Actions", @@ -209,7 +207,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "Heal Area", "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "Defense Single", "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "How many gcds are needed to add the status.", - "InDebugDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "How many targets are needed to use this action.", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "Speed", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "Add Events", @@ -222,14 +219,7 @@ "UseStopCastingName": "Stops casting when the target is dead.", "AutoOpenChestName": "Auto Open the treasure chest", "PoslockCastingDescription": "LT is for gamepad player", - "UseStopCastingDescription": "", - "AutoOpenChestDescription": "", - "PosPassageOfArmsName": "", - "PosTenChiJinName": "", - "PosFlameThrowerName": "", - "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", @@ -253,17 +243,17 @@ "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "BossTimeToKillDescription": "", - "AddEnemyListToHostileDescription": "", - "ChooseAttackMarkDescription": "", - "FilterStopMarkDescription": "", - "HostileTypeDescription": "", - "SwitchTargetFriendlyDescription": "", - "TargetHuntingRelicLevePriorityDescription": "", - "TargetQuestPriorityDescription": "", - "DyingTimeToKillDescription": "", "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "ChangeTargetForFateDescription": "", - "OnlyAttackInViewDescription": "", - "OnlyAttackInVisionConeDescription": "" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", + "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", + "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", + "DownloadRotationsName": "Auto Download Rotations", + "AutoLoadCustomRotationsName": "Auto load rotations", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" } \ No newline at end of file From 4717c6c047e39252664082b196dea81c6825bc89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:14:41 +0800 Subject: [PATCH 16/48] New translations localization.json (German) --- RotationSolver/Localization/de.json | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/RotationSolver/Localization/de.json b/RotationSolver/Localization/de.json index 333158add..6c3d53656 100644 --- a/RotationSolver/Localization/de.json +++ b/RotationSolver/Localization/de.json @@ -198,8 +198,6 @@ "ActionAheadName": "Action Ahead", "SpecialDurationName": "The duration of special windows set by commands", "CountDownAheadName": "The starting when abilities will be used before finishing the countdown", - "WeaponDelayDescription": "", - "ClickingDelayDescription": "", "RotationSolver.Data.UiString.ConfigWindow_List_Description": "In this window, you can set the parameters that can be customised using lists.", "RotationSolver.Data.UiString.ConfigWindow_List_Statuses": "Statuses", "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "Actions", @@ -209,7 +207,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "Heal Area", "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "Defense Single", "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "How many gcds are needed to add the status.", - "InDebugDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "How many targets are needed to use this action.", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "Speed", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "Add Events", @@ -222,14 +219,7 @@ "UseStopCastingName": "Stops casting when the target is dead.", "AutoOpenChestName": "Auto Open the treasure chest", "PoslockCastingDescription": "LT is for gamepad player", - "UseStopCastingDescription": "", - "AutoOpenChestDescription": "", - "PosPassageOfArmsName": "", - "PosTenChiJinName": "", - "PosFlameThrowerName": "", - "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", @@ -253,17 +243,17 @@ "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "BossTimeToKillDescription": "", - "AddEnemyListToHostileDescription": "", - "ChooseAttackMarkDescription": "", - "FilterStopMarkDescription": "", - "HostileTypeDescription": "", - "SwitchTargetFriendlyDescription": "", - "TargetHuntingRelicLevePriorityDescription": "", - "TargetQuestPriorityDescription": "", - "DyingTimeToKillDescription": "", "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "ChangeTargetForFateDescription": "", - "OnlyAttackInViewDescription": "", - "OnlyAttackInVisionConeDescription": "" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", + "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", + "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", + "DownloadRotationsName": "Auto Download Rotations", + "AutoLoadCustomRotationsName": "Auto load rotations", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" } \ No newline at end of file From cb40f92ae5b8722a6f77ddb09e6d31bef92b0dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:14:42 +0800 Subject: [PATCH 17/48] New translations localization.json (Japanese) --- RotationSolver/Localization/ja.json | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/RotationSolver/Localization/ja.json b/RotationSolver/Localization/ja.json index 333158add..6c3d53656 100644 --- a/RotationSolver/Localization/ja.json +++ b/RotationSolver/Localization/ja.json @@ -198,8 +198,6 @@ "ActionAheadName": "Action Ahead", "SpecialDurationName": "The duration of special windows set by commands", "CountDownAheadName": "The starting when abilities will be used before finishing the countdown", - "WeaponDelayDescription": "", - "ClickingDelayDescription": "", "RotationSolver.Data.UiString.ConfigWindow_List_Description": "In this window, you can set the parameters that can be customised using lists.", "RotationSolver.Data.UiString.ConfigWindow_List_Statuses": "Statuses", "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "Actions", @@ -209,7 +207,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "Heal Area", "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "Defense Single", "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "How many gcds are needed to add the status.", - "InDebugDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "How many targets are needed to use this action.", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "Speed", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "Add Events", @@ -222,14 +219,7 @@ "UseStopCastingName": "Stops casting when the target is dead.", "AutoOpenChestName": "Auto Open the treasure chest", "PoslockCastingDescription": "LT is for gamepad player", - "UseStopCastingDescription": "", - "AutoOpenChestDescription": "", - "PosPassageOfArmsName": "", - "PosTenChiJinName": "", - "PosFlameThrowerName": "", - "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", @@ -253,17 +243,17 @@ "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "BossTimeToKillDescription": "", - "AddEnemyListToHostileDescription": "", - "ChooseAttackMarkDescription": "", - "FilterStopMarkDescription": "", - "HostileTypeDescription": "", - "SwitchTargetFriendlyDescription": "", - "TargetHuntingRelicLevePriorityDescription": "", - "TargetQuestPriorityDescription": "", - "DyingTimeToKillDescription": "", "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "ChangeTargetForFateDescription": "", - "OnlyAttackInViewDescription": "", - "OnlyAttackInVisionConeDescription": "" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", + "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", + "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", + "DownloadRotationsName": "Auto Download Rotations", + "AutoLoadCustomRotationsName": "Auto load rotations", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" } \ No newline at end of file From b9224397ff7c8fe7121688b569af16fd53b40db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:14:43 +0800 Subject: [PATCH 18/48] New translations localization.json (Chinese Simplified) --- RotationSolver/Localization/zh.json | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/RotationSolver/Localization/zh.json b/RotationSolver/Localization/zh.json index 4d7edb995..f9dbaf1e0 100644 --- a/RotationSolver/Localization/zh.json +++ b/RotationSolver/Localization/zh.json @@ -198,8 +198,6 @@ "ActionAheadName": "技能点击提前量", "SpecialDurationName": "设置命令所设置的特殊窗口期的持续时间", "CountDownAheadName": "完成倒计时前开始使用技能的提前量", - "WeaponDelayDescription": "", - "ClickingDelayDescription": "", "RotationSolver.Data.UiString.ConfigWindow_List_Description": "在此窗口中,您可以设置可以使用列表自定义的参数。", "RotationSolver.Data.UiString.ConfigWindow_List_Statuses": "状态", "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "技能", @@ -209,7 +207,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "群体治疗", "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "单体减伤", "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "需要多少gcd才能添加状态", - "InDebugDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "需要多少目标才能使用此操作", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "速度", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "添加事件", @@ -222,14 +219,7 @@ "UseStopCastingName": "在目标死亡时停止施法", "AutoOpenChestName": "自动开启宝箱", "PoslockCastingDescription": "LT is for gamepad player", - "UseStopCastingDescription": "", - "AutoOpenChestDescription": "", - "PosPassageOfArmsName": "", - "PosTenChiJinName": "", - "PosFlameThrowerName": "", - "PosImprovisationName": "", "PoslockModifierName": "自定义键位临时取消移动锁定", - "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", @@ -253,17 +243,17 @@ "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "BossTimeToKillDescription": "", - "AddEnemyListToHostileDescription": "", - "ChooseAttackMarkDescription": "", - "FilterStopMarkDescription": "", - "HostileTypeDescription": "", - "SwitchTargetFriendlyDescription": "", - "TargetHuntingRelicLevePriorityDescription": "", - "TargetQuestPriorityDescription": "", - "DyingTimeToKillDescription": "", "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "ChangeTargetForFateDescription": "", - "OnlyAttackInViewDescription": "", - "OnlyAttackInVisionConeDescription": "" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", + "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", + "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", + "DownloadRotationsName": "Auto Download Rotations", + "AutoLoadCustomRotationsName": "Auto load rotations", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" } \ No newline at end of file From 37c0e7ed6fd37c475cf75a28867fe43174ef7286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:14:43 +0800 Subject: [PATCH 19/48] New translations localization.json (French) --- RotationSolver/Localization/fr.json | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/RotationSolver/Localization/fr.json b/RotationSolver/Localization/fr.json index 333158add..6c3d53656 100644 --- a/RotationSolver/Localization/fr.json +++ b/RotationSolver/Localization/fr.json @@ -198,8 +198,6 @@ "ActionAheadName": "Action Ahead", "SpecialDurationName": "The duration of special windows set by commands", "CountDownAheadName": "The starting when abilities will be used before finishing the countdown", - "WeaponDelayDescription": "", - "ClickingDelayDescription": "", "RotationSolver.Data.UiString.ConfigWindow_List_Description": "In this window, you can set the parameters that can be customised using lists.", "RotationSolver.Data.UiString.ConfigWindow_List_Statuses": "Statuses", "RotationSolver.Data.UiString.ConfigWindow_List_Actions": "Actions", @@ -209,7 +207,6 @@ "RotationSolver.Data.UiString.SpecialCommandType_HealArea": "Heal Area", "RotationSolver.Data.UiString.SpecialCommandType_DefenseSingle": "Defense Single", "RotationSolver.Data.UiString.ConfigWindow_Actions_GcdCount": "How many gcds are needed to add the status.", - "InDebugDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "How many targets are needed to use this action.", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "Speed", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "Add Events", @@ -222,14 +219,7 @@ "UseStopCastingName": "Stops casting when the target is dead.", "AutoOpenChestName": "Auto Open the treasure chest", "PoslockCastingDescription": "LT is for gamepad player", - "UseStopCastingDescription": "", - "AutoOpenChestDescription": "", - "PosPassageOfArmsName": "", - "PosTenChiJinName": "", - "PosFlameThrowerName": "", - "PosImprovisationName": "", "PoslockModifierName": "The modifier key to unlock the movement temporary", - "PoslockModifierDescription": "", "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", @@ -253,17 +243,17 @@ "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "BossTimeToKillDescription": "", - "AddEnemyListToHostileDescription": "", - "ChooseAttackMarkDescription": "", - "FilterStopMarkDescription": "", - "HostileTypeDescription": "", - "SwitchTargetFriendlyDescription": "", - "TargetHuntingRelicLevePriorityDescription": "", - "TargetQuestPriorityDescription": "", - "DyingTimeToKillDescription": "", "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "ChangeTargetForFateDescription": "", - "OnlyAttackInViewDescription": "", - "OnlyAttackInVisionConeDescription": "" + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", + "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", + "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", + "DownloadRotationsName": "Auto Download Rotations", + "AutoLoadCustomRotationsName": "Auto load rotations", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" } \ No newline at end of file From 9b04b049d8947aeed110fe2711afe67713f4e95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 19:22:08 +0800 Subject: [PATCH 20/48] fix: feint target. --- Resources/HostileCastingArea.json | 3 ++- Resources/RotationSolverRecord.json | 4 ++-- RotationSolver.Basic/Actions/ActionSetting.cs | 19 ++++++++++++++++++- .../Actions/ActionTargetInfo.cs | 12 ++++++------ RotationSolver.Basic/Configuration/Configs.cs | 2 +- .../Rotations/CustomRotation_Ability.cs | 2 +- RotationSolver/Localization/Localization.json | 11 ++++++++++- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/Resources/HostileCastingArea.json b/Resources/HostileCastingArea.json index 1770c2fe8..6f2ed079f 100644 --- a/Resources/HostileCastingArea.json +++ b/Resources/HostileCastingArea.json @@ -506,5 +506,6 @@ 11464, 11612, 11306, - 11308 + 11308, + 33139 ] \ No newline at end of file diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 102d5e144..4af6db0f5 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 77828, - "SayingHelloCount": 67, + "ClickingCount": 81534, + "SayingHelloCount": 72, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionSetting.cs b/RotationSolver.Basic/Actions/ActionSetting.cs index 920863cc4..3739bacb0 100644 --- a/RotationSolver.Basic/Actions/ActionSetting.cs +++ b/RotationSolver.Basic/Actions/ActionSetting.cs @@ -35,7 +35,24 @@ public class ActionSetting() private TargetType _type = TargetType.Big; public TargetType TargetType { - get => IBaseAction.TargetOverride ?? _type; + get + { + var type = IBaseAction.TargetOverride ?? _type; + if (IsFriendly) + { + + } + else + { + switch (type) + { + case TargetType.BeAttacked: + return _type; + } + } + + return type; + } set => _type = value; } diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index 330c29930..ccbbc0d45 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -557,12 +557,12 @@ private readonly bool CanGetTarget(GameObject target, GameObject subTarget) BattleChara? FindTargetForMoving() { - if (!IBaseAction.ActionPreview && !DataCenter.MergedStatus.HasFlag(AutoStatus.MoveForward)) - { - var o = gameObjects.MinBy(b => b.DistanceToPlayer()); - if (o.DistanceToPlayer() < 1) return o; - return null; - } + //if (!IBaseAction.ActionPreview && !DataCenter.MergedStatus.HasFlag(AutoStatus.MoveForward)) + //{ + // var o = gameObjects.MinBy(b => b.DistanceToPlayer()); + // if (o.DistanceToPlayer() < 1) return o; + // return null; + //} const float DISTANCE_TO_MOVE = 3; diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 83b52feb4..c9c153793 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -140,7 +140,7 @@ public const string private static readonly bool _showTarget = true; [ConditionBool, UI("Show the target's time to kill.", - Parent = UiOverlay)] + Parent = nameof(ShowTarget))] private static readonly bool _showTargetTimeToKill = false; [ConditionBool, UI("Priority attack targets with attack markers", diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs index e7a29f73b..60b0866d9 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs @@ -45,7 +45,7 @@ private bool Ability(IAction nextGCD, out IAction? act) if (DataCenter.MergedStatus.HasFlag(AutoStatus.Positional) && role == JobRole.Melee && !(Player?.HasStatus(false, StatusHelper.NoPositionalStatus) ?? true)) { - if (TrueNorthPvE.CanUse(out act, isEmpty: true)) return true; + if (TrueNorthPvE.CanUse(out act, isEmpty: true, onLastAbility: true)) return true; } IBaseAction.TargetOverride = TargetType.Heal; diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 6c3d53656..3ca7a3b84 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -255,5 +255,14 @@ "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", - "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", + "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "DrawingHeightName": "The height of the drawing things.", + "SampleLengthName": "Drawing smoothness.", + "BeneficialPositionColorName": "The color of beneficial AoE positions", + "HoveredBeneficialPositionColorName": "The color of the hovered beneficial position", + "StateIconHeightName": "State icon height", + "StateIconSizeName": "State icon size", + "TargetIconSizeName": "The size of the next ability that will be used icon.", + "ShowTargetTimeToKillName": "Show the target's time to kill." } \ No newline at end of file From 34739eb59c2da41673802b9a422b3352e9833f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Sun, 25 Feb 2024 19:22:27 +0800 Subject: [PATCH 21/48] chore: 4.0.3.3 released. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index cbae7cbed..2f09d09a7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net7.0-windows enable ArchiTed - 4.0.3.2 + 4.0.3.3 x64 AnyCPU latest From b8a5f9ad48351a925287d9f8f2e88882e58be1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Mon, 26 Feb 2024 16:32:53 +0800 Subject: [PATCH 22/48] feat: add offset delay. --- Resources/HostileCastingArea.json | 6 ++- Resources/RotationSolverRecord.json | 4 +- .../Conditions/DelayCondition.cs | 9 ++++- RotationSolver.Basic/Data/OffsetDelay.cs | 39 +++++++++++++++++++ RotationSolver.Basic/Data/RandomDelay.cs | 2 +- RotationSolver/Data/UiString.cs | 3 ++ RotationSolver/UI/ConditionDrawer.cs | 9 ++++- 7 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 RotationSolver.Basic/Data/OffsetDelay.cs diff --git a/Resources/HostileCastingArea.json b/Resources/HostileCastingArea.json index 6f2ed079f..bbabcf426 100644 --- a/Resources/HostileCastingArea.json +++ b/Resources/HostileCastingArea.json @@ -507,5 +507,9 @@ 11612, 11306, 11308, - 33139 + 33139, + 21009, + 26071, + 26072, + 26064 ] \ No newline at end of file diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 4af6db0f5..7a517daa9 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 81534, - "SayingHelloCount": 72, + "ClickingCount": 84394, + "SayingHelloCount": 73, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Configuration/Conditions/DelayCondition.cs b/RotationSolver.Basic/Configuration/Conditions/DelayCondition.cs index 02158cc06..950398c04 100644 --- a/RotationSolver.Basic/Configuration/Conditions/DelayCondition.cs +++ b/RotationSolver.Basic/Configuration/Conditions/DelayCondition.cs @@ -6,8 +6,10 @@ internal abstract class DelayCondition : ICondition { public float DelayMin = 0; public float DelayMax = 0; + public float DelayOffset = 0; RandomDelay _delay = default; + OffsetDelay _offsetDelay = default; public bool Not = false; @@ -31,13 +33,18 @@ public bool IsTrue(ICustomRotation? rotation) _delay = new(() => (DelayMin, DelayMax)); } + if(_offsetDelay.GetDelay == null) + { + _offsetDelay = new(() => DelayOffset); + } + _callingStack.Push(this); var value = CheckBefore(rotation) && IsTrueInside(rotation); if (Not) { value = !value; } - var result = _delay.Delay(value); + var result = _delay.Delay(_offsetDelay.Delay(value)); _callingStack.Pop(); return result; diff --git a/RotationSolver.Basic/Data/OffsetDelay.cs b/RotationSolver.Basic/Data/OffsetDelay.cs new file mode 100644 index 000000000..0f1fff8a7 --- /dev/null +++ b/RotationSolver.Basic/Data/OffsetDelay.cs @@ -0,0 +1,39 @@ +namespace RotationSolver.Basic.Data; + +/// +/// Off set the whole bool +/// +/// +public struct OffsetDelay(Func getDelay) +{ + bool _lastValue = false; + bool _nowValue = false; + readonly Queue _changeTimes = new(); + + /// + /// + /// + public readonly Func GetDelay => getDelay; + + /// + /// Delay the value to change. + /// + /// + /// + public bool Delay(bool originData) + { + if (originData != _lastValue) + { + _lastValue = originData; + _changeTimes.Enqueue(DateTime.Now + TimeSpan.FromSeconds(GetDelay())); + } + + if (_changeTimes.TryPeek(out var time) && time < DateTime.Now) + { + _changeTimes.Dequeue(); + _nowValue = !_nowValue; + } + + return _nowValue; + } +} diff --git a/RotationSolver.Basic/Data/RandomDelay.cs b/RotationSolver.Basic/Data/RandomDelay.cs index 832aca770..e768add33 100644 --- a/RotationSolver.Basic/Data/RandomDelay.cs +++ b/RotationSolver.Basic/Data/RandomDelay.cs @@ -31,7 +31,7 @@ public RandomDelay(Func getRange) } /// - /// Delay the bool. + /// Delay the bool to be true. /// /// /// diff --git a/RotationSolver/Data/UiString.cs b/RotationSolver/Data/UiString.cs index fe882bc37..56d31eb99 100644 --- a/RotationSolver/Data/UiString.cs +++ b/RotationSolver/Data/UiString.cs @@ -450,6 +450,9 @@ internal enum UiString [Description("Delay its turning to true.")] ActionSequencer_Delay_Description, + [Description("Delay its turning.")] + ActionSequencer_Offset_Description, + [Description("Enought Level")] ActionConditionType_EnoughLevel, diff --git a/RotationSolver/UI/ConditionDrawer.cs b/RotationSolver/UI/ConditionDrawer.cs index 405268105..fdb6a1c60 100644 --- a/RotationSolver/UI/ConditionDrawer.cs +++ b/RotationSolver/UI/ConditionDrawer.cs @@ -250,7 +250,7 @@ public static void Draw(this ICondition condition, ICustomRotation rotation) private static void DrawDelay(this DelayCondition condition) { - const float MIN = 0, MAX = 60; + const float MIN = 0, MAX = 600; ImGui.SetNextItemWidth(80 * ImGuiHelpers.GlobalScale); if (ImGui.DragFloatRange2($"##Random Delay {condition.GetHashCode()}", ref condition.DelayMin, ref condition.DelayMax, 0.1f, MIN, MAX, @@ -261,6 +261,13 @@ private static void DrawDelay(this DelayCondition condition) } ImguiTooltips.HoveredTooltip(UiString.ActionSequencer_Delay_Description.Local() + "\n" + ConfigUnitType.Seconds.Local()); + + ImGui.SetNextItemWidth(40 * ImGuiHelpers.GlobalScale); + ImGui.DragFloat($"##Offset Delay {condition.GetHashCode()}", ref condition.DelayOffset, 0.1f, MIN, MAX, + $"{condition.DelayOffset:F1}{ConfigUnitType.Seconds.ToSymbol()}"); + + ImguiTooltips.HoveredTooltip(UiString.ActionSequencer_Offset_Description.Local() + + "\n" + ConfigUnitType.Seconds.Local()); } private static void DrawBefore(this ICondition condition) From 0afa0ec26839f453790f5982b25dc1304b0e0934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:13:34 +0800 Subject: [PATCH 23/48] New translations localization.json (Spanish) --- RotationSolver/Localization/es.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/es.json b/RotationSolver/Localization/es.json index 6c3d53656..3ca7a3b84 100644 --- a/RotationSolver/Localization/es.json +++ b/RotationSolver/Localization/es.json @@ -255,5 +255,14 @@ "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", - "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", + "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "DrawingHeightName": "The height of the drawing things.", + "SampleLengthName": "Drawing smoothness.", + "BeneficialPositionColorName": "The color of beneficial AoE positions", + "HoveredBeneficialPositionColorName": "The color of the hovered beneficial position", + "StateIconHeightName": "State icon height", + "StateIconSizeName": "State icon size", + "TargetIconSizeName": "The size of the next ability that will be used icon.", + "ShowTargetTimeToKillName": "Show the target's time to kill." } \ No newline at end of file From 84bca6f371b0f1dc31e8c7fbffcd631b4cf66e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:13:35 +0800 Subject: [PATCH 24/48] New translations localization.json (German) --- RotationSolver/Localization/de.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/de.json b/RotationSolver/Localization/de.json index 6c3d53656..3ca7a3b84 100644 --- a/RotationSolver/Localization/de.json +++ b/RotationSolver/Localization/de.json @@ -255,5 +255,14 @@ "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", - "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", + "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "DrawingHeightName": "The height of the drawing things.", + "SampleLengthName": "Drawing smoothness.", + "BeneficialPositionColorName": "The color of beneficial AoE positions", + "HoveredBeneficialPositionColorName": "The color of the hovered beneficial position", + "StateIconHeightName": "State icon height", + "StateIconSizeName": "State icon size", + "TargetIconSizeName": "The size of the next ability that will be used icon.", + "ShowTargetTimeToKillName": "Show the target's time to kill." } \ No newline at end of file From 8a641421bde045f3f41b4fea361b587ebd6568cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:13:37 +0800 Subject: [PATCH 25/48] New translations localization.json (Japanese) --- RotationSolver/Localization/ja.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/ja.json b/RotationSolver/Localization/ja.json index 6c3d53656..3ca7a3b84 100644 --- a/RotationSolver/Localization/ja.json +++ b/RotationSolver/Localization/ja.json @@ -255,5 +255,14 @@ "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", - "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", + "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "DrawingHeightName": "The height of the drawing things.", + "SampleLengthName": "Drawing smoothness.", + "BeneficialPositionColorName": "The color of beneficial AoE positions", + "HoveredBeneficialPositionColorName": "The color of the hovered beneficial position", + "StateIconHeightName": "State icon height", + "StateIconSizeName": "State icon size", + "TargetIconSizeName": "The size of the next ability that will be used icon.", + "ShowTargetTimeToKillName": "Show the target's time to kill." } \ No newline at end of file From d14c60c5dbcfd3228382b991a070fafa8d812c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:13:38 +0800 Subject: [PATCH 26/48] New translations localization.json (Chinese Simplified) --- RotationSolver/Localization/zh.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/zh.json b/RotationSolver/Localization/zh.json index f9dbaf1e0..762cb555f 100644 --- a/RotationSolver/Localization/zh.json +++ b/RotationSolver/Localization/zh.json @@ -255,5 +255,14 @@ "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", - "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", + "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "DrawingHeightName": "The height of the drawing things.", + "SampleLengthName": "Drawing smoothness.", + "BeneficialPositionColorName": "The color of beneficial AoE positions", + "HoveredBeneficialPositionColorName": "The color of the hovered beneficial position", + "StateIconHeightName": "State icon height", + "StateIconSizeName": "State icon size", + "TargetIconSizeName": "The size of the next ability that will be used icon.", + "ShowTargetTimeToKillName": "Show the target's time to kill." } \ No newline at end of file From 9274ace158eccda0686c7d279adf21d5a0168310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:13:39 +0800 Subject: [PATCH 27/48] New translations localization.json (French) --- RotationSolver/Localization/fr.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RotationSolver/Localization/fr.json b/RotationSolver/Localization/fr.json index 6c3d53656..3ca7a3b84 100644 --- a/RotationSolver/Localization/fr.json +++ b/RotationSolver/Localization/fr.json @@ -255,5 +255,14 @@ "DownloadRotationsName": "Auto Download Rotations", "AutoLoadCustomRotationsName": "Auto load rotations", "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", - "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break" + "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", + "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "DrawingHeightName": "The height of the drawing things.", + "SampleLengthName": "Drawing smoothness.", + "BeneficialPositionColorName": "The color of beneficial AoE positions", + "HoveredBeneficialPositionColorName": "The color of the hovered beneficial position", + "StateIconHeightName": "State icon height", + "StateIconSizeName": "State icon size", + "TargetIconSizeName": "The size of the next ability that will be used icon.", + "ShowTargetTimeToKillName": "Show the target's time to kill." } \ No newline at end of file From ad5c20973436e5dfff8b446778a5dd3b97a25eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:32:35 +0800 Subject: [PATCH 28/48] fix: make drawing faster. --- RotationSolver.Basic/Configuration/Configs.cs | 5 +- RotationSolver/Localization/Localization.json | 47 ++++++++++++++++++- .../Localization/LocalizationManager.cs | 4 +- RotationSolver/UI/ConditionDrawer.cs | 2 + RotationSolver/UI/PainterManager.cs | 3 +- XIVPainter | 2 +- 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index c9c153793..5880fe9e0 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -424,7 +424,10 @@ public const string [UI("Drawing smoothness.", Parent = nameof(UseOverlayWindow))] [Range(0.005f, 0.05f, ConfigUnitType.Yalms, 0.001f)] - public float SampleLength { get; set; } = 0.2f; + public float SampleLength { get; set; } = 1; + + [ConditionBool, UI("Use tasks for making the overlay window faster.", Parent = nameof(UseOverlayWindow))] + private static readonly bool _useTasksForOverlay = false; [UI("The angle of your vision cone", Parent = nameof(OnlyAttackInVisionCone))] [Range(0, 90, ConfigUnitType.Degree, 0.02f)] diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 3ca7a3b84..68b284d99 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -102,7 +102,6 @@ "RotationSolver.Basic.Configuration.Conditions.ActionCondition": "Action Condition", "RotationSolver.Basic.Configuration.Conditions.TraitCondition": "Trait Condition", "RotationSolver.Basic.Configuration.Conditions.TargetCondition": "Target Condition", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "RotationSolver.Basic.Configuration.Conditions.RotationCondition", "RotationSolver.Basic.Configuration.Conditions.NamedCondition": "Named Condition", "RotationSolver.Basic.Configuration.Conditions.TerritoryCondition": "Territory Condition", "RotationSolver.Data.UiString.ActionSequencer_FromClipboard": "From Clipboard", @@ -264,5 +263,49 @@ "StateIconHeightName": "State icon height", "StateIconSizeName": "State icon size", "TargetIconSizeName": "The size of the next ability that will be used icon.", - "ShowTargetTimeToKillName": "Show the target's time to kill." + "ShowTargetTimeToKillName": "Show the target's time to kill.", + "UseTasksForOverlayName": "Use tasks for making the overlay window faster.", + "RotationSolver.Data.UiString.ActionSequencer_Offset_Description": "Delay its turning.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_Copy": "Copy to Clipboard", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Bool": "Boolean", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Integer": "Byte", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Float": "Float", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Last": "Last", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MemberName": "Member Name", + "RotationSolver.Basic.Attributes.ConfigUnitType.None": "None", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Single AddersgallTime": "Single AddersgallTime", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersMinHP": "Min HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersAverHP": "Average HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single AverageTimeToKill": "Average time to kill", + "RotationSolver.Basic.Rotations.CustomRotation.Single Ping": "Your ping", + "RotationSolver.Basic.Rotations.CustomRotation.Single NextAbilityToNextGCD": "Time from next ability to next GCD", + "RotationSolver.Basic.Rotations.CustomRotation.Single CombatTime": "Combat time", + "RotationSolver.Basic.Rotations.CustomRotation.Single StopMovingTime": "Stop moving time", + "RotationSolver.Basic.Rotations.CustomRotation.Single CountDownAhead": "Count Down ahead", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbility": "Health of Area Ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpell": "Health of Area spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbilityHot": "Health of Area Ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpellHot": "Health of Area spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbility": "Health of single ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpell": "Health of single spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbilityHot": "Health of single ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpellHot": "Health of single spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthForDyingTanks": "Health of dying tank", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Boolean HasEukrasia": "Boolean HasEukrasia", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasSwift": "Has Swift", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasTankStance": "Has tank stance", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsMoving": "Is Moving or Jumping", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean InCombat": "In Combat", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean NotInCombatDelay": "Not In Combat Delay", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasCompanion": "Has companion", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsFullParty": "Is Full Party", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInRange": "Has hostiles in Range", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInMaxRange": "Has hostiles in 25 yalms", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean MobsTime": "Mobs Time", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean AutoState": "The state of auto. True for on.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsManual": "The state of manual. True for manual.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" } \ No newline at end of file diff --git a/RotationSolver/Localization/LocalizationManager.cs b/RotationSolver/Localization/LocalizationManager.cs index 0dc984a03..44bf34890 100644 --- a/RotationSolver/Localization/LocalizationManager.cs +++ b/RotationSolver/Localization/LocalizationManager.cs @@ -28,10 +28,10 @@ public static string Local(this Type type) internal static string Local(this string key, string @default) { - if (_rightLang.TryGetValue(key, out var value)) return value; - #if DEBUG _rightLang[key] = @default; +#else + if (_rightLang.TryGetValue(key, out var value)) return value; #endif return @default; } diff --git a/RotationSolver/UI/ConditionDrawer.cs b/RotationSolver/UI/ConditionDrawer.cs index fdb6a1c60..120a02789 100644 --- a/RotationSolver/UI/ConditionDrawer.cs +++ b/RotationSolver/UI/ConditionDrawer.cs @@ -262,6 +262,8 @@ private static void DrawDelay(this DelayCondition condition) ImguiTooltips.HoveredTooltip(UiString.ActionSequencer_Delay_Description.Local() + "\n" + ConfigUnitType.Seconds.Local()); + ImGui.SameLine(); + ImGui.SetNextItemWidth(40 * ImGuiHelpers.GlobalScale); ImGui.DragFloat($"##Offset Delay {condition.GetHashCode()}", ref condition.DelayOffset, 0.1f, MIN, MAX, $"{condition.DelayOffset:F1}{ConfigUnitType.Seconds.ToSymbol()}"); diff --git a/RotationSolver/UI/PainterManager.cs b/RotationSolver/UI/PainterManager.cs index 09b89c8bf..f2f5768bf 100644 --- a/RotationSolver/UI/PainterManager.cs +++ b/RotationSolver/UI/PainterManager.cs @@ -300,7 +300,8 @@ public static void UpdateSettings() if (_painter == null) return; _painter.DrawingHeight = Service.Config.DrawingHeight; _painter.SampleLength = Service.Config.SampleLength; - _painter.Enable = !Svc.Condition[Dalamud.Game.ClientState.Conditions.ConditionFlag.OccupiedInCutSceneEvent] && Service.Config.UseOverlayWindow; + _painter.UseTaskToAccelerate = Service.Config.UseTasksForOverlay; + _painter.Enable = !Svc.Condition[ConditionFlag.OccupiedInCutSceneEvent] && Service.Config.UseOverlayWindow; } public static void Dispose() diff --git a/XIVPainter b/XIVPainter index 03263805d..e0fb5bbc6 160000 --- a/XIVPainter +++ b/XIVPainter @@ -1 +1 @@ -Subproject commit 03263805d94b4764ca0efc86613485c263f1715b +Subproject commit e0fb5bbc686e807e6cd34ba6eb5b3cc96aac7640 From 9c060b1d76e1e4712a2fb294e515c1566c3ec3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 00:07:09 +0800 Subject: [PATCH 29/48] fix: longer delay for social. --- Resources/RotationSolverRecord.json | 4 ++-- RotationSolver/Updaters/SocialUpdater.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 7a517daa9..948a38e9c 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 84394, - "SayingHelloCount": 73, + "ClickingCount": 84660, + "SayingHelloCount": 74, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs index 2c7b150a5..06c4c5300 100644 --- a/RotationSolver/Updaters/SocialUpdater.cs +++ b/RotationSolver/Updaters/SocialUpdater.cs @@ -134,7 +134,7 @@ internal static void Disable() Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; } - static RandomDelay socialDelay = new(() => (3, 5)); + static RandomDelay socialDelay = new(() => (5, 8)); internal static async void UpdateSocial() { if (DataCenter.InCombat) return; From 01fbd836c7fe75bc47a37f07e31760e7f7bfe5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 00:23:15 +0800 Subject: [PATCH 30/48] fix: time fixed. --- Resources/RotationSolverRecord.json | 4 ++-- RotationSolver.Basic/Actions/ActionCooldownInfo.cs | 2 +- RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs | 4 ++-- RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs | 2 +- RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs | 2 +- RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs | 2 +- RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 948a38e9c..24b2ffcde 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 84660, - "SayingHelloCount": 74, + "ClickingCount": 86231, + "SayingHelloCount": 75, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs index 4a5440853..201169214 100644 --- a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs +++ b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs @@ -50,7 +50,7 @@ namespace RotationSolver.Basic.Actions; /// /// /// - public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw - DataCenter.WeaponRemain; + public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw + DataCenter.WeaponRemain; float RecastTimeRemainOneChargeRaw => RecastTimeRemain % RecastTimeOneChargeRaw; diff --git a/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs b/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs index c4548f4c5..c974535ec 100644 --- a/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs @@ -56,7 +56,7 @@ partial class BlackMageRotation /// /// /// - public static float EnochianTime => EnochianTimeRaw - DataCenter.WeaponRemain; + public static float EnochianTime => EnochianTimeRaw + DataCenter.WeaponRemain; /// /// @@ -79,7 +79,7 @@ protected static bool EnchinaEndAfterGCD(uint gcdCount = 0, float offset = 0) /// /// /// - protected static float ElementTime => ElementTimeRaw - DataCenter.WeaponRemain; + protected static float ElementTime => ElementTimeRaw + DataCenter.WeaponRemain; /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs b/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs index 294e90ab6..64a4ab032 100644 --- a/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs @@ -23,7 +23,7 @@ partial class DragoonRotation /// /// /// - public static float LOTDTime => LOTDTimeRaw - DataCenter.WeaponRemain; + public static float LOTDTime => LOTDTimeRaw + DataCenter.WeaponRemain; /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs b/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs index 7a22740be..9014e2156 100644 --- a/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs @@ -26,7 +26,7 @@ partial class MachinistRotation /// /// /// - public static float OverheatTime => OverheatTimeRemainingRaw - DataCenter.WeaponRemain; + public static float OverheatTime => OverheatTimeRemainingRaw + DataCenter.WeaponRemain; /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs index b0203b1a1..5b3c9aa3a 100644 --- a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs @@ -21,7 +21,7 @@ partial class ScholarRotation /// /// /// - public static float SeraphTime => SeraphTimeRaw - DataCenter.WeaponRemain; + public static float SeraphTime => SeraphTimeRaw + DataCenter.WeaponRemain; #endregion private sealed protected override IBaseAction Raise => ResurrectionPvE; diff --git a/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs b/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs index 5ac33553c..290f46082 100644 --- a/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/WhiteMageRotation.cs @@ -21,7 +21,7 @@ partial class WhiteMageRotation /// /// /// - public static float LilyTime => LilyTimeRaw - DataCenter.WeaponRemain; + public static float LilyTime => LilyTimeRaw + DataCenter.WeaponRemain; /// /// From 7924c557b65c725ccaaae2129cd047d30141097c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Tue, 27 Feb 2024 09:33:30 +0800 Subject: [PATCH 31/48] fix: action condition can use fix. --- Directory.Build.props | 2 +- Resources/RotationSolverRecord.json | 2 +- RotationSolver.Basic/Actions/BaseAction.cs | 60 ++++++++++++-- RotationSolver.Basic/Actions/IBaseAction.cs | 6 +- .../Conditions/ActionCondition.cs | 2 +- RotationSolver.Basic/Data/ActionOption.cs | 83 ------------------- RotationSolver.Basic/Data/CanUseOption.cs | 61 ++++++++++++++ .../Rotations/Basic/DancerRotation.cs | 8 +- .../Rotations/Basic/DragoonRotation.cs | 2 +- .../Rotations/Basic/MonkRotation.cs | 2 +- .../Rotations/Basic/ScholarRotation.cs | 2 +- .../Rotations/CustomRotation_Ability.cs | 6 +- .../Rotations/CustomRotation_GCD.cs | 4 +- RotationSolver/UI/ConditionDrawer.cs | 57 ++++++------- RotationSolver/UI/ControlWindow.cs | 2 +- RotationSolver/UI/RotationConfigWindow.cs | 4 +- RotationSolver/Updaters/ActionUpdater.cs | 2 +- 17 files changed, 161 insertions(+), 144 deletions(-) delete mode 100644 RotationSolver.Basic/Data/ActionOption.cs create mode 100644 RotationSolver.Basic/Data/CanUseOption.cs diff --git a/Directory.Build.props b/Directory.Build.props index 2f09d09a7..e1e36a74f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net7.0-windows enable ArchiTed - 4.0.3.3 + 4.0.3.4 x64 AnyCPU latest diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 24b2ffcde..002bca4f1 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 86231, + "ClickingCount": 86236, "SayingHelloCount": 75, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index 884bed93b..94010c013 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -6,51 +6,72 @@ namespace RotationSolver.Basic.Actions; public class BaseAction : IBaseAction { + /// public TargetResult? Target { get; set; } = null; + + /// public TargetResult? PreviewTarget { get; private set; } = null; + /// public Action Action { get; } + /// public ActionTargetInfo TargetInfo { get; } + /// public ActionBasicInfo Info { get; } + /// public ActionCooldownInfo Cooldown { get; } ICooldown IAction.Cooldown => Cooldown; + /// public uint ID => Info.ID; + /// public uint AdjustedID => Info.AdjustedID; + /// public float AnimationLockTime => Info.AnimationLockTime; + /// public uint SortKey => Cooldown.CoolDownGroup; - public bool IsCoolingDown => Cooldown.IsCoolingDown; - + /// public uint IconID => ID == 3 ? 104 : Info.IconID; + /// public string Name => Info.Name; + + /// public string Description => string.Empty; + /// public byte Level => Info.Level; + + /// public bool IsEnabled { get => Config.IsEnabled; set => Config.IsEnabled = value; } + + /// public bool IsInCooldown { get => Config.IsInCooldown; set => Config.IsInCooldown = value; } + /// public bool EnoughLevel => Info.EnoughLevel; + /// public ActionSetting Setting { get; set; } + /// public ActionConfig Config { get @@ -68,6 +89,11 @@ public ActionConfig Config } } + /// + /// The default constructor + /// + /// action id + /// is this action a duty action public BaseAction(ActionID actionID, bool isDutyAction = false) { Action = Service.GetSheet().GetRow((uint)actionID)!; @@ -78,8 +104,9 @@ public BaseAction(ActionID actionID, bool isDutyAction = false) Setting = new(); } - public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipCombo = false, bool ignoreCastingCheck = false, - bool isEmpty = false, bool onLastAbility = false, bool ignoreClippingCheck = false, bool skipAoeCheck = false, byte gcdCountForAbility = 0) + /// + public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipCombo = false, bool skipCastingCheck = false, + bool usedUp = false, bool onLastAbility = false, bool skipClippingCheck = false, bool skipAoeCheck = false, byte gcdCountForAbility = 0) { act = this!; @@ -87,20 +114,20 @@ public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool sk if (IBaseAction.ActionPreview) { - ignoreCastingCheck = ignoreClippingCheck = true; + skipCastingCheck = skipClippingCheck = true; } if (IBaseAction.AllEmpty) { - isEmpty = true; + usedUp = true; } if (IBaseAction.IgnoreClipping) { - ignoreClippingCheck = true; + skipClippingCheck = true; } - if (!Info.BasicCheck(skipStatusProvideCheck, skipCombo, ignoreCastingCheck)) return false; + if (!Info.BasicCheck(skipStatusProvideCheck, skipCombo, skipCastingCheck)) return false; - if (!Cooldown.CooldownCheck(isEmpty, onLastAbility, ignoreClippingCheck, gcdCountForAbility)) return false; + if (!Cooldown.CooldownCheck(usedUp, onLastAbility, skipClippingCheck, gcdCountForAbility)) return false; if (Setting.IsMeleeRange && IActionHelper.IsLastAction(IActionHelper.MovingActions)) return false; //No range actions after moving. @@ -116,6 +143,21 @@ public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool sk return true; } + /// + public bool CanUse(out IAction act, CanUseOption option, byte gcdCountForAbility = 0) + { + return CanUse(out act, + option.HasFlag(CanUseOption.SkipStatusProvideCheck), + option.HasFlag(CanUseOption.SkipCombo), + option.HasFlag(CanUseOption.SkipCastingCheck), + option.HasFlag(CanUseOption.UsedUp), + option.HasFlag(CanUseOption.OnLastAbility), + option.HasFlag(CanUseOption.SkipClippingCheck), + option.HasFlag(CanUseOption.SkipAoeCheck), + gcdCountForAbility); + } + + /// public unsafe bool Use() { if (!Target.HasValue) return false; diff --git a/RotationSolver.Basic/Actions/IBaseAction.cs b/RotationSolver.Basic/Actions/IBaseAction.cs index 7e3a45e84..e9e73ad1c 100644 --- a/RotationSolver.Basic/Actions/IBaseAction.cs +++ b/RotationSolver.Basic/Actions/IBaseAction.cs @@ -21,6 +21,8 @@ public interface IBaseAction : IAction ActionSetting Setting { get; set; } internal ActionConfig Config { get; } - bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipCombo = false, bool ignoreCastingCheck = false, - bool isEmpty = false, bool onLastAbility = false, bool ignoreClippingCheck = false, bool skipAoeCheck = false, byte gcdCountForAbility = 0); + bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipCombo = false, bool skipCastingCheck = false, + bool usedUp = false, bool onLastAbility = false, bool skipClippingCheck = false, bool skipAoeCheck = false, byte gcdCountForAbility = 0); + + bool CanUse(out IAction act, CanUseOption option, byte gcdCountForAbility = 0); } diff --git a/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs b/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs index 2e82cf63b..7f1c9ef6e 100644 --- a/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs +++ b/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs @@ -37,7 +37,7 @@ protected override bool IsTrueInside(ICustomRotation rotation) return !_action.Cooldown.WillHaveOneChargeGCD((uint)Param1, Param2); // Smaller case ActionConditionType.CanUse: - return _action.CanUse(out _); + return _action.CanUse(out _, (CanUseOption)Param1); case ActionConditionType.EnoughLevel: return _action.EnoughLevel; diff --git a/RotationSolver.Basic/Data/ActionOption.cs b/RotationSolver.Basic/Data/ActionOption.cs deleted file mode 100644 index 56ec31966..000000000 --- a/RotationSolver.Basic/Data/ActionOption.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace RotationSolver.Basic.Data; - -/// -/// The type of action. -/// -[Flags] -public enum ActionOption : byte -{ - /// - /// The normal one. - /// - None = 0, - - /// - /// is a friendly or supporting action - /// - Friendly = 1 << 0, - - /// - /// is hot or dot action - /// - Eot = 1 << 1, - - /// - /// end special after using it - /// - EndSpecial = 1 << 2, - - /// - /// Is a GCD action. - /// - GeneralGCD = 1 << 3, - - /// - /// Is a simple gcd action, without other cooldown. - /// - RealGCD = 1 << 4, - - /// - /// The duty action - /// - DutyAction = 1 << 5, - - /// - /// flag to check this action is heal. - /// - HealFlag = 1 << 6, - - /// - /// Is the action a resource taken action. - /// - UseResources = 1 << 7, - - /// - /// Dot action - /// - Dot = Eot, - - /// - /// Attack action - /// - Attack = None, - - /// - /// Heal action - /// - Heal = Friendly | HealFlag, - - /// - /// Defense action (you need to change the targeting strategy.) - /// - Defense = Friendly, - - /// - /// Hot action - /// - Hot = Heal | Eot, - - /// - /// Some buff - /// - Buff = Friendly, -} diff --git a/RotationSolver.Basic/Data/CanUseOption.cs b/RotationSolver.Basic/Data/CanUseOption.cs new file mode 100644 index 000000000..d26a085b7 --- /dev/null +++ b/RotationSolver.Basic/Data/CanUseOption.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RotationSolver.Basic.Data; + +/// +/// the option for the +/// +[Flags] +public enum CanUseOption : byte +{ + /// + /// None. + /// + None, + + /// + /// Skip Status Provide Check + /// + [Description("Skip Status Provide Check")] + SkipStatusProvideCheck = 1 << 0, + + /// + /// Skip Combo Check + /// + [Description("Skip Combo Check")] + SkipCombo = 1 << 1, + + /// + /// Skip Casting and Moving Check + /// + [Description("Skip Casting and Moving Check")] + SkipCastingCheck = 1 << 2, + + /// + /// Is it used up all stacks + /// + [Description("Is it used up all stacks")] + UsedUp = 1 << 3, + + /// + /// Is it on the last ability + /// + [Description("Is it on the last ability")] + OnLastAbility = 1 << 4, + + /// + /// Skip clipping Check + /// + [Description("Skip clipping Check")] + SkipClippingCheck = 1 << 5, + + /// + /// Skip aoe Check + /// + [Description("Skip aoe Check")] + SkipAoeCheck = 1 << 6, +} diff --git a/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs b/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs index b75604136..36dae8fdd 100644 --- a/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DancerRotation.cs @@ -265,7 +265,7 @@ protected bool ExecuteStepGCD(out IAction? act) [RotationDesc(ActionID.EnAvantPvE)] protected sealed override bool MoveForwardAbility(out IAction act) { - if (EnAvantPvE.CanUse(out act, isEmpty:true)) return true; + if (EnAvantPvE.CanUse(out act, usedUp:true)) return true; return false; } @@ -277,8 +277,8 @@ protected sealed override bool MoveForwardAbility(out IAction act) [RotationDesc(ActionID.CuringWaltzPvE, ActionID.ImprovisationPvE)] protected sealed override bool HealAreaAbility(out IAction act) { - if (CuringWaltzPvE.CanUse(out act, isEmpty: true)) return true; - if (ImprovisationPvE.CanUse(out act, isEmpty: true)) return true; + if (CuringWaltzPvE.CanUse(out act, usedUp: true)) return true; + if (ImprovisationPvE.CanUse(out act, usedUp: true)) return true; return false; } @@ -290,7 +290,7 @@ protected sealed override bool HealAreaAbility(out IAction act) [RotationDesc(ActionID.ShieldSambaPvE)] protected sealed override bool DefenseAreaAbility(out IAction act) { - if (ShieldSambaPvE.CanUse(out act, isEmpty: true)) return true; + if (ShieldSambaPvE.CanUse(out act, usedUp: true)) return true; return false; } } diff --git a/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs b/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs index 64a4ab032..889025079 100644 --- a/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs @@ -145,7 +145,7 @@ protected sealed override bool DefenseAreaAbility(out IAction? act) [RotationDesc(ActionID.ElusiveJumpPvE)] protected override bool MoveBackAbility(out IAction? act) { - if (ElusiveJumpPvE.CanUse(out act, ignoreClippingCheck: true)) return true; + if (ElusiveJumpPvE.CanUse(out act, skipClippingCheck: true)) return true; return base.MoveBackAbility(out act); } } \ No newline at end of file diff --git a/RotationSolver.Basic/Rotations/Basic/MonkRotation.cs b/RotationSolver.Basic/Rotations/Basic/MonkRotation.cs index 47cc59d41..1bf588125 100644 --- a/RotationSolver.Basic/Rotations/Basic/MonkRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/MonkRotation.cs @@ -131,7 +131,7 @@ protected sealed override bool HealAreaAbility(out IAction? act) [RotationDesc(ActionID.RiddleOfEarthPvE)] protected sealed override bool DefenseSingleAbility(out IAction? act) { - if (RiddleOfEarthPvE.CanUse(out act, isEmpty: true)) return true; + if (RiddleOfEarthPvE.CanUse(out act, usedUp: true)) return true; return base.DefenseSingleAbility(out act); } } diff --git a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs index 5b3c9aa3a..b4f739574 100644 --- a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs @@ -145,7 +145,7 @@ static partial void ModifyDeploymentTacticsPvE(ref ActionSetting setting) [RotationDesc(ActionID.ExpedientPvE)] protected override bool SpeedAbility(out IAction? act) { - if (InCombat && ExpedientPvE.CanUse(out act, isEmpty: true)) return true; + if (InCombat && ExpedientPvE.CanUse(out act, usedUp: true)) return true; return base.SpeedAbility(out act); } } \ No newline at end of file diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs index 60b0866d9..809f2e68b 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs @@ -8,7 +8,7 @@ private bool Ability(IAction nextGCD, out IAction? act) IBaseAction.ForceEnable = true; if (act is IBaseAction a && a != null && !a.Info.IsRealGCD && a.CanUse(out _, - isEmpty: true, skipAoeCheck: true)) return true; + usedUp: true, skipAoeCheck: true)) return true; IBaseAction.ForceEnable = false; if (act is IBaseItem i && i.CanUse(out _, true)) return true; @@ -45,7 +45,7 @@ private bool Ability(IAction nextGCD, out IAction? act) if (DataCenter.MergedStatus.HasFlag(AutoStatus.Positional) && role == JobRole.Melee && !(Player?.HasStatus(false, StatusHelper.NoPositionalStatus) ?? true)) { - if (TrueNorthPvE.CanUse(out act, isEmpty: true, onLastAbility: true)) return true; + if (TrueNorthPvE.CanUse(out act, usedUp: true, onLastAbility: true)) return true; } IBaseAction.TargetOverride = TargetType.Heal; @@ -266,7 +266,7 @@ protected virtual bool EmergencyAbility(IAction nextGCD, out IAction? act) { if (action.Setting.EnemyPositional != action.Target?.Target?.FindEnemyPositional() && (action.Target?.Target?.HasPositional() ?? false)) { - if (TrueNorthPvE.CanUse(out act, isEmpty: true, onLastAbility: true)) return true; + if (TrueNorthPvE.CanUse(out act, usedUp: true, onLastAbility: true)) return true; } } } diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index b6257690d..f362116c0 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -11,7 +11,7 @@ partial class CustomRotation IBaseAction.ForceEnable = true; if (act is IBaseAction a && a != null && a.Info.IsRealGCD - && a.CanUse(out _, isEmpty: true, skipAoeCheck: true)) return act; + && a.CanUse(out _, usedUp: true, skipAoeCheck: true)) return act; IBaseAction.ForceEnable = false; IBaseAction.ShouldEndSpecial = true; @@ -174,7 +174,7 @@ private bool RaiseSpell(out IAction? act, bool mustUse) bool RaiseAction(out IAction act, bool ignoreCastingCheck) { - if (Player.CurrentMp > Service.Config.LessMPNoRaise && (Raise?.CanUse(out act, ignoreCastingCheck: ignoreCastingCheck) ?? false)) return true; + if (Player.CurrentMp > Service.Config.LessMPNoRaise && (Raise?.CanUse(out act, skipCastingCheck: ignoreCastingCheck) ?? false)) return true; act = null!; return false; diff --git a/RotationSolver/UI/ConditionDrawer.cs b/RotationSolver/UI/ConditionDrawer.cs index 120a02789..cfbfee8c7 100644 --- a/RotationSolver/UI/ConditionDrawer.cs +++ b/RotationSolver/UI/ConditionDrawer.cs @@ -435,37 +435,32 @@ private static void DrawAfter(this ActionCondition actionCondition, ICustomRotat } break; - case ActionConditionType.CanUse: //TODO: canuse - //var popUpId = "Can Use Id" + actionCondition.GetHashCode().ToString(); - //var option = (CanUseOption)actionCondition.Param1; - - //if (ImGui.Selectable($"{option}##CanUse{actionCondition.GetHashCode()}")) - //{ - // if (!ImGui.IsPopupOpen(popUpId)) ImGui.OpenPopup(popUpId); - //} - - //using (var popUp = ImRaii.Popup(popUpId)) - //{ - // if (popUp.Success) - // { - // var showedValues = Enum.GetValues().Where(i => i.GetAttribute() == null); - - // foreach (var value in showedValues) - // { - // var b = option.HasFlag(value); - // if (ImGui.Checkbox(value.ToString(), ref b)) - // { - // option ^= value; - // actionCondition.Param1 = (int)option; - // } - // } - // } - //} - - //if (DrawDragInt($"{LocalizationManager._rightLang.ActionSequencer_AOECount}##AOECount{actionCondition.GetHashCode()}", ref actionCondition.Param2)) - //{ - // actionCondition.Param2 = Math.Max(0, actionCondition.Param2); - //} + case ActionConditionType.CanUse: + var popUpId = "Can Use Id" + actionCondition.GetHashCode().ToString(); + var option = (CanUseOption)actionCondition.Param1; + + if (ImGui.Selectable($"{option}##CanUse{actionCondition.GetHashCode()}")) + { + if (!ImGui.IsPopupOpen(popUpId)) ImGui.OpenPopup(popUpId); + } + + using (var popUp = ImRaii.Popup(popUpId)) + { + if (popUp.Success) + { + var showedValues = Enum.GetValues().Where(i => i.GetAttribute() == null); + + foreach (var value in showedValues) + { + var b = option.HasFlag(value); + if (ImGui.Checkbox(value.Local(), ref b)) + { + option ^= value; + actionCondition.Param1 = (int)option; + } + } + } + } break; case ActionConditionType.CurrentCharges: diff --git a/RotationSolver/UI/ControlWindow.cs b/RotationSolver/UI/ControlWindow.cs index b9736ded4..20a5d22e1 100644 --- a/RotationSolver/UI/ControlWindow.cs +++ b/RotationSolver/UI/ControlWindow.cs @@ -406,7 +406,7 @@ internal static (Vector2, Vector2) DrawIAction(IAction? action, float width, flo if (action is IBaseAction act) { IBaseAction.ForceEnable = true; - canDoIt = act.CanUse(out _, isEmpty: true, ignoreClippingCheck: true, skipAoeCheck: true); + canDoIt = act.CanUse(out _, usedUp: true, skipClippingCheck: true, skipAoeCheck: true); IBaseAction.ForceEnable = false; } else if (action is IBaseItem item) diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index e3c46a23f..fdaeb9949 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -1424,8 +1424,8 @@ private static unsafe void DrawActions() ImGui.Text("Recast One: " + action.Cooldown.RecastTimeOneChargeRaw.ToString()); ImGui.Text("Recast Elapsed: " + action.Cooldown.RecastTimeElapsedRaw.ToString()); - ImGui.Text($"Can Use: {action.CanUse(out _, ignoreClippingCheck: true)} "); - ImGui.Text("IgnoreCastCheck:" + action.CanUse(out _, ignoreClippingCheck: true, ignoreCastingCheck : true).ToString()); + ImGui.Text($"Can Use: {action.CanUse(out _, skipClippingCheck: true)} "); + ImGui.Text("IgnoreCastCheck:" + action.CanUse(out _, skipClippingCheck: true, skipCastingCheck : true).ToString()); if (action.Target != null) { ImGui.Text("Target Name: " + action.Target.Value.Target?.Name ?? string.Empty); diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs index d54490d75..c70a51979 100644 --- a/RotationSolver/Updaters/ActionUpdater.cs +++ b/RotationSolver/Updaters/ActionUpdater.cs @@ -44,7 +44,7 @@ internal static void UpdateNextAction() { return !action.Setting.IsFriendly && action.Config.IsInMistake && action.Setting.TargetType != TargetType.Move - && action.CanUse(out _, isEmpty: true, skipStatusProvideCheck: true, ignoreClippingCheck: true, skipAoeCheck: true); + && action.CanUse(out _, usedUp: true, skipStatusProvideCheck: true, skipClippingCheck: true, skipAoeCheck: true); } return false; }); From 86264c019a0a72876698948629aad0943f3df465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Tue, 27 Feb 2024 11:03:46 +0800 Subject: [PATCH 32/48] docs: add comments. --- .../Actions/ActionBasicInfo.cs | 76 +++++- RotationSolver.Basic/Actions/ActionConfig.cs | 31 ++- .../Actions/ActionCooldownInfo.cs | 12 + RotationSolver.Basic/Actions/ActionSetting.cs | 52 +++++ .../Actions/ActionTargetInfo.cs | 135 +++++------ RotationSolver.Basic/Actions/BaseAction.cs | 14 +- RotationSolver.Basic/Actions/BaseItem.cs | 7 +- RotationSolver.Basic/Actions/IBaseAction.cs | 54 ++++- RotationSolver.Basic/Actions/ICooldown.cs | 16 ++ RotationSolver.Basic/Actions/ItemConfig.cs | 11 + .../Attributes/DutyTerritoryAttribute.cs | 7 + .../Attributes/IDAttribute.cs | 2 +- .../Attributes/RangeAttribute.cs | 43 ++++ .../Attributes/RotationAttribute.cs | 18 ++ .../Attributes/UIAttributes.cs | 39 ++++ .../Conditions/IConditionConverter.cs | 15 +- .../Data/BeneficialAreaStrategy.cs | 16 ++ RotationSolver.Basic/Data/CanUseOption.cs | 2 +- RotationSolver.Basic/Data/ObjectListDelay.cs | 6 +- RotationSolver.Basic/Data/RandomDelay.cs | 10 + .../Helpers/ActionIdHelper.cs | 26 ++- RotationSolver.Basic/Helpers/ObjectHelper.cs | 129 ++--------- RotationSolver.Basic/Helpers/TargetFilter.cs | 9 +- .../Rotations/Basic/BlackMageRotation.cs | 4 +- .../Rotations/Basic/DragoonRotation.cs | 2 +- .../Rotations/Basic/MachinistRotation.cs | 2 +- .../Rotations/Basic/NinjaRotation.cs | 3 + .../Rotations/Basic/ScholarRotation.cs | 2 +- .../Rotations/CustomRotation_Actions.cs | 18 ++ .../Rotations/CustomRotation_BasicInfo.cs | 44 +++- .../Rotations/CustomRotation_GCD.cs | 10 + .../Rotations/CustomRotation_OtherInfo.cs | 2 +- .../Rotations/Duties/BozjaRotation.cs | 3 + .../Rotations/Duties/DutyRotation.cs | 2 + RotationSolver/Data/UiString.cs | 3 + RotationSolver/UI/ConditionDrawer.cs | 2 +- RotationSolver/UI/RotationConfigWindow.cs | 219 ++++++++++-------- RotationSolver/Updaters/SocialUpdater.cs | 10 +- 38 files changed, 745 insertions(+), 311 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionBasicInfo.cs b/RotationSolver.Basic/Actions/ActionBasicInfo.cs index 4ef37d51b..578bd4c42 100644 --- a/RotationSolver.Basic/Actions/ActionBasicInfo.cs +++ b/RotationSolver.Basic/Actions/ActionBasicInfo.cs @@ -5,7 +5,10 @@ namespace RotationSolver.Basic.Actions; -public struct ActionBasicInfo +/// +/// The action info for the . +/// +public readonly struct ActionBasicInfo { internal static readonly uint[] ActionsNoNeedCasting = [ @@ -15,25 +18,65 @@ public struct ActionBasicInfo ]; private readonly IBaseAction _action; + + /// + /// The name of the action. + /// public readonly string Name => _action.Action.Name; + + /// + /// The ID of the action. + /// public readonly uint ID => _action.Action.RowId; + + /// + /// The icon of the action. + /// public readonly uint IconID => ID == (uint)ActionID.SprintPvE ? 104u : _action.Action.Icon; + /// + /// The adjust id of this action. + /// public readonly uint AdjustedID => (uint)Service.GetAdjustedActionId((ActionID)ID); + /// + /// The attack type of this action. + /// public readonly AttackType AttackType => (AttackType)(_action.Action.AttackType.Value?.RowId ?? byte.MaxValue); + /// + /// The aspect of this action. + /// + public Aspect Aspect { get; } + + /// + /// The animation lock time of this action. + /// public readonly float AnimationLockTime => OtherConfiguration.AnimationLockTime?.TryGetValue(AdjustedID, out var time) ?? false ? time : 0.6f; + /// + /// The level of this action. + /// public readonly byte Level => _action.Action.ClassJobLevel; + + /// + /// If this action is enough level to use. + /// public readonly bool EnoughLevel => Player.Level >= Level; + /// + /// If this action a pvp action. + /// public readonly bool IsPvP => _action.Action.IsPvP; + /// /// Casting time. /// public readonly unsafe float CastTime => ((ActionID)AdjustedID).GetCastTime(); + /// + /// How many mp does this action needs. + /// public readonly unsafe uint MPNeed { get @@ -47,6 +90,9 @@ public readonly unsafe uint MPNeed } } + /// + /// Is thia action on the slot. + /// public readonly bool IsOnSlot { get @@ -64,12 +110,32 @@ public readonly bool IsOnSlot return IsPvP == DataCenter.Territory?.IsPvpZone; } } + + /// + /// Is this action is a lb action. + /// public bool IsLimitBreak { get; } + + /// + /// Is this action a general gcd. + /// public bool IsGeneralGCD { get; } + + /// + /// Is this action a real gcd. + /// public bool IsRealGCD { get; } + + /// + /// Is this action a duty action. + /// public bool IsDutyAction { get; } - public Aspect Aspect { get; } + /// + /// The basic way to create a basic info + /// + /// the action + /// if it is a duty action. public ActionBasicInfo(IBaseAction action, bool isDutyAction) { _action = action; @@ -81,7 +147,7 @@ public ActionBasicInfo(IBaseAction action, bool isDutyAction) Aspect = (Aspect)_action.Action.Aspect; } - internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipCombo, bool ignoreCastingCheck) + internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipComboCheck, bool skipCastingCheck) { if (!_action.Config.IsEnabled || !IsOnSlot) return false; @@ -112,7 +178,7 @@ internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipCombo, b if (CustomRotation.LimitBreakLevel <= 1) return false; } - if (!skipCombo && IsGeneralGCD) + if (!skipComboCheck && IsGeneralGCD) { if (!CheckForCombo()) return false; } @@ -134,7 +200,7 @@ internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipCombo, b //Is knocking back. if (DateTime.Now > DataCenter.KnockbackStart && DateTime.Now < DataCenter.KnockbackFinished) return false; - if (DataCenter.NoPoslock && DataCenter.IsMoving && !ignoreCastingCheck) return false; + if (DataCenter.NoPoslock && DataCenter.IsMoving && !skipCastingCheck) return false; } if (IsGeneralGCD && _action.Setting.StatusProvide?.Length > 0 && _action.Setting.IsFriendly diff --git a/RotationSolver.Basic/Actions/ActionConfig.cs b/RotationSolver.Basic/Actions/ActionConfig.cs index 695214103..4ea840efc 100644 --- a/RotationSolver.Basic/Actions/ActionConfig.cs +++ b/RotationSolver.Basic/Actions/ActionConfig.cs @@ -6,19 +6,48 @@ public class ActionConfig() { private bool _isEnable = true; + + /// + /// If this action is enabled. + /// public bool IsEnabled { get => IBaseAction.ForceEnable || _isEnable; set => _isEnable = value; } + /// + /// Should check the status for this action. + /// + public bool ShouldCheckStatus { get; set; } = true; + + /// + /// The status count in gcd for adding the status. + /// public byte StatusGcdCount { get; set; } = 2; + + /// + /// The aoe count of this action. + /// public byte AoeCount { get; set; } = 3; + + /// + /// How many ttk should this action use. + /// public float TimeToKill { get; set; } = 0; - public bool ShouldCheckStatus { get; set; } = true; + /// + /// The heal ratio for the auto heal. + /// public float AutoHealRatio { get; set; } = 0.8f; + /// + /// Is this action in the cd window. + /// public bool IsInCooldown { get; set; } = true; + + /// + /// Is this action should be a mistake action. + /// public bool IsInMistake { get; set; } } diff --git a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs index 201169214..ddcdb4a0a 100644 --- a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs +++ b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs @@ -2,9 +2,17 @@ using FFXIVClientStructs.FFXIV.Client.Game; namespace RotationSolver.Basic.Actions; + +/// +/// The action cooldown information. +/// public readonly struct ActionCooldownInfo : ICooldown { private readonly IBaseAction _action; + + /// + /// The cd group. + /// public byte CoolDownGroup { get; } unsafe RecastDetail* CoolDownDetail => ActionIdHelper.GetCoolDownDetail(CoolDownGroup); @@ -61,6 +69,10 @@ namespace RotationSolver.Basic.Actions; float RecastTimeElapsedOneChargeRaw => RecastTimeElapsedRaw % RecastTimeOneChargeRaw; + /// + /// The default constructor. + /// + /// the action. public ActionCooldownInfo(IBaseAction action) { _action = action; diff --git a/RotationSolver.Basic/Actions/ActionSetting.cs b/RotationSolver.Basic/Actions/ActionSetting.cs index 3739bacb0..1c80ef255 100644 --- a/RotationSolver.Basic/Actions/ActionSetting.cs +++ b/RotationSolver.Basic/Actions/ActionSetting.cs @@ -5,16 +5,51 @@ /// public class ActionSetting() { + /// + /// The Ninjutsu action of this action. + /// public IBaseAction[]? Ninjutsu { get; set; } = null; + + /// + /// The override of the . + /// public Func? MPOverride { get; set; } = null; + + /// + /// Is this action in the melee range. + /// public bool IsMeleeRange { get; set; } = false; + + /// + /// Is this status is added by the plyer. + /// public bool StatusFromSelf { get; set; } = true; + + /// + /// The status that it provides to the target. + /// public StatusID[]? TargetStatusProvide { get; set; } = null; + + /// + /// The status that it needs on the target. + /// public StatusID[]? TargetStatusNeed { get; set; } = null; + + /// + /// Can the target be targeted. + /// public Func CanTarget { get; set; } = t => true; + + /// + /// The additional not combo ids. + /// public ActionID[]? ComboIdsNot { get; set; } + /// + /// The additional combo ids. + /// public ActionID[]? ComboIds { get; set; } + /// /// Status that this action provides. /// @@ -25,14 +60,25 @@ public class ActionSetting() /// public StatusID[]? StatusNeed { get; set; } = null; + /// + /// Your custom rotation check for your rotation. + /// public Func? RotationCheck { get; set; } = null; + internal Func? ActionCheck { get; set; } = null; internal Func? CreateConfig { get; set; } = null; + /// + /// Is this action friendly. + /// public bool IsFriendly { get; set; } private TargetType _type = TargetType.Big; + + /// + /// The strategy to target the target. + /// public TargetType TargetType { get @@ -56,8 +102,14 @@ public TargetType TargetType set => _type = value; } + /// + /// The enemy positional for this action. + /// public EnemyPositional EnemyPositional { get; set; } = EnemyPositional.None; + /// + /// Should end the special. + /// public bool EndSpecial { get; set; } } diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index ccbbc0d45..322def838 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -9,17 +9,31 @@ namespace RotationSolver.Basic.Actions; -public struct ActionTargetInfo(IBaseAction _action) +/// +/// The target info +/// +/// the input action. +public struct ActionTargetInfo(IBaseAction action) { - public readonly bool TargetArea => _action.Action.TargetArea; - - public readonly float Range => ActionManager.GetActionRange(_action.Info.ID); + /// + /// The range of this action. + /// + public readonly float Range => ActionManager.GetActionRange(action.Info.ID); - public readonly float EffectRange => (ActionID)_action.Info.ID == ActionID.LiturgyOfTheBellPvE ? 20 : _action.Action.EffectRange; + /// + /// The effect range of this action. + /// + public readonly float EffectRange => (ActionID)action.Info.ID == ActionID.LiturgyOfTheBellPvE ? 20 : action.Action.EffectRange; - public readonly bool IsSingleTarget => _action.Action.CastType == 1; + /// + /// Is this action single target. + /// + public readonly bool IsSingleTarget => action.Action.CastType == 1; + /// + /// Is this action target are. + /// - public readonly bool IsTargetArea => _action.Action.TargetArea; + public readonly bool IsTargetArea => action.Action.TargetArea; private static bool NoAOE { @@ -41,7 +55,7 @@ private static bool NoAOE #region Target Finder. //The delay of finding the targets. private readonly ObjectListDelay _canTargets = new (() => Service.Config.TargetDelay); - public readonly IEnumerable GetCanTargets(bool skipStatusProvideCheck, TargetType type) + private readonly IEnumerable GetCanTargets(bool skipStatusProvideCheck, TargetType type) { var items = TargetFilter.GetObjectInRadius(DataCenter.AllTargets, Range); var objs = new List(items.Count()); @@ -54,15 +68,15 @@ public readonly IEnumerable GetCanTargets(bool skipStatusProvideChe objs.Add(obj); } - _canTargets.Delay(objs.Where(CanUseTo).Where(InViewTarget).Where(_action.Setting.CanTarget)); + _canTargets.Delay(objs.Where(CanUseTo).Where(InViewTarget).Where(action.Setting.CanTarget)); return _canTargets; } - public readonly IEnumerable GetCanAffects(bool skipStatusProvideCheck, TargetType type) + private readonly IEnumerable GetCanAffects(bool skipStatusProvideCheck, TargetType type) { if (EffectRange == 0) return []; - var items = TargetFilter.GetObjectInRadius(_action.Setting.IsFriendly + var items = TargetFilter.GetObjectInRadius(action.Setting.IsFriendly ? DataCenter.PartyMembers : DataCenter.AllHostileTargets, Range + EffectRange); @@ -111,8 +125,8 @@ private readonly unsafe bool CanUseTo(GameObject tar) var tarAddress = tar.Struct(); if (tarAddress == null) return false; - if ((ActionID)_action.Info.ID != ActionID.AethericMimicryPvE - && !ActionManager.CanUseActionOnTarget(_action.Info.AdjustedID, tarAddress)) return false; + if ((ActionID)action.Info.ID != ActionID.AethericMimicryPvE + && !ActionManager.CanUseActionOnTarget(action.Info.AdjustedID, tarAddress)) return false; return tar.CanSee(); } @@ -140,18 +154,18 @@ private readonly bool GeneralCheck(BattleChara gameObject, bool skipStatusProvid private readonly bool CheckStatus(GameObject gameObject, bool skipStatusProvideCheck) { - if (!_action.Config.ShouldCheckStatus) return true; + if (!action.Config.ShouldCheckStatus) return true; - if (_action.Setting.TargetStatusNeed != null) + if (action.Setting.TargetStatusNeed != null) { if (gameObject.WillStatusEndGCD(0, 0, - _action.Setting.StatusFromSelf, _action.Setting.TargetStatusNeed)) return false; + action.Setting.StatusFromSelf, action.Setting.TargetStatusNeed)) return false; } - if (_action.Setting.TargetStatusProvide != null && !skipStatusProvideCheck) + if (action.Setting.TargetStatusProvide != null && !skipStatusProvideCheck) { - if (!gameObject.WillStatusEndGCD(_action.Config.StatusGcdCount, 0, - _action.Setting.StatusFromSelf, _action.Setting.TargetStatusProvide)) return false; + if (!gameObject.WillStatusEndGCD(action.Config.StatusGcdCount, 0, + action.Setting.StatusFromSelf, action.Setting.TargetStatusProvide)) return false; } return true; @@ -159,14 +173,14 @@ private readonly bool CheckStatus(GameObject gameObject, bool skipStatusProvideC private readonly bool CheckResistance(GameObject gameObject) { - if (_action.Info.AttackType == AttackType.Magic) + if (action.Info.AttackType == AttackType.Magic) { if (gameObject.HasStatus(false, StatusHelper.MagicResistance)) { return false; } } - else if(_action.Info.Aspect != Aspect.Piercing) // Physic + else if(action.Info.Aspect != Aspect.Piercing) // Physic { if (gameObject.HasStatus(false, StatusHelper.PhysicResistancec)) { @@ -188,7 +202,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { if (gameObject is not BattleChara b) return false; var time = b.GetTimeToKill(); - return float.IsNaN(time) || time >= _action.Config.TimeToKill; + return float.IsNaN(time) || time >= action.Config.TimeToKill; } #endregion @@ -206,7 +220,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { return new(player, [], player.Position); } - var type = _action.Setting.TargetType; + var type = action.Setting.TargetType; var canTargets = GetCanTargets(skipStatusProvideCheck, type); var canAffects = GetCanAffects(skipStatusProvideCheck, type); @@ -219,7 +233,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { var t = Svc.Targets.Target as BattleChara; - if (t == null || !_action.Setting.CanTarget(t)) return null; + if (t == null || !action.Setting.CanTarget(t)) return null; if (type == TargetType.Move) { @@ -235,7 +249,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) else { var effects = GetAffects(t, canAffects).ToArray(); - if (effects.Length >= _action.Config.AoeCount || skipAoeCheck) + if (effects.Length >= action.Config.AoeCount || skipAoeCheck) { return new(t, effects, t.Position); } @@ -244,12 +258,12 @@ private readonly bool CheckTimeToKill(GameObject gameObject) } var targets = GetMostCanTargetObjects(canTargets, canAffects, - skipAoeCheck ? 0 : _action.Config.AoeCount); - if (type == TargetType.BeAttacked && !_action.Setting.IsFriendly) + skipAoeCheck ? 0 : action.Config.AoeCount); + if (type == TargetType.BeAttacked && !action.Setting.IsFriendly) { type = TargetType.Big; } - var target = FindTargetByType(targets, type, _action.Config.AutoHealRatio, _action.Setting.IsMeleeRange); + var target = FindTargetByType(targets, type, action.Config.AutoHealRatio, action.Setting.IsMeleeRange); if (target == null) return null; return new(target, [.. GetAffects(target, canAffects)], target.Position); @@ -258,11 +272,11 @@ private readonly bool CheckTimeToKill(GameObject gameObject) private readonly TargetResult? FindTargetArea(IEnumerable canTargets, IEnumerable canAffects, float range, PlayerCharacter player) { - if (_action.Setting.TargetType is TargetType.Move) + if (action.Setting.TargetType is TargetType.Move) { return FindTargetAreaMove(range); } - else if (_action.Setting.IsFriendly) + else if (action.Setting.IsFriendly) { if (!Service.Config.UseGroundBeneficialAbility) return null; if (!Service.Config.UseGroundBeneficialAbilityWhenMoving && DataCenter.IsMoving) return null; @@ -271,7 +285,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) } else { - return FindTargetAreaHostile(canTargets, canAffects, _action.Config.AoeCount); + return FindTargetAreaHostile(canTargets, canAffects, action.Config.AoeCount); } } @@ -311,7 +325,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { var availableCharas = DataCenter.AllTargets.Where(b => b.ObjectId != Player.Object.ObjectId); var target = FindTargetByType(TargetFilter.GetObjectInRadius(availableCharas, range), - TargetType.Move, _action.Config.AutoHealRatio, _action.Setting.IsMeleeRange); + TargetType.Move, action.Config.AutoHealRatio, action.Setting.IsMeleeRange); if (target == null) return null; return new(target, [], target.Position); } @@ -335,7 +349,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) DataCenter.TerritoryContentType == TerritoryContentType.Raids && DataCenter.AllianceMembers.Count(p => p is PlayerCharacter) == 8) { - pts = pts.Union(new Vector3[] { Vector3.Zero, new(100, 0, 100) }).ToArray(); + pts = pts.Union([Vector3.Zero, new(100, 0, 100)]).ToArray(); } } @@ -373,7 +387,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) { var effectRange = EffectRange; var attackT = FindTargetByType(DataCenter.AllianceMembers.GetObjectInRadius(range + effectRange), - TargetType.BeAttacked, _action.Config.AutoHealRatio, _action.Setting.IsMeleeRange); + TargetType.BeAttacked, action.Config.AutoHealRatio, action.Setting.IsMeleeRange); if (attackT == null) { @@ -425,7 +439,7 @@ private readonly IEnumerable GetAffects(BattleChara tar, IEnumerabl private readonly IEnumerable GetMostCanTargetObjects(IEnumerable canTargets, IEnumerable canAffects, int aoeCount) { if (IsSingleTarget || EffectRange <= 0) return canTargets; - if (!_action.Setting.IsFriendly && NoAOE) return []; + if (!action.Setting.IsFriendly && NoAOE) return []; List objectMax = new(canTargets.Count()); @@ -474,7 +488,7 @@ private readonly bool CanGetTarget(GameObject target, GameObject subTarget) Vector3 dir = target.Position - pPos; Vector3 tdir = subTarget.Position - pPos; - switch (_action.Action.CastType) + switch (action.Action.CastType) { case 2: // Circle return Vector3.Distance(target.Position, subTarget.Position) - subTarget.HitboxRadius <= EffectRange; @@ -495,7 +509,7 @@ private readonly bool CanGetTarget(GameObject target, GameObject subTarget) return dis <= EffectRange && dis >= 8; } - Svc.Log.Debug(_action.Action.Name.RawString + "'s CastType is not valid! The value is " + _action.Action.CastType.ToString()); + Svc.Log.Debug(action.Action.Name.RawString + "'s CastType is not valid! The value is " + action.Action.CastType.ToString()); return false; } #endregion @@ -799,64 +813,35 @@ private readonly bool CanGetTarget(GameObject target, GameObject subTarget) #endregion } +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public enum TargetType : byte { - /// - /// Find the target whose hit box is biggest. - /// Big, - - /// - /// Find the target whose hit box is smallest. - /// Small, - - /// - /// Find the target whose hp is highest. - /// HighHP, - - /// - /// Find the target whose hp is lowest. - /// LowHP, - - /// - /// Find the target whose max hp is highest. - /// HighMaxHP, - - /// - /// Find the target whose max hp is lowest. - /// LowMaxHP, - - Interrupt, - Provoke, - Death, - Dispel, - Move, - BeAttacked, - Heal, - Tank, - Melee, - Range, - Physical, - Magical, - Self, } - +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + +/// +/// The target result +/// +/// the target. +/// the targets that be affected by this action. +/// the position to use this action. public readonly record struct TargetResult(BattleChara? Target, BattleChara[] AffectedTargets, Vector3? Position); \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index 94010c013..1c5b015d4 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -4,6 +4,10 @@ using Action = Lumina.Excel.GeneratedSheets.Action; namespace RotationSolver.Basic.Actions; + +/// +/// The base action for all actions. +/// public class BaseAction : IBaseAction { /// @@ -39,7 +43,7 @@ public class BaseAction : IBaseAction public uint SortKey => Cooldown.CoolDownGroup; /// - public uint IconID => ID == 3 ? 104 : Info.IconID; + public uint IconID => Info.IconID; /// public string Name => Info.Name; @@ -105,7 +109,7 @@ public BaseAction(ActionID actionID, bool isDutyAction = false) } /// - public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipCombo = false, bool skipCastingCheck = false, + public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipComboCheck = false, bool skipCastingCheck = false, bool usedUp = false, bool onLastAbility = false, bool skipClippingCheck = false, bool skipAoeCheck = false, byte gcdCountForAbility = 0) { act = this!; @@ -125,7 +129,7 @@ public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool sk skipClippingCheck = true; } - if (!Info.BasicCheck(skipStatusProvideCheck, skipCombo, skipCastingCheck)) return false; + if (!Info.BasicCheck(skipStatusProvideCheck, skipComboCheck, skipCastingCheck)) return false; if (!Cooldown.CooldownCheck(usedUp, onLastAbility, skipClippingCheck, gcdCountForAbility)) return false; @@ -148,7 +152,7 @@ public bool CanUse(out IAction act, CanUseOption option, byte gcdCountForAbility { return CanUse(out act, option.HasFlag(CanUseOption.SkipStatusProvideCheck), - option.HasFlag(CanUseOption.SkipCombo), + option.HasFlag(CanUseOption.SkipComboCheck), option.HasFlag(CanUseOption.SkipCastingCheck), option.HasFlag(CanUseOption.UsedUp), option.HasFlag(CanUseOption.OnLastAbility), @@ -165,7 +169,7 @@ public unsafe bool Use() var target = Target.Value; var adjustId = AdjustedID; - if (TargetInfo.TargetArea) + if (TargetInfo.IsTargetArea) { if (adjustId != ID) return false; if (!target.Position.HasValue) return false; diff --git a/RotationSolver.Basic/Actions/BaseItem.cs b/RotationSolver.Basic/Actions/BaseItem.cs index 5d1ae08e4..1238187d2 100644 --- a/RotationSolver.Basic/Actions/BaseItem.cs +++ b/RotationSolver.Basic/Actions/BaseItem.cs @@ -56,6 +56,9 @@ readonly struct ItemCooldown(uint id) : ICooldown /// public string Name => _item.Name; + /// + /// The item configs. + /// public ItemConfig Config { get @@ -124,7 +127,8 @@ public bool IsInCooldown /// protected virtual bool CanUseThis => true; - public ICooldown Cooldown => throw new NotImplementedException(); + /// + public ICooldown Cooldown { get; } /// /// Create by row. @@ -149,6 +153,7 @@ public unsafe BaseItem(Item item) _ => 65535, //TODO: better A4! }; SortKey = (uint)ActionManager.Instance()->GetRecastGroup((int)ActionType.Item, ID); + Cooldown = new ItemCooldown(ID); } /// diff --git a/RotationSolver.Basic/Actions/IBaseAction.cs b/RotationSolver.Basic/Actions/IBaseAction.cs index e9e73ad1c..4c45d771f 100644 --- a/RotationSolver.Basic/Actions/IBaseAction.cs +++ b/RotationSolver.Basic/Actions/IBaseAction.cs @@ -2,6 +2,9 @@ namespace RotationSolver.Basic.Actions; +/// +/// The interface of the base action. +/// public interface IBaseAction : IAction { internal static TargetType? TargetOverride { get; set; } = null; @@ -12,17 +15,64 @@ public interface IBaseAction : IAction internal static bool AllEmpty { get; set; } = false; internal static bool ShouldEndSpecial { get; set; } = false; + /// + /// The action itself. + /// Action Action { get; } + + /// + /// The target to use on. + /// TargetResult? Target { get; set; } + + /// + /// The target for preview. + /// TargetResult? PreviewTarget { get; } + + /// + /// The information about the target. + /// ActionTargetInfo TargetInfo { get; } + + /// + /// The basic information of this action. + /// ActionBasicInfo Info { get; } + + /// + /// The cd information. + /// new ActionCooldownInfo Cooldown { get; } + + /// + /// The setting to use this action. + /// ActionSetting Setting { get; set; } - internal ActionConfig Config { get; } + internal ActionConfig Config { get; } - bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipCombo = false, bool skipCastingCheck = false, + /// + /// Can I use this action. + /// + /// The return action + /// Skip Status Provide Check + /// Skip Combo Check + /// Skip Casting and Moving Check + /// Is it used up all stacks + /// Is it on the last ability + /// Skip clipping Check + /// Skip aoe Check + /// the gcd count for the ability. + /// can I use it + bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool skipComboCheck = false, bool skipCastingCheck = false, bool usedUp = false, bool onLastAbility = false, bool skipClippingCheck = false, bool skipAoeCheck = false, byte gcdCountForAbility = 0); + /// + /// Can I use this action. + /// + /// The return action + /// The options + /// the gcd count for the ability. + /// can I use it bool CanUse(out IAction act, CanUseOption option, byte gcdCountForAbility = 0); } diff --git a/RotationSolver.Basic/Actions/ICooldown.cs b/RotationSolver.Basic/Actions/ICooldown.cs index 69b3afd12..ae27861a0 100644 --- a/RotationSolver.Basic/Actions/ICooldown.cs +++ b/RotationSolver.Basic/Actions/ICooldown.cs @@ -1,9 +1,25 @@ namespace RotationSolver.Basic.Actions; + +/// +/// The cd information. +/// public interface ICooldown { internal float RecastTimeOneChargeRaw { get; } internal float RecastTimeElapsedRaw { get; } + + /// + /// Is still in cooling down. + /// bool IsCoolingDown { get; } + + /// + /// The mac charges. + /// ushort MaxCharges { get; } + + /// + /// The current charges. + /// ushort CurrentCharges { get; } } diff --git a/RotationSolver.Basic/Actions/ItemConfig.cs b/RotationSolver.Basic/Actions/ItemConfig.cs index 82f35e1aa..62ed40689 100644 --- a/RotationSolver.Basic/Actions/ItemConfig.cs +++ b/RotationSolver.Basic/Actions/ItemConfig.cs @@ -1,6 +1,17 @@ namespace RotationSolver.Basic.Actions; + +/// +/// The item config. +/// public class ItemConfig { + /// + /// Is in the cooldown window. + /// public bool IsInCooldown { get; set; } + + /// + /// Is this action enabled. + /// public bool IsEnabled { get; set; } } diff --git a/RotationSolver.Basic/Attributes/DutyTerritoryAttribute.cs b/RotationSolver.Basic/Attributes/DutyTerritoryAttribute.cs index 4b9b7bd79..0b524bc45 100644 --- a/RotationSolver.Basic/Attributes/DutyTerritoryAttribute.cs +++ b/RotationSolver.Basic/Attributes/DutyTerritoryAttribute.cs @@ -1,8 +1,15 @@ namespace RotationSolver.Basic.Attributes; +/// +/// The duty territory attribute. which contains the +/// +/// [AttributeUsage(AttributeTargets.Class)] public class DutyTerritoryAttribute(params uint[] territoryIds) : Attribute { + /// + /// The terriotry ids. + /// public uint[] TerritoryIds => territoryIds; } diff --git a/RotationSolver.Basic/Attributes/IDAttribute.cs b/RotationSolver.Basic/Attributes/IDAttribute.cs index 7bb902337..8f426a571 100644 --- a/RotationSolver.Basic/Attributes/IDAttribute.cs +++ b/RotationSolver.Basic/Attributes/IDAttribute.cs @@ -1,7 +1,7 @@ namespace RotationSolver.Basic.Attributes; [AttributeUsage(AttributeTargets.Property)] -public class IDAttribute(uint id) : Attribute +internal class IDAttribute(uint id) : Attribute { public uint ID => id; } diff --git a/RotationSolver.Basic/Attributes/RangeAttribute.cs b/RotationSolver.Basic/Attributes/RangeAttribute.cs index e5a9ed216..cd7de460f 100644 --- a/RotationSolver.Basic/Attributes/RangeAttribute.cs +++ b/RotationSolver.Basic/Attributes/RangeAttribute.cs @@ -1,30 +1,73 @@ namespace RotationSolver.Basic.Attributes; +/// +/// The range of the configs. +/// +/// min value +/// max value +/// the unit type +/// the speed. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class RangeAttribute(float minValue, float maxValue, ConfigUnitType unitType, float speed = 0.005f) : Attribute { + /// + /// Min Value + /// public float MinValue => minValue; + + /// + /// Max Value + /// public float MaxValue => maxValue; + + /// + /// Speed + /// public float Speed => speed; + + /// + /// The unit type. + /// public ConfigUnitType UnitType => unitType; } +/// +/// The config unit type. +/// public enum ConfigUnitType : byte { + /// + /// None unit type. + /// None, + /// + /// + /// [Description("Time Unit, in seconds.")] Seconds, + /// + /// + /// [Description("Angle Unit, in degrees.")] Degree, + /// + /// + /// [Description("Distance Unit, in yalms.")] Yalms, + /// + /// + /// [Description("Ratio Unit, as percentage.")] Percent, + /// + /// + /// [Description("Display Unit, in pixels.")] Pixels, } \ No newline at end of file diff --git a/RotationSolver.Basic/Attributes/RotationAttribute.cs b/RotationSolver.Basic/Attributes/RotationAttribute.cs index 242cf8407..38f843a92 100644 --- a/RotationSolver.Basic/Attributes/RotationAttribute.cs +++ b/RotationSolver.Basic/Attributes/RotationAttribute.cs @@ -1,12 +1,30 @@ namespace RotationSolver.Basic.Attributes; +/// +/// Your custom rotation attribute. +/// +/// the name of this rotation. +/// the type of this rotation. [AttributeUsage(AttributeTargets.Class)] public class RotationAttribute(string name, CombatType type) : Attribute { + /// + /// The name of this rotation. + /// public string Name => name; + + /// + /// The type of this rotation. + /// public CombatType Type => type; + /// + /// Your description about this rotation. + /// public string? Description { get; set; } + /// + /// The Game version of this rotation. + /// public string? GameVersion { get; set; } } diff --git a/RotationSolver.Basic/Attributes/UIAttributes.cs b/RotationSolver.Basic/Attributes/UIAttributes.cs index b8f033b3d..408fac99e 100644 --- a/RotationSolver.Basic/Attributes/UIAttributes.cs +++ b/RotationSolver.Basic/Attributes/UIAttributes.cs @@ -1,21 +1,59 @@ namespace RotationSolver.Basic.Attributes; +/// +/// The attribute for the ui configs. +/// +/// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class UIAttribute(string name) : Attribute { + /// + /// The name of this config. + /// public string Name => name; + + /// + /// The description about this ui item. + /// public string Description { get; set; } = ""; + + /// + /// The parent of this ui item. + /// public string Parent { get; set; } = ""; + + /// + /// The filter to get this ui item. + /// public string Filter { get; set; } = ""; + + /// + /// The order of this item. + /// public byte Order { get; set; } = 0; + + /// + /// The section of this item. + /// public byte Section { get; set; } = 0; + /// + /// The action id + /// public ActionID Action { get; set; } + /// + /// The filter for the pvp. + /// public JobFilterType PvPFilter { get; set; } + + /// + /// The filter for the pve. + /// public JobFilterType PvEFilter { get; set; } } +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public enum JobFilterType : byte { None, @@ -28,6 +66,7 @@ public enum JobFilterType : byte Tank, Melee, } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] internal class JobConfigAttribute : Attribute diff --git a/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs b/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs index 32dfb726e..8e7c31ef5 100644 --- a/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs +++ b/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs @@ -4,7 +4,7 @@ namespace RotationSolver.Basic.Configuration.Conditions; internal class IConditionConverter : JsonCreationConverter { - protected override ICondition Create(JObject jObject) + protected override ICondition? Create(JObject jObject) { if (FieldExists(nameof(ConditionSet.Conditions), jObject)) { @@ -48,7 +48,7 @@ private static bool FieldExists(string fieldName, JObject jObject) internal abstract class JsonCreationConverter : JsonConverter { - protected abstract T Create(JObject jObject); + protected abstract T? Create(JObject jObject); public override bool CanConvert(Type objectType) { @@ -57,20 +57,23 @@ public override bool CanConvert(Type objectType) public override bool CanWrite => false; - public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { } - public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { // Load JObject from stream JObject jObject = JObject.Load(reader); // Create target object based on JObject - T target = Create(jObject); + var target = Create(jObject); // Populate the object properties - serializer.Populate(jObject.CreateReader(), target); + if (target != null) + { + serializer.Populate(jObject.CreateReader(), target); + } return target; } diff --git a/RotationSolver.Basic/Data/BeneficialAreaStrategy.cs b/RotationSolver.Basic/Data/BeneficialAreaStrategy.cs index 04c1628e4..d5ae4b209 100644 --- a/RotationSolver.Basic/Data/BeneficialAreaStrategy.cs +++ b/RotationSolver.Basic/Data/BeneficialAreaStrategy.cs @@ -1,15 +1,31 @@ namespace RotationSolver.Basic.Data; + +/// +/// The way to place the beneficial area action. +/// public enum BeneficialAreaStrategy : byte { + /// + /// On predefined location + /// [Description("On predefined location")] OnLocations, + /// + /// Only on predefined location + /// [Description("Only on predefined location")] OnlyOnLocations, + /// + /// On target + /// [Description("On target")] OnTarget, + /// + /// On the calculated location + /// [Description("On the calculated location")] OnCalculated, } \ No newline at end of file diff --git a/RotationSolver.Basic/Data/CanUseOption.cs b/RotationSolver.Basic/Data/CanUseOption.cs index d26a085b7..3d11060a0 100644 --- a/RotationSolver.Basic/Data/CanUseOption.cs +++ b/RotationSolver.Basic/Data/CanUseOption.cs @@ -27,7 +27,7 @@ public enum CanUseOption : byte /// Skip Combo Check /// [Description("Skip Combo Check")] - SkipCombo = 1 << 1, + SkipComboCheck = 1 << 1, /// /// Skip Casting and Moving Check diff --git a/RotationSolver.Basic/Data/ObjectListDelay.cs b/RotationSolver.Basic/Data/ObjectListDelay.cs index e0d943088..24dacb538 100644 --- a/RotationSolver.Basic/Data/ObjectListDelay.cs +++ b/RotationSolver.Basic/Data/ObjectListDelay.cs @@ -14,11 +14,15 @@ namespace RotationSolver.Basic.Data; public class ObjectListDelay(Func<(float min, float max)> getRange) : IEnumerable where T : GameObject { - IEnumerable _list = Array.Empty(); + IEnumerable _list = []; readonly Func<(float min, float max)> _getRange = getRange; SortedList _revealTime = []; readonly Random _ran = new(DateTime.Now.Millisecond); + /// + /// The default creator from the config. + /// + /// the way to get the config. public ObjectListDelay(Func getRange) : this(() => { diff --git a/RotationSolver.Basic/Data/RandomDelay.cs b/RotationSolver.Basic/Data/RandomDelay.cs index e768add33..aea427f58 100644 --- a/RotationSolver.Basic/Data/RandomDelay.cs +++ b/RotationSolver.Basic/Data/RandomDelay.cs @@ -20,6 +20,10 @@ public struct RandomDelay(Func<(float min, float max)> getRange) /// public readonly Func<(float min, float max)> GetRange => getRange; + /// + /// The constructor for the config. + /// + /// The way to get the config. public RandomDelay(Func getRange) : this(() => { @@ -66,6 +70,12 @@ public bool Delay(bool originData) return false; } + /// + /// The delay to get the item. + /// + /// the type of anything. + /// original data. + /// the value. public T? Delay(T? originData) where T : class { var b = originData != null; diff --git a/RotationSolver.Basic/Helpers/ActionIdHelper.cs b/RotationSolver.Basic/Helpers/ActionIdHelper.cs index 571a01692..3e7b91829 100644 --- a/RotationSolver.Basic/Helpers/ActionIdHelper.cs +++ b/RotationSolver.Basic/Helpers/ActionIdHelper.cs @@ -3,19 +3,38 @@ using Action = Lumina.Excel.GeneratedSheets.Action; namespace RotationSolver.Basic.Helpers; -public static class ActionIdHelper + +/// +/// The helper for the action id. +/// +public static class ActionIdHelper { + /// + /// Is this action cooling down. + /// + /// the action id. + /// public unsafe static bool IsCoolingDown(this ActionID actionID) { return IsCoolingDown(actionID.GetAction().GetCoolDownGroup()); } + /// + /// Is this action cooling down. + /// + /// + /// public unsafe static bool IsCoolingDown(byte cdGroup) { var detail = GetCoolDownDetail(cdGroup); return detail != null && detail->IsActive != 0; } + /// + /// The cd details + /// + /// + /// public static unsafe RecastDetail* GetCoolDownDetail(byte cdGroup) => ActionManager.Instance()->GetRecastGroupDetail(cdGroup - 1); @@ -24,6 +43,11 @@ private static Action GetAction(this ActionID actionID) return Svc.Data.GetExcelSheet()!.GetRow((uint)actionID)!; } + /// + /// The cast time. + /// + /// + /// public unsafe static float GetCastTime(this ActionID actionID) { return ActionManager.GetAdjustedCastTime(ActionType.Action, (uint)actionID) / 1000f; ; diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index a4331dfc8..8e1f248c0 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -30,10 +30,10 @@ public static class ObjectHelper return Service.GetSheet().GetRow(obj.DataId); } - public static bool CanProvoke(this GameObject target) + internal static bool CanProvoke(this GameObject target) { //Removed the listed names. - IEnumerable names = Array.Empty(); + IEnumerable names = []; if (OtherConfiguration.NoProvokeNames.TryGetValue(Svc.ClientState.TerritoryType, out var ns1)) names = names.Union(ns1); @@ -54,23 +54,13 @@ public static bool CanProvoke(this GameObject target) return false; } - /// - /// Is the target have positional. - /// - /// - /// - public static bool HasPositional(this GameObject obj) + internal static bool HasPositional(this GameObject obj) { if (obj == null) return false; return !(obj.GetObjectNPC()?.Unknown10 ?? false); } - /// - /// Is this target belongs to other players. - /// - /// - /// - public static unsafe bool IsOthersPlayers(this GameObject obj) + internal static unsafe bool IsOthersPlayers(this GameObject obj) { //SpecialType but no NamePlateIcon if (_eventType.Contains(obj.GetEventType())) @@ -80,7 +70,7 @@ public static unsafe bool IsOthersPlayers(this GameObject obj) return false; } - public static bool IsAttackable(this BattleChara battleChara) + internal static bool IsAttackable(this BattleChara battleChara) { //Dead. if (battleChara.CurrentHp <= 1) return false; @@ -90,7 +80,7 @@ public static bool IsAttackable(this BattleChara battleChara) if (Svc.ClientState == null) return false; //In No Hostiles Names - IEnumerable names = Array.Empty(); + IEnumerable names = []; if (OtherConfiguration.NoHostileNames.TryGetValue(Svc.ClientState.TerritoryType, out var ns1)) names = names.Union(ns1); @@ -142,7 +132,7 @@ public static bool IsAttackable(this BattleChara battleChara) }; } - public static unsafe bool IsInEnemiesList(this BattleChara battleChara) + internal static unsafe bool IsInEnemiesList(this BattleChara battleChara) { var addons = Service.GetAddons(); @@ -161,26 +151,16 @@ public static unsafe bool IsInEnemiesList(this BattleChara battleChara) return false; } - /// - /// Is this target an enemy (can be attacked). - /// - /// - /// - public static unsafe bool IsEnemy(this GameObject obj) + internal static unsafe bool IsEnemy(this GameObject obj) => obj != null && ActionManager.CanUseActionOnTarget((uint)ActionID.BlizzardPvE, obj.Struct()); - /// - /// Is alliance (can be healed). - /// - /// - /// - public static unsafe bool IsAlliance(this GameObject obj) + internal static unsafe bool IsAlliance(this GameObject obj) => obj != null && obj.ObjectId is not 0 and not GameObject.InvalidGameObjectId && (!(DataCenter.Territory?.IsPvpZone ?? false) && obj is PlayerCharacter || ActionManager.CanUseActionOnTarget((uint)ActionID.CurePvE, obj.Struct())); - public static bool IsParty(this GameObject gameObject) + internal static bool IsParty(this GameObject gameObject) { if (gameObject.ObjectId == Player.Object.ObjectId) return true; if (Svc.Party.Any(p => p.GameObject?.ObjectId == gameObject.ObjectId)) return true; @@ -188,12 +168,12 @@ public static bool IsParty(this GameObject gameObject) return false; } - public static bool IsTargetOnSelf(this BattleChara battleChara) + internal static bool IsTargetOnSelf(this BattleChara battleChara) { return battleChara.TargetObject?.TargetObject == battleChara; } - public static bool IsDeathToRaise(this GameObject obj) + internal static bool IsDeathToRaise(this GameObject obj) { if (obj == null) return false; if (!obj.IsDead) return false; @@ -210,12 +190,10 @@ public static bool IsDeathToRaise(this GameObject obj) return true; } - public static bool IsAlive(this GameObject obj) + internal static bool IsAlive(this GameObject obj) { if (obj is BattleChara b && b.CurrentHp <= 1) return false; - if (!obj.IsTargetable) return false; - return true; } @@ -258,16 +236,11 @@ or 71344 //Major Quest internal static unsafe uint GetNamePlateIcon(this GameObject obj) => obj.Struct()->NamePlateIconId; internal static unsafe EventHandlerType GetEventType(this GameObject obj) => obj.Struct()->EventId.Type; - /// - /// The sub kind of the target. - /// - /// - /// - public static unsafe BattleNpcSubKind GetBattleNPCSubKind(this GameObject obj) => (BattleNpcSubKind)obj.Struct()->SubKind; + internal static unsafe BattleNpcSubKind GetBattleNPCSubKind(this GameObject obj) => (BattleNpcSubKind)obj.Struct()->SubKind; internal static unsafe uint FateId(this GameObject obj) => obj.Struct()->FateId; - static readonly Dictionary _effectRangeCheck = new(); + static readonly Dictionary _effectRangeCheck = []; internal static bool CanInterrupt(this GameObject o) { if (o is not BattleChara b) return false; @@ -287,19 +260,9 @@ internal static bool CanInterrupt(this GameObject o) return _effectRangeCheck[id] = true; } - /// - /// Is object a dummy. - /// - /// - /// - public static bool IsDummy(this BattleChara obj) => obj?.NameId == 541; + internal static bool IsDummy(this BattleChara obj) => obj?.NameId == 541; - /// - /// Is character a boss? Calculate from ttk. - /// - /// - /// - public static bool IsBossFromTTK(this BattleChara obj) + internal static bool IsBossFromTTK(this BattleChara obj) { if (obj == null) return false; @@ -311,12 +274,7 @@ public static bool IsBossFromTTK(this BattleChara obj) return false; } - /// - /// Is character a boss? Calculated from the icon. - /// - /// - /// - public static bool IsBossFromIcon(this BattleChara obj) + internal static bool IsBossFromIcon(this BattleChara obj) { if (obj == null) return false; @@ -328,37 +286,21 @@ public static bool IsBossFromIcon(this BattleChara obj) return false; } - /// - /// Is character a dying? Current HP is below a certain amount. It is for running out of resources. - /// - /// - /// - public static bool IsDying(this BattleChara b) + internal static bool IsDying(this BattleChara b) { if (b == null) return false; if (b.IsDummy() && !Service.Config.ShowTargetTimeToKill) return false; return b.GetTimeToKill() <= Service.Config.DyingTimeToKill || b.GetHealthRatio() < 0.02f; } - /// - /// Whether the character is in combat. - /// - /// - /// - public static unsafe bool InCombat(this BattleChara obj) + internal static unsafe bool InCombat(this BattleChara obj) { return obj.Struct()->Character.InCombat; } private static readonly TimeSpan CheckSpan = TimeSpan.FromSeconds(2.5); - /// - /// How many seconds will the target die. - /// - /// - /// whole time to die. - /// - public static float GetTimeToKill(this BattleChara b, bool wholeTime = false) + internal static float GetTimeToKill(this BattleChara b, bool wholeTime = false) { if (b == null) return float.NaN; if (b.IsDummy()) return 999.99f; @@ -388,12 +330,7 @@ public static float GetTimeToKill(this BattleChara b, bool wholeTime = false) return (float)timespan.TotalSeconds / ratioReduce * (wholeTime ? 1 : ratioNow); } - /// - /// Whether the target is attacked. - /// - /// - /// - public static bool IsAttacked(this BattleChara b) + internal static bool IsAttacked(this BattleChara b) { foreach (var (id, time) in DataCenter.AttackedTargets) { @@ -405,12 +342,7 @@ public static bool IsAttacked(this BattleChara b) return false; } - /// - /// Can the player see the object. - /// - /// - /// - public static unsafe bool CanSee(this GameObject b) + internal static unsafe bool CanSee(this GameObject b) { var point = Player.Object.Position + Vector3.UnitY * Player.GameObject->Height; var tarPt = b.Position + Vector3.UnitY * b.Struct()->Height; @@ -451,24 +383,13 @@ internal static EnemyPositional FindEnemyPositional(this GameObject enemy) return EnemyPositional.Flank; } - /// - /// Get the face vector - /// - /// - /// - public static Vector2 GetFaceVector(this GameObject obj) + internal static Vector2 GetFaceVector(this GameObject obj) { float rotation = obj.Rotation; return new((float)Math.Cos(rotation), (float)Math.Sin(rotation)); } - /// - /// Get two vector's angle - /// - /// - /// - /// - public static double AngleTo(this Vector2 vec1, Vector2 vec2) + internal static double AngleTo(this Vector2 vec1, Vector2 vec2) { return Math.Acos(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length()); } diff --git a/RotationSolver.Basic/Helpers/TargetFilter.cs b/RotationSolver.Basic/Helpers/TargetFilter.cs index 838a462d0..05b3f310e 100644 --- a/RotationSolver.Basic/Helpers/TargetFilter.cs +++ b/RotationSolver.Basic/Helpers/TargetFilter.cs @@ -60,11 +60,18 @@ public static bool IsJobCategory(this GameObject obj, JobRole role) return obj.IsJobs(validJobs); } + /// + /// Is the target in the jobs. + /// + /// + /// + /// public static bool IsJobs(this GameObject obj, params Job[] validJobs) { return obj.IsJobs(new SortedSet( validJobs.Select(j => (byte)(uint)j))); } - public static bool IsJobs(this GameObject obj, SortedSet validJobs) + + private static bool IsJobs(this GameObject obj, SortedSet validJobs) { if(obj is not BattleChara b) return false; return validJobs.Contains((byte?)b.ClassJob.GameData?.RowId ?? 0); diff --git a/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs b/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs index c974535ec..c4548f4c5 100644 --- a/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/BlackMageRotation.cs @@ -56,7 +56,7 @@ partial class BlackMageRotation /// /// /// - public static float EnochianTime => EnochianTimeRaw + DataCenter.WeaponRemain; + public static float EnochianTime => EnochianTimeRaw - DataCenter.WeaponRemain; /// /// @@ -79,7 +79,7 @@ protected static bool EnchinaEndAfterGCD(uint gcdCount = 0, float offset = 0) /// /// /// - protected static float ElementTime => ElementTimeRaw + DataCenter.WeaponRemain; + protected static float ElementTime => ElementTimeRaw - DataCenter.WeaponRemain; /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs b/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs index 889025079..6ce6c833f 100644 --- a/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/DragoonRotation.cs @@ -23,7 +23,7 @@ partial class DragoonRotation /// /// /// - public static float LOTDTime => LOTDTimeRaw + DataCenter.WeaponRemain; + public static float LOTDTime => LOTDTimeRaw - DataCenter.WeaponRemain; /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs b/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs index 9014e2156..7a22740be 100644 --- a/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/MachinistRotation.cs @@ -26,7 +26,7 @@ partial class MachinistRotation /// /// /// - public static float OverheatTime => OverheatTimeRemainingRaw + DataCenter.WeaponRemain; + public static float OverheatTime => OverheatTimeRemainingRaw - DataCenter.WeaponRemain; /// /// diff --git a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs index fe78e9253..b0695f51c 100644 --- a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs @@ -115,6 +115,9 @@ static partial void ModifyDotonPvE(ref ActionSetting setting) setting.StatusProvide = [StatusID.Doton]; } + /// + /// + /// public NinjaRotation() { FumaShurikenPvE.Setting.Ninjutsu = [TenPvE]; diff --git a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs index b4f739574..f1858785d 100644 --- a/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/ScholarRotation.cs @@ -21,7 +21,7 @@ partial class ScholarRotation /// /// /// - public static float SeraphTime => SeraphTimeRaw + DataCenter.WeaponRemain; + public static float SeraphTime => SeraphTimeRaw - DataCenter.WeaponRemain; #endregion private sealed protected override IBaseAction Raise => ResurrectionPvE; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs index ad7132cf6..146bb5357 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs @@ -132,6 +132,9 @@ static partial void ModifySprintPvP(ref ActionSetting setting) private protected virtual IBaseAction? LimitBreak3 => null; private protected virtual IBaseAction? LimitBreakPvP => null; + /// + /// All actions of this rotation. + /// public virtual IAction[] AllActions => [ .. AllBaseActions.Where(i => @@ -150,14 +153,29 @@ .. HpPotions.Where(i => i.HasIt), .. AllItems.Where(i => i.HasIt), ]; + /// + /// All traits of this action. + /// public virtual IBaseTrait[] AllTraits { get; } = []; PropertyInfo[]? _allBools; + + /// + /// All bools of this rotation. + /// public PropertyInfo[] AllBools => _allBools ??= GetType().GetStaticProperties(); PropertyInfo[]? _allBytes; + + /// + /// All bytes or integers of this rotation. + /// public PropertyInfo[] AllBytesOrInt => _allBytes ??= GetType().GetStaticProperties().Union(GetType().GetStaticProperties()).ToArray(); PropertyInfo[]? _allFloats; + + /// + /// All floats of this rotation. + /// public PropertyInfo[] AllFloats => _allFloats ??= GetType().GetStaticProperties(); } diff --git a/RotationSolver.Basic/Rotations/CustomRotation_BasicInfo.cs b/RotationSolver.Basic/Rotations/CustomRotation_BasicInfo.cs index 19fc7fd28..07d9690fd 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_BasicInfo.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_BasicInfo.cs @@ -4,15 +4,20 @@ namespace RotationSolver.Basic.Rotations; -[Jobs()] partial class CustomRotation : ICustomRotation { private Job? _job = null; + + /// public Job Job => _job ??= this.GetType().GetCustomAttribute()?.Jobs[0] ?? Job.ADV; private JobRole? _role = null; + + /// public JobRole Role => _role ??= Svc.Data.GetExcelSheet()!.GetRow((uint)Job)!.GetJobRole(); private string? _name = null; + + /// public string Name { get @@ -25,6 +30,7 @@ public string Name } } + /// public bool IsEnabled { get => !Service.Config.DisabledJobs.Contains(Job); @@ -41,52 +47,83 @@ public bool IsEnabled } } + /// public uint IconID { get; } + /// public IRotationConfigSet Configs { get; } + /// public static Vector3? MoveTarget { get; internal set; } + /// public string Description => this.GetType().GetCustomAttribute()?.Description ?? string.Empty; + /// public IAction? ActionHealAreaGCD { get; private set; } + /// public IAction? ActionHealAreaAbility { get; private set; } + /// public IAction? ActionHealSingleGCD { get; private set; } + /// public IAction? ActionHealSingleAbility { get; private set; } + /// public IAction? ActionDefenseAreaGCD { get; private set; } + /// public IAction? ActionDefenseAreaAbility { get; private set; } + /// public IAction? ActionDefenseSingleGCD { get; private set; } + /// public IAction? ActionDefenseSingleAbility { get; private set; } + /// public IAction? ActionMoveForwardGCD { get; private set; } + /// public IAction? ActionMoveForwardAbility { get; private set; } + /// public IAction? ActionMoveBackAbility { get; private set; } + /// public IAction? ActionSpeedAbility { get; private set; } + /// public IAction? ActionDispelStancePositionalGCD { get; private set; } + /// public IAction? ActionDispelStancePositionalAbility { get; private set; } + /// public IAction? ActionRaiseShirkGCD { get; private set; } + /// public IAction? ActionRaiseShirkAbility { get; private set; } + /// public IAction? ActionAntiKnockbackAbility { get; private set; } + /// + /// Is this action valid. + /// [Description("Is this rotation valid")] public bool IsValid { get; private set; } = true; + + /// + /// Why this action is not valid. + /// public string WhyNotValid { get; private set; } = string.Empty; + /// + /// Should show the status to the users. + /// [Description("Show the status")] public virtual bool ShowStatus => false; @@ -96,11 +133,16 @@ private protected CustomRotation() Configs = CreateConfiguration(); } + /// + /// The way to create the configurations. + /// + /// protected virtual IRotationConfigSet CreateConfiguration() { return new RotationConfigSet(); } + /// public override string ToString() => this.GetType().GetCustomAttribute()?.Name ?? this.GetType().Name; /// diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index f362116c0..6bd24dea7 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -181,12 +181,22 @@ bool RaiseAction(out IAction act, bool ignoreCastingCheck) } } + /// + /// The gcd for raising. + /// + /// the action. + /// protected virtual bool RaiseGCD(out IAction? act) { if (DataCenter.RightNowDutyRotation?.RaiseGCD(out act) ?? false) return true; act = null; return false; } + /// + /// The gcd for dispeling. + /// + /// the action. + /// protected virtual bool DispelGCD(out IAction? act) { if (EsunaPvE.CanUse(out act)) return true; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs b/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs index a3647b332..ea9f2ebb3 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs @@ -110,7 +110,7 @@ partial class CustomRotation /// /// The player's target, or null if no valid target. (null clears the target) /// - protected static BattleChara CurrentTarget => Svc.Targets.Target is BattleChara b ? b : null; + protected static BattleChara? CurrentTarget => Svc.Targets.Target is BattleChara b ? b : null; /// /// The last attacked hostile target. diff --git a/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs b/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs index 0b6952b58..43a9fe8d5 100644 --- a/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs @@ -1,5 +1,8 @@ namespace RotationSolver.Basic.Rotations.Duties; +/// +/// The bozja action. +/// [DutyTerritory] //TODO: the bozja territory ids! public abstract class BozjaRotation : DutyRotation { diff --git a/RotationSolver.Basic/Rotations/Duties/DutyRotation.cs b/RotationSolver.Basic/Rotations/Duties/DutyRotation.cs index 9a1e8aad1..84efc739b 100644 --- a/RotationSolver.Basic/Rotations/Duties/DutyRotation.cs +++ b/RotationSolver.Basic/Rotations/Duties/DutyRotation.cs @@ -1,4 +1,5 @@ namespace RotationSolver.Basic.Rotations.Duties; +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member partial class DutyRotation { @@ -128,3 +129,4 @@ internal IAction[] AllActions } } } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member diff --git a/RotationSolver/Data/UiString.cs b/RotationSolver/Data/UiString.cs index 56d31eb99..f09d16187 100644 --- a/RotationSolver/Data/UiString.cs +++ b/RotationSolver/Data/UiString.cs @@ -123,6 +123,9 @@ internal enum UiString [Description("How many targets are needed to use this action.")] ConfigWindow_Actions_AoeCount, + [Description("Should this action check the stauts.")] + ConfigWindow_Actions_CheckStatus, + [Description("How many gcds are needed to add the status.")] ConfigWindow_Actions_GcdCount, diff --git a/RotationSolver/UI/ConditionDrawer.cs b/RotationSolver/UI/ConditionDrawer.cs index cfbfee8c7..22463934f 100644 --- a/RotationSolver/UI/ConditionDrawer.cs +++ b/RotationSolver/UI/ConditionDrawer.cs @@ -82,7 +82,7 @@ internal static void DrawCondition(bool? tag) private static IEnumerable GetAllMethods(this Type? type, Func> getFunc) { - if (type == null || getFunc == null) return Array.Empty(); + if (type == null || getFunc == null) return []; var methods = getFunc(type); return methods.Union(type.BaseType.GetAllMethods(getFunc)); diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index fdaeb9949..8d9762701 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2,6 +2,7 @@ using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using Dalamud.Interface.Utility.Table; using Dalamud.Interface.Windowing; using Dalamud.Utility; using ECommons.DalamudServices; @@ -1405,105 +1406,76 @@ private static unsafe void DrawActions() ImGui.TableNextColumn(); - if (Service.Config.InDebug) - { - if (_activeAction is IBaseAction action) - { + DrawConfigsOfAction(); + DrawActionDebug(); - try - { -#if DEBUG - ImGui.Text("Is Real GCD: " + action.Info.IsRealGCD.ToString()); - ImGui.Text("Status: " + FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetActionStatus(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Action, action.AdjustedID).ToString()); - ImGui.Text("Cast Time: " + action.Info.CastTime.ToString()); - ImGui.Text("MP: " + action.Info.MPNeed.ToString()); -#endif - ImGui.Text("AttackType: " + action.Info.AttackType.ToString()); - ImGui.Text("Aspect: " + action.Info.Aspect.ToString()); - ImGui.Text("Has One:" + action.Cooldown.HasOneCharge.ToString()); - ImGui.Text("Recast One: " + action.Cooldown.RecastTimeOneChargeRaw.ToString()); - ImGui.Text("Recast Elapsed: " + action.Cooldown.RecastTimeElapsedRaw.ToString()); - - ImGui.Text($"Can Use: {action.CanUse(out _, skipClippingCheck: true)} "); - ImGui.Text("IgnoreCastCheck:" + action.CanUse(out _, skipClippingCheck: true, skipCastingCheck : true).ToString()); - if (action.Target != null) - { - ImGui.Text("Target Name: " + action.Target.Value.Target?.Name ?? string.Empty); - } - } - catch - { + ImGui.TextWrapped(UiString.ConfigWindow_Actions_ConditionDescription.Local()); + _sequencerList?.Draw(); + } - } - } - else if (_activeAction is IBaseItem item) - { - try - { - ImGui.Text("Status: " + FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetActionStatus(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID).ToString()); - ImGui.Text("Status HQ: " + FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetActionStatus(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID + 1000000).ToString()); - var remain = FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetRecastTime(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID) - FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetRecastTimeElapsed(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID); - ImGui.Text("remain: " + remain.ToString()); - ImGui.Text("CanUse: " + item.CanUse(out _, true).ToString()); + static void DrawConfigsOfAction() + { + if (_activeAction == null) return; - if (item is HpPotionItem healPotionItem) - { - ImGui.Text("MaxHP:" + healPotionItem.MaxHp.ToString()); - } - } - catch - { + var enable = _activeAction.IsEnabled; + if (ImGui.Checkbox($"{_activeAction.Name}##{_activeAction.Name} Enabled", ref enable)) + { + _activeAction.IsEnabled = enable; + } - } - } + const string key = "Action Enable Popup"; + var cmd = ToCommandStr(OtherCommandType.ToggleActions, _activeAction.ToString()!); + ImGuiHelper.DrawHotKeysPopup(key, cmd); + ImGuiHelper.ExecuteHotKeysPopup(key, cmd, string.Empty, false); + + enable = _activeAction.IsInCooldown; + if (ImGui.Checkbox($"{UiString.ConfigWindow_Actions_ShowOnCDWindow.Local()}##{_activeAction.Name}InCooldown", ref enable)) + { + _activeAction.IsInCooldown = enable; } - if (_activeAction != null) + if (_activeAction is IBaseAction a) { - var enable = _activeAction.IsEnabled; - if (ImGui.Checkbox($"{_activeAction.Name}##{_activeAction.Name} Enabled", ref enable)) + DrawConfigsOfBaseAction(a); + } + + ImGui.Separator(); + + static void DrawConfigsOfBaseAction(IBaseAction a) + { + var config = a.Config; + + if (Service.Config.MistakeRatio > 0 + && !a.Setting.IsFriendly + && a.Setting.TargetType != TargetType.Move) { - _activeAction.IsEnabled = enable; + var enable = config.IsInMistake; + if (ImGui.Checkbox($"{UiString.ConfigWindow_Actions_IsInMistake.Local()}##{a.Name}InMistake", ref enable)) + { + config.IsInMistake = enable; + } } - const string key = "Action Enable Popup"; - var cmd = ToCommandStr(OtherCommandType.ToggleActions, _activeAction.ToString()!); - ImGuiHelper.DrawHotKeysPopup(key, cmd); - ImGuiHelper.ExecuteHotKeysPopup(key, cmd, string.Empty, false); + ImGui.Separator(); - enable = _activeAction.IsInCooldown; - if (ImGui.Checkbox($"{UiString.ConfigWindow_Actions_ShowOnCDWindow.Local()}##{_activeAction.Name}InCooldown", ref enable)) + var ttk = config.TimeToKill; + ImGui.SetNextItemWidth(Scale * 150); + if (ImGui.DragFloat($"{UiString.ConfigWindow_Actions_TTK.Local()}##{a}", + ref ttk, 0.1f, 0, 120, $"{ttk:F2}{ConfigUnitType.Seconds.ToSymbol()}")) { - _activeAction.IsInCooldown = enable; + config.TimeToKill = ttk; } + ImguiTooltips.HoveredTooltip(ConfigUnitType.Seconds.Local()); - if (_activeAction is IBaseAction a) + if (a.Setting.StatusProvide != null || a.Setting.TargetStatusProvide != null) { - var config = a.Config; - - if (Service.Config.MistakeRatio > 0 - && !a.Setting.IsFriendly - && a.Setting.TargetType != TargetType.Move) - { - enable = config.IsInMistake; - if (ImGui.Checkbox($"{UiString.ConfigWindow_Actions_IsInMistake.Local()}##{a.Name}InMistake", ref enable)) - { - config.IsInMistake = enable; - } - } - - ImGui.Separator(); - - var ttk = config.TimeToKill; - ImGui.SetNextItemWidth(Scale * 150); - if (ImGui.DragFloat($"{UiString.ConfigWindow_Actions_TTK.Local()}##{a}", - ref ttk, 0.1f, 0, 120, $"{ttk:F2}{ConfigUnitType.Seconds.ToSymbol()}")) + var shouldStatus = config.ShouldCheckStatus; + if (ImGui.Checkbox($"{UiString.ConfigWindow_Actions_CheckStatus.Local()}##{a}", ref shouldStatus)) { - config.TimeToKill = ttk; + config.ShouldCheckStatus = shouldStatus; } - ImguiTooltips.HoveredTooltip(ConfigUnitType.Seconds.Local()); - if (a.Setting.StatusProvide != null || a.Setting.TargetStatusProvide != null) + if (shouldStatus) { var statusGcdCount = (int)config.StatusGcdCount; ImGui.SetNextItemWidth(Scale * 150); @@ -1513,33 +1485,84 @@ private static unsafe void DrawActions() config.StatusGcdCount = (byte)statusGcdCount; } } + } - if (!a.TargetInfo.IsSingleTarget) + if (!a.TargetInfo.IsSingleTarget) + { + var aoeCount = (int)config.AoeCount; + ImGui.SetNextItemWidth(Scale * 150); + if (ImGui.DragInt($"{UiString.ConfigWindow_Actions_AoeCount.Local()}##{a}", + ref aoeCount, 0.05f, 1, 10)) { - var aoeCount = (int)config.AoeCount; - ImGui.SetNextItemWidth(Scale * 150); - if (ImGui.DragInt($"{UiString.ConfigWindow_Actions_AoeCount.Local()}##{a}", - ref aoeCount, 0.05f, 1, 10)) - { - config.AoeCount = (byte)aoeCount; - } + config.AoeCount = (byte)aoeCount; } + } - var ratio = config.AutoHealRatio; - ImGui.SetNextItemWidth(Scale * 150); - if (ImGui.DragFloat($"{UiString.ConfigWindow_Actions_HealRatio.Local()}##{a}", - ref ratio, 0.002f, 0, 1, $"{ratio * 100:F1}{ConfigUnitType.Percent.ToSymbol()}")) + var ratio = config.AutoHealRatio; + ImGui.SetNextItemWidth(Scale * 150); + if (ImGui.DragFloat($"{UiString.ConfigWindow_Actions_HealRatio.Local()}##{a}", + ref ratio, 0.002f, 0, 1, $"{ratio * 100:F1}{ConfigUnitType.Percent.ToSymbol()}")) + { + config.AutoHealRatio = ratio; + } + ImguiTooltips.HoveredTooltip(ConfigUnitType.Percent.Local()); + + } + } + + static void DrawActionDebug() + { + if (!Service.Config.InDebug) return; + + if (_activeAction is IBaseAction action) + { + + try + { +#if DEBUG + ImGui.Text("Is Real GCD: " + action.Info.IsRealGCD.ToString()); + ImGui.Text("Status: " + FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetActionStatus(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Action, action.AdjustedID).ToString()); + ImGui.Text("Cast Time: " + action.Info.CastTime.ToString()); + ImGui.Text("MP: " + action.Info.MPNeed.ToString()); +#endif + ImGui.Text("AttackType: " + action.Info.AttackType.ToString()); + ImGui.Text("Aspect: " + action.Info.Aspect.ToString()); + ImGui.Text("Has One:" + action.Cooldown.HasOneCharge.ToString()); + ImGui.Text("Recast One: " + action.Cooldown.RecastTimeOneChargeRaw.ToString()); + ImGui.Text("Recast Elapsed: " + action.Cooldown.RecastTimeElapsedRaw.ToString()); + + ImGui.Text($"Can Use: {action.CanUse(out _, skipClippingCheck: true)} "); + ImGui.Text("IgnoreCastCheck:" + action.CanUse(out _, skipClippingCheck: true, skipCastingCheck: true).ToString()); + if (action.Target != null) { - config.AutoHealRatio = ratio; + ImGui.Text("Target Name: " + action.Target.Value.Target?.Name ?? string.Empty); } - ImguiTooltips.HoveredTooltip(ConfigUnitType.Percent.Local()); } + catch + { - ImGui.Separator(); + } } + else if (_activeAction is IBaseItem item) + { + try + { + ImGui.Text("Status: " + FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetActionStatus(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID).ToString()); + ImGui.Text("Status HQ: " + FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetActionStatus(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID + 1000000).ToString()); + var remain = FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetRecastTime(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID) - FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance()->GetRecastTimeElapsed(FFXIVClientStructs.FFXIV.Client.Game.ActionType.Item, item.ID); + ImGui.Text("remain: " + remain.ToString()); + ImGui.Text("CanUse: " + item.CanUse(out _, true).ToString()); - ImGui.TextWrapped(UiString.ConfigWindow_Actions_ConditionDescription.Local()); - _sequencerList?.Draw(); + if (item is HpPotionItem healPotionItem) + { + ImGui.Text("MaxHP:" + healPotionItem.MaxHp.ToString()); + } + } + catch + { + + } + } } } diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs index 06c4c5300..f780adba3 100644 --- a/RotationSolver/Updaters/SocialUpdater.cs +++ b/RotationSolver/Updaters/SocialUpdater.cs @@ -20,6 +20,9 @@ namespace RotationSolver.Updaters; internal class SocialUpdater { +#if DEBUG +#else + private static readonly List _macroToAuthor = [ "blush", @@ -30,6 +33,7 @@ internal class SocialUpdater "cheer", "stroke", ]; +#endif private static readonly HashSet saidAuthors = []; @@ -68,7 +72,7 @@ internal static void Enable() static async void DutyState_DutyCompleted(object? sender, ushort e) { - if (DataCenter.PartyMembers.Count() < 2) return; + if (DataCenter.PartyMembers.Length < 2) return; await Task.Delay(new Random().Next(4000, 6000)); @@ -91,6 +95,10 @@ static void ClientState_TerritoryChanged(ushort id) DataCenter.Territory = territory; +#if DEBUG + Svc.Log.Debug($"Move to {DataCenter.TerritoryName} ({id})"); +#endif + _dutyRotations ??= [..RotationUpdater.TryGetTypes(typeof(SocialUpdater).Assembly) .Where(t => t.IsAssignableTo(typeof(DutyRotation)) && !t.IsAbstract)]; From 3bafba6f4f4e4de6924e68efd574e2e6efbdec1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Tue, 27 Feb 2024 11:24:26 +0800 Subject: [PATCH 33/48] fix: make some methods public. --- Directory.Build.props | 2 +- RotationSolver.Basic/Helpers/ObjectHelper.cs | 22 ++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index e1e36a74f..9796471a9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net7.0-windows enable ArchiTed - 4.0.3.4 + 4.0.3.5 x64 AnyCPU latest diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 8e1f248c0..84a5a2391 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -262,7 +262,12 @@ internal static bool CanInterrupt(this GameObject o) internal static bool IsDummy(this BattleChara obj) => obj?.NameId == 541; - internal static bool IsBossFromTTK(this BattleChara obj) + /// + /// Is target a boss depends on the ttk. + /// + /// the object. + /// + public static bool IsBossFromTTK(this BattleChara obj) { if (obj == null) return false; @@ -273,8 +278,12 @@ internal static bool IsBossFromTTK(this BattleChara obj) return false; } - - internal static bool IsBossFromIcon(this BattleChara obj) + /// + /// Is target a boss depends on the icon. + /// + /// the object. + /// + public static bool IsBossFromIcon(this BattleChara obj) { if (obj == null) return false; @@ -286,7 +295,12 @@ internal static bool IsBossFromIcon(this BattleChara obj) return false; } - internal static bool IsDying(this BattleChara b) + /// + /// Is object dying. + /// + /// + /// + public static bool IsDying(this BattleChara b) { if (b == null) return false; if (b.IsDummy() && !Service.Config.ShowTargetTimeToKill) return false; From 3016cefad9bb13655fb733be1c8f239a86a29add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Tue, 27 Feb 2024 15:43:33 +0800 Subject: [PATCH 34/48] fix: add opcode. --- .../Rotations/Duties/BozjaRotation.cs | 4 +- RotationSolver.GameData/Program.cs | 26 ++ .../RotationSolver.GameData.csproj | 3 +- RotationSolver.GameData/Util.cs | 19 + .../Properties/Resources.Designer.cs | 68 ++- .../Properties/Resources.resx | 442 ++++++++++++++++++ .../StaticCodeGenerator.cs | 22 + RotationSolver/Watcher.cs | 4 +- 8 files changed, 569 insertions(+), 19 deletions(-) diff --git a/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs b/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs index 43a9fe8d5..05f5fccea 100644 --- a/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs @@ -1,4 +1,6 @@ -namespace RotationSolver.Basic.Rotations.Duties; +using ECommons.DalamudServices; + +namespace RotationSolver.Basic.Rotations.Duties; /// /// The bozja action. diff --git a/RotationSolver.GameData/Program.cs b/RotationSolver.GameData/Program.cs index 6945a4b6c..f884c9050 100644 --- a/RotationSolver.GameData/Program.cs +++ b/RotationSolver.GameData/Program.cs @@ -1,9 +1,11 @@ using Lumina; using Lumina.Data; using Lumina.Excel.GeneratedSheets; +using Newtonsoft.Json.Linq; using RotationSolver.GameData; using RotationSolver.GameData.Getters; using RotationSolver.GameData.Getters.Actions; +using System.Net; using System.Resources.NetStandard; var gameData = new GameData("C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack", new LuminaOptions @@ -89,6 +91,30 @@ namespace RotationSolver.Basic.Rotations.Basic; .Select(job => new RotationGetter(gameData, job).GetCode()); res.AddResource("Rotation", header + string.Join("\n\n", rotations)); +using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; +using var result = await client.GetAsync("https://raw.githubusercontent.com/karashiiro/FFXIVOpcodes/master/opcodes.json"); + +if (result.StatusCode != HttpStatusCode.OK) return; +var responseStream = await result.Content.ReadAsStringAsync(); + + +var strs = JToken.Parse(responseStream)[0]!["lists"]!.Children() + .SelectMany(i => i.Children()).SelectMany(i => i.Children()).Cast() + .Select(i => + { + var name = ((JValue)i["name"]!).Value as string; + var description = name!.Space(); + + return $$""" + /// + ///{{description}} + /// + [Description("{{description}}")] + {{name}} = {{((JValue)i["opcode"]!).Value}}, + """; + }); +res.AddResource("OpCode", string.Join("\n", strs)); + res.Generate(); Console.WriteLine("Finished!"); \ No newline at end of file diff --git a/RotationSolver.GameData/RotationSolver.GameData.csproj b/RotationSolver.GameData/RotationSolver.GameData.csproj index 3d2cc4dcb..6afd5a25e 100644 --- a/RotationSolver.GameData/RotationSolver.GameData.csproj +++ b/RotationSolver.GameData/RotationSolver.GameData.csproj @@ -1,4 +1,4 @@ - + Exe @@ -9,6 +9,7 @@ + diff --git a/RotationSolver.GameData/Util.cs b/RotationSolver.GameData/Util.cs index 70c23b762..b68ef346e 100644 --- a/RotationSolver.GameData/Util.cs +++ b/RotationSolver.GameData/Util.cs @@ -15,6 +15,25 @@ public static bool IsSingleJobForCombat(this ClassJobCategory jobCategory) public static string Table(this string str) => " " + str.Replace("\n", "\n "); + public static string Space(this string str) + { + string result = string.Empty; + + bool lower = false; + foreach (var c in str) + { + var isLower = char.IsLower(c); + if (lower && !isLower) + { + result += ' '; + } + lower = isLower; + result += c; + } + + return result; + } + public static string OnlyAscii(this string input) => new(input.Where(char.IsAscii).ToArray()); public static string ToPascalCase(this string input) diff --git a/RotationSolver.SourceGenerators/Properties/Resources.Designer.cs b/RotationSolver.SourceGenerators/Properties/Resources.Designer.cs index fdc9b6d59..025c46bd5 100644 --- a/RotationSolver.SourceGenerators/Properties/Resources.Designer.cs +++ b/RotationSolver.SourceGenerators/Properties/Resources.Designer.cs @@ -67,18 +67,18 @@ internal Resources() { /// ////// <summary> ////// The Custom Rotation. - ////// <br>Number of Actions: 609</br> + ////// <br>Number of Actions: 48</br> ////// </summary> - ///public partial class CustomRotation + ///public abstract partial class CustomRotation ///{ - /// private readonly Lazy<IBaseAction> _KeyItemPvECreator = new(() => + ///#region Actions + /// private readonly Lazy<IBaseAction> _SprintPvECreator = new(() => /// { - /// IBaseAction action = new BaseAction(ActionID.KeyItemPvE, true); - /// LoadActionConfigAndSetting(ref action); + /// IBaseAction action = new BaseAction((ActionID)3, false); + /// CustomRotation.LoadActionSetting(ref action); /// /// var setting = action.Setting; - /// ModifyKeyItemPvE(ref setting); - /// [rest of string was truncated]";. + /// ModifySprintPvE(r [rest of string was truncated]";. /// internal static string Action { get { @@ -137,17 +137,17 @@ internal static string ActionCategory { /// /// Looks up a localized string similar to /// <summary> - ////// <see href="https://garlandtools.org/db/#action/1"><strong>Key Item</strong></see> <i>PvE</i> (All Classes) [1] [Event] + ////// <see href="https://garlandtools.org/db/#action/3"><strong>Sprint</strong></see> <i>PvE</i> (All Classes) [3] [System] ////// <para></para> ////// </summary> - ///KeyItemPvE = 1, + ///SprintPvE = 3, ////// <summary> - ////// <see href="https://garlandtools.org/db/#action/2"><strong>Interaction</strong></see> <i>PvE</i> (All Classes) [2] [Event] + ////// <see href="https://garlandtools.org/db/#action/5"><strong>Teleport</strong></see> <i>PvE</i> (All Classes) [5] [System] ////// <para></para> ////// </summary> - ///InteractionPvE = 2, + ///TeleportPvE = 5, ////// <summary> - ////// <see href="https://garlandtools.org/db/#action/3"><strong>Sprint</strong></see> <i>PvE</i> (All Classes) [ [rest of string was truncated]";. + ////// <see href="https://garlandtools.org/db/#action/6"><strong>Return</strong></see> <i>PvE</i> (All Classes) [6] [Sys [rest of string was truncated]";. /// internal static string ActionId { get { @@ -204,7 +204,23 @@ internal static string ContentType { } /// - /// Looks up a localized string similar to . + /// Looks up a localized string similar to using RotationSolver.Basic.Actions; + /// + ///namespace RotationSolver.Basic.Rotations.Duties; + /// + ////// <summary> + ////// The Custom Rotation. + ////// <br>Number of Actions: 388</br> + ////// </summary> + ///public abstract partial class DutyRotation + ///{ + /// private readonly Lazy<IBaseAction> _MagitekCannonPvECreator = new(() => + /// { + /// IBaseAction action = new BaseAction((ActionID)1128, true); + /// CustomRotation.LoadActionSetting(ref action); + /// + /// var setting = action.Setting; + /// ModifyMagitekCannon [rest of string was truncated]";. /// internal static string DutyAction { get { @@ -213,8 +229,30 @@ internal static string DutyAction { } /// - /// Looks up a localized string similar to { - /// "PaladinRotation": "using ECommons.DalamudServices;\r\nusing ECommons.ExcelServices;\r\nusing RotationSolver.Basic.Actions;\r\nusing RotationSolver.Basic.Traits;\r\n\r\nnamespace RotationSolver.Basic.Rotations.Basic;\r\n\r\n/// <summary>\r\n/// <see href=\"https://na.finalfantasyxiv.com/jobguide/paladin\"><strong>Paladin</strong></see>\r\n/// <br>Number of Actions: 47</br>\r\n/// <br>Number of Traits: 15</br>\r\n/// </summary>\r\npublic abstract partial class PaladinRotation : CustomRotation\r\n{\r\n [rest of string was truncated]";. + /// Looks up a localized string similar to . + /// + internal static string OpCode { + get { + return ResourceManager.GetString("OpCode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to using ECommons.DalamudServices; + ///using ECommons.ExcelServices; + ///using RotationSolver.Basic.Actions; + ///using RotationSolver.Basic.Traits; + /// + ///namespace RotationSolver.Basic.Rotations.Basic; + ////// <summary> + ////// <see href="https://na.finalfantasyxiv.com/jobguide/paladin"><strong>Paladin</strong></see> + ////// <br>Number of Actions: 46</br> + ////// <br>Number of Traits: 15</br> + ////// </summary> + ///[Jobs(Job.PLD, Job.GLA)] + ///public abstract partial class PaladinRotation : CustomRotation + ///{ + /// static PLDGauge JobGauge => S [rest of string was truncated]";. /// internal static string Rotation { get { diff --git a/RotationSolver.SourceGenerators/Properties/Resources.resx b/RotationSolver.SourceGenerators/Properties/Resources.resx index b67af275e..baeea786c 100644 --- a/RotationSolver.SourceGenerators/Properties/Resources.resx +++ b/RotationSolver.SourceGenerators/Properties/Resources.resx @@ -74506,4 +74506,446 @@ private sealed protected override IBaseAction LimitBreakPvP => MesotesPvP; #endregion } + + /// <summary> +///Actor Control Target +/// </summary> +[Description("Actor Control Target")] +ActorControlTarget = 973, +/// <summary> +///Actor Control Self +/// </summary> +[Description("Actor Control Self")] +ActorControlSelf = 813, +/// <summary> +///Airship Timers +/// </summary> +[Description("Airship Timers")] +AirshipTimers = 370, +/// <summary> +///Actor Gauge +/// </summary> +[Description("Actor Gauge")] +ActorGauge = 431, +/// <summary> +///Actor Move +/// </summary> +[Description("Actor Move")] +ActorMove = 845, +/// <summary> +///Airship Status +/// </summary> +[Description("Airship Status")] +AirshipStatus = 471, +/// <summary> +///Airship Exploration Result +/// </summary> +[Description("Airship Exploration Result")] +AirshipExplorationResult = 286, +/// <summary> +///Airship Status List +/// </summary> +[Description("Airship Status List")] +AirshipStatusList = 517, +/// <summary> +///Actor Set Pos +/// </summary> +[Description("Actor Set Pos")] +ActorSetPos = 174, +/// <summary> +///Actor Control +/// </summary> +[Description("Actor Control")] +ActorControl = 717, +/// <summary> +///Actor Cast +/// </summary> +[Description("Actor Cast")] +ActorCast = 421, +/// <summary> +///CFNotify +/// </summary> +[Description("CFNotify")] +CFNotify = 556, +/// <summary> +///Currency Crystal Info +/// </summary> +[Description("Currency Crystal Info")] +CurrencyCrystalInfo = 390, +/// <summary> +///Container Info +/// </summary> +[Description("Container Info")] +ContainerInfo = 849, +/// <summary> +///Desynth Result +/// </summary> +[Description("Desynth Result")] +DesynthResult = 967, +/// <summary> +///Event Play 8 +/// </summary> +[Description("Event Play 8")] +EventPlay8 = 805, +/// <summary> +///Effect Result Basic +/// </summary> +[Description("Effect Result Basic")] +EffectResultBasic = 507, +/// <summary> +///Event Play 128 +/// </summary> +[Description("Event Play 128")] +EventPlay128 = 790, +/// <summary> +///Event Play 16 +/// </summary> +[Description("Event Play 16")] +EventPlay16 = 577, +/// <summary> +///Event Play +/// </summary> +[Description("Event Play")] +EventPlay = 423, +/// <summary> +///Event Play 32 +/// </summary> +[Description("Event Play 32")] +EventPlay32 = 799, +/// <summary> +///Examine Search Info +/// </summary> +[Description("Examine Search Info")] +ExamineSearchInfo = 410, +/// <summary> +///Effect Result +/// </summary> +[Description("Effect Result")] +EffectResult = 898, +/// <summary> +///Environment Control +/// </summary> +[Description("Environment Control")] +EnvironmentControl = 141, +/// <summary> +///Event Play 4 +/// </summary> +[Description("Event Play 4")] +EventPlay4 = 955, +/// <summary> +///Event Play 255 +/// </summary> +[Description("Event Play 255")] +EventPlay255 = 936, +/// <summary> +///Event Finish +/// </summary> +[Description("Event Finish")] +EventFinish = 267, +/// <summary> +///Event Play 64 +/// </summary> +[Description("Event Play 64")] +EventPlay64 = 894, +/// <summary> +///Event Start +/// </summary> +[Description("Event Start")] +EventStart = 709, +/// <summary> +///Examine +/// </summary> +[Description("Examine")] +Examine = 862, +/// <summary> +///Effect +/// </summary> +[Description("Effect")] +Effect = 316, +/// <summary> +///Free Company Dialog +/// </summary> +[Description("Free Company Dialog")] +FreeCompanyDialog = 644, +/// <summary> +///Free Company Info +/// </summary> +[Description("Free Company Info")] +FreeCompanyInfo = 354, +/// <summary> +///Inventory Action Ack +/// </summary> +[Description("Inventory Action Ack")] +InventoryActionAck = 522, +/// <summary> +///Init Zone +/// </summary> +[Description("Init Zone")] +InitZone = 560, +/// <summary> +///Inventory Transaction +/// </summary> +[Description("Inventory Transaction")] +InventoryTransaction = 196, +/// <summary> +///Inventory Transaction Finish +/// </summary> +[Description("Inventory Transaction Finish")] +InventoryTransactionFinish = 781, +/// <summary> +///Item Market Board Info +/// </summary> +[Description("Item Market Board Info")] +ItemMarketBoardInfo = 596, +/// <summary> +///Item Info +/// </summary> +[Description("Item Info")] +ItemInfo = 810, +/// <summary> +///Island Workshop Supply Demand +/// </summary> +[Description("Island Workshop Supply Demand")] +IslandWorkshopSupplyDemand = 358, +/// <summary> +///Logout +/// </summary> +[Description("Logout")] +Logout = 435, +/// <summary> +///Market Board Search Result +/// </summary> +[Description("Market Board Search Result")] +MarketBoardSearchResult = 773, +/// <summary> +///Market Board Item Listing +/// </summary> +[Description("Market Board Item Listing")] +MarketBoardItemListing = 567, +/// <summary> +///Market Board Item Listing History +/// </summary> +[Description("Market Board Item Listing History")] +MarketBoardItemListingHistory = 330, +/// <summary> +///Market Board Item Listing Count +/// </summary> +[Description("Market Board Item Listing Count")] +MarketBoardItemListingCount = 510, +/// <summary> +///Market Board Purchase +/// </summary> +[Description("Market Board Purchase")] +MarketBoardPurchase = 374, +/// <summary> +///Npc Spawn +/// </summary> +[Description("Npc Spawn")] +NpcSpawn = 636, +/// <summary> +///Object Spawn +/// </summary> +[Description("Object Spawn")] +ObjectSpawn = 658, +/// <summary> +///Player Stats +/// </summary> +[Description("Player Stats")] +PlayerStats = 692, +/// <summary> +///Playtime +/// </summary> +[Description("Playtime")] +Playtime = 938, +/// <summary> +///Player Setup +/// </summary> +[Description("Player Setup")] +PlayerSetup = 607, +/// <summary> +///Player Spawn +/// </summary> +[Description("Player Spawn")] +PlayerSpawn = 221, +/// <summary> +///Place Field Marker Preset +/// </summary> +[Description("Place Field Marker Preset")] +PlaceFieldMarkerPreset = 307, +/// <summary> +///Place Field Marker +/// </summary> +[Description("Place Field Marker")] +PlaceFieldMarker = 539, +/// <summary> +///Prepare Zoning +/// </summary> +[Description("Prepare Zoning")] +PrepareZoning = 819, +/// <summary> +///Result Dialog +/// </summary> +[Description("Result Dialog")] +ResultDialog = 169, +/// <summary> +///Retainer Information +/// </summary> +[Description("Retainer Information")] +RetainerInformation = 830, +/// <summary> +///Submarine Timers +/// </summary> +[Description("Submarine Timers")] +SubmarineTimers = 797, +/// <summary> +///System Log Message +/// </summary> +[Description("System Log Message")] +SystemLogMessage = 313, +/// <summary> +///Submarine Progression Status +/// </summary> +[Description("Submarine Progression Status")] +SubmarineProgressionStatus = 968, +/// <summary> +///Status Effect List 2 +/// </summary> +[Description("Status Effect List 2")] +StatusEffectList2 = 878, +/// <summary> +///Submarine Status List +/// </summary> +[Description("Submarine Status List")] +SubmarineStatusList = 292, +/// <summary> +///Status Effect List 3 +/// </summary> +[Description("Status Effect List 3")] +StatusEffectList3 = 741, +/// <summary> +///Status Effect List +/// </summary> +[Description("Status Effect List")] +StatusEffectList = 649, +/// <summary> +///Submarine Exploration Result +/// </summary> +[Description("Submarine Exploration Result")] +SubmarineExplorationResult = 119, +/// <summary> +///Update Class Info +/// </summary> +[Description("Update Class Info")] +UpdateClassInfo = 234, +/// <summary> +///Update Hp Mp Tp +/// </summary> +[Description("Update Hp Mp Tp")] +UpdateHpMpTp = 920, +/// <summary> +///Update Search Info +/// </summary> +[Description("Update Search Info")] +UpdateSearchInfo = 574, +/// <summary> +///Update Inventory Slot +/// </summary> +[Description("Update Inventory Slot")] +UpdateInventorySlot = 548, +/// <summary> +///Weather Change +/// </summary> +[Description("Weather Change")] +WeatherChange = 127, +/// <summary> +///Client Trigger +/// </summary> +[Description("Client Trigger")] +ClientTrigger = 716, +/// <summary> +///Inventory Modify Handler +/// </summary> +[Description("Inventory Modify Handler")] +InventoryModifyHandler = 553, +/// <summary> +///Market Board Purchase Handler +/// </summary> +[Description("Market Board Purchase Handler")] +MarketBoardPurchaseHandler = 631, +/// <summary> +///Set Search Info Handler +/// </summary> +[Description("Set Search Info Handler")] +SetSearchInfoHandler = 689, +/// <summary> +///Update Position Handler +/// </summary> +[Description("Update Position Handler")] +UpdatePositionHandler = 417, +/// <summary> +///Update Position Instance +/// </summary> +[Description("Update Position Instance")] +UpdatePositionInstance = 608, +/// <summary> +///Lobby Error +/// </summary> +[Description("Lobby Error")] +LobbyError = 2, +/// <summary> +///Lobby Service Account List +/// </summary> +[Description("Lobby Service Account List")] +LobbyServiceAccountList = 12, +/// <summary> +///Lobby Char List +/// </summary> +[Description("Lobby Char List")] +LobbyCharList = 13, +/// <summary> +///Lobby Char Create +/// </summary> +[Description("Lobby Char Create")] +LobbyCharCreate = 14, +/// <summary> +///Lobby Enter World +/// </summary> +[Description("Lobby Enter World")] +LobbyEnterWorld = 15, +/// <summary> +///Lobby Server List +/// </summary> +[Description("Lobby Server List")] +LobbyServerList = 21, +/// <summary> +///Lobby Retainer List +/// </summary> +[Description("Lobby Retainer List")] +LobbyRetainerList = 23, +/// <summary> +///Client Version Info +/// </summary> +[Description("Client Version Info")] +ClientVersionInfo = 5, +/// <summary> +///Req Char List +/// </summary> +[Description("Req Char List")] +ReqCharList = 3, +/// <summary> +///Req Enter World +/// </summary> +[Description("Req Enter World")] +ReqEnterWorld = 4, +/// <summary> +///Req Char Delete +/// </summary> +[Description("Req Char Delete")] +ReqCharDelete = 10, +/// <summary> +///Req Char Create +/// </summary> +[Description("Req Char Create")] +ReqCharCreate = 11, + \ No newline at end of file diff --git a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs index 0f1fcdffb..19a6eaf31 100644 --- a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs +++ b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs @@ -25,6 +25,28 @@ private static void Execute(SourceProductionContext context) GenerateActionCate(context); GenerateBaseRotation(context); GenerateRotations(context); + GenerateOpCode(context); + } + + private static async void GenerateOpCode(SourceProductionContext context) + { + var code = $$""" + namespace RotationSolver.Basic.Data; + + /// + /// The opcode + /// + public enum OpCode : ushort + { + /// + /// + /// + None = 0, + {{Properties.Resources.OpCode.Table()}} + } + """; + + context.AddSource("OpCode.g.cs", code); } private static void GenerateStatus(SourceProductionContext context) diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index cc2a49c53..977aef99d 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -190,7 +190,7 @@ private static void ActionFromEnemy(ActionEffectSet set) } } - if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Count() >= 4 && set.Action.Cast100ms > 0) + if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action.Cast100ms > 0) { var type = set.Action.GetActionCate(); @@ -200,7 +200,7 @@ private static void ActionFromEnemy(ActionEffectSet set) DataCenter.PartyMembers.Any(p => p.ObjectId == e.TargetID) && e.GetSpecificTypeEffect(ActionEffectType.Damage, out var effect) && (effect.value > 0 || (effect.param0 & 6) == 6)) - == DataCenter.PartyMembers.Count()) + == DataCenter.PartyMembers.Length) { if (Service.Config.RecordCastingArea) { From ab3a209a10315f2d49eb43455e885b66d15c2ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:21:11 +0800 Subject: [PATCH 35/48] New translations localization.json (Spanish) --- RotationSolver/Localization/es.json | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/RotationSolver/Localization/es.json b/RotationSolver/Localization/es.json index 3ca7a3b84..68b284d99 100644 --- a/RotationSolver/Localization/es.json +++ b/RotationSolver/Localization/es.json @@ -102,7 +102,6 @@ "RotationSolver.Basic.Configuration.Conditions.ActionCondition": "Action Condition", "RotationSolver.Basic.Configuration.Conditions.TraitCondition": "Trait Condition", "RotationSolver.Basic.Configuration.Conditions.TargetCondition": "Target Condition", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "RotationSolver.Basic.Configuration.Conditions.RotationCondition", "RotationSolver.Basic.Configuration.Conditions.NamedCondition": "Named Condition", "RotationSolver.Basic.Configuration.Conditions.TerritoryCondition": "Territory Condition", "RotationSolver.Data.UiString.ActionSequencer_FromClipboard": "From Clipboard", @@ -264,5 +263,49 @@ "StateIconHeightName": "State icon height", "StateIconSizeName": "State icon size", "TargetIconSizeName": "The size of the next ability that will be used icon.", - "ShowTargetTimeToKillName": "Show the target's time to kill." + "ShowTargetTimeToKillName": "Show the target's time to kill.", + "UseTasksForOverlayName": "Use tasks for making the overlay window faster.", + "RotationSolver.Data.UiString.ActionSequencer_Offset_Description": "Delay its turning.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_Copy": "Copy to Clipboard", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Bool": "Boolean", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Integer": "Byte", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Float": "Float", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Last": "Last", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MemberName": "Member Name", + "RotationSolver.Basic.Attributes.ConfigUnitType.None": "None", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Single AddersgallTime": "Single AddersgallTime", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersMinHP": "Min HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersAverHP": "Average HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single AverageTimeToKill": "Average time to kill", + "RotationSolver.Basic.Rotations.CustomRotation.Single Ping": "Your ping", + "RotationSolver.Basic.Rotations.CustomRotation.Single NextAbilityToNextGCD": "Time from next ability to next GCD", + "RotationSolver.Basic.Rotations.CustomRotation.Single CombatTime": "Combat time", + "RotationSolver.Basic.Rotations.CustomRotation.Single StopMovingTime": "Stop moving time", + "RotationSolver.Basic.Rotations.CustomRotation.Single CountDownAhead": "Count Down ahead", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbility": "Health of Area Ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpell": "Health of Area spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbilityHot": "Health of Area Ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpellHot": "Health of Area spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbility": "Health of single ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpell": "Health of single spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbilityHot": "Health of single ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpellHot": "Health of single spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthForDyingTanks": "Health of dying tank", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Boolean HasEukrasia": "Boolean HasEukrasia", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasSwift": "Has Swift", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasTankStance": "Has tank stance", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsMoving": "Is Moving or Jumping", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean InCombat": "In Combat", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean NotInCombatDelay": "Not In Combat Delay", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasCompanion": "Has companion", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsFullParty": "Is Full Party", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInRange": "Has hostiles in Range", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInMaxRange": "Has hostiles in 25 yalms", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean MobsTime": "Mobs Time", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean AutoState": "The state of auto. True for on.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsManual": "The state of manual. True for manual.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" } \ No newline at end of file From 05cab6a7dc9220ff322c872033ae9c19b6adbfa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:21:12 +0800 Subject: [PATCH 36/48] New translations localization.json (German) --- RotationSolver/Localization/de.json | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/RotationSolver/Localization/de.json b/RotationSolver/Localization/de.json index 3ca7a3b84..68b284d99 100644 --- a/RotationSolver/Localization/de.json +++ b/RotationSolver/Localization/de.json @@ -102,7 +102,6 @@ "RotationSolver.Basic.Configuration.Conditions.ActionCondition": "Action Condition", "RotationSolver.Basic.Configuration.Conditions.TraitCondition": "Trait Condition", "RotationSolver.Basic.Configuration.Conditions.TargetCondition": "Target Condition", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "RotationSolver.Basic.Configuration.Conditions.RotationCondition", "RotationSolver.Basic.Configuration.Conditions.NamedCondition": "Named Condition", "RotationSolver.Basic.Configuration.Conditions.TerritoryCondition": "Territory Condition", "RotationSolver.Data.UiString.ActionSequencer_FromClipboard": "From Clipboard", @@ -264,5 +263,49 @@ "StateIconHeightName": "State icon height", "StateIconSizeName": "State icon size", "TargetIconSizeName": "The size of the next ability that will be used icon.", - "ShowTargetTimeToKillName": "Show the target's time to kill." + "ShowTargetTimeToKillName": "Show the target's time to kill.", + "UseTasksForOverlayName": "Use tasks for making the overlay window faster.", + "RotationSolver.Data.UiString.ActionSequencer_Offset_Description": "Delay its turning.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_Copy": "Copy to Clipboard", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Bool": "Boolean", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Integer": "Byte", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Float": "Float", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Last": "Last", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MemberName": "Member Name", + "RotationSolver.Basic.Attributes.ConfigUnitType.None": "None", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Single AddersgallTime": "Single AddersgallTime", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersMinHP": "Min HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersAverHP": "Average HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single AverageTimeToKill": "Average time to kill", + "RotationSolver.Basic.Rotations.CustomRotation.Single Ping": "Your ping", + "RotationSolver.Basic.Rotations.CustomRotation.Single NextAbilityToNextGCD": "Time from next ability to next GCD", + "RotationSolver.Basic.Rotations.CustomRotation.Single CombatTime": "Combat time", + "RotationSolver.Basic.Rotations.CustomRotation.Single StopMovingTime": "Stop moving time", + "RotationSolver.Basic.Rotations.CustomRotation.Single CountDownAhead": "Count Down ahead", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbility": "Health of Area Ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpell": "Health of Area spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbilityHot": "Health of Area Ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpellHot": "Health of Area spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbility": "Health of single ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpell": "Health of single spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbilityHot": "Health of single ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpellHot": "Health of single spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthForDyingTanks": "Health of dying tank", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Boolean HasEukrasia": "Boolean HasEukrasia", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasSwift": "Has Swift", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasTankStance": "Has tank stance", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsMoving": "Is Moving or Jumping", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean InCombat": "In Combat", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean NotInCombatDelay": "Not In Combat Delay", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasCompanion": "Has companion", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsFullParty": "Is Full Party", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInRange": "Has hostiles in Range", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInMaxRange": "Has hostiles in 25 yalms", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean MobsTime": "Mobs Time", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean AutoState": "The state of auto. True for on.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsManual": "The state of manual. True for manual.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" } \ No newline at end of file From 4b96b5b12e4fab5e27e3983458b2a51719198974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:21:13 +0800 Subject: [PATCH 37/48] New translations localization.json (Japanese) --- RotationSolver/Localization/ja.json | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/RotationSolver/Localization/ja.json b/RotationSolver/Localization/ja.json index 3ca7a3b84..68b284d99 100644 --- a/RotationSolver/Localization/ja.json +++ b/RotationSolver/Localization/ja.json @@ -102,7 +102,6 @@ "RotationSolver.Basic.Configuration.Conditions.ActionCondition": "Action Condition", "RotationSolver.Basic.Configuration.Conditions.TraitCondition": "Trait Condition", "RotationSolver.Basic.Configuration.Conditions.TargetCondition": "Target Condition", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "RotationSolver.Basic.Configuration.Conditions.RotationCondition", "RotationSolver.Basic.Configuration.Conditions.NamedCondition": "Named Condition", "RotationSolver.Basic.Configuration.Conditions.TerritoryCondition": "Territory Condition", "RotationSolver.Data.UiString.ActionSequencer_FromClipboard": "From Clipboard", @@ -264,5 +263,49 @@ "StateIconHeightName": "State icon height", "StateIconSizeName": "State icon size", "TargetIconSizeName": "The size of the next ability that will be used icon.", - "ShowTargetTimeToKillName": "Show the target's time to kill." + "ShowTargetTimeToKillName": "Show the target's time to kill.", + "UseTasksForOverlayName": "Use tasks for making the overlay window faster.", + "RotationSolver.Data.UiString.ActionSequencer_Offset_Description": "Delay its turning.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_Copy": "Copy to Clipboard", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Bool": "Boolean", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Integer": "Byte", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Float": "Float", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Last": "Last", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MemberName": "Member Name", + "RotationSolver.Basic.Attributes.ConfigUnitType.None": "None", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Single AddersgallTime": "Single AddersgallTime", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersMinHP": "Min HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersAverHP": "Average HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single AverageTimeToKill": "Average time to kill", + "RotationSolver.Basic.Rotations.CustomRotation.Single Ping": "Your ping", + "RotationSolver.Basic.Rotations.CustomRotation.Single NextAbilityToNextGCD": "Time from next ability to next GCD", + "RotationSolver.Basic.Rotations.CustomRotation.Single CombatTime": "Combat time", + "RotationSolver.Basic.Rotations.CustomRotation.Single StopMovingTime": "Stop moving time", + "RotationSolver.Basic.Rotations.CustomRotation.Single CountDownAhead": "Count Down ahead", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbility": "Health of Area Ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpell": "Health of Area spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbilityHot": "Health of Area Ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpellHot": "Health of Area spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbility": "Health of single ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpell": "Health of single spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbilityHot": "Health of single ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpellHot": "Health of single spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthForDyingTanks": "Health of dying tank", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Boolean HasEukrasia": "Boolean HasEukrasia", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasSwift": "Has Swift", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasTankStance": "Has tank stance", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsMoving": "Is Moving or Jumping", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean InCombat": "In Combat", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean NotInCombatDelay": "Not In Combat Delay", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasCompanion": "Has companion", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsFullParty": "Is Full Party", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInRange": "Has hostiles in Range", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInMaxRange": "Has hostiles in 25 yalms", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean MobsTime": "Mobs Time", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean AutoState": "The state of auto. True for on.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsManual": "The state of manual. True for manual.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" } \ No newline at end of file From f237f4b769e4ce969f200b1a47b751aba3ecc4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:21:13 +0800 Subject: [PATCH 38/48] New translations localization.json (Chinese Simplified) --- RotationSolver/Localization/zh.json | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/RotationSolver/Localization/zh.json b/RotationSolver/Localization/zh.json index 762cb555f..531d2e188 100644 --- a/RotationSolver/Localization/zh.json +++ b/RotationSolver/Localization/zh.json @@ -102,7 +102,6 @@ "RotationSolver.Basic.Configuration.Conditions.ActionCondition": "技能使用条件", "RotationSolver.Basic.Configuration.Conditions.TraitCondition": "特性条件", "RotationSolver.Basic.Configuration.Conditions.TargetCondition": "目标选择条件", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "RotationSolver.Basic.Configuration.Conditions.RotationCondition", "RotationSolver.Basic.Configuration.Conditions.NamedCondition": "已命名条件", "RotationSolver.Basic.Configuration.Conditions.TerritoryCondition": "地图条件", "RotationSolver.Data.UiString.ActionSequencer_FromClipboard": "从剪贴板冲粘贴", @@ -264,5 +263,49 @@ "StateIconHeightName": "State icon height", "StateIconSizeName": "State icon size", "TargetIconSizeName": "The size of the next ability that will be used icon.", - "ShowTargetTimeToKillName": "Show the target's time to kill." + "ShowTargetTimeToKillName": "Show the target's time to kill.", + "UseTasksForOverlayName": "Use tasks for making the overlay window faster.", + "RotationSolver.Data.UiString.ActionSequencer_Offset_Description": "Delay its turning.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_Copy": "Copy to Clipboard", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Bool": "Boolean", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Integer": "Byte", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Float": "Float", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Last": "Last", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MemberName": "Member Name", + "RotationSolver.Basic.Attributes.ConfigUnitType.None": "None", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Single AddersgallTime": "Single AddersgallTime", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersMinHP": "Min HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersAverHP": "Average HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single AverageTimeToKill": "Average time to kill", + "RotationSolver.Basic.Rotations.CustomRotation.Single Ping": "Your ping", + "RotationSolver.Basic.Rotations.CustomRotation.Single NextAbilityToNextGCD": "Time from next ability to next GCD", + "RotationSolver.Basic.Rotations.CustomRotation.Single CombatTime": "Combat time", + "RotationSolver.Basic.Rotations.CustomRotation.Single StopMovingTime": "Stop moving time", + "RotationSolver.Basic.Rotations.CustomRotation.Single CountDownAhead": "Count Down ahead", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbility": "Health of Area Ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpell": "Health of Area spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbilityHot": "Health of Area Ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpellHot": "Health of Area spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbility": "Health of single ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpell": "Health of single spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbilityHot": "Health of single ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpellHot": "Health of single spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthForDyingTanks": "Health of dying tank", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Boolean HasEukrasia": "Boolean HasEukrasia", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasSwift": "Has Swift", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasTankStance": "Has tank stance", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsMoving": "Is Moving or Jumping", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean InCombat": "In Combat", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean NotInCombatDelay": "Not In Combat Delay", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasCompanion": "Has companion", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsFullParty": "Is Full Party", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInRange": "Has hostiles in Range", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInMaxRange": "Has hostiles in 25 yalms", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean MobsTime": "Mobs Time", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean AutoState": "The state of auto. True for on.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsManual": "The state of manual. True for manual.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" } \ No newline at end of file From 6168aae75601941279264b3f2ab8695d2edb748f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:21:14 +0800 Subject: [PATCH 39/48] New translations localization.json (French) --- RotationSolver/Localization/fr.json | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/RotationSolver/Localization/fr.json b/RotationSolver/Localization/fr.json index 3ca7a3b84..68b284d99 100644 --- a/RotationSolver/Localization/fr.json +++ b/RotationSolver/Localization/fr.json @@ -102,7 +102,6 @@ "RotationSolver.Basic.Configuration.Conditions.ActionCondition": "Action Condition", "RotationSolver.Basic.Configuration.Conditions.TraitCondition": "Trait Condition", "RotationSolver.Basic.Configuration.Conditions.TargetCondition": "Target Condition", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "RotationSolver.Basic.Configuration.Conditions.RotationCondition", "RotationSolver.Basic.Configuration.Conditions.NamedCondition": "Named Condition", "RotationSolver.Basic.Configuration.Conditions.TerritoryCondition": "Territory Condition", "RotationSolver.Data.UiString.ActionSequencer_FromClipboard": "From Clipboard", @@ -264,5 +263,49 @@ "StateIconHeightName": "State icon height", "StateIconSizeName": "State icon size", "TargetIconSizeName": "The size of the next ability that will be used icon.", - "ShowTargetTimeToKillName": "Show the target's time to kill." + "ShowTargetTimeToKillName": "Show the target's time to kill.", + "UseTasksForOverlayName": "Use tasks for making the overlay window faster.", + "RotationSolver.Data.UiString.ActionSequencer_Offset_Description": "Delay its turning.", + "RotationSolver.Data.UiString.ConfigWindow_Actions_Copy": "Copy to Clipboard", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Bool": "Boolean", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Integer": "Byte", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Float": "Float", + "RotationSolver.Basic.Configuration.Conditions.ComboConditionType.Last": "Last", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MemberName": "Member Name", + "RotationSolver.Basic.Attributes.ConfigUnitType.None": "None", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Single AddersgallTime": "Single AddersgallTime", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersMinHP": "Min HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single PartyMembersAverHP": "Average HP in party members.", + "RotationSolver.Basic.Rotations.CustomRotation.Single AverageTimeToKill": "Average time to kill", + "RotationSolver.Basic.Rotations.CustomRotation.Single Ping": "Your ping", + "RotationSolver.Basic.Rotations.CustomRotation.Single NextAbilityToNextGCD": "Time from next ability to next GCD", + "RotationSolver.Basic.Rotations.CustomRotation.Single CombatTime": "Combat time", + "RotationSolver.Basic.Rotations.CustomRotation.Single StopMovingTime": "Stop moving time", + "RotationSolver.Basic.Rotations.CustomRotation.Single CountDownAhead": "Count Down ahead", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbility": "Health of Area Ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpell": "Health of Area spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaAbilityHot": "Health of Area Ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthAreaSpellHot": "Health of Area spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbility": "Health of single ability", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpell": "Health of single spell", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleAbilityHot": "Health of single ability Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthSingleSpellHot": "Health of single spell Hot", + "RotationSolver.Basic.Rotations.CustomRotation.Single HealthForDyingTanks": "Health of dying tank", + "RotationSolver.Basic.Rotations.Basic.SageRotation.Boolean HasEukrasia": "Boolean HasEukrasia", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasSwift": "Has Swift", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasTankStance": "Has tank stance", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsMoving": "Is Moving or Jumping", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean InCombat": "In Combat", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean NotInCombatDelay": "Not In Combat Delay", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasCompanion": "Has companion", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsFullParty": "Is Full Party", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInRange": "Has hostiles in Range", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean HasHostilesInMaxRange": "Has hostiles in 25 yalms", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean MobsTime": "Mobs Time", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean AutoState": "The state of auto. True for on.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsManual": "The state of manual. True for manual.", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", + "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" } \ No newline at end of file From 57ad3f81d1468fc27b41f4435eb76c68b3e88244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:52:48 +0800 Subject: [PATCH 40/48] feat: add a thing to check the timeline. --- Resources/RotationSolverRecord.json | 4 +- .../Actions/ActionTargetInfo.cs | 2 +- RotationSolver.Basic/Actions/BaseAction.cs | 3 + RotationSolver.Basic/Data/TimelineItem.cs | 24 ++++++ RotationSolver.Basic/DataCenter.cs | 4 + .../Rotations/CustomRotation_OtherInfo.cs | 4 +- .../Rotations/Duties/BozjaRotation.cs | 4 +- .../StaticCodeGenerator.cs | 42 +++++----- RotationSolver/RotationSolverPlugin.cs | 2 +- RotationSolver/UI/RotationConfigWindow.cs | 24 ++---- RotationSolver/Updaters/ActionUpdater.cs | 1 + RotationSolver/Updaters/MajorUpdater.cs | 4 +- RotationSolver/Updaters/RaidTimeUpdater.cs | 83 +++++++++++++++++++ RotationSolver/Updaters/SocialUpdater.cs | 1 - RotationSolver/Watcher.cs | 25 +++++- 15 files changed, 176 insertions(+), 51 deletions(-) create mode 100644 RotationSolver.Basic/Data/TimelineItem.cs create mode 100644 RotationSolver/Updaters/RaidTimeUpdater.cs diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index 002bca4f1..ff50d467c 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 86236, - "SayingHelloCount": 75, + "ClickingCount": 87877, + "SayingHelloCount": 76, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index 322def838..115ea7bc0 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -349,7 +349,7 @@ private readonly bool CheckTimeToKill(GameObject gameObject) DataCenter.TerritoryContentType == TerritoryContentType.Raids && DataCenter.AllianceMembers.Count(p => p is PlayerCharacter) == 8) { - pts = pts.Union([Vector3.Zero, new(100, 0, 100)]).ToArray(); + pts = [.. pts, Vector3.Zero, new(100, 0, 100)]; } } diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index 1c5b015d4..b9372bd96 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -187,4 +187,7 @@ public unsafe bool Use() return ActionManager.Instance()->UseAction(ActionType.Action, adjustId, target.Target?.ObjectId ?? GameObject.InvalidGameObjectId); } } + + /// + public override string ToString() => Name; } diff --git a/RotationSolver.Basic/Data/TimelineItem.cs b/RotationSolver.Basic/Data/TimelineItem.cs new file mode 100644 index 000000000..8b1594e8f --- /dev/null +++ b/RotationSolver.Basic/Data/TimelineItem.cs @@ -0,0 +1,24 @@ +using System.Text.RegularExpressions; + +namespace RotationSolver.Basic.Data; +internal readonly struct TimelineItem(float time, string name, params string[] ids) +{ + public float Time => time; + + public bool IsShown => !name.StartsWith("--") && !name.EndsWith("--"); + + public bool IsIdMatched(uint id) + { + return ids.Any(i => new Regex(i).IsMatch(id.ToString("X"))); + } + + public override string ToString() + { + return $""" + IsShown: {IsShown}, + Time: {time}, + Name: {name}, + Ids: {string.Join(", ", ids)} + """; + } +} diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 483013446..d5fba5536 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -261,6 +261,10 @@ internal static SpecialCommandType SpecialType public static bool NotInCombatDelay => _notInCombatDelay.Delay(!InCombat); internal static float CombatTimeRaw { get; set; } + internal static float RaidTimeOffset { get; set; } = 0; + internal static float RaidTimeRaw => CombatTimeRaw + RaidTimeOffset; + + internal static TimelineItem[] TimelineItems { get; set; } = []; public static BattleChara[] PartyMembers { get; internal set; } = []; public static BattleChara[] AllianceMembers { get; internal set; } = []; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs b/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs index ea9f2ebb3..e24cc007e 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs @@ -173,7 +173,9 @@ public unsafe static byte LimitBreakLevel get { var controller = UIState.Instance()->LimitBreakController; - return (byte)(controller.CurrentValue / *(ushort*)&controller.BarValue); + var barValue = *(ushort*)&controller.BarValue; + if (barValue == 0) return 0; + return (byte)(controller.CurrentValue / barValue); } } diff --git a/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs b/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs index 05f5fccea..43a9fe8d5 100644 --- a/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Duties/BozjaRotation.cs @@ -1,6 +1,4 @@ -using ECommons.DalamudServices; - -namespace RotationSolver.Basic.Rotations.Duties; +namespace RotationSolver.Basic.Rotations.Duties; /// /// The bozja action. diff --git a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs index 19a6eaf31..f1a261659 100644 --- a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs +++ b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs @@ -25,29 +25,29 @@ private static void Execute(SourceProductionContext context) GenerateActionCate(context); GenerateBaseRotation(context); GenerateRotations(context); - GenerateOpCode(context); + //GenerateOpCode(context); } - private static async void GenerateOpCode(SourceProductionContext context) - { - var code = $$""" - namespace RotationSolver.Basic.Data; - - /// - /// The opcode - /// - public enum OpCode : ushort - { - /// - /// - /// - None = 0, - {{Properties.Resources.OpCode.Table()}} - } - """; - - context.AddSource("OpCode.g.cs", code); - } + //private static void GenerateOpCode(SourceProductionContext context) + //{ + // var code = $$""" + // namespace RotationSolver.Basic.Data; + + // /// + // /// The opcode + // /// + // public enum OpCode : ushort + // { + // /// + // /// + // /// + // None = 0, + // {{Properties.Resources.OpCode.Table()}} + // } + // """; + + // context.AddSource("OpCode.g.cs", code); + //} private static void GenerateStatus(SourceProductionContext context) { diff --git a/RotationSolver/RotationSolverPlugin.cs b/RotationSolver/RotationSolverPlugin.cs index 80306f3e0..db069194e 100644 --- a/RotationSolver/RotationSolverPlugin.cs +++ b/RotationSolver/RotationSolverPlugin.cs @@ -38,7 +38,7 @@ public RotationSolverPlugin(DalamudPluginInterface pluginInterface) IconSet.InIt(); //Init! - Clipper.InflatePaths(new PathsD(new PathD[] { Clipper.MakePath(new double[] { 0, 0, 1, 1 }) }), 0, JoinType.Round, EndType.Joined); + Clipper.InflatePaths(new PathsD([Clipper.MakePath(new double[] { 0, 0, 1, 1 })]), 0, JoinType.Round, EndType.Joined); _dis.Add(new Service()); try diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index 8d9762701..762466f35 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2,7 +2,6 @@ using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; -using Dalamud.Interface.Utility.Table; using Dalamud.Interface.Windowing; using Dalamud.Utility; using ECommons.DalamudServices; @@ -2386,7 +2385,7 @@ private static void DrawDebug() {() =>"Target Data", DrawTargetData }, {() =>"Next Action", DrawNextAction }, {() =>"Last Action", DrawLastAction }, - {() =>"Icon", DrawIcon }, + {() =>"Others", DrawOthers }, {() =>"Effect", () => { ImGui.Text(Watcher.ShowStrSelf); @@ -2512,7 +2511,6 @@ private static void DrawNextAction() ImGui.Text("Ability Remain: " + DataCenter.AbilityRemain.ToString()); ImGui.Text("Action Remain: " + DataCenter.ActionRemain.ToString()); ImGui.Text("Weapon Remain: " + DataCenter.WeaponRemain.ToString()); - ImGui.Text("Time: " + (DataCenter.CombatTimeRaw + DataCenter.WeaponRemain).ToString()); } private static void DrawLastAction() @@ -2523,23 +2521,11 @@ private static void DrawLastAction() DrawAction(DataCenter.LastComboAction, nameof(DataCenter.LastComboAction)); } - private static unsafe void DrawIcon() + private static unsafe void DrawOthers() { - //ImGui.Text("Hate"); - //foreach (var hate in UIState.Instance()->Hate.HateArraySpan) - //{ - // var name = Svc.Objects.SearchById(hate.ObjectId)?.Name ?? "Unknown"; - // ImGui.Text($"{name} : {hate.Enmity}"); - //} - //ImGui.Spacing(); - //ImGui.Text("Hater"); - //foreach (var hater in UIState.Instance()->Hater.HaterArraySpan) - //{ - // var name = Svc.Objects.SearchById(hater.ObjectId)?.Name ?? "Unknown"; - // ImGui.Text($"{name} : {hater.Enmity}"); - //} - - ImGui.Text(CustomRotation.LimitBreakLevel.ToString()); + ImGui.Text("Combat Time: " + (DataCenter.CombatTimeRaw).ToString()); + ImGui.Text("Raid Time: " + (DataCenter.RaidTimeRaw).ToString()); + ImGui.Text("Limit Break: " + CustomRotation.LimitBreakLevel.ToString()); } private static void DrawAction(ActionID id, string type) diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs index c70a51979..d1cf5cc6f 100644 --- a/RotationSolver/Updaters/ActionUpdater.cs +++ b/RotationSolver/Updaters/ActionUpdater.cs @@ -143,6 +143,7 @@ private static void UpdateCombatTime() if (_startCombatTime == DateTime.MinValue) { DataCenter.CombatTimeRaw = 0; + DataCenter.RaidTimeOffset = 0; } else { diff --git a/RotationSolver/Updaters/MajorUpdater.cs b/RotationSolver/Updaters/MajorUpdater.cs index 2120f6054..4c5702604 100644 --- a/RotationSolver/Updaters/MajorUpdater.cs +++ b/RotationSolver/Updaters/MajorUpdater.cs @@ -119,7 +119,7 @@ private static void ShowWarning() { if (id == 2) { - Util.OpenLink("http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EyT0BfZWCVq8v2yiMjSqcb4lEqYuaF_P&authKey=UJFoVZ3OljlBhSilXpeLKIIzofI4ZUjJfjuqCgr%2BiaT3Y6HmQFVbXZ5xBOlSv5yZ&noverify=0&group_code=552689154"); + Util.OpenLink("https://discord.gg/w2DPwRRZuT"); } }), new UIForegroundPayload(31), @@ -149,6 +149,7 @@ public static void Enable() { ActionSequencerUpdater.Enable(Svc.PluginInterface.ConfigDirectory.FullName + "\\Conditions"); SocialUpdater.Enable(); + RaidTimeUpdater.Enable(); Svc.Framework.Update += FrameworkUpdate; } @@ -283,5 +284,6 @@ public static void Dispose() ActionSequencerUpdater.SaveFiles(); SocialUpdater.Disable(); ActionUpdater.ClearNextAction(); + RaidTimeUpdater.Disable(); } } diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs new file mode 100644 index 000000000..c2ae69b49 --- /dev/null +++ b/RotationSolver/Updaters/RaidTimeUpdater.cs @@ -0,0 +1,83 @@ +using ECommons.DalamudServices; +using Newtonsoft.Json.Linq; +using System.Text.RegularExpressions; + +namespace RotationSolver.Updaters; +internal static partial class RaidTimeUpdater +{ + internal static void Enable() + { + Svc.ClientState.TerritoryChanged += ClientState_TerritoryChanged; + ClientState_TerritoryChanged(Svc.ClientState.TerritoryType); + } + + internal static void Disable() + { + Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; + } + + private static void ClientState_TerritoryChanged(ushort obj) + { + try + { + UpdateRaidTime("06-ew/raid/p9s.txt"); + } + catch (Exception e) + { + Svc.Log.Warning(e, "Failed to update the raid timeline!"); + } + } + static async void UpdateRaidTime(string path) + { + using var client = new HttpClient(); + var message = await client.GetAsync("https://raw.githubusercontent.com/OverlayPlugin/cactbot/main/ui/raidboss/data/" + path); + + if (!message.IsSuccessStatusCode) return; + + var str = await message.Content.ReadAsStringAsync(); + + var result = new List(); + foreach (var timelineItem in TimeLineItem().Matches(str).Cast()) + { + try + { + var header = TimeHeader().Match(timelineItem.Value); + var action = JObject.Parse(ActionGetter().Match(timelineItem.Value).Value)["id"]; + var time = float.Parse(Time().Match(header.Value).Value); + var name = Name().Match(header.Value).Value[1..^1]; + + string[] ids = []; + if (action is JArray array) + { + ids = [..array.Select(i => i.ToString())]; + } + else + { + ids = [action?.ToString() ?? string.Empty]; + } + + result.Add(new (time, name, ids)); + } + catch (Exception ex) + { + Svc.Log.Warning(ex, "sth wrong with matching!"); + } + } + DataCenter.TimelineItems = [..result]; + } + + [GeneratedRegex("\\d+\\.\\d.*Ability.*")] + private static partial Regex TimeLineItem(); + + [GeneratedRegex("^\\d+\\.\\d")] + private static partial Regex Time(); + + [GeneratedRegex("\".*?\"")] + private static partial Regex Name(); + + [GeneratedRegex("^\\d+\\.\\d \".*?\"")] + private static partial Regex TimeHeader(); + + [GeneratedRegex("{.*}")] + private static partial Regex ActionGetter(); +} diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs index f780adba3..a2a112c89 100644 --- a/RotationSolver/Updaters/SocialUpdater.cs +++ b/RotationSolver/Updaters/SocialUpdater.cs @@ -22,7 +22,6 @@ internal class SocialUpdater { #if DEBUG #else - private static readonly List _macroToAuthor = [ "blush", diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index 977aef99d..2d1359adb 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -62,8 +62,22 @@ public static void Enable() Svc.Log.Debug(effect.ToString()); #endif }); + + //Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage; } +// private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction) +// { +// if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return; +// OpCode op = (OpCode)opCode; + +//#if DEBUG +// var source = Svc.Objects.SearchById(sourceActorId)?.Name.TextValue; +// var target = Svc.Objects.SearchById(targetActorId)?.Name.TextValue; +// Svc.Log.Debug($"From {source} to {target} by {op}."); +//#endif +// } + public static void Disable() { #if DEBUG @@ -74,6 +88,7 @@ public static void Disable() MapEffect.Dispose(); ActionEffect.ActionEffectEvent -= ActionFromEnemy; ActionEffect.ActionEffectEvent -= ActionFromSelf; + //Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage; } private static IntPtr ActorVfxNewHandler(string path, IntPtr a2, IntPtr a3, float a4, char a5, ushort a6, char a7) @@ -153,6 +168,15 @@ private static void UpdateRTTDetour(dynamic obj) private static void ActionFromEnemy(ActionEffectSet set) { + foreach (var item in DataCenter.TimelineItems) + { + if (!item.IsIdMatched(set.Action?.RowId ?? 0)) continue; + + DataCenter.RaidTimeOffset = item.Time - DataCenter.CombatTimeRaw; + Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeOffset)} to {DataCenter.RaidTimeOffset} by the action {set.Action}"); + break; + } + //Check Source. var source = set.Source; if (source == null) return; @@ -174,7 +198,6 @@ private static void ActionFromEnemy(ActionEffectSet set) ShowStrEnemy = $"Damage Ratio: {damageRatio}\n{set}"; - foreach (var effect in set.TargetEffects) { if (effect.TargetID != Player.Object.ObjectId) continue; From de75e3ce3d51b921e7de3d3a71f4bc944f88f1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Wed, 28 Feb 2024 09:01:26 +0800 Subject: [PATCH 41/48] fix: add raise delay. --- RotationSolver.Basic/Actions/ActionTargetInfo.cs | 5 +---- RotationSolver.Basic/Configuration/Configs.cs | 10 ++++++++-- RotationSolver.Basic/Data/ObjectListDelay.cs | 1 - RotationSolver.Basic/DataCenter.cs | 4 +++- RotationSolver.Basic/Helpers/ObjectHelper.cs | 4 ++-- RotationSolver.Basic/Rotations/CustomRotation_GCD.cs | 2 +- RotationSolver/UI/RotationConfigWindow.cs | 6 +++--- RotationSolver/UI/SearchableConfigs/Searchable.cs | 2 +- RotationSolver/Updaters/RaidTimeUpdater.cs | 6 +++++- RotationSolver/Updaters/RotationUpdater.cs | 2 +- RotationSolver/Updaters/StateUpdater.cs | 4 ++-- RotationSolver/Updaters/TargetUpdater.cs | 12 +++++++----- 12 files changed, 34 insertions(+), 24 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index 115ea7bc0..1f6773373 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -53,8 +53,6 @@ private static bool NoAOE } #region Target Finder. - //The delay of finding the targets. - private readonly ObjectListDelay _canTargets = new (() => Service.Config.TargetDelay); private readonly IEnumerable GetCanTargets(bool skipStatusProvideCheck, TargetType type) { var items = TargetFilter.GetObjectInRadius(DataCenter.AllTargets, Range); @@ -68,8 +66,7 @@ private readonly IEnumerable GetCanTargets(bool skipStatusProvideCh objs.Add(obj); } - _canTargets.Delay(objs.Where(CanUseTo).Where(InViewTarget).Where(action.Setting.CanTarget)); - return _canTargets; + return objs.Where(CanUseTo).Where(InViewTarget).Where(action.Setting.CanTarget); } private readonly IEnumerable GetCanAffects(bool skipStatusProvideCheck, TargetType type) diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 5880fe9e0..75b736d05 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -110,6 +110,12 @@ public const string PvEFilter = JobFilterType.Raise, PvPFilter = JobFilterType.NoJob)] private static readonly bool _raiseBrinkOfDeath = true; + [UI("The random delay between for raising.", + Filter = AutoActionUsage, Section = 2, + PvEFilter = JobFilterType.Raise, PvPFilter = JobFilterType.NoJob)] + [Range(0, 10, ConfigUnitType.Seconds, 0.002f)] + public Vector2 RaiseDelay { get; set; } = new(1, 2); + [ConditionBool, UI("Add enemy list to the hostile targets.", Filter = TargetConfig)] private static readonly bool _addEnemyListToHostile = true; @@ -484,9 +490,9 @@ public const string [Range(0, 0.5f, ConfigUnitType.Seconds, 0.002f)] public float ActionAheadForLast0GCD { get; set; } = 0.06f; - [UI("This is the delay time.")] + [UI("The range of random delay for finding a target.", Filter =TargetConfig)] [Range(0, 3, ConfigUnitType.Seconds)] - public Vector2 TargetDelay { get; set; } = new(0, 0); + public Vector2 TargetDelay { get; set; } = new(1, 2); [UI("This is the clipping time.\nGCD is over. However, RS forgets to click the next action.", Filter = BasicTimer)] diff --git a/RotationSolver.Basic/Data/ObjectListDelay.cs b/RotationSolver.Basic/Data/ObjectListDelay.cs index 24dacb538..2428ec7c5 100644 --- a/RotationSolver.Basic/Data/ObjectListDelay.cs +++ b/RotationSolver.Basic/Data/ObjectListDelay.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Drawing; namespace RotationSolver.Basic.Data; diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index d5fba5536..983b300f7 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -95,6 +95,8 @@ internal static bool HasApplyStatus(uint id, StatusID[] ids) public static string TerritoryName => Territory?.PlaceName?.Value?.Name?.RawString ?? "Territory"; + public static bool IsPvP => Territory?.IsPvpZone ?? false; + public static ContentFinderCondition? ContentFinder => Territory?.ContentFinderCondition?.Value; public static string ContentFinderName => ContentFinder?.Name?.RawString ?? "Duty"; @@ -276,7 +278,7 @@ internal static SpecialCommandType SpecialType public static BattleChara? DeathTarget { get; internal set; } public static BattleChara? DispelTarget { get; internal set; } - public static ObjectListDelay AllTargets { get; } = new(() => (1, 3)); + public static ObjectListDelay AllTargets { get; } = new(() => Service.Config.TargetDelay); public static uint[] TreasureCharas { get; internal set; } = []; public static bool HasHostilesInRange => NumberOfHostilesInRange > 0; diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 84a5a2391..0e121e32f 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -122,7 +122,7 @@ internal static bool IsAttackable(this BattleChara battleChara) if (battleChara.IsTopPriorityHostile()) return true; - if (Service.CountDownTime > 0 || (DataCenter.Territory?.IsPvpZone ?? false)) return true; + if (Service.CountDownTime > 0 || DataCenter.IsPvP) return true; return DataCenter.RightNowTargetToHostileType switch { @@ -157,7 +157,7 @@ internal static unsafe bool IsEnemy(this GameObject obj) internal static unsafe bool IsAlliance(this GameObject obj) => obj != null && obj.ObjectId is not 0 and not GameObject.InvalidGameObjectId - && (!(DataCenter.Territory?.IsPvpZone ?? false) && obj is PlayerCharacter + && (!(DataCenter.IsPvP) && obj is PlayerCharacter || ActionManager.CanUseActionOnTarget((uint)ActionID.CurePvE, obj.Struct())); internal static bool IsParty(this GameObject gameObject) diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index 6bd24dea7..861639213 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -123,7 +123,7 @@ private bool UseLimitBreak(out IAction? act) return LimitBreakLevel switch { - 1 => ((DataCenter.Territory?.IsPvpZone ?? false) + 1 => ((DataCenter.IsPvP) ? LimitBreakPvP?.CanUse(out act, skipAoeCheck: true) : LimitBreak1?.CanUse(out act, skipAoeCheck: true)) ?? false, 2 => LimitBreak2?.CanUse(out act, skipAoeCheck: true) ?? false, diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index 762466f35..ab3a1bd10 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -406,7 +406,7 @@ private void DrawHeader(float wholeWidth) if (rot == null) return; - if (DataCenter.Territory?.IsPvpZone ?? false) + if (DataCenter.IsPvP) { rotations = rotations.Where(r => r.GetCustomAttribute()?.Type.HasFlag(CombatType.PvP) ?? false).ToArray(); } @@ -532,7 +532,7 @@ private static void DrawRotationCombo(float comboSize, Type[] rotations, ICustom ? ImGuiColors.DalamudWhite : ImGuiColors.DalamudOrange); if (ImGui.Selectable(rAttr.Name)) { - if( DataCenter.Territory?.IsPvpZone ?? false) + if( DataCenter.IsPvP) { Service.Config.PvPRotationChoice = r.GetType().FullName; } @@ -1178,7 +1178,7 @@ private static void DrawRotationConfiguration() foreach (var config in set.Configs) { - if (DataCenter.Territory?.IsPvpZone ?? false) + if (DataCenter.IsPvP) { if (!config.Type.HasFlag(CombatType.PvP)) continue; } diff --git a/RotationSolver/UI/SearchableConfigs/Searchable.cs b/RotationSolver/UI/SearchableConfigs/Searchable.cs index b8836c060..e82a73ebd 100644 --- a/RotationSolver/UI/SearchableConfigs/Searchable.cs +++ b/RotationSolver/UI/SearchableConfigs/Searchable.cs @@ -183,7 +183,7 @@ public virtual string Command public unsafe void Draw() { - var filter = (DataCenter.Territory?.IsPvpZone ?? false) ? PvPFilter : PvEFilter; + var filter = (DataCenter.IsPvP) ? PvPFilter : PvEFilter; if (!filter.CanDraw) { diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs index c2ae69b49..d698c77d7 100644 --- a/RotationSolver/Updaters/RaidTimeUpdater.cs +++ b/RotationSolver/Updaters/RaidTimeUpdater.cs @@ -32,7 +32,11 @@ static async void UpdateRaidTime(string path) using var client = new HttpClient(); var message = await client.GetAsync("https://raw.githubusercontent.com/OverlayPlugin/cactbot/main/ui/raidboss/data/" + path); - if (!message.IsSuccessStatusCode) return; + if (!message.IsSuccessStatusCode) + { + DataCenter.TimelineItems = []; + return; + } var str = await message.Content.ReadAsStringAsync(); diff --git a/RotationSolver/Updaters/RotationUpdater.cs b/RotationSolver/Updaters/RotationUpdater.cs index 397c0d85b..5b04457e8 100644 --- a/RotationSolver/Updaters/RotationUpdater.cs +++ b/RotationSolver/Updaters/RotationUpdater.cs @@ -535,7 +535,7 @@ private static void UpdateCustomRotation() static Type? GetChosenRotation(CustomRotationGroup group) { - var isPvP = DataCenter.Territory?.IsPvpZone ?? false; + var isPvP = DataCenter.IsPvP; var rotations = group.Rotations .Where(r => diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs index fc6e7e1ad..f3eea3fa0 100644 --- a/RotationSolver/Updaters/StateUpdater.cs +++ b/RotationSolver/Updaters/StateUpdater.cs @@ -6,7 +6,7 @@ internal static class StateUpdater { private static bool CanUseHealAction => //PvP - (DataCenter.Territory?.IsPvpZone ?? false) + (DataCenter.IsPvP) //Job || (DataCenter.Role == JobRole.Healer || Service.Config.UseHealWhenNotAHealer) && Service.Config.AutoHeal @@ -164,7 +164,7 @@ private static AutoStatus StatusFromAutomatic() status |= AutoStatus.Dispel; } else if (!DataCenter.HasHostilesInRange || Service.Config.DispelAll - || (DataCenter.Territory?.IsPvpZone ?? false)) + || (DataCenter.IsPvP)) { status |= AutoStatus.Dispel; } diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs index 4f4897764..873cbf036 100644 --- a/RotationSolver/Updaters/TargetUpdater.cs +++ b/RotationSolver/Updaters/TargetUpdater.cs @@ -11,6 +11,9 @@ namespace RotationSolver.Updaters; internal static partial class TargetUpdater { + static readonly ObjectListDelay + _raisePartyTargets = new(() => Service.Config.RaiseDelay), + _raiseAllTargets = new(() => Service.Config.RaiseDelay); internal unsafe static void UpdateTarget() { DataCenter.AllTargets.Delay(Svc.Objects.GetObjectInRadius(30).OfType()); @@ -47,7 +50,6 @@ internal static void ClearTarget() = empty; DataCenter.InterruptTarget = DataCenter.ProvokeTarget = null; - DataCenter.AllTargets.Delay(empty); } @@ -210,9 +212,9 @@ private unsafe static void UpdateFriends(IEnumerable allTargets) var mayPet = allTargets.OfType().Where(npc => npc.OwnerId == Player.Object.ObjectId); DataCenter.HasPet = mayPet.Any(npc => npc.BattleNpcKind == BattleNpcSubKind.Pet); - var deathAll = DataCenter.AllianceMembers.GetDeath(); - var deathParty = DataCenter.PartyMembers.GetDeath(); - DataCenter.DeathTarget = GetDeathTarget(deathAll, deathParty); + _raiseAllTargets.Delay(DataCenter.AllianceMembers.GetDeath()); + _raisePartyTargets.Delay(DataCenter.PartyMembers.GetDeath()); + DataCenter.DeathTarget = GetDeathTarget(_raiseAllTargets, _raisePartyTargets); var weakenPeople = DataCenter.PartyMembers.Where(o => o is BattleChara b && b.StatusList.Any(StatusHelper.CanDispel)); var dyingPeople = weakenPeople.Where(o => o is BattleChara b && b.StatusList.Any(StatusHelper.IsDangerous)); @@ -326,6 +328,6 @@ private static void UpdateNamePlate(IEnumerable allTargets) if (b == null || b.CurrentHp == 0) continue; charas.Add(b.ObjectId); } - DataCenter.TreasureCharas = charas.ToArray(); + DataCenter.TreasureCharas = [.. charas]; } } From d13d8469f3cceefba131650e4607eacf6d7f0331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Wed, 28 Feb 2024 11:13:52 +0800 Subject: [PATCH 42/48] fix: add more thing to sync the raid time. --- .../Actions/ActionCooldownInfo.cs | 13 +++- RotationSolver.Basic/Data/TimelineItem.cs | 75 +++++++++++++++++-- RotationSolver.Basic/DataCenter.cs | 18 ++++- RotationSolver.Basic/Helpers/ObjectHelper.cs | 40 +++++++--- .../StaticCodeGenerator.cs | 42 +++++------ RotationSolver/Commands/RSCommands_Actions.cs | 6 +- RotationSolver/Helpers/DownloadHelper.cs | 3 +- RotationSolver/UI/RotationConfigWindow.cs | 6 +- .../UI/RotationConfigWindow_Config.cs | 8 +- RotationSolver/Updaters/ActionUpdater.cs | 13 +++- RotationSolver/Updaters/RaidTimeUpdater.cs | 54 ++++++++----- RotationSolver/Updaters/RotationUpdater.cs | 9 +-- RotationSolver/Updaters/SocialUpdater.cs | 29 +------ RotationSolver/Updaters/StateUpdater.cs | 2 +- RotationSolver/Updaters/TargetUpdater.cs | 38 ++++++++-- RotationSolver/Watcher.cs | 63 +++++++++++++--- 16 files changed, 304 insertions(+), 115 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs index ddcdb4a0a..807750ad8 100644 --- a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs +++ b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs @@ -58,7 +58,7 @@ namespace RotationSolver.Basic.Actions; /// /// /// - public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw + DataCenter.WeaponRemain; + public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw - DataCenter.WeaponRemain; float RecastTimeRemainOneChargeRaw => RecastTimeRemain % RecastTimeOneChargeRaw; @@ -130,6 +130,17 @@ public bool WillHaveOneChargeGCD(uint gcdCount = 0, float offset = 0) public bool WillHaveOneCharge(float remain) => HasOneCharge || RecastTimeRemainOneCharge <= remain; + /// + /// Is this action used after several time. + /// + /// + /// + public bool JustUsedAfter(float time) + { + var elapsed = RecastTimeElapsedRaw % RecastTimeOneChargeRaw; + return elapsed + DataCenter.WeaponRemain < time; + } + internal bool CooldownCheck(bool isEmpty, bool onLastAbility, bool ignoreClippingCheck, byte gcdCountForAbility) { if (!_action.Info.IsGeneralGCD) diff --git a/RotationSolver.Basic/Data/TimelineItem.cs b/RotationSolver.Basic/Data/TimelineItem.cs index 8b1594e8f..0de27c62e 100644 --- a/RotationSolver.Basic/Data/TimelineItem.cs +++ b/RotationSolver.Basic/Data/TimelineItem.cs @@ -1,11 +1,75 @@ -using System.Text.RegularExpressions; +using ECommons.DalamudServices; +using Newtonsoft.Json.Linq; +using System.Text.RegularExpressions; namespace RotationSolver.Basic.Data; -internal readonly struct TimelineItem(float time, string name, params string[] ids) +internal enum TimelineType : byte { + Unknown, + InCombat, + GameLog, + Ability, + StartsUsing, + SystemLogMessage, + ActorControl, +} + +internal readonly struct TimelineItem(float time, string name, TimelineType type, string[] ids, JObject? obj) +{ + public TimelineType Type => type; + public float Time => time; - public bool IsShown => !name.StartsWith("--") && !name.EndsWith("--"); + public JObject? Object => obj; + + public string Name => name; + + public bool IsShown => name is not "--Reset--" and not "--sync--"; + + public string this[string propertyName] => Object?[propertyName]?.ToString() ?? string.Empty; + + public TimelineItem(float time, string name, string type, string[] ids, JObject? obj) + : this(time, name, GetTypeFromName(type), ids, obj) + { + + } + + private static TimelineType GetTypeFromName(string type) + { + switch(type) + { + case "": + return TimelineType.Unknown; + + case "ActorControl": + return TimelineType.ActorControl; //TODO: how to check that command. + + case "InCombat": + return TimelineType.InCombat; + + case "GameLog": + return TimelineType.GameLog; + + case "Ability": + return TimelineType.Ability; + + case "StartsUsing": + return TimelineType.StartsUsing; + + case "SystemLogMessage": + return TimelineType.SystemLogMessage; //TODO: a6s for testing + + default: + Svc.Log.Warning($"New timelinetype: {type}"); + return TimelineType.Unknown; + } + } + + public void UpdateRaidTimeOffset() + { + DataCenter.RaidTimeRaw = Time; + Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeRaw)} to {DataCenter.RaidTimeRaw}."); + } public bool IsIdMatched(uint id) { @@ -16,8 +80,9 @@ public override string ToString() { return $""" IsShown: {IsShown}, - Time: {time}, - Name: {name}, + Time: {Time}, + Name: {Name}, + Type: {Type}, Ids: {string.Join(", ", ids)} """; } diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 983b300f7..2ff63fd95 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -263,8 +263,19 @@ internal static SpecialCommandType SpecialType public static bool NotInCombatDelay => _notInCombatDelay.Delay(!InCombat); internal static float CombatTimeRaw { get; set; } - internal static float RaidTimeOffset { get; set; } = 0; - internal static float RaidTimeRaw => CombatTimeRaw + RaidTimeOffset; + private static DateTime _startRaidTime = DateTime.MinValue; + internal static float RaidTimeRaw + { + get + { + if (_startRaidTime == DateTime.MinValue) return 0; + return (float)(DateTime.Now - _startRaidTime).TotalSeconds; + } + set + { + _startRaidTime = DateTime.Now - TimeSpan.FromSeconds(value); + } + } internal static TimelineItem[] TimelineItems { get; set; } = []; @@ -423,4 +434,7 @@ internal static void AddDamageRec(float damageRatio) internal static DateTime KnockbackFinished { get; set; } = DateTime.MinValue; internal static DateTime KnockbackStart { get; set; } = DateTime.MinValue; #endregion + + internal static SortedList AuthorHashes { get; set; } = []; + internal static string[] ContributorsHash { get; set; } = []; } diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 0e121e32f..b30468bd6 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -9,6 +9,8 @@ using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Configuration; +using System.Security.Cryptography; +using System.Text; using System.Text.RegularExpressions; namespace RotationSolver.Basic.Helpers; @@ -87,17 +89,16 @@ internal static bool IsAttackable(this BattleChara battleChara) if (names.Any(n => !string.IsNullOrEmpty(n) && new Regex(n).Match(battleChara.Name.TextValue).Success)) return false; - //if (gameObject is PlayerCharacter p) - //{ - // var hash = SocialUpdater.EncryptString(p); - - // //Don't attack authors!! - // if (RotationUpdater.AuthorHashes.ContainsKey(hash)) return false; + if (battleChara is PlayerCharacter p) + { + var hash = EncryptString(p); - // //Don't attack contributors!! - // if (DownloadHelper.ContributorsHash.Contains(hash)) return false; - //} + //Don't attack authors!! + if (DataCenter.AuthorHashes.ContainsKey(hash)) return false; + //Don't attack contributors!! + if (DataCenter.ContributorsHash.Contains(hash)) return false; + } //Fate if (DataCenter.TerritoryContentType != TerritoryContentType.Eureka) @@ -132,6 +133,27 @@ internal static bool IsAttackable(this BattleChara battleChara) }; } + + internal static string EncryptString(this PlayerCharacter player) + { + if (player == null) return string.Empty; + + try + { + byte[] inputByteArray = Encoding.UTF8.GetBytes(player.HomeWorld.GameData!.InternalName.ToString() + + " - " + player.Name.ToString() + "U6Wy.zCG"); + + var tmpHash = MD5.HashData(inputByteArray); + var retB = Convert.ToBase64String(tmpHash); + return retB; + } + catch (Exception ex) + { + Svc.Log.Warning(ex, "Failed to read the player's name and world."); + return string.Empty; + } + } + internal static unsafe bool IsInEnemiesList(this BattleChara battleChara) { var addons = Service.GetAddons(); diff --git a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs index f1a261659..a95a604b9 100644 --- a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs +++ b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs @@ -25,29 +25,29 @@ private static void Execute(SourceProductionContext context) GenerateActionCate(context); GenerateBaseRotation(context); GenerateRotations(context); - //GenerateOpCode(context); + GenerateOpCode(context); } - //private static void GenerateOpCode(SourceProductionContext context) - //{ - // var code = $$""" - // namespace RotationSolver.Basic.Data; - - // /// - // /// The opcode - // /// - // public enum OpCode : ushort - // { - // /// - // /// - // /// - // None = 0, - // {{Properties.Resources.OpCode.Table()}} - // } - // """; - - // context.AddSource("OpCode.g.cs", code); - //} + private static void GenerateOpCode(SourceProductionContext context) + { + var code = $$""" + namespace RotationSolver.Basic.Data; + + /// + /// The opcode + /// + public enum OpCode : ushort + { + /// + /// + /// + None = 0, + {{Properties.Resources.OpCode.Table()}} + } + """; + + context.AddSource("OpCode.g.cs", code); + } private static void GenerateStatus(SourceProductionContext context) { diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index e901d3b01..f99ed22ec 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -55,11 +55,11 @@ public static void DoAction() && act1.TargetInfo.IsSingleTarget && act1.Target?.Target is PlayerCharacter p && p != Player.Object) { - var hash = SocialUpdater.EncryptString(p); + var hash = p.EncryptString(); //Don't attack authors and contributors!! - if ((RotationUpdater.AuthorHashes.ContainsKey(hash) - || DownloadHelper.ContributorsHash.Contains(hash))) + if ((DataCenter.AuthorHashes.ContainsKey(hash) + || DataCenter.ContributorsHash.Contains(hash))) { Svc.Chat.PrintError($"Please don't attack RS developers with RS by {act1}!"); return; diff --git a/RotationSolver/Helpers/DownloadHelper.cs b/RotationSolver/Helpers/DownloadHelper.cs index 87f2b5c31..6a7dd1d63 100644 --- a/RotationSolver/Helpers/DownloadHelper.cs +++ b/RotationSolver/Helpers/DownloadHelper.cs @@ -10,7 +10,6 @@ public static class DownloadHelper /// Maximum HP and MP are increased. /// public static string[] LinkLibraries { get; private set; } = []; - public static string[] ContributorsHash { get; private set; } = []; public static string[] UsersHash { get; private set; } = []; public static string[] Supporters { get; private set; } = []; public static IncompatiblePlugin[] IncompatiblePlugins { get; private set; } = []; @@ -20,7 +19,7 @@ public static async Task DownloadAsync() LinkLibraries = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/downloadList.json") ?? []; IncompatiblePlugins = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/IncompatiblePlugins.json") ?? []; - ContributorsHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/ContributorsHash.json") ?? []; + DataCenter.ContributorsHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/ContributorsHash.json") ?? []; UsersHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/UsersHash.json") ?? []; diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index ab3a1bd10..ff5d634eb 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2432,8 +2432,8 @@ private static unsafe void DrawStatus() } private static unsafe void DrawParty() { - ImGui.Text("Party: " + DataCenter.PartyMembers.Count().ToString()); - ImGui.Text("Alliance: " + DataCenter.AllianceMembers.Count().ToString()); + ImGui.Text("Party: " + DataCenter.PartyMembers.Length.ToString()); + ImGui.Text("Alliance: " + DataCenter.AllianceMembers.Length.ToString()); ImGui.Text("PartyMembersAverHP: " + DataCenter.PartyMembersAverHP.ToString()); @@ -2495,7 +2495,7 @@ private static unsafe void DrawTargetData() } ImGui.Text("All: " + DataCenter.AllTargets.Count().ToString()); - ImGui.Text("Hostile: " + DataCenter.AllHostileTargets.Count().ToString()); + ImGui.Text("Hostile: " + DataCenter.AllHostileTargets.Length.ToString()); foreach (var item in DataCenter.AllHostileTargets) { ImGui.Text(item.Name.ToString()); diff --git a/RotationSolver/UI/RotationConfigWindow_Config.cs b/RotationSolver/UI/RotationConfigWindow_Config.cs index 8d3a4bbc4..109a6673b 100644 --- a/RotationSolver/UI/RotationConfigWindow_Config.cs +++ b/RotationSolver/UI/RotationConfigWindow_Config.cs @@ -220,7 +220,7 @@ private static void DrawBasicNamedConditions() { if (!DataCenter.RightSet.NamedConditions.Any(c => string.IsNullOrEmpty(c.Name))) { - DataCenter.RightSet.NamedConditions = DataCenter.RightSet.NamedConditions.Append((string.Empty, new ConditionSet())).ToArray(); + DataCenter.RightSet.NamedConditions = [.. DataCenter.RightSet.NamedConditions, (string.Empty, new ConditionSet())]; } ImGui.Spacing(); @@ -271,13 +271,13 @@ private static void DrawBasicOthers() if (Service.Config.SayHelloToAll) { - var str = SocialUpdater.EncryptString(Player.Object); + var str = Player.Object.EncryptString(); ImGui.SetNextItemWidth(ImGui.CalcTextSize(str).X + 10); ImGui.InputText("That is your HASH:", ref str, 100); - if (!DownloadHelper.ContributorsHash.Contains(str) + if (!DataCenter.ContributorsHash.Contains(str) && !DownloadHelper.UsersHash.Contains(str) - && !RotationUpdater.AuthorHashes.ContainsKey(str)) + && !DataCenter.AuthorHashes.ContainsKey(str)) { if (ImGui.Button("DM your Hash to ArchiTed for being greeted.")) { diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs index d1cf5cc6f..a32e4f8e2 100644 --- a/RotationSolver/Updaters/ActionUpdater.cs +++ b/RotationSolver/Updaters/ActionUpdater.cs @@ -5,6 +5,8 @@ using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using RotationSolver.Commands; +using System.Text.RegularExpressions; +using System.Windows.Forms; namespace RotationSolver.Updaters; @@ -143,10 +145,19 @@ private static void UpdateCombatTime() if (_startCombatTime == DateTime.MinValue) { DataCenter.CombatTimeRaw = 0; - DataCenter.RaidTimeOffset = 0; } else { + if (DataCenter.CombatTimeRaw == 0) + { + foreach (var item in DataCenter.TimelineItems) + { + if (item.Type is not TimelineType.InCombat) continue; + + item.UpdateRaidTimeOffset(); + break; + } + } DataCenter.CombatTimeRaw = (float)(DateTime.Now - _startCombatTime).TotalSeconds; } } diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs index d698c77d7..bc5b18388 100644 --- a/RotationSolver/Updaters/RaidTimeUpdater.cs +++ b/RotationSolver/Updaters/RaidTimeUpdater.cs @@ -16,26 +16,25 @@ internal static void Disable() Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; } - private static void ClientState_TerritoryChanged(ushort obj) + private static async void ClientState_TerritoryChanged(ushort obj) { try { - UpdateRaidTime("06-ew/raid/p9s.txt"); + DataCenter.TimelineItems = await UpdateRaidTime("06-ew/raid/p9s.txt"); } catch (Exception e) { Svc.Log.Warning(e, "Failed to update the raid timeline!"); } } - static async void UpdateRaidTime(string path) + static async Task UpdateRaidTime(string path) { using var client = new HttpClient(); var message = await client.GetAsync("https://raw.githubusercontent.com/OverlayPlugin/cactbot/main/ui/raidboss/data/" + path); if (!message.IsSuccessStatusCode) { - DataCenter.TimelineItems = []; - return; + return []; } var str = await message.Content.ReadAsStringAsync(); @@ -45,32 +44,51 @@ static async void UpdateRaidTime(string path) { try { - var header = TimeHeader().Match(timelineItem.Value); - var action = JObject.Parse(ActionGetter().Match(timelineItem.Value).Value)["id"]; - var time = float.Parse(Time().Match(header.Value).Value); - var name = Name().Match(header.Value).Value[1..^1]; + var timeline = timelineItem.Value; + var header = TimeHeader().Match(timeline).Value; + var time = float.Parse(Time().Match(header).Value); + var name = Name().Match(header).Value[1..^1]; + + var item = JObject.Parse(ActionGetter().Match(timeline).Value); string[] ids = []; - if (action is JArray array) - { - ids = [..array.Select(i => i.ToString())]; - } - else + if (item != null) { - ids = [action?.ToString() ?? string.Empty]; + var id = item["id"]; + if (id is JArray array) + { + ids = [.. array.Select(i => i.ToString())]; + } + else + { + ids = [id?.ToString() ?? string.Empty]; + } } - result.Add(new (time, name, ids)); + var rest = timeline[header.Length..]; + var type = Type().Match(rest)?.Value[1..^1] ?? string.Empty; + + result.Add(new (time, name, type, ids, item)); } catch (Exception ex) { Svc.Log.Warning(ex, "sth wrong with matching!"); } } - DataCenter.TimelineItems = [..result]; + +#if DEBUG + foreach (var item in result) + { + Svc.Log.Debug(item.ToString()); + } +#endif + return [..result]; } - [GeneratedRegex("\\d+\\.\\d.*Ability.*")] + [GeneratedRegex(" .*? ")] + private static partial Regex Type(); + + [GeneratedRegex("\\d+\\.\\d.*")] private static partial Regex TimeLineItem(); [GeneratedRegex("^\\d+\\.\\d")] diff --git a/RotationSolver/Updaters/RotationUpdater.cs b/RotationSolver/Updaters/RotationUpdater.cs index 5b04457e8..60a10856f 100644 --- a/RotationSolver/Updaters/RotationUpdater.cs +++ b/RotationSolver/Updaters/RotationUpdater.cs @@ -14,7 +14,6 @@ internal static class RotationUpdater internal record CustomRotationGroup(Job JobId, Job[] ClassJobIds, Type[] Rotations); internal static SortedList CustomRotationsDict { get; private set; } = []; - internal static SortedList AuthorHashes { get; private set; } = []; internal static CustomRotationGroup[] CustomRotations { get; set; } = []; internal static SortedList DutyRotations { get; set; } = []; @@ -99,7 +98,7 @@ private static void LoadRotationsFromLocal(string relayFolder) } } - AuthorHashes = []; + DataCenter.AuthorHashes = []; foreach (var assembly in assemblies) { try @@ -112,13 +111,13 @@ private static void LoadRotationsFromLocal(string relayFolder) var value = $"{assembly.GetInfo().Author} - {assembly.GetInfo().Name}"; - if (AuthorHashes.ContainsKey(key)) + if (DataCenter.AuthorHashes.ContainsKey(key)) { - AuthorHashes[key] += $", {value}"; + DataCenter.AuthorHashes[key] += $", {value}"; } else { - AuthorHashes.Add(key, value); + DataCenter.AuthorHashes.Add(key, value); } } } diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs index a2a112c89..e724f9706 100644 --- a/RotationSolver/Updaters/SocialUpdater.cs +++ b/RotationSolver/Updaters/SocialUpdater.cs @@ -13,8 +13,6 @@ using RotationSolver.Helpers; using RotationSolver.Localization; using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Text; namespace RotationSolver.Updaters; @@ -168,21 +166,21 @@ private static async void SayHelloToUsers() #else .Where(c => c.ObjectId != Player.Object.ObjectId) #endif - .Select(player => (player, EncryptString(player))) + .Select(player => (player, player.EncryptString())) .Where(pair => !saidAuthors.Contains(pair.Item2) && !OtherConfiguration.RotationSolverRecord.SaidUsers.Contains(pair.Item2)); IEnumerable entities = players .Select(c => { - if (!RotationUpdater.AuthorHashes.TryGetValue(c.Item2, out var nameDesc)) nameDesc = string.Empty; + if (!DataCenter.AuthorHashes.TryGetValue(c.Item2, out var nameDesc)) nameDesc = string.Empty; return (c.player, nameDesc); }) .Where(p => !string.IsNullOrEmpty(p.nameDesc)) .Select(p => new RotationAuthorChatEntity(p.player, p.nameDesc)); entities = entities.Union(players - .Where(p => DownloadHelper.ContributorsHash.Contains(p.Item2)) + .Where(p => DataCenter.ContributorsHash.Contains(p.Item2)) .Select(p => new ContributorChatEntity(p.player)), _comparer); if (Service.Config.SayHelloToUsers) @@ -218,25 +216,6 @@ private static async void SayHelloToUsers() } } - internal static string EncryptString(PlayerCharacter player) - { - if (player == null) return string.Empty; - - try - { - byte[] inputByteArray = Encoding.UTF8.GetBytes(player.HomeWorld.GameData!.InternalName.ToString() - + " - " + player.Name.ToString() + "U6Wy.zCG"); - - var tmpHash = MD5.HashData(inputByteArray); - var retB = Convert.ToBase64String(tmpHash); - return retB; - } - catch (Exception ex) - { - Svc.Log.Warning(ex, "Failed to read the player's name and world."); - return string.Empty; - } - } internal abstract class ChatEntity(PlayerCharacter character) : IDisposable { @@ -276,7 +255,7 @@ public bool CanTarget public void Dispose() { OtherConfiguration.RotationSolverRecord.SayingHelloCount++; - var hash = EncryptString(player); + var hash = player.EncryptString(); saidAuthors.Add(hash); if (Service.Config.JustSayHelloOnce) { diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs index f3eea3fa0..81c6cb9d6 100644 --- a/RotationSolver/Updaters/StateUpdater.cs +++ b/RotationSolver/Updaters/StateUpdater.cs @@ -69,7 +69,7 @@ private static AutoStatus StatusFromAutomatic() var canHealAreaAbility = singleAbility > 2; var canHealAreaSpell = singleSpell > 2; - if (DataCenter.PartyMembers.Count() > 2) + if (DataCenter.PartyMembers.Length > 2) { //TODO: Beneficial area status. var ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots); diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs index 873cbf036..314d90d4b 100644 --- a/RotationSolver/Updaters/TargetUpdater.cs +++ b/RotationSolver/Updaters/TargetUpdater.cs @@ -16,13 +16,41 @@ static readonly ObjectListDelay _raiseAllTargets = new(() => Service.Config.RaiseDelay); internal unsafe static void UpdateTarget() { - DataCenter.AllTargets.Delay(Svc.Objects.GetObjectInRadius(30).OfType()); + var battles = Svc.Objects.OfType(); + + DataCenter.AllTargets.Delay(battles.GetObjectInRadius(30)); UpdateHostileTargets(DataCenter.AllTargets); UpdateFriends(DataCenter.AllTargets .Where(b => b.Character()->CharacterData.OnlineStatus != 15 //Removed the one watching cutscene. && b.IsTargetable //Removed the one can't target. )); - UpdateNamePlate(Svc.Objects.OfType()); + UpdateNamePlate(battles); + } + + static readonly Dictionary _castingTargets = []; + private static void UpdateCastingRefine(IEnumerable allTargets) + { + foreach (BattleChara b in allTargets) + { + if (!_castingTargets.TryGetValue(b.ObjectId, out var isLastCasting)) continue; + if (isLastCasting) continue; + + if (!b.IsCasting) continue; + + foreach (var item in DataCenter.TimelineItems) + { + if (item.Type is not TimelineType.StartsUsing) continue; + if (!item.IsIdMatched(b.CastActionId)) continue; + + item.UpdateRaidTimeOffset(); + break; + } + } + + foreach (BattleChara b in allTargets) + { + _castingTargets[b.ObjectId] = b.IsCasting; + } } private static DateTime _lastUpdateTimeToKill = DateTime.MinValue; @@ -96,13 +124,13 @@ private unsafe static void UpdateHostileTargets(IEnumerable allTarg if (b is PlayerCharacter p) { - var hash = SocialUpdater.EncryptString(p); + var hash = p.EncryptString(); //Don't attack authors!! - if (RotationUpdater.AuthorHashes.ContainsKey(hash)) return false; + if (DataCenter.AuthorHashes.ContainsKey(hash)) return false; //Don't attack contributors!! - if (DownloadHelper.ContributorsHash.Contains(hash)) return false; + if (DataCenter.ContributorsHash.Contains(hash)) return false; } return true; }).ToArray(); diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index 2d1359adb..4ec1086d1 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Game; using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Configuration; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -63,20 +64,61 @@ public static void Enable() #endif }); - //Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage; + Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage; + Svc.Chat.ChatMessage += Chat_ChatMessage; } -// private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction) -// { -// if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return; -// OpCode op = (OpCode)opCode; + private static void Chat_ChatMessage(Dalamud.Game.Text.XivChatType type, uint senderId, ref Dalamud.Game.Text.SeStringHandling.SeString sender, ref Dalamud.Game.Text.SeStringHandling.SeString message, ref bool isHandled) + { + foreach (var item in DataCenter.TimelineItems) + { + if (item.Type is not TimelineType.GameLog) continue; + + var typeString = ((uint)type).ToString("X4"); + if (!new Regex(item["code"]).IsMatch(typeString)) continue; + + //TODO: multi language. + if (!new Regex(item["line"]).IsMatch(message.TextValue)) continue; + item.UpdateRaidTimeOffset(); + break; + } + } + + private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction) + { + if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return; + OpCode op = (OpCode)opCode; + + switch (op) + { + case OpCode.SystemLogMessage: + OnSystemLogMessage(dataPtr); + break; + } //#if DEBUG // var source = Svc.Objects.SearchById(sourceActorId)?.Name.TextValue; // var target = Svc.Objects.SearchById(targetActorId)?.Name.TextValue; // Svc.Log.Debug($"From {source} to {target} by {op}."); //#endif -// } + } + + private static void OnSystemLogMessage(IntPtr dataPtr) + { + var bytes = new byte[32]; + Marshal.Copy(dataPtr, bytes, 0, 32); + Svc.Log.Debug("SystemLogMessage: " + Convert.ToHexString(bytes)); + + //TODO: the refine to the raidtime. + + //if (logId == DataIds.SystemLogPomanderUsed) + // OnPomanderUsed((Pomander)Marshal.ReadByte(dataPtr, 16)); + //else if (logId == DataIds.SystemLogDutyEnded) + // ExitDeepDungeon(); + //else if (logId == DataIds.SystemLogTransferenceInitiated) + // nextFloorTransfer = true; + + } public static void Disable() { @@ -88,7 +130,8 @@ public static void Disable() MapEffect.Dispose(); ActionEffect.ActionEffectEvent -= ActionFromEnemy; ActionEffect.ActionEffectEvent -= ActionFromSelf; - //Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage; + Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage; + Svc.Chat.ChatMessage -= Chat_ChatMessage; } private static IntPtr ActorVfxNewHandler(string path, IntPtr a2, IntPtr a3, float a4, char a5, ushort a6, char a7) @@ -170,10 +213,10 @@ private static void ActionFromEnemy(ActionEffectSet set) { foreach (var item in DataCenter.TimelineItems) { + if (item.Type is not TimelineType.Ability) continue; if (!item.IsIdMatched(set.Action?.RowId ?? 0)) continue; - DataCenter.RaidTimeOffset = item.Time - DataCenter.CombatTimeRaw; - Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeOffset)} to {DataCenter.RaidTimeOffset} by the action {set.Action}"); + item.UpdateRaidTimeOffset(); break; } @@ -213,7 +256,7 @@ private static void ActionFromEnemy(ActionEffectSet set) } } - if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action.Cast100ms > 0) + if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action?.Cast100ms > 0) { var type = set.Action.GetActionCate(); From 635cf32d8aad80d03db80b001770fbfb23c7952f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Wed, 28 Feb 2024 11:35:36 +0800 Subject: [PATCH 43/48] feat: add a ttu for all actions. --- RotationSolver.Basic/Actions/ActionConfig.cs | 5 ++++ RotationSolver.Basic/Actions/BaseAction.cs | 1 + RotationSolver.Basic/DataCenter.cs | 25 +++++++++++++++++++- RotationSolver/Data/UiString.cs | 3 +++ RotationSolver/UI/RotationConfigWindow.cs | 9 +++++++ RotationSolver/Watcher.cs | 14 ++++++++++- 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionConfig.cs b/RotationSolver.Basic/Actions/ActionConfig.cs index 4ea840efc..92ac1db27 100644 --- a/RotationSolver.Basic/Actions/ActionConfig.cs +++ b/RotationSolver.Basic/Actions/ActionConfig.cs @@ -36,6 +36,11 @@ public bool IsEnabled /// public float TimeToKill { get; set; } = 0; + /// + /// How many ttu should this action use. + /// + public float TimeToUntargetable { get; set; } = 0; + /// /// The heal ratio for the auto heal. /// diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index b9372bd96..ec5d67109 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -136,6 +136,7 @@ public bool CanUse(out IAction act, bool skipStatusProvideCheck = false, bool sk if (Setting.IsMeleeRange && IActionHelper.IsLastAction(IActionHelper.MovingActions)) return false; //No range actions after moving. if (DataCenter.AverageTimeToKill < Config.TimeToKill) return false; + if (DataCenter.TimeToUntargetable < Config.TimeToUntargetable) return false; PreviewTarget = TargetInfo.FindTarget(skipAoeCheck, skipStatusProvideCheck); if (PreviewTarget == null) return false; diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 2ff63fd95..c0e630e3c 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -299,7 +299,30 @@ internal static float RaidTimeRaw public static int NumberOfAllHostilesInRange { get; internal set; } public static int NumberOfAllHostilesInMaxRange { get; internal set; } public static bool MobsTime { get; internal set; } - public static float AverageTimeToKill { get; internal set; } + + private static float _averageTimeToKill; + public static float AverageTimeToKill + { + get => _averageTimeToKill; + internal set + { + _averageTimeToKill = value; + + foreach (var item in TimelineItems) + { + if (item.Time < RaidTimeRaw) continue; + if (item.Name is not "--untargetable--") continue; + + var time = item.Time - RaidTimeRaw; + TimeToUntargetable = MathF.Min(time, _averageTimeToKill); + return; + } + + TimeToUntargetable = _averageTimeToKill; + } + } + + public static float TimeToUntargetable { get; private set; } public static bool IsHostileCastingAOE { get; internal set; } diff --git a/RotationSolver/Data/UiString.cs b/RotationSolver/Data/UiString.cs index f09d16187..d6534c457 100644 --- a/RotationSolver/Data/UiString.cs +++ b/RotationSolver/Data/UiString.cs @@ -120,6 +120,9 @@ internal enum UiString [Description("TTK that this action needs the target be.")] ConfigWindow_Actions_TTK, + [Description("Time to untargetable that this action needs the timeline be.")] + ConfigWindow_Actions_TTU, + [Description("How many targets are needed to use this action.")] ConfigWindow_Actions_AoeCount, diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index ff5d634eb..9ae5d549b 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -1466,6 +1466,15 @@ static void DrawConfigsOfBaseAction(IBaseAction a) } ImguiTooltips.HoveredTooltip(ConfigUnitType.Seconds.Local()); + var ttu = config.TimeToUntargetable; + ImGui.SetNextItemWidth(Scale * 150); + if (ImGui.DragFloat($"{UiString.ConfigWindow_Actions_TTU.Local()}##{a}", + ref ttu, 0.1f, 0, 120, $"{ttu:F2}{ConfigUnitType.Seconds.ToSymbol()}")) + { + config.TimeToUntargetable = ttu; + } + ImguiTooltips.HoveredTooltip(ConfigUnitType.Seconds.Local()); + if (a.Setting.StatusProvide != null || a.Setting.TargetStatusProvide != null) { var shouldStatus = config.ShouldCheckStatus; diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index 4ec1086d1..a9e03a4ab 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -94,6 +94,10 @@ private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint case OpCode.SystemLogMessage: OnSystemLogMessage(dataPtr); break; + + case OpCode.ActorControl: + OnActorControl(dataPtr); + break; } //#if DEBUG @@ -103,6 +107,15 @@ private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint //#endif } + private static void OnActorControl(IntPtr dataPtr) + { + var bytes = new byte[32]; + Marshal.Copy(dataPtr, bytes, 0, 32); + Svc.Log.Debug("ActorControl: " + Convert.ToHexString(bytes)); + + //TODO: the refine to the raidtime. + } + private static void OnSystemLogMessage(IntPtr dataPtr) { var bytes = new byte[32]; @@ -117,7 +130,6 @@ private static void OnSystemLogMessage(IntPtr dataPtr) // ExitDeepDungeon(); //else if (logId == DataIds.SystemLogTransferenceInitiated) // nextFloorTransfer = true; - } public static void Disable() From 4cda9b1b49b92846c1265934f1668b6a45180775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Wed, 28 Feb 2024 17:02:36 +0800 Subject: [PATCH 44/48] feat: add timeline ui stuff. --- .../Conditions/IConditionConverter.cs | 10 +- RotationSolver.Basic/Configuration/Configs.cs | 3 + .../Timeline/ActionTimelineItem.cs | 17 ++ .../Configuration/Timeline/ITimelineItem.cs | 8 + .../Timeline/ITimelineItemConverter.cs | 20 ++ .../Timeline/StateTimelineItem.cs | 18 ++ RotationSolver.Basic/DataCenter.cs | 5 +- .../RotationSolver.Basic.csproj | 3 +- RotationSolver/RotationSolverPlugin.cs | 3 +- RotationSolver/UI/ConditionDrawer.cs | 6 +- RotationSolver/UI/RotationConfigWindow.cs | 207 +++++++++++++++++- RotationSolver/UI/RotationConfigWindowTab.cs | 3 + RotationSolver/Updaters/MajorUpdater.cs | 1 + RotationSolver/Updaters/RaidTimeUpdater.cs | 65 +++++- RotationSolver/Updaters/StateUpdater.cs | 30 ++- 15 files changed, 379 insertions(+), 20 deletions(-) create mode 100644 RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs create mode 100644 RotationSolver.Basic/Configuration/Timeline/ITimelineItem.cs create mode 100644 RotationSolver.Basic/Configuration/Timeline/ITimelineItemConverter.cs create mode 100644 RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs diff --git a/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs b/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs index 8e7c31ef5..6206f4808 100644 --- a/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs +++ b/RotationSolver.Basic/Configuration/Conditions/IConditionConverter.cs @@ -39,11 +39,6 @@ internal class IConditionConverter : JsonCreationConverter return null; } } - - private static bool FieldExists(string fieldName, JObject jObject) - { - return jObject[fieldName] != null; - } } internal abstract class JsonCreationConverter : JsonConverter @@ -77,4 +72,9 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer return target; } + + protected static bool FieldExists(string fieldName, JObject jObject) + { + return jObject[fieldName] != null; + } } diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 75b736d05..71bf98366 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -1,6 +1,7 @@ using Dalamud.Configuration; using ECommons.DalamudServices; using ECommons.ExcelServices; +using RotationSolver.Basic.Configuration.Timeline; namespace RotationSolver.Basic.Configuration; @@ -748,6 +749,8 @@ public const string public Dictionary DutyRotationChoice { get; set; } = []; + public Dictionary>> Timeline { get; set; } = []; + public void Save() { #if DEBUG diff --git a/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs b/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs new file mode 100644 index 000000000..3bf3bf275 --- /dev/null +++ b/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs @@ -0,0 +1,17 @@ +namespace RotationSolver.Basic.Configuration.Timeline; +internal class ActionTimelineItem : ITimelineItem +{ + public ActionID ID { get; set; } = ActionID.None; + public float Time { get; set; } = 3; + public float Duration { get; set; } = 3; + + public bool InPeriod(TimelineItem item) + { + var time = item.Time - DataCenter.RaidTimeRaw; + + if (time < 0) return false; + + if (time > Time || Time - time > 3) return false; + return true; + } +} diff --git a/RotationSolver.Basic/Configuration/Timeline/ITimelineItem.cs b/RotationSolver.Basic/Configuration/Timeline/ITimelineItem.cs new file mode 100644 index 000000000..8457e80e9 --- /dev/null +++ b/RotationSolver.Basic/Configuration/Timeline/ITimelineItem.cs @@ -0,0 +1,8 @@ +namespace RotationSolver.Basic.Configuration.Timeline; + +internal interface ITimelineItem +{ + public float Time { get; set; } + public float Duration { get; set; } + public bool InPeriod(TimelineItem item); +} diff --git a/RotationSolver.Basic/Configuration/Timeline/ITimelineItemConverter.cs b/RotationSolver.Basic/Configuration/Timeline/ITimelineItemConverter.cs new file mode 100644 index 000000000..e5ee17d7e --- /dev/null +++ b/RotationSolver.Basic/Configuration/Timeline/ITimelineItemConverter.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json.Linq; +using RotationSolver.Basic.Configuration.Conditions; + +namespace RotationSolver.Basic.Configuration.Timeline; + +internal class ITimelineItemConverter : JsonCreationConverter +{ + protected override ITimelineItem? Create(JObject jObject) + { + if (FieldExists(nameof(ActionTimelineItem.ID), jObject)) + { + return new ActionTimelineItem(); + } + else if (FieldExists(nameof(StateTimelineItem.State), jObject)) + { + return new StateTimelineItem(); + } + return null; + } +} diff --git a/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs b/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs new file mode 100644 index 000000000..e0bdc55fe --- /dev/null +++ b/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs @@ -0,0 +1,18 @@ +namespace RotationSolver.Basic.Configuration.Timeline; + +internal class StateTimelineItem : ITimelineItem +{ + public AutoStatus State { get; set; } + public float Time { get; set; } = 3; + public float Duration { get; set; } = 3; + + public bool InPeriod(TimelineItem item) + { + var time = item.Time - DataCenter.RaidTimeRaw; + + if (time < 0) return false; + + if (time > Time || Time - time > Duration) return false; + return true; + } +} diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index c0e630e3c..0487f6e65 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -1,5 +1,4 @@ using Dalamud.Game.ClientState.Conditions; -using Dalamud.Game.ClientState.Objects.SubKinds; using ECommons.DalamudServices; using ECommons.ExcelServices; using ECommons.GameHelpers; @@ -117,8 +116,6 @@ public static IAction? CommandNextAction { get { - if (ActionSequencerAction != null) return ActionSequencerAction; - var next = NextActs.FirstOrDefault(); while (next != null && NextActs.Count > 0 && (next.DeadTime < DateTime.Now || IActionHelper.IsLastAction(true, next.Act))) @@ -126,7 +123,7 @@ public static IAction? CommandNextAction NextActs.RemoveAt(0); next = NextActs.FirstOrDefault(); } - return next?.Act; + return next?.Act ?? ActionSequencerAction; } } public static Job Job { get; set; } diff --git a/RotationSolver.Basic/RotationSolver.Basic.csproj b/RotationSolver.Basic/RotationSolver.Basic.csproj index 59d8f7ba8..fd9ad2f82 100644 --- a/RotationSolver.Basic/RotationSolver.Basic.csproj +++ b/RotationSolver.Basic/RotationSolver.Basic.csproj @@ -67,8 +67,7 @@ all - + True diff --git a/RotationSolver/RotationSolverPlugin.cs b/RotationSolver/RotationSolverPlugin.cs index db069194e..250016795 100644 --- a/RotationSolver/RotationSolverPlugin.cs +++ b/RotationSolver/RotationSolverPlugin.cs @@ -8,6 +8,7 @@ using ECommons.GameHelpers; using ECommons.ImGuiMethods; using RotationSolver.Basic.Configuration; +using RotationSolver.Basic.Configuration.Timeline; using RotationSolver.Commands; using RotationSolver.Data; using RotationSolver.Helpers; @@ -44,7 +45,7 @@ public RotationSolverPlugin(DalamudPluginInterface pluginInterface) try { Service.Config = JsonConvert.DeserializeObject( - File.ReadAllText(Svc.PluginInterface.ConfigFile.FullName)) + File.ReadAllText(Svc.PluginInterface.ConfigFile.FullName), new ITimelineItemConverter()) ?? new Configs(); } catch (Exception ex) diff --git a/RotationSolver/UI/ConditionDrawer.cs b/RotationSolver/UI/ConditionDrawer.cs index 22463934f..56c975f4d 100644 --- a/RotationSolver/UI/ConditionDrawer.cs +++ b/RotationSolver/UI/ConditionDrawer.cs @@ -88,7 +88,7 @@ private static IEnumerable GetAllMethods(this Type? type, Func(string name, ref T value) where T : struct, Enum + public static bool DrawByteEnum(string name, ref T value) where T : struct, Enum { var values = Enum.GetValues().Where(i => i.GetAttribute() == null).ToHashSet().ToArray(); var index = Array.IndexOf(values, value); @@ -97,7 +97,9 @@ public static void DrawByteEnum(string name, ref T value) where T : struct, E if (ImGuiHelper.SelectableCombo(name, names, ref index)) { value = values[index]; + return true; } + return false; } public static bool DrawDragFloat(ConfigUnitType type, string name, ref float value) @@ -401,7 +403,7 @@ private static void DrawAfter(this ActionCondition actionCondition, ICustomRotat ActionSelectorPopUp(popUpKey, _actionsList, rotation, item => actionCondition.ID = (ActionID)item.ID); - if (actionCondition._action?.GetTexture(out var icon) ?? false || IconSet.GetTexture(4, out icon)) + if ((actionCondition._action?.GetTexture(out var icon) ?? false) || IconSet.GetTexture(4, out icon)) { var cursor = ImGui.GetCursorPos(); if (ImGuiHelper.NoPaddingNoColorImageButton(icon.ImGuiHandle, Vector2.One * IconSize, actionCondition.GetHashCode().ToString())) diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index 9ae5d549b..fc6f59fbd 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -1,4 +1,5 @@ -using Dalamud.Interface.Colors; +using Dalamud.Game.ClientState.Keys; +using Dalamud.Interface.Colors; using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; @@ -14,13 +15,17 @@ using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Configuration; +using RotationSolver.Basic.Configuration.Conditions; +using RotationSolver.Basic.Configuration.Timeline; using RotationSolver.Data; using RotationSolver.Helpers; using RotationSolver.Localization; using RotationSolver.UI.SearchableConfigs; using RotationSolver.Updaters; using System.Diagnostics; +using System.Xml.Linq; using GAction = Lumina.Excel.GeneratedSheets.Action; +using TargetType = RotationSolver.Basic.Actions.TargetType; namespace RotationSolver.UI; @@ -619,6 +624,10 @@ private void DrawBody() DrawTarget(); break; + case RotationConfigWindowTab.Timeline: + DrawTimeline(); + break; + case RotationConfigWindowTab.Extra: DrawExtra(); break; @@ -631,6 +640,184 @@ private void DrawBody() } } + #region Timeline + private static int _territoryIndex = 0; + private static readonly CollapsingHeaderGroup _timelineGroup = new() + { + HeaderSize = 18, + }; + private static readonly CollapsingHeaderGroup _timelineActionsList = new() + { + HeaderSize = 12, + }; + private static void DrawTimeline() + { + var territory = Svc.Data.GetExcelSheet(); + if (territory == null) return; + + var ids = RaidTimeUpdater._pathForRaids.Keys.ToArray(); + var territories = ids.Select(territory.GetRow).ToArray(); + + using (var font = ImRaii.PushFont(ImGuiHelper.GetFont(21))) + { + using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); + var names = territories.Select(t => t?.ContentFinderCondition?.Value?.Name?.RawString ?? "Unnamed Duty").ToArray(); + + ImGuiHelper.DrawItemMiddle(() => + { + ImGuiHelper.SelectableCombo("##Choice the specific dungeon", names, ref _territoryIndex); + }, ImGui.GetWindowWidth(), ImGui.CalcTextSize(names[_territoryIndex]).X + ImGui.GetStyle().ItemSpacing.X * 2); + } + + DrawContentFinder(territories[_territoryIndex]?.ContentFinderCondition.Value); + + if (_timelineGroup == null) return; + + var id = ids[_territoryIndex]; + if (!Service.Config.Timeline.TryGetValue(id, out var timeLine)) return; + + ImGui.Separator(); + + if (ImGui.Selectable(UiString.ConfigWindow_Actions_Copy.Local())) + { + var str = JsonConvert.SerializeObject(timeLine, Formatting.Indented); + ImGui.SetClipboardText(str); + } + + ImGui.SameLine(); + + if (ImGui.Selectable(UiString.ActionSequencer_FromClipboard.Local())) + { + var str = ImGui.GetClipboardText(); + try + { + var set = JsonConvert.DeserializeObject>>(str, new ITimelineItemConverter())!; + Service.Config.Timeline[id] = timeLine = set; + } + catch (Exception ex) + { + Svc.Log.Warning(ex, "Failed to load the condition."); + } + } + + _timelineGroup.ClearCollapsingHeader(); + + foreach (var item in DataCenter.TimelineItems) + { + _timelineGroup.AddCollapsingHeader(() => item.Name, () => + { + if(!timeLine.TryGetValue(item.Name, out var timeLineItems)) + { + timeLine[item.Name] = timeLineItems = []; + } + AddButton(); + + for (int i = 0; i < timeLineItems.Count; i++) + { + var timeLineItem = timeLineItems[i]; + + void Delete() + { + timeLineItems.RemoveAt(i); + }; + + void Up() + { + timeLineItems.RemoveAt(i); + timeLineItems.Insert(Math.Max(0, i - 1), timeLineItem); + }; + + void Down() + { + timeLineItems.RemoveAt(i); + timeLineItems.Insert(Math.Min(timeLineItems.Count, i + 1), timeLineItem); + } + + var key = $"TimelineItem Pop Up: {timeLineItem.GetHashCode()}"; + + ImGuiHelper.DrawHotKeysPopup(key, string.Empty, + (UiString.ConfigWindow_List_Remove.Local(), Delete, ["Delete"]), + (UiString.ConfigWindow_Actions_MoveUp.Local(), Up, ["↑"]), + (UiString.ConfigWindow_Actions_MoveDown.Local(), Down, ["↓"])); + + ConditionDrawer.DrawCondition(timeLineItem.InPeriod(item)); + + ImGuiHelper.ExecuteHotKeysPopup(key, string.Empty, string.Empty, true, + (Delete, [VirtualKey.DELETE]), + (Up, [VirtualKey.UP]), + (Down, [VirtualKey.DOWN])); + + var time = timeLineItem.Time; + if(ConditionDrawer.DrawDragFloat(ConfigUnitType.Seconds, $"Time##Time{timeLineItem.GetHashCode()}", ref time)) + { + timeLineItem.Time = time; + } + + time = timeLineItem.Duration; + if (ConditionDrawer.DrawDragFloat(ConfigUnitType.Seconds, $"Duration##Duration{timeLineItem.GetHashCode()}", ref time)) + { + timeLineItem.Duration = time; + } + + if (timeLineItem is ActionTimelineItem actionItem) + { + if(DataCenter.RightNowRotation != null) + { + var popUpKey = $"Action Finder{timeLineItem.GetHashCode()}"; + ConditionDrawer.ActionSelectorPopUp(popUpKey, _timelineActionsList, DataCenter.RightNowRotation, item => actionItem.ID = (ActionID)item.ID); + + if (actionItem.ID.GetTexture(out var icon) || IconSet.GetTexture(4, out icon)) + { + ImGui.SameLine(); + var cursor = ImGui.GetCursorPos(); + if (ImGuiHelper.NoPaddingNoColorImageButton(icon.ImGuiHandle, Vector2.One * ConditionDrawer.IconSize, timeLineItem.GetHashCode().ToString())) + { + if (!ImGui.IsPopupOpen(popUpKey)) ImGui.OpenPopup(popUpKey); + } + ImGuiHelper.DrawActionOverlay(cursor, ConditionDrawer.IconSize, 1); + } + } + } + else if (timeLineItem is StateTimelineItem stateItem) + { + var state = stateItem.State; + if (ConditionDrawer.DrawByteEnum($"##AutoStatus{timeLineItem.GetHashCode()}", ref state)) + { + stateItem.State = state; + } + } + } + + void AddButton() + { + if (ImGuiEx.IconButton(FontAwesomeIcon.Plus, "AddTimelineButton" + item.Name)) + { + ImGui.OpenPopup("PopupTimelineButton" + item.Name); + } + + using var popUp = ImRaii.Popup("PopupTimelineButton" + item.Name); + if (popUp) + { + AddOneCondition(); + AddOneCondition(); + } + + void AddOneCondition() where T : ITimelineItem + { + if (ImGui.Selectable(typeof(T).Local())) + { + timeLineItems.Add(Activator.CreateInstance()); + ImGui.CloseCurrentPopup(); + } + } + } + }); + } + + _timelineGroup.Draw(); + } + #endregion + #region About private static readonly SortedList CountStringPair = new() { @@ -2227,6 +2414,8 @@ private static void DrawListTerritories() }, ImGui.GetWindowWidth(), ImGui.CalcTextSize(territoryName).X + ImGui.GetStyle().ItemSpacing.X + iconSize); } + DrawContentFinder(DataCenter.ContentFinder); + using var table = ImRaii.Table("Rotation Solver List Territories", 3, ImGuiTableFlags.BordersInner | ImGuiTableFlags.Resizable | ImGuiTableFlags.SizingStretchSame); if (table) { @@ -2377,6 +2566,22 @@ private static void DrawListTerritories() } #endregion + private static void DrawContentFinder(ContentFinderCondition? content) + { + var badge = content?.Image; + if (badge != null && badge.Value != 0 + && IconSet.GetTexture(badge.Value, out var badgeTexture)) + { + var wholeWidth = ImGui.GetWindowWidth(); + var size = new Vector2(badgeTexture.Width, badgeTexture.Height) * MathF.Min(1, MathF.Min(320, wholeWidth) / badgeTexture.Width); + + ImGuiHelper.DrawItemMiddle(() => + { + ImGui.Image(badgeTexture.ImGuiHandle, size); + }, wholeWidth, size.X); + } + } + #region Debug private static void DrawDebug() { diff --git a/RotationSolver/UI/RotationConfigWindowTab.cs b/RotationSolver/UI/RotationConfigWindowTab.cs index 5c7a2f49a..4f7b8cc67 100644 --- a/RotationSolver/UI/RotationConfigWindowTab.cs +++ b/RotationSolver/UI/RotationConfigWindowTab.cs @@ -40,6 +40,9 @@ internal enum RotationConfigWindowTab : byte [Description("The way to find the targets, hostiles or friends.")] [TabIcon(Icon = 16)] Target, + [Description("Your custom time line about the specific duty.")] + [TabIcon(Icon = 73)] Timeline, + [Description("Some features that shouldn't be included in RS but help you.")] [TabIcon(Icon = 51)] Extra, diff --git a/RotationSolver/Updaters/MajorUpdater.cs b/RotationSolver/Updaters/MajorUpdater.cs index 4c5702604..c216bd7b0 100644 --- a/RotationSolver/Updaters/MajorUpdater.cs +++ b/RotationSolver/Updaters/MajorUpdater.cs @@ -181,6 +181,7 @@ private static void UpdateWork() RotationUpdater.UpdateRotation(); + RaidTimeUpdater.UpdateTimeline(); ActionSequencerUpdater.UpdateActionSequencerAction(); ActionUpdater.UpdateNextAction(); diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs index bc5b18388..8644bab11 100644 --- a/RotationSolver/Updaters/RaidTimeUpdater.cs +++ b/RotationSolver/Updaters/RaidTimeUpdater.cs @@ -1,10 +1,58 @@ using ECommons.DalamudServices; using Newtonsoft.Json.Linq; +using RotationSolver.Basic.Configuration.Timeline; using System.Text.RegularExpressions; namespace RotationSolver.Updaters; internal static partial class RaidTimeUpdater { + internal static readonly Dictionary _pathForRaids = new() + { + { 1148, "06-ew/raid/p9s.txt" }, + { 1150, "06-ew/raid/p10s.txt" }, + { 1152, "06-ew/raid/p11s.txt" }, + { 1154, "06-ew/raid/p12s.txt" }, + }; + + private static readonly Dictionary _savedTimeLines = []; + private static readonly Queue<(DateTime, ActionTimelineItem)> _addedItems = new(); + + internal static void UpdateTimeline() + { + if (!Service.Config.Timeline.TryGetValue(Svc.ClientState.TerritoryType, out var timeline)) return; + + while (_addedItems.TryPeek(out var addedItem)) + { + if (DateTime.Now - addedItem.Item1 < TimeSpan.FromSeconds(5)) break; + _addedItems.Dequeue(); + } + + foreach (var item in DataCenter.TimelineItems) + { + var time = item.Time - DataCenter.RaidTimeRaw; + + if (time < 0) continue; + if (!timeline.TryGetValue(item.Name, out var items)) continue; + + foreach (var item2 in items.OfType() + .Where(i => !_addedItems.Any(added => added.Item2 == i))) + { + if (!item2.InPeriod(item)) continue; + + var act = DataCenter.RightNowRotation?.AllBaseActions.FirstOrDefault(a => (ActionID)a.ID == item2.ID); + + if (act == null) continue; + + DataCenter.AddCommandAction(act, item2.Duration); + _addedItems.Enqueue((DateTime.Now, item2)); + +#if DEBUG + Svc.Log.Debug($"Added the action{act} to timeline."); +#endif + } + } + } + internal static void Enable() { Svc.ClientState.TerritoryChanged += ClientState_TerritoryChanged; @@ -16,18 +64,29 @@ internal static void Disable() Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; } - private static async void ClientState_TerritoryChanged(ushort obj) + private static async void ClientState_TerritoryChanged(ushort id) { try { - DataCenter.TimelineItems = await UpdateRaidTime("06-ew/raid/p9s.txt"); + DataCenter.TimelineItems = await GetRaidTimeAsync(id); } catch (Exception e) { Svc.Log.Warning(e, "Failed to update the raid timeline!"); } } - static async Task UpdateRaidTime(string path) + + static async Task GetRaidTimeAsync(ushort id) + { + if (_savedTimeLines.TryGetValue(id, out var value)) return value; + if (_pathForRaids.TryGetValue(id, out var path)) + { + return await DownloadRaidTimeAsync(path); + } + return []; + } + + static async Task DownloadRaidTimeAsync(string path) { using var client = new HttpClient(); var message = await client.GetAsync("https://raw.githubusercontent.com/OverlayPlugin/cactbot/main/ui/raidboss/data/" + path); diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs index 81c6cb9d6..f9a80fa06 100644 --- a/RotationSolver/Updaters/StateUpdater.cs +++ b/RotationSolver/Updaters/StateUpdater.cs @@ -1,5 +1,7 @@ -using ECommons.GameHelpers; +using ECommons.DalamudServices; +using ECommons.GameHelpers; using RotationSolver.Basic.Configuration.Conditions; +using RotationSolver.Basic.Configuration.Timeline; namespace RotationSolver.Updaters; internal static class StateUpdater @@ -16,7 +18,7 @@ internal static class StateUpdater public static void UpdateState() { DataCenter.CommandStatus = StatusFromCmdOrCondition(); - DataCenter.AutoStatus = StatusFromAutomatic(); + DataCenter.AutoStatus = StatusFromAutomatic() | StatusFromTimeline(); } static RandomDelay @@ -25,6 +27,30 @@ static RandomDelay _healDelay3 = new(() => Service.Config.HealDelay), _healDelay4 = new(() => Service.Config.HealDelay); + private static AutoStatus StatusFromTimeline() + { + AutoStatus status = AutoStatus.None; + + if (!Service.Config.Timeline.TryGetValue(Svc.ClientState.TerritoryType, out var timeline)) return status; + + foreach (var item in DataCenter.TimelineItems) + { + var time = item.Time - DataCenter.RaidTimeRaw; + + if (time < 0) continue; + if (!timeline.TryGetValue(item.Name, out var items)) continue; + + foreach (var item2 in items.OfType()) + { + if (!item2.InPeriod(item)) continue; + + status |= item2.State; + } + } + + return status; + } + private static AutoStatus StatusFromAutomatic() { AutoStatus status = AutoStatus.None; From 1911644e7313db7907389cbf42547d226a73fa25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:52:17 +0800 Subject: [PATCH 45/48] New translations localization.json (Chinese Simplified) --- RotationSolver/Localization/zh.json | 66 ++++++++++++++--------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/RotationSolver/Localization/zh.json b/RotationSolver/Localization/zh.json index 531d2e188..7433d0b6d 100644 --- a/RotationSolver/Localization/zh.json +++ b/RotationSolver/Localization/zh.json @@ -209,53 +209,53 @@ "RotationSolver.Data.UiString.ConfigWindow_Actions_AoeCount": "需要多少目标才能使用此操作", "RotationSolver.Data.UiString.SpecialCommandType_Speed": "速度", "RotationSolver.Data.UiString.ConfigWindow_Events_AddEvent": "添加事件", - "RotationSolver.Data.UiString.ConfigWindow_Events_Description": "In this window, you can set what macro will be trigger after using an action.", + "RotationSolver.Data.UiString.ConfigWindow_Events_Description": "在这个窗口,你可以设定释放特定技能后将自动触发哪个宏。", "RotationSolver.Data.UiString.ConfigWindow_Events_DutyStart": "任务开始 ", "RotationSolver.Data.UiString.ConfigWindow_Events_MacroIndex": "宏编号", - "RotationSolver.Data.UiString.ConfigWindow_Events_ShareMacro": "Is Shared", + "RotationSolver.Data.UiString.ConfigWindow_Events_ShareMacro": "是共享宏", "RotationSolver.Data.UiString.ConfigWindow_Events_DutyEnd": "任务结束 ", "PoslockCastingName": "在施法或执行某些操作时锁定移动", "UseStopCastingName": "在目标死亡时停止施法", "AutoOpenChestName": "自动开启宝箱", - "PoslockCastingDescription": "LT is for gamepad player", + "PoslockCastingDescription": "手柄玩家为按下LT无视咏唱锁", "PoslockModifierName": "自定义键位临时取消移动锁定", - "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "Conditions for forced automatic use of action.", - "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "Conditions for automatic use of action being disabled.", - "MoveTargetAngleName": "The size of the sector angle that can be selected as the moveable target", - "DistanceForMovingName": "Use gapcloser as a damage ability if the distance to your target is less then this.", - "MoveTowardsScreenCenterName": "Using movement actions towards the object in the center of the screen", + "RotationSolver.Data.UiString.ConfigWindow_Actions_ForcedConditionSet_Description": "强制自动使用技能的条件。", + "RotationSolver.Data.UiString.ConfigWindow_Actions_DisabledConditionSet_Description": "禁止自动使用技能的条件。", + "MoveTargetAngleName": "可选为移动目标的扇区角度的大小", + "DistanceForMovingName": "如果和目标的距离小于此值,使用其作为伤害能力技使用。", + "MoveTowardsScreenCenterName": "移动技能优先以屏幕中心的对象为目标", "MoveAreaActionFarthestName": "Target movement area ability to the farthest possible location", - "BossTimeToKillName": "If target's time until death is higher than this, regard it as boss.", - "DyingTimeToKillName": "If target's time until death is lower than this, regard it is dying.", - "ChangeTargetForFateName": "Select only Fate targets in Fate", - "OnlyAttackInViewName": "Only attack the target in view.", + "BossTimeToKillName": "如果目标整个死亡时间高于此值,则将其视为Boss", + "DyingTimeToKillName": "如果目标的死亡时间低于这个值,则认为其为濒死状态。", + "ChangeTargetForFateName": "在Fate中只选择Fate目标", + "OnlyAttackInViewName": "只攻击视野中的目标。", "OnlyAttackInVisionConeName": "Only attack the targets in vision cone", - "TargetFatePriorityName": "Target Fate priority", - "TargetHuntingRelicLevePriorityName": "Target Hunt/Relic/Leve priority.", - "TargetQuestPriorityName": "Target quest priority.", - "AddEnemyListToHostileName": "Add enemy list to the hostile targets.", - "ChooseAttackMarkName": "Priority attack targets with attack markers", - "FilterStopMarkName": "Never attack targets with stop markers", - "HostileTypeName": "Engage settings", - "SwitchTargetFriendlyName": "Target allies for friendly actions.", - "TargetAllForFriendlyName": "Target all for friendly actions (include passerby)", - "MoveTargetAngleDescription": "If the selection mode is based on character facing, i.e., targets within the character's viewpoint are moveable targets. \nIf the selection mode is screen-centered, i.e., targets within a sector drawn upward from the character's point are movable targets.", + "TargetFatePriorityName": "优先选择Fate目标", + "TargetHuntingRelicLevePriorityName": "目标 Hunt/Relic/Leve 优先级。", + "TargetQuestPriorityName": "优先选择任务目标", + "AddEnemyListToHostileName": "将敌对列表添加至攻击目标", + "ChooseAttackMarkName": "优先攻击带有攻击标记的目标", + "FilterStopMarkName": "不攻击带有禁止标记的目标", + "HostileTypeName": "开怪设置", + "SwitchTargetFriendlyName": "治疗(增益)类技能只对队友释放", + "TargetAllForFriendlyName": "治疗(增益)类技能可以瞄准所有人(包括路人)", + "MoveTargetAngleDescription": "如果选择模式基于角色面向,即角色视野点中的目标是位移技能的目标。 \n如果选择模式以屏幕为中心,即从角色点向上绘制的扇区内目标是位移技能的目标。", "RotationSolver.Basic.Attributes.ConfigUnitType.Degree": "Angle Unit, in degrees.", - "MoveAreaActionFarthestDescription": "Move to the furthest position for targeting are movement actions.", - "MoveTowardsScreenCenterDescription": "Using movement actions towards the object in the center of the screen, otherwise toward the facing object.", - "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "You can The logic of hostile target selection to allow flexibility in switching the logic of selecting hostile in battle.", - "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "Remove", - "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "Move Up", - "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "Move Down", - "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "Hostile target selection condition", + "MoveAreaActionFarthestDescription": "位移技能移动到最远的目标位置上", + "MoveTowardsScreenCenterDescription": "移动技能优先以屏幕中心的对象为目标,否则以角色面向为优先方向", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileDesc": "你可以设定敌对的选择,以便于在战斗中灵活切换选择敌对的逻辑。", + "RotationSolver.Data.UiString.ConfigWindow_List_Remove": "删除", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveUp": "上移", + "RotationSolver.Data.UiString.ConfigWindow_Actions_MoveDown": "下移", + "RotationSolver.Data.UiString.ConfigWindow_Param_HostileCondition": "敌对目标选中条件", "RotationSolver.Basic.Attributes.ConfigUnitType.Yalms": "Distance Unit, in yalms.", "RotationSolver.Data.UiString.SpecialCommandType_Dispel": "Dispel", "RotationSolver.Data.UiString.SpecialCommandType_Raise": "Raise", - "DownloadRotationsName": "Auto Download Rotations", - "AutoLoadCustomRotationsName": "Auto load rotations", - "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "The folder contains rotation libs or the download url about rotation lib.", + "DownloadRotationsName": "自动下载循环", + "AutoLoadCustomRotationsName": "自动加载循环", + "RotationSolver.Data.UiString.ConfigWindow_Rotations_Library": "此文件夹包含循环库或循环库的下载URL。", "RotationSolver.Data.UiString.SpecialCommandType_LimitBreak": "Limit Break", - "UseOverlayWindowDescription": "This top window is used to display some extra information on your game window, such as target's positional, target and sub-target, etc.", + "UseOverlayWindowDescription": "此悬浮层用于在您的游戏窗口中显示一些额外信息,例如目标位置、目标和子目标等。", "DrawingHeightName": "The height of the drawing things.", "SampleLengthName": "Drawing smoothness.", "BeneficialPositionColorName": "The color of beneficial AoE positions", From 858d2fb07d93d4f88df0435131a8c568f17ad72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Wed, 28 Feb 2024 22:12:40 +0800 Subject: [PATCH 46/48] fix: fixed some things about the timeline. --- Resources/BeneficialPositions.json | 3 +- Resources/NoHostileNames.json | 3 + Resources/NoProvokeNames.json | 3 + Resources/RotationSolverRecord.json | 4 +- RotationSolver.Basic/Configuration/Configs.cs | 2 +- .../Timeline/ActionTimelineItem.cs | 2 + .../Timeline/StateTimelineItem.cs | 1 + RotationSolver.Basic/Data/TimelineItem.cs | 19 +++- RotationSolver.Basic/DataCenter.cs | 9 +- RotationSolver/Localization/Localization.json | 33 +++++- RotationSolver/UI/ImGuiHelper.cs | 15 ++- RotationSolver/UI/RotationConfigWindow.cs | 52 +++++---- RotationSolver/Updaters/ActionUpdater.cs | 1 + RotationSolver/Updaters/RaidTimeUpdater.cs | 60 ++++++++-- RotationSolver/Updaters/SocialUpdater.cs | 9 -- RotationSolver/Updaters/StateUpdater.cs | 2 +- RotationSolver/Updaters/TargetUpdater.cs | 1 + RotationSolver/Watcher.cs | 104 +++++++++++++----- 18 files changed, 243 insertions(+), 80 deletions(-) diff --git a/Resources/BeneficialPositions.json b/Resources/BeneficialPositions.json index 96989288e..d9ac20fd2 100644 --- a/Resources/BeneficialPositions.json +++ b/Resources/BeneficialPositions.json @@ -3,5 +3,6 @@ "641": [], "837": [], "613": [], - "1175": [] + "1175": [], + "340": [] } \ No newline at end of file diff --git a/Resources/NoHostileNames.json b/Resources/NoHostileNames.json index 75aaf25f7..efbf1f5b4 100644 --- a/Resources/NoHostileNames.json +++ b/Resources/NoHostileNames.json @@ -13,5 +13,8 @@ ], "1175": [ "" + ], + "340": [ + "" ] } \ No newline at end of file diff --git a/Resources/NoProvokeNames.json b/Resources/NoProvokeNames.json index 47f57fa7a..66069cd9b 100644 --- a/Resources/NoProvokeNames.json +++ b/Resources/NoProvokeNames.json @@ -4,5 +4,8 @@ ], "1175": [ "" + ], + "340": [ + "" ] } \ No newline at end of file diff --git a/Resources/RotationSolverRecord.json b/Resources/RotationSolverRecord.json index ff50d467c..83ce4d76e 100644 --- a/Resources/RotationSolverRecord.json +++ b/Resources/RotationSolverRecord.json @@ -1,5 +1,5 @@ { - "ClickingCount": 87877, - "SayingHelloCount": 76, + "ClickingCount": 87918, + "SayingHelloCount": 95, "SaidUsers": [] } \ No newline at end of file diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 71bf98366..7a3f31bc5 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -749,7 +749,7 @@ public const string public Dictionary DutyRotationChoice { get; set; } = []; - public Dictionary>> Timeline { get; set; } = []; + public Dictionary>> Timeline { get; set; } = []; public void Save() { diff --git a/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs b/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs index 3bf3bf275..9574d13cf 100644 --- a/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs +++ b/RotationSolver.Basic/Configuration/Timeline/ActionTimelineItem.cs @@ -1,4 +1,6 @@ namespace RotationSolver.Basic.Configuration.Timeline; + +[Description("Action Timeline")] internal class ActionTimelineItem : ITimelineItem { public ActionID ID { get; set; } = ActionID.None; diff --git a/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs b/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs index e0bdc55fe..a25639cef 100644 --- a/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs +++ b/RotationSolver.Basic/Configuration/Timeline/StateTimelineItem.cs @@ -1,5 +1,6 @@ namespace RotationSolver.Basic.Configuration.Timeline; +[Description("State Timeline")] internal class StateTimelineItem : ITimelineItem { public AutoStatus State { get; set; } diff --git a/RotationSolver.Basic/Data/TimelineItem.cs b/RotationSolver.Basic/Data/TimelineItem.cs index 0de27c62e..8a4e1cace 100644 --- a/RotationSolver.Basic/Data/TimelineItem.cs +++ b/RotationSolver.Basic/Data/TimelineItem.cs @@ -24,7 +24,7 @@ internal readonly struct TimelineItem(float time, string name, TimelineType type public string Name => name; - public bool IsShown => name is not "--Reset--" and not "--sync--"; + public bool IsShown => Name is not "--Reset--" and not "--sync--"; public string this[string propertyName] => Object?[propertyName]?.ToString() ?? string.Empty; @@ -50,6 +50,7 @@ private static TimelineType GetTypeFromName(string type) case "GameLog": return TimelineType.GameLog; + case "#Ability": case "Ability": return TimelineType.Ability; @@ -57,18 +58,28 @@ private static TimelineType GetTypeFromName(string type) return TimelineType.StartsUsing; case "SystemLogMessage": - return TimelineType.SystemLogMessage; //TODO: a6s for testing + return TimelineType.SystemLogMessage; default: +#if DEBUG Svc.Log.Warning($"New timelinetype: {type}"); +#endif return TimelineType.Unknown; } } public void UpdateRaidTimeOffset() { - DataCenter.RaidTimeRaw = Time; - Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeRaw)} to {DataCenter.RaidTimeRaw}."); + if (Name == "--Reset--") + { + DataCenter.RaidTimeRaw = -1; + Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeRaw)}."); + } + else + { + DataCenter.RaidTimeRaw = Time; + Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeRaw)} to {DataCenter.RaidTimeRaw}."); + } } public bool IsIdMatched(uint id) diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 0487f6e65..1668b5743 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -270,7 +270,14 @@ internal static float RaidTimeRaw } set { - _startRaidTime = DateTime.Now - TimeSpan.FromSeconds(value); + if (value < 0) + { + _startRaidTime = DateTime.MinValue; + } + else + { + _startRaidTime = DateTime.Now - TimeSpan.FromSeconds(value); + } } } diff --git a/RotationSolver/Localization/Localization.json b/RotationSolver/Localization/Localization.json index 68b284d99..a6f08330e 100644 --- a/RotationSolver/Localization/Localization.json +++ b/RotationSolver/Localization/Localization.json @@ -307,5 +307,36 @@ "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInHighEndDuty": "Is in the high-end duty", "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsInDuty": "Is player in duty", "RotationSolver.Basic.Rotations.CustomRotation.Boolean IsBurst": "Is burst", - "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition" + "RotationSolver.Basic.Configuration.Conditions.RotationCondition": "Rotation Condition", + "RotationSolver.Data.UiString.ConfigWindow_List_NoHostile": "Don't target", + "RotationSolver.Data.UiString.ConfigWindow_List_NoProvoke": "Don't provoke", + "RotationSolver.Data.UiString.ConfigWindow_List_BeneficialPositions": "Beneficial AoE locations", + "RotationSolver.Data.UiString.ConfigWindow_List_NoHostileDesc": "Enemies that will never be targeted.", + "RotationSolver.Data.UiString.ConfigWindow_List_NoProvokeDesc": "Enemies that will never be provoked.", + "RotationSolver.Data.UiString.ConfigWindow_List_AddPosition": "Add beneficial AoE location", + "RotationSolver.Data.UiString.ConfigWindow_List_NoHostilesName": "The name of the enemy that you don't want to be targeted", + "RotationSolver.Data.UiString.ConfigWindow_List_NoProvokeName": "The name of the enemy that you don't want to be provoked", + "RotationSolver.UI.RotationConfigWindowTab.Timeline": "Your custom time line about the specific duty.", + "RotationSolver.Basic.Configuration.Timeline.ActionTimelineItem": "Action Timeline", + "RotationSolver.Basic.Configuration.Timeline.StateTimelineItem": "State Timeline", + "RotationSolver.Basic.Data.AutoStatus.None": "None", + "RotationSolver.Basic.Data.AutoStatus.Interrupt": "Interrupt", + "RotationSolver.Basic.Data.AutoStatus.TankStance": "TankStance", + "RotationSolver.Basic.Data.AutoStatus.Provoke": "Provoke", + "RotationSolver.Basic.Data.AutoStatus.DefenseSingle": "DefenseSingle", + "RotationSolver.Basic.Data.AutoStatus.DefenseArea": "DefenseArea", + "RotationSolver.Basic.Data.AutoStatus.HealSingleAbility": "HealSingleAbility", + "RotationSolver.Basic.Data.AutoStatus.HealSingleSpell": "HealSingleSpell", + "RotationSolver.Basic.Data.AutoStatus.HealAreaAbility": "HealAreaAbility", + "RotationSolver.Basic.Data.AutoStatus.HealAreaSpell": "HealAreaSpell", + "RotationSolver.Basic.Data.AutoStatus.Raise": "Raise", + "RotationSolver.Basic.Data.AutoStatus.Dispel": "Dispel", + "RotationSolver.Basic.Data.AutoStatus.Positional": "Positional", + "RotationSolver.Basic.Data.AutoStatus.Shirk": "Shirk", + "RotationSolver.Basic.Data.AutoStatus.MoveForward": "MoveForward", + "RotationSolver.Basic.Data.AutoStatus.MoveBack": "MoveBack", + "RotationSolver.Basic.Data.AutoStatus.AntiKnockback": "AntiKnockback", + "RotationSolver.Basic.Data.AutoStatus.Burst": "Burst", + "RotationSolver.Basic.Data.AutoStatus.Speed": "Speed", + "RotationSolver.Basic.Data.AutoStatus.LimitBreak": "LimitBreak" } \ No newline at end of file diff --git a/RotationSolver/UI/ImGuiHelper.cs b/RotationSolver/UI/ImGuiHelper.cs index 35e86aa45..f2a993852 100644 --- a/RotationSolver/UI/ImGuiHelper.cs +++ b/RotationSolver/UI/ImGuiHelper.cs @@ -103,13 +103,22 @@ public unsafe static ImFontPtr GetFont(float size) return font; } - public static unsafe bool SelectableCombo(string popUp, string[] items, ref int index) + public static unsafe bool SelectableCombo(string popUp, string[] items, ref int index, ImFontPtr? font = null, Vector4? color = null) { var count = items.Length; var name = items[index % count] + "##" + popUp; var result = false; + List disposables = new(2); + if (font != null) + { + disposables.Add(ImRaii.PushFont(font.Value)); + } + if(color != null) + { + disposables.Add(ImRaii.PushColor(ImGuiCol.Text, color.Value)); + } if (SelectableButton(name)) { if (count < 3) @@ -122,6 +131,10 @@ public static unsafe bool SelectableCombo(string popUp, string[] items, ref int if (!ImGui.IsPopupOpen(popUp)) ImGui.OpenPopup(popUp); } } + foreach (var item in disposables) + { + item.Dispose(); + } if (ImGui.IsItemHovered()) { diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index fc6f59fbd..7d85e0f76 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -15,7 +15,6 @@ using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Configuration; -using RotationSolver.Basic.Configuration.Conditions; using RotationSolver.Basic.Configuration.Timeline; using RotationSolver.Data; using RotationSolver.Helpers; @@ -23,7 +22,6 @@ using RotationSolver.UI.SearchableConfigs; using RotationSolver.Updaters; using System.Diagnostics; -using System.Xml.Linq; using GAction = Lumina.Excel.GeneratedSheets.Action; using TargetType = RotationSolver.Basic.Actions.TargetType; @@ -644,7 +642,7 @@ private void DrawBody() private static int _territoryIndex = 0; private static readonly CollapsingHeaderGroup _timelineGroup = new() { - HeaderSize = 18, + HeaderSize = 12, }; private static readonly CollapsingHeaderGroup _timelineActionsList = new() { @@ -657,28 +655,35 @@ private static void DrawTimeline() var ids = RaidTimeUpdater._pathForRaids.Keys.ToArray(); var territories = ids.Select(territory.GetRow).ToArray(); - - using (var font = ImRaii.PushFont(ImGuiHelper.GetFont(21))) - { - using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - var names = territories.Select(t => t?.ContentFinderCondition?.Value?.Name?.RawString ?? "Unnamed Duty").ToArray(); - ImGuiHelper.DrawItemMiddle(() => - { - ImGuiHelper.SelectableCombo("##Choice the specific dungeon", names, ref _territoryIndex); - }, ImGui.GetWindowWidth(), ImGui.CalcTextSize(names[_territoryIndex]).X + ImGui.GetStyle().ItemSpacing.X * 2); + var names = territories.Select(t => t?.ContentFinderCondition?.Value?.Name?.RawString ?? "Unnamed Duty").ToArray(); + + var imFont = ImGuiHelper.GetFont(21); + float width = 0; + using (var font = ImRaii.PushFont(imFont)) + { + width = ImGui.CalcTextSize(names[_territoryIndex]).X + ImGui.GetStyle().ItemSpacing.X * 2; } + ImGuiHelper.DrawItemMiddle(() => + { + ImGuiHelper.SelectableCombo("##Choice the specific dungeon", names, ref _territoryIndex, + imFont, ImGuiColors.DalamudYellow); + }, ImGui.GetWindowWidth(), width); + DrawContentFinder(territories[_territoryIndex]?.ContentFinderCondition.Value); if (_timelineGroup == null) return; var id = ids[_territoryIndex]; - if (!Service.Config.Timeline.TryGetValue(id, out var timeLine)) return; + if (!Service.Config.Timeline.TryGetValue(id, out var timeLine)) + { + Service.Config.Timeline[id] = timeLine = []; + } ImGui.Separator(); - if (ImGui.Selectable(UiString.ConfigWindow_Actions_Copy.Local())) + if (ImGui.Button(UiString.ConfigWindow_Actions_Copy.Local())) { var str = JsonConvert.SerializeObject(timeLine, Formatting.Indented); ImGui.SetClipboardText(str); @@ -686,12 +691,12 @@ private static void DrawTimeline() ImGui.SameLine(); - if (ImGui.Selectable(UiString.ActionSequencer_FromClipboard.Local())) + if (ImGui.Button(UiString.ActionSequencer_FromClipboard.Local())) { var str = ImGui.GetClipboardText(); try { - var set = JsonConvert.DeserializeObject>>(str, new ITimelineItemConverter())!; + var set = JsonConvert.DeserializeObject>>(str, new ITimelineItemConverter())!; Service.Config.Timeline[id] = timeLine = set; } catch (Exception ex) @@ -702,13 +707,15 @@ private static void DrawTimeline() _timelineGroup.ClearCollapsingHeader(); - foreach (var item in DataCenter.TimelineItems) + foreach (var item in RaidTimeUpdater.GetRaidTime((ushort)id)) { - _timelineGroup.AddCollapsingHeader(() => item.Name, () => + if (!item.IsShown) continue; + + _timelineGroup.AddCollapsingHeader(() =>$"{item.Name} ({item.Time} s)" , () => { - if(!timeLine.TryGetValue(item.Name, out var timeLineItems)) + if(!timeLine.TryGetValue(item.Time, out var timeLineItems)) { - timeLine[item.Name] = timeLineItems = []; + timeLine[item.Time] = timeLineItems = []; } AddButton(); @@ -781,6 +788,7 @@ void Down() else if (timeLineItem is StateTimelineItem stateItem) { var state = stateItem.State; + ImGui.SameLine(); if (ConditionDrawer.DrawByteEnum($"##AutoStatus{timeLineItem.GetHashCode()}", ref state)) { stateItem.State = state; @@ -813,7 +821,7 @@ void AddOneCondition() where T : ITimelineItem } }); } - + using var child = ImRaii.Child("Timeline Items Body", -Vector2.One); _timelineGroup.Draw(); } #endregion @@ -2573,7 +2581,7 @@ private static void DrawContentFinder(ContentFinderCondition? content) && IconSet.GetTexture(badge.Value, out var badgeTexture)) { var wholeWidth = ImGui.GetWindowWidth(); - var size = new Vector2(badgeTexture.Width, badgeTexture.Height) * MathF.Min(1, MathF.Min(320, wholeWidth) / badgeTexture.Width); + var size = new Vector2(badgeTexture.Width, badgeTexture.Height) * MathF.Min(1, MathF.Min(480, wholeWidth) / badgeTexture.Width); ImGuiHelper.DrawItemMiddle(() => { diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs index a32e4f8e2..0757dec40 100644 --- a/RotationSolver/Updaters/ActionUpdater.cs +++ b/RotationSolver/Updaters/ActionUpdater.cs @@ -152,6 +152,7 @@ private static void UpdateCombatTime() { foreach (var item in DataCenter.TimelineItems) { + if (item.Time < DataCenter.RaidTimeRaw) continue; if (item.Type is not TimelineType.InCombat) continue; item.UpdateRaidTimeOffset(); diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs index 8644bab11..3b1163ec8 100644 --- a/RotationSolver/Updaters/RaidTimeUpdater.cs +++ b/RotationSolver/Updaters/RaidTimeUpdater.cs @@ -8,6 +8,11 @@ internal static partial class RaidTimeUpdater { internal static readonly Dictionary _pathForRaids = new() { +#if DEBUG + { 296, "02-arr/trial/titan-ex.txt" }, + { 530, "03-hw/raid/a6s.txt" }, //For System log + { 748, "04-sb/raid/o5n.txt" }, //For Actor Control +#endif { 1148, "06-ew/raid/p9s.txt" }, { 1150, "06-ew/raid/p10s.txt" }, { 1152, "06-ew/raid/p11s.txt" }, @@ -32,7 +37,7 @@ internal static void UpdateTimeline() var time = item.Time - DataCenter.RaidTimeRaw; if (time < 0) continue; - if (!timeline.TryGetValue(item.Name, out var items)) continue; + if (!timeline.TryGetValue(item.Time, out var items)) continue; foreach (var item2 in items.OfType() .Where(i => !_addedItems.Any(added => added.Item2 == i))) @@ -57,11 +62,20 @@ internal static void Enable() { Svc.ClientState.TerritoryChanged += ClientState_TerritoryChanged; ClientState_TerritoryChanged(Svc.ClientState.TerritoryType); + Svc.DutyState.DutyWiped += DutyState_DutyWiped; + Svc.DutyState.DutyCompleted += DutyState_DutyWiped; + } + + private static void DutyState_DutyWiped(object? sender, ushort e) + { + DataCenter.RaidTimeRaw = -1; } internal static void Disable() { Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; + Svc.DutyState.DutyWiped -= DutyState_DutyWiped; + Svc.DutyState.DutyCompleted -= DutyState_DutyWiped; } private static async void ClientState_TerritoryChanged(ushort id) @@ -76,12 +90,29 @@ private static async void ClientState_TerritoryChanged(ushort id) } } + private static List _downloading = []; + public static TimelineItem[] GetRaidTime(ushort id) + { + if (_savedTimeLines.TryGetValue(id, out var value)) return value; + if (!_pathForRaids.TryGetValue(id, out var path)) return []; + if (_downloading.Contains(id)) return []; + + _downloading.Add(id); + Task.Run(async () => + { + _savedTimeLines[id] = await DownloadRaidTimeAsync(path); + _downloading.Remove(id); + }); + + return []; + } + static async Task GetRaidTimeAsync(ushort id) { if (_savedTimeLines.TryGetValue(id, out var value)) return value; if (_pathForRaids.TryGetValue(id, out var path)) { - return await DownloadRaidTimeAsync(path); + return _savedTimeLines[id] = await DownloadRaidTimeAsync(path); } return []; } @@ -104,15 +135,22 @@ static async Task DownloadRaidTimeAsync(string path) try { var timeline = timelineItem.Value; + var header = TimeHeader().Match(timeline).Value; + + if (string.IsNullOrEmpty(header)) continue; + var time = float.Parse(Time().Match(header).Value); var name = Name().Match(header).Value[1..^1]; - var item = JObject.Parse(ActionGetter().Match(timeline).Value); + var timelineStr = ActionGetter().Match(timeline).Value; + JObject? item = null; string[] ids = []; - if (item != null) + if (!string.IsNullOrEmpty(timelineStr)) { + item = JObject.Parse(timelineStr); + var id = item["id"]; if (id is JArray array) { @@ -125,7 +163,11 @@ static async Task DownloadRaidTimeAsync(string path) } var rest = timeline[header.Length..]; - var type = Type().Match(rest)?.Value[1..^1] ?? string.Empty; + var type = Type().Match(rest)?.Value ?? string.Empty; + if (type.Length > 3) + { + type = type[1..^2].Split(' ').LastOrDefault() ?? string.Empty; + } result.Add(new (time, name, type, ids, item)); } @@ -144,19 +186,19 @@ static async Task DownloadRaidTimeAsync(string path) return [..result]; } - [GeneratedRegex(" .*? ")] + [GeneratedRegex(" .*? {")] private static partial Regex Type(); - [GeneratedRegex("\\d+\\.\\d.*")] + [GeneratedRegex("[\\d\\.]+.*")] private static partial Regex TimeLineItem(); - [GeneratedRegex("^\\d+\\.\\d")] + [GeneratedRegex("^[\\d\\.]+")] private static partial Regex Time(); [GeneratedRegex("\".*?\"")] private static partial Regex Name(); - [GeneratedRegex("^\\d+\\.\\d \".*?\"")] + [GeneratedRegex("^[\\d\\.]+ \".*?\"")] private static partial Regex TimeHeader(); [GeneratedRegex("{.*}")] diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs index e724f9706..da4f0e362 100644 --- a/RotationSolver/Updaters/SocialUpdater.cs +++ b/RotationSolver/Updaters/SocialUpdater.cs @@ -81,8 +81,6 @@ static async void DutyState_DutyCompleted(object? sender, ushort e) } } - - static Type[]? _dutyRotations = null; static void ClientState_TerritoryChanged(ushort id) { DataCenter.ResetAllRecords(); @@ -96,13 +94,6 @@ static void ClientState_TerritoryChanged(ushort id) Svc.Log.Debug($"Move to {DataCenter.TerritoryName} ({id})"); #endif - _dutyRotations ??= [..RotationUpdater.TryGetTypes(typeof(SocialUpdater).Assembly) - .Where(t => t.IsAssignableTo(typeof(DutyRotation)) && !t.IsAbstract)]; - - var nowRotationType = _dutyRotations.FirstOrDefault(r => r.GetCustomAttribute()?.TerritoryIds.Contains(id) ?? false); - - DataCenter.RightNowDutyRotation = nowRotationType == null ? null : Activator.CreateInstance(nowRotationType) as DutyRotation; - try { DataCenter.RightNowRotation?.OnTerritoryChanged(); diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs index f9a80fa06..8025521d2 100644 --- a/RotationSolver/Updaters/StateUpdater.cs +++ b/RotationSolver/Updaters/StateUpdater.cs @@ -38,7 +38,7 @@ private static AutoStatus StatusFromTimeline() var time = item.Time - DataCenter.RaidTimeRaw; if (time < 0) continue; - if (!timeline.TryGetValue(item.Name, out var items)) continue; + if (!timeline.TryGetValue(item.Time, out var items)) continue; foreach (var item2 in items.OfType()) { diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs index 314d90d4b..a2f69c1c1 100644 --- a/RotationSolver/Updaters/TargetUpdater.cs +++ b/RotationSolver/Updaters/TargetUpdater.cs @@ -39,6 +39,7 @@ private static void UpdateCastingRefine(IEnumerable allTargets) foreach (var item in DataCenter.TimelineItems) { + if (item.Time < DataCenter.RaidTimeRaw) continue; if (item.Type is not TimelineType.StartsUsing) continue; if (!item.IsIdMatched(b.CastActionId)) continue; diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index a9e03a4ab..62c1fb8ec 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -10,6 +10,7 @@ using RotationSolver.Basic.Configuration; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using static Dalamud.Interface.Utility.Raii.ImRaii; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; namespace RotationSolver; @@ -72,6 +73,7 @@ private static void Chat_ChatMessage(Dalamud.Game.Text.XivChatType type, uint se { foreach (var item in DataCenter.TimelineItems) { + if (item.Time < DataCenter.RaidTimeRaw) continue; if (item.Type is not TimelineType.GameLog) continue; var typeString = ((uint)type).ToString("X4"); @@ -94,42 +96,87 @@ private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint case OpCode.SystemLogMessage: OnSystemLogMessage(dataPtr); break; - - case OpCode.ActorControl: - OnActorControl(dataPtr); - break; + //case OpCode.ActorControlTarget: + // var bytes = new byte[32]; + // Marshal.Copy(dataPtr, bytes, 0, 32); + // Svc.Log.Debug("ActorControlTarget: " + HexString(bytes)); + // break; + //case OpCode.ActorControlSelf: + // bytes = new byte[32]; + // Marshal.Copy(dataPtr, bytes, 0, 32); + // Svc.Log.Debug("ActorControlSelf: " + HexString(bytes)); + // break; + //case OpCode.ActorControl: + // OnActorControl(dataPtr); + // break; } - -//#if DEBUG -// var source = Svc.Objects.SearchById(sourceActorId)?.Name.TextValue; -// var target = Svc.Objects.SearchById(targetActorId)?.Name.TextValue; -// Svc.Log.Debug($"From {source} to {target} by {op}."); -//#endif } - private static void OnActorControl(IntPtr dataPtr) + //private static void OnActorControl(IntPtr dataPtr) + //{ + // foreach (var item in DataCenter.TimelineItems) + // { + // if (item.Time < DataCenter.RaidTimeRaw) continue; + // if (item.Type is not TimelineType.ActorControl) continue; + // //if (!item.IsIdMatched(ReadNumber(dataPtr, 4))) continue; + + // //var param1 = item["param1"]; + // //if (!string.IsNullOrEmpty(param1)) + // //{ + // // if (!new Regex(param1).IsMatch(ReadNumber(dataPtr, 12).ToString("X"))) + // // { + // // continue; + // // } + // //} + // //item.UpdateRaidTimeOffset(); + // break; + // } + + // var bytes = new byte[32]; + // Marshal.Copy(dataPtr, bytes, 0, 32); + // Svc.Log.Debug("ActorControl: " + HexString(bytes)); + //} + + private static void OnSystemLogMessage(IntPtr dataPtr) { - var bytes = new byte[32]; - Marshal.Copy(dataPtr, bytes, 0, 32); - Svc.Log.Debug("ActorControl: " + Convert.ToHexString(bytes)); + foreach (var item in DataCenter.TimelineItems) + { + if (item.Time < DataCenter.RaidTimeRaw) continue; + if (item.Type is not TimelineType.SystemLogMessage) continue; + if (!item.IsIdMatched(ReadNumber(dataPtr, 4))) continue; - //TODO: the refine to the raidtime. + var param1 = item["param1"]; + if (!string.IsNullOrEmpty(param1)) + { + if(!new Regex(param1).IsMatch(ReadNumber(dataPtr, 12).ToString("X"))) + { + continue; + } + } + item.UpdateRaidTimeOffset(); + break; + } } - private static void OnSystemLogMessage(IntPtr dataPtr) + private unsafe static uint ReadNumber(IntPtr dataPtr, int offset) + { + return *(uint*)(dataPtr + offset); + } + + private static string HexString(byte[] bytes) { - var bytes = new byte[32]; - Marshal.Copy(dataPtr, bytes, 0, 32); - Svc.Log.Debug("SystemLogMessage: " + Convert.ToHexString(bytes)); - - //TODO: the refine to the raidtime. - - //if (logId == DataIds.SystemLogPomanderUsed) - // OnPomanderUsed((Pomander)Marshal.ReadByte(dataPtr, 16)); - //else if (logId == DataIds.SystemLogDutyEnded) - // ExitDeepDungeon(); - //else if (logId == DataIds.SystemLogTransferenceInitiated) - // nextFloorTransfer = true; + var str = Convert.ToHexString(bytes); + + string result = string.Empty; + for (int i = 0; i < str.Length; i++) + { + if (i % 4 == 0) + { + result += " "; + } + result += str[i]; + } + return result; } public static void Disable() @@ -225,6 +272,7 @@ private static void ActionFromEnemy(ActionEffectSet set) { foreach (var item in DataCenter.TimelineItems) { + if (item.Time < DataCenter.RaidTimeRaw) continue; if (item.Type is not TimelineType.Ability) continue; if (!item.IsIdMatched(set.Action?.RowId ?? 0)) continue; From 06e30792cfb0284a8db2484c657f8b2cb352e9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Wed, 28 Feb 2024 22:13:54 +0800 Subject: [PATCH 47/48] chore: 4.1.0 released. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9796471a9..45d36c5c4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net7.0-windows enable ArchiTed - 4.0.3.5 + 4.1.0 x64 AnyCPU latest From 6146e87d27bbeefb36d096b91a572c769d780863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <53346444+ArchiDog1998@users.noreply.github.com> Date: Wed, 28 Feb 2024 22:16:26 +0800 Subject: [PATCH 48/48] fix: default ttu. --- RotationSolver.Basic/Actions/ActionSetting.cs | 1 - RotationSolver.Basic/Actions/BaseAction.cs | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RotationSolver.Basic/Actions/ActionSetting.cs b/RotationSolver.Basic/Actions/ActionSetting.cs index 1c80ef255..d91e15d9b 100644 --- a/RotationSolver.Basic/Actions/ActionSetting.cs +++ b/RotationSolver.Basic/Actions/ActionSetting.cs @@ -111,5 +111,4 @@ public TargetType TargetType /// Should end the special. /// public bool EndSpecial { get; set; } - } diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index ec5d67109..056960e59 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -88,6 +88,10 @@ public ActionConfig Config { value.TimeToKill = 10; } + if (value.TimeToUntargetable == 0) + { + value.TimeToUntargetable = value.TimeToKill; + } } return value; }