Skip to content

Commit

Permalink
Merge pull request #462 from FFXIV-CombatReborn/performance-and-caching
Browse files Browse the repository at this point in the history
Rework target acquisition so it caches objects per frame
  • Loading branch information
LTS-FFXIV authored Dec 4, 2024
2 parents 483a9a8 + cb4f43d commit e3fd696
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 179 deletions.
175 changes: 9 additions & 166 deletions RotationSolver.Basic/DataCenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,181 +317,24 @@ internal static float RaidTimeRaw
}
}

public unsafe static IBattleChara[] PartyMembers => AllianceMembers.Where(ObjectHelper.IsParty)
.Where(b => b.Character()->CharacterData.OnlineStatus != 15 && b.IsTargetable).ToArray();
public static List<IBattleChara> PartyMembers { get; set; } = [];

public unsafe static IBattleChara[] AllianceMembers => AllTargets.Where(ObjectHelper.IsAlliance)
.Where(b => b.Character()->CharacterData.OnlineStatus != 15 && b.IsTargetable).ToArray();
public static List<IBattleChara> AllianceMembers { get; set; } = [];

public unsafe static IBattleChara[] FriendlyNPCMembers
{
get
{
// Check if the configuration setting is true
if (!Service.Config.FriendlyBattleNpcHeal && !Service.Config.FriendlyPartyNpcHealRaise2)
{
return Array.Empty<IBattleChara>();
}

try
{
// Ensure Svc.Objects is not null
if (Svc.Objects == null)
{
return Array.Empty<IBattleChara>();
}

// Filter and cast objects safely
var friendlyNpcs = Svc.Objects
.Where(obj => obj != null && obj.ObjectKind == ObjectKind.BattleNpc)
.Where(obj =>
{
try
{
return obj.GetNameplateKind() == NameplateKind.FriendlyBattleNPC ||
obj.GetBattleNPCSubKind() == BattleNpcSubKind.NpcPartyMember;
}
catch (Exception ex)
{
// Log the exception for debugging purposes
Svc.Log.Error($"Error filtering object in get_FriendlyNPCMembers: {ex.Message}");
return false;
}
})
.OfType<IBattleChara>()
.ToArray();

return friendlyNpcs;
}
catch (Exception ex)
{
// Log the exception for debugging purposes
Svc.Log.Error($"Error in get_FriendlyNPCMembers: {ex.Message}");
return Array.Empty<IBattleChara>();
}
}
}


public static IBattleChara[] AllHostileTargets
{
get
{
var strongOfShieldPositional = EnemyPositional.Front;

return AllTargets.Where(b =>
{
// Check if the target is an enemy and targetable.
if (!b.IsEnemy() || !b.IsTargetable) return false;

// Check if the target is invincible.
if (b.StatusList.Any(StatusHelper.IsInvincible)) return false;

// Special exception for Jeuno raid Ark Angels.
if (b.IsWrongEpicFatedVaunted()) return false;

// Special exception for the Strong of Shield status on Hansel and Gretel.
if (b.HasStatus(true, StatusID.StrongOfShield) && strongOfShieldPositional != b.FindEnemyPositional()) return false;

// If all checks pass, the target is considered hostile.
return true;
}).ToArray();
}
}

public static IBattleChara? InterruptTarget =>
AllHostileTargets.FirstOrDefault(ObjectHelper.CanInterrupt);

public static IBattleChara? ProvokeTarget => AllHostileTargets.FirstOrDefault(ObjectHelper.CanProvoke);

public static IBattleChara? DeathTarget
{
get
{
// Added so it only tracks deathtarget if you are on a raise job
var rotation = DataCenter.RightNowRotation;
if (Player.Job == Job.WHM || Player.Job == Job.SCH || Player.Job == Job.AST || Player.Job == Job.SGE ||
Player.Job == Job.SMN || Player.Job == Job.RDM)
{
// Ensure AllianceMembers and PartyMembers are not null
if (AllianceMembers == null || PartyMembers == null) return null;

var deathAll = AllianceMembers.GetDeath();
var deathParty = PartyMembers.GetDeath();
var deathNPC = FriendlyNPCMembers.GetDeath();

// Check death in party members
if (deathParty.Any())
{
var deathT = deathParty.GetJobCategory(JobRole.Tank).ToList();
var deathH = deathParty.GetJobCategory(JobRole.Healer).ToList();

if (deathT.Count > 1) return deathT.FirstOrDefault();
if (deathH.Any()) return deathH.FirstOrDefault();
if (deathT.Any()) return deathT.FirstOrDefault();
public static List<IBattleChara> FriendlyNPCMembers { get; set; } = [];

return deathParty.FirstOrDefault();
}

// Check death in alliance members
if (deathAll.Any())
{
if (Service.Config.RaiseType == RaiseType.PartyAndAllianceHealers)
{
var deathAllH = deathAll.GetJobCategory(JobRole.Healer).ToList();
if (deathAllH.Any()) return deathAllH.FirstOrDefault();
}

if (Service.Config.RaiseType == RaiseType.PartyAndAlliance)
{
var deathAllH = deathAll.GetJobCategory(JobRole.Healer).ToList();
var deathAllT = deathAll.GetJobCategory(JobRole.Tank).ToList();

if (deathAllH.Any()) return deathAllH.FirstOrDefault();
if (deathAllT.Any()) return deathAllT.FirstOrDefault();

return deathAll.FirstOrDefault();
}
}
public static List<IBattleChara> AllHostileTargets { get; set; } = [];

// Check death in friendly NPC members
if (deathNPC.Any() && Service.Config.FriendlyPartyNpcHealRaise2)
{
var deathNPCT = deathNPC.GetJobCategory(JobRole.Tank).ToList();
var deathNPCH = deathNPC.GetJobCategory(JobRole.Healer).ToList();
public static IBattleChara? InterruptTarget { get; set; }

if (deathNPCT.Count > 1) return deathNPCT.FirstOrDefault();
if (deathNPCH.Any()) return deathNPCH.FirstOrDefault();
if (deathNPCT.Any()) return deathNPCT.FirstOrDefault();
public static IBattleChara? ProvokeTarget { get; set; }

return deathNPC.FirstOrDefault();
}

return null;
}
return null;
}
}
public static IBattleChara? DeathTarget { get; set; }

