diff --git a/BossMod/BossModule/ArenaColor.cs b/BossMod/BossModule/ArenaColor.cs index d13ea24eb0..b7629dee40 100644 --- a/BossMod/BossModule/ArenaColor.cs +++ b/BossMod/BossModule/ArenaColor.cs @@ -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; diff --git a/BossMod/Modules/Endwalker/Alliance/A32Llymlaen/LlymlaenSurgingWave.cs b/BossMod/Modules/Endwalker/Alliance/A32Llymlaen/LlymlaenSurgingWave.cs index 3075af6c44..a3f34bc1ca 100644 --- a/BossMod/Modules/Endwalker/Alliance/A32Llymlaen/LlymlaenSurgingWave.cs +++ b/BossMod/Modules/Endwalker/Alliance/A32Llymlaen/LlymlaenSurgingWave.cs @@ -18,6 +18,7 @@ public override void OnEventEnvControl(BossModule module, byte index, uint state if (state == 0x08000400 && index == 0x49) Shape = Arena.ExtendEast; } + public override void Update(BossModule module) { diff --git a/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33Oschon.cs b/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33Oschon.cs index a567ade795..3f2ed292d2 100644 --- a/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33Oschon.cs +++ b/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33Oschon.cs @@ -101,4 +101,4 @@ protected override void DrawEnemies(int pcSlot, Actor pc) Arena.Actor(_oschonP1, ArenaColor.Enemy); Arena.Actor(_oschonP2, ArenaColor.Enemy); } -} \ No newline at end of file +} diff --git a/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33OschonEnum.cs b/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33OschonEnum.cs index 32416a7126..d807f38a8a 100644 --- a/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33OschonEnum.cs +++ b/BossMod/Modules/Endwalker/Alliance/A33Oschon/A33OschonEnum.cs @@ -90,4 +90,4 @@ public enum IconID : uint FlintedFoehnStack = 316, // player TankbusterP1 = 344, // player TankbusterP2 = 500, // player -}; \ No newline at end of file +}; diff --git a/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonFlintedFoehn.cs b/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonFlintedFoehn.cs index 4b5250534f..1ff13182c2 100644 --- a/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonFlintedFoehn.cs +++ b/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonFlintedFoehn.cs @@ -50,4 +50,4 @@ public override void OnEventIcon(BossModule module, Actor actor, uint iconID) if (iconID == (uint)IconID.FlintedFoehnStack) AddStack(actor, module.WorldState.CurrentTime.AddSeconds(4.5f)); } -} \ No newline at end of file +} diff --git a/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingShot.cs b/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingShot.cs index 4450c0e826..cda242b50c 100644 --- a/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingShot.cs +++ b/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingShot.cs @@ -25,4 +25,4 @@ public override void OnEventCast(BossModule module, Actor caster, ActorCastEvent if ((AID)spell.Action.ID == AID.GreatWhirlwind) _aoe = null; } -} \ No newline at end of file +} diff --git a/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingVolley.cs b/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingVolley.cs index 0da2a7abc0..170731ec3b 100644 --- a/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingVolley.cs +++ b/BossMod/Modules/Endwalker/Alliance/A33Oschon/OschonP2WanderingVolley.cs @@ -3,7 +3,7 @@ class WanderingVolley : Components.Knockback { private readonly List _sources = []; - private static AOEShapeCone _shape = new(30, 90.Degrees()); + private static readonly AOEShapeCone _shape = new(30, 90.Degrees()); public override IEnumerable Sources(BossModule module, int slot, Actor actor) => _sources; @@ -63,4 +63,4 @@ public WanderingVolleyRaidwide1() : base(ActionID.MakeSpell(AID.WanderingVolley) class WanderingVolleyRaidwide2 : Components.RaidwideCast { public WanderingVolleyRaidwide2() : base(ActionID.MakeSpell(AID.WanderingVolley2)) { } -} \ No newline at end of file +} diff --git a/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaByregotStrike.cs b/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaByregotStrike.cs index cbcd6cbea9..ac5476a693 100644 --- a/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaByregotStrike.cs +++ b/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaByregotStrike.cs @@ -14,7 +14,7 @@ class ByregotStrikeCone : Components.GenericAOEs { private readonly List _aoes = []; - private static AOEShapeCone _shape = new(90, 22.5f.Degrees()); + private static readonly AOEShapeCone _shape = new(90, 22.5f.Degrees()); public override IEnumerable ActiveAOEs(BossModule module, int slot, Actor actor) => _aoes; diff --git a/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaQuintessence.cs b/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaQuintessence.cs index f8ac584e03..08dbfc1601 100644 --- a/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaQuintessence.cs +++ b/BossMod/Modules/Endwalker/Alliance/A36Eulogia/EulogiaQuintessence.cs @@ -128,7 +128,7 @@ public override void OnCastStarted(BossModule module, Actor caster, ActorCastInf if (_index is 0x53 or 0x54 or 0x57 or 0x56 or 0x51 or 0x50) _aoes.Add(new(donut, position, activation: _activation3)); } - } + } public override void OnCastFinished(BossModule module, Actor caster, ActorCastInfo spell) { @@ -138,4 +138,4 @@ public override void OnCastFinished(BossModule module, Actor caster, ActorCastIn _aoes.RemoveAt(0); } } -} \ No newline at end of file +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/BlueHiddenMines.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/BlueHiddenMines.cs new file mode 100644 index 0000000000..621ced2495 --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/BlueHiddenMines.cs @@ -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!"); + } + } +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5Menenius.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5Menenius.cs new file mode 100644 index 0000000000..f264b9832c --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5Menenius.cs @@ -0,0 +1,72 @@ +namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; + +class SpiralScourge : Components.SingleTargetCast +{ + public SpiralScourge() : base(ActionID.MakeSpell(AID.SpiralScourge), "Use Manawall, Excellence, or Invuln.") { } +} + +class CallousCrossfire : Components.SingleTargetCast +{ + public CallousCrossfire() : base(ActionID.MakeSpell(AID.CallousCrossfire), "Use Light Curtain / Reflect.") { } +} + +class ReactiveMunition : Components.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 : Components.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 : Components.StatusDrivenForcedMarch +{ + public MagitekImpetus() : base(3, (uint)SID.ForwardMarch, (uint)SID.AboutFace, (uint)SID.LeftFace, (uint)SID.RightFace) + { + ActivationLimit = 1; + } +} + +class ProactiveMunition : Components.StandardChasingAOEs +{ + public ProactiveMunition() : base(new AOEShapeCircle(6), ActionID.MakeSpell(AID.ProactiveMunitionTrackingStart), ActionID.MakeSpell(AID.ProactiveMunitionTrackingMove), 6, 1, 5) { } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "SourP", GroupType = BossModuleInfo.GroupType.BozjaDuel, GroupID = 778, NameID = 23)] // bnpcname=9695 +public class Duel5Menenius : BossModule +{ + public Duel5Menenius(WorldState ws, Actor primary) : base(ws, primary, new ArenaBoundsSquare(new(-810, 520 /*y=260.3*/), 20)) { } +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5MeneniusEnums.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5MeneniusEnums.cs new file mode 100644 index 0000000000..4061c5eaff --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5MeneniusEnums.cs @@ -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, +}; diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5MeneniusStates.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5MeneniusStates.cs new file mode 100644 index 0000000000..16f4ba9f09 --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Duel5MeneniusStates.cs @@ -0,0 +1,142 @@ +namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; + +class Duel5MeneniusStates : StateMachineBuilder +{ + public Duel5MeneniusStates(BossModule module) : base(module) + { + DeathPhase(0, SinglePhase) + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } + + private void SinglePhase(uint id) + { + CallousCrossfire(id, 13); + + MagitekMinefield(id + 0x10000, 12); + GigaTempest(id + 0x20000, 11.25f); + ReadyShot(id + 0x30000, 15.5f); + Gunberd(id + 0x40000, 2); + MagitekImpetus(id + 0x50000, 2.6f); + MagitekMinefield(id + 0x60000, 6.2f); + ReadyShot(id + 0x70000, 10.25f); + Gunberd(id + 0x80000, 2); + MagitekMinefield(id + 0x90000, 5.6f); + Ruination(id + 0xA0000, 6.5f); + SpiralScourge(id + 0xB0000, 18.25f); + + ProactiveMunition(id + 0x100000, 7.25f); + MagitekMinefield(id + 0x110000, 7.25f); + ReactiveMunition(id + 0x120000, 7.25f); + SenseWeakness(id + 0x130000, 13); + ReadyShot(id + 0x140000, 12.25f); + GigaTempest(id + 0x150000, 2); + MagitekImpetus(id + 0x160000, 3.3f); + Gunberd(id + 0x170000, 10); + ReactiveMunition(id + 0x180000, 13.6f); + Ruination(id + 0x190000, 4f); + SenseWeakness(id + 0x1A0000, 8.25f); + IndiscriminateDetonation(id + 0x1B0000, 3.25f); + + ReadyShot(id + 0x200000, 11.25f); + ReactiveMunition(id + 0x210000, 2); + MagitekMinefield(id + 0x220000, 2); + MagitekImpetus(id + 0x230000, 10.3f); + MagitekMinefield(id + 0x240000, 8.25f); + Gunberd(id + 0x250000, 9.25f); + MagitekMinefield(id + 0x260000, 8.6f); + ReadyShot(id + 0x270000, 12.5f); + Ruination(id + 0x280000, 2.25f); + MagitekMinefield(id + 0x290000, 10.3f); + Gunberd(id + 0x2A0000, 9.3f); + ReadyShot(id + 0x2B0000, 13.8f); + GigaTempest(id + 0x2C0000, 2.2f); + MagitekImpetus(id + 0x2D0000, 3.5f); + Gunberd(id + 0x2E0000, 10.3f); + ReactiveMunition(id + 0x2F0000, 13.7f); + Ruination(id + 0x300000, 4.5f); + SenseWeakness(id + 0x310000, 8.25f); + IndiscriminateDetonation(id + 0x320000, 3.2f); + + TeraTempest(id + 0x400000, 12.9f); + } + + private void CallousCrossfire(uint id, float delay) + { + Cast(id, AID.CallousCrossfire, delay, 4, "Turret Crossfire") + .ActivateOnEnter() + .DeactivateOnExit(); + } + + 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() + .DeactivateOnExit(); + } + + private void SpiralScourge(uint id, float delay) + { + Cast(id, AID.SpiralScourge, delay, 6, "Tankbuster") + .ActivateOnEnter() + .DeactivateOnExit(); + } + 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() + .DeactivateOnExit(); + } + + private void TeraTempest(uint id, float delay) + { + Cast(id, AID.TeraTempest, delay, 25, "Enrage"); + } +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs new file mode 100644 index 0000000000..c94b340ed6 --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs @@ -0,0 +1,101 @@ +namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; + +abstract class GigaTempest : Components.Exaflare +{ + public GigaTempest(AOEShapeRect shape) : base(shape) { } + + public abstract bool IsStart(AID aid); + public abstract bool IsMove(AID aid); + + public override void OnCastStarted(BossModule module, Actor caster, ActorCastInfo spell) + { + if (IsStart((AID)spell.Action.ID)) + { + WDir? advance = GetExaDirection(caster); + if (advance == null) return; + Lines.Add(new() + { + Next = caster.Position, + Advance = advance.Value, + NextExplosion = caster.CastInfo!.NPCFinishAt, + TimeToMove = 0.9f, + ExplosionsLeft = 5, + MaxShownExplosions = 5, + Rotation = caster.Rotation, + }); + } + } + + public override void OnEventCast(BossModule module, Actor caster, ActorCastEvent spell) + { + if (Lines.Count > 0 && IsStart((AID)spell.Action.ID) || IsMove((AID)spell.Action.ID)) + { + int index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); + if (index < 0) return; + AdvanceLine(module, Lines[index], caster.Position); + if (Lines[index].ExplosionsLeft == 0) + Lines.RemoveAt(index); + } + } + + // The Gigatempest caster's heading is only used for rotating the AOE shape. + // The exaflare direction must be derived from the caster's location. + private WDir? GetExaDirection(Actor caster) + { + Angle? forwardAngle = null; + if (caster.Position.Z == 536) + { + forwardAngle = 180.Degrees(); + } + if (caster.Position.Z == 504) + { + forwardAngle = 0.Degrees(); + } + if (caster.Position.X == -826) + { + forwardAngle = 90.Degrees(); + } + if (caster.Position.X == -794) + { + forwardAngle = 270.Degrees(); + } + + if (forwardAngle == null) return null; + + const float _advanceDistance = 8; + return _advanceDistance * forwardAngle.Value.ToDirection(); + } +} + +class SmallGigaTempest : GigaTempest +{ + private static readonly AOEShapeRect _aoeShapeSmall = new(10, 6.5f, 0); + + public SmallGigaTempest() : base(_aoeShapeSmall) { } + + public override bool IsStart(AID aid) + { + return aid is AID.GigaTempestSmallStart; + } + public override bool IsMove(AID aid) + { + return aid is AID.GigaTempestSmallMove; + } + +} + +class LargeGigaTempest : GigaTempest +{ + private static readonly AOEShapeRect _aoeShapeLarge = new(35, 6.5f, 0); + + public LargeGigaTempest() : base(_aoeShapeLarge) { } + + public override bool IsStart(AID aid) + { + return aid is AID.GigaTempestLargeStart; + } + public override bool IsMove(AID aid) + { + return aid is AID.GigaTempestLargeMove; + } +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GunberdShot.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GunberdShot.cs new file mode 100644 index 0000000000..d50ef49cf7 --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GunberdShot.cs @@ -0,0 +1,71 @@ +namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; + +class GunberdShot : BossComponent +{ + private Actor? _gunberdCaster; + + public bool darkShotLoaded { get; private set; } + public bool windslicerLoaded { get; private set; } + + public bool Gunberding { get; private set; } + + public override void AddGlobalHints(BossModule module, GlobalHints hints) + { + if (Gunberding) + { + if (darkShotLoaded) + hints.Add("Maintain Distance"); + if (windslicerLoaded) + hints.Add("Knockback"); + } + else + { + if (darkShotLoaded) + hints.Add("Dark Loaded"); + if (windslicerLoaded) + hints.Add("Windslicer Loaded"); + } + } + + public override void OnCastStarted(BossModule module, Actor caster, ActorCastInfo spell) + { + switch ((AID)spell.Action.ID) + { + case AID.DarkShot: + darkShotLoaded = true; + break; + case AID.WindslicerShot: + windslicerLoaded = true; + break; + case AID.GunberdDark: + case AID.GunberdWindslicer: + Gunberding = true; + _gunberdCaster = caster; + break; + } + } + + public override void OnCastFinished(BossModule module, Actor caster, ActorCastInfo spell) + { + switch ((AID)spell.Action.ID) + { + case AID.GunberdDark: + darkShotLoaded = false; + Gunberding = false; + break; + case AID.GunberdWindslicer: + windslicerLoaded = false; + Gunberding = false; + break; + } + } + + public override void DrawArenaForeground(BossModule module, int pcSlot, Actor pc, MiniArena arena) + { + if (Gunberding && windslicerLoaded) + { + var adjPos = Components.Knockback.AwayFromSource(pc.Position, _gunberdCaster, 10); + Components.Knockback.DrawKnockback(pc, adjPos, arena); + } + } +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/RedHiddenMines.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/RedHiddenMines.cs new file mode 100644 index 0000000000..61121871f3 --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/RedHiddenMines.cs @@ -0,0 +1,35 @@ +namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; + +class RedHiddenMines : Components.GenericAOEs +{ + private List _mines = new(); + private static readonly AOEShapeCircle _shapeTrigger = new(3.6f); + private static readonly AOEShapeCircle _shapeExplosion = new(8f); + + public override IEnumerable ActiveAOEs(BossModule module, int slot, Actor actor) => _mines; + + public override void OnEventCast(BossModule module, Actor caster, ActorCastEvent spell) + { + if ((AID)spell.Action.ID is AID.ActivateRedMine) + { + _mines.Add(new(_shapeTrigger, caster.Position, color: ArenaColor.Trap)); + } + if ((AID)spell.Action.ID is AID.DetonateRedMine) + { + _mines.RemoveAll(t => t.Origin.AlmostEqual(caster.Position, 1)); + } + } + + public override void OnCastStarted(BossModule module, Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.IndiscriminateDetonation) + { + List _detonatingMines = new(); + for (int i = 0; i < _mines.Count; i++) + { + _detonatingMines.Add(new(_shapeExplosion, _mines[i].Origin, color: ArenaColor.AOE)); + } + _mines = _detonatingMines; + } + } +} diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs new file mode 100644 index 0000000000..8a042ab92a --- /dev/null +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs @@ -0,0 +1,65 @@ +namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; + +class RuinationCross : Components.GenericAOEs +{ + private static readonly AOEShapeRect _aoeShape = new(20, 4, 20); + private List _aoes = new(); + + public override IEnumerable ActiveAOEs(BossModule module, int slot, Actor actor) => _aoes; + + public override void OnCastStarted(BossModule module, Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.Ruination) + { + _aoes.Add(new(_aoeShape, caster.Position)); + _aoes.Add(new(_aoeShape, caster.Position, rotation: 90.Degrees())); + } + } + + public override void OnCastFinished(BossModule module, Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.Ruination) + { + _aoes.Clear(); + } + } +} + +class RuinationExaflare : Components.Exaflare +{ + public RuinationExaflare() : base(4) { } + + class LineWithActor : Line + { + public Actor Caster; + + public LineWithActor(Actor caster) + { + Next = caster.Position; + Advance = 4 * caster.Rotation.ToDirection(); + NextExplosion = caster.CastInfo!.NPCFinishAt; + TimeToMove = 1.1f; + ExplosionsLeft = 6; + MaxShownExplosions = 7; + Caster = caster; + } + } + + public override void OnCastStarted(BossModule module, Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.RuinationExaStart) + Lines.Add(new LineWithActor(caster)); + } + + public override void OnEventCast(BossModule module, Actor caster, ActorCastEvent spell) + { + if (Lines.Count > 0 && (AID)spell.Action.ID is AID.RuinationExaStart or AID.RuinationExaMove) + { + int index = Lines.FindIndex(item => ((LineWithActor)item).Caster == caster); + if (index < 0) return; + AdvanceLine(module, Lines[index], caster.Position); + if (Lines[index].ExplosionsLeft == 0) + Lines.RemoveAt(index); + } + } +}