This repository has been archived by the owner on Aug 28, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
638afa1
commit ed62c5b
Showing
7 changed files
with
431 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,158 @@ | ||
namespace RotationSolver.Basic.Actions; | ||
using ECommons.GameHelpers; | ||
using FFXIVClientStructs.FFXIV.Client.Game; | ||
using RotationSolver.Basic.Configuration; | ||
|
||
namespace RotationSolver.Basic.Actions; | ||
public struct ActionBasicInfo | ||
{ | ||
private readonly IBaseActionNew _action; | ||
internal static readonly uint[] ActionsNoNeedCasting = | ||
[ | ||
5, | ||
(uint)ActionID.PowerfulShotPvP, | ||
(uint)ActionID.BlastChargePvP, | ||
]; | ||
|
||
private readonly IBaseActionNew _action; | ||
public readonly string Name => _action.Action.Name; | ||
public readonly uint ID => _action.Action.RowId; | ||
public readonly uint IconID => ID == (uint)ActionID.SprintPvE ? 104u : _action.Action.Icon; | ||
|
||
public readonly uint AdjustedID => (uint)Service.GetAdjustedActionId((ActionID)ID); | ||
|
||
public AttackType AttackType => (AttackType)(_action.Action.AttackType.Value?.RowId ?? byte.MaxValue); | ||
public readonly AttackType AttackType => (AttackType)(_action.Action.AttackType.Value?.RowId ?? byte.MaxValue); | ||
|
||
public readonly float AnimationLockTime => OtherConfiguration.AnimationLockTime?.TryGetValue(AdjustedID, out var time) ?? false ? time : 0.6f; | ||
|
||
public readonly byte Level => _action.Action.ClassJobLevel; | ||
public readonly bool EnoughLevel => Player.Level >= Level; | ||
|
||
public readonly bool IsPvP => _action.Action.IsPvP; | ||
/// <summary> | ||
/// Casting time. | ||
/// </summary> | ||
public readonly unsafe float CastTime => ActionManager.GetAdjustedCastTime(ActionType.Action, AdjustedID) / 1000f; | ||
|
||
public readonly bool IsOnSlot | ||
{ | ||
get | ||
{ | ||
if (IsDutyAction) | ||
{ | ||
return DataCenter.DutyActions.Contains(AdjustedID); | ||
} | ||
|
||
return IsPvP == DataCenter.Territory?.IsPvpZone; | ||
} | ||
} | ||
public bool IsLimitBreak { get; } | ||
public bool IsGeneralGCD { get; } | ||
public bool IsRealGCD { get; } | ||
public bool IsDutyAction { get; } | ||
public Aspect Aspect { get; } | ||
|
||
public bool IsFriendly { get; set; } | ||
public bool IsEnable { get; set; } = true; | ||
internal ActionID[]? ComboIdsNot { get; set; } | ||
|
||
public ActionBasicInfo(IBaseActionNew action) | ||
internal ActionID[]? ComboIds { get; set; } | ||
/// <summary> | ||
/// Status that this action provides. | ||
/// </summary> | ||
public StatusID[]? StatusProvide { get; set; } = null; | ||
|
||
/// <summary> | ||
/// Status that this action needs. | ||
/// </summary> | ||
public StatusID[]? StatusNeed { get; set; } = null; | ||
|
||
public Func<bool>? ActionCheck { get; set; } = null; | ||
|
||
public ActionBasicInfo(IBaseActionNew action, bool isDutyAction) | ||
{ | ||
_action = action; | ||
|
||
IsGeneralGCD = _action.Action.IsGeneralGCD(); | ||
IsRealGCD = _action.Action.IsRealGCD(); | ||
IsLimitBreak = _action.Action.ActionCategory?.Value?.RowId == 9; | ||
IsDutyAction = isDutyAction; | ||
Aspect = (Aspect)_action.Action.Aspect; | ||
//TODO: better friendly check. | ||
IsFriendly = _action.Action.CanTargetFriendly; | ||
} | ||
} | ||
|
||
public enum ActionType : byte | ||
{ | ||
Move, | ||
Heal, | ||
Defence, | ||
Attack, | ||
internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipCombo, bool ignoreCastingCheck) | ||
{ | ||
if (!IsEnable || !IsOnSlot) return false; | ||
|
||
//Disabled. | ||
if (DataCenter.DisabledActionSequencer?.Contains(ID) ?? false) return false; | ||
|
||
if (!EnoughLevel) return false; | ||
|
||
var player = Player.Object; | ||
|
||
if (StatusNeed != null) | ||
{ | ||
if (!player.HasStatus(true, StatusNeed)) return false; | ||
} | ||
|
||
if (StatusProvide != null && !skipStatusProvideCheck) | ||
{ | ||
if (player.HasStatus(true, StatusProvide)) return false; | ||
} | ||
|
||
if(!skipCombo && IsGeneralGCD) | ||
{ | ||
if (!CheckForCombo()) return false; | ||
} | ||
|
||
//Need casting. | ||
if (CastTime > 0 && !player.HasStatus(true, | ||
[ | ||
StatusID.Swiftcast, | ||
StatusID.Triplecast, | ||
StatusID.Dualcast, | ||
]) | ||
&& !ActionsNoNeedCasting.Contains(ID)) | ||
{ | ||
//Is knocking back. | ||
if (DateTime.Now > DataCenter.KnockbackStart && DateTime.Now < DataCenter.KnockbackFinished) return false; | ||
|
||
if (DataCenter.NoPoslock && DataCenter.IsMoving && !ignoreCastingCheck) return false; | ||
} | ||
|
||
if (IsGeneralGCD && StatusProvide?.Length > 0 && IsFriendly && IActionHelper.IsLastGCD(true, _action) | ||
&& DataCenter.TimeSinceLastAction.TotalSeconds < 3) return false; | ||
|
||
if (!(ActionCheck?.Invoke() ?? true)) return false; | ||
|
||
return true; | ||
} | ||
|
||
private readonly bool CheckForCombo() | ||
{ | ||
if (ComboIdsNot != null) | ||
{ | ||
if (ComboIdsNot.Contains(DataCenter.LastComboAction)) return false; | ||
} | ||
|
||
var comboActions = (_action.Action.ActionCombo?.Row ?? 0) != 0 | ||
? new ActionID[] { (ActionID)_action.Action.ActionCombo!.Row } | ||
: []; | ||
if (ComboIds != null) comboActions = comboActions.Union(ComboIds).ToArray(); | ||
|
||
if (comboActions.Length > 0) | ||
{ | ||
if (comboActions.Contains(DataCenter.LastComboAction)) | ||
{ | ||
if (DataCenter.ComboTime < DataCenter.WeaponRemain) return false; | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
using ECommons.GameHelpers; | ||
using FFXIVClientStructs.FFXIV.Client.Game; | ||
using static Dalamud.Interface.Utility.Raii.ImRaii; | ||
|
||
namespace RotationSolver.Basic.Actions; | ||
public readonly struct ActionCooldownInfo | ||
{ | ||
private readonly IBaseActionNew _action; | ||
public byte CoolDownGroup { get; } | ||
|
||
unsafe RecastDetail* CoolDownDetail => ActionManager.Instance()->GetRecastGroupDetail(CoolDownGroup - 1); | ||
|
||
private unsafe float RecastTime => CoolDownDetail == null ? 0 : CoolDownDetail->Total; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public float RecastTimeElapsed => RecastTimeElapsedRaw - DataCenter.WeaponElapsed; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
unsafe float RecastTimeElapsedRaw => CoolDownDetail == null ? 0 : CoolDownDetail->Elapsed; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public unsafe bool IsCoolingDown => CoolDownDetail != null && CoolDownDetail->IsActive != 0; | ||
|
||
private float RecastTimeRemain => RecastTime - RecastTimeElapsedRaw; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public unsafe ushort MaxCharges => Math.Max(ActionManager.GetMaxCharges(_action.Info.AdjustedID, (uint)Player.Level), (ushort)1); | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public bool HasOneCharge => !IsCoolingDown || RecastTimeElapsedRaw >= RecastTimeOneChargeRaw; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public ushort CurrentCharges => IsCoolingDown ? (ushort)(RecastTimeElapsedRaw / RecastTimeOneChargeRaw) : MaxCharges; | ||
|
||
private float RecastTimeOneChargeRaw => ActionManager.GetAdjustedRecastTime(ActionType.Action, _action.Info.AdjustedID) / 1000f; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw - DataCenter.WeaponRemain; | ||
|
||
float RecastTimeRemainOneChargeRaw => RecastTimeRemain % RecastTimeOneChargeRaw; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
public float RecastTimeElapsedOneCharge => RecastTimeElapsedOneChargeRaw - DataCenter.WeaponElapsed; | ||
|
||
float RecastTimeElapsedOneChargeRaw => RecastTimeElapsedRaw % RecastTimeOneChargeRaw; | ||
|
||
|
||
public ActionCooldownInfo(IBaseActionNew action) | ||
{ | ||
_action = action; | ||
CoolDownGroup = _action.Action.GetCoolDownGroup(); | ||
} | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="gcdCount"></param> | ||
/// <param name="offset"></param> | ||
/// <returns></returns> | ||
public bool ElapsedOneChargeAfterGCD(uint gcdCount = 0, float offset = 0) | ||
=> ElapsedOneChargeAfter(DataCenter.GCDTime(gcdCount, offset)); | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="time"></param> | ||
/// <returns></returns> | ||
public bool ElapsedOneChargeAfter(float time) | ||
=> IsCoolingDown && time <= RecastTimeElapsedOneCharge; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="gcdCount"></param> | ||
/// <param name="offset"></param> | ||
/// <returns></returns> | ||
public bool ElapsedAfterGCD(uint gcdCount = 0, float offset = 0) | ||
=> ElapsedAfter(DataCenter.GCDTime(gcdCount, offset)); | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="time"></param> | ||
/// <returns></returns> | ||
public bool ElapsedAfter(float time) | ||
=> IsCoolingDown && time <= RecastTimeElapsed; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="gcdCount"></param> | ||
/// <param name="offset"></param> | ||
/// <returns></returns> | ||
public bool WillHaveOneChargeGCD(uint gcdCount = 0, float offset = 0) | ||
=> WillHaveOneCharge(DataCenter.GCDTime(gcdCount, offset)); | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="remain"></param> | ||
/// <returns></returns> | ||
public bool WillHaveOneCharge(float remain) | ||
=> HasOneCharge || RecastTimeRemainOneCharge <= remain; | ||
|
||
internal bool CooldownCheck(bool isEmpty, bool onLastAbility, bool ignoreClippingCheck, byte gcdCountForAbility) | ||
{ | ||
if (!_action.Info.IsGeneralGCD) | ||
{ | ||
if (IsCoolingDown) | ||
{ | ||
if (_action.Info.IsRealGCD) | ||
{ | ||
if (!WillHaveOneChargeGCD(0, 0)) return false; | ||
} | ||
else | ||
{ | ||
if (!HasOneCharge && RecastTimeRemainOneChargeRaw > DataCenter.ActionRemain) return false; | ||
} | ||
} | ||
|
||
if (!isEmpty) | ||
{ | ||
if (RecastTimeRemain > DataCenter.WeaponRemain + DataCenter.WeaponTotal * gcdCountForAbility) | ||
return false; | ||
} | ||
} | ||
|
||
if (!_action.Info.IsRealGCD) | ||
{ | ||
if (onLastAbility) | ||
{ | ||
if (DataCenter.NextAbilityToNextGCD > _action.Info.AnimationLockTime + DataCenter.Ping + DataCenter.MinAnimationLock) return false; | ||
} | ||
else if (!ignoreClippingCheck) | ||
{ | ||
if (DataCenter.NextAbilityToNextGCD < _action.Info.AnimationLockTime) return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
Oops, something went wrong.