Skip to content

Commit 12be1ba

Browse files
authored
Merge pull request #1137 from FFXIV-CombatReborn/TargetOverrides
Improve VFX detection, updates to RDM, NIN, MCH, and GNB
2 parents ac156d9 + 13a7b5e commit 12be1ba

File tree

10 files changed

+156
-81
lines changed

10 files changed

+156
-81
lines changed

RotationSolver.Basic/Configuration/Configs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ public const string
410410
[ConditionBool, UI("Show Cooldown Window", Filter = UiWindows)]
411411
private static readonly bool _showCooldownWindow = false;
412412

413-
[ConditionBool, UI("Show Action Timeline Window (currently bugged)", Filter = UiWindows)]
413+
[ConditionBool, UI("Show Action Timeline Window", Filter = UiWindows)]
414414
private static readonly bool _showActionTimelineWindow = false;
415415

416416
[ConditionBool, UI("Only show timeline in combat", Parent = nameof(ShowActionTimelineWindow))]

RotationSolver.Basic/DataCenter.cs

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using RotationSolver.Basic.Configuration.Conditions;
1515
using RotationSolver.Basic.Rotations.Duties;
1616
using System.Collections.Concurrent;
17+
using System.Collections.Frozen;
1718
using Action = Lumina.Excel.Sheets.Action;
1819
using CharacterManager = FFXIVClientStructs.FFXIV.Client.Game.Character.CharacterManager;
1920
using CombatRole = RotationSolver.Basic.Data.CombatRole;
@@ -1244,9 +1245,44 @@ public static bool IsPhysicalDamageIncoming()
12441245
return false;
12451246
}
12461247

1248+
// Cached, case-insensitive path sets modeled after WrathCombo VFX.cs
1249+
private static readonly FrozenSet<string> TankbusterPaths = FrozenSet.ToFrozenSet(
1250+
[
1251+
"vfx/lockon/eff/tank", // Generic TB check
1252+
"vfx/lockon/eff/x6fe_fan100_50_0t1", // Necron Blue Shockwave - Cone Tankbuster
1253+
"vfx/common/eff/mon_eisyo03t", // M10 Deep Impact AoE TB (also generic?)
1254+
"vfx/lockon/eff/m0676trg_tw_d0t1p", // M10 Hot Impact shared TB
1255+
"vfx/lockon/eff/m0676trg_tw_s6_d0t1p", // M11 Raw Steel
1256+
"vfx/lockon/eff/z6r2b3_8sec_lockon_c0a1",// Kam'lanaut Princely Blow
1257+
"vfx/lockon/eff/m0742trg_b1t1", // M7 Abominable Blink
1258+
"vfx/lockon/eff/x6r9_tank_lockonae" // M9 Hardcore Large TB
1259+
], StringComparer.OrdinalIgnoreCase);
1260+
1261+
private static readonly FrozenSet<string> MultiHitSharedPaths = FrozenSet.ToFrozenSet(
1262+
[
1263+
"vfx/lockon/eff/com_share4a1",
1264+
"vfx/lockon/eff/com_share5a1",
1265+
"vfx/lockon/eff/com_share6m7s_1v",
1266+
"vfx/lockon/eff/com_share8s_0v",
1267+
"vfx/lockon/eff/share_laser_5s_c0w", // Line
1268+
"vfx/lockon/eff/share_laser_8s_c0g", // Line
1269+
"vfx/lockon/eff/m0922trg_t2w"
1270+
], StringComparer.OrdinalIgnoreCase);
1271+
1272+
private static readonly FrozenSet<string> SharedDamagePaths = FrozenSet.ToFrozenSet(
1273+
[
1274+
"vfx/lockon/eff/coshare",
1275+
"vfx/lockon/eff/share_laser",
1276+
"vfx/lockon/eff/com_share",
1277+
// Duty-specific AOE share markers
1278+
"vfx/monster/gimmick2/eff/z3o7_b1_g06c0t", // Puppet's Bunker, Superior Flight Unit.
1279+
"vfx/monster/gimmick4/eff/z5r1_b4_g09c0c" // Aglaia, Nald'thal
1280+
], StringComparer.OrdinalIgnoreCase);
1281+
1282+
private static readonly StringComparison PathCmp = StringComparison.OrdinalIgnoreCase;
12471283

