Skip to content

Commit

Permalink
Add new configs, refactor, and enhance documentation
Browse files Browse the repository at this point in the history
Added new configuration options to ignore specific immune targets in various scenarios. Updated multiple files to include XML documentation comments for better code readability and maintenance. Refactored methods and data structures for improved performance and readability. Enhanced error handling and code generation logic in source generators.
  • Loading branch information
LTS-FFXIV committed Jan 17, 2025
1 parent ab4242c commit 485a079
Show file tree
Hide file tree
Showing 23 changed files with 7,421 additions and 803 deletions.
12 changes: 12 additions & 0 deletions RotationSolver.Basic/Configuration/Configs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ public const string
Filter = TargetConfig)]
private static readonly bool _filterOneHPInvincible = true;

[ConditionBool, UI("Ignore immune Ark Angels in Jenuo: The First Walk.",
Filter = TargetConfig)]
private static readonly bool _jeunoTarget = true;

[ConditionBool, UI("Ignore immune targets in Cloud of Darkenss Chaotic.",
Filter = TargetConfig)]
private static readonly bool _cODTarget = true;

[ConditionBool, UI("Ignore Strong of Shield target (Hansel and Gretel) in The Tower at Paradigm's Breach if you will hit shield.",
Filter = TargetConfig)]
private static readonly bool _strongOfSheildTarget = true;

[ConditionBool, UI("Teaching mode", Filter = UiInformation)]
private static readonly bool _teachingMode = false;

Expand Down
78 changes: 77 additions & 1 deletion RotationSolver.Basic/Helpers/ObjectHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ internal static bool IsAttackable(this IBattleChara battleChara)
if (Service.Config.FilterOneHpInvincible && battleChara.CurrentHp <= 1) return false;

// Check if the target is invincible.
if (battleChara.IsJeunoBossImmune()) return false;
if (Service.Config.CodTarget && battleChara.IsCODBossImmune()) return false;
if (Service.Config.JeunoTarget && battleChara.IsJeunoBossImmune()) return false;
if (Service.Config.StrongOfSheildTarget && battleChara.IsHanselorGretelSheilded()) return false;

// Ensure StatusList is not null before accessing it
if (battleChara.StatusList == null) return false;
Expand Down Expand Up @@ -437,6 +439,12 @@ internal static bool CanInterrupt(this IGameObject o)
{
if (o is not IBattleChara b) return false;

// Ensure the IBattleChara object is valid before accessing its properties
unsafe
{
if (b.Struct() == null) return false;
}

var baseCheck = b.IsCasting && b.IsCastInterruptible && b.TotalCastTime >= 2;
if (!baseCheck) return false;
if (!Service.Config.InterruptibleMoreCheck) return false;
Expand Down Expand Up @@ -504,6 +512,74 @@ public static bool IsJeunoBossImmune(this IBattleChara obj)
return false;
}

/// <summary>
/// Is target COD Boss immune.
/// </summary>
/// <param name="obj">the object.</param>
/// <returns></returns>
public static bool IsCODBossImmune(this IBattleChara obj)
{
var player = Player.Object;

var StygianStatus = StatusID.UnnamedStatus_4388;
var CloudOfDarknessStatus = StatusID.VeilOfDarkness;
var AntiCloudOfDarknessStatus = StatusID.Rsv41771100S74Cfc3B0E74Cfc3B0;
var AntiStygianStatus = StatusID.Rsv41771100S74Cfc3B0E74Cfc3B0;

if (obj.IsEnemy())
{
if (obj.HasStatus(false, CloudOfDarknessStatus) &&
player.HasStatus(false, AntiCloudOfDarknessStatus))
{
if (Service.Config.InDebug)
{
Svc.Log.Information("IsCODBossImmune: VeilOfDarkness status found, CloudOfDarkness immune");
}
return true;
}

if (obj.HasStatus(false, StygianStatus) &&
player.HasStatus(false, AntiStygianStatus))
{
if (Service.Config.InDebug)
{
Svc.Log.Information("IsCODBossImmune: UnnamedStatus4388 status found, Stygian immune");
}
return true;
}
}

return false;
}

