Skip to content

Commit

Permalink
Merge pull request #516 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
Paglth'an modules
  • Loading branch information
CarnifexOptimus authored Dec 26, 2024
2 parents e944e06 + 82e69c9 commit 57603d6
Show file tree
Hide file tree
Showing 86 changed files with 1,340 additions and 718 deletions.
7 changes: 3 additions & 4 deletions BossMod/Components/BaitAway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class GenericBaitAway(BossModule module, ActionID aid = default, bool alw
{
public record struct Bait(Actor Source, Actor Target, AOEShape Shape, DateTime Activation = default)
{
public Angle? CustomRotation { get; init; }
public Angle? CustomRotation;

public readonly Angle Rotation => CustomRotation ?? (Source != Target ? Angle.FromDirection(Target.Position - Source.Position) : Source.Rotation);

Expand All @@ -18,8 +18,8 @@ public Bait(Actor source, Actor target, AOEShape shape, DateTime activation, Ang
}
}

public bool AlwaysDrawOtherBaits = alwaysDrawOtherBaits; // if false, other baits are drawn only if they are clipping a player
public bool CenterAtTarget = centerAtTarget; // if true, aoe source is at target
public readonly bool AlwaysDrawOtherBaits = alwaysDrawOtherBaits; // if false, other baits are drawn only if they are clipping a player
public readonly bool CenterAtTarget = centerAtTarget; // if true, aoe source is at target
public bool AllowDeadTargets = true; // if false, baits with dead targets are ignored
public bool EnableHints = true;
public bool IgnoreOtherBaits; // if true, don't show hints/aoes for baits by others
Expand Down Expand Up @@ -81,7 +81,6 @@ private void AddTargetSpecificHints(Actor actor, Bait bait, AIHints hints)
case AOEShapeCone cone:
hints.AddForbiddenZone(ShapeDistance.Cone(bait.Source.Position, 100, bait.Source.AngleTo(a), cone.HalfAngle), bait.Activation);
break;

case AOEShapeRect rect:
hints.AddForbiddenZone(ShapeDistance.Cone(bait.Source.Position, 100, bait.Source.AngleTo(a), Angle.Asin(rect.HalfWidth / (a.Position - bait.Source.Position).Length())), bait.Activation);
break;
Expand Down
8 changes: 4 additions & 4 deletions BossMod/Components/CastCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
// generic component that counts specified casts
public class CastCounter(BossModule module, ActionID aid) : BossComponent(module)
{
public ActionID WatchedAction { get; private set; } = aid;
public int NumCasts { get; protected set; }
public readonly ActionID WatchedAction = aid;
public int NumCasts;

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
Expand All @@ -15,8 +15,8 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)

public class CastCounterMulti(BossModule module, ActionID[] aids) : BossComponent(module)
{
public ActionID[] WatchedActions = aids;
public int NumCasts { get; protected set; }
public readonly ActionID[] WatchedActions = aids;
public int NumCasts;

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
Expand Down
10 changes: 5 additions & 5 deletions BossMod/Components/CastHint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace BossMod.Components;
public class CastHint(BossModule module, ActionID aid, string hint, bool showCastTimeLeft = false) : CastCounter(module, aid)
{
public string Hint = hint;
public bool ShowCastTimeLeft = showCastTimeLeft; // if true, show cast time left until next instance
public readonly bool ShowCastTimeLeft = showCastTimeLeft; // if true, show cast time left until next instance
public readonly List<Actor> Casters = [];

public bool Active => Casters.Count > 0;
Expand All @@ -30,10 +30,10 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)

public class CastInterruptHint : CastHint
{
public bool CanBeInterrupted { get; init; }
public bool CanBeStunned { get; init; }
public bool ShowNameInHint { get; init; } // important if there are several targets
public string HintExtra { get; init; }
public readonly bool CanBeInterrupted;
public readonly bool CanBeStunned;
public readonly bool ShowNameInHint; // important if there are several targets
public readonly string HintExtra;

public CastInterruptHint(BossModule module, ActionID aid, bool canBeInterrupted = true, bool canBeStunned = false, string hintExtra = "", bool showNameInHint = false) : base(module, aid, "")
{
Expand Down
4 changes: 2 additions & 2 deletions BossMod/Components/Chains.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ namespace BossMod.Components;
// component for breakable chains - Note that chainLength for AI considers the minimum distance needed for a chain-pair to be broken (assuming perfectly stacked at cast)
public class Chains(BossModule module, uint tetherID, ActionID aid = default, float chainLength = 0, bool spreadChains = true) : CastCounter(module, aid)
{
public uint TID { get; init; } = tetherID;
public bool TethersAssigned { get; private set; }
public readonly uint TID = tetherID;
public bool TethersAssigned;
private readonly Actor?[] _partner = new Actor?[PartyState.MaxAllies];

public override void AddHints(int slot, Actor actor, TextHints hints)
Expand Down
16 changes: 8 additions & 8 deletions BossMod/Components/ChasingAOEs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ private void AddForbiddenZones(Actor actor, AIHints hints, bool isTarget)
// standard chasing aoe; first cast is long - assume it is baited on the nearest allowed target; successive casts are instant
public class StandardChasingAOEs(BossModule module, AOEShape shape, ActionID actionFirst, ActionID actionRest, float moveDistance, float secondsBetweenActivations, int maxCasts, bool resetExcludedTargets = false, uint icon = default, float activationDelay = 5.1f) : GenericChasingAOEs(module, moveDistance)
{
public AOEShape Shape = shape;
public ActionID ActionFirst = actionFirst;
public ActionID ActionRest = actionRest;
public float MoveDistance = moveDistance;
public float SecondsBetweenActivations = secondsBetweenActivations;
public readonly AOEShape Shape = shape;
public readonly ActionID ActionFirst = actionFirst;
public readonly ActionID ActionRest = actionRest;
public readonly float MoveDistance = moveDistance;
public readonly float SecondsBetweenActivations = secondsBetweenActivations;
public int MaxCasts = maxCasts;
public BitMask ExcludedTargets; // any targets in this mask aren't considered to be possible targets
public uint Icon = icon;
public float ActivationDelay = activationDelay;
public bool ResetExcludedTargets = resetExcludedTargets;
public readonly uint Icon = icon;
public readonly float ActivationDelay = activationDelay;
public readonly bool ResetExcludedTargets = resetExcludedTargets;
public readonly List<Actor> Actors = []; // to keep track of the icon before mechanic starts for handling custom forbidden zones
public DateTime Activation;

Expand Down
8 changes: 4 additions & 4 deletions BossMod/Components/Cleave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
// enemy OID == 0 means 'primary actor'
public class Cleave(BossModule module, ActionID aid, AOEShape shape, uint enemyOID = 0, bool activeForUntargetable = false, bool originAtTarget = false, bool activeWhileCasting = true) : CastCounter(module, aid)
{
public AOEShape Shape { get; init; } = shape;
public bool ActiveForUntargetable { get; init; } = activeForUntargetable;
public bool ActiveWhileCasting { get; init; } = activeWhileCasting;
public bool OriginAtTarget { get; init; } = originAtTarget;
public readonly AOEShape Shape = shape;
public readonly bool ActiveForUntargetable = activeForUntargetable;
public readonly bool ActiveWhileCasting = activeWhileCasting;
public readonly bool OriginAtTarget = originAtTarget;
public DateTime NextExpected;
private readonly List<Actor> _enemies = module.Enemies(enemyOID != 0 ? enemyOID : module.PrimaryActor.OID);

Expand Down
4 changes: 2 additions & 2 deletions BossMod/Components/ConcentricAOEs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public struct Sequence
public int NumCastsDone;
}

public AOEShape[] Shapes = shapes;
public List<Sequence> Sequences = [];
public readonly AOEShape[] Shapes = shapes;
public readonly List<Sequence> Sequences = [];

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Sequences.Where(s => s.NumCastsDone < Shapes.Length).Select(s => new AOEInstance(Shapes[s.NumCastsDone], s.Origin, s.Rotation, s.NextActivation));

Expand Down
4 changes: 2 additions & 2 deletions BossMod/Components/Exaflare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ public class Line
public int MaxShownExplosions;
}

public AOEShape Shape { get; init; } = shape;
public readonly AOEShape Shape = shape;
public uint ImminentColor = Colors.Danger;
public uint FutureColor = Colors.AOE;
protected List<Line> Lines = [];
protected readonly List<Line> Lines = [];

public bool Active => Lines.Count > 0;

Expand Down
18 changes: 9 additions & 9 deletions BossMod/Components/ForcedMarch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ public class PlayerState
public bool Active(BossModule module) => ForcedEnd > module.WorldState.CurrentTime || PendingMoves.Count > 0;
}

public int NumActiveForcedMarches { get; private set; }
public Dictionary<ulong, PlayerState> State = []; // key = instance ID
public int NumActiveForcedMarches;
public readonly Dictionary<ulong, PlayerState> State = []; // key = instance ID
public float MovementSpeed = 6; // default movement speed, can be overridden if necessary
public float ActivationLimit = activationLimit; // do not show pending moves that activate later than this limit
public readonly float ActivationLimit = activationLimit; // do not show pending moves that activate later than this limit

// called to determine whether we need to show hint
public virtual bool DestinationUnsafe(int slot, Actor actor, WPos pos) => !Module.InBounds(pos);
Expand Down Expand Up @@ -123,12 +123,12 @@ public override void OnStatusLose(Actor actor, ActorStatus status)
// action driven forced march
public class ActionDrivenForcedMarch(BossModule module, ActionID aid, float duration, Angle rotation, float actioneffectdelay, uint statusForced = 1257, uint statusForcedNPCs = 3629, float activationLimit = float.MaxValue) : GenericForcedMarch(module, activationLimit)
{
public float Duration = duration;
public float Actioneffectdelay = actioneffectdelay;
public Angle Rotation = rotation;
public uint StatusForced = statusForced;
public uint StatusForcedNPCs = statusForcedNPCs;
public ActionID Aid = aid;
public readonly float Duration = duration;
public readonly float Actioneffectdelay = actioneffectdelay;
public readonly Angle Rotation = rotation;
public readonly uint StatusForced = statusForced;
public readonly uint StatusForcedNPCs = statusForcedNPCs;
public readonly ActionID Aid = aid;

public override void OnStatusGain(Actor actor, ActorStatus status)
{
Expand Down
54 changes: 44 additions & 10 deletions BossMod/Components/GenericAOEs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,29 @@ public override void DrawArenaBackground(int pcSlot, Actor pc)
// self-targeted aoe that happens at the end of the cast
public class SelfTargetedAOEs(BossModule module, ActionID aid, AOEShape shape, int maxCasts = int.MaxValue, uint color = 0) : GenericAOEs(module, aid)
{
public AOEShape Shape { get; init; } = shape;
public readonly AOEShape Shape = shape;
public int MaxCasts = maxCasts; // used for staggered aoes, when showing all active would be pointless
public uint Color = color;
public bool Risky = true; // can be customized if needed
public readonly List<Actor> Casters = [];

public IEnumerable<Actor> ActiveCasters => Casters.Take(MaxCasts);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => ActiveCasters.Select(c => new AOEInstance(Shape, c.Position, c.CastInfo!.Rotation, Module.CastFinishAt(c.CastInfo)));
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var count = Casters.Count;
if (count == 0)
return [];
var min = count > MaxCasts ? MaxCasts : count;
var aoes = new List<AOEInstance>(min);
for (var i = 0; i < min; ++i)
{
var caster = Casters[i];
AOEInstance aoeInstance = new(Shape, caster.Position, caster.CastInfo!.Rotation, Module.CastFinishAt(caster.CastInfo));
aoes.Add(aoeInstance);
}
return aoes;
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
Expand All @@ -61,7 +75,7 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
// self-targeted aoe that uses current caster's rotation instead of rotation from cast-info - used by legacy modules written before i've reversed real cast rotation
public class SelfTargetedLegacyRotationAOEs(BossModule module, ActionID aid, AOEShape shape, int maxCasts = int.MaxValue) : GenericAOEs(module, aid)
{
public AOEShape Shape { get; init; } = shape;
public readonly AOEShape Shape = shape;
public int MaxCasts = maxCasts; // used for staggered aoes, when showing all active would be pointless
public readonly List<Actor> Casters = [];

Expand All @@ -86,16 +100,23 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
public class LocationTargetedAOEs(BossModule module, ActionID aid, AOEShape shape, int maxCasts = int.MaxValue, bool targetIsLocation = false) : GenericAOEs(module, aid)
{
public LocationTargetedAOEs(BossModule module, ActionID aid, float radius, int maxCasts = int.MaxValue, bool targetIsLocation = false) : this(module, aid, new AOEShapeCircle(radius), maxCasts, targetIsLocation) { }
public AOEShape Shape { get; init; } = shape;
public int MaxCasts = maxCasts; // used for staggered aoes, when showing all active would be pointless
public readonly AOEShape Shape = shape;
public readonly int MaxCasts = maxCasts; // used for staggered aoes, when showing all active would be pointless
public uint Color; // can be customized if needed
public bool Risky = true; // can be customized if needed
public bool TargetIsLocation { get; init; } = targetIsLocation; // can be customized if needed
public readonly bool TargetIsLocation = targetIsLocation; // can be customized if needed

public List<AOEInstance> Casters { get; } = [];
public readonly List<AOEInstance> Casters = [];
public IEnumerable<AOEInstance> ActiveCasters => Casters.Take(MaxCasts);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Casters.Take(MaxCasts);
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var count = Casters.Count;
if (count == 0)
yield break;
for (var i = 0; i < (count > MaxCasts ? MaxCasts : count); ++i)
yield return Casters[i];
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
Expand All @@ -116,10 +137,23 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
// 'charge at location' aoes that happen at the end of the cast
public class ChargeAOEs(BossModule module, ActionID aid, float halfWidth) : GenericAOEs(module, aid)
{
public float HalfWidth { get; init; } = halfWidth;
public readonly float HalfWidth = halfWidth;
public readonly List<(Actor caster, AOEShape shape, Angle direction)> Casters = [];

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Casters.Select(csr => new AOEInstance(csr.shape, csr.caster.Position, csr.direction, Module.CastFinishAt(csr.caster.CastInfo)));
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var count = Casters.Count;
if (count == 0)
return [];
var aoes = new List<AOEInstance>(count);
for (var i = 0; i < count; ++i)
{
var csr = Casters[i];
AOEInstance aoeInstance = new(csr.shape, csr.caster.Position, csr.direction, Module.CastFinishAt(csr.caster.CastInfo));
aoes.Add(aoeInstance);
}
return aoes;
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
Expand Down
22 changes: 11 additions & 11 deletions BossMod/Components/Knockback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ protected struct PlayerImmuneState
public readonly bool ImmuneAt(DateTime time) => RoleBuffExpire > time || JobBuffExpire > time || DutyBuffExpire > time;
}

public List<SafeWall> SafeWalls = safeWalls ?? [];
public bool IgnoreImmunes = ignoreImmunes;
public bool StopAtWall = stopAtWall; // use if wall is solid rather than deadly
public bool StopAfterWall = stopAfterWall; // use if the wall is a polygon where you need to check for intersections
public int MaxCasts = maxCasts; // use to limit number of drawn knockbacks
public readonly List<SafeWall> SafeWalls = safeWalls ?? [];
public readonly bool IgnoreImmunes = ignoreImmunes;
public readonly bool StopAtWall = stopAtWall; // use if wall is solid rather than deadly
public readonly bool StopAfterWall = stopAfterWall; // use if the wall is a polygon where you need to check for intersections
public readonly int MaxCasts = maxCasts; // use to limit number of drawn knockbacks
private const float approxHitBoxRadius = 0.499f; // calculated because due to floating point errors this does not result in 0.001
private const float maxIntersectionError = 0.5f - approxHitBoxRadius; // calculated because due to floating point errors this does not result in 0.001

protected PlayerImmuneState[] PlayerImmunes = new PlayerImmuneState[PartyState.MaxAllies];
protected readonly PlayerImmuneState[] PlayerImmunes = new PlayerImmuneState[PartyState.MaxAllies];

public bool IsImmune(int slot, DateTime time) => !IgnoreImmunes && PlayerImmunes[slot].ImmuneAt(time);

Expand Down Expand Up @@ -225,11 +225,11 @@ public override void OnStatusLose(Actor actor, ActorStatus status)
public class KnockbackFromCastTarget(BossModule module, ActionID aid, float distance, bool ignoreImmunes = false, int maxCasts = int.MaxValue, AOEShape? shape = null, Kind kind = Kind.AwayFromOrigin, float minDistance = 0, bool minDistanceBetweenHitboxes = false, bool stopAtWall = false, bool stopAfterWall = false, List<SafeWall>? safeWalls = null)
: Knockback(module, aid, ignoreImmunes, maxCasts, stopAtWall, stopAfterWall, safeWalls)
{
public float Distance = distance;
public AOEShape? Shape = shape;
public Kind KnockbackKind = kind;
public float MinDistance = minDistance;
public bool MinDistanceBetweenHitboxes = minDistanceBetweenHitboxes;
public readonly float Distance = distance;
public readonly AOEShape? Shape = shape;
public readonly Kind KnockbackKind = kind;
public readonly float MinDistance = minDistance;
public readonly bool MinDistanceBetweenHitboxes = minDistanceBetweenHitboxes;
public readonly List<Actor> Casters = [];

public override IEnumerable<Source> Sources(int slot, Actor actor)
Expand Down
Loading

0 comments on commit 57603d6

Please sign in to comment.