12481284
public static bool IsHostileCastingAOE =>
1249-
InCombat && (IsCastingAreaVfx() || (AllHostileTargets != null && IsAnyHostileCastingArea()));
1285+
InCombat && (IsCastingAreaVfx() || (AllHostileTargets != null && IsAnyHostileCastingArea()));
12501286

12511287
private static bool IsAnyHostileCastingArea()
12521288
{
@@ -1392,64 +1428,99 @@ public static bool IsCastingVfx(ConcurrentQueue<VfxNewData> vfxData, Func<VfxNew
13921428
return false;
13931429
}
13941430

1431+
// Improved multi-hit detection using a cached set of known multi-hit share paths
13951432
public static bool IsCastingMultiHit()
13961433
{
13971434
return IsCastingVfx(VfxDataQueue, s =>
13981435
{
1399-
if (!Player.Available)
1436+
if (!Player.Available || Player.Object == null)
14001437
{
14011438
return false;
14021439
}
14031440

1404-
// For x6fe, ignore target and player role checks.
1405-
if (s.Path.StartsWith("vfx/lockon/eff/com_share5a1"))
1441+
if (string.IsNullOrEmpty(s.Path))
14061442
{
1407-
return true;
1443+
return false;
14081444
}
14091445

1410-
if (s.Path.StartsWith("vfx/lockon/eff/m0922trg_t2w"))
1446+
// Any path in multi-hit share list qualifies
1447+
foreach (var p in MultiHitSharedPaths)
14111448
{
1412-
return true;
1449+
if (s.Path.StartsWith(p, PathCmp))
1450+
{
1451+
return true;
1452+
}
14131453
}
14141454

14151455
return false;
14161456
});
14171457
}
14181458

1459+
// Improved tank VFX detection: recognizes generic and specific TB paths, plus preserves original tank lock-on checks
14191460
public static bool IsCastingTankVfx()
14201461
{
14211462
return IsCastingVfx(VfxDataQueue, s =>
14221463
{
1423-
if (!Player.Available)
1464+
if (!Player.Available || Player.Object == null)
14241465
{
14251466
return false;
14261467
}
14271468

1428-
if (Player.Object == null)
1469+
if (string.IsNullOrEmpty(s.Path))
14291470
{
14301471
return false;
14311472
}
14321473

1433-
// For x6fe, ignore target and player role checks.
1434-
if (s.Path.StartsWith("vfx/lockon/eff/x6fe"))
1474+
// Specific tankbuster effect paths (priority)
1475+
foreach (var p in TankbusterPaths)
14351476
{
1436-
return true;
1477+
if (s.Path.StartsWith(p, PathCmp))
1478+
{
1479+
return true;
1480+
}
14371481
}
14381482

14391483
// Preserve original checks for other tank lock-on effects.
14401484
return (!TargetFilter.PlayerJobCategory(JobRole.Tank) || s.ObjectId == Player.Object.GameObjectId)
1441-
&& (s.Path.StartsWith("vfx/lockon/eff/tank_lockon")
1442-
|| s.Path.StartsWith("vfx/lockon/eff/tank_laser"));
1485+
&& (s.Path.StartsWith("vfx/lockon/eff/tank_lockon", PathCmp)
1486+
|| s.Path.StartsWith("vfx/lockon/eff/tank_laser", PathCmp));
14431487
});
14441488
}
14451489

1490+
// Improved shared AOE detection using cached sets, covers both regular and multi-hit stack markers
14461491
public static bool IsCastingAreaVfx()
14471492
{
14481493
return IsCastingVfx(VfxDataQueue, s =>
14491494
{
1450-
return Player.Available && (s.Path.StartsWith("vfx/lockon/eff/coshare")
1451-
|| s.Path.StartsWith("vfx/lockon/eff/share_laser")
1452-
|| s.Path.StartsWith("vfx/lockon/eff/com_share"));
1495+
if (!Player.Available || Player.Object == null)
1496+
{
1497+
return false;
1498+
}
1499+
1500+
if (string.IsNullOrEmpty(s.Path))
1501+
{
1502+
return false;
1503+
}
1504+
1505+
// Multi-hit markers (treated as area/stack mechanics)
1506+
foreach (var p in MultiHitSharedPaths)
1507+
{
1508+
if (s.Path.StartsWith(p, PathCmp))
1509+
{
1510+
return true;
1511+
}
1512+
}
1513+
1514+
// Regular shared markers
1515+
foreach (var p in SharedDamagePaths)
1516+
{
1517+
if (s.Path.StartsWith(p, PathCmp))
1518+
{
1519+
return true;
1520+
}
1521+
}
1522+
1523+
return false;
14531524
});
14541525
}
14551526

RotationSolver.Basic/RotationSolver.Basic.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383

8484
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
8585

86-
<PackageReference Include="System.Drawing.Common" Version="10.0.1" />
86+
<PackageReference Include="System.Drawing.Common" Version="10.0.2" />
8787
<ProjectReference Include="..\RotationSolver.SourceGenerators\RotationSolver.SourceGenerators.csproj" OutputItemType="Analyzer" ExcludeAssets="All" />
8888

8989
<None Include="..\COPYING.LESSER">

RotationSolver.Basic/packages.lock.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
},
3131
"System.Drawing.Common": {
3232
"type": "Direct",
33-
"requested": "[10.0.1, )",
34-
"resolved": "10.0.1",
35-
"contentHash": "cmkIduwyVPfuO8kNehu3xNcjo26nMgUoJyfso9S/zTO85YovvPPG0/AFLjanx1iVgQ80OpJVnLb1jU3aIKNLdg==",
33+
"requested": "[10.0.2, )",
34+
"resolved": "10.0.2",
35+
"contentHash": "nCq8jeGD4vWaUjX3nGaDMKYqfFPposw2f4KCFnuY5Rt3NhK6zMZ/c0a52a3TZK/nnkCJfyQMwb+POKwAnZexZw==",
3636
"dependencies": {
37-
"Microsoft.Win32.SystemEvents": "10.0.1"
37+
"Microsoft.Win32.SystemEvents": "10.0.2"
3838
}
3939
},
4040
"ExCSS": {
@@ -57,8 +57,8 @@
5757
},
5858
"Microsoft.Win32.SystemEvents": {
5959
"type": "Transitive",
60-
"resolved": "10.0.1",
61-
"contentHash": "trU4LN2C04nj5qnl8HqoX0NJGPCFg9h2f5o+jjp3Xm8c8fQtu6Ga1/fNFMf3AapoHyLP1WlP21XnL2ij/TLKlg=="
60+
"resolved": "10.0.2",
61+
"contentHash": "1G4BdeFwc8E95MpwtR7pO4rEh2YCG/o5gqcOszdQRGrWX1ayHTZtho3xPuYZTbBxKb/3kQHjL3b4nQeNGvbM9g=="
6262
},
6363
"rotationsolver.sourcegenerators": {
6464
"type": "Project",

RotationSolver/RebornRotations/Magical/RDM_Reborn.cs

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -295,39 +295,38 @@ protected override bool RaiseGCD(out IAction? act)
295295

296296
protected override bool GeneralGCD(out IAction? act)
297297
{
298-
if (ManaStacks == 3)
299-
{
300-
// Prefer Verholy if BlackMana > WhiteMana and can't use VerStone
301-
if (BlackMana > WhiteMana && !CanVerStone && VerholyPvE.CanUse(out act))
302-
return true;
303-
304-
// Prefer Verflare if WhiteMana > BlackMana and can't use VerFire
305-
if (WhiteMana > BlackMana && !CanVerFire && VerflarePvE.CanUse(out act))
306-
return true;
298+
if (ManaStacks == 3)
299+
{
300+
int diff = BlackMana - WhiteMana;
301+
int gap = Math.Abs(diff);
307302

308-
// Fallbacks: try Verflare, then Verholy
309-
if (!CanVerFire && VerflarePvE.CanUse(out act))
310-
return true;
303+
bool forceBalance = HasEmbolden || gap >= 19;
311304

312-
if (!CanVerStone && VerholyPvE.CanUse(out act))
313-
return true;
314-
315-
// Prefer Verholy if BlackMana > WhiteMana
316-
if (BlackMana > WhiteMana && VerholyPvE.CanUse(out act))
317-
return true;
305+
if (forceBalance)
306+
{
307+
// Balance first
308+
if (diff > 0 && VerholyPvE.CanUse(out act)) return true; // Black leads -> add White
309+
if (diff < 0 && VerflarePvE.CanUse(out act)) return true; // White leads -> add Black
310+
}
311+
else
312+
{
313+
// Slight imbalance: proc-aware preference to avoid overwriting existing procs
314+
if (CanVerFire && VerholyPvE.CanUse(out act)) return true;
315+
if (CanVerStone && VerflarePvE.CanUse(out act)) return true;
316+
}
318317

319-
// Prefer Verflare if WhiteMana > BlackMana
320-
if (WhiteMana > BlackMana && VerflarePvE.CanUse(out act))
321-
return true;
318+
// Fallbacks
319+
if (diff > 0 && VerholyPvE.CanUse(out act)) return true;
320+
if (diff < 0 && VerflarePvE.CanUse(out act)) return true;
322321

323-
if (VerflarePvE.CanUse(out act))
324-
return true;
322+
if (CanVerFire && !CanVerStone && VerholyPvE.CanUse(out act)) return true;
323+
if (CanVerStone && !CanVerFire && VerflarePvE.CanUse(out act)) return true;
325324

326-
if (VerholyPvE.CanUse(out act))
327-
return true;
328-
}
325+
if (VerholyPvE.CanUse(out act)) return true;
326+
if (VerflarePvE.CanUse(out act)) return true;
327+
}
329328

330-
if (CanInstantCast && !CanVerEither)
329+
if (CanInstantCast && !CanVerEither)
331330
{
332331
if (ScatterPvE.CanUse(out act))
333332
{
@@ -398,7 +397,7 @@ protected override bool GeneralGCD(out IAction? act)
398397
//Check if you can start melee combo
399398
if (EnoughMana)
400399
{
401-
if (!IsLastGCD(true, EnchantedRipostePvE_45960) && ((HasEmbolden && CanMagickedSwordplay) || StatusHelper.PlayerWillStatusEndGCD(4, 0, true, StatusID.MagickedSwordplay)) && EnchantedRipostePvE_45960.CanUse(out act))
400+
if (EnchantedRipostePvE.Config.IsEnabled && !IsLastGCD(true, EnchantedRipostePvE_45960) && ((HasEmbolden && CanMagickedSwordplay) || StatusHelper.PlayerWillStatusEndGCD(4, 0, true, StatusID.MagickedSwordplay)) && EnchantedRipostePvE_45960.CanUse(out act))
402401
{
403402
return true;
404403
}

RotationSolver/RebornRotations/Melee/NIN_Reborn.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ public sealed class NIN_Reborn : NinjaRotation
1717
[RotationConfig(CombatType.PvE, Name = "Use Mudras outside of combat when enemies are near")]
1818
public bool CombatMudra { get; set; } = true;
1919

20-
[RotationConfig(CombatType.PvE, Name = "Use both stacks of Mudras")]
20+
[RotationConfig(CombatType.PvE, Name = "Use both stacks of Mudras")]
2121
public bool BurnMudraStacks { get; set; } = false;
2222

2323
[RotationConfig(CombatType.PvE, Name = "Use Forked Raiju instead of Fleeting Raiju if you are outside of range (Dangerous)")]
2424
public bool ForkedUse { get; set; } = false;
25-
#endregion
25+
#endregion
2626

27-
#region Tracking Properties
28-
// Properties to track RabbitMediumPvE failures and related information.
29-
//private int _rabbitMediumFailures = GetActionUsageCount((uint)ActionID.RabbitMediumPvE);
30-
private IBaseAction? _lastNinActionAim = null;
27+
#region Tracking Properties
28+
// Properties to track RabbitMediumPvE failures and related information.
29+
//private int _rabbitMediumFailures = GetActionUsageCount((uint)ActionID.RabbitMediumPvE);
30+
private IBaseAction? _lastNinActionAim = null;
3131
// Holds the next ninjutsu action to perform.
3232
private IBaseAction? _ninActionAim = null;
3333
private readonly ActionID NinjutsuPvEid = AdjustId(ActionID.NinjutsuPvE);
@@ -123,7 +123,7 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
123123
}
124124

125125
// Side-effect: decide/refresh ninjutsu aim; do not consume the oGCD slot here.
126-
if (InCombat || (CombatMudra && HasHostilesInMaxRange && TenPvE.Cooldown.CurrentCharges == TenPvE.Cooldown.MaxCharges))
126+
if ((InCombat && HasHostilesInMaxRange) || (CombatMudra && HasHostilesInMaxRange && TenPvE.Cooldown.CurrentCharges == TenPvE.Cooldown.MaxCharges))
127127
{
128128
_ = ChoiceNinjutsu(out _);
129129
}

RotationSolver/RebornRotations/Ranged/MCH_Reborn.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,17 @@ public sealed class MCH_Reborn : MachinistRotation
3030
return act;
3131
}
3232

33-
if (remainTime < 4.8f && ReassemblePvE.CanUse(out act))
33+
if (remainTime < 4.75f && ReassemblePvE.CanUse(out act))
3434
{
3535
return act;
3636
}
3737

38-
if (IsBurst && OpenerBurstMeds && remainTime <= 1f && UseBurstMedicine(out act))
38+
if (AirAnchorCountdown && IsBurst && OpenerBurstMeds && remainTime <= 1.5f && UseBurstMedicine(out act))
39+
{
40+
return act;
41+
}
42+
43+
if (!AirAnchorCountdown && IsBurst && OpenerBurstMeds && remainTime <= 1f && UseBurstMedicine(out act))
3944
{
4045
return act;
4146
}

RotationSolver/RebornRotations/Tank/GNB_Reborn.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ public sealed class GNB_Reborn : GunbreakerRotation
2929
#region oGCD Logic
3030
protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
3131
{
32-
33-
if (AbdomenTearPvE.CanUse(out act))
32+
if (JugularRipPvE.CanUse(out act))
33+
{
34+
return true;
35+
}
36+
37+
if (AbdomenTearPvE.CanUse(out act))
3438
{
3539
return true;
3640
}
@@ -40,12 +44,12 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
4044
return true;
4145
}
4246

43-
if (FatedBrandPvE.CanUse(out act))
44-
{
45-
return true;
46-
}
47+
if (HypervelocityPvE.CanUse(out act))
48+
{
49+
return true;
50+
}
4751

48-
if (HypervelocityPvE.CanUse(out act))
52+
if (FatedBrandPvE.CanUse(out act))
4953
{
5054
return true;
5155
}
@@ -200,11 +204,6 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act)
200204
return base.AttackAbility(nextGCD, out act);
201205
}
202206

203-
if (JugularRipPvE.CanUse(out act))
204-
{
205-
return true;
206-
}
207-
208207
if (DangerZonePvE.CanUse(out act) && !DoubleDownPvE.EnoughLevel)
209208
{
210209

RotationSolver/UI/RotationConfigWindow.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ private void DrawDiagnosticInfoCube()
417417
_ = diagInfo.AppendLine($"Update Frequency: {Service.Config.MinUpdatingTime}");
418418
_ = diagInfo.AppendLine($"Intercept: {Service.Config.InterceptAction2}");
419419
_ = diagInfo.AppendLine($"Player Level: {DataCenter.PlayerSyncedLevel()}");
420-
_ = diagInfo.AppendLine($"Player Job: {Player.Job}");
420+
_ = diagInfo.AppendLine($"Rotation Name: {_curRotationAttribute?.Name ?? string.Empty}");
421+
_ = diagInfo.AppendLine($"Player Job: {Player.Job}");
421422
_ = diagInfo.AppendLine($"AutoFaceTargetOnActionSetting: {DataCenter.AutoFaceTargetOnActionSetting()}");
422423
var moveModeValue = DataCenter.MoveModeSetting();
423424
string moveModeText = moveModeValue switch

0 commit comments

Comments
 (0)