Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EW quest boss fight modules #564

Merged
merged 1 commit into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions BossMod/Components/RotationModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using BossMod.QuestBattle;

namespace BossMod.Components;

public abstract class RotationModule<R>(BossModule module) : BossComponent(module) where R : UnmanagedRotation
{
private readonly R _rotation = New<R>.Constructor<WorldState>()(module.WorldState);
public sealed override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) => _rotation.Execute(actor, hints);
}
65 changes: 65 additions & 0 deletions BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P1Furcas.cs
Original file line number Diff line number Diff line change
@@ -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<ZeroAI>(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<AutoZero>()
.ActivateOnEnter<Explosion>()
.ActivateOnEnter<VoidSlash>()
.ActivateOnEnter<JongleursX>()
.ActivateOnEnter<StraightSpindle>()
.ActivateOnEnter<VoidTorch>()
.ActivateOnEnter<HellishScythe>()
.ActivateOnEnter<FlameBlast>()
.ActivateOnEnter<Blackout>()
.ActivateOnEnter<JestersReward>();
}
}

[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);
}
132 changes: 132 additions & 0 deletions BossMod/Modules/Endwalker/Quest/AnUnforeseenBargain/P2Andromalius.cs
Original file line number Diff line number Diff line change
@@ -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<Actor> Casters = [];

public override IEnumerable<AOEInstance> 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<StraightSpindle>()
.ActivateOnEnter<Dark>()
.ActivateOnEnter<EvilMist>()
.ActivateOnEnter<Explosion>()
.ActivateOnEnter<Hellsnap>()
.ActivateOnEnter<Decay>()
.ActivateOnEnter<ShieldHint>()
.ActivateOnEnter<StraightSpindleAdds>()
.ActivateOnEnter<Voidblood>()
.ActivateOnEnter<VoidSlash>()
.ActivateOnEnter<ProtectZero>();
}
}

[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);
}
115 changes: 115 additions & 0 deletions BossMod/Modules/Endwalker/Quest/AsTheHeavensBurn/P1TerminusIdolizer.cs
Original file line number Diff line number Diff line change
@@ -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<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (Module.FindComponent<TentacleWhipR1>()?.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<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (Module.FindComponent<TentacleWhipL1>()?.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<Actor> 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<AlphinaudAI>(module);

class TerminusIdolizerStates : StateMachineBuilder
{
public TerminusIdolizerStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<DeadlyCharge>()
.ActivateOnEnter<GriefOfParting>()
.ActivateOnEnter<TentacleWhipL1>()
.ActivateOnEnter<TentacleWhipL2>()
.ActivateOnEnter<TentacleWhipR1>()
.ActivateOnEnter<TentacleWhipR2>()
.ActivateOnEnter<DeadlyTentacles>()
.ActivateOnEnter<SelfDestruct>()
.ActivateOnEnter<Petrifaction>()
.ActivateOnEnter<Whack>()
.ActivateOnEnter<AutoAlphi>();
}
}

[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;
}
Loading
Loading