Skip to content

Commit

Permalink
Jeuno 4 improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Dec 10, 2024
1 parent f187ebe commit 1c721f8
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace BossMod.Dawntrail.Alliance.A14ShadowLord;

class Teleport(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.Teleport));
class TeraSlash(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.TeraSlash));
class UnbridledRage(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeRect(100, 4), (uint)IconID.UnbridledRage, ActionID.MakeSpell(AID.UnbridledRageAOE), 5.9f);
class DarkNova(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.DarkNova), 6);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,21 @@ public enum AID : uint
BindingSigilPreview = 41513, // Helper->self, 1.5s cast, range 9 circle, visual
SoulBinding = 41514, // Helper->self, 1.0s cast, range 9 circle, apply debuff
SoulBindingBurst = 41531, // Helper->self, no cast, vuln on debuffed people
DamningStrikes = 40791, // Boss->self, 8.0s cast, single-target, visual (towers)
DamningStrikes1 = 40791, // Boss->self, 8.0s cast, single-target, visual (towers)
DamningStrikes2 = 41054, // Boss->self, 8.7s cast, single-target, visual (towers, much more rare, ??? difference)
DamningStrikesGrab = 41530, // Helper->player, no cast, single-target, grab target
DamningStrikesTeleport1 = 41816, // Boss->location, no cast, single-target, teleport before first hit
DamningStrikesTeleport2 = 41815, // Boss->location, no cast, single-target, teleport before second & third hits
DamningStrikesVisual1 = 42052, // Boss->self, no cast, single-target, visual before first and second hits
DamningStrikesVisual2 = 40793, // Boss->self, no cast, single-target, visual before third hit
DamningStrikesTeleport1 = 40794, // Boss->location, no cast, single-target, teleport before hit
DamningStrikesTeleport2 = 41815, // Boss->location, no cast, single-target, teleport before hit
DamningStrikesTeleport3 = 41816, // Boss->location, no cast, single-target, teleport before hit
DamningStrikesTeleport4 = 42054, // Boss->location, no cast, single-target, teleport before hit
DamningStrikesTeleport5 = 42055, // Boss->location, no cast, single-target, teleport before hit
DamningStrikesVisual1 = 40793, // Boss->self, no cast, single-target, visual before hit
DamningStrikesVisual2 = 42052, // Boss->self, no cast, single-target, visual before hit
DamningStrikesVisual3 = 42053, // Boss->self, no cast, single-target, visual before hit
DamningStrikesImpact1 = 40792, // Helper->self, 10.5s cast, range 3 circle, tower 1
DamningStrikesImpact2 = 41110, // Helper->self, 13.0s cast, range 3 circle, tower 2
DamningStrikesImpact3 = 41111, // Helper->self, 15.7s cast, range 3 circle, tower 3
DamningStrikesShockwave = 41112, // Helper->self, no cast, range 100 circle, raidwide with dot if tower is not soaked
DoomArc = 40806, // Boss->self, 15.0s cast, range 100 circle, raidwide with bleed + damage up
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,58 @@ public A14ShadowLordStates(BossModule module) : base(module)

