Skip to content

Commit

Permalink
Merge pull request #545 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
DRS and DRN improvement
  • Loading branch information
CarnifexOptimus authored Jan 5, 2025
2 parents 0376f88 + 4a5ee9b commit 9609033
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae;

public abstract class TrinitySeeker(WorldState ws, Actor primary) : BossModule(ws, primary, StartingArena.Center, StartingArena)
{
private static readonly WPos ArenaCenter = new(0, 278);
private static readonly DonutSegmentV[] barricades = [.. GenerateBarricades(), .. GenerateBarricadesHitBoxCushion()];

private static List<DonutSegmentV> GenerateBarricades()
{
List<DonutSegmentV> barricades = new(4);
for (var i = 0; i < 4; ++i)
barricades.Add(new(ArenaCenter, 18.7f, 21.6f, 45.Degrees() + 90.Degrees() * i, 22.5f.Degrees(), 6));
return barricades;
}
private static List<DonutSegmentV> GenerateBarricadesHitBoxCushion()
{
List<DonutSegmentV> barricades = new(8);
for (var i = 0; i < 4; ++i)
{
barricades.Add(new(ArenaCenter, 18.7f, 21.6f, 26.1f.Degrees() + 90.Degrees() * i, 5.Degrees(), 2));
barricades.Add(new(ArenaCenter, 18.7f, 21.6f, 63.9f.Degrees() + 90.Degrees() * i, 5.Degrees(), 2));
}
return barricades;
}

public static readonly ArenaBoundsComplex StartingArena = new([new Polygon(ArenaCenter, 29.5f, 48)], [.. barricades, new Rectangle(new(0, 248), 20, 1.25f), new Rectangle(new(0, 308), 20, 1.43f)]);
public static readonly ArenaBoundsComplex DefaultArena = new([new Polygon(ArenaCenter, 25, 48)], barricades);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class ArenaChange(BossModule module) : Components.GenericAOEs(module)
{
private static readonly AOEShapeDonut donut = new(25, 30);
private AOEInstance? _aoe;

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe);
public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.VerdantTempest && Arena.Bounds == TrinitySeeker.StartingArena)
_aoe = new(donut, Arena.Center, default, Module.CastFinishAt(spell, 3.8f));
}

public override void OnEventEnvControl(byte index, uint state)
{
if (state == 0x00020001 && index == 0x1B)
{
Arena.Bounds = TrinitySeeker.DefaultArena;
Arena.Center = TrinitySeeker.DefaultArena.Center;
_aoe = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ class BalefulBlade(BossModule module) : BossComponent(module)
{
private bool _phantomEdge;

private static readonly AOEShapeCone _shapeFront = new(DRN1TrinitySeeker.BarricadeRadius, 22.5f.Degrees());
private static readonly AOEShapeDonutSector _shapeBehind = new(DRN1TrinitySeeker.BarricadeRadius, 30, 22.5f.Degrees());
private static readonly AOEShapeCone _shapeFront = new(20, 22.5f.Degrees());
private static readonly AOEShapeDonutSector _shapeBehind = new(20, 30, 22.5f.Degrees());

public override void AddHints(int slot, Actor actor, TextHints hints)
{
Expand Down Expand Up @@ -37,7 +37,7 @@ public bool IsSafe(Actor actor)
angle = Math.Abs(angle - 45.Degrees().Rad); // rotate and fold again - now barricade is [0, 22.5]
if (angle > 22.5f.Degrees().Rad)
return false; // this is always unsafe, will be knocked into wall
var behind = offset.LengthSq() > DRN1TrinitySeeker.BarricadeRadius * DRN1TrinitySeeker.BarricadeRadius;
var behind = offset.LengthSq() > 400;
return behind != _phantomEdge;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,4 @@ class DeadIron : Components.BaitAwayTethers
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "The Combat Reborn Team", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 760, NameID = 9834)]
public class DRN1TrinitySeeker(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, 278), new ArenaBoundsCircle(25))
{
public const float BarricadeRadius = 20;

protected override void DrawArenaForeground(int pcSlot, Actor pc)
{
for (var i = 0; i < 4; ++i)
{
var center = (45 + i * 90).Degrees();
Arena.PathArcTo(Center, BarricadeRadius, (center - 22.5f.Degrees()).Rad, (center + 22.5f.Degrees()).Rad);
MiniArena.PathStroke(false, Colors.Border, 2);
}
}
}
public class DRN1TrinitySeeker(WorldState ws, Actor primary) : TrinitySeeker(ws, primary);
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
public enum OID : uint
{
Boss = 0x3131, // R4.000, x1
Helper = 0x233C, // R0.500, x16, 523 type
AetherialOrb = 0x3132, // R2.000, x0 (spawn during fight)
AetherialOrb = 0x3132, // R2.0
Helper = 0x233C
}

public enum AID : uint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class DRN1TrinitySeekerStates : StateMachineBuilder
public DRN1TrinitySeekerStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<ArenaChange>()
.ActivateOnEnter<MercifulBreeze>()
.ActivateOnEnter<MercifulBlooms>()
.ActivateOnEnter<MercifulArc>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.DRS1TrinitySeeker;

class ArenaChange(BossModule module) : Components.GenericAOEs(module)
{
private static readonly AOEShapeDonut donut = new(25, 30);
private AOEInstance? _aoe;

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe);
public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.VerdantTempest && Arena.Bounds == TrinitySeeker.StartingArena)
_aoe = new(donut, Arena.Center, default, Module.CastFinishAt(spell, 3.8f));
}