public static IBattleChara? DispelTarget
{
get
{
var weakenPeople = DataCenter.PartyMembers?
.Where(o => o is IBattleChara b && b.StatusList != null && b.StatusList.Any(status => status != null && StatusHelper.CanDispel(status))) ?? Enumerable.Empty<IBattleChara>();
var weakenNPC = DataCenter.FriendlyNPCMembers?
.Where(o => o is IBattleChara b && b.StatusList != null && b.StatusList.Any(status => status != null && StatusHelper.CanDispel(status))) ?? Enumerable.Empty<IBattleChara>();
var dyingPeople = weakenPeople
.Where(o => o is IBattleChara b && b.StatusList != null && b.StatusList.Any(status => status != null && StatusHelper.IsDangerous(status)));

return dyingPeople.OrderBy(ObjectHelper.DistanceToPlayer).FirstOrDefault()
?? weakenPeople.OrderBy(ObjectHelper.DistanceToPlayer).FirstOrDefault()
?? weakenNPC.OrderBy(ObjectHelper.DistanceToPlayer).FirstOrDefault();
}
}
public static IBattleChara? DispelTarget { get; set; }

public static IBattleChara[] AllTargets => Svc.Objects.OfType<IBattleChara>().GetObjectInRadius(30)
.Where(o => !o.IsDummy() || !Service.Config.DisableTargetDummys).ToArray();
public static List<IBattleChara> AllTargets { get; set; } = [];

public static ulong[] TreasureCharas
{
Expand Down
8 changes: 4 additions & 4 deletions RotationSolver.Basic/Helpers/ObjectHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@ internal static bool IsAttackable(this IBattleChara battleChara)
{
TargetHostileType.AllTargetsCanAttack => true,
TargetHostileType.TargetsHaveTarget => battleChara.TargetObject is IBattleChara,
TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Length < 2 || battleChara.TargetObject is IBattleChara,
TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Length < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56]))
TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Count() < 2 || battleChara.TargetObject is IBattleChara,
TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Count() < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56]))
|| battleChara.TargetObject is IBattleChara,
TargetHostileType.TargetIsInEnemiesList => battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => (DataCenter.PartyMembers.Length < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
TargetHostileType.AllTargetsWhenSoloInDutyTargetIsInEnemiesList => DataCenter.PartyMembers.Length < 2 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => (DataCenter.PartyMembers.Count() < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
TargetHostileType.AllTargetsWhenSoloInDutyTargetIsInEnemiesList => DataCenter.PartyMembers.Count() < 2 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
_ => true,
};
}
Expand Down
6 changes: 3 additions & 3 deletions RotationSolver/UI/RotationConfigWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2671,8 +2671,8 @@ private static unsafe void DrawStatus()

private static unsafe void DrawParty()
{
ImGui.Text($"Party: {DataCenter.PartyMembers.Length}");
ImGui.Text($"Alliance: {DataCenter.AllianceMembers.Length}");
ImGui.Text($"Party: {DataCenter.PartyMembers.Count()}");
ImGui.Text($"Alliance: {DataCenter.AllianceMembers.Count()}");

ImGui.Text($"PartyMembersAverHP: {DataCenter.PartyMembersAverHP}");

Expand Down Expand Up @@ -2752,7 +2752,7 @@ private static unsafe void DrawTargetData()
}

ImGui.Text($"All: {DataCenter.AllTargets.Count()}");
ImGui.Text($"Hostile: {DataCenter.AllHostileTargets.Length}");
ImGui.Text($"Hostile: {DataCenter.AllHostileTargets.Count()}");
foreach (var item in DataCenter.AllHostileTargets)
{
ImGui.Text(item.Name.ToString());
Expand Down
2 changes: 1 addition & 1 deletion RotationSolver/Updaters/MajorUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ private static void UpdateWork()

if (DataCenter.IsActivated())
{
TargetUpdater.UpdateTarget();
TargetUpdater.UpdateTargets();
ActionSequencerUpdater.UpdateActionSequencerAction();
ActionUpdater.UpdateNextAction();
}
Expand Down
2 changes: 1 addition & 1 deletion RotationSolver/Updaters/StateUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private static AutoStatus StatusFromAutomatic()
var canHealAreaAbility = singleAbility > 2;
var canHealAreaSpell = singleSpell > 2;

if (DataCenter.PartyMembers.Length > 2)
if (DataCenter.PartyMembers.Count() > 2)
{
//TODO: Beneficial area status.
var ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots);
Expand Down
Loading

0 comments on commit e3fd696

Please sign in to comment.