/// <summary>
/// Is target Jeuno Boss immune.
/// </summary>
/// <param name="obj">the object.</param>
/// <returns></returns>
public static bool IsHanselorGretelSheilded(this IBattleChara obj)
{
var player = Player.Object;

var strongOfShieldPositional = EnemyPositional.Front;
var strongOfShieldStatus = StatusID.StrongOfShield;

if (obj.IsEnemy())
{
if (obj.HasStatus(false, strongOfShieldStatus) &&
strongOfShieldPositional != obj.FindEnemyPositional())
{
if (Service.Config.InDebug)
{
Svc.Log.Information("IsHanselorGretelSheilded: StrongOfShield status found, ignoring status haver if player is out of position");
}
return true;
}
}

return false;
}

/// <summary>
/// Is target a boss depends on the ttk.
/// </summary>
Expand Down
21 changes: 19 additions & 2 deletions RotationSolver.GameData/Getters/Actions/ActionCategoryGetter.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
using Lumina.Excel.GeneratedSheets;

namespace RotationSolver.GameData.Getters.Actions;

/// <summary>
/// Class for getting ActionCategory rows from the game data.
/// </summary>
internal class ActionCategoryGetter(Lumina.GameData gameData)
: ExcelRowGetter<ActionCategory>(gameData)
{
private readonly List<string> _addedNames = [];
private readonly HashSet<string> _addedNames = new();

/// <summary>
/// Called before creating the list of items. Clears the list of added names.
/// </summary>
protected override void BeforeCreating()
{
_addedNames.Clear();
base.BeforeCreating();
}

/// <summary>
/// Determines whether the specified ActionCategory item should be added to the list.
/// </summary>
/// <param name="item">The ActionCategory item to check.</param>
/// <returns>True if the item should be added; otherwise, false.</returns>
protected override bool AddToList(ActionCategory item)
{
var name = item.Name.RawString;
Expand All @@ -20,6 +32,11 @@ protected override bool AddToList(ActionCategory item)
return true;
}

/// <summary>
/// Converts the specified ActionCategory item to its code representation.
/// </summary>
/// <param name="item">The ActionCategory item to convert.</param>
/// <returns>The code representation of the item.</returns>
protected override string ToCode(ActionCategory item)
{
var name = item.Name.RawString.ToPascalCase();
Expand All @@ -40,4 +57,4 @@ protected override string ToCode(ActionCategory item)
{name} = {item.RowId},
""";
}
}
}
72 changes: 46 additions & 26 deletions RotationSolver.GameData/Getters/Actions/ActionGetterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,79 @@

namespace RotationSolver.GameData.Getters.Actions;

internal abstract class ActionGetterBase(Lumina.GameData gameData)
: ExcelRowGetter<Action>(gameData)
/// <summary>
/// Abstract base class for getting action rows from the Excel sheet.
/// </summary>
internal abstract class ActionGetterBase : ExcelRowGetter<Action>
{
public List<string> AddedNames { get; } = [];
private string[] _notCombatJobs = [];
/// <summary>
/// Initializes a new instance of the <see cref="ActionGetterBase"/> class.
/// </summary>
/// <param name="gameData">The game data.</param>
protected ActionGetterBase(Lumina.GameData gameData) : base(gameData) { }

/// <summary>
/// Gets the list of added names.
/// </summary>
public List<string> AddedNames { get; } = new();

private string[] _notCombatJobs = Array.Empty<string>();

/// <summary>
/// Called before creating the list of items.
/// </summary>
protected override void BeforeCreating()
{
AddedNames.Clear();
_notCombatJobs = [.. _gameData.GetExcelSheet<ClassJob>()!.Where(c =>
{
return c.ClassJobCategory.Row is 32 or 33;
}).Select(c => c.Abbreviation.RawString)];
_notCombatJobs = _gameData.GetExcelSheet<ClassJob>()!
.Where(c => c.ClassJobCategory.Row is 32 or 33)
.Select(c => c.Abbreviation.RawString)
.ToArray();
base.BeforeCreating();
}

/// <summary>
/// Determines whether the specified action should be added to the list.
/// </summary>
/// <param name="item">The action to check.</param>
/// <returns>True if the action should be added; otherwise, false.</returns>
protected override bool AddToList(Action item)
{
if (item.RowId is 3 or 120) return true; //Sprint and cure.
if (item.RowId is 3 or 120) return true; // Sprint and cure.
if (item.ClassJobCategory.Row == 0) return false;

var name = item.Name.RawString;
if (string.IsNullOrEmpty(name)) return false;
if (!name.All(char.IsAscii)) return false;
if (item.Icon is 0 or 405) return false;

if (item.ActionCategory.Row
is 6 or 7 // No DoL or DoH Action
or 8 //No Event.
or 12 // No Mount,
or > 14 // No item manipulation and other thing.
or 9 //No LB,
) return false;
if (item.ActionCategory.Row is 6 or 7 or 8 or 12 or > 14 or 9) return false;

//No crafting or gathering.
var category = item.ClassJobCategory.Value;
if (category == null) return false;

if (category.RowId == 1) return true;

if (_notCombatJobs.Any(name =>
{
return (bool?)category.GetType().GetRuntimeProperty(name)?.GetValue(category) ?? false;
}))
if (_notCombatJobs.Any(jobName => (bool?)category.GetType().GetRuntimeProperty(jobName)?.GetValue(category) ?? false))
{
return false;
}

return true;
}

/// <summary>
/// Gets the name of the specified action.
/// </summary>
/// <param name="item">The action.</param>
/// <returns>The name of the action.</returns>
protected string GetName(Action item)
{
var name = item.Name.RawString.ToPascalCase()
+ (item.IsPvP ? "PvP" : "PvE");
var name = item.Name.RawString.ToPascalCase() + (item.IsPvP ? "PvP" : "PvE");

if (AddedNames.Contains(name))
{
name += "_" + item.RowId.ToString();
name += "_" + item.RowId;
}
else
{
Expand All @@ -68,10 +84,14 @@ protected string GetName(Action item)
return name;
}

/// <summary>
/// Gets the description of the specified action.
/// </summary>
/// <param name="item">The action.</param>
/// <returns>The description of the action.</returns>
protected string GetDesc(Action item)
{
var desc = _gameData.GetExcelSheet<ActionTransient>()?.GetRow(item.RowId)?.Description.RawString ?? string.Empty;

return $"<para>{desc.Replace("\n", "</para>\n/// <para>")}</para>";
}
}
}
22 changes: 19 additions & 3 deletions RotationSolver.GameData/Getters/Actions/ActionIdGetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@

namespace RotationSolver.GameData.Getters.Actions;

internal class ActionIdGetter(Lumina.GameData gameData)
: ActionGetterBase(gameData)
/// <summary>
/// Class for getting action IDs from the Excel sheet.
/// </summary>
internal class ActionIdGetter : ActionGetterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ActionIdGetter"/> class.
/// </summary>
/// <param name="gameData">The game data.</param>
public ActionIdGetter(Lumina.GameData gameData)
: base(gameData)
{
}

/// <summary>
/// Converts the specified action to its code representation.
/// </summary>
/// <param name="item">The action to convert.</param>
/// <returns>The code representation of the action.</returns>
protected override string ToCode(Action item)
{
var name = GetName(item);
Expand All @@ -17,4 +33,4 @@ protected override string ToCode(Action item)
{name} = {item.RowId},
""";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
namespace RotationSolver.GameData.Getters.Actions;
internal abstract class ActionRotationGetterBase(Lumina.GameData gameData)
: ActionGetterBase(gameData)

/// <summary>
/// Abstract base class for getting action rotation rows from the Excel sheet.
/// </summary>
internal abstract class ActionRotationGetterBase : ActionGetterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ActionRotationGetterBase"/> class.
/// </summary>
/// <param name="gameData">The game data.</param>
protected ActionRotationGetterBase(Lumina.GameData gameData)
: base(gameData)
{
}

/// <summary>
/// Converts the specified action item to its code representation.
/// </summary>
/// <param name="item">The action item to convert.</param>
/// <returns>The code representation of the action item.</returns>
protected override string ToCode(Lumina.Excel.GeneratedSheets.Action item)
{
var name = GetName(item);
Expand All @@ -10,5 +27,8 @@ protected override string ToCode(Lumina.Excel.GeneratedSheets.Action item)
return item.ToCode(name, descName, GetDesc(item), IsDutyAction);
}

/// <summary>
/// Gets a value indicating whether the action is a duty action.
/// </summary>
public abstract bool IsDutyAction { get; }
}
}
Loading

0 comments on commit 485a079

Please sign in to comment.