Skip to content

Commit

Permalink
Merge pull request #48 from FFXIV-CombatReborn/Delubrum-Reginae-Normal
Browse files Browse the repository at this point in the history
basic Delubrum Reginae Normal support
  • Loading branch information
LTS-FFXIV authored Apr 20, 2024
2 parents 0864452 + 224e65b commit 7df0e59
Show file tree
Hide file tree
Showing 103 changed files with 2,056 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class MercifulBreeze(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.MercifulBreeze), new AOEShapeRect(50, 2.5f));
class MercifulBlooms(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.MercifulBlooms), new AOEShapeCircle(20));
class MercifulArc(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCone(12, 45.Degrees()), (uint)IconID.MercifulArc, ActionID.MakeSpell(AID.MercifulArc)); // TODO: verify angle

class BurningChains(BossModule module) : Components.Chains(module, (uint)TetherID.BurningChains, ActionID.MakeSpell(AID.ScorchingShackle));

class IronImpact(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.IronImpact));
class IronRose(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.IronRose), new AOEShapeRect(50, 4));

class DeadIron : Components.BaitAwayTethers
{
public DeadIron(BossModule module) : base(module, new AOEShapeCone(50, 15.Degrees()), (uint)TetherID.DeadIron, ActionID.MakeSpell(AID.DeadIronAOE)) { DrawTethers = false; }
}

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

protected override void DrawArenaForeground(int pcSlot, Actor pc)
{
for (int i = 0; i < 4; ++i)
{
var center = (45 + i * 90).Degrees();
Arena.PathArcTo(Bounds.Center, BarricadeRadius, (center - 22.5f.Degrees()).Rad, (center + 22.5f.Degrees()).Rad);
MiniArena.PathStroke(false, ArenaColor.Border, 2);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

public enum OID : uint
{
Boss = 0x3131, // R4.000, x1
Helper = 0x233C, // R0.500, x16, 523 type
AetherialOrb = 0x3132, // R2.000, x0 (spawn during fight)
}


public enum AID : uint
{
AutoAttack1 = 6497, // Boss->player, no cast, single-target
AutoAttack2 = 6499, // Boss->player, no cast, single-target
Teleport = 23194, // Boss/SeekerAvatar->location, no cast, single-target, teleport

VerdantTempest = 23222, // Boss->self, 5.0s cast, range 50 circle

VerdantPathKatana = 23191, // Boss->self, 3.0s cast, single-target, visual (switch to katana, cross aoe)
ActOfMercy = 23218, // Boss->self, 3.0s cast, range 50 width 8 cross
FirstMercy = 23389, // Boss->self, 3.0s cast, single-target
SecondMercy = 23390, // Boss->self, 3.0s cast, single-target
ThirdMercy = 23391, // Boss->self, 3.0s cast, single-target
FourthMercy = 23392, // Boss->self, 3.0s cast, single-target
MercyFourfold = 23195, // Boss->self, 2.0s cast, single-target
MercyFourfoldAOE = 23348, // Helper->self, 1.7s cast, range 50 180-degree cone
MercyFourfold1 = 23196, // Boss->self, no cast, single-target
MercyFourfold2 = 23197, // Boss->self, no cast, single-target
MercyFourfold3 = 23198, // Boss->self, no cast, single-target
MercyFourfold4 = 23199, // Boss->self, no cast, single-target
SeasonsOfMercy = 23210, // SeekerAvatar/Boss->self, 5.0s cast, single-target, visual (crisscross + gaze + big aoe)
MercifulBreeze = 23211, // Helper->self, 2.5s cast, range 50 width 5 rect aoe (crisscross)
MercifulMoon = 23212, // AetherialOrb->self, no cast, range 50 circle gaze
MercifulBlooms = 23213, // Helper->self, 9.0s cast, range 4 circle aoe (real radius is 20 due to influence up)
MercifulArc = 23223, // Boss->self, no cast, range 12 ?-degree cone cleave

VerdantPathSword = 23192, // Boss->self, 3.0s cast, single-target, visual (switch to sword, side aoes)
BalefulSwathe = 23219, // Boss->self, no cast, single-target, visual (side aoes)
BalefulSwatheAOE = 23220, // Helper->self, no cast, range 50 ?-degree cone (doesn't really look like cone...)
PhantomEdge = 23200, // Boss->self, 4.0s cast, single-target, visual (applies status changing some effects)
ScorchingShackle = 23214, // Helper->self, no cast, ??? (happens if chains aren't broken in time)
BalefulBlade1 = 23336, // Boss->self, 8.0s cast, range 30 circle, visual (knockback, 'blockable' variant)
BalefulBlade2 = 23337, // Boss->self, 8.0s cast, range 30 circle, visual (knockback, 'unblockable' variant)
BalefulBladeAOE1 = 23201, // Helper->self, no cast, ???, LOSable knockback 30
BalefulBladeAOE2 = 23202, // Helper->self, no cast, ???, knockback 30

VerdantPathFist = 23193, // Boss->self, 3.0s cast, single-target, visual (switch to fists, line stack)
IronImpact = 23259, // Boss->self, no cast, range 50 width 8 rect line stack
IronRose = 23221, // SeekerAvatar->self, 3.5s cast, range 50 width 8 rect aoe
IronSplitter = 23203, // Boss/SeekerAvatar->self, 5.0s cast, single-target, visual (tiles/sands)
IronSplitterTile1 = 23204, // Helper->self, no cast, range 4 circle
IronSplitterTile2 = 23205, // Helper->self, no cast, range 8-12 donut
IronSplitterTile3 = 23206, // Helper->self, no cast, range 16-20 donut
IronSplitterSand1 = 23207, // Helper->self, no cast, range 4-8 donut
IronSplitterSand2 = 23208, // Helper->self, no cast, range 12-16 donut
IronSplitterSand3 = 23209, // Helper->self, no cast, range 20-25 donut
DeadIron = 23215, // SeekerAvatar->self, 4.0s cast, single-target, visual (earthshakers)
DeadIronAOE = 23216, // Helper->self, no cast, range 50 30-degree cone earthshaker
DeadIronSecond = 23364, // SeekerAvatar->self, no cast, single-target, visual (second earthshakers, without cast)

Ability_Unknown = 14588, // Helper->player, no cast, single-target
}


public enum SID : uint
{
Mercy = 2056, // none->Boss, extra=0xFA/0xF8/0xF9/0xF7
BalefulAir = 2490, // Boss->Boss, extra=0x195
PhantomEdge = 2488, // Boss->Boss, extra=0x0
IronAir = 2491, // Boss->Boss, extra=0x196
BurningChains = 769, // none->player, extra=0x0
MercifulAir = 2489, // Boss->Boss, extra=0x194
}

public enum IconID : uint
{
MercifulArc = 243, // player
BurningChains = 238, // player
DeadIron = 237, // player
}

public enum TetherID : uint
{
BurningChains = 128, // player->player
DeadIron = 138, // player->Boss
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class DRN1TrinitySeekerStates : StateMachineBuilder
{
public DRN1TrinitySeekerStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<MercifulBreeze>()
.ActivateOnEnter<MercifulBlooms>()
.ActivateOnEnter<MercifulArc>()
.ActivateOnEnter<BurningChains>()
.ActivateOnEnter<IronImpact>()
.ActivateOnEnter<IronRose>()
//.ActivateOnEnter<ActOfMercy>() //cross aoes always on
//.ActivateOnEnter<BalefulBlade>() //hide behind barrier always on
//.ActivateOnEnter<BalefulSwathe>() //side aoes always on
.ActivateOnEnter<IronSplitter>()
.ActivateOnEnter<MercyFourfold>()
//.ActivateOnEnter<MercifulMoon>() //gaze mechanic does not disable
.ActivateOnEnter<DeadIron>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class ActOfMercy(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.ActOfMercy))
{
private readonly DateTime _activation = module.WorldState.FutureTime(7.6f); // from verdant path cast start
private static readonly AOEShapeCross _shape = new(50, 4);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
yield return new(_shape, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation, _activation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

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());

public override void AddHints(int slot, Actor actor, TextHints hints)
{
hints.Add(_phantomEdge ? "Stay in front of barricade!" : "Hide behind barricade!", !IsSafe(actor));
}

public override void DrawArenaBackground(int pcSlot, Actor pc)
{
AOEShape shape = _phantomEdge ? _shapeFront : _shapeBehind;
for (int i = 0; i < 4; ++i)
{
var center = (45 + i * 90).Degrees();
shape.Draw(Arena, Module.Bounds.Center, center, ArenaColor.SafeFromAOE);
}
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.BalefulBlade2)
_phantomEdge = true;
}

public bool IsSafe(Actor actor)
{
var offset = actor.Position - Module.Bounds.Center;
var angle = Angle.FromDirection(offset).Rad; // 4 barricades to check, at +-45 and +-135
angle = Math.Abs(angle); // fold around z axis, leaving two barricades to check - at 45 and 135
angle = Math.Abs(angle - 90.Degrees().Rad); // rotate and fold again, leaving one barricade at 45 +- 22.5
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;
return behind != _phantomEdge;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class BalefulSwathe(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.BalefulSwathe))
{
private DateTime _activation = module.WorldState.FutureTime(7.6f); // from verdant path cast start
private static readonly AOEShapeRect _shape = new(50, 50, -5);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
yield return new(_shape, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation + 90.Degrees(), _activation);
yield return new(_shape, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation - 90.Degrees(), _activation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class IronSplitter(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.IronSplitter))
{
private List<AOEInstance> _aoes = new();

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

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if (spell.Action == WatchedAction)
{
var distance = (caster.Position - Module.Bounds.Center).Length();
if (distance is < 3 or > 9 and < 11 or > 17 and < 19) // tiles
{
_aoes.Add(new(new AOEShapeCircle(4), Module.Bounds.Center, new(), spell.NPCFinishAt));
_aoes.Add(new(new AOEShapeDonut(8, 12), Module.Bounds.Center, new(), spell.NPCFinishAt));
_aoes.Add(new(new AOEShapeDonut(16, 20), Module.Bounds.Center, new(), spell.NPCFinishAt));
}
else
{
_aoes.Add(new(new AOEShapeDonut(4, 8), Module.Bounds.Center, new(), spell.NPCFinishAt));
_aoes.Add(new(new AOEShapeDonut(12, 16), Module.Bounds.Center, new(), spell.NPCFinishAt));
_aoes.Add(new(new AOEShapeDonut(20, 25), Module.Bounds.Center, new(), spell.NPCFinishAt));
}
}
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if (spell.Action == WatchedAction)
{
_aoes.Clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class MercyFourfold(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.MercyFourfoldAOE))
{
public readonly List<AOEInstance> AOEs = new();
private readonly List<AOEInstance?> _safezones = new();
private static readonly AOEShapeCone _shapeAOE = new(50, 90.Degrees());
private static readonly AOEShapeCone _shapeSafe = new(50, 45.Degrees());

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (AOEs.Count > 0)
yield return AOEs[0];
if (_safezones.Count > 0 && _safezones[0] != null)
yield return _safezones[0]!.Value;
}

public override void OnStatusGain(Actor actor, ActorStatus status)
{
if ((SID)status.ID != SID.Mercy)
return;

var dirOffset = status.Extra switch
{
0xF7 => -45.Degrees(),
0xF8 => -135.Degrees(),
0xF9 => 45.Degrees(),
0xFA => 135.Degrees(),
_ => 0.Degrees()
};
if (dirOffset == default)
return;

var dir = actor.Rotation + dirOffset;
if (AOEs.Count > 0)
{
// see whether there is a safezone for two contiguous aoes
var mid = dir.ToDirection() + AOEs.Last().Rotation.ToDirection(); // length should be either ~sqrt(2) or ~0
if (mid.LengthSq() > 1)
_safezones.Add(new(_shapeSafe, actor.Position, Angle.FromDirection(-mid), new(), ArenaColor.SafeFromAOE, false));
else
_safezones.Add(null);
}

var activationDelay = 15 - 1.3f * AOEs.Count;
AOEs.Add(new(_shapeAOE, actor.Position, dir, WorldState.FutureTime(activationDelay)));
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
base.OnEventCast(caster, spell);
if (spell.Action == WatchedAction)
{
if (AOEs.Count > 0)
AOEs.RemoveAt(0);
if (_safezones.Count > 0)
_safezones.RemoveAt(0);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN1TrinitySeeker;

class MercifulMoon(BossModule module) : Components.GenericGaze(module, ActionID.MakeSpell(AID.MercifulMoon))
{
private Eye? _eye;

public override IEnumerable<Eye> ActiveEyes(int slot, Actor actor) => Utils.ZeroOrOne(_eye);

public override void Update()
{
if (_eye == null && Module.Enemies(OID.AetherialOrb).FirstOrDefault() is var orb && orb != null)
_eye = new(orb.Position, WorldState.FutureTime(5.8f)); // time from spawn to cast
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN2Dahu;

class FallingRock(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.FallingRock), 4);
class HotCharge(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.HotCharge), 4);
class Firebreathe(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Firebreathe), new AOEShapeCone(60, 45.Degrees()));
class HeadDown(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.HeadDown), 2);
class HuntersClaw(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HuntersClaw), new AOEShapeCircle(8));

[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "CombatReborn Team", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 760, NameID = 9751)]
public class DRN2Dahu(WorldState ws, Actor primary) : BossModule(ws, primary, new ArenaBoundsCircle(new(82, 138), 30))
{
protected override void DrawEnemies(int pcSlot, Actor pc)
{
base.DrawEnemies(pcSlot, pc);
Arena.Actors(Enemies(OID.Marchosias), ArenaColor.Enemy);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace BossMod.Shadowbringers.Foray.DelubrumReginae.Normal.DRN2Dahu;

public enum OID : uint
{
Dahu = 0x233C, // R0.500, x?, 523 type
Boss = 0x30A6, // R4.600, x?
Marchosias = 0x30A7, // R2.500, x?
}

public enum AID : uint
{
AutoAttack = 6497, // Boss/CrownedMarchosias->player, no cast, single-target
ReverberatingRoar = 22363, // Boss->self, no cast, single-target, visual (falling rocks)
FallingRock = 22364, // Helper->location, 3.0s cast, range 4 circle puddle
HotCharge = 22372, // Boss->location, 3.0s cast, width 8 rect charge
Firebreathe = 22373, // Boss->self, 5.0s cast, range 60 90-degree cone
AddAppear = 22360, // Marchosias/CrownedMarchosias->self, no cast, single-target, visual
HeadDown = 22358, // Marchosias->location, 3.0s cast, width 4 rect charge
LeftSidedShockwaveFirst = 22368, // Boss->self, 3.0s cast, range 15 180-degree cone
RightSidedShockwaveFirst = 22369, // Boss->self, 3.0s cast, range 15 180-degree cone
LeftSidedShockwaveSecond = 22370, // Boss->self, 1.0s cast, range 15 180-degree cone
RightSidedShockwaveSecond = 22371, // Boss->self, 1.0s cast, range 15 180-degree cone
FeralHowl = 22357, // Boss->self, 5.0s cast, single-target, visual (knockback)
HuntersClaw = 22359, // Marchosias->self, 8.5s cast, range 8 circle puddle
FirebreatheRotating = 22361, // Boss->self, 5.0s cast, single-target, visual (rotating cone)
FirebreatheRotatingAOE = 22362, // Boss->self, 0.5s cast, range 60 90-degree cone

//Savage-only below
//FeralHowlAOE = 23349, // Helper->self, no cast, range 50 circle, raidwide knockback 30
//SpitFlame = 22390, // Boss->self, 8.0s cast, single-target, visual (icons)
//SpitFlameAOE = 22391, // Boss->players, no cast, range 4 circle
//HystericAssault = 22392, // Boss->self, 5.0s cast, single-target, visual (knockback)
//HystericAssaultAOE = 23363, // Helper->self, no cast, range 50 circle, raidwide knockback 30
//Burn = 21603, // Helper->players, no cast, range 45 circle with ? falloff
}

public enum SID : uint
{
Staggered = 715, // Boss->player, extra=0xECA
TwiceComeRuin = 2485, // Boss->player, extra=0x1
}

public enum IconID : uint
{
FirebreatheCW = 167, // Boss
FirebreatheCCW = 168, // Boss
}
Loading

0 comments on commit 7df0e59

Please sign in to comment.