From 570220369175ef9743c3f6319976e95fca46c583 Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:25:00 +0200 Subject: [PATCH 1/2] masked carnivale stage 31 --- .../Stage21Act1.cs | 2 +- .../Stage21Act2.cs | 2 +- .../Stage29RedFraughtAndBlue/Stage29Act2.cs | 2 +- .../Stage30Act2.cs | 1 + .../Stage31AnythingGogos/Stage31Act1.cs | 112 ++++++++++++++ .../Stage31AnythingGogos/Stage31Act2.cs | 137 ++++++++++++++++++ 6 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act1.cs create mode 100644 BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act2.cs diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act1.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act1.cs index cb48c81669..16953d3861 100644 --- a/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act1.cs +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act1.cs @@ -27,7 +27,7 @@ class Hints2(BossModule module) : BossComponent(module) { public override void AddGlobalHints(GlobalHints hints) { - hints.Add("The imps are weak to fire spells and strong against ice.\nInterrupt Void Blizzard with Spitting Sardine."); + hints.Add("The imps are weak to fire spells and strong against ice.\nInterrupt Void Blizzard with Flying Sardine."); } } diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act2.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act2.cs index 45e25aae38..893dfbadb7 100644 --- a/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act2.cs +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage21ChimeraOnAHotTinRoof/Stage21Act2.cs @@ -39,7 +39,7 @@ class Hints2(BossModule module) : BossComponent(module) public override void AddGlobalHints(GlobalHints hints) { if (!Module.Enemies(OID.ArenaImp).All(e => e.IsDead)) - hints.Add("The imps are weak to fire spells and strong against ice.\nInterrupt Void Blizzard with Spitting Sardine."); + hints.Add("The imps are weak to fire spells and strong against ice.\nInterrupt Void Blizzard with Flying Sardine."); } } diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage29RedFraughtAndBlue/Stage29Act2.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage29RedFraughtAndBlue/Stage29Act2.cs index 9dbe1508ba..ba9eff1a61 100644 --- a/BossMod/Modules/Global/MaskedCarnivale/Stage29RedFraughtAndBlue/Stage29Act2.cs +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage29RedFraughtAndBlue/Stage29Act2.cs @@ -90,7 +90,7 @@ class FluidConvectionDynamic(BossModule module) : Components.GenericAOEs(module) public override IEnumerable ActiveAOEs(int slot, Actor actor) { if (shape != default) - yield return new(shape, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation, _activation); + yield return new(shape, Module.PrimaryActor.Position, default, _activation); } public override void OnCastStarted(Actor caster, ActorCastInfo spell) diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage30TheCatchOfTheSiegfried/Stage30Act2.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage30TheCatchOfTheSiegfried/Stage30Act2.cs index 195ad4763b..005190a978 100644 --- a/BossMod/Modules/Global/MaskedCarnivale/Stage30TheCatchOfTheSiegfried/Stage30Act2.cs +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage30TheCatchOfTheSiegfried/Stage30Act2.cs @@ -7,6 +7,7 @@ public enum OID : uint FireVoidzone = 0x1E8D9B, Helper = 0x233C, } + public enum AID : uint { LawOfTheTorch = 18838, // Boss->self, 3,0s cast, range 34 20-degree cone diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act1.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act1.cs new file mode 100644 index 0000000000..ad51e47fd5 --- /dev/null +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act1.cs @@ -0,0 +1,112 @@ +namespace BossMod.Global.MaskedCarnivale.Stage31.Act1; + +public enum OID : uint +{ + Boss = 0x30F5, //R=2.0 + Imitation = 0x30F6, // R=2.0 + Helper = 0x233C, +} + +public enum AID : uint +{ + AutoAttack = 6499, // 30F5->player, no cast, single-target + Mimic = 23097, // 30F5->self, 5,0s cast, single-target, stop everything that does dmg + MimickedFlameThrower = 23116, // 30F5->self, no cast, range 8 90-degree cone, unavoidable + MimickedSap0 = 23104, // 30F5->self, 3,5s cast, single-target + MimickedSap1 = 23101, // 233C->location, 4,0s cast, range 8 circle + MimickedSap2 = 23105, // 30F5->self, 1,5s cast, single-target + MimickedSap3 = 23106, // 233C->location, 2,0s cast, range 8 circle + MimickedDoomImpending = 23113, // 30F5->self, 8,0s cast, range 80 circle, applies doom + MimickedBunshin = 23107, // 30F5->self, 3,0s cast, single-target, summons Imitation + MimickedFireBlast = 23109, // 30F5->self, 2,0s cast, single-target + MimickedFireBlast2 = 23110, // 233C->self, 2,0s cast, range 70+R width 4 rect + MimickedProteanWave = 23111, // 30F6->self, 2,0s cast, single-target + MimickedProteanWave2 = 23112, // 233C->self, 2,0s cast, range 50 30-degree cone + MimickedRawInstinct = 23115, // 30F5->self, 3,0s cast, single-target, buffs self with critical strikes, can be removed + MimickedImpSong = 23114, // 30F5->self, 6,0s cast, range 40 circle, interruptible, turns player into imp + MimickedFlare = 23098, // Boss->player, 3,0s cast, range 80 circle, possible punishment for ignoring Mimic + MimickedHoly = 23100, // Boss->player, 3,0s cast, range 6 circle, possible punishment for ignoring Mimic + MimickedPowerfulHit = 23103, // Boss->player, 3,0s cast, single-target, possible punishment for ignoring Mimic + MimickedCriticalHit = 23102, // Boss->player, 3,0s cast, single-target, possible punishment for ignoring Mimic +} + +public enum SID : uint +{ + Mimicry = 2450, // none->Boss, extra=0x0 + Doom = 1769, // Boss->player, extra=0x0 + Incurable = 1488, // Boss->player, extra=0x0 + CriticalStrikes = 1797, // Boss->Boss, extra=0x0 + DamageUp = 443, // none->Boss, extra=0x1 + Imp = 1103, // Boss->player, extra=0x2E +} + +class Mimic(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.Mimic), "Stop attacking when cast ends"); +class MimickedSap1(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.MimickedSap1), 8); +class MimickedSap2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.MimickedSap3), 8); +class MimickedDoomImpending(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.MimickedDoomImpending), "Heal to full before cast ends!"); +class MimickedProteanWave(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.MimickedProteanWave2), new AOEShapeCone(50, 15.Degrees())); +class MimickedFireBlast(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.MimickedFireBlast2), new AOEShapeRect(70.5f, 2)); +class MimickedImpSong(BossModule module) : Components.CastInterruptHint(module, ActionID.MakeSpell(AID.MimickedImpSong)); +class MimickedRawInstinct(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.MimickedRawInstinct), "Applies buff, dispel it"); +class MimickedFlare(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.MimickedFlare), "Use Diamondback!"); +class MimickedHoly(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.MimickedHoly), "Use Diamondback!"); +class MimickedCriticalHit(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.MimickedCriticalHit), "Use Diamondback!"); +class MimickedPowerfulHit(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.MimickedPowerfulHit), "Use Diamondback!"); + +class Hints2(BossModule module) : BossComponent(module) +{ + public override void AddHints(int slot, Actor actor, TextHints hints) + { + var mimicry = Module.PrimaryActor.FindStatus(SID.Mimicry); + if (mimicry != null) + hints.Add($"Do no damage!"); + var crit = Module.PrimaryActor.FindStatus(SID.CriticalStrikes); + if (crit != null) + hints.Add("Dispel buff!"); + } +} + +class Hints(BossModule module) : BossComponent(module) +{ + public override void AddGlobalHints(GlobalHints hints) + { + hints.Add($"For this fight Diamondback, Exuviation, Flying Sardine and a healing\nability (preferably Pom Cure with healer mimicry) are mandatory.\nEerie Soundwave is also recommended."); + } + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + hints.Add("Requirements for achievement: Take no optional damage and finish faster\nthan ideal time.", false); + } +} + +class Stage31Act1States : StateMachineBuilder +{ + public Stage31Act1States(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .DeactivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.MaskedCarnivale, GroupID = 754, NameID = 9908, SortOrder = 1)] +public class Stage31Act1 : BossModule +{ + public Stage31Act1(WorldState ws, Actor primary) : base(ws, primary, new ArenaBoundsCircle(new(100, 100), 16)) + { + ActivateComponent(); + } +} diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act2.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act2.cs new file mode 100644 index 0000000000..114c9a4ab8 --- /dev/null +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage31AnythingGogos/Stage31Act2.cs @@ -0,0 +1,137 @@ +namespace BossMod.Global.MaskedCarnivale.Stage31.Act2; + +public enum OID : uint +{ + Boss = 0x30F7, //R=2.0 + Maelstrom = 0x30F9, //R=1.0 + Voidzone = 0x1E9684, + Helper = 0x233C, +} + +public enum AID : uint +{ + AutoAttack = 6499, // Boss->player, no cast, single-target + Teleport = 23108, // Boss->location, no cast, single-target + GogoFireIII = 23117, // Boss->self, 3,0s cast, range 60 circle, applies pyretic + GogoBlizzardIII = 23118, // Boss->self, 3,0s cast, range 6+R circle + GogoThunderIII = 23119, // Boss->location, 3,0s cast, range 6 circle + GogoFlare = 23128, // Boss->player, 6,0s cast, range 80 circle + GogoHoly = 23130, // Boss->player, 6,0s cast, range 6 circle, unavoidable, basically a raidwide + GogoMeteorVisual = 23120, // Boss->self, 3,0s cast, single-target + GogoMeteorVisual2 = 22023, // Boss->self, no cast, single-target + GogoMeteor1 = 23121, // Helper->location, 3,0s cast, range 5 circle + GogoMeteor2 = 23123, // Helper->location, 12,0s cast, range 100 circle, damage fall off AOE, ca. 16 distance seems fine + GogoMeteor3 = 23131, // Helper->location, 4,0s cast, range 8 circle + GogoMeteor4 = 23122, // Helper->location, 11,0s cast, range 8 circle + GogoMeteor5 = 23129, // Helper->location, 7,0s cast, range 100 circle, damage fall off AOE, ca. 16 distance seems fine + GogoMeteor6 = 23124, // Helper->location, 10,0s cast, range 100 circle, wipe without diamondback + Charybdis = 20055, // Boss->self, 3,0s cast, single-target + Charybdis2 = 20056, // Helper->self, 4,0s cast, range 8 circle + Icestorm = 23126, // Boss->self, 3,0s cast, single-target + Icestorm2 = 23127, // Helper->self, no cast, range 60 circle, applies frostbite + heavy, should be removed with exuviation + AetherialPull = 23125, // Maelstrom->self, 1,0s cast, range 8 circle, pull 40 between centers +} + +public enum SID : uint +{ + Pyretic = 960, // Boss->player, extra=0x0 + Frostbite = 268, // Helper->player, extra=0x0 + Heavy = 1107, // Helper->player, extra=0x50 +} + +class Charybdis(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Charybdis), 8); +class Maelstrom(BossModule module) : Components.PersistentVoidzone(module, 8, m => m.Enemies(OID.Maelstrom)); +class GogoFlare(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GogoFlare)); +class GogoHoly(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GogoHoly)); +class GogoMeteor1(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GogoMeteor1), 5); +class GogoMeteor2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GogoMeteor2), 16); +class GogoMeteor3(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GogoMeteor3), 8); +class GogoMeteor4(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GogoMeteor4), 8); +class GogoMeteor5(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GogoMeteor5), 16); +class GogoMeteorBig(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GogoMeteor6), "Use Diamondback!"); +class Icestorm(BossModule module) : Components.RaidwideCastDelay(module, ActionID.MakeSpell(AID.Icestorm), ActionID.MakeSpell(AID.Icestorm2), 0.9f, "Raidwide + Frostbite + Heavy"); +class ThunderIII(BossModule module) : Components.PersistentVoidzoneAtCastTarget(module, 6, ActionID.MakeSpell(AID.GogoThunderIII), m => m.Enemies(OID.Voidzone).Where(e => e.EventState != 7), 0.8f); + +class GogoBlizzardIII(BossModule module) : Components.GenericAOEs(module) +{ + private static readonly AOEShapeCircle circle = new(8); + private DateTime _activation; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + if (_activation != default) + yield return new(circle, Module.PrimaryActor.Position, default, _activation); + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == AID.GogoFireIII) + _activation = spell.NPCFinishAt.AddSeconds(5.1f); + } + + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if ((AID)spell.Action.ID == AID.GogoBlizzardIII) + _activation = default; + } +} + +class GogoFireIIIHint(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.GogoFireIII), "Pyretic, dodge AOE then stop everything!"); + +class Pyretic(BossModule module) : Components.StayMove(module) +{ + public override void OnStatusGain(Actor actor, ActorStatus status) + { + if ((SID)status.ID is SID.Pyretic) + { + if (Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0 && slot < Requirements.Length) + Requirements[slot] = Requirement.Stay; + } + } + + public override void OnStatusLose(Actor actor, ActorStatus status) + { + if ((SID)status.ID is SID.Pyretic) + { + if (Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0 && slot < Requirements.Length) + Requirements[slot] = Requirement.None; + } + } +} + +class Hints(BossModule module) : BossComponent(module) +{ + public override void AddHints(int slot, Actor actor, TextHints hints) + { + var debuff = actor.FindStatus(SID.Heavy) != null || actor.FindStatus(SID.Frostbite) != null; + if (debuff) + hints.Add($"Cleanse debuffs!"); + } +} + +class Stage31Act2States : StateMachineBuilder +{ + public Stage31Act2States(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.MaskedCarnivale, GroupID = 754, NameID = 9908, SortOrder = 2)] +public class Stage31Act2(WorldState ws, Actor primary) : BossModule(ws, primary, new ArenaBoundsCircle(new(100, 100), 16)); From 4831e2c5054bd93b31bd726c67e4395793890723 Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Fri, 26 Apr 2024 18:50:16 +0200 Subject: [PATCH 2/2] masked carnivale 32 --- BossMod/BossModule/AOEShapes.cs | 2 +- BossMod/BossModule/ArenaBounds.cs | 32 ++++ BossMod/BossModule/MiniArena.cs | 1 + .../LyssaChrysine.cs | 2 +- .../Stage32AGoldenOpportunity/Stage32Act1.cs | 154 ++++++++++++++++++ .../Stage32AGoldenOpportunity/Stage32Act2.cs | 142 ++++++++++++++++ 6 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act1.cs create mode 100644 BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act2.cs diff --git a/BossMod/BossModule/AOEShapes.cs b/BossMod/BossModule/AOEShapes.cs index 12058f3f97..a381b712e5 100644 --- a/BossMod/BossModule/AOEShapes.cs +++ b/BossMod/BossModule/AOEShapes.cs @@ -152,7 +152,7 @@ public void SetEndPointFromCastLocation(Actor caster) public override string ToString() => $"Cross: l={Length:f3}, w={HalfWidth * 2}, off={DirectionOffset}"; public override bool Check(WPos position, WPos origin, Angle rotation) => position.InRect(origin, rotation + DirectionOffset, Length, Length, HalfWidth) || position.InRect(origin, rotation + DirectionOffset, HalfWidth, HalfWidth, Length); - public override void Draw(MiniArena arena, WPos origin, Angle rotation, uint color = ArenaColor.AOE) => arena.Zone(arena.Bounds.ClipAndTriangulate(ContourPoints(origin, rotation)), color); + public override void Draw(MiniArena arena, WPos origin, Angle rotation, uint color = ArenaColor.AOE) => arena.ZoneCross(origin, Length, HalfWidth, rotation + DirectionOffset, color); public override void Outline(MiniArena arena, WPos origin, Angle rotation, uint color = ArenaColor.Danger) { diff --git a/BossMod/BossModule/ArenaBounds.cs b/BossMod/BossModule/ArenaBounds.cs index d5c1bc84c0..319229bcbe 100644 --- a/BossMod/BossModule/ArenaBounds.cs +++ b/BossMod/BossModule/ArenaBounds.cs @@ -159,6 +159,38 @@ public float ScreenHalfSize cache[(start, end, halfWidth)] = result; return result; } + + public List<(WPos, WPos, WPos)> ClipAndTriangulateCross(WPos origin, float length, float halfWidth, Angle rotation) + { + if (cache.TryGetValue((origin, length, halfWidth, rotation), out var cachedResult)) + return cachedResult; + var dx = rotation.ToDirection(); + var dy = dx.OrthoL(); + var dx1 = dx * length; + var dx2 = dx * halfWidth; + var dy1 = dy * length; + var dy2 = dy * halfWidth; + + var points = new List + { + origin + dx1 - dy2, + origin + dx2 - dy2, + origin + dx2 - dy1, + origin - dx2 - dy1, + origin - dx2 - dy2, + origin - dx1 - dy2, + origin - dx1 + dy2, + origin - dx2 + dy2, + origin - dx2 + dy1, + origin + dx2 + dy1, + origin + dx2 + dy2, + origin + dx1 + dy2, + }; + + var result = ClipAndTriangulate(points); + cache[(origin, length, halfWidth, rotation)] = result; + return result; + } } public class ArenaBoundsCircle(WPos center, float radius) : ArenaBounds(center, radius) diff --git a/BossMod/BossModule/MiniArena.cs b/BossMod/BossModule/MiniArena.cs index 3c110d7c45..781792d18a 100644 --- a/BossMod/BossModule/MiniArena.cs +++ b/BossMod/BossModule/MiniArena.cs @@ -186,6 +186,7 @@ public void Zone(List<(WPos, WPos, WPos)> triangulation, uint color) public void ZoneRect(WPos origin, WDir direction, float lenFront, float lenBack, float halfWidth, uint color) => Zone(Bounds.ClipAndTriangulateRect(origin, direction, lenFront, lenBack, halfWidth), color); public void ZoneRect(WPos origin, Angle direction, float lenFront, float lenBack, float halfWidth, uint color) => Zone(Bounds.ClipAndTriangulateRect(origin, direction, lenFront, lenBack, halfWidth), color); public void ZoneRect(WPos start, WPos end, float halfWidth, uint color) => Zone(Bounds.ClipAndTriangulateRect(start, end, halfWidth), color); + public void ZoneCross(WPos origin, float length, float halfWidth, Angle rotation, uint color) => Zone(Bounds.ClipAndTriangulateCross(origin, length, halfWidth, rotation), color); public void TextScreen(Vector2 center, string text, uint color, float fontSize = 17) { diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs index 1f56eace7e..5ddc942409 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs @@ -33,7 +33,7 @@ class FrigidStone2(BossModule module) : Components.LocationTargetedAOEs(module, class OutInAOE(BossModule module) : Components.ConcentricAOEs(module, _shapes) { - private static readonly AOEShape[] _shapes = { new AOEShapeCircle(10), new AOEShapeDonut(10, 20) }; + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10), new AOEShapeDonut(10, 20)]; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act1.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act1.cs new file mode 100644 index 0000000000..841b7afca7 --- /dev/null +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act1.cs @@ -0,0 +1,154 @@ +namespace BossMod.Global.MaskedCarnivale.Stage32.Act1; + +public enum OID : uint +{ + Boss = 0x3FA5, //R=2.5 + GlitteringSlime = 0x3FAB, // R=2.0 + BallOfFire = 0x3FA6, // R=1.5 + Helper = 0x233C, +} + +public enum AID : uint +{ + AutoAttack = 34444, // Boss->player, no cast, single-target + GoldorFireIII = 34447, // Boss->self, 4,2s cast, single-target + GoldorFireIII2 = 34448, // Helper->location, 5,0s cast, range 8 circle + GoldorFireIII3 = 34449, // Helper->location, 2,5s cast, range 8 circle + GoldorBlast = 34450, // Boss->self, 3,5s cast, range 60 width 10 rect + SlimySummon = 34462, // Boss->self, 4,0s cast, single-target, summons glittering slime + Rupture = 34463, // GlitteringSlime->self, 8,0s cast, range 100 circle, enrage, slime must be killed fast with The Ram's Voice + Ultravibration + GoldorQuakeVisual = 34456, // Boss->self, 3,0s cast, single-target + GoldorQuake1 = 34457, // Helper->self, 4,0s cast, range 10 circle + GoldorQuake2 = 34458, // Helper->self, 5,5s cast, range 10-20 donut + GoldorQuake3 = 34459, // Helper->self, 7,0s cast, range 20-30 donut + GoldorFire = 34445, // Boss->self, 4,0s cast, single-target + GoldorAeroIII = 34460, // Boss->self, 4,0s cast, range 50 circle, raidwide, knockback 10 away from source + Burn = 34446, // BallOfFire->self, 12,0s cast, range 10 circle + GoldorGravity = 34451, // Boss->self, 5,0s cast, single-target + GoldorGravity2 = 34452, // Helper->player, no cast, single-target, heavy damage + heavy + GoldorThunderIIIVisual = 34453, // Boss->self, 4,0s cast, single-target + GoldorThunderIII1 = 34454, // Helper->player, no cast, range 5 circle, applies cleansable electrocution + GoldorThunderIII2 = 34455, // Helper->location, 2,5s cast, range 6 circle + GoldorBlizzardIIIVisual = 34589, // Boss->self, 6,0s cast, single-target, interruptible, freezes player + GoldorBlizzardIII = 34590, // Helper->player, no cast, range 6 circle +} + +public enum SID : uint +{ + Heavy = 240, // Helper->player, extra=0x63 + Electrocution = 3779, // Helper->player, extra=0x0 +} + +class SlimySummon(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.SlimySummon), "Prepare to kill add ASAP"); +class GoldorFireIII(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorFireIII2), 8); +class GoldorFireIII2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorFireIII3), 8); +class GoldorBlast(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorBlast), new AOEShapeRect(60, 5)); +class Rupture(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.Rupture), "Kill slime ASAP! (The Ram's Voice + Ultravibration)", true); + +class GoldorQuake(BossModule module) : Components.ConcentricAOEs(module, _shapes) +{ + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10), new AOEShapeDonut(10, 20), new AOEShapeDonut(20, 30)]; + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == AID.GoldorQuake1) + AddSequence(Module.PrimaryActor.Position, spell.NPCFinishAt); + } + + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if (Sequences.Count > 0) + { + var order = (AID)spell.Action.ID switch + { + AID.GoldorQuake1 => 0, + AID.GoldorQuake2 => 1, + AID.GoldorQuake3 => 2, + _ => -1 + }; + AdvanceSequence(order, Module.PrimaryActor.Position, WorldState.FutureTime(1.5f)); + } + } +} + +class GoldorAeroIII(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.GoldorAeroIII), 10) +{ + public override bool DestinationUnsafe(int slot, Actor actor, WPos pos) => (Module.FindComponent()?.ActiveAOEs(slot, actor).Any(z => z.Shape.Check(pos, z.Origin, z.Rotation)) ?? false) || !Module.Bounds.Contains(pos); +} + +class GoldorAeroIIIRaidwide(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GoldorAeroIII)); +class Burn(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Burn), new AOEShapeCircle(10)); +class GoldorGravity(BossModule module) : Components.RaidwideCastDelay(module, ActionID.MakeSpell(AID.GoldorGravity), ActionID.MakeSpell(AID.GoldorGravity2), 0.8f, "Dmg + Heavy debuff"); +class GoldorThunderIII(BossModule module) : Components.RaidwideCastDelay(module, ActionID.MakeSpell(AID.GoldorThunderIIIVisual), ActionID.MakeSpell(AID.GoldorThunderIII1), 0.8f, "Prepare to cleanse Electrocution"); +class GoldorThunderIII2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorThunderIII2), 6); +class GoldorBlizzardIII(BossModule module) : Components.CastInterruptHint(module, ActionID.MakeSpell(AID.GoldorBlizzardIIIVisual)); + +class Hints2(BossModule module) : BossComponent(module) +{ + public override void AddHints(int slot, Actor actor, TextHints hints) + { + var electrocution = actor.FindStatus(SID.Electrocution); + if (electrocution != null) + hints.Add($"Cleanse Electrocution!"); + var heavy = actor.FindStatus(SID.Heavy); + if (heavy != null) + hints.Add("Use Loom to dodge AOEs!"); + var fireballs = Module.Enemies(OID.BallOfFire).Where(x => !x.IsDead).FirstOrDefault(); + if (fireballs != null) + hints.Add("Destroy at least one fireball to create a safe spot!"); + } +} + +class Hints(BossModule module) : BossComponent(module) +{ + public override void AddGlobalHints(GlobalHints hints) + { + hints.Add($"For this fight The Ram's Voice, Ultravibration, Diamondback,\nExuviation, Flying Sardine, Loom, a physical dmg ability and a healing\nability (preferably Pom Cure with healer mimicry) are mandatory."); + } + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + hints.Add("Requirements for achievement: Don't destroy the crystal in act 2,\nuse no sprint, use all 6 magic elements, take no optional damage.", false); + } +} + +class Stage32Act1States : StateMachineBuilder +{ + public Stage32Act1States(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .DeactivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.MaskedCarnivale, GroupID = 948, NameID = 12471, SortOrder = 1)] +public class Stage32Act1 : BossModule +{ + public Stage32Act1(WorldState ws, Actor primary) : base(ws, primary, new ArenaBoundsCircle(new(100, 100), 16)) + { + ActivateComponent(); + } + + protected override void DrawEnemies(int pcSlot, Actor pc) + { + Arena.Actor(PrimaryActor, ArenaColor.Enemy); + foreach (var s in Enemies(OID.BallOfFire)) + Arena.Actor(s, ArenaColor.Object); + foreach (var s in Enemies(OID.GlitteringSlime)) + Arena.Actor(s, ArenaColor.Object); + } +} diff --git a/BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act2.cs b/BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act2.cs new file mode 100644 index 0000000000..ab41c29262 --- /dev/null +++ b/BossMod/Modules/Global/MaskedCarnivale/Stage32AGoldenOpportunity/Stage32Act2.cs @@ -0,0 +1,142 @@ +namespace BossMod.Global.MaskedCarnivale.Stage32.Act2; + +public enum OID : uint +{ + Boss = 0x3FA7, //R=2.5 + GildedCrystal = 0x3FAC, //R=3.0 + GildedGolem = 0x3FA9, //R=2.1 + GildedMarionette = 0x3FA8, //R=1.2 + GildedCyclops = 0x3FAA, //R=3.2 + Helper = 0x233C, +} + +public enum AID : uint +{ + AutoAttack = 34444, // Boss->player, no cast, single-target + ShiningSummon = 34461, // Boss->self, 4,0s cast, single-target + Teleport = 34129, // Helper->location, no cast, single-target + GoldorAeroIII = 34460, // Boss->self, 4,0s cast, range 50 circle, raidwide, knockback 10 away from source + GoldenCross = 34465, // GildedGolem->self, 7,0s cast, range 100 width 7 cross + GoldorQuakeVisual = 34456, // Boss->self, 3,0s cast, single-target + GoldorQuake1 = 34457, // Helper->self, 4,0s cast, range 10 circle + GoldorQuake2 = 34458, // Helper->self, 5,5s cast, range 10-20 donut + GoldorQuake3 = 34459, // Helper->self, 7,0s cast, range 20-30 donut + GoldenBeam = 34464, // GildedMarionette->self, 7,0s cast, range 40 120-degree cone + GoldorThunderIIIVisual = 34453, // Boss->self, 4,0s cast, single-target + GoldorThunderIII1 = 34454, // Helper->player, no cast, range 5 circle, applies cleansable electrocution + GoldorThunderIII2 = 34455, // Helper->location, 2,5s cast, range 6 circle + TwentyFourCaratInhale = 34466, // GildedCyclops->self, 2,0s cast, range 50 circle, pull 30 between centers + TwentyFourCaratSwing = 34467, // GildedCyclops->self, 4,0s cast, range 12 circle + GoldFever = 34469, // Boss->self, 8,0s cast, single-target, Goldor draws power from crystal + GoldorBlast = 34450, // Boss->self, 3,5s cast, range 60 width 10 rect + GoldorGravity = 34451, // Boss->self, 5,0s cast, single-target + GoldorGravity2 = 34452, // Helper->player, no cast, single-target, heavy damage + heavy + GoldorRush = 34468, // Boss->self, 4,0s cast, range 50 circle, knockback 10 away from source, raidwide + GoldorFireIII = 34449, // Helper->location, 2,5s cast, range 8 circle + GoldorBlizzardIIIVisual = 34589, // Boss->self, 6,0s cast, single-target, interruptible, freezes player + GoldorBlizzardIII = 34590, // Helper->player, no cast, range 6 circle +} + +public enum SID : uint +{ + Heavy = 240, // Helper->player, extra=0x63 + Electrocution = 3779, // Helper->player, extra=0x0 + MagicDamageUp = 3707, // none->Boss, extra=0x0 + MagicResistance = 3621, // none->Boss, extra=0x0 +} + +class GoldorFireIII(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorFireIII), 8); +class GoldorBlast(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorBlast), new AOEShapeRect(60, 5)); +class GoldenCross(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GoldenCross), new AOEShapeCross(100, 3.5f)); +class GoldenBeam(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GoldenBeam), new AOEShapeCone(40, 60.Degrees())); +class TwentyFourCaratSwing(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TwentyFourCaratSwing), new AOEShapeCircle(12)); + +class GoldorQuake(BossModule module) : Components.ConcentricAOEs(module, _shapes) +{ + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10), new AOEShapeDonut(10, 20), new AOEShapeDonut(20, 30)]; + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == AID.GoldorQuake1) + AddSequence(Module.PrimaryActor.Position, spell.NPCFinishAt); + } + + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if (Sequences.Count > 0) + { + var order = (AID)spell.Action.ID switch + { + AID.GoldorQuake1 => 0, + AID.GoldorQuake2 => 1, + AID.GoldorQuake3 => 2, + _ => -1 + }; + AdvanceSequence(order, Module.PrimaryActor.Position, WorldState.FutureTime(1.5f)); + } + } +} + +class GoldorAeroIII(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.GoldorAeroIII), 10) +{ + public override bool DestinationUnsafe(int slot, Actor actor, WPos pos) => (Module.FindComponent()?.ActiveAOEs(slot, actor).Any(z => z.Shape.Check(pos, z.Origin, z.Rotation)) ?? false) || !Module.Bounds.Contains(pos); +} + +class GoldorAeroIIIRaidwide(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GoldorAeroIII)); +class GoldorRush(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.GoldorRush), 10); +class GoldorRushRaidwide(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.GoldorRush)); +class TwentyFourCaratInhale(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.TwentyFourCaratInhale), 30, kind: Kind.TowardsOrigin); +class GoldorGravity(BossModule module) : Components.RaidwideCastDelay(module, ActionID.MakeSpell(AID.GoldorGravity), ActionID.MakeSpell(AID.GoldorGravity2), 0.8f, "Dmg + Heavy debuff"); +class GoldorThunderIII(BossModule module) : Components.RaidwideCastDelay(module, ActionID.MakeSpell(AID.GoldorThunderIIIVisual), ActionID.MakeSpell(AID.GoldorThunderIII1), 0.8f, "Prepare to cleanse Electrocution"); +class GoldorThunderIII2(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.GoldorThunderIII2), 6); +class GoldorBlizzardIII(BossModule module) : Components.CastInterruptHint(module, ActionID.MakeSpell(AID.GoldorBlizzardIIIVisual)); + +class Hints(BossModule module) : BossComponent(module) +{ + public override void AddGlobalHints(GlobalHints hints) + { + var magicabsorb = Module.PrimaryActor.FindStatus(SID.MagicResistance); + if (magicabsorb != null) + hints.Add($"{Module.PrimaryActor.Name} is immune to magic damage! (Destroy crystal to remove buff)"); + } + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + var electrocution = actor.FindStatus(SID.Electrocution); + if (electrocution != null) + hints.Add($"Cleanse Electrocution!"); + var heavy = actor.FindStatus(SID.Heavy); + if (heavy != null) + hints.Add("Use Loom to dodge AOEs!"); + var marionettes = Module.Enemies(OID.GildedMarionette).Count > 1; + if (marionettes) + hints.Add("Use Diamondback behind + between 2 marionettes!"); + } +} + +class Stage31Act2States : StateMachineBuilder +{ + public Stage31Act2States(BossModule module) : base(module) + { + TrivialPhase() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter(); + } +} + +[ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.MaskedCarnivale, GroupID = 948, NameID = 12471, SortOrder = 2)] +public class Stage31Act2(WorldState ws, Actor primary) : BossModule(ws, primary, new ArenaBoundsCircle(new(100, 100), 16));