From 2bebe13a3fc4e49cbb6ae708b5cc076284c247f4 Mon Sep 17 00:00:00 2001 From: xanunderscore <149614526+xanunderscore@users.noreply.github.com> Date: Thu, 9 Jan 2025 17:17:57 -0500 Subject: [PATCH] EW boss fights --- BossMod/Components/RotationModule.cs | 9 ++ .../Quest/AnUnforeseenBargain/P1Furcas.cs | 65 +++++++++ .../AnUnforeseenBargain/P2Andromalius.cs | 132 ++++++++++++++++++ .../AsTheHeavensBurn/P1TerminusIdolizer.cs | 115 +++++++++++++++ .../AsTheHeavensBurn/P2TerminusLacerator.cs | 80 +++++++++++ .../AsTheHeavensBurn/P3TerminusLacerator.cs | 78 +++++++++++ .../Endwalker/Quest/FrostyReception.cs | 84 +++++++++++ .../Modules/Endwalker/Quest/TheGameIsAfoot.cs | 55 ++++++++ .../Quest/WhereEverythingBegins/P1.cs | 36 +++++ .../Quest/WhereEverythingBegins/P2.cs | 126 +++++++++++++++++ 10 files changed, 780 insertions(+) create mode 100644 BossMod/Components/RotationModule.cs create mode 100644 BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P1Furcas.cs create mode 100644 BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P2Andromalius.cs create mode 100644 BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P1TerminusIdolizer.cs create mode 100644 BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P2TerminusLacerator.cs create mode 100644 BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P3TerminusLacerator.cs create mode 100644 BossMod/Modules/Endwalker/Quest/FrostyReception.cs create mode 100644 BossMod/Modules/Endwalker/Quest/TheGameIsAfoot.cs create mode 100644 BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P1.cs create mode 100644 BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P2.cs diff --git a/BossMod/Components/RotationModule.cs b/BossMod/Components/RotationModule.cs new file mode 100644 index 0000000000..9f8084d5eb --- /dev/null +++ b/BossMod/Components/RotationModule.cs @@ -0,0 +1,9 @@ +using BossMod.QuestBattle; + +namespace BossMod.Components; + +public abstract class RotationModule(BossModule module) : BossComponent(module) where R : UnmanagedRotation +{ + private readonly R _rotation = New.Constructor()(module.WorldState); + public sealed override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) => _rotation.Execute(actor, hints); +} diff --git a/BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P1Furcas.cs b/BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P1Furcas.cs new file mode 100644 index 0000000000..b537c51992 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P1Furcas.cs @@ -0,0 +1,65 @@ +using BossMod.QuestBattle.Endwalker.MSQ; + +namespace BossMod.Endwalker.Quest.AnUnforeseenBargain.P1Furcas; + +public enum OID : uint +{ + Boss = 0x3D71, + Helper = 0x233C, +} + +public enum AID : uint +{ + VoidSlash = 33027, // _Gen_VisitantBlackguard->self, 4.0s cast, range 8+R 90-degree cone + Explosion = 33004, // Helper->self, 10.0s cast, range 5 circle + JongleursX = 31802, // Boss->player, 4.0s cast, single-target + StraightSpindle = 31796, // _Gen_VisitantVoidskipper->self, 4.0s cast, range 50+R width 5 rect + VoidTorch = 33007, // Helper->location, 3.0s cast, range 6 circle + HellishScythe = 31800, // Boss->self, 5.0s cast, range 10 circle + FlameBlast = 33008, // 3ED4->self, 4.0s cast, range 80+R width 4 rect + Blackout = 31798, // _Gen_VisitantVoidskipper->self, 13.0s cast, range 60 circle + JestersReward = 33031, // Boss->self, 6.0s cast, range 28 180-degree cone +} + +class AutoZero(BossModule module) : Components.RotationModule(module); +class Explosion(BossModule module) : Components.CastTowers(module, ActionID.MakeSpell(AID.Explosion), 5); +class VoidSlash(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.VoidSlash), new AOEShapeCone(9.7f, 45.Degrees())); +class JongleursX(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.JongleursX)); +class StraightSpindle(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.StraightSpindle), new AOEShapeRect(50, 2.5f)); +class VoidTorch(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.VoidTorch), 6); +class HellishScythe(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HellishScythe), new AOEShapeCircle(10)); +class FlameBlast(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.FlameBlast), new AOEShapeRect(80, 2)); +class JestersReward(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.JestersReward), new AOEShapeCone(28, 90.Degrees())); +class Blackout(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.Blackout), "Kill wasp before enrage!", true) +{ + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + foreach (var h in hints.PriorityTargets) + if (h.Actor.CastInfo?.Action == WatchedAction) + h.Priority = 5; + } +} + +class FurcasStates : StateMachineBuilder +{ + public FurcasStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70209, NameID = 12066)] +public class Furcas(WorldState ws, Actor primary) : BossModule(ws, primary, new(97.85f, 286), new ArenaBoundsCircle(19.5f)) +{ + protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly), ArenaColor.Enemy); +} diff --git a/BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P2Andromalius.cs b/BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P2Andromalius.cs new file mode 100644 index 0000000000..d333db37e2 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P2Andromalius.cs @@ -0,0 +1,132 @@ +namespace BossMod.Endwalker.Quest.AnUnforeseenBargain.P2Andromalius; + +public enum OID : uint +{ + Boss = 0x3D76, // R6.000, x1 + Helper = 0x233C, // R0.500, x13, Helper type + AlphiShield = 0x1EB87A, +} + +public enum AID : uint +{ + StraightSpindleFast = 31808, // 3D78->self, 5.0s cast, range 50+R width 5 rect + Dark = 31815, // Helper->location, 5.0s cast, range 10 circle + StraightSpindleSlow = 31809, // 3D78->self, 9.0s cast, range 50+R width 5 rect + EvilMist = 31825, // Boss->self, 5.0s cast, range 60 circle + Explosion = 33010, // Helper->self, 10.0s cast, range 5 circle + Hellsnap = 31816, // Boss->3D80, 5.0s cast, range 6 circle + Decay = 32857, // _Gen_VisitantVoidskipper->self, 13.0s cast, range 60 circle + StraightSpindleAdds = 33174, // 3D77->self, 8.0s cast, range 50+R width 5 rect + Voidblood = 33172, // 3EE4->location, 9.0s cast, range 6 circle + VoidSlash = 33173, // 3EE5->self, 11.0s cast, range 8+R 90-degree cone +} + +class StraightSpindleAdds(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.StraightSpindleAdds), new AOEShapeRect(50, 2.5f)); +class Voidblood(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Voidblood), 6); +class VoidSlash(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.VoidSlash), new AOEShapeCone(9.7f, 45.Degrees())); +class EvilMist(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.EvilMist)); +class Explosion(BossModule module) : Components.CastTowers(module, ActionID.MakeSpell(AID.Explosion), 5); +class Dark(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Dark), 10); +class Hellsnap(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Hellsnap), 6); + +class StraightSpindle(BossModule module) : Components.GenericAOEs(module) +{ + private readonly List Casters = []; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) => Casters.Select(c => new AOEInstance(new AOEShapeRect(50, 2.5f), c.Position, c.Rotation, Module.CastFinishAt(c.CastInfo))).OrderBy(x => x.Activation).Take(3); + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.StraightSpindleFast or AID.StraightSpindleSlow) + Casters.Add(caster); + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.StraightSpindleFast or AID.StraightSpindleSlow) + Casters.Remove(caster); + } +} +class Decay(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.Decay), "Kill wasp before enrage!", true) +{ + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + foreach (var h in hints.PriorityTargets) + if (h.Actor.CastInfo?.Action == WatchedAction) + h.Priority = 5; + } +} +class ShieldHint(BossModule module) : BossComponent(module) +{ + private Actor? Shield; + + public override void OnActorCreated(Actor actor) + { + if (actor.OID == (uint)OID.AlphiShield) + Shield = actor; + } + + public override void OnEventDirectorUpdate(uint updateID, uint param1, uint param2, uint param3, uint param4) + { + if (updateID == 0x8000000C && param1 == 0x46) + Shield = null; + } + + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + if (Shield is Actor s) + hints.GoalZones.Add(hints.GoalSingleTarget(s.Position, 5)); + } + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + if (Shield is Actor s && !actor.Position.InCircle(s.Position, 5)) + hints.Add("Take cover!"); + } + + public override void DrawArenaBackground(int pcSlot, Actor pc) + { + if (Shield is Actor s) + Arena.ZoneCircle(s.Position, 5, ArenaColor.SafeFromAOE); + } +} + +class ProtectZero(BossModule module) : BossComponent(module) +{ + private Actor? CastingZero => Raid.WithoutSlot().FirstOrDefault(x => x.OID == 0x3D80 && x.FindStatus(2056) != null); + + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + if (CastingZero is Actor z) + { + foreach (var h in hints.PotentialTargets) + if (h.Actor.TargetID == z.InstanceID) + h.Priority = 5; + } + } +} + +class AndromaliusStates : StateMachineBuilder +{ + public AndromaliusStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70209, NameID = 12071)] +public class Andromalius(WorldState ws, Actor primary) : BossModule(ws, primary, new(97.85f, 286), new ArenaBoundsCircle(19.5f)) +{ + protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly), ArenaColor.Enemy); +} diff --git a/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P1TerminusIdolizer.cs b/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P1TerminusIdolizer.cs new file mode 100644 index 0000000000..0ac864c7b6 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P1TerminusIdolizer.cs @@ -0,0 +1,115 @@ +using BossMod.QuestBattle.Endwalker.MSQ; + +namespace BossMod.Endwalker.Quest.AsTheHeavensBurn.P1TerminusIdolizer; + +public enum OID : uint +{ + Boss = 0x35E9, + Helper = 0x233C, + TerminusDetonator = 0x35E5, // R2.000, x0 (spawn during fight) +} + +public enum AID : uint +{ + DeadlyCharge = 26995, // Boss->location, 5.0s cast, width 10 rect charge + GriefOfParting = 26996, // Boss->self, 5.0s cast, range 40 circle + DeadlyTentacles = 26997, // Boss->35F5, 5.0s cast, single-target + TentacleWhipLFirst = 27004, // Boss->self, 5.0s cast, range 60 180-degree cone + TentacleWhipRSecond = 27006, // Helper->self, 7.0s cast, range 60 180-degree cone + SelfDestruct = 26991, // TerminusDetonator->self, no cast, range 6 circle + Petrifaction = 26999, // Boss->self, 4.0s cast, range 60 circle + TentacleWhipRFirst = 27001, // Boss->self, 5.0s cast, range 60 180-degree cone + TentacleWhipLSecond = 27003, // Helper->self, 7.0s cast, range 60 180-degree cone + Whack = 27007, // Boss->35F5, 5.0s cast, single-target +} + +public enum IconID : uint +{ + Stack = 218, // 35F5 +} + +public enum TetherID : uint +{ + BombTether = 17, // 35E5->35F5 +} + +class DeadlyCharge(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.DeadlyCharge), 5); +class GriefOfParting(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GriefOfParting)); +class DeadlyTentacles(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.DeadlyTentacles)); +class TentacleWhipR1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TentacleWhipRFirst), new AOEShapeCone(60, 90.Degrees())); +class TentacleWhipR2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TentacleWhipLSecond), new AOEShapeCone(60, 90.Degrees())) +{ + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + if (Module.FindComponent()?.Casters.Count > 0) + yield break; + else + foreach (var h in base.ActiveAOEs(slot, actor)) + yield return h; + } +} +class TentacleWhipL1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TentacleWhipLFirst), new AOEShapeCone(60, 90.Degrees())); +class TentacleWhipL2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TentacleWhipRSecond), new AOEShapeCone(60, 90.Degrees())) +{ + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + if (Module.FindComponent()?.Casters.Count > 0) + yield break; + else + foreach (var h in base.ActiveAOEs(slot, actor)) + yield return h; + } +} +class SelfDestruct(BossModule module) : Components.GenericStackSpread(module) +{ + private readonly List Bombs = []; + + public override void OnTethered(Actor source, ActorTetherInfo tether) + { + if (tether.ID == (uint)TetherID.BombTether) + { + Bombs.Add(source); + if (Stacks.Count == 0) + Stacks.Add(new Stack(WorldState.Actors.Find(tether.Target)!, 3, activation: WorldState.FutureTime(9.1f))); + } + } + + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if (spell.Action.ID == (uint)AID.SelfDestruct) + { + Bombs.Remove(caster); + if (Bombs.Count == 0) + Stacks.Clear(); + } + } +} +class Petrifaction(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.Petrifaction)); +class Whack(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.Whack)); + +class AutoAlphi(BossModule module) : Components.RotationModule(module); + +class TerminusIdolizerStates : StateMachineBuilder +{ + public TerminusIdolizerStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 804, NameID = 10932)] +public class TerminusIdolizer(WorldState ws, Actor primary) : BossModule(ws, primary, new(-300.75f, 151.5f), new ArenaBoundsCircle(19.5f)) +{ + protected override bool CheckPull() => PrimaryActor.InCombat; +} diff --git a/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P2TerminusLacerator.cs b/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P2TerminusLacerator.cs new file mode 100644 index 0000000000..bf7e773e16 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P2TerminusLacerator.cs @@ -0,0 +1,80 @@ +using BossMod.QuestBattle.Endwalker.MSQ; + +namespace BossMod.Endwalker.Quest.AsTheHeavensBurn.P2TerminusLacerator; + +public enum OID : uint +{ + Boss = 0x35EC, + Helper = 0x233C, + Meteorite = 0x35ED +} + +public enum AID : uint +{ + BlackStar = 27012, // Helper->location, 6.0s cast, range 40 circle + DeadlyImpact = 27014, // Helper->location, 7.0s cast, range 10 circle + Burst = 27021, // Helper->location, 7.5s cast, range 5 circle + DeadlyImpactMeteorite = 27025, // 35ED->self, 5.0s cast, range 20 circle + DeadlyImpactMeteorite2 = 27023, // Boss->self, 6.0s cast, single-target + DeadlyImpactHelper = 27024, // Helper->location, 6.0s cast, range 20 circle + Explosion = 27026, // 35ED->self, 3.0s cast, range 6 circle +} + +class Burst(BossModule module) : Components.CastTowers(module, ActionID.MakeSpell(AID.Burst), 5); +class DeadlyImpact(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.DeadlyImpact), 10, maxCasts: 6); +class BlackStar(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.BlackStar)); +class DeadlyImpactProximity(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.DeadlyImpactMeteorite), new AOEShapeCircle(8)); +class DeadlyImpactProximity2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.DeadlyImpactMeteorite2), new AOEShapeCircle(10)); +class MeteorExplosion(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Explosion), new AOEShapeCircle(6)); +class Meteor(BossModule module) : Components.GenericLineOfSightAOE(module, default, 100, false) +{ + public record MeteorObj(Actor Actor, DateTime Explosion); + + private readonly List Meteors = []; + + private void Refresh() + { + var meteor = Meteors.FirstOrDefault(); + Modify(meteor?.Actor.Position, Module.Enemies(0x35ED).Where(m => !m.IsDead && m.ModelState.AnimState1 != 1).Select(m => (m.Position, m.HitboxRadius)), meteor?.Explosion ?? default); + } + + public override void OnActorCreated(Actor actor) + { + if (actor.OID == 0x1EB291) + { + Meteors.Add(new(actor, WorldState.FutureTime(11.9f))); + Refresh(); + } + } + + public override void OnActorDestroyed(Actor actor) + { + if (Meteors.RemoveAll(x => x.Actor == actor) > 0) + Refresh(); + } +} + +class AutoAlisaie(BossModule module) : Components.RotationModule(module); + +class TerminusLaceratorStates : StateMachineBuilder +{ + public TerminusLaceratorStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 804, NameID = 10933)] +public class TerminusLacerator(WorldState ws, Actor primary) : BossModule(ws, primary, new(-260.28f, 80.75f), new ArenaBoundsCircle(19.5f)) +{ + protected override bool CheckPull() => PrimaryActor.InCombat; +} + diff --git a/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P3TerminusLacerator.cs b/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P3TerminusLacerator.cs new file mode 100644 index 0000000000..c04fe42906 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P3TerminusLacerator.cs @@ -0,0 +1,78 @@ +namespace BossMod.Endwalker.Quest.AsTheHeavensBurn.P3TerminusLacerator; + +public enum OID : uint +{ + Boss = 0x35EE, + Helper = 0x233C, + Vanquisher = 0x35EF, +} + +public enum AID : uint +{ + BlackStar = 27012, // Helper->location, 6.0s cast, range 40 circle + DeadlyImpact = 27014, // Helper->location, 7.0s cast, range 10 circle + ForcefulImpactAOE = 26239, // Vanquisher->location, 5.0s cast, range 7 circle + ForcefulImpactKB = 27030, // Helper->self, 5.6s cast, range 20 circle + MutableLawsBig = 27041, // Helper->location, 10.0s cast, range 6 circle + MutableLawsSmall = 27040, // Helper->location, 10.0s cast, range 6 circle + AccursedTongue = 27038, // Helper->35F5/35FA/35F9/35F7, 5.0s cast, range 6 circle + Shock = 27035, // 35F0->self, 5.0s cast, range 10 circle + Depress = 27036, // 35EF->35FA, 5.0s cast, range 7 circle + ForcefulImpact = 27029, // 35EF->location, 5.0s cast, range 7 circle +} + +class DeadlyImpact(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.DeadlyImpact), 10, maxCasts: 6); +class BlackStar(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.BlackStar)); + +class ForcefulImpact(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.ForcefulImpactAOE), 7); +class ForcefulImpactKB(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.ForcefulImpactKB), 10, stopAtWall: true) +{ + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + if (Casters.FirstOrDefault() is Actor c) + hints.PredictedDamage.Add((WorldState.Party.WithSlot().Mask(), Module.CastFinishAt(c.CastInfo))); + } +} +class MutableLaws1(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.MutableLawsBig), 15); +class MutableLaws2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.MutableLawsSmall), 6); +class AccursedTongue(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.AccursedTongue), 6); +class ForcefulImpact2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.ForcefulImpact), 7); +class Shock(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Shock), new AOEShapeCircle(10), maxCasts: 6); +class Depress(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Depress), 7); + +class TerminusLaceratorStates : StateMachineBuilder +{ + private readonly TerminusLacerator _module; + + public TerminusLaceratorStates(TerminusLacerator module) : base(module) + { + _module = module; + + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter(); + TrivialPhase(1) + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .Raw.Update = () => _module.BossP2?.IsDeadOrDestroyed ?? false; + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 804, NameID = 10933)] +public class TerminusLacerator(WorldState ws, Actor primary) : BossModule(ws, primary, new(-260.28f, 80.75f), new ArenaBoundsCircle(19.5f)) +{ + public Actor? BossP2 => Enemies(OID.Vanquisher).FirstOrDefault(); + + protected override void DrawEnemies(int pcSlot, Actor pc) + { + Arena.Actor(PrimaryActor, ArenaColor.Enemy); + Arena.Actor(BossP2, ArenaColor.Enemy); + } +} + diff --git a/BossMod/Modules/Endwalker/Quest/FrostyReception.cs b/BossMod/Modules/Endwalker/Quest/FrostyReception.cs new file mode 100644 index 0000000000..668945c0cd --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/FrostyReception.cs @@ -0,0 +1,84 @@ +namespace BossMod.Endwalker.Quest.AFrostyReception; + +public enum OID : uint +{ + Boss = 0x3646, + Helper = 0x233C, + LockOn = 0x3648, // R1.200, x0 (spawn during fight) +} + +public enum AID : uint +{ + GigaTempest = 27440, // Boss->self, 5.0s cast, range 20 circle + Ruination1 = 27443, // Boss->self, 5.0s cast, range 40 width 8 cross + Ruination2 = 27444, // Helper->self, 5.0s cast, range 30 width 8 rect + ResinBomb = 27449, // Helper->Lyse/Magnai/Sadu/Pipin/Lucia/Cirina, 5.0s cast, range 5 circle + LockOn1 = 27461, // _Gen_6->self, 1.0s cast, range 6 circle + MagitekCannon = 27457, // _Gen_TelotekReaper1->player/Lyse/Sadu/Magnai/Lucia/Cirina/Pipin, 5.0s cast, range 6 circle + Bombardment = 27459, // Helper->location, 4.0s cast, range 6 circle + LockOn2 = 27463, // _Gen_6->self, 1.0s cast, range 6 circle +} + +class GigaTempest(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GigaTempest)); +class Ruination(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Ruination1), new AOEShapeCross(40, 4)); +class Ruination2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Ruination2), new AOEShapeRect(30, 4)); +class ResinBomb(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.ResinBomb), 5); +class MagitekCannon(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.MagitekCannon), 6); +class Bombardment(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Bombardment), 6); + +class LockOn(BossModule module) : Components.GenericAOEs(module) +{ + private class Caster + { + public required Actor Actor; + public required DateTime FinishAt; + } + + private readonly List Casters = []; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) => Casters.Select(c => new AOEInstance(new AOEShapeCircle(6), c.Actor.Position, default, c.FinishAt)); + + public override void OnActorCreated(Actor actor) + { + if (actor.OID == (uint)OID.LockOn) + Casters.Add(new() { Actor = actor, FinishAt = WorldState.FutureTime(6.6f) }); + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.LockOn1 or AID.LockOn2) + { + var c = Casters.FindIndex(p => p.Actor == caster); + if (c >= 0) + Casters[c].FinishAt = Module.CastFinishAt(spell); + } + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.LockOn1 or AID.LockOn2) + Casters.RemoveAll(c => c.Actor == caster); + } +} + +class VergiliaVanCorculumStates : StateMachineBuilder +{ + public VergiliaVanCorculumStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69919, NameID = 10572)] +public class VergiliaVanCorculum(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -78), new ArenaBoundsCircle(19.5f)) +{ + protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly), ArenaColor.Enemy); +} + diff --git a/BossMod/Modules/Endwalker/Quest/TheGameIsAfoot.cs b/BossMod/Modules/Endwalker/Quest/TheGameIsAfoot.cs new file mode 100644 index 0000000000..5ba5a1d7e2 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/TheGameIsAfoot.cs @@ -0,0 +1,55 @@ +namespace BossMod.Endwalker.Quest.TheGameIsAfoot; + +public enum OID : uint +{ + Boss = 0x4037, + Helper = 0x233C, +} + +public enum AID : uint +{ + WindUnbound = 34883, // Boss->self, 5.0s cast, range 40 circle + SnatchMorsel = 34884, // Boss->402D, 5.0s cast, single-target + PeckingFlurry = 34886, // Boss->self, 4.0s cast, range 40 circle + FallingRock = 34888, // Helper->location, 5.0s cast, range 6 circle + StickySpit = 34890, // Helper->player, 5.0s cast, range 6 circle + Swoop = 35717, // Boss->location, 5.0s cast, width 16 rect charge + FurlingFlapping = 34893, // Helper->players/402D/402E, 5.0s cast, range 8 circle + DeadlySwoop = 35888, // Boss->location, 30.0s cast, width 16 rect charge +} + +class PeckingFlurry(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.PeckingFlurry)); +class WindUnbound(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.WindUnbound)); +class SnatchMorsel(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.SnatchMorsel), "Wukbuster"); +class FallingRock(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.FallingRock), 6, maxCasts: 8); +class StickySpit(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.StickySpit), 6); +class Swoop(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.Swoop), 8); +class FurlingFlapping(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.FurlingFlapping), 8); +class DeadlySwoop(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.DeadlySwoop), 8) +{ + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + if (Raid.WithoutSlot().Count() == 3) + base.AddAIHints(slot, actor, assignment, hints); + } +} + +class GiantColibriStates : StateMachineBuilder +{ + public GiantColibriStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + ; + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70288, NameID = 12499)] +public class GiantColibri(WorldState ws, Actor primary) : BossModule(ws, primary, new(425, -440), new ArenaBoundsCircle(15)); diff --git a/BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P1.cs b/BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P1.cs new file mode 100644 index 0000000000..8a5c87e0fa --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P1.cs @@ -0,0 +1,36 @@ +namespace BossMod.Endwalker.Quest.WhereEverythingBegins.P1; +public enum OID : uint +{ + Boss = 0x39B3, + Helper = 0x233C, +} + +public enum AID : uint +{ + Nox = 30021, // Helper->self, 8.0s cast, range 10 circle + VoidVortex = 30025, // Helper->39BE, 5.0s cast, range 6 circle + VoidGravity = 30023, // Helper->player/39BC/39BF/39BE, 5.0s cast, range 6 circle +} + +class Nox(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Nox), new AOEShapeCircle(10), maxCasts: 5); +class VoidGravity(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.VoidGravity), 6); +class VoidVortex(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.VoidVortex), 6); + +class ScarmiglioneStates : StateMachineBuilder +{ + public ScarmiglioneStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70130, NameID = 11407)] +public class Scarmiglione(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -148), new ArenaBoundsCircle(19.5f)) +{ + protected override bool CheckPull() => PrimaryActor.InCombat; + protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly), ArenaColor.Enemy); +} + diff --git a/BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P2.cs b/BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P2.cs new file mode 100644 index 0000000000..7fcf7bfff9 --- /dev/null +++ b/BossMod/Modules/Endwalker/Quest/WhereEverythingBegins/P2.cs @@ -0,0 +1,126 @@ +namespace BossMod.Endwalker.Quest.WhereEverythingBegins; + +public enum OID : uint +{ + Boss = 0x39B7, + FilthyShackle = 0x39BB, + VarshahnShield = 0x1EB762, +} + +public enum AID : uint +{ + BlightedSweep = 30052, + CursedNoise = 30026, + BlightedBuffet = 30032, + VacuumWave = 30033, + BlightedSwathe = 30044, + VoidQuakeIII = 30046, + // stack on varshahn + VoidVortex = 30025, // 233C->players, 5.0s cast, range 6 circle + RottenRampage = 30031, // 233C->location, 10.0s cast, range 6 circle + RottenRampageSpread = 30056, // 233C->player/39C1/39BC/39BF/39BE, 10.0s cast, range 6 circle +} + +class VoidVortex(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.VoidVortex), 6, minStackSize: 1); +class RottenRampageSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.RottenRampageSpread), 6); +class RottenRampage(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.RottenRampage), 6); +class BlightedSwathe(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlightedSwathe), new AOEShapeCone(40, 90.Degrees())); +class BlightedSweep(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlightedSweep), new AOEShapeCone(40, 90.Degrees())); +class BlightedBuffet(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlightedBuffet), new AOEShapeCircle(9)); +class VacuumWave(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.VacuumWave), 5) +{ + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + foreach (var c in Casters) + hints.AddForbiddenZone(new AOEShapeDonut(13, 60), c.Position, activation: Module.CastFinishAt(c.CastInfo)); + } +} +class VoidQuakeIII(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.VoidQuakeIII), new AOEShapeCross(40, 5)); +class DeathWall(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.CursedNoise)) +{ + private DateTime? WallActivate; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + if (WallActivate is DateTime dt) + yield return new AOEInstance(new AOEShapeDonut(18, 100), Arena.Center, Activation: dt); + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + base.OnCastStarted(caster, spell); + // 5.7 seconds + + if (spell.Action == WatchedAction && NumCasts == 0) + WallActivate = WorldState.FutureTime(5.7f); + } + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + if (WallActivate != null) + hints.Add("Raidwide + poison wall spawn", false); + } + + public override void OnEventEnvControl(byte index, uint state) + { + if (index == 1 && state == 0x00020001) + { + WallActivate = null; + Arena.Bounds = new ArenaBoundsCircle(18); + } + } +} + +class Shield(BossModule module) : BossComponent(module) +{ + private const float ShieldRadius = 5; + private Actor? ShieldObj => Module.Enemies(OID.VarshahnShield).FirstOrDefault(); + + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + if (ShieldObj != null) + hints.AddForbiddenZone(new AOEShapeDonut(ShieldRadius, 30), ShieldObj.Position); + } + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + if (ShieldObj is Actor obj && (actor.Position - obj.Position).Length() > ShieldRadius) + hints.Add("Go to safe zone!"); + } + + public override void DrawArenaBackground(int pcSlot, Actor pc) + { + if (ShieldObj is Actor obj) + Arena.AddCircleFilled(obj.Position, ShieldRadius, ArenaColor.SafeFromAOE); + } +} + +public class ScarmiglioneStates : StateMachineBuilder +{ + public ScarmiglioneStates(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70130, NameID = 11407)] +public class Scarmiglione(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -148), new ArenaBoundsCircle(19.5f)) +{ + protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + foreach (var e in hints.PotentialTargets) + e.Priority = e.Actor.OID == (uint)OID.FilthyShackle ? 1 : 0; + } + + protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly), ArenaColor.Enemy); +}