private void SinglePhase(uint id)
{
GigaSlash(id, 10.2f);
UmbraSmashGigaSlash(id + 0x10000, 6.5f);
FlamesOfHatred(id + 0x20000, 4.1f);
Implosion(id + 0x30000, 3.1f);
CthonicFury1(id + 0x40000, 3.5f);
NightfallTeraSlash(id + 0x50000, 4.8f);

GigaSlashNightfall(id + 0x100000, 12.2f);
ShadowSpawnGigaSlashNightfallImplosion(id + 0x110000, 5.4f);
UnbridledRage(id + 0x120000, 2.6f);
EchoesOfAgony(id + 0x130000, 1.2f, 7);
BindingSigil(id + 0x140000, 2.6f);
DamningStrikes(id + 0x150000, 3.5f);
CthonicFury2(id + 0x160000, 9.1f);
ShadowSpawnUmbraSmashGigaSlashNightfall(id + 0x170000, 4.5f);
DoomArc(id + 0x180000, 2.9f);

// TODO: mechanic repeats?..
GigaSlashNightfall(id + 0x200000, 10.2f);
ShadowSpawnGigaSlashNightfallImplosion(id + 0x210000, 5.4f);
UnbridledRage(id + 0x220000, 2.6f); // TODO: never seen this and below...
EchoesOfAgony(id + 0x230000, 1.2f, 7);
BindingSigil(id + 0x240000, 2.6f);
DamningStrikes(id + 0x250000, 3.5f);
CthonicFury2(id + 0x260000, 9.1f);
ShadowSpawnUmbraSmashGigaSlashNightfall(id + 0x270000, 4.5f);
DoomArc(id + 0x280000, 2.9f);
// note: this is a very weird fight, if you wipe, it uses a slightly different script (no initial giga slash and slightly different cthonic fury 1)
Dictionary<bool, (uint seqID, Action<uint> buildState)> dispatch = new()
{
[true] = (1, SinglePhaseInitial),
[false] = (2, SinglePhaseAfterWipe),
};
ConditionFork(id, 10.2f, () => Module.PrimaryActor.CastInfo != null || Module.FindComponent<Teleport>()?.NumCasts > 0, () => (AID)(Module.PrimaryActor.CastInfo?.Action.ID ?? 0) is AID.GigaSlashL or AID.GigaSlashR, dispatch, "First mechanic...")
.ActivateOnEnter<Teleport>()
.DeactivateOnExit<Teleport>();
}

private void SinglePhaseInitial(uint id)
{
GigaSlash(id, 0);
PhaseInitial(id + 0x100000, 6.5f, true);
PhaseRepeats(id + 0x200000, 12.2f);
PhaseRepeats(id + 0x300000, 10.2f);
PhaseRepeats(id + 0x400000, 10.2f);
SimpleState(id + 0xFF0000, 10000, "???");
}

private void SinglePhaseAfterWipe(uint id)
{
PhaseInitial(id + 0x100000, 2.4f, false);
PhaseRepeats(id + 0x200000, 12.2f);
PhaseRepeats(id + 0x300000, 10.2f);
PhaseRepeats(id + 0x400000, 10.2f);
SimpleState(id + 0xFF0000, 10000, "???");
}

private void PhaseInitial(uint id, float delay, bool initialPull)
{
UmbraSmashGigaSlash(id, delay);
FlamesOfHatred(id + 0x10000, 4.1f);
Implosion(id + 0x20000, 3.2f);
CthonicFury1(id + 0x30000, 3.6f, initialPull);
NightfallTeraSlash(id + 0x40000, 4.8f);
}

private void PhaseRepeats(uint id, float delay)
{
GigaSlashNightfall(id, delay);
ShadowSpawnGigaSlashNightfallImplosion(id + 0x10000, 5.4f);
UnbridledRage(id + 0x20000, 2.6f);
EchoesOfAgony(id + 0x30000, 1.2f, 7);
BindingSigil(id + 0x40000, 2.6f);
DamningStrikes(id + 0x50000, 3.5f);
CthonicFury2(id + 0x60000, 9.0f);
ShadowSpawnUmbraSmashGigaSlashNightfall(id + 0x70000, 4.6f);
DoomArc(id + 0x80000, 3);
}

private State GigaSlash(uint id, float delay)
{
CastMulti(id, [AID.GigaSlashL, AID.GigaSlashR], delay, 11)
Expand Down Expand Up @@ -72,18 +93,12 @@ private void Implosion(uint id, float delay)
.DeactivateOnExit<Implosion>();
}

