Skip to content

Commit

Permalink
Add Menenius Duel Module
Browse files Browse the repository at this point in the history
  • Loading branch information
sourpuh committed Apr 5, 2024
1 parent 3e73a7f commit 27f0430
Show file tree
Hide file tree
Showing 11 changed files with 593 additions and 9 deletions.
1 change: 1 addition & 0 deletions BossMod/BossModule/ArenaColor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public static class ArenaColor
public const uint SafeFromAOE = 0x80008000;
public const uint Danger = 0xff00ffff;
public const uint Safe = 0xff00ff00;
public const uint Trap = 0x80000080;
public const uint PC = 0xff00ff00;
public const uint Enemy = 0xff0000ff;
public const uint Object = 0xff0080ff;
Expand Down
16 changes: 9 additions & 7 deletions BossMod/Components/Exaflare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class Line
public float TimeToMove;
public int ExplosionsLeft;
public int MaxShownExplosions;
// non-circular Shapes may be rotated based on the orientation of the caster.
public Angle Rotation;
}

public AOEShape Shape { get; private init; }
Expand All @@ -28,15 +30,15 @@ public class Line

public override IEnumerable<AOEInstance> ActiveAOEs(BossModule module, int slot, Actor actor)
{
foreach (var (c, t) in FutureAOEs(module.WorldState.CurrentTime))
yield return new(Shape, c, activation: t, color: FutureColor);
foreach (var (c, t) in ImminentAOEs())
yield return new(Shape, c, activation: t, color: ImminentColor);
foreach (var (c, t, a) in FutureAOEs(module.WorldState.CurrentTime))
yield return new(Shape, c, rotation: a, activation: t, color: FutureColor);
foreach (var (c, t, a) in ImminentAOEs())
yield return new(Shape, c, rotation: a, activation: t, color: ImminentColor);
}

protected IEnumerable<(WPos, DateTime)> ImminentAOEs() => Lines.Where(l => l.ExplosionsLeft > 0).Select(l => (l.Next, l.NextExplosion));
protected IEnumerable<(WPos, DateTime, Angle)> ImminentAOEs() => Lines.Where(l => l.ExplosionsLeft > 0).Select(l => (l.Next, l.NextExplosion, l.Rotation));