public override void OnEventEnvControl(byte index, uint state)
{
if (state == 0x00020001 && index == 0x1B)
{
Arena.Bounds = TrinitySeeker.DefaultArena;
Arena.Center = TrinitySeeker.DefaultArena.Center;
_aoe = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ class BalefulBlade(BossModule module) : BossComponent(module)
{
private bool _phantomEdge;

private static readonly AOEShapeCone _shapeFront = new(DRS1.BarricadeRadius, 22.5f.Degrees());
private static readonly AOEShapeDonutSector _shapeBehind = new(DRS1.BarricadeRadius, 30, 22.5f.Degrees());
private static readonly AOEShapeCone _shapeFront = new(20, 22.5f.Degrees());
private static readonly AOEShapeDonutSector _shapeBehind = new(20, 30, 22.5f.Degrees());

public override void AddHints(int slot, Actor actor, TextHints hints)
{
Expand Down Expand Up @@ -37,7 +37,7 @@ public bool IsSafe(Actor actor)
angle = Math.Abs(angle - 45.Degrees().Rad); // rotate and fold again - now barricade is [0, 22.5]
if (angle > 22.5f.Degrees().Rad)
return false; // this is always unsafe, will be knocked into wall
var behind = offset.LengthSq() > DRS1.BarricadeRadius * DRS1.BarricadeRadius;
var behind = offset.LengthSq() > 400;
return behind != _phantomEdge;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,4 @@ class DeadIron : Components.BaitAwayTethers
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 761, NameID = 9834, PlanLevel = 80)]
public class DRS1(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, 278), new ArenaBoundsCircle(25))
{
public const float BarricadeRadius = 20;

protected override void DrawArenaForeground(int pcSlot, Actor pc)
{
for (var i = 0; i < 4; ++i)
{
var center = (45 + i * 90).Degrees();
Arena.PathArcTo(Center, BarricadeRadius, (center - 22.5f.Degrees()).Rad, (center + 22.5f.Degrees()).Rad);
MiniArena.PathStroke(false, Colors.Border, 2);
}
}
}
public class DRS1(WorldState ws, Actor primary) : TrinitySeeker(ws, primary);
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

public enum OID : uint
{
Boss = 0x3135, // R4.000, x1
SeekerAvatar = 0x3136, // R4.000, x7
Helper = 0x233C, // R0.500, x16
AetherialOrb = 0x3137, // R2.000, spawn during fight
Boss = 0x3135, // R4.0
SeekerAvatar = 0x3136, // R4.0
AetherialOrb = 0x3137, // R2.0
Helper = 0x233C
}

public enum AID : uint
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Xml.Linq;

namespace BossMod.Shadowbringers.Foray.DelubrumReginae.DRS1TrinitySeeker;
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.DRS1TrinitySeeker;

class DRS1States : StateMachineBuilder
{
public DRS1States(BossModule module) : base(module)
{
SimplePhase(0, Phase1, "P1")
.ActivateOnEnter<ArenaChange>()
.Raw.Update = () => Module.PrimaryActor.IsDestroyed || Module.PrimaryActor.HPMP.CurHP <= 1 || (Module.PrimaryActor.CastInfo?.IsSpell(AID.VerdantPathSword) ?? false);
SimplePhase(1, Phase2, "P2")
.Raw.Update = () => Module.PrimaryActor.IsDestroyed || Module.PrimaryActor.HPMP.CurHP <= 1 || (Module.PrimaryActor.CastInfo?.IsSpell(AID.VerdantPathFist) ?? false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@

class Border(BossModule module) : Components.GenericAOEs(module)
{
public static readonly WPos BoundsCenter = new(-416, -184);
public static readonly ArenaBoundsCircle DefaultBounds = new(34.5f);
private const float _innerRingRadius = 14.5f;
private const float _outerRingRadius = 27.5f;
private const float _ringHalfWidth = 2.5f;
private const float _alcoveDepth = 1;
private const float _alcoveWidth = 2;
private bool Active;
private static readonly Shape[] labyrinth = [new PolygonCustom(InDanger()), new PolygonCustom(MidDanger()), new PolygonCustom(OutDanger())];
private static readonly AOEShapeCustom customShape = new(labyrinth);
private static readonly ArenaBoundsComplex labPhase = new([new Circle(BoundsCenter, 34.5f)], labyrinth);
private static readonly WPos ArenaCenter = new(-416, -184);
private static readonly Polygon[] DefaultPolygon = [new(ArenaCenter, 34.5f, 48)];
public static readonly ArenaBoundsComplex DefaultBounds = new(DefaultPolygon, [new Rectangle(new(-416, -219), 20, 1.4f), new Rectangle(new(-416, -149), 20, 1.25f)]);
private static readonly Shape[] labyrinthDifference = [new DonutV(ArenaCenter, 30, 34.5f, 48), new DonutV(ArenaCenter, 17, 25, 48), new Polygon(ArenaCenter, 12, 48)];
private static readonly Rectangle[] labyrinthUnion = [.. GenerateAlcoves(new(-416, -211.5f)), .. GenerateAlcoves(WPos.RotateAroundOrigin(22.5f, ArenaCenter, new(-416, -198.5f)), 22.5f.Degrees())];
private static readonly ArenaBoundsComplex labPhase = new(DefaultPolygon, labyrinthDifference, labyrinthUnion);
private static readonly AOEShapeCustom customShape = new(labyrinthDifference, labyrinthUnion);

private static List<Rectangle> GenerateAlcoves(WPos basePosition, Angle start = default)
{
List<Rectangle> rects = new(8)
{
new(basePosition, 2, 4, start)
};
for (var i = 1; i < 8; ++i)
rects.Add(new(WPos.RotateAroundOrigin(i * 45, ArenaCenter, basePosition), 2, 4, start + 45.Degrees() * i));
return rects;
}

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
Expand All @@ -26,48 +34,7 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
Active = true;
Arena.Bounds = labPhase;
Arena.Center = labPhase.Center;
}
}

private static List<WPos> RingBorder(Angle centerOffset, float ringRadius, bool innerBorder)
{
var offsetMultiplier = innerBorder ? -1 : 1;
var halfWidth = (_alcoveWidth / ringRadius).Radians();
var radiusWithDepth = ringRadius + offsetMultiplier * (_ringHalfWidth + _alcoveDepth);
var radiusWithoutDepth = ringRadius + offsetMultiplier * _ringHalfWidth;
var stepAngle = 45.Degrees();

var points = new List<WPos>();

for (var i = 0; i < 8; ++i)
{
var currentCenter = centerOffset + i * stepAngle;
points.AddRange(CurveApprox.CircleArc(BoundsCenter, radiusWithDepth, currentCenter - halfWidth, currentCenter + halfWidth, Shape.MaxApproxError));
var nextCenter = currentCenter + stepAngle;
points.AddRange(CurveApprox.CircleArc(BoundsCenter, radiusWithoutDepth, currentCenter + halfWidth, nextCenter - halfWidth, Shape.MaxApproxError));
}
points.Add(points[0]);

return points;
}

private static List<WPos> InDanger() => RingBorder(22.5f.Degrees(), _innerRingRadius, true);

private static List<WPos> MidDanger()
{
var outerRing = RingBorder(default, _outerRingRadius, true);
var innerRing = RingBorder(22.5f.Degrees(), _innerRingRadius, false);
innerRing.Reverse();
outerRing.AddRange(innerRing);
return outerRing;
}

private static List<WPos> OutDanger()
{
var outerBoundary = CurveApprox.Rect(BoundsCenter, new(0, 1), 34.5f, 34.5f).ToList(); // using a square instead of circle to have less vertices. polygon will get clipped with circle border anyway
outerBoundary.Add(outerBoundary[0]);
var innerRing = RingBorder(default, _outerRingRadius, false);
outerBoundary.AddRange(innerRing);
return outerBoundary;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DRS7 : BossModule
private readonly List<Actor> _ballsEarth;
private readonly List<Actor> _ballsFire;

public DRS7(WorldState ws, Actor primary) : base(ws, primary, Border.BoundsCenter, Border.DefaultBounds)
public DRS7(WorldState ws, Actor primary) : base(ws, primary, Border.DefaultBounds.Center, Border.DefaultBounds)
{
_monks = Enemies(OID.StygimolochMonk);
_ballsEarth = Enemies(OID.BallOfEarth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public DRS7States(BossModule module) : base(module)
SimplePhase(1, PhaseAdds, "Adds")
.ActivateOnEnter<Border>()
.OnExit(() => Module.Arena.Bounds = Border.DefaultBounds)
.OnExit(() => Module.Arena.Center = Border.DefaultBounds.Center)
.Raw.Update = () => Module.PrimaryActor.IsDestroyed || Module.PrimaryActor.IsTargetable;
DeathPhase(2, PhaseAfterAdds)
.ActivateOnEnter<RapidBoltsAOE>();
Expand Down

0 comments on commit 9609033

Please sign in to comment.