private void BurningCourtMoat(uint id, float delay, bool withRect)
private void BurningCourtMoat(uint id, float delay)
{
Condition(id, delay, () => Module.FindComponent<BurningCourt>()?.Casters.Count > 0 || Module.FindComponent<BurningMoat>()?.Casters.Count > 0)
.ActivateOnEnter<BurningCourt>()
.ActivateOnEnter<BurningMoat>()
.ActivateOnEnter<BurningKeep>(withRect)
.ActivateOnEnter<BurningBattlements>(withRect);
Condition(id + 1, 7, () => Module.FindComponent<BurningCourt>()?.NumCasts > 0 || Module.FindComponent<BurningMoat>()?.NumCasts > 0, "Platform in/out")
.DeactivateOnExit<BurningCourt>()
.DeactivateOnExit<BurningMoat>()
.DeactivateOnExit<BurningKeep>(withRect)
.DeactivateOnExit<BurningBattlements>(withRect);
ComponentCondition<BurningCourtMoatKeepBattlements>(id, delay, comp => comp.AOEs.Count > 0)
.ActivateOnEnter<BurningCourtMoatKeepBattlements>();
ComponentCondition<BurningCourtMoatKeepBattlements>(id + 1, 7, comp => comp.AOEs.Count == 0, "Platform in/out")
.DeactivateOnExit<BurningCourtMoatKeepBattlements>();
}

private void EchoesOfAgony(uint id, float delay, int numCasts)
Expand Down Expand Up @@ -119,14 +134,22 @@ private void DarkNebula(uint id, float delay)
.DeactivateOnExit<DarkNebula>();
}

private void CthonicFury1(uint id, float delay)
private void CthonicFury1(uint id, float delay, bool initialPull)
{
CthonicFuryStart(id, delay);
BurningCourtMoat(id + 0x1000, 8.2f, false);
BurningCourtMoat(id + 0x2000, 3, false);
DarkNebula(id + 0x3000, 6);
Implosion(id + 0x4000, 4);
BurningCourtMoat(id + 0x5000, 2.2f, true);
if (initialPull)
{
BurningCourtMoat(id + 0x1000, 8.2f);
BurningCourtMoat(id + 0x2000, 3.1f);
DarkNebula(id + 0x3000, 6);
}
else
{
BurningCourtMoat(id + 0x1000, 3.2f);
DarkNebula(id + 0x3000, 3);
}
Implosion(id + 0x4000, 4.1f);
BurningCourtMoat(id + 0x5000, 2.2f);
EchoesOfAgony(id + 0x6000, 4, 5);
CthonicFuryEnd(id + 0x7000, 1.7f);
}
Expand Down Expand Up @@ -189,15 +212,15 @@ private void BindingSigil(uint id, float delay)
{
Cast(id, AID.BindingSigil, delay, 12)
.ActivateOnEnter<BindingSigil>();
ComponentCondition<BindingSigil>(id + 2, 2.2f, comp => comp.NumCasts > 0, "Puddles 1"); // 8 or 9
ComponentCondition<BindingSigil>(id + 2, 2.1f, comp => comp.NumCasts > 0, "Puddles 1"); // 8 or 9
ComponentCondition<BindingSigil>(id + 3, 2.5f, comp => comp.NumCasts > 9, "Puddles 2"); // 16 or 17
ComponentCondition<BindingSigil>(id + 4, 2.5f, comp => comp.NumCasts > 17, "Puddles 3") // 25
.DeactivateOnExit<BindingSigil>();
}

private void DamningStrikes(uint id, float delay)
{
Cast(id, AID.DamningStrikes, delay, 8)
CastMulti(id, [AID.DamningStrikes1, AID.DamningStrikes2], delay, 8) // note: alt cast is longer by 0.7s, whatever...
.ActivateOnEnter<DamningStrikes>();
ComponentCondition<DamningStrikes>(id + 2, 2.5f, comp => comp.NumCasts >= 1, "Tower 1");
ComponentCondition<DamningStrikes>(id + 3, 2.5f, comp => comp.NumCasts >= 2, "Tower 2");
Expand All @@ -208,7 +231,7 @@ private void DamningStrikes(uint id, float delay)
private void DarkNebulaGigaSlashNightfall(uint id, float delay)
{
Cast(id, AID.DarkNebula, delay, 3);
ComponentCondition<DarkNebula>(id + 0x10, 1.1f, comp => comp.Casters.Count > 0)
ComponentCondition<DarkNebula>(id + 0x10, 1.2f, comp => comp.Casters.Count > 0)
.ActivateOnEnter<DarkNebula>();
ComponentCondition<DarkNebula>(id + 0x20, 13, comp => comp.NumCasts > 0, "Knockback 1");
ComponentCondition<DarkNebula>(id + 0x21, 3, comp => comp.NumCasts > 1, "Knockback 2");
Expand All @@ -228,18 +251,18 @@ private void CthonicFury2(uint id, float delay)
{
CthonicFuryStart(id, delay);
DarkNebulaGigaSlashNightfall(id + 0x1000, 3.2f);
BurningCourtMoat(id + 0x2000, 3, true);
BurningCourtMoat(id + 0x2000, 3);
DarkNova(id + 0x3000, 5.4f);
CthonicFuryEnd(id + 0x4000, 2);
CthonicFuryEnd(id + 0x4000, 2.1f);
}

private void ShadowSpawnUmbraSmashGigaSlashNightfall(uint id, float delay)
{
Cast(id, AID.ShadowSpawn, delay, 3);
Cast(id + 0x10, AID.UmbraSmashBoss, 4.1f, 4)
Cast(id + 0x10, AID.UmbraSmashBoss, 4.2f, 4)
.ActivateOnEnter<UmbraSmash>();
ComponentCondition<UmbraSmash>(id + 0x20, 0.5f, comp => comp.NumCasts > 0, "Exalines");
GigaSlashNightfall(id + 0x100, 12)
GigaSlashNightfall(id + 0x100, 12.2f)
.DeactivateOnExit<UmbraSmash>();
}

Expand Down
39 changes: 34 additions & 5 deletions BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,46 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
}
}

class BurningCourt(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BurningCourt), new AOEShapeCircle(8));
class BurningMoat(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BurningMoat), new AOEShapeDonut(5, 15));
class BurningKeep(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BurningKeep), new AOEShapeRect(11.5f, 11.5f, 11.5f));
class BurningBattlements(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BurningBattlements), new AOEShapeCustom(BuildPolygon()))
class BurningCourtMoatKeepBattlements(BossModule module) : Components.GenericAOEs(module)
{
public static RelSimplifiedComplexPolygon BuildPolygon()
public readonly List<AOEInstance> AOEs = [];

private static readonly AOEShape _shapeC = new AOEShapeCircle(8);
private static readonly AOEShape _shapeM = new AOEShapeDonut(5, 15);
private static readonly AOEShape _shapeK = new AOEShapeRect(11.5f, 11.5f, 11.5f);
private static readonly AOEShape _shapeB = new AOEShapeCustom(BuildBattlementsPolygon());

private static RelSimplifiedComplexPolygon BuildBattlementsPolygon()
{
RelPolygonWithHoles poly = new([.. CurveApprox.Rect(new(100, 0), new(0, 100))]);
poly.AddHole(CurveApprox.Rect(new(11.5f, 0), new(0, 11.5f)));
return new([poly]);
}

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => AOEs;

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
var shape = ShapeForAction(spell.Action);
if (shape != null)
AOEs.Add(new(shape, caster.Position, spell.Rotation, Module.CastFinishAt(spell)));
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
var shape = ShapeForAction(spell.Action);
if (shape != null)
AOEs.RemoveAll(aoe => aoe.Shape == shape && aoe.Origin.AlmostEqual(caster.Position, 1));
}

private AOEShape? ShapeForAction(ActionID aid) => (AID)aid.ID switch
{
AID.BurningCourt => _shapeC,
AID.BurningMoat => _shapeM,
AID.BurningKeep => _shapeK,
AID.BurningBattlements => _shapeB,
_ => null
};
}

class DarkNebula(BossModule module) : Components.Knockback(module)
Expand Down

0 comments on commit 1c721f8

Please sign in to comment.