protected IEnumerable<(WPos, DateTime)> FutureAOEs(DateTime currentTime)
protected IEnumerable<(WPos, DateTime, Angle)> FutureAOEs(DateTime currentTime)
{
foreach (var l in Lines)
{
Expand All @@ -47,7 +49,7 @@ public override IEnumerable<AOEInstance> ActiveAOEs(BossModule module, int slot,
{
pos += l.Advance;
time = time.AddSeconds(l.TimeToMove);
yield return (pos, time);
yield return (pos, time, l.Rotation);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,9 @@ public FierceBeating() : base(4) { }

public override IEnumerable<AOEInstance> ActiveAOEs(BossModule module, int slot, Actor actor)
{
foreach (var (c, t) in FutureAOEs(module.WorldState.CurrentTime))
foreach (var (c, t, a) in FutureAOEs(module.WorldState.CurrentTime))
yield return new(Shape, c, activation: t, color: FutureColor);
foreach (var (c, t) in ImminentAOEs())
foreach (var (c, t, a) in ImminentAOEs())
yield return new(Shape, c, activation: t, color: ImminentColor);
if (Lines.Count > 0 && linesstartedcount1 < 8)
yield return new(circle, CalculateCirclePosition(linesstartedcount1, module.Bounds.Center, _casters[0]), activation: _activation.AddSeconds(linesstartedcount1 * 3.7f));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius;

internal class BlueHiddenMines : Components.GenericTowers
{
public override void OnEventCast(BossModule module, Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.ActivateBlueMine)
{
Towers.Add(new(caster.Position, 3.6f));
}
else if ((AID)spell.Action.ID is AID.DetonateBlueMine)
{
Towers.RemoveAll(t => t.Position.AlmostEqual(caster.Position, 1));
}
}

public override void AddHints(BossModule module, int slot, Actor actor, TextHints hints, MovementHints? movementHints)
{
if (Towers.Count > 0)
{
hints.Add("Soak the mine!");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using BossMod.Components;

namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius;

internal class SpiralScourge : SingleTargetCast
{
public SpiralScourge() :
base(ActionID.MakeSpell(AID.SpiralScourge), "Use Manawall, Excellence, or Invuln.")
{ }
}

internal class CallousCrossfire : SingleTargetCast
{
public CallousCrossfire() :
base(ActionID.MakeSpell(AID.CallousCrossfire), "Use Light Curtain / Reflect.")
{ }
}

class ReactiveMunition : StayMove
{
public override void OnStatusGain(BossModule module, Actor actor, ActorStatus status)
{
if ((SID)status.ID is SID.AccelerationBomb)
{
if (module.Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0 && slot < Requirements.Length)
Requirements[slot] = Requirement.Stay;
}
}

public override void OnStatusLose(BossModule module, Actor actor, ActorStatus status)
{
if ((SID)status.ID is SID.AccelerationBomb)
{
if (module.Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0 && slot < Requirements.Length)
Requirements[slot] = Requirement.None;
}
}
}

class SenseWeakness : StayMove
{
public override void OnCastStarted(BossModule module, Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.SenseWeakness)
{
if (module.Raid.FindSlot(caster.TargetID) is var slot && slot >= 0 && slot < Requirements.Length)
Requirements[slot] = Requirement.Move;
}
}

public override void OnEventCast(BossModule module, Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID == AID.SenseWeakness)
{
if (module.Raid.FindSlot(caster.TargetID) is var slot && slot >= 0 && slot < Requirements.Length)
Requirements[slot] = Requirement.None;
}
}
}

class MagitekImpetus : StatusDrivenForcedMarch
{
public MagitekImpetus() : base(
3,
(uint)SID.ForwardMarch,
(uint)SID.AboutFace,
(uint)SID.LeftFace,
(uint)SID.RightFace)
{
ActivationLimit = 1;
}
}

class ProactiveMunition : StandardChasingAOEs
{
public ProactiveMunition() : base(
new AOEShapeCircle(6),
ActionID.MakeSpell(AID.ProactiveMunitionTrackingStart),
ActionID.MakeSpell(AID.ProactiveMunitionTrackingMove),
6,
1,
5)
{ }
}


[ModuleInfo(NameID = 0x25DF)]
public class Duel5Menenius : BossModule
{
public Duel5Menenius(WorldState ws, Actor primary) : base(ws, primary, new ArenaBoundsSquare(new(-810, 520 /*y=260.3*/), 20)) { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius;

public enum AID : uint
{
AutoAttack = 6497,
TeraTempest = 0x5D60,
CallousCrossfire = 23901,
MagitekMinefield = 23887,
ActivateBlueMine = 23888,
DetonateBlueMine = 23890,
ActivateRedMine = 23889,
DetonateRedMine = 0x5D41,
IndiscriminateDetonation = 23892,
GigaTempest = 23875,
GigaTempestLargeStart = 0x5D44,
GigaTempestLargeMove = 0x5D46,
GigaTempestSmallStart = 0x5D45,
GigaTempestSmallMove = 0x5D47,
Ruination = 23880,
RuinationExaStart = 23881,
RuinationExaMove = 23882,
SpiralScourge = 23900,
WindslicerShot = 23883,
GunberdWindslicer = 23885,
DarkShot = 23884,
GunberdDark = 23886,
ProactiveMunition = 0x5D58,
ProactiveMunitionTrackingStart = 0x5D59,
ProactiveMunitionTrackingMove = 0x5D5A,
ReactiveMunition = 23894,
SenseWeakness = 23893,
MagitekImpetus = 23899,
};

public enum SID : uint
{
ForwardMarch = 0x50D,
AboutFace = 0x50E,
LeftFace = 0x50F,
RightFace = 0x510,
AccelerationBomb = 0x430,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using static BossMod.Shadowbringers.Foray.Duel.Duel5Menenius.GigaTempest;

namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius;

class Duel5MeneniusStates : StateMachineBuilder
{
uint i = 1;
private uint nextId => i++ * 0x10000;

public Duel5MeneniusStates(BossModule module) : base(module)
{
DeathPhase(0, SinglePhase)
.ActivateOnEnter<ReactiveMunition>()
.ActivateOnEnter<GunberdShot>()
.ActivateOnEnter<LargeGigaTempest>()
.ActivateOnEnter<SmallGigaTempest>()
.ActivateOnEnter<RuinationExaflare>()
.ActivateOnEnter<ProactiveMunition>()
.ActivateOnEnter<MagitekImpetus>()
.ActivateOnEnter<BlueHiddenMines>()
.ActivateOnEnter<RedHiddenMines>();
}

private void SinglePhase(uint id)
{
CallousCrossfire(id, 13);

MagitekMinefield(id + nextId, 12);
GigaTempest(id + nextId, 11.25f);
ReadyShot(id + nextId, 15.5f);
Gunberd(id + nextId, 2);
MagitekImpetus(id + nextId, 2.6f);
MagitekMinefield(id + nextId, 6.2f);
ReadyShot(id + nextId, 10.25f);
Gunberd(id + nextId, 2);
MagitekMinefield(id + nextId, 5.6f);
Ruination(id + nextId, 6.5f);
SpiralScourge(id + nextId, 18.25f);

ProactiveMunition(id + nextId, 7.25f);
MagitekMinefield(id + nextId, 7.25f);
ReactiveMunition(id + nextId, 7.25f);
SenseWeakness(id + nextId, 13);
ReadyShot(id + nextId, 12.25f);
GigaTempest(id + nextId, 2);
MagitekImpetus(id + nextId, 3.3f);
Gunberd(id + nextId, 10);
ReactiveMunition(id + nextId, 13.6f);
Ruination(id + nextId, 4f);
SenseWeakness(id + nextId, 8.25f);
IndiscriminateDetonation(id + nextId, 3.25f);

ReadyShot(id + nextId, 11.25f);
ReactiveMunition(id + nextId, 2);
MagitekMinefield(id + nextId, 2);
MagitekImpetus(id + nextId, 10.3f);
MagitekMinefield(id + nextId, 8.25f);
Gunberd(id + nextId, 9.25f);
MagitekMinefield(id + nextId, 8.6f);
ReadyShot(id + nextId, 12.5f);
Ruination(id + nextId, 2.25f);
MagitekMinefield(id + nextId, 10.3f);
Gunberd(id + nextId, 9.3f);
ReadyShot(id + nextId, 13.8f);
GigaTempest(id + nextId, 2.2f);
MagitekImpetus(id + nextId, 3.5f);
Gunberd(id + nextId, 10.3f);
ReactiveMunition(id + nextId, 13.7f);
Ruination(id + nextId, 4.5f);
SenseWeakness(id + nextId, 8.25f);
IndiscriminateDetonation(id + nextId, 3.2f);

TeraTempest(id + nextId, 12.9f);
}

private void CallousCrossfire(uint id, float delay)
{
Cast(id, AID.CallousCrossfire, delay, 4, "Turret Crossfire")
.ActivateOnEnter<CallousCrossfire>()
.DeactivateOnExit<CallousCrossfire>();
}

private void MagitekMinefield(uint id, float delay)
{
Cast(id, AID.MagitekMinefield, delay, 3, "Place Mine");
}

private void IndiscriminateDetonation(uint id, float delay)
{
Cast(id, AID.IndiscriminateDetonation, delay, 4, "Detonate Mines");
}

private void GigaTempest(uint id, float delay)
{
Cast(id, AID.GigaTempest, delay, 5, "Gigatempest");
}

private void MagitekImpetus(uint id, float delay)
{
Cast(id, AID.MagitekImpetus, delay, 3, "Place Forced March");
}

private void ReadyShot(uint id, float delay)
{
CastMulti(id, new[] { AID.DarkShot, AID.WindslicerShot }, delay, 4, "Load Dark/Windslicer Shot");
}

private void Gunberd(uint id, float delay)
{
CastMulti(id, new[] { AID.GunberdDark, AID.GunberdWindslicer }, delay, 4, "Shoot Dark/Windslicer Shot");
}

private void Ruination(uint id, float delay)
{
Cast(id, AID.Ruination, delay, 4, "Ruination")
.ActivateOnEnter<RuinationCross>()
.DeactivateOnExit<RuinationCross>();
}

private void SpiralScourge(uint id, float delay)
{
Cast(id, AID.SpiralScourge, delay, 6, "Tankbuster")
.ActivateOnEnter<SpiralScourge>()
.DeactivateOnExit<SpiralScourge>();
}
private void ProactiveMunition(uint id, float delay)
{
Cast(id, AID.ProactiveMunition, delay, 5, "Chasing AOE");
}

private void ReactiveMunition(uint id, float delay)
{
Cast(id, AID.ReactiveMunition, delay, 3, "Place Acceleration Bomb");
}

private void SenseWeakness(uint id, float delay)
{
Cast(id, AID.SenseWeakness, delay, 4.5f, "Move")
.ActivateOnEnter<SenseWeakness>()
.DeactivateOnExit<SenseWeakness>();
}

private void TeraTempest(uint id, float delay)
{
Cast(id + nextId, AID.TeraTempest, delay, 25, "Enrage");
}
}
Loading

0 comments on commit 27f0430

Please sign in to comment.