From e5b562d6822f1fdd1f5d2f6bab63f7f22e14ba86 Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Wed, 19 Feb 2025 05:20:56 +0100 Subject: [PATCH] some cleanups --- BossMod/BossModule/BossModule.cs | 33 ++- BossMod/Components/Gaze.cs | 6 +- BossMod/Components/GenericAOEs.cs | 6 +- .../Alliance/A11Prishe/BanishStorm.cs | 17 +- .../Alliance/A12Fafnir/HurricaneWing.cs | 5 +- .../Alliance/A13ArkAngels/HavokSpiral.cs | 2 +- .../Alliance/A14ShadowLord/CthonicFury.cs | 5 +- .../Alliance/A14ShadowLord/Implosion.cs | 5 +- .../Alliance/A14ShadowLord/UmbraSmash.cs | 45 ++-- .../Chaotic/Ch01CloudOfDarkness/Break.cs | 3 +- .../ParticleConcentration.cs | 6 +- .../Dungeon/D01Ihuykatumu/D013Apollyon.cs | 2 +- .../Dungeon/D05Origenics/D052Deceiver.cs | 4 +- .../Dungeon/D05Origenics/D053Ambrose.cs | 3 +- .../D081HisRoyalHeadnessLeonoggI.cs | 55 +++-- .../Ex3QueenEternal/DimensionalDistortion.cs | 26 ++- .../Ex3QueenEternal/VirtualShiftEarth.cs | 36 ++-- .../TheSerpentlordSeethes/DesertDustdevil.cs | 36 ++-- .../Modules/Dawntrail/Hunt/RankA/Heshuala.cs | 56 ++--- .../Modules/Dawntrail/Hunt/RankA/Nechuciho.cs | 2 +- .../Dawntrail/Hunt/RankS/Neyoozoteel.cs | 2 +- .../JanquetilaquesPortrait.cs | 63 +++--- .../Quest/Job/Viper/VengeanceOfTheViper.cs | 76 ++++--- .../TheProtectorAndTheDestroyer/E2Gwyddrud.cs | 5 +- .../Quest/Role/BarThePassage/P3Apyaahi.cs | 21 +- .../Quest/Role/HeroesAndPretenders.cs | 25 ++- .../Raid/M03NBruteBomber/Firespin.cs | 19 +- .../Savage/M04SWickedThunder/Sabertail.cs | 20 +- .../Ultimate/FRU/P4CrystallizeTime.cs | 187 +++++++++-------- .../Dawntrail/Ultimate/FRU/P5FulgentBlade.cs | 52 ++--- .../Unreal/Un1Byakko/HundredfoldHavoc.cs | 28 +-- .../Alliance/A11Byregot/Reproduce.cs | 29 +-- .../Alliance/A12Rhalgr/HandOfTheDestroyer.cs | 21 +- .../Endwalker/Alliance/A13Azeyma/SolarFans.cs | 84 ++++---- .../Alliance/A14Naldthal/FarAboveDeepBelow.cs | 50 +++-- .../A14Naldthal/OnceAboveEverBelow.cs | 61 +++--- .../Alliance/A21Nophica/SowingCircle.cs | 30 +-- .../Alliance/A31Thaliak/Rheognosis.cs | 21 +- .../Alliance/A31Thaliak/Tetraktys.cs | 14 +- .../Alliance/A32Llymlaen/SurgingWave.cs | 38 +++- .../Alliance/A33Oschon/P2ArrowTrail.cs | 18 +- .../Alliance/A34Eulogia/AsAboveSoBelow.cs | 43 ++-- .../Alliance/A34Eulogia/SolarFans.cs | 54 +++-- .../C01ASS/C011Silkie/EasternEwers.cs | 22 +- .../C02AMR/C022Gorai/CloudToGround.cs | 24 ++- .../D06DeadEnds/D061CausticGrebuloff.cs | 29 +-- .../Dungeon/D12Aetherfont/D121Lyngbakr.cs | 32 +-- .../MSQ/AnUnforeseenBargain/P2Andromalius.cs | 35 ++-- .../Endwalker/Quest/MSQ/Endwalker/Exaflare.cs | 27 +-- .../Endwalker/Quest/MSQ/WorthyOfHisBack.cs | 194 +++++++++++------- .../Savage/P12S2PallasAthena/Ekpyrosis.cs | 34 +-- .../Savage/P7SAgdistis/BladesOfAttis.cs | 44 ++-- .../GymnasiouAcheloios.cs | 2 +- .../Ultimate/DSW2/P7ExaflaresEdge.cs | 70 +++++-- .../Endwalker/Ultimate/TOP/P6WaveCannon.cs | 28 +-- .../Variant/V01SS/V012Silkie/V012Silkie.cs | 28 +-- .../Variant/V02MR/V022Moko/SpearmanOrders.cs | 46 +++-- .../Variant/V02MR/V022Moko/Upwell.cs | 42 ++-- .../Dungeon/D13SohrKhai/D133Hraesvelgr.cs | 21 +- .../Dungeon/D10StoneVigil/D102Koshchei.cs | 28 +-- .../RealmReborn/Quest/MSQ/TheStepsOfFaith.cs | 106 +++++----- .../D01Holminster/D011ForgivenDissonance.cs | 6 +- .../D01Holminster/D012TesleentheForgiven.cs | 26 ++- .../Dungeon/D01Holminster/D013Philia.cs | 60 ++++-- .../CriticalEngagement/CE44FamiliarFace.cs | 97 ++++----- .../CriticalEngagement/CE62LooksToDieFor.cs | 59 +++--- .../Duel/Duel2Lyon/Duel2LyonGenericAttacks.cs | 170 +++++++-------- .../Foray/Duel/Duel5Menenius/GigaTempest.cs | 50 ++--- .../Foray/Duel/Duel5Menenius/Ruination.cs | 43 ++-- .../Duel/Duel6Lyon/Duel6LyonGenericAttacks.cs | 93 ++++----- .../Quest/Job/Dancer/SaveTheLastDanceForMe.cs | 52 +++-- .../Quest/MSQ/FadedMemories/Ardbert.cs | 174 +++++++++++----- .../Quest/MSQ/FadedMemories/FadedMemories.cs | 53 ----- .../MSQ/FadedMemories/FlameGeneralAldynn.cs | 36 +++- .../Quest/MSQ/FadedMemories/KingThordan.cs | 48 ++++- .../Quest/MSQ/FadedMemories/Nidhogg.cs | 23 ++- .../Quest/MSQ/FadedMemories/Zenos.cs | 56 ++++- .../SecretCladoselache.cs | 10 +- .../SecretPorxie.cs | 21 +- .../BA2Raiden/CloudToGround.cs | 19 +- .../Quest/MSQ/ARequiemForHeroes/P2.cs | 67 +++--- .../Stormblood/Trial/T07Byakko/T07Byakko.cs | 30 +-- .../Stormblood/Ultimate/UCOB/P5Exaflare.cs | 27 +-- 83 files changed, 1937 insertions(+), 1409 deletions(-) delete mode 100644 BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FadedMemories.cs diff --git a/BossMod/BossModule/BossModule.cs b/BossMod/BossModule/BossModule.cs index f6ee901cc6..7b275388a5 100644 --- a/BossMod/BossModule/BossModule.cs +++ b/BossMod/BossModule/BossModule.cs @@ -28,24 +28,41 @@ public abstract class BossModule : IDisposable public List Enemies(uint oid) { - var entry = RelevantEnemies.GetValueOrDefault(oid); - entry ??= RelevantEnemies[oid] = [.. WorldState.Actors.Where(actor => actor.OID == oid)]; + if (!RelevantEnemies.TryGetValue(oid, out var entry)) + { + entry = []; + foreach (var actor in WorldState.Actors.Actors.Values) + { + if (actor.OID == oid) + entry.Add(actor); + } + RelevantEnemies[oid] = entry; + } return entry; } - public List Enemies(ReadOnlySpan enemies) + public List Enemies(uint[] enemies) { - List relevantenemies = []; + List relevantEnemies = []; var len = enemies.Length; for (var i = 0; i < len; ++i) { var enemy = enemies[i]; - var entry = RelevantEnemies.GetValueOrDefault(enemy); - entry ??= RelevantEnemies[enemy] = [.. WorldState.Actors.Where(actor => actor.OID == enemy)]; - relevantenemies.AddRange(entry); + if (!RelevantEnemies.TryGetValue(enemy, out var entry)) + { + entry = []; + foreach (var actor in WorldState.Actors.Actors.Values) + { + if (actor.OID == enemy) + entry.Add(actor); + } + RelevantEnemies[enemy] = entry; + } + relevantEnemies.AddRange(entry); } - return relevantenemies; + return relevantEnemies; } + public List Enemies(OID oid) where OID : Enum => Enemies((uint)(object)oid); // component management: at most one component of any given type can be active at any time diff --git a/BossMod/Components/Gaze.cs b/BossMod/Components/Gaze.cs index 7d8f29ea1a..7d7e342d99 100644 --- a/BossMod/Components/Gaze.cs +++ b/BossMod/Components/Gaze.cs @@ -105,12 +105,12 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) if (spell.Action == WatchedAction) { var count = Eyes.Count; + var id = caster.InstanceID; for (var i = 0; i < count; ++i) { - var eye = Eyes[i]; - if (eye.ActorID == caster.InstanceID) + if (Eyes[i].ActorID == id) { - Eyes.Remove(eye); + Eyes.RemoveAt(i); break; } } diff --git a/BossMod/Components/GenericAOEs.cs b/BossMod/Components/GenericAOEs.cs index b75f0a7354..08d1d020f3 100644 --- a/BossMod/Components/GenericAOEs.cs +++ b/BossMod/Components/GenericAOEs.cs @@ -100,12 +100,12 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) if (spell.Action == WatchedAction) { var count = Casters.Count; + var id = caster.InstanceID; for (var i = 0; i < count; ++i) { - var aoe = Casters[i]; - if (aoe.ActorID == caster.InstanceID) + if (Casters[i].ActorID == id) { - Casters.Remove(aoe); + Casters.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/BanishStorm.cs b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/BanishStorm.cs index 4a92e9bcd8..6aa8b6dcb2 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/BanishStorm.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/BanishStorm.cs @@ -76,10 +76,19 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (spell.Action.ID == (uint)AID.Banish) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + break; + } + } } } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs b/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs index 67cfdf508b..88211e96cb 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs @@ -32,10 +32,9 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) { for (var i = 0; i < AOEs.Count; ++i) { - var aoe = AOEs[i]; - if (aoe.ActorID == caster.InstanceID) + if (AOEs[i].ActorID == caster.InstanceID) { - AOEs.Remove(aoe); + AOEs.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs index 9f6b7cc03e..3f45a605a7 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs @@ -25,7 +25,7 @@ private void InitIfReady() if (_rotation.Count == 3 && _increment != default) { for (var i = 0; i < 3; ++i) - Sequences.Add(new(_shape, Arena.Center, _rotation[i], _increment, _activation, 1.2f, 8)); + Sequences.Add(new(_shape, WPos.ClampToGrid(Arena.Center), _rotation[i], _increment, _activation, 1.2f, 8)); _rotation.Clear(); _increment = default; } diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs index 3a8e6ddbf7..3e1c14aa17 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs @@ -62,10 +62,9 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) var count = AOEs.Count; for (var i = 0; i < count; ++i) { - var aoe = AOEs[i]; - if (aoe.ActorID == caster.InstanceID) + if (AOEs[i].ActorID == caster.InstanceID) { - AOEs.Remove(aoe); + AOEs.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/Implosion.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/Implosion.cs index 8f30cd7f5f..b8078ff3f6 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/Implosion.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/Implosion.cs @@ -28,10 +28,9 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) var count = _aoes.Count; for (var i = 0; i < count; ++i) { - var aoe = _aoes[i]; - if (aoe.ActorID == caster.InstanceID) + if (_aoes[i].ActorID == caster.InstanceID) { - _aoes.Remove(aoe); + _aoes.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/UmbraSmash.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/UmbraSmash.cs index b898e664e5..b2ffa426d4 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/UmbraSmash.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/UmbraSmash.cs @@ -11,7 +11,8 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme var imminentAOEs = ImminentAOEs(linesCount); // use only imminent aoes for hints - for (var i = 0; i < imminentAOEs.Length; ++i) + var len = imminentAOEs.Length; + for (var i = 0; i < len; ++i) { var aoe = imminentAOEs[i]; hints.AddForbiddenZone(Shape, aoe.Item1, aoe.Item3, aoe.Item2); @@ -20,6 +21,9 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme public override void OnCastStarted(Actor caster, ActorCastInfo spell) { + void AddLine(WPos origin, WDir direction, Angle offset) + => Lines.Add(new() { Next = origin, Advance = 5f * direction, Rotation = spell.Rotation + offset, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.3f, ExplosionsLeft = 6, MaxShownExplosions = 2 }); + switch (spell.Action.ID) { case (uint)AID.UmbraSmashAOE1: @@ -29,8 +33,8 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) case (uint)AID.UmbraSmashAOEClone: var dir = spell.Rotation.ToDirection(); var origin = caster.Position + 30f * dir; - Lines.Add(new() { Next = origin, Advance = 5f * dir.OrthoL(), Rotation = spell.Rotation + 90f.Degrees(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.3f, ExplosionsLeft = 6, MaxShownExplosions = 2 }); - Lines.Add(new() { Next = origin, Advance = 5f * dir.OrthoR(), Rotation = spell.Rotation - 90f.Degrees(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.3f, ExplosionsLeft = 6, MaxShownExplosions = 2 }); + AddLine(origin, dir.OrthoL(), 90f.Degrees()); + AddLine(origin, dir.OrthoR(), -90f.Degrees()); break; } } @@ -46,26 +50,35 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) case (uint)AID.UmbraSmashAOEClone: ++NumCasts; var origin = caster.Position + 30f * spell.Rotation.ToDirection(); - foreach (var l in Lines.Where(l => l.Next.AlmostEqual(origin, 1f))) + var count = Lines.Count; + for (var i = 0; i < count; ++i) { - l.Next = origin + l.Advance; - l.TimeToMove = 1; - l.NextExplosion = WorldState.FutureTime(l.TimeToMove); - --l.ExplosionsLeft; + var l = Lines[i]; + if (l.Next.AlmostEqual(origin, 1f)) + { + l.Next = origin + l.Advance; + l.TimeToMove = 1f; + l.NextExplosion = WorldState.FutureTime(l.TimeToMove); + --l.ExplosionsLeft; + } } break; case (uint)AID.UmbraWave: ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index == -1) + var count2 = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count2; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); break; } } diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs index 9f09d519ad..b2b13d8cf6 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs @@ -17,10 +17,11 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (spell.Action.ID is (uint)AID.BreakBossAOE or (uint)AID.BreakEyeAOE) { var count = Eyes.Count; + var pos = caster.Position; for (var i = 0; i < count; ++i) { var eye = Eyes[i]; - if (eye.Position == caster.Position) + if (eye.Position == pos) { Eyes.Remove(eye); break; diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs index d830d35050..c391bec438 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs @@ -148,12 +148,12 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) { ++NumCasts; var count = Towers.Count; + var pos = caster.Position; for (var i = 0; i < count; ++i) { - var tower = Towers[i]; - if (tower.Position == caster.Position) + if (Towers[i].Position == pos) { - Towers.Remove(tower); + Towers.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs index 696924fd64..392c344cf4 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs @@ -120,7 +120,7 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) public override void OnActorCreated(Actor actor) { - void AddWhirlwind(ReadOnlySpan pos) + void AddWhirlwind(WPos[] pos) { for (var i = 0; i < 3; ++i) { diff --git a/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D052Deceiver.cs b/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D052Deceiver.cs index 0aa7718161..3f367c410e 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D052Deceiver.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D052Deceiver.cs @@ -250,9 +250,11 @@ public D052DeceiverStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 825, NameID = 12693, SortOrder = 3)] public class D052Deceiver(WorldState ws, Actor primary) : BossModule(ws, primary, ArenaChanges.ArenaCenter, ArenaChanges.StartingBounds) { + private static readonly uint[] adds = [(uint)OID.OrigenicsSentryG92, (uint)OID.OrigenicsSentryG91]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies([(uint)OID.OrigenicsSentryG92, (uint)OID.OrigenicsSentryG91])); + Arena.Actors(Enemies(adds)); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D053Ambrose.cs b/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D053Ambrose.cs index f8b9fa53a4..8f57172614 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D053Ambrose.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D05Origenics/D053Ambrose.cs @@ -289,10 +289,11 @@ public class D053Ambrose(WorldState ws, Actor primary) : BossModule(ws, primary, public static readonly WPos ArenaCenter = new(190f, default); public static readonly ArenaBoundsRect StartingBounds = new(32.5f, 24f); public static readonly ArenaBoundsRect DefaultBounds = new(15f, 19.5f); + private static readonly uint[] adds = [(uint)OID.Superfluity, (uint)OID.OrigenicsEyeborg]; protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies([(uint)OID.Superfluity, (uint)OID.OrigenicsEyeborg])); + Arena.Actors(Enemies(adds)); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D08StrayboroughDeadwalk/D081HisRoyalHeadnessLeonoggI.cs b/BossMod/Modules/Dawntrail/Dungeon/D08StrayboroughDeadwalk/D081HisRoyalHeadnessLeonoggI.cs index 7aae101974..4e26bf6414 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D08StrayboroughDeadwalk/D081HisRoyalHeadnessLeonoggI.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D08StrayboroughDeadwalk/D081HisRoyalHeadnessLeonoggI.cs @@ -47,13 +47,13 @@ public enum IconID : uint class MaliciousMistArenaChange(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeDonut donut = new(14, 20); + private static readonly AOEShapeDonut donut = new(14f, 20f); private AOEInstance? _aoe; public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.MaliciousMist && Arena.Bounds == D081HisRoyalHeadnessLeonoggI.StartingBounds) + if (spell.Action.ID == (uint)AID.MaliciousMist && Arena.Bounds == D081HisRoyalHeadnessLeonoggI.StartingBounds) _aoe = new(donut, Arena.Center, default, Module.CastFinishAt(spell, 0.9f)); } @@ -99,31 +99,39 @@ class FallingNightmare(BossModule module) : Components.GenericAOEs(module) public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id) { - if ((OID)actor.OID == OID.NobleNoggin && id == 0x11D1) - _aoes.Add(new(circle, actor.Position, default, WorldState.FutureTime(3))); // can be 3 or 4 seconds depending on mechanic + if (actor.OID == (uint)OID.NobleNoggin && id == 0x11D1) + _aoes.Add(new(circle, actor.Position, default, WorldState.FutureTime(3d))); // can be 3 or 4 seconds depending on mechanic } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if (_aoes.Count > 0 && (AID)spell.Action.ID is AID.FallingNightmare1 or AID.FallingNightmare2) + if (_aoes.Count > 0 && spell.Action.ID is (uint)AID.FallingNightmare1 or (uint)AID.FallingNightmare2) _aoes.RemoveAt(0); } } class SpiritedCharge(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeRect rect = new(6, 1); + private static readonly AOEShapeRect rect = new(6f, 1f); private readonly List _charges = []; public override IEnumerable ActiveAOEs(int slot, Actor actor) { - foreach (var c in _charges) - yield return new(rect, c.Position, c.Rotation); + var count = _charges.Count; + if (count == 0) + return []; + var aoes = new AOEInstance[count]; + for (var i = 0; i < count; ++i) + { + var c = _charges[i]; + aoes[i] = new(rect, c.Position, c.Rotation); + } + return aoes; } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.SpiritedChargeStart) + if (spell.Action.ID == (uint)AID.SpiritedChargeStart) _charges.Add(caster); } @@ -134,24 +142,31 @@ public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id) } } -class EvilScheme(BossModule module) : Components.Exaflare(module, 4) +class EvilScheme(BossModule module) : Components.Exaflare(module, 4f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.EvilSchemeFirst) - Lines.Add(new() { Next = spell.LocXZ, Advance = 4 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell, 1.6f), TimeToMove = 1.5f, ExplosionsLeft = 5, MaxShownExplosions = 5 }); + if (spell.Action.ID == (uint)AID.EvilSchemeFirst) + Lines.Add(new() { Next = caster.Position, Advance = 4f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell, 1.6f), TimeToMove = 1.5f, ExplosionsLeft = 5, MaxShownExplosions = 5 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.EvilSchemeFirst or AID.EvilSchemeRest) + if (spell.Action.ID is (uint)AID.EvilSchemeFirst or (uint)AID.EvilSchemeRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + break; + } + } } } } @@ -160,7 +175,7 @@ class MaliciousMist(BossModule module) : Components.RaidwideCast(module, ActionI class Scream : Components.SimpleAOEs { - public Scream(BossModule module) : base(module, ActionID.MakeSpell(AID.Scream), new AOEShapeCone(20, 30.Degrees()), 4) { MaxDangerColor = 2; } + public Scream(BossModule module) : base(module, ActionID.MakeSpell(AID.Scream), new AOEShapeCone(20f, 30f.Degrees()), 4) { MaxDangerColor = 2; } } class D081HisRoyalHeadnessLeonoggIStates : StateMachineBuilder diff --git a/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/DimensionalDistortion.cs b/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/DimensionalDistortion.cs index 21c5d506c8..baca11fcf4 100644 --- a/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/DimensionalDistortion.cs +++ b/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/DimensionalDistortion.cs @@ -1,10 +1,10 @@ namespace BossMod.Dawntrail.Extreme.Ex3QueenEternal; -class DimensionalDistortion(BossModule module) : Components.Exaflare(module, 6) +class DimensionalDistortion(BossModule module) : Components.Exaflare(module, 6f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.DimensionalDistortionFirst) + if (spell.Action.ID is (uint)AID.DimensionalDistortionFirst) { Lines.Add(new() { Next = spell.LocXZ, Advance = 8.5f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); } @@ -12,19 +12,23 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.DimensionalDistortionFirst or AID.DimensionalDistortionRest) + if (spell.Action.ID is (uint)AID.DimensionalDistortionFirst or (uint)AID.DimensionalDistortionRest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/VirtualShiftEarth.cs b/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/VirtualShiftEarth.cs index ac7cba6a4a..8f312d9124 100644 --- a/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/VirtualShiftEarth.cs +++ b/BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/VirtualShiftEarth.cs @@ -4,9 +4,9 @@ class VirtualShiftEarth(BossModule module) : BossComponent(module) { public BitMask Flying; - public static readonly WPos Midpoint = new(100, 94); - public static readonly WDir CenterOffset = new(8, 0); - public static readonly WDir HalfExtent = new(4, 8); + public static readonly WPos Midpoint = new(100f, 94f); + public static readonly WDir CenterOffset = new(8f, default); + public static readonly WDir HalfExtent = new(4f, 8f); public static bool OnPlatform(WPos p) { @@ -19,19 +19,19 @@ public static bool OnPlatform(WPos p) public override void DrawArenaForeground(int pcSlot, Actor pc) { - Arena.AddRect(Midpoint + CenterOffset, new(0, 1), HalfExtent.Z, HalfExtent.Z, HalfExtent.X, Colors.Border, 2); - Arena.AddRect(Midpoint - CenterOffset, new(0, 1), HalfExtent.Z, HalfExtent.Z, HalfExtent.X, Colors.Border, 2); + Arena.AddRect(Midpoint + CenterOffset, new(default, 1f), HalfExtent.Z, HalfExtent.Z, HalfExtent.X, Colors.Border, 2f); + Arena.AddRect(Midpoint - CenterOffset, new(default, 1f), HalfExtent.Z, HalfExtent.Z, HalfExtent.X, Colors.Border, 2f); } public override void OnStatusGain(Actor actor, ActorStatus status) { - if ((SID)status.ID == SID.GravitationalAnomaly) + if (status.ID == (uint)SID.GravitationalAnomaly) Flying.Set(Raid.FindSlot(actor.InstanceID)); } public override void OnStatusLose(Actor actor, ActorStatus status) { - if ((SID)status.ID == SID.GravitationalAnomaly) + if (status.ID == (uint)SID.GravitationalAnomaly) Flying[Raid.FindSlot(actor.InstanceID)] = default; } } @@ -66,7 +66,7 @@ class LawsOfEarthBurst1 : LawsOfEarthBurst { public LawsOfEarthBurst1(BossModule module) : base(module) { - AddTowers(WorldState.FutureTime(5), VirtualShiftEarth.Midpoint, VirtualShiftEarth.CenterOffset, new(0, 6), new(2, 0)); + AddTowers(WorldState.FutureTime(5d), VirtualShiftEarth.Midpoint, VirtualShiftEarth.CenterOffset, new(default, 6f), new(2f, default)); } } @@ -74,12 +74,12 @@ class LawsOfEarthBurst2 : LawsOfEarthBurst { public LawsOfEarthBurst2(BossModule module) : base(module) { - AddTowers(WorldState.FutureTime(8.8f), VirtualShiftEarth.Midpoint, VirtualShiftEarth.CenterOffset, new(0, 5)); + AddTowers(WorldState.FutureTime(8.8d), VirtualShiftEarth.Midpoint, VirtualShiftEarth.CenterOffset, new(default, 5f)); } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.GravityPillar) + if (spell.Action.ID == (uint)AID.GravityPillar) foreach (ref var t in Towers.AsSpan()) t.ForbiddenSoakers.Set(Raid.FindSlot(spell.TargetID)); } @@ -152,7 +152,7 @@ public override void AddHints(int slot, Actor actor, TextHints hints) public override void DrawArenaForeground(int pcSlot, Actor pc) { foreach (var p in Raid.WithSlot(true, true, true).IncludedInMask(_meteorsAbovePlatforms).Actors()) - Arena.AddCircle(p.Position, 4, Colors.Danger); + Arena.AddCircle(p.Position, 4f); } public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) @@ -166,11 +166,11 @@ public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.MeteorImpactPlatform or AID.MeteorImpactFall) + if (spell.Action.ID is (uint)AID.MeteorImpactPlatform or (uint)AID.MeteorImpactFall) { _activeMeteors.Reset(); // assume all meteors fall at the same time ++NumCasts; - if ((AID)spell.Action.ID == AID.MeteorImpactPlatform) + if (spell.Action.ID == (uint)AID.MeteorImpactPlatform) ++_numPlacedMeteors; } } @@ -212,21 +212,21 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.WeightyBlow) + if (spell.Action.ID == (uint)AID.WeightyBlow) _activeBaits = true; } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.MeteorImpactPlatform: + case (uint)AID.MeteorImpactPlatform: _boulders.Add(caster); break; - case AID.WeightyBlowDestroy: + case (uint)AID.WeightyBlowDestroy: _boulders.Remove(caster); break; - case AID.WeightyBlowAOE: + case (uint)AID.WeightyBlowAOE: ++NumCasts; break; } diff --git a/BossMod/Modules/Dawntrail/FATE/TheSerpentlordSeethes/DesertDustdevil.cs b/BossMod/Modules/Dawntrail/FATE/TheSerpentlordSeethes/DesertDustdevil.cs index 26a27be6ca..044f9894ba 100644 --- a/BossMod/Modules/Dawntrail/FATE/TheSerpentlordSeethes/DesertDustdevil.cs +++ b/BossMod/Modules/Dawntrail/FATE/TheSerpentlordSeethes/DesertDustdevil.cs @@ -2,18 +2,18 @@ namespace BossMod.Dawntrail.FATE.Ttokrrone; class DesertDustdevil(BossModule module) : Components.GenericRotatingAOE(module) { - private static readonly AOEShapeCone cone = new(60, 45.Degrees()); - private static readonly Angle offset = 180.Degrees(); - private static readonly Angle a90 = 90.Degrees(); + private static readonly AOEShapeCone cone = new(60f, 45f.Degrees()); + private static readonly Angle offset = 180f.Degrees(); + private static readonly Angle a90 = 90f.Degrees(); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.FangwardDustdevilVisualCW: - case AID.FangwardDustdevilVisualCCW: - case AID.TailwardDustdevilVisualCW: - case AID.TailwardDustdevilVisualCCW: + case (uint)AID.FangwardDustdevilVisualCW: + case (uint)AID.FangwardDustdevilVisualCCW: + case (uint)AID.TailwardDustdevilVisualCW: + case (uint)AID.TailwardDustdevilVisualCCW: AddSequence(spell, Sequences.Count == 0 ? 7 : 4); break; } @@ -24,23 +24,23 @@ private void AddSequence(ActorCastInfo spell, int repeats) var rotation = spell.Rotation; var direction = a90; - if ((AID)spell.Action.ID is AID.FangwardDustdevilVisualCW or AID.TailwardDustdevilVisualCW) + if (spell.Action.ID is (uint)AID.FangwardDustdevilVisualCW or (uint)AID.TailwardDustdevilVisualCW) direction = -a90; - if ((AID)spell.Action.ID is AID.TailwardDustdevilVisualCW or AID.TailwardDustdevilVisualCCW) + else if (spell.Action.ID is (uint)AID.TailwardDustdevilVisualCW or (uint)AID.TailwardDustdevilVisualCCW) rotation += offset; if (Sequences.Count != 0) Sequences.Clear(); - Sequences.Add(new(cone, Module.PrimaryActor.Position, rotation, direction, Module.CastFinishAt(spell, 1), 2.6f, repeats)); + Sequences.Add(new(cone, WPos.ClampToGrid(Module.PrimaryActor.Position), rotation, direction, Module.CastFinishAt(spell, 1), 2.6f, repeats)); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.TailwardDustdevilFirst: - case AID.FangwardDustdevilFirst: - case AID.RightwardSandspoutDDRest: - case AID.LeftwardSandspoutDDRest: + case (uint)AID.TailwardDustdevilFirst: + case (uint)AID.FangwardDustdevilFirst: + case (uint)AID.RightwardSandspoutDDRest: + case (uint)AID.LeftwardSandspoutDDRest: AdvanceSequence(0, WorldState.CurrentTime); break; } @@ -49,7 +49,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) class DustcloakDustdevil(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeCircle circle = new(13); + private static readonly AOEShapeCircle circle = new(13f); private AOEInstance? _aoe; public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); @@ -58,7 +58,7 @@ public override void Update() { var component = Module.FindComponent()!.Sequences.Count == 0; if (_aoe == null && !component) - _aoe = new(circle, Module.PrimaryActor.Position, default, WorldState.FutureTime(8.1f)); + _aoe = new(circle, Module.PrimaryActor.Position, default, WorldState.FutureTime(8.1d)); else if (_aoe != null && component) _aoe = null; } diff --git a/BossMod/Modules/Dawntrail/Hunt/RankA/Heshuala.cs b/BossMod/Modules/Dawntrail/Hunt/RankA/Heshuala.cs index 93f2ea748e..a6273e10ad 100644 --- a/BossMod/Modules/Dawntrail/Hunt/RankA/Heshuala.cs +++ b/BossMod/Modules/Dawntrail/Hunt/RankA/Heshuala.cs @@ -32,36 +32,36 @@ public enum SID : uint class SpinShock(BossModule module) : Components.GenericRotatingAOE(module) { - private static readonly Angle _increment = 90.Degrees(); - private static readonly AOEShapeCone cone = new(50, 45.Degrees()); + private static readonly AOEShapeCone cone = new(50f, 45f.Degrees()); public int Spins; - private static readonly Dictionary spinMapping = new() - { - { AID.HigherPower1, 3 }, - { AID.HigherPower2, 4 }, - { AID.HigherPower3, 5 }, - { AID.HigherPower4, 6 } - }; - public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if (spinMapping.TryGetValue((AID)spell.Action.ID, out var spinCount)) - Spins = spinCount; - switch ((AID)spell.Action.ID) + var spincount = spell.Action.ID switch + { + (uint)AID.HigherPower1 => 3, + (uint)AID.HigherPower2 => 4, + (uint)AID.HigherPower3 => 5, + (uint)AID.HigherPower4 => 6, + _ => 0 + }; + if (spincount != 0) + Spins = spincount; + switch (spell.Action.ID) { - case AID.SpinshockFirstCCW: - Sequences.Add(new(cone, Module.PrimaryActor.Position, spell.Rotation, _increment, Module.CastFinishAt(spell), 2.7f, Spins)); + case (uint)AID.SpinshockFirstCCW: + AddSequence(90f.Degrees()); break; - case AID.SpinshockFirstCW: - Sequences.Add(new(cone, Module.PrimaryActor.Position, spell.Rotation, -_increment, Module.CastFinishAt(spell), 2.7f, Spins)); + case (uint)AID.SpinshockFirstCW: + AddSequence(-90f.Degrees()); break; } + void AddSequence(Angle angle) => Sequences.Add(new(cone, WPos.ClampToGrid(caster.Position), spell.Rotation, angle, Module.CastFinishAt(spell), 2.7f, Spins)); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.SpinshockFirstCCW or AID.SpinshockFirstCW or AID.SpinshockRest) + if (spell.Action.ID is (uint)AID.SpinshockFirstCCW or (uint)AID.SpinshockFirstCW or (uint)AID.SpinshockRest) { AdvanceSequence(caster.Position, spell.Rotation, WorldState.CurrentTime); --Spins; @@ -74,23 +74,25 @@ class ShockingCrossXMarksTheShock(BossModule module) : Components.GenericAOEs(mo private readonly SpinShock _rotation = module.FindComponent()!; private enum Cross { None, Cardinal, Intercardinal } private Cross currentCross; - private static readonly AOEShapeCross _cross = new(50, 5); + private static readonly AOEShapeCross _cross = new(50f, 5f); private AOEInstance? _aoe; public override IEnumerable ActiveAOEs(int slot, Actor actor) { - if (_aoe != null && _rotation.Spins < 3) - yield return _aoe.Value; + if (_aoe is AOEInstance aoe && _rotation.Spins < 3) + return [aoe]; + else + return []; } public override void OnStatusGain(Actor actor, ActorStatus status) { - switch ((SID)status.ID) + switch (status.ID) { - case SID.XMarksTheShock: + case (uint)SID.XMarksTheShock: currentCross = Cross.Intercardinal; break; - case SID.ShockingCross: + case (uint)SID.ShockingCross: currentCross = Cross.Cardinal; break; } @@ -98,7 +100,7 @@ public override void OnStatusGain(Actor actor, ActorStatus status) public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.XMarksTheShock or AID.ShockingCross) + if (spell.Action.ID is (uint)AID.XMarksTheShock or (uint)AID.ShockingCross) { currentCross = Cross.None; _aoe = null; @@ -111,13 +113,13 @@ public override void Update() { var sequence = _rotation.Sequences[0]; var rotationOffset = currentCross == Cross.Cardinal ? default : 45.Degrees(); - var activation = WorldState.FutureTime(11.5f + _rotation.Spins * 2); + var activation = WorldState.FutureTime(11.5d + _rotation.Spins * 2d); _aoe = new(_cross, sequence.Origin, sequence.Rotation + rotationOffset, activation); } } } -class LightningBolt(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightningBolt), 5); +class LightningBolt(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightningBolt), 5f); class ElectricalOverload(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.ElectricalOverload)); class HeshualaStates : StateMachineBuilder diff --git a/BossMod/Modules/Dawntrail/Hunt/RankA/Nechuciho.cs b/BossMod/Modules/Dawntrail/Hunt/RankA/Nechuciho.cs index 8ffd34c527..ab52c15ee4 100644 --- a/BossMod/Modules/Dawntrail/Hunt/RankA/Nechuciho.cs +++ b/BossMod/Modules/Dawntrail/Hunt/RankA/Nechuciho.cs @@ -73,7 +73,7 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) AddAOEs([a180, a90, -a90, default]); break; } - void AddAOEs(ReadOnlySpan angles) + void AddAOEs(Angle[] angles) { for (var i = 0; i < 4; ++i) { diff --git a/BossMod/Modules/Dawntrail/Hunt/RankS/Neyoozoteel.cs b/BossMod/Modules/Dawntrail/Hunt/RankS/Neyoozoteel.cs index eff90855f5..8e4a1204af 100644 --- a/BossMod/Modules/Dawntrail/Hunt/RankS/Neyoozoteel.cs +++ b/BossMod/Modules/Dawntrail/Hunt/RankS/Neyoozoteel.cs @@ -100,7 +100,7 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) AddAOEs([a90, a180, a90]); break; } - void AddAOEs(ReadOnlySpan angles) + void AddAOEs(Angle[] angles) { for (var i = 0; i < 3; ++i) { diff --git a/BossMod/Modules/Dawntrail/Quest/Job/Pictomancer/SomewhereOnlySheKnows/JanquetilaquesPortrait.cs b/BossMod/Modules/Dawntrail/Quest/Job/Pictomancer/SomewhereOnlySheKnows/JanquetilaquesPortrait.cs index c856c3202c..2b9db508de 100644 --- a/BossMod/Modules/Dawntrail/Quest/Job/Pictomancer/SomewhereOnlySheKnows/JanquetilaquesPortrait.cs +++ b/BossMod/Modules/Dawntrail/Quest/Job/Pictomancer/SomewhereOnlySheKnows/JanquetilaquesPortrait.cs @@ -36,42 +36,42 @@ public enum AID : uint Earthquake2 = 37532, // Helper->self, 7.0s cast, range 10-20 donut Earthquake3 = 37533, // Helper->self, 9.0s cast, range 20-30 donut - FreezeInCyan = 37540, // Boss->self, 5.0s cast, range 40 45-degree cone + FreezeInCyan = 37540 // Boss->self, 5.0s cast, range 40 45-degree cone } -class FreezeInCyan(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FreezeInCyan), new AOEShapeCone(40, 22.5f.Degrees())); -class NineIvies(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.NineIvies), new AOEShapeCone(50, 10.Degrees()), 9); -class TornadoInGreen(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TornadoInGreen), new AOEShapeDonut(10, 40)); +class FreezeInCyan(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FreezeInCyan), new AOEShapeCone(40f, 22.5f.Degrees())); +class NineIvies(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.NineIvies), new AOEShapeCone(50f, 10f.Degrees()), 9); +class TornadoInGreen(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TornadoInGreen), new AOEShapeDonut(10f, 40f)); class SculptureCast(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.SculptureCast)); class BlazeInRed(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.BlazeInRed)); class BloodyCaress(BossModule module) : Components.GenericAOEs(module) { private AOEInstance? _aoe; - private static readonly AOEShapeCone cone = new(60, 90.Degrees()); + private static readonly AOEShapeCone cone = new(60f, 90f.Degrees()); public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.AFlowerInTheSun) + if (actor.OID == (uint)OID.AFlowerInTheSun) _aoe = new(cone, actor.Position, actor.Rotation, WorldState.FutureTime(9.8f)); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.BloodyCaress) + if (spell.Action.ID == (uint)AID.BloodyCaress) _aoe = null; } } class Earthquake(BossModule module) : Components.ConcentricAOEs(module, _shapes) { - private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10), new AOEShapeDonut(10, 20), new AOEShapeDonut(20, 30)]; + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10f), new AOEShapeDonut(10f, 20f), new AOEShapeDonut(20f, 30f)]; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.Earthquake1) + if (spell.Action.ID == (uint)AID.Earthquake1) AddSequence(spell.LocXZ, Module.CastFinishAt(spell)); } @@ -79,47 +79,56 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (Sequences.Count != 0) { - var order = (AID)spell.Action.ID switch + var order = spell.Action.ID switch { - AID.Earthquake1 => 0, - AID.Earthquake2 => 1, - AID.Earthquake3 => 2, + (uint)AID.Earthquake1 => 0, + (uint)AID.Earthquake2 => 1, + (uint)AID.Earthquake3 => 2, _ => -1 }; - AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2)); + AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2d)); } } } class FloodInBlueFirst : Components.SimpleAOEs { - public FloodInBlueFirst(BossModule module) : base(module, ActionID.MakeSpell(AID.FloodInBlueFirst), new AOEShapeRect(50, 5)) { Color = Colors.Danger; } + public FloodInBlueFirst(BossModule module) : base(module, ActionID.MakeSpell(AID.FloodInBlueFirst), new AOEShapeRect(50f, 5f)) { Color = Colors.Danger; } } -class FloodInBlueRest(BossModule module) : Components.Exaflare(module, new AOEShapeRect(25, 2.5f, 25)) +class FloodInBlueRest(BossModule module) : Components.Exaflare(module, new AOEShapeRect(25f, 2.5f, 25f)) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.FloodInBlueFirst) + void AddLine(WPos first, WDir dir, Angle offset) + => Lines.Add(new() { Next = first, Advance = dir, Rotation = spell.Rotation + offset, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); + if (spell.Action.ID == (uint)AID.FloodInBlueFirst) { - var advance1 = spell.Rotation.ToDirection().OrthoR() * 7.5f; - var advance2 = spell.Rotation.ToDirection().OrthoR() * 5; + var advance = spell.Rotation.ToDirection().OrthoR(); + var advance1 = advance * 7.5f; + var advance2 = advance * 5f; var pos = caster.Position; - Lines.Add(new() { Next = pos + advance1, Advance = advance2, Rotation = spell.Rotation, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = 5, MaxShownExplosions = 2 }); - Lines.Add(new() { Next = pos - advance1, Advance = -advance2, Rotation = (spell.Rotation + 180.Degrees()).Normalized(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = 5, MaxShownExplosions = 2 }); + AddLine(pos + advance1, advance2, default); + AddLine(pos - advance1, -advance2, 180f.Degrees()); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.FloodInBlueRest) + if (spell.Action.ID == (uint)AID.FloodInBlueRest) { - var index = Lines.FindIndex(l => l.Next.AlmostEqual(caster.Position, 3)); - if (index >= 0) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - AdvanceLine(Lines[index], caster.Position + 2.5f * Lines[index].Rotation.ToDirection().OrthoR()); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 3f)) + { + AdvanceLine(line, caster.Position + 2.5f * line.Rotation.ToDirection().OrthoR()); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + break; + } } } } diff --git a/BossMod/Modules/Dawntrail/Quest/Job/Viper/VengeanceOfTheViper.cs b/BossMod/Modules/Dawntrail/Quest/Job/Viper/VengeanceOfTheViper.cs index 01890d1951..50a9ac2e70 100644 --- a/BossMod/Modules/Dawntrail/Quest/Job/Viper/VengeanceOfTheViper.cs +++ b/BossMod/Modules/Dawntrail/Quest/Job/Viper/VengeanceOfTheViper.cs @@ -50,48 +50,58 @@ public enum AID : uint Razorwind = 38736 // Helper->Keshkwa, 5.0s cast, range 7 circle, stack } -class Razorwind(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Razorwind), 7, 2, 2); -class Explosion(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Explosion), 15); +class Razorwind(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Razorwind), 7f, 2, 2); +class Explosion(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Explosion), 15f); -class SwoopingFrenzy1(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SwoopingFrenzy1), 15); +class SwoopingFrenzy1(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SwoopingFrenzy1), 15f); class SwoopingFrenzy2(BossModule module) : Components.GenericAOEs(module) { private readonly List _aoes = []; private static readonly AOEShapeCircle circle = new(15); - public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes.Take(3); + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = _aoes.Count; + if (count == 0) + return []; + var max = count > 3 ? 3 : count; + var aoes = new AOEInstance[max]; + for (var i = 0; i < max; ++i) + aoes[i] = _aoes[i]; + return aoes; + } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.SwoopingFrenzy2 || (AID)spell.Action.ID == AID.SwoopingFrenzyTelegraph && !_aoes.Any(x => x.Origin.AlmostEqual(spell.LocXZ, 1))) + if (spell.Action.ID == (uint)AID.SwoopingFrenzy2 || spell.Action.ID == (uint)AID.SwoopingFrenzyTelegraph && !_aoes.Any(x => x.Origin.AlmostEqual(spell.LocXZ, 1f))) _aoes.Add(new(circle, spell.LocXZ, default, Module.CastFinishAt(spell, 1.5f))); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (_aoes.Count != 0 && (AID)spell.Action.ID is AID.SwoopingFrenzy2 or AID.SwoopingFrenzy3) + if (_aoes.Count != 0 && spell.Action.ID is (uint)AID.SwoopingFrenzy2 or (uint)AID.SwoopingFrenzy3) _aoes.RemoveAt(0); } } -class BrutalStroke(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.BrutalStroke), 25); +class BrutalStroke(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.BrutalStroke), 25f); class CatchingChaos(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.CatchingChaos)); -class Galeripper(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Galeripper), new AOEShapeCone(60, 45.Degrees())); +class Galeripper(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Galeripper), new AOEShapeCone(60f, 45f.Degrees())); -abstract class BitingScratch(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCone(40, 45.Degrees())); +abstract class BitingScratch(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCone(40f, 45f.Degrees())); class BitingScratch1(BossModule module) : BitingScratch(module, AID.BitingScratch1); class BitingScratch2(BossModule module) : BitingScratch(module, AID.BitingScratch2); -class FervidImpact(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FervidImpact), 12); -class FrigidPulse(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FrigidPulse), new AOEShapeDonut(12, 60)); +class FervidImpact(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FervidImpact), 12f); +class FrigidPulse(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FrigidPulse), new AOEShapeDonut(12f, 60f)); class FirestormCycle(BossModule module) : Components.ConcentricAOEs(module, _shapes) { - private static readonly AOEShape[] _shapes = [new AOEShapeCircle(12), new AOEShapeDonut(12, 60)]; + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(12f), new AOEShapeDonut(12f, 60f)]; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.FirestormCycleOut) + if (spell.Action.ID == (uint)AID.FirestormCycleOut) AddSequence(spell.LocXZ, Module.CastFinishAt(spell)); } @@ -99,24 +109,24 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (Sequences.Count != 0) { - var order = (AID)spell.Action.ID switch + var order = spell.Action.ID switch { - AID.FirestormCycleOut => 0, - AID.FirestormCycleIn => 1, + (uint)AID.FirestormCycleOut => 0, + (uint)AID.FirestormCycleIn => 1, _ => -1 }; - AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2.4f)); + AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2.4d)); } } } class StormkissedFlames(BossModule module) : Components.ConcentricAOEs(module, _shapes) { - private static readonly AOEShape[] _shapes = [new AOEShapeDonut(12, 60), new AOEShapeCircle(12)]; + private static readonly AOEShape[] _shapes = [new AOEShapeDonut(12f, 60f), new AOEShapeCircle(12f)]; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.StormkissedFlamesIn) + if (spell.Action.ID == (uint)AID.StormkissedFlamesIn) AddSequence(spell.LocXZ, Module.CastFinishAt(spell)); } @@ -124,39 +134,39 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (Sequences.Count != 0) { - var order = (AID)spell.Action.ID switch + var order = spell.Action.ID switch { - AID.StormkissedFlamesIn => 0, - AID.StormkissedFlamesOut => 1, + (uint)AID.StormkissedFlamesIn => 0, + (uint)AID.StormkissedFlamesOut => 1, _ => -1 }; - AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2.4f)); + AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2.4d)); } } } class FervidPulse(BossModule module) : Components.GenericRotatingAOE(module) { - private static readonly AOEShapeCross cross = new(50, 7); - private static readonly Angle increment = 22.5f.Degrees(); + private static readonly AOEShapeCross cross = new(50f, 7f); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.FervidPulseFirstCCW: - Sequences.Add(new(cross, spell.LocXZ, spell.Rotation, increment, Module.CastFinishAt(spell), 2.9f, 5)); + case (uint)AID.FervidPulseFirstCCW: + AddSequence(22.5f.Degrees()); break; - case AID.FervidPulseFirstCW1: - case AID.FervidPulseFirstCW2: - Sequences.Add(new(cross, spell.LocXZ, spell.Rotation, -increment, Module.CastFinishAt(spell), 2.9f, 5)); + case (uint)AID.FervidPulseFirstCW1: + case (uint)AID.FervidPulseFirstCW2: + AddSequence(-22.5f.Degrees()); break; } + void AddSequence(Angle angle) => Sequences.Add(new(cross, spell.LocXZ, spell.Rotation, angle, Module.CastFinishAt(spell), 2.9f, 5)); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.FervidPulseFirstCCW or AID.FervidPulseFirstCW1 or AID.FervidPulseFirstCW2 or AID.FervidPulseRest) + if (spell.Action.ID is (uint)AID.FervidPulseFirstCCW or (uint)AID.FervidPulseFirstCW1 or (uint)AID.FervidPulseFirstCW2 or (uint)AID.FervidPulseRest) AdvanceSequence(0, WorldState.CurrentTime); } } @@ -186,7 +196,7 @@ public VengeanceOfTheViperStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus)", GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70389, NameID = 12829)] public class VengeanceOfTheViper(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly ArenaBoundsComplex arena = new([new Polygon(new(-402, 738), 19.5f, 20)]); + private static readonly ArenaBoundsComplex arena = new([new Polygon(new(-402f, 738f), 19.5f, 20)]); private static readonly uint[] all = [(uint)OID.Boss, (uint)OID.RightWing, (uint)OID.LeftWing]; protected override void DrawEnemies(int pcSlot, Actor pc) diff --git a/BossMod/Modules/Dawntrail/Quest/MSQ/TheProtectorAndTheDestroyer/E2Gwyddrud.cs b/BossMod/Modules/Dawntrail/Quest/MSQ/TheProtectorAndTheDestroyer/E2Gwyddrud.cs index 2a5995ba7d..9e7fc78e8a 100644 --- a/BossMod/Modules/Dawntrail/Quest/MSQ/TheProtectorAndTheDestroyer/E2Gwyddrud.cs +++ b/BossMod/Modules/Dawntrail/Quest/MSQ/TheProtectorAndTheDestroyer/E2Gwyddrud.cs @@ -182,10 +182,9 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) case (uint)AID.UntamedCurrentAOE10: for (var i = 0; i < count; ++i) { - var aoe = _aoes[i]; - if (aoe.ActorID == caster.InstanceID) + if (_aoes[i].ActorID == caster.InstanceID) { - _aoes.Remove(aoe); + _aoes.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Dawntrail/Quest/Role/BarThePassage/P3Apyaahi.cs b/BossMod/Modules/Dawntrail/Quest/Role/BarThePassage/P3Apyaahi.cs index 54d687d4b5..8b5faab225 100644 --- a/BossMod/Modules/Dawntrail/Quest/Role/BarThePassage/P3Apyaahi.cs +++ b/BossMod/Modules/Dawntrail/Quest/Role/BarThePassage/P3Apyaahi.cs @@ -139,19 +139,26 @@ class SpoilingSmashEnrage(BossModule module) : Components.CastHint(module, Actio public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID == (uint)AID.BurstFirst) - Lines.Add(new() { Next = caster.Position, Advance = 5 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.2f, ExplosionsLeft = 9, MaxShownExplosions = 4 }); + Lines.Add(new() { Next = caster.Position, Advance = 5f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.2f, ExplosionsLeft = 9, MaxShownExplosions = 4 }); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (spell.Action.ID is (uint)AID.BurstFirst or (uint)AID.BurstRepeat) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(spell.LocXZ, 1)); - if (index == -1) - return; - AdvanceLine(Lines[index], spell.LocXZ); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = spell.LocXZ; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + break; + } + } } } } diff --git a/BossMod/Modules/Dawntrail/Quest/Role/HeroesAndPretenders.cs b/BossMod/Modules/Dawntrail/Quest/Role/HeroesAndPretenders.cs index 6d1ce1b23f..91ef600335 100644 --- a/BossMod/Modules/Dawntrail/Quest/Role/HeroesAndPretenders.cs +++ b/BossMod/Modules/Dawntrail/Quest/Role/HeroesAndPretenders.cs @@ -50,24 +50,31 @@ public override void AddGlobalHints(GlobalHints hints) } } -class ForeseenFlurry(BossModule module) : Components.Exaflare(module, 4) +class ForeseenFlurry(BossModule module) : Components.Exaflare(module, 4f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID == (uint)AID.ForeseenFlurryFirst) - Lines.Add(new() { Next = caster.Position, Advance = 5 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 8, MaxShownExplosions = 3 }); + Lines.Add(new() { Next = caster.Position, Advance = 5f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 8, MaxShownExplosions = 3 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { if (spell.Action.ID is (uint)AID.ForeseenFlurryFirst or (uint)AID.ForeseenFlurryRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + break; + } + } } } } @@ -89,7 +96,7 @@ public HeroesAndPretendersStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus)", GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70383, NameID = 13176)] public class HeroesAndPretenders(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly ArenaBoundsComplex arena = new([new Polygon(new(676, 41), 14.5f, 20)]); + private static readonly ArenaBoundsComplex arena = new([new Polygon(new(676f, 41f), 14.5f, 20)]); private static readonly uint[] all = [(uint)OID.Boss, (uint)OID.PerchOfTheApex, (uint)OID.CultivatedMossFungus, (uint)OID.CultivatedOchu1, (uint)OID.CultivatedOchu2, (uint)OID.CultivatedMorbolSeedling]; diff --git a/BossMod/Modules/Dawntrail/Raid/M03NBruteBomber/Firespin.cs b/BossMod/Modules/Dawntrail/Raid/M03NBruteBomber/Firespin.cs index d240937786..957db5e797 100644 --- a/BossMod/Modules/Dawntrail/Raid/M03NBruteBomber/Firespin.cs +++ b/BossMod/Modules/Dawntrail/Raid/M03NBruteBomber/Firespin.cs @@ -2,26 +2,27 @@ namespace BossMod.Dawntrail.Raid.M03NBruteBomber; class FireSpin(BossModule module) : Components.GenericRotatingAOE(module) { - private static readonly AOEShapeCone cone = new(40, 30.Degrees()); + private static readonly AOEShapeCone cone = new(40f, 30f.Degrees()); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.FireSpinCCW: - case AID.InfernalSpinCCW: - Sequences.Add(new(cone, Module.PrimaryActor.Position, spell.Rotation, 45.Degrees(), Module.CastFinishAt(spell, 0.5f), 1, 8)); + case (uint)AID.FireSpinCCW: + case (uint)AID.InfernalSpinCCW: + AddSequence(45f.Degrees()); break; - case AID.FireSpinCW: - case AID.InfernalSpinCW: - Sequences.Add(new(cone, Module.PrimaryActor.Position, spell.Rotation, -45.Degrees(), Module.CastFinishAt(spell, 0.5f), 1, 8)); + case (uint)AID.FireSpinCW: + case (uint)AID.InfernalSpinCW: + AddSequence(-45f.Degrees()); break; } + void AddSequence(Angle angle) => Sequences.Add(new(cone, spell.LocXZ, spell.Rotation, angle, Module.CastFinishAt(spell, 0.5f), 1f, 8)); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.FireSpinFirst or AID.FireSpinRest or AID.InfernalSpinFirst or AID.InfernalSpinRest) + if (spell.Action.ID is (uint)AID.FireSpinFirst or (uint)AID.FireSpinRest or (uint)AID.InfernalSpinFirst or (uint)AID.InfernalSpinRest) AdvanceSequence(0, WorldState.CurrentTime); } } diff --git a/BossMod/Modules/Dawntrail/Savage/M04SWickedThunder/Sabertail.cs b/BossMod/Modules/Dawntrail/Savage/M04SWickedThunder/Sabertail.cs index 8da383ffd5..d3ae7c459e 100644 --- a/BossMod/Modules/Dawntrail/Savage/M04SWickedThunder/Sabertail.cs +++ b/BossMod/Modules/Dawntrail/Savage/M04SWickedThunder/Sabertail.cs @@ -19,16 +19,20 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (spell.Action.ID is (uint)AID.SabertailFirst or (uint)AID.SabertailRest) { ++NumCasts; - int index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs index c4d77b8081..0d74a48df9 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/P4CrystallizeTime.cs @@ -20,26 +20,26 @@ public enum Mechanic { None, FangEruption, FangWater, FangDarkness, FangBlizzard public override void OnStatusGain(Actor actor, ActorStatus status) { - switch ((SID)status.ID) + switch (status.ID) { - case SID.SpellInWaitingDarkEruption: + case (uint)SID.SpellInWaitingDarkEruption: AssignMechanic(actor, Mechanic.FangEruption); // always paired with fang break; - case SID.SpellInWaitingDarkWater: + case (uint)SID.SpellInWaitingDarkWater: AssignMechanic(actor, Mechanic.FangWater); // always paired with fang break; - case SID.SpellInWaitingUnholyDarkness: + case (uint)SID.SpellInWaitingUnholyDarkness: AssignMechanic(actor, Mechanic.FangDarkness); // always paired with fang break; - case SID.SpellInWaitingDarkBlizzard: + case (uint)SID.SpellInWaitingDarkBlizzard: AssignMechanic(actor, Mechanic.FangBlizzard, higherPrio: Mechanic.ClawBlizzard); // paired with either, we'll reassign to claw when reacting to claw buff break; - case SID.SpellInWaitingDarkAero: + case (uint)SID.SpellInWaitingDarkAero: AssignMechanic(actor, Mechanic.ClawAir); // always paired with claw break; - case SID.Wyrmfang: + case (uint)SID.Wyrmfang: break; // don't react - case SID.Wyrmclaw: + case (uint)SID.Wyrmclaw: var duration = (status.ExpireAt - WorldState.CurrentTime).TotalSeconds; // 40s for aero, 17s for claw if (duration > 25) AssignMechanic(actor, Mechanic.ClawAir); @@ -53,7 +53,7 @@ public override void OnStatusGain(Actor actor, ActorStatus status) public override void OnStatusLose(Actor actor, ActorStatus status) { - if ((SID)status.ID is SID.Wyrmclaw or SID.Wyrmfang) + if (status.ID is (uint)SID.Wyrmclaw or (uint)SID.Wyrmfang) Cleansed.Set(Raid.FindSlot(actor.InstanceID)); } @@ -116,9 +116,9 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme foreach (var p in _puddles.Where(p => p.puddle.EventState != 7)) { if (p.soaker != pcAssignment) - hints.AddForbiddenZone(ShapeDistance.Circle(p.puddle.Position, 2)); + hints.AddForbiddenZone(ShapeDistance.Circle(p.puddle.Position, 2f)); else if (_numMaelstroms >= 6) - hints.GoalZones.Add(hints.GoalProximity(p.puddle.Position, 15, 0.25f)); + hints.GoalZones.Add(hints.GoalProximity(p.puddle.Position, 15f, 0.25f)); } } } @@ -130,7 +130,7 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) Arena.Actor(h.head, Colors.Object, true); var interceptor = FindInterceptor(h.head, h.side); if (interceptor != null) - Arena.AddCircle(interceptor.Position, 12, Colors.Danger); + Arena.AddCircle(interceptor.Position, 12f); } } @@ -141,18 +141,18 @@ public override void DrawArenaBackground(int pcSlot, Actor pc) var pcAssignment = _ct.PlayerMechanics[pcSlot]; foreach (var p in _puddles) if (p.puddle.EventState != 7) - Arena.ZoneCircle(p.puddle.Position, 1, p.soaker == pcAssignment ? Colors.SafeFromAOE : Colors.AOE); + Arena.ZoneCircle(p.puddle.Position, 1f, p.soaker == pcAssignment ? Colors.SafeFromAOE : 0); } } public override void OnActorCreated(Actor actor) { - switch ((OID)actor.OID) + switch (actor.OID) { - case OID.DrachenWanderer: + case (uint)OID.DrachenWanderer: Heads.Add((actor, actor.Position.X > Arena.Center.X ? 1 : -1)); break; - case OID.DragonPuddle: + case (uint)OID.DragonPuddle: // TODO: this is very arbitrary var mechanic = actor.Position.X < Arena.Center.X ? AssignPuddle(P4CrystallizeTime.Mechanic.FangEruption, P4CrystallizeTime.Mechanic.FangBlizzard) @@ -164,12 +164,12 @@ public override void OnActorCreated(Actor actor) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.DrachenWandererDisappear: + case (uint)AID.DrachenWandererDisappear: Heads.RemoveAll(h => h.head == caster); break; - case AID.CrystallizeTimeMaelstrom: + case (uint)AID.CrystallizeTimeMaelstrom: ++_numMaelstroms; break; } @@ -182,7 +182,7 @@ class P4CrystallizeTimeMaelstrom(BossModule module) : Components.GenericAOEs(mod { public readonly List AOEs = []; - private static readonly AOEShapeCircle _shape = new(12); + private static readonly AOEShapeCircle _shape = new(12f); public override IEnumerable ActiveAOEs(int slot, Actor actor) => AOEs.Take(2); @@ -191,24 +191,24 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme // assuming that this component is activated when speed cast starts - all hourglasses should be already created, and tethers should have appeared few frames ago public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.SorrowsHourglass) + if (actor.OID == (uint)OID.SorrowsHourglass) { - AOEs.Add(new(_shape, actor.Position, actor.Rotation, WorldState.FutureTime(13.2f))); + AOEs.Add(new(_shape, actor.Position, actor.Rotation, WorldState.FutureTime(13.2d))); AOEs.SortBy(aoe => aoe.Activation); } } public override void OnTethered(Actor source, ActorTetherInfo tether) { - var delay = (TetherID)tether.ID switch + var delay = tether.ID switch { - TetherID.UltimateRelativitySlow => 18.3f, - TetherID.UltimateRelativityQuicken => 7.7f, + (uint)TetherID.UltimateRelativitySlow => 18.3d, + (uint)TetherID.UltimateRelativityQuicken => 7.7d, _ => 0 }; if (delay != 0) { - var index = AOEs.FindIndex(aoe => aoe.Origin.AlmostEqual(source.Position, 1)); + var index = AOEs.FindIndex(aoe => aoe.Origin.AlmostEqual(source.Position, 1f)); if (index >= 0) { AOEs.Ref(index).Activation = WorldState.FutureTime(delay); @@ -222,18 +222,18 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (spell.Action == WatchedAction) { ++NumCasts; - AOEs.RemoveAll(aoe => aoe.Origin.AlmostEqual(caster.Position, 1)); + AOEs.RemoveAll(aoe => aoe.Origin.AlmostEqual(caster.Position, 1f)); } } } -class P4CrystallizeTimeDarkWater(BossModule module) : Components.UniformStackSpread(module, 6, 0, 4, 4) +class P4CrystallizeTimeDarkWater(BossModule module) : Components.UniformStackSpread(module, 6f, default, 4, 4) { public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { } // handled by other components public override void OnStatusGain(Actor actor, ActorStatus status) { - if ((SID)status.ID == SID.SpellInWaitingDarkWater) + if (status.ID == (uint)SID.SpellInWaitingDarkWater) { BitMask forbidden = default; if (Module.FindComponent() is var ct && ct != null) @@ -245,7 +245,7 @@ public override void OnStatusGain(Actor actor, ActorStatus status) { P4CrystallizeTime.Mechanic.FangEruption => true, P4CrystallizeTime.Mechanic.ClawBlizzard => true, - P4CrystallizeTime.Mechanic.ClawAir => ct.ClawSides[i] * ct.NorthSlowHourglass.X > 0, + P4CrystallizeTime.Mechanic.ClawAir => ct.ClawSides[i] * ct.NorthSlowHourglass.X > 0f, _ => false }; } @@ -256,20 +256,20 @@ public override void OnStatusGain(Actor actor, ActorStatus status) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.DarkWater) + if (spell.Action.ID == (uint)AID.DarkWater) Stacks.Clear(); } } class P4CrystallizeTimeDarkEruption(BossModule module) : Components.GenericBaitAway(module, ActionID.MakeSpell(AID.DarkEruption)) { - private static readonly AOEShapeCircle _shape = new(6); + private static readonly AOEShapeCircle _shape = new(6f); public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { } // handled by other components public override void OnStatusGain(Actor actor, ActorStatus status) { - if ((SID)status.ID == SID.SpellInWaitingDarkEruption) + if (status.ID == (uint)SID.SpellInWaitingDarkEruption) { CurrentBaits.Add(new(actor, actor, _shape, status.ExpireAt)); } @@ -281,13 +281,26 @@ public override void OnStatusGain(Actor actor, ActorStatus status) private readonly List _sources = []; private DateTime _activation; - private static readonly AOEShapeCircle _shape = new(15); + private static readonly AOEShapeCircle _shape = new(15f); - public override IEnumerable Sources(int slot, Actor actor) => _sources.Exclude(actor).Select(s => new Source(s.Position, 30, _activation, _shape)); + public override IEnumerable Sources(int slot, Actor actor) + { + var count = _sources.Count; + if (count == 0) + return []; + List sources = []; + for (var i = 0; i < count; ++i) + { + var s = _sources[i]; + if (s != actor) + sources.Add(new(s.Position, 30f, _activation, _shape)); + } + return sources; + } public override void OnStatusGain(Actor actor, ActorStatus status) { - if ((SID)status.ID == SID.SpellInWaitingDarkAero) + if (status.ID == (uint)SID.SpellInWaitingDarkAero) { _sources.Add(actor); _activation = status.ExpireAt; @@ -295,13 +308,13 @@ public override void OnStatusGain(Actor actor, ActorStatus status) } } -class P4CrystallizeTimeUnholyDarkness(BossModule module) : Components.UniformStackSpread(module, 6, 0, 5, 5) +class P4CrystallizeTimeUnholyDarkness(BossModule module) : Components.UniformStackSpread(module, 6f, default, 5, 5) { public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { } // handled by other components public override void OnStatusGain(Actor actor, ActorStatus status) { - if ((SID)status.ID == SID.SpellInWaitingUnholyDarkness) + if (status.ID == (uint)SID.SpellInWaitingUnholyDarkness) { BitMask forbidden = default; if (Module.FindComponent() is var ct && ct != null) @@ -311,7 +324,7 @@ public override void OnStatusGain(Actor actor, ActorStatus status) // should not be shared by all claws except blizzard on slow side forbidden[i] = ct.PlayerMechanics[i] switch { - P4CrystallizeTime.Mechanic.ClawBlizzard => ct.ClawSides[i] * ct.NorthSlowHourglass.X < 0, + P4CrystallizeTime.Mechanic.ClawBlizzard => ct.ClawSides[i] * ct.NorthSlowHourglass.X < 0f, P4CrystallizeTime.Mechanic.ClawAir => true, _ => false }; @@ -323,7 +336,7 @@ public override void OnStatusGain(Actor actor, ActorStatus status) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.UltimateRelativityUnholyDarkness) + if (spell.Action.ID == (uint)AID.UltimateRelativityUnholyDarkness) Stacks.Clear(); } } @@ -333,16 +346,16 @@ class P4CrystallizeTimeTidalLight : Components.Exaflare public List<(WPos pos, Angle dir)> StartingPositions = []; public WDir StartingOffsetSum; - public P4CrystallizeTimeTidalLight(BossModule module) : base(module, new AOEShapeRect(10, 20)) + public P4CrystallizeTimeTidalLight(BossModule module) : base(module, new AOEShapeRect(10f, 20f)) { ImminentColor = Colors.AOE; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.TidalLightAOEFirst) + if (spell.Action.ID == (uint)AID.TidalLightAOEFirst) { - Lines.Add(new() { Next = caster.Position, Advance = 10 * spell.Rotation.ToDirection(), Rotation = spell.Rotation, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.1f, ExplosionsLeft = 4, MaxShownExplosions = 1 }); + Lines.Add(new() { Next = caster.Position, Advance = 10f * spell.Rotation.ToDirection(), Rotation = spell.Rotation, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.1f, ExplosionsLeft = 4, MaxShownExplosions = 1 }); StartingPositions.Add((caster.Position, spell.Rotation)); StartingOffsetSum += caster.Position - Arena.Center; } @@ -350,19 +363,23 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.TidalLightAOEFirst or AID.TidalLightAOERest) + if (spell.Action.ID is (uint)AID.TidalLightAOEFirst or (uint)AID.TidalLightAOERest) { ++NumCasts; - int index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } @@ -399,7 +416,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme if (hint.offset != default) { // we want to stay really close to border - if (hint.offset.LengthSq() > 324) + if (hint.offset.LengthSq() > 324f) hint.offset *= 1.02632f; if (hint.hint.HasFlag(Hint.KnockbackFrom) && Raid.WithoutSlot(false, true, true).Any(p => p.PendingKnockbacks.Count > 0)) @@ -408,12 +425,12 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme } if (hint.hint.HasFlag(Hint.SafespotRough)) { - hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Arena.Center + hint.offset, 1), DateTime.MaxValue); + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Arena.Center + hint.offset, 1f), DateTime.MaxValue); } if (hint.hint.HasFlag(Hint.SafespotPrecise)) { hints.PathfindMapBounds = FRU.PathfindHugBorderBounds; - hints.AddForbiddenZone(ShapeDistance.PrecisePosition(Arena.Center + hint.offset, new(0, 1), Arena.Bounds.MapResolution, actor.Position, 0.1f)); + hints.AddForbiddenZone(ShapeDistance.PrecisePosition(Arena.Center + hint.offset, new(default, 1f), Arena.Bounds.MapResolution, actor.Position, 0.1f)); } if (hint.hint.HasFlag(Hint.Maelstrom) && _hourglass != null) { @@ -424,11 +441,11 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme { foreach (var h in _heads.Heads) if (_heads.FindInterceptor(h.head, h.side) is var interceptor && interceptor != null && interceptor != actor) - hints.AddForbiddenZone(ShapeDistance.Circle(interceptor.Position, 12)); + hints.AddForbiddenZone(ShapeDistance.Circle(interceptor.Position, 12f)); } if (hint.hint.HasFlag(Hint.Knockback) && _ct != null) { - var source = _ct.FindPlayerByAssignment(P4CrystallizeTime.Mechanic.ClawAir, _ct.NorthSlowHourglass.X > 0 ? -1 : 1); + var source = _ct.FindPlayerByAssignment(P4CrystallizeTime.Mechanic.ClawAir, _ct.NorthSlowHourglass.X > 0f ? -1 : 1); var dest = Arena.Center + SafeOffsetDarknessStack(_ct.NorthSlowHourglass.X > 0 ? 1 : -1); var pos = source != null ? source.Position + 2 * (dest - source.Position).Normalized() : Arena.Center + hint.offset; hints.AddForbiddenZone(ShapeDistance.PrecisePosition(pos, new(0, 1), Arena.Bounds.MapResolution, actor.Position, 0.1f)); @@ -436,8 +453,8 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme if (hint.hint.HasFlag(Hint.Mid) && _hourglass != null && !_hourglass.AOEs.Take(2).Any(aoe => aoe.Check(actor.Position))) { // stay on correct side - var dest = Arena.Center + new WDir(0, hint.offset.Z > 0 ? 18 : -18); - hints.GoalZones.Add(hints.GoalSingleTarget(dest, 2, 0.5f)); + var dest = Arena.Center + new WDir(default, hint.offset.Z > 0f ? 18f : -18f); + hints.GoalZones.Add(hints.GoalSingleTarget(dest, 2f, 0.5f)); } } } @@ -446,33 +463,33 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) { var safeOffset = CalculateHint(pcSlot).offset; if (safeOffset != default) - Arena.AddCircle(Arena.Center + safeOffset, 1, Colors.Safe); + Arena.AddCircle(Arena.Center + safeOffset, 1f, Colors.Safe); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.CrystallizeTimeDarkAero: - KnockbacksResolve = WorldState.FutureTime(1); // it takes ~0.8s to resolve knockbacks + case (uint)AID.CrystallizeTimeDarkAero: + KnockbacksResolve = WorldState.FutureTime(1d); // it takes ~0.8s to resolve knockbacks break; - case AID.UltimateRelativityUnholyDarkness: + case (uint)AID.UltimateRelativityUnholyDarkness: DarknessDone = true; break; } } // these are all possible 'raw' safespot offsets; they expect valid arguments - private static readonly Angle IdealSecondHeadBaitAngle = 33.Degrees(); - private static WDir SafeOffsetDodgeFirstHourglassSouth(int side) => 19 * (side * 40).Degrees().ToDirection(); - private static WDir SafeOffsetPreKnockbackSouth(int side, float radius) => radius * (side * 30).Degrees().ToDirection(); - private static WDir SafeOffsetDarknessStack(int side) => 19 * (side * 140).Degrees().ToDirection(); - private static WDir SafeOffsetDodgeSecondHourglassSouth(int side) => 19 * (side * 20).Degrees().ToDirection(); - private static WDir SafeOffsetDodgeSecondHourglassEW(int side) => 19 * (side * 80).Degrees().ToDirection(); // for ice that doesn't share unholy darkness - private static WDir SafeOffsetFirstHeadBait(int side) => 13 * (side * 90).Degrees().ToDirection(); - private static WDir SafeOffsetSecondHeadBait(int side) => 13 * (side * IdealSecondHeadBaitAngle).ToDirection(); - private static WDir SafeOffsetChillNorth(int side) => 6 * (side * 150).Degrees().ToDirection(); // final for non-airs - private static WDir SafeOffsetChillSouth(int side) => 6 * (side * 30).Degrees().ToDirection(); // final for 2 airs + private static readonly Angle IdealSecondHeadBaitAngle = 33f.Degrees(); + private static WDir SafeOffsetDodgeFirstHourglassSouth(int side) => 19f * (side * 40f).Degrees().ToDirection(); + private static WDir SafeOffsetPreKnockbackSouth(int side, float radius) => radius * (side * 30f).Degrees().ToDirection(); + private static WDir SafeOffsetDarknessStack(int side) => 19f * (side * 140f).Degrees().ToDirection(); + private static WDir SafeOffsetDodgeSecondHourglassSouth(int side) => 19f * (side * 20f).Degrees().ToDirection(); + private static WDir SafeOffsetDodgeSecondHourglassEW(int side) => 19f * (side * 80f).Degrees().ToDirection(); // for ice that doesn't share unholy darkness + private static WDir SafeOffsetFirstHeadBait(int side) => 13f * (side * 90f).Degrees().ToDirection(); + private static WDir SafeOffsetSecondHeadBait(int side) => 13f * (side * IdealSecondHeadBaitAngle).ToDirection(); + private static WDir SafeOffsetChillNorth(int side) => 6f * (side * 150f).Degrees().ToDirection(); // final for non-airs + private static WDir SafeOffsetChillSouth(int side) => 6f * (side * 30f).Degrees().ToDirection(); // final for 2 airs // these determine rough safespot offset (depending on player state and mechanic progress) for drawing on arena or adding ai hints private (WDir offset, Hint hint) CalculateHint(int slot) @@ -480,7 +497,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (_ct == null || _heads == null || _hourglass == null || _ct.NorthSlowHourglass.X == 0) return default; var clawSide = _ct.ClawSides[slot]; - var northSlowSide = _ct.NorthSlowHourglass.X > 0 ? 1 : -1; + var northSlowSide = _ct.NorthSlowHourglass.X > 0f ? 1 : -1; return _ct.PlayerMechanics[slot] switch { P4CrystallizeTime.Mechanic.ClawAir => clawSide != 0 ? HintClawAir(clawSide, _hourglass.NumCasts, northSlowSide) : default, @@ -536,7 +553,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (numHourglassesDone < 2) return (SafeOffsetDodgeFirstHourglassSouth(-northSlowSide), Hint.SafespotRough | Hint.Maelstrom); // dodge first hourglass by the south side if (KnockbacksResolve == default) - return (SafeOffsetPreKnockbackSouth(-northSlowSide, 17), Hint.Knockback); // preposition to knockback across arena + return (SafeOffsetPreKnockbackSouth(-northSlowSide, 17f), Hint.Knockback); // preposition to knockback across arena // from now on move together with eruption return HintFangEruption(northSlowSide, numHourglassesDone); } @@ -553,7 +570,7 @@ public override IEnumerable Sources(int slot, Actor actor) { if (!RewindDone && _ct != null && _exalines != null && _ct.Cleansed[slot]) foreach (var s in _exalines.StartingPositions) - yield return new(s.pos, 20, Direction: s.dir, Kind: Kind.DirForward); + yield return new(s.pos, 20f, Direction: s.dir, Kind: Kind.DirForward); } public override void AddHints(int slot, Actor actor, TextHints hints) @@ -568,9 +585,9 @@ public override void AddHints(int slot, Actor actor, TextHints hints) var zOrder = Array.IndexOf(players, actor); if (xOrder >= 0 && zOrder >= 0) { - if (_exalines.StartingOffsetSum.X > 0) + if (_exalines.StartingOffsetSum.X > 0f) xOrder = players.Length - 1 - xOrder; - if (_exalines.StartingOffsetSum.Z > 0) + if (_exalines.StartingOffsetSum.Z > 0f) zOrder = players.Length - 1 - zOrder; var isFirst = xOrder == 0 || zOrder == 0; @@ -590,9 +607,9 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme if (!RewindDone && _ct != null && _exalines != null && _ct.Cleansed[slot]) { var midpoint = SafeCorner(); - hints.GoalZones.Add(hints.GoalProximity(midpoint, 15, 0.5f)); + hints.GoalZones.Add(hints.GoalProximity(midpoint, 15f, 0.5f)); var destPoint = midpoint + AssignedPositionOffset(actor, assignment); - hints.GoalZones.Add(hints.GoalProximity(destPoint, 1, 1)); + hints.GoalZones.Add(hints.GoalProximity(destPoint, 1f, 1f)); } } @@ -602,7 +619,7 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) if (!RewindDone && _exalines != null) { var midpoint = SafeCorner(); - Arena.AddCircle(midpoint, 1, Colors.Danger); + Arena.AddCircle(midpoint, 1f); var offset = AssignedPositionOffset(pc, Service.Config.Get()[Module.Raid.Members[pcSlot].ContentId]); if (offset != default) Arena.AddCircle(midpoint + offset, 1, Colors.Safe); @@ -611,12 +628,12 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) public override void OnStatusGain(Actor actor, ActorStatus status) { - switch ((SID)status.ID) + switch (status.ID) { - case SID.Return: + case (uint)SID.Return: RewindDone = true; break; - case SID.Stun: + case (uint)SID.Stun: ReturnDone = true; break; } diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/P5FulgentBlade.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/P5FulgentBlade.cs index 29b6246505..65d325b6e1 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/P5FulgentBlade.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/P5FulgentBlade.cs @@ -6,7 +6,7 @@ class P5FulgentBlade : Components.Exaflare private WDir _initialSafespot; private DateTime _nextBundle; - public P5FulgentBlade(BossModule module) : base(module, new AOEShapeRect(5, 40)) + public P5FulgentBlade(BossModule module) : base(module, new AOEShapeRect(5f, 40f)) { ImminentColor = Colors.AOE; } @@ -16,7 +16,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme base.AddAIHints(slot, actor, assignment, hints); // add an extra hint to move to safe spot (TODO: reconsider? this can fuck up positionals for melee etc) if (Lines.Count != 0 && NumCasts <= 6 && SafeSpot() is var safespot && safespot != default) - hints.AddForbiddenZone(ShapeDistance.InvertedCircle(safespot, 1), DateTime.MaxValue); + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(safespot, 1f), DateTime.MaxValue); //if (Lines.Count > 0 && NumCasts <= 6 && _lines.Count == 6) //{ // var shape = NumCasts switch @@ -34,12 +34,12 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) { var safespot = SafeSpot(); if (safespot != default) - Arena.AddCircle(safespot, 1, Colors.Safe); + Arena.AddCircle(safespot, 1f, Colors.Safe); } public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.FulgentBladeLine) + if (actor.OID == (uint)OID.FulgentBladeLine) { var dir = (actor.Position - Arena.Center).Normalized(); _lines.Add((actor, dir)); @@ -54,51 +54,57 @@ public override void OnActorCreated(Actor actor) public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.PathOfLightFirst or AID.PathOfDarknessFirst) + if (spell.Action.ID is (uint)AID.PathOfLightFirst or (uint)AID.PathOfDarknessFirst) { if (Lines.Count == 0) UpdateOrder(caster.Position); // first line - we should have all 6 line actors already created, and it should match position of first or last two var dir = spell.Rotation.ToDirection(); - var distanceToBorder = Intersect.RayCircle(caster.Position - Arena.Center, dir, 22); - Lines.Add(new() { Next = spell.LocXZ, Advance = 5 * dir, Rotation = spell.Rotation, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = (int)(distanceToBorder / 5) + 1, MaxShownExplosions = 1 }); + var distanceToBorder = Intersect.RayCircle(caster.Position - Arena.Center, dir, 22f); + Lines.Add(new() { Next = caster.Position, Advance = 5f * dir, Rotation = spell.Rotation, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = (int)(distanceToBorder / 5) + 1, MaxShownExplosions = 1 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.PathOfLightFirst or AID.PathOfDarknessFirst or AID.PathOfLightRest or AID.PathOfDarknessRest) + if (spell.Action.ID is (uint)AID.PathOfLightFirst or (uint)AID.PathOfDarknessFirst or (uint)AID.PathOfLightRest or (uint)AID.PathOfDarknessRest) { if (WorldState.CurrentTime > _nextBundle) { ++NumCasts; - _nextBundle = WorldState.FutureTime(1); + _nextBundle = WorldState.FutureTime(1d); } - int index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1) && item.Rotation.AlmostEqual(spell.Rotation, 0.1f)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + var rot = spell.Rotation; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f) && line.Rotation.AlmostEqual(rot, 0.1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } private void UpdateOrder(WPos firstPos) { - if (_lines.Count != 6) + var count = _lines.Count; + if (count != 6) { ReportError($"Unexpected number of lines at cast start: {_lines.Count}"); } - else if (_lines[^1].actor.Position.AlmostEqual(firstPos, 1) || _lines[^2].actor.Position.AlmostEqual(firstPos, 1)) + else if (_lines[count - 1].actor.Position.AlmostEqual(firstPos, 1) || _lines[count - 2].actor.Position.AlmostEqual(firstPos, 1f)) { _lines.Reverse(); // we guessed incorrectly, update the order } - else if (!_lines[0].actor.Position.AlmostEqual(firstPos, 1) && !_lines[1].actor.Position.AlmostEqual(firstPos, 1)) + else if (!_lines[0].actor.Position.AlmostEqual(firstPos, 1) && !_lines[1].actor.Position.AlmostEqual(firstPos, 1f)) { ReportError($"First cast at {firstPos} does not correspond to any of the first/last two lines"); } @@ -128,10 +134,10 @@ private WPos SafeSpot(int i1, int i2, float distance) var p2 = l2.actor.Position - distance * l2.dir; var d1 = l1.dir.OrthoL(); var d2 = l2.dir.OrthoL(); - p1 -= 50 * d1; // rays are 0 to infinity, oh well - p2 -= 50 * d2; + p1 -= 50f * d1; // rays are 0 to infinity, oh well + p2 -= 50f * d2; var t = Intersect.RayLine(p1, d1, p2, d2); - return t is > 0 and < 100 ? p1 + t * d1 : default; + return t is > 0f and < 100f ? p1 + t * d1 : default; } //private Func LineIntersection(int i1, int i2, float distance = 5, float cushion = 1) diff --git a/BossMod/Modules/Dawntrail/Unreal/Un1Byakko/HundredfoldHavoc.cs b/BossMod/Modules/Dawntrail/Unreal/Un1Byakko/HundredfoldHavoc.cs index 8aae571153..16b1ba7869 100644 --- a/BossMod/Modules/Dawntrail/Unreal/Un1Byakko/HundredfoldHavoc.cs +++ b/BossMod/Modules/Dawntrail/Unreal/Un1Byakko/HundredfoldHavoc.cs @@ -1,30 +1,34 @@ namespace BossMod.Dawntrail.Unreal.Un1Byakko; -class HundredfoldHavoc(BossModule module) : Components.Exaflare(module, 5) +class HundredfoldHavoc(BossModule module) : Components.Exaflare(module, 5f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.HundredfoldHavocFirst) + if (spell.Action.ID == (uint)AID.HundredfoldHavocFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 5 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 4, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = caster.Position, Advance = 5 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 4, MaxShownExplosions = 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.HundredfoldHavocFirst or AID.HundredfoldHavocRest) + if (spell.Action.ID is (uint)AID.HundredfoldHavocFirst or (uint)AID.HundredfoldHavocRest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Alliance/A11Byregot/Reproduce.cs b/BossMod/Modules/Endwalker/Alliance/A11Byregot/Reproduce.cs index fae4062b06..7c5efde83b 100644 --- a/BossMod/Modules/Endwalker/Alliance/A11Byregot/Reproduce.cs +++ b/BossMod/Modules/Endwalker/Alliance/A11Byregot/Reproduce.cs @@ -4,29 +4,32 @@ class Reproduce(BossModule module) : Components.Exaflare(module, 7) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.CloudToGroundFast or AID.CloudToGroundSlow) + if (spell.Action.ID is (uint)AID.CloudToGroundFast or (uint)AID.CloudToGroundSlow) { - var fast = (AID)spell.Action.ID == AID.CloudToGroundFast; - Lines.Add(new() { Next = spell.LocXZ, Advance = new(-8.5f, 0), NextExplosion = Module.CastFinishAt(spell), TimeToMove = fast ? 0.6f : 1.4f, ExplosionsLeft = 6, MaxShownExplosions = fast ? 5 : 2 }); + var fast = spell.Action.ID == (uint)AID.CloudToGroundFast; + Lines.Add(new() { Next = caster.Position, Advance = new(-8.5f, default), NextExplosion = Module.CastFinishAt(spell), TimeToMove = fast ? 0.6f : 1.4f, ExplosionsLeft = 6, MaxShownExplosions = fast ? 5 : 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.CloudToGroundFast or AID.CloudToGroundSlow or AID.CloudToGroundFastAOE or AID.CloudToGroundSlowAOE) + if (spell.Action.ID is (uint)AID.CloudToGroundFast or (uint)AID.CloudToGroundSlow or (uint)AID.CloudToGroundFastAOE or (uint)AID.CloudToGroundSlowAOE) { ++NumCasts; - - var index = Lines.FindIndex(item => Math.Abs(item.Next.Z - caster.Position.Z) < 1); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].Next.X < Arena.Center.X - Arena.Bounds.Radius) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Alliance/A12Rhalgr/HandOfTheDestroyer.cs b/BossMod/Modules/Endwalker/Alliance/A12Rhalgr/HandOfTheDestroyer.cs index b659d4c589..6c750c13f2 100644 --- a/BossMod/Modules/Endwalker/Alliance/A12Rhalgr/HandOfTheDestroyer.cs +++ b/BossMod/Modules/Endwalker/Alliance/A12Rhalgr/HandOfTheDestroyer.cs @@ -33,34 +33,35 @@ class BrokenShards(BossModule module) : Components.GenericAOEs(module) private static readonly WPos[] _eastLocations = [new(-30.025f, 266.9f), new(-46.525f, 269.6f), new(-26.225f, 292.9f), new(-2.825f, 283.5f), new(-37.425f, 283.7f), new(1.575f, 271.5f), new(-18.825f, 278.8f), new(-12.325f, 298.3f), new(-34.125f, 250.5f)]; private static readonly WPos[] _westLocations = [new(-6.925f, 268.0f), new(-0.175f, 285.0f), new(-25.625f, 298.5f), new(-34.225f, 283.5f), new(-11.625f, 293.5f), new(-46.125f, 270.5f), new(-18.125f, 279.0f), new(-40.325f, 290.5f), new(-2.125f, 252.0f)]; - private static readonly AOEShapeCircle _shape = new(20); + private static readonly AOEShapeCircle _shape = new(20f); public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - var locs = (AID)spell.Action.ID switch + var locs = spell.Action.ID switch { - AID.BrokenShardsE => _eastLocations, - AID.BrokenShardsW => _westLocations, + (uint)AID.BrokenShardsE => _eastLocations, + (uint)AID.BrokenShardsW => _westLocations, _ => null }; if (locs != null) for (var i = 0; i < 9; ++i) - _aoes.Add(new(_shape, locs[i], default, Module.CastFinishAt(spell))); + _aoes.Add(new(_shape, WPos.ClampToGrid(locs[i]), default, Module.CastFinishAt(spell))); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.BrokenShardsAOE) + if (spell.Action.ID == (uint)AID.BrokenShardsAOE) { ++NumCasts; - for (var i = 0; i < _aoes.Count; ++i) + var count = _aoes.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - var aoe = _aoes[i]; - if (aoe.Origin.AlmostEqual(caster.Position, 0.1f)) + if (_aoes[i].Origin.AlmostEqual(pos, 0.1f)) { - _aoes.Remove(aoe); + _aoes.RemoveAt(i); break; } } diff --git a/BossMod/Modules/Endwalker/Alliance/A13Azeyma/SolarFans.cs b/BossMod/Modules/Endwalker/Alliance/A13Azeyma/SolarFans.cs index 84de98fcef..5bb9906186 100644 --- a/BossMod/Modules/Endwalker/Alliance/A13Azeyma/SolarFans.cs +++ b/BossMod/Modules/Endwalker/Alliance/A13Azeyma/SolarFans.cs @@ -1,75 +1,71 @@ namespace BossMod.Endwalker.Alliance.A13Azeyma; -class SolarFans(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.SolarFansAOE), 5); +class SolarFans(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.SolarFansAOE), 5f); class RadiantRhythm(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.RadiantFlight)) { - private static readonly AOEShapeDonutSector _shape = new(20, 30, 45.Degrees()); - private readonly List _aoes = []; - private readonly List _aoes2 = []; + private static readonly AOEShapeDonutSector _shape = new(20f, 30f, 45f.Degrees()); + private readonly List _aoes = new(10); public override IEnumerable ActiveAOEs(int slot, Actor actor) { - if (_aoes.Count > 0) + var count = _aoes.Count; + if (count == 0) + return []; + var max = count > 4 ? 4 : count; + var aoes = new AOEInstance[max]; + for (var i = 0; i < max; ++i) { - yield return _aoes[0] with { Color = Colors.Danger }; - yield return _aoes2[0] with { Color = Colors.Danger }; - } - if (_aoes.Count > 1) - { - yield return _aoes[1]; - yield return _aoes2[1]; + var aoe = _aoes[i]; + if (i < 2) + aoes[i] = count > 2 ? aoe with { Color = Colors.Danger } : aoe; + else + aoes[i] = aoe; } + return aoes; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - var _activation = Module.CastFinishAt(spell, 7.7f); - if ((AID)spell.Action.ID == AID.SolarFansCharge) //since it seems impossible to determine early enough if 5 or 6 casts happen, we draw one extra one just incase + if (_aoes.Count == 0 && spell.Action.ID == (uint)AID.SolarFansCharge) // since it seems impossible to determine early enough if 4 or 5 casts happen, we draw one extra one just incase { - if (spell.LocXZ.AlmostEqual(new(-775, -750), 10)) - for (var i = 1; i < 6; ++i) - { - _aoes.Add(new(_shape, Arena.Center, (225 + i * 90).Degrees(), _activation.AddSeconds(1.3f * (i - 1)))); - _aoes2.Add(new(_shape, Arena.Center, (45 + i * 90).Degrees(), _activation.AddSeconds(1.3f * (i - 1)))); - } - else if (spell.LocXZ.AlmostEqual(new(-750, -775), 1)) - for (var i = 1; i < 6; ++i) - { - _aoes.Add(new(_shape, Arena.Center, (135 + i * 90).Degrees(), _activation.AddSeconds(1.3f * (i - 1)))); - _aoes2.Add(new(_shape, Arena.Center, (-45 + i * 90).Degrees(), _activation.AddSeconds(1.3f * (i - 1)))); - } + var activation = Module.CastFinishAt(spell, 7.7f); + var pattern1 = false; + if ((int)spell.LocXZ.Z == -750f) + pattern1 = true; + for (var i = 1; i < 6; ++i) + { + var act = activation.AddSeconds(1.3d * (i - 1)); + var angle = ((pattern1 ? 225f : 135f) + i * 90f).Degrees(); + AddAOE(angle, act); + AddAOE(angle + 180f.Degrees(), act); + } } + void AddAOE(Angle rotation, DateTime activation) => _aoes.Add(new(_shape, WPos.ClampToGrid(Arena.Center), rotation, activation)); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.RadiantFinish) - { + if (spell.Action.ID == (uint)AID.RadiantFinish) _aoes.Clear(); - _aoes2.Clear(); - } - else if (_aoes.Count > 0 && (AID)spell.Action.ID == AID.RadiantFlight && ++NumCasts % 2 == 0) - { + else if (_aoes.Count != 0 && spell.Action.ID == (uint)AID.RadiantFlight) _aoes.RemoveAt(0); - _aoes2.RemoveAt(0); - } } } class RadiantFlourish(BossModule module) : Components.GenericAOEs(module) { private int teleportcounter; - private static readonly AOEShapeCircle circle = new(25); - private readonly List _aoes = []; + private static readonly AOEShapeCircle circle = new(25f); + private readonly List _aoes = new(2); public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes; public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.SolarFansAOE) - _aoes.Add(new(circle, spell.LocXZ, default, WorldState.FutureTime(16.6f))); - else if ((AID)spell.Action.ID == AID.RadiantFlourish) + if (spell.Action.ID == (uint)AID.SolarFansAOE) + _aoes.Add(new(circle, spell.LocXZ, default, WorldState.FutureTime(16.6d))); + else if (spell.Action.ID == (uint)AID.RadiantFlourish) { _aoes.Clear(); teleportcounter = 0; @@ -78,14 +74,18 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.TeleportFlame) //correct circle location if Flight happens 10 times instead of 8 times, ugly hack but i couldn't find a better difference in logs + if (spell.Action.ID == (uint)AID.TeleportFlame) // correct circle location if Flight happens 10 times instead of 8 times, ugly hack but i couldn't find a better difference in logs { if (++teleportcounter > 8) { teleportcounter = 0; - _aoes.Add(new(circle, WPos.RotateAroundOrigin(90, Arena.Center, _aoes[0].Origin), default, _aoes[0].Activation.AddSeconds(1.4f))); - _aoes.Add(new(circle, WPos.RotateAroundOrigin(90, Arena.Center, _aoes[1].Origin), default, _aoes[1].Activation.AddSeconds(1.4f))); + var aoe0 = _aoes[0]; + var activation = aoe0.Activation.AddSeconds(1.4d); + AddAOE(aoe0.Origin); + AddAOE(_aoes[1].Origin); _aoes.RemoveRange(0, 2); + + void AddAOE(WPos origin) => _aoes.Add(new(circle, WPos.ClampToGrid(WPos.RotateAroundOrigin(90f, Arena.Center, origin)), default, activation)); } } } diff --git a/BossMod/Modules/Endwalker/Alliance/A14Naldthal/FarAboveDeepBelow.cs b/BossMod/Modules/Endwalker/Alliance/A14Naldthal/FarAboveDeepBelow.cs index 27e98fa32b..3829da95ea 100644 --- a/BossMod/Modules/Endwalker/Alliance/A14Naldthal/FarAboveDeepBelow.cs +++ b/BossMod/Modules/Endwalker/Alliance/A14Naldthal/FarAboveDeepBelow.cs @@ -1,14 +1,14 @@ namespace BossMod.Endwalker.Alliance.A14Naldthal; // TODO: create and use generic 'line stack' component -class FarFlungFire(BossModule module) : Components.GenericWildCharge(module, 3, fixedLength: 40) +class FarFlungFire(BossModule module) : Components.GenericWildCharge(module, 3f, fixedLength: 40f) { private bool _real; private ulong _targetID; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.FarAboveDeepBelowNald or AID.HearthAboveFlightBelowNald or AID.HearthAboveFlightBelowThalNald) + if (spell.Action.ID is (uint)AID.FarAboveDeepBelowNald or (uint)AID.HearthAboveFlightBelowNald or (uint)AID.HearthAboveFlightBelowThalNald) { _real = true; InitIfReal(); @@ -17,14 +17,14 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.FarFlungFireVisual: + case (uint)AID.FarFlungFireVisual: Source = caster; _targetID = spell.MainTargetID; InitIfReal(); break; - case AID.FarFlungFireAOE: + case (uint)AID.FarFlungFireAOE: ++NumCasts; _real = false; Source = null; @@ -44,13 +44,13 @@ class DeepestPit(BossModule module) : Components.GenericAOEs(module) { private bool _real; private readonly List _targets = []; - private readonly List _casters = []; + private readonly List _aoes = []; - public bool Active => _casters.Count != 0; + public bool Active => _aoes.Count != 0; - private static readonly AOEShapeCircle _shape = new(6); + private static readonly AOEShapeCircle _shape = new(6f); - public override IEnumerable ActiveAOEs(int slot, Actor actor) => _casters.Select(c => new AOEInstance(_shape, c.CastInfo!.LocXZ, default, Module.CastFinishAt(c.CastInfo))); + public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes; public override PlayerPriority CalcPriority(int pcSlot, Actor pc, int playerSlot, Actor player, ref uint customColor) => _real && _targets.Contains(player) ? PlayerPriority.Danger : PlayerPriority.Irrelevant; @@ -58,38 +58,46 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) { if (_real) foreach (var t in _targets) - Arena.AddCircle(t.Position, _shape.Radius, Colors.Danger); + Arena.AddCircle(t.Position, _shape.Radius); } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.FarAboveDeepBelowThal: - case AID.HearthAboveFlightBelowThal: - case AID.HearthAboveFlightBelowNaldThal: + case (uint)AID.FarAboveDeepBelowThal: + case (uint)AID.HearthAboveFlightBelowThal: + case (uint)AID.HearthAboveFlightBelowNaldThal: _real = true; break; - case AID.DeepestPitFirst: - case AID.DeepestPitRest: - _casters.Add(caster); + case (uint)AID.DeepestPitFirst: + case (uint)AID.DeepestPitRest: + _aoes.Add(new(_shape, spell.LocXZ, default, Module.CastFinishAt(spell), ActorID: caster.InstanceID)); break; } } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.DeepestPitFirst or AID.DeepestPitRest) + if (spell.Action.ID is (uint)AID.DeepestPitFirst or (uint)AID.DeepestPitRest) { - _casters.Remove(caster); - if (_casters.Count == 0) + var count = _aoes.Count; + for (var i = 0; i < count; ++i) + { + if (_aoes[i].ActorID == caster.InstanceID) + { + _aoes.RemoveAt(i); + break; + } + } + if (count == 0) _targets.Clear(); } } public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { - if ((IconID)iconID == IconID.DeepestPitTarget) + if (iconID == (uint)IconID.DeepestPitTarget) { _targets.Add(actor); } diff --git a/BossMod/Modules/Endwalker/Alliance/A14Naldthal/OnceAboveEverBelow.cs b/BossMod/Modules/Endwalker/Alliance/A14Naldthal/OnceAboveEverBelow.cs index 96f3506e30..9b7ffb80b0 100644 --- a/BossMod/Modules/Endwalker/Alliance/A14Naldthal/OnceAboveEverBelow.cs +++ b/BossMod/Modules/Endwalker/Alliance/A14Naldthal/OnceAboveEverBelow.cs @@ -1,51 +1,56 @@ namespace BossMod.Endwalker.Alliance.A14Naldthal; -class OnceAboveEverBelow(BossModule module) : Components.Exaflare(module, 6) +class OnceAboveEverBelow(BossModule module) : Components.Exaflare(module, 6f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.EverfireFirst or AID.OnceBurnedFirst) + if (spell.Action.ID is (uint)AID.EverfireFirst or (uint)AID.OnceBurnedFirst) { - var pos = spell.LocXZ; - var advance = 6 * spell.Rotation.ToDirection(); - var activation = Module.CastFinishAt(spell); + var pos = caster.Position; + var advance = 6f * spell.Rotation.ToDirection(); // lines are offset by 6/18/30; outer have 1 explosion only, mid have 4 or 5, inner 5 - var numExplosions = (pos - Arena.Center).LengthSq() > 500 ? 1 : 5; - Lines.Add(new() { Next = pos, Advance = advance, NextExplosion = activation, TimeToMove = 1.5f, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); - Lines.Add(new() { Next = pos, Advance = -advance, NextExplosion = activation, TimeToMove = 1.5f, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); + var numExplosions = (pos - Arena.Center).LengthSq() > 500f ? 1 : 5; + AddLine(advance); + AddLine(-advance); + + void AddLine(WDir dir) + => Lines.Add(new() { Next = pos, Advance = dir, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.5f, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.EverfireFirst: - case AID.OnceBurnedFirst: + case (uint)AID.EverfireFirst: + case (uint)AID.OnceBurnedFirst: var dir = caster.Rotation.ToDirection(); - Advance(caster.Position, dir); - Advance(caster.Position, -dir); + Advance(dir); + Advance(-dir); ++NumCasts; break; - case AID.EverfireRest: - case AID.OnceBurnedRest: - Advance(caster.Position, caster.Rotation.ToDirection()); + case (uint)AID.EverfireRest: + case (uint)AID.OnceBurnedRest: + Advance(caster.Rotation.ToDirection()); ++NumCasts; break; } - } - - private void Advance(WPos position, WDir dir) - { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(position, 1) && item.Advance.Dot(dir) > 5); - if (index == -1) + void Advance(WDir dir) { - ReportError($"Failed to find entry for {position} / {dir}"); - return; + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f) && line.Advance.Dot(dir) > 5f) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } + ReportError($"Failed to find entry for {caster.InstanceID:X}, {pos} / {dir}"); } - - AdvanceLine(Lines[index], position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); } } diff --git a/BossMod/Modules/Endwalker/Alliance/A21Nophica/SowingCircle.cs b/BossMod/Modules/Endwalker/Alliance/A21Nophica/SowingCircle.cs index e410064188..bbd5bdd38e 100644 --- a/BossMod/Modules/Endwalker/Alliance/A21Nophica/SowingCircle.cs +++ b/BossMod/Modules/Endwalker/Alliance/A21Nophica/SowingCircle.cs @@ -1,30 +1,34 @@ namespace BossMod.Endwalker.Alliance.A21Nophica; -class SowingCircle(BossModule module) : Components.Exaflare(module, 5) +class SowingCircle(BossModule module) : Components.Exaflare(module, 5f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.SowingCircleFirst) + if (spell.Action.ID == (uint)AID.SowingCircleFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 5 * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 10, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = spell.LocXZ, Advance = 5f * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 10, MaxShownExplosions = 2 }); } } - public override void OnEventCast(Actor caster, ActorCastEvent spell) + public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.SowingCircleFirst or AID.SowingCircleRest) + if (spell.Action.ID is (uint)AID.SowingCircleFirst or (uint)AID.SowingCircleRest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(spell.TargetXZ, 1)); - if (index == -1) + var count = Lines.Count; + var pos = spell.LocXZ; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], spell.TargetXZ); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Rheognosis.cs b/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Rheognosis.cs index 897fd01c18..37e7e20cd9 100644 --- a/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Rheognosis.cs +++ b/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Rheognosis.cs @@ -24,7 +24,7 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) public class RheognosisCrash : Components.Exaflare { - public RheognosisCrash(BossModule module) : base(module, new AOEShapeRect(10f, 12f), ActionID.MakeSpell(AID.RheognosisCrash)) => ImminentColor = Colors.AOE; + public RheognosisCrash(BossModule module) : base(module, new AOEShapeRect(10f, 12f)) => ImminentColor = Colors.AOE; public override void OnEventEnvControl(byte index, uint state) { @@ -41,16 +41,23 @@ public override void OnEventEnvControl(byte index, uint state) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (spell.Action == WatchedAction) + if (spell.Action.ID == (uint)AID.RheognosisCrash) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index >= 0) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Tetraktys.cs b/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Tetraktys.cs index 5d9db334b0..0b06999629 100644 --- a/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Tetraktys.cs +++ b/BossMod/Modules/Endwalker/Alliance/A31Thaliak/Tetraktys.cs @@ -57,13 +57,11 @@ public override void OnEventEnvControl(byte index, uint state) // 5 E // 678 F 10 // 9ABCD - void AddAOEs(AOEShapeTriCone shape, ReadOnlySpan positions, ReadOnlySpan rotations) + void AddAOEs(WPos[] positions, Angle[] rotations) { - for (var i = 0; i < positions.Length; ++i) + for (var i = 0; i < 3; ++i) { - var pos = positions[i]; - var rot = rotations[i]; - AddAOE(shape, pos, rot); + AddAOE(_triSmall, positions[i], rotations[i]); } } void AddAOE(AOEShapeTriCone shape, WPos pos, Angle rot) => AOEs.Add(new(shape, WPos.ClampToGrid(pos), rot, WorldState.FutureTime(3.8d))); @@ -73,13 +71,13 @@ void AddAOEs(AOEShapeTriCone shape, ReadOnlySpan positions, ReadOnlySpan m.Enemies(OID.SeaFoam).Where(x => !x.IsDead)); +class SurgingWaveSeaFoam(BossModule module) : Components.PersistentVoidzone(module, 1.5f, GetVoidzones) +{ + private static Actor[] GetVoidzones(BossModule module) + { + var enemies = module.Enemies((uint)OID.SeaFoam); + var count = enemies.Count; + if (count == 0) + return []; + + var voidzones = new Actor[count]; + var index = 0; + for (var i = 0; i < count; ++i) + { + var z = enemies[i]; + if (!z.IsDead) + voidzones[index++] = z; + } + return voidzones[..index]; + } +} public class SurgingWaveFrothingSea : Components.Exaflare { @@ -30,18 +49,16 @@ public class SurgingWaveFrothingSea : Components.Exaflare FutureColor = Colors.Danger; } - private static readonly Angle _rot1 = 90.Degrees(); - private static readonly Angle _rot2 = -90.Degrees(); - public override void OnEventEnvControl(byte index, uint state) { - var _activation = WorldState.FutureTime(30d); + void AddLine(WPos first, Angle rot) + => Lines.Add(new() { Next = first, Advance = 2.3f * rot.ToDirection(), NextExplosion = WorldState.FutureTime(30d), TimeToMove = 0.9f, ExplosionsLeft = 13, MaxShownExplosions = 2, Rotation = rot }); if (index == 0x49) { if (state == 0x00800040) - Lines.Add(new() { Next = new(-80, -900), Advance = 2.3f * _rot1.ToDirection(), NextExplosion = _activation, TimeToMove = 0.9f, ExplosionsLeft = 13, MaxShownExplosions = 2, Rotation = _rot1 }); - if (state == 0x08000400) - Lines.Add(new() { Next = new(80, -900), Advance = 2.3f * _rot2.ToDirection(), NextExplosion = _activation, TimeToMove = 0.9f, ExplosionsLeft = 13, MaxShownExplosions = 2, Rotation = _rot2 }); + AddLine(new(-80f, -900f), 90f.Degrees()); + else if (state == 0x08000400) + AddLine(new(80f, -900f), -90f.Degrees()); } } @@ -52,8 +69,9 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) ++NumCasts; if (Lines.Count != 0) { - AdvanceLine(Lines[0], Lines[0].Next + 2.3f * Lines[0].Rotation.ToDirection()); - if (Lines[0].ExplosionsLeft == 0) + var line = Lines[0]; + AdvanceLine(line, line.Next + 2.3f * line.Rotation.ToDirection()); + if (line.ExplosionsLeft == 0) Lines.RemoveAt(0); } } diff --git a/BossMod/Modules/Endwalker/Alliance/A33Oschon/P2ArrowTrail.cs b/BossMod/Modules/Endwalker/Alliance/A33Oschon/P2ArrowTrail.cs index 22d1977ca1..c9095c7e50 100644 --- a/BossMod/Modules/Endwalker/Alliance/A33Oschon/P2ArrowTrail.cs +++ b/BossMod/Modules/Endwalker/Alliance/A33Oschon/P2ArrowTrail.cs @@ -5,7 +5,7 @@ namespace BossMod.Endwalker.Alliance.A33Oschon; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID == (uint)AID.ArrowTrailHint) - Lines.Add(new() { Next = caster.Position, Advance = 5 * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell, 0.4f), TimeToMove = 0.5f, ExplosionsLeft = 8, MaxShownExplosions = 3 }); + Lines.Add(new() { Next = caster.Position, Advance = 5f * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell, 0.4f), TimeToMove = 0.5f, ExplosionsLeft = 8, MaxShownExplosions = 3 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) @@ -13,12 +13,18 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (spell.Action.ID == (uint)AID.ArrowTrailAOE) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index >= 0) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } } } diff --git a/BossMod/Modules/Endwalker/Alliance/A34Eulogia/AsAboveSoBelow.cs b/BossMod/Modules/Endwalker/Alliance/A34Eulogia/AsAboveSoBelow.cs index ecd0f2bd3a..93e4fa5124 100644 --- a/BossMod/Modules/Endwalker/Alliance/A34Eulogia/AsAboveSoBelow.cs +++ b/BossMod/Modules/Endwalker/Alliance/A34Eulogia/AsAboveSoBelow.cs @@ -6,13 +6,15 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID is (uint)AID.EverfireFirst or (uint)AID.OnceBurnedFirst) { - var advance = 6 * spell.Rotation.ToDirection(); + var advance = 6f * spell.Rotation.ToDirection(); var pos = caster.Position; - var activation = Module.CastFinishAt(spell); // outer lines have 4 explosion only, rest 5 var numExplosions = (pos - Arena.Center).LengthSq() > 500f ? 4 : 6; - Lines.Add(new() { Next = pos, Advance = advance, NextExplosion = activation, TimeToMove = 1.5f, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); - Lines.Add(new() { Next = pos, Advance = -advance, NextExplosion = activation, TimeToMove = 1.5f, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); + AddLine(advance); + AddLine(-advance); + + void AddLine(WDir dir) + => Lines.Add(new() { Next = pos, Advance = dir, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.5f, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); } } @@ -23,29 +25,32 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) case (uint)AID.EverfireFirst: case (uint)AID.OnceBurnedFirst: var dir = caster.Rotation.ToDirection(); - Advance(caster.Position, dir); - Advance(caster.Position, -dir); + Advance(dir); + Advance(-dir); ++NumCasts; break; case (uint)AID.EverfireRest: case (uint)AID.OnceBurnedRest: - Advance(caster.Position, caster.Rotation.ToDirection()); + Advance(caster.Rotation.ToDirection()); ++NumCasts; break; } - } - - private void Advance(WPos position, WDir dir) - { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(position, 1f) && item.Advance.Dot(dir) > 5f); - if (index == -1) + void Advance(WDir dir) { - ReportError($"Failed to find entry for {position} / {dir}"); - return; + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f) && line.Advance.Dot(dir) > 5f) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } + ReportError($"Failed to find entry for {caster.InstanceID:X}, {pos} / {dir}"); } - - AdvanceLine(Lines[index], position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); } } diff --git a/BossMod/Modules/Endwalker/Alliance/A34Eulogia/SolarFans.cs b/BossMod/Modules/Endwalker/Alliance/A34Eulogia/SolarFans.cs index c73d917de7..f29323c80a 100644 --- a/BossMod/Modules/Endwalker/Alliance/A34Eulogia/SolarFans.cs +++ b/BossMod/Modules/Endwalker/Alliance/A34Eulogia/SolarFans.cs @@ -4,28 +4,23 @@ class SolarFans(BossModule module) : Components.ChargeAOEs(module, ActionID.Make class RadiantRhythm(BossModule module) : Components.GenericAOEs(module) { - private Angle _nextAngle; - private DateTime _activation; + private readonly List _aoes = new(8); private static readonly AOEShapeDonutSector _shape = new(20f, 30f, 45f.Degrees()); public override IEnumerable ActiveAOEs(int slot, Actor actor) { - if (_activation == default) + var count = _aoes.Count; + if (count == 0) return []; - - var aoes = new AOEInstance[2]; - var center = Arena.Center; - // assumption: we always have 4 moves - if (NumCasts < 8) - { - aoes[0] = new(_shape, center, _nextAngle, _activation, Colors.Danger); - aoes[1] = new(_shape, center, _nextAngle + 180f.Degrees(), _activation, Colors.Danger); - } - if (NumCasts < 6) + var max = count > 4 ? 4 : count; + var aoes = new AOEInstance[max]; + for (var i = 0; i < max; ++i) { - var future = _activation.AddSeconds(2.1d); - aoes[0] = new(_shape, center, _nextAngle + 90f.Degrees(), future); - aoes[1] = new(_shape, center, _nextAngle - 90f.Degrees(), future); + var aoe = _aoes[i]; + if (i < 2) + aoes[i] = count > 2 ? aoe with { Color = Colors.Danger } : aoe; + else + aoes[i] = aoe; } return aoes; } @@ -35,10 +30,19 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) if (spell.Action.ID == (uint)AID.SolarFansAOE) { // assumption: flames always move CCW - var startingAngle = Angle.FromDirection(spell.LocXZ - Arena.Center); + var pattern1 = false; + if ((int)spell.LocXZ.Z == -945f) + pattern1 = true; NumCasts = 0; - _nextAngle = startingAngle + 45f.Degrees(); - _activation = Module.CastFinishAt(spell, 2.8f); + var activation = Module.CastFinishAt(spell, 2.8f); + for (var i = 1; i < 5; ++i) + { + var act = activation.AddSeconds(2.1d * (i - 1)); + var angle = ((pattern1 ? 225f : 135f) + i * 90f).Degrees(); + AddAOE(angle, act); + AddAOE(angle + 180f.Degrees(), act); + } + void AddAOE(Angle rotation, DateTime activation) => _aoes.Add(new(_shape, WPos.ClampToGrid(Arena.Center), rotation, activation)); } } @@ -47,16 +51,8 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) if (spell.Action.ID == (uint)AID.RadiantFlight) { ++NumCasts; - if (NumCasts == 8) - { - _nextAngle = default; - _activation = default; - } - else if ((NumCasts & 1) == 0) - { - _nextAngle += 90f.Degrees(); - _activation = WorldState.FutureTime(2.1d); - } + if (_aoes.Count != 0) + _aoes.RemoveAt(0); } } } diff --git a/BossMod/Modules/Endwalker/Criterion/C01ASS/C011Silkie/EasternEwers.cs b/BossMod/Modules/Endwalker/Criterion/C01ASS/C011Silkie/EasternEwers.cs index a2025eafa7..425264818e 100644 --- a/BossMod/Modules/Endwalker/Criterion/C01ASS/C011Silkie/EasternEwers.cs +++ b/BossMod/Modules/Endwalker/Criterion/C01ASS/C011Silkie/EasternEwers.cs @@ -6,7 +6,7 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID is (uint)AID.NBrimOver or (uint)AID.SBrimOver) { - Lines.Add(new() { Next = spell.LocXZ, Advance = new(0, 5.1f), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 0.8f, ExplosionsLeft = 11, MaxShownExplosions = int.MaxValue }); + Lines.Add(new() { Next = caster.Position, Advance = new(default, 5.1f), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 0.8f, ExplosionsLeft = 11, MaxShownExplosions = int.MaxValue }); } } @@ -14,16 +14,20 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) { if (spell.Action.ID is (uint)AID.NBrimOver or (uint)AID.SBrimOver or (uint)AID.NRinse or (uint)AID.SRinse) { - var index = Lines.FindIndex(item => Math.Abs(item.Next.X - caster.Position.X) < 1f); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Criterion/C02AMR/C022Gorai/CloudToGround.cs b/BossMod/Modules/Endwalker/Criterion/C02AMR/C022Gorai/CloudToGround.cs index 4a40b523ed..1284d9d4ba 100644 --- a/BossMod/Modules/Endwalker/Criterion/C02AMR/C022Gorai/CloudToGround.cs +++ b/BossMod/Modules/Endwalker/Criterion/C02AMR/C022Gorai/CloudToGround.cs @@ -1,13 +1,13 @@ namespace BossMod.Endwalker.VariantCriterion.C02AMR.C022Gorai; -class CloudToGround(BossModule module) : Components.Exaflare(module, 6) +class CloudToGround(BossModule module) : Components.Exaflare(module, 6f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID is (uint)AID.NCloudToGroundAOEFirst or (uint)AID.SCloudToGroundAOEFirst) { // 4 central exaflares (+-6 along one axis, 0 along other) have 3 casts, 4 side exaflares (+-20 along one axis, +-5/15 along other) have 7 casts - Lines.Add(new() { Next = spell.LocXZ, Advance = 6 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = (caster.Position - Module.Center).LengthSq() > 100 ? 7 : 3, MaxShownExplosions = 3 }); + Lines.Add(new() { Next = caster.Position, Advance = 6f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = (caster.Position - Module.Center).LengthSq() > 100 ? 7 : 3, MaxShownExplosions = 3 }); } } @@ -16,16 +16,20 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if (spell.Action.ID is (uint)AID.NCloudToGroundAOEFirst or (uint)AID.SCloudToGroundAOEFirst or (uint)AID.NCloudToGroundAOERest or (uint)AID.SCloudToGroundAOERest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Dungeon/D06DeadEnds/D061CausticGrebuloff.cs b/BossMod/Modules/Endwalker/Dungeon/D06DeadEnds/D061CausticGrebuloff.cs index c34324dd2c..e7645ad652 100644 --- a/BossMod/Modules/Endwalker/Dungeon/D06DeadEnds/D061CausticGrebuloff.cs +++ b/BossMod/Modules/Endwalker/Dungeon/D06DeadEnds/D061CausticGrebuloff.cs @@ -120,7 +120,7 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) Pattern.Northward => GetNorthwardExplosions(caster.Position, Arena.Center), _ => 0 }; - var advance = 6 * (CurrentWind == Pattern.Southward ? new WDir(0f, 1f) : new(0f, -1f)); + var advance = 6 * (CurrentWind == Pattern.Southward ? new WDir(default, 1f) : new(default, -1f)); Lines.Add(new() { Next = caster.Position, Advance = advance, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = numExplosions, MaxShownExplosions = 5 }); } } @@ -154,19 +154,22 @@ private static int GetNorthwardExplosions(WPos position, WPos center) public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (spell.Action.ID is (uint)AID.NecroticFluid or (uint)AID.NecroticMist) - Advance(spell.LocXZ); - } - - private void Advance(WPos position) - { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(position, 1f)); - if (index != -1) { - AdvanceLine(Lines[index], position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); - if (Lines.Count == 0) - CurrentWind = Pattern.None; + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + if (Lines.Count == 0) + CurrentWind = Pattern.None; + return; + } + } } } diff --git a/BossMod/Modules/Endwalker/Dungeon/D12Aetherfont/D121Lyngbakr.cs b/BossMod/Modules/Endwalker/Dungeon/D12Aetherfont/D121Lyngbakr.cs index 578d6cd8fc..5eea8e0fbd 100644 --- a/BossMod/Modules/Endwalker/Dungeon/D12Aetherfont/D121Lyngbakr.cs +++ b/BossMod/Modules/Endwalker/Dungeon/D12Aetherfont/D121Lyngbakr.cs @@ -25,8 +25,8 @@ public enum AID : uint class ExplosiveResonantFrequency(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeCircle circleSmall = new(8), circleBig = new(15); - private readonly List _aoes = []; + private static readonly AOEShapeCircle circleSmall = new(8f), circleBig = new(15f); + private readonly List _aoes = new(11); public override IEnumerable ActiveAOEs(int slot, Actor actor) { @@ -37,7 +37,7 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) for (var i = 0; i < count; ++i) { var aoe = _aoes[i]; - if ((aoe.Activation - _aoes[0].Activation).TotalSeconds <= 1) + if ((aoe.Activation - _aoes[0].Activation).TotalSeconds <= 1d) aoes.Add(aoe); } return aoes; @@ -45,22 +45,22 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.ResonantFrequency) - _aoes.Add(new(circleSmall, spell.LocXZ, default, Module.CastFinishAt(spell), ActorID: caster.InstanceID)); - else if ((AID)spell.Action.ID == AID.ExplosiveFrequency) - _aoes.Add(new(circleBig, spell.LocXZ, default, Module.CastFinishAt(spell), ActorID: caster.InstanceID)); + void AddAOE(AOEShapeCircle shape) => _aoes.Add(new(shape, spell.LocXZ, default, Module.CastFinishAt(spell), ActorID: caster.InstanceID)); + if (spell.Action.ID == (uint)AID.ResonantFrequency) + AddAOE(circleSmall); + else if (spell.Action.ID == (uint)AID.ExplosiveFrequency) + AddAOE(circleBig); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.ResonantFrequency or AID.ExplosiveFrequency) + if (spell.Action.ID is (uint)AID.ResonantFrequency or (uint)AID.ExplosiveFrequency) { for (var i = 0; i < _aoes.Count; ++i) { - var aoe = _aoes[i]; - if (aoe.ActorID == caster.InstanceID) + if (_aoes[i].ActorID == caster.InstanceID) { - _aoes.Remove(aoe); + _aoes.RemoveAt(i); break; } } @@ -69,9 +69,9 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) } class SonicBloop(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.SonicBloop)); -class Waterspout(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.Waterspout), 5); -class TidalBreath(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TidalBreath), new AOEShapeCone(40, 90.Degrees())); -class Tidalspout(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Tidalspout), 6, 4, 4); +class Waterspout(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.Waterspout), 5f); +class TidalBreath(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TidalBreath), new AOEShapeCone(40f, 90f.Degrees())); +class Tidalspout(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Tidalspout), 6f, 4, 4); class Upsweep(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Upsweep)); class BodySlam(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.BodySlam)); @@ -93,6 +93,6 @@ public D121LyngbakrStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "dhoggpt, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 822, NameID = 12336, SortOrder = 3)] public class D121Lyngbakr(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly ArenaBoundsComplex arena = new([new Polygon(new(-322, 120), 19.5f * CosPI.Pi40th, 48)], [new Rectangle(new(-322, 99), 20, 2.25f), - new Rectangle(new(-322, 140), 20, 1.25f)]); + private static readonly ArenaBoundsComplex arena = new([new Polygon(new(-322f, 120f), 19.5f * CosPI.Pi40th, 48)], [new Rectangle(new(-322f, 99f), 20f, 2.25f), + new Rectangle(new(-322f, 140f), 20f, 1.25f)]); } diff --git a/BossMod/Modules/Endwalker/Quest/MSQ/AnUnforeseenBargain/P2Andromalius.cs b/BossMod/Modules/Endwalker/Quest/MSQ/AnUnforeseenBargain/P2Andromalius.cs index fa8e60fd51..ab3e0e68d8 100644 --- a/BossMod/Modules/Endwalker/Quest/MSQ/AnUnforeseenBargain/P2Andromalius.cs +++ b/BossMod/Modules/Endwalker/Quest/MSQ/AnUnforeseenBargain/P2Andromalius.cs @@ -72,16 +72,16 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) if (count == 0) return []; var max = count > 3 ? 3 : count; - List aoes = new(max); + var aoes = new AOEInstance[max]; for (var i = 0; i < max; ++i) { - aoes.Add(_aoes[i]); + aoes[i] = _aoes[i]; } return aoes; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.StraightSpindleFast or AID.StraightSpindleSlow or AID.StraightSpindleAdds) + if (spell.Action.ID is (uint)AID.StraightSpindleFast or (uint)AID.StraightSpindleSlow or (uint)AID.StraightSpindleAdds) { _aoes.Add(new(rect, spell.LocXZ, spell.Rotation, Module.CastFinishAt(spell), ActorID: caster.InstanceID)); if (_aoes.Count == 6) @@ -91,13 +91,12 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.StraightSpindleFast or AID.StraightSpindleSlow or AID.StraightSpindleAdds) + if (spell.Action.ID is (uint)AID.StraightSpindleFast or (uint)AID.StraightSpindleSlow or (uint)AID.StraightSpindleAdds) for (var i = 0; i < _aoes.Count; ++i) { - var aoe = _aoes[i]; - if (aoe.ActorID == caster.InstanceID) + if (_aoes[i].ActorID == caster.InstanceID) { - _aoes.Remove(aoe); + _aoes.RemoveAt(i); break; } } @@ -108,7 +107,8 @@ class Decay(BossModule module) : Components.CastHint(module, ActionID.MakeSpell( { public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - for (var i = 0; i < hints.PotentialTargets.Count; ++i) + var count = hints.PotentialTargets.Count; + for (var i = 0; i < count; ++i) { var e = hints.PotentialTargets[i]; if (e.Actor.CastInfo?.Action == WatchedAction) @@ -119,7 +119,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme class Shield(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeCircle circle = new(5, true); + private static readonly AOEShapeCircle circle = new(5f, true); private AOEInstance? _aoe; private const string Hint = "Go under shield!"; @@ -127,7 +127,7 @@ class Shield(BossModule module) : Components.GenericAOEs(module) public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.AlphinaudShield) + if (actor.OID == (uint)OID.AlphinaudShield) _aoe = new(circle, actor.Position, Color: Colors.SafeFromAOE); } @@ -135,15 +135,15 @@ public override void AddHints(int slot, Actor actor, TextHints hints) { if (_aoe == null) return; - if (!_aoe.Value.Check(actor.Position)) - hints.Add(Hint); - else - hints.Add(Hint, false); + var check = true; + if (_aoe.Value.Check(actor.Position)) + check = false; + hints.Add(Hint, check); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.VoidEvocation) + if (spell.Action.ID == (uint)AID.VoidEvocation) _aoe = null; } } @@ -152,7 +152,7 @@ class ProtectZero(BossModule module) : BossComponent(module) { public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - var zeros = Module.Enemies(OID.Zero); + var zeros = Module.Enemies((uint)OID.Zero); Actor? zero = null; for (var i = 0; i < zeros.Count; ++i) { @@ -165,7 +165,8 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme } if (zero != null) { - for (var i = 0; i < hints.PotentialTargets.Count; ++i) + var count = hints.PotentialTargets.Count; + for (var i = 0; i < count; ++i) { var e = hints.PotentialTargets[i]; if (e.Actor.TargetID == zero.InstanceID) diff --git a/BossMod/Modules/Endwalker/Quest/MSQ/Endwalker/Exaflare.cs b/BossMod/Modules/Endwalker/Quest/MSQ/Endwalker/Exaflare.cs index f30eb2a472..b93fe39589 100644 --- a/BossMod/Modules/Endwalker/Quest/MSQ/Endwalker/Exaflare.cs +++ b/BossMod/Modules/Endwalker/Quest/MSQ/Endwalker/Exaflare.cs @@ -4,27 +4,30 @@ class Exaflare(BossModule module) : Components.Exaflare(module, 6) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.ExaflareFirstHit) + if (spell.Action.ID == (uint)AID.ExaflareFirstHit) { - Lines.Add(new() { Next = caster.Position, Advance = 8 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.1f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = caster.Position, Advance = 8f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.1f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.ExaflareFirstHit or AID.ExaflareRest) + if (spell.Action.ID is (uint)AID.ExaflareFirstHit or (uint)AID.ExaflareRest) { - ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Quest/MSQ/WorthyOfHisBack.cs b/BossMod/Modules/Endwalker/Quest/MSQ/WorthyOfHisBack.cs index 368f1b6e56..c5ec4db822 100644 --- a/BossMod/Modules/Endwalker/Quest/MSQ/WorthyOfHisBack.cs +++ b/BossMod/Modules/Endwalker/Quest/MSQ/WorthyOfHisBack.cs @@ -78,162 +78,202 @@ public enum IconID : uint RotateCCW = 168 // Boss } +class ArenaChange(BossModule module) : Components.GenericAOEs(module) +{ + private AOEInstance? _aoe; + private readonly AOEShapeDonut donut = new(20f, 25f); + + public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if (spell.Action.ID == (uint)AID.Kleos) + _aoe = new(donut, Arena.Center, default, Module.CastFinishAt(spell, 1.3f)); + } + + public override void OnActorCreated(Actor actor) + { + if (actor.OID == (uint)OID.DeathWall) + { + _aoe = null; + Arena.Bounds = WorthyOfHisBack.DefaultBounds; + } + } +} + class Kleos(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Kleos)); class TrueHolyRaidwide(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.TrueHoly)); class TrueAeroIV(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.TrueAeroIV)); class ParhelionCone(BossModule module) : Components.GenericRotatingAOE(module) { - private static readonly AOEShapeCone cone = new(20, 22.5f.Degrees()); - private Angle increment; + private Angle _increment; + private DateTime _activation; + private readonly List _rotation = new(3); - public override void OnCastStarted(Actor caster, ActorCastInfo spell) + private static readonly AOEShapeCone _shape = new(20f, 22.5f.Degrees()); + + public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { - if ((AID)spell.Action.ID == AID.ParhelionRotationFirst) - Sequences.Add(new(cone, spell.LocXZ, spell.Rotation, increment, Module.CastFinishAt(spell), 2.6f, 9)); + _increment = iconID switch + { + (uint)IconID.RotateCW => -45f.Degrees(), + (uint)IconID.RotateCCW => 45f.Degrees(), + _ => default + }; + _activation = WorldState.FutureTime(5d); + InitIfReady(); } - public override void OnCastFinished(Actor caster, ActorCastInfo spell) + private void InitIfReady() { - if ((AID)spell.Action.ID is AID.ParhelionRotationFirst or AID.ParhelionRotationRest) - AdvanceSequence(spell.LocXZ, spell.Rotation, WorldState.CurrentTime); + if (_rotation.Count == 3 && _increment != default) + { + for (var i = 0; i < 3; ++i) + Sequences.Add(new(_shape, WPos.ClampToGrid(Arena.Center), _rotation[i], _increment, _activation, 2.6f, 9)); + _rotation.Clear(); + _increment = default; + } } - public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if (spell.Action.ID == (uint)AID.ParhelionRotationFirst) + { + _rotation.Add(spell.Rotation); + InitIfReady(); + } + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if (iconID == (uint)IconID.RotateCCW) - increment = 45.Degrees(); - else if (iconID == (uint)IconID.RotateCW) - increment = -45.Degrees(); - for (var i = 0; i < Sequences.Count; i++) - Sequences[i] = Sequences[i] with { Increment = increment }; + if (spell.Action.ID is (uint)AID.ParhelionRotationFirst or (uint)AID.ParhelionRotationRest) + AdvanceSequence(caster.Position, spell.Rotation, WorldState.CurrentTime); } } -class ParhelionDonut(BossModule module) : Components.ConcentricAOEs(module, [new AOEShapeCircle(10), new AOEShapeDonut(10, 15), new AOEShapeDonut(15, 20)]) +class ParhelionDonut(BossModule module) : Components.ConcentricAOEs(module, [new AOEShapeCircle(10f), new AOEShapeDonut(10f, 15f), new AOEShapeDonut(15f, 20f)]) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.Parhelion1) + if (spell.Action.ID == (uint)AID.Parhelion1) AddSequence(spell.LocXZ, Module.CastFinishAt(spell)); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - var order = (AID)spell.Action.ID switch + var order = spell.Action.ID switch { - AID.Parhelion1 => 0, - AID.Parhelion2 => 1, - AID.Parhelion3 => 2, + (uint)AID.Parhelion1 => 0, + (uint)AID.Parhelion2 => 1, + (uint)AID.Parhelion3 => 2, _ => -1 }; - AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(3)); + AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(3d)); } } class EpeaPteroenta(BossModule module) : Components.GenericAOEs(module) { - private readonly List _aoes = []; - private static readonly AOEShapeCone cone = new(20, 60.Degrees()); + private readonly List _aoes = new(3); + private static readonly AOEShapeCone cone = new(20f, 60f.Degrees()); public override IEnumerable ActiveAOEs(int slot, Actor actor) { - for (var i = 0; i < _aoes.Count; ++i) + var count = _aoes.Count; + if (count == 0) + return []; + var max = count > 2 ? 2 : count; + var aoes = new AOEInstance[max]; + for (var i = 0; i < max; ++i) { + var aoe = _aoes[i]; if (i == 0) - yield return _aoes[i] with { Color = Colors.Danger }; - else if (i == 1) - yield return _aoes[i]; + aoes[i] = count > 1 ? aoe with { Color = Colors.Danger } : aoe; + else + aoes[i] = aoe with { Risky = false }; } + return aoes; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.EpeaPteroentaFirst or AID.EpeaPteroentaRest) - _aoes.Add(new(cone, caster.Position, spell.Rotation, Module.CastFinishAt(spell))); + if (spell.Action.ID is (uint)AID.EpeaPteroentaFirst or (uint)AID.EpeaPteroentaRest) + _aoes.Add(new(cone, spell.LocXZ, spell.Rotation, Module.CastFinishAt(spell))); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if (_aoes.Count > 0 && (AID)spell.Action.ID is AID.EpeaPteroentaFirst or AID.EpeaPteroentaRest) + if (_aoes.Count != 0 && spell.Action.ID is (uint)AID.EpeaPteroentaFirst or (uint)AID.EpeaPteroentaRest) _aoes.RemoveAt(0); } } -class CrepuscularRay(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.CrepuscularRay), 4); +class CrepuscularRay(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.CrepuscularRay), 4f); -abstract class CircumzenithalArc(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCone(40, 90.Degrees())); +abstract class CircumzenithalArc(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCone(40f, 90f.Degrees())); class CircumzenithalArcFirst(BossModule module) : CircumzenithalArc(module, AID.CircumzenithalArcFirst); class CircumzenithalArcSecond(BossModule module) : CircumzenithalArc(module, AID.CircumzenithalArcSecond) { - private CrepuscularRay? ray; + private readonly CrepuscularRay _aoe = module.FindComponent()!; + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - ray ??= Module.FindComponent(); - if (ray?.Casters.Count == 0) + if (_aoe.Casters.Count == 0) base.AddAIHints(slot, actor, assignment, hints); } } -class CircleOfBrilliance(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.CircleOfBrilliance), 5); -class Enomotos(BossModule module) : Components.Exaflare(module, new AOEShapeCircle(6), ActionID.MakeSpell(AID.EnomotosFirst)) +class CircleOfBrilliance(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.CircleOfBrilliance), 5f); + +class Enomotos(BossModule module) : Components.Exaflare(module, 6f, ActionID.MakeSpell(AID.EnomotosFirst)) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action == WatchedAction) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 5 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 9, MaxShownExplosions = 3 }); + Lines.Add(new() { Next = caster.Position, Advance = 5f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1f, ExplosionsLeft = 9, MaxShownExplosions = 3 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.EnomotosFirst or AID.EnomotosRest) + if (spell.Action.ID is (uint)AID.EnomotosFirst or (uint)AID.EnomotosRest) { - var line = Lines.FirstOrDefault(x => x.Next.AlmostEqual(caster.Position, 1)); - if (line != null) - AdvanceLine(line, caster.Position); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + break; + } + } } } } -class ArenaChange(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.Kleos)) +class Windage(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Windage), 5f); +class AfflatusAzem(BossModule module) : Components.StandardChasingAOEs(module, new AOEShapeCircle(5f), ActionID.MakeSpell(AID.AfflatusAzemFirst), ActionID.MakeSpell(AID.AfflatusAzemChase), 5f, 2.1f, 5, true); +class WindageSlow(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WindageSlow), 5f); +class TrueHoly(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.TrueHoly), 20f) { - private AOEInstance? _aoe; - private readonly AOEShapeDonut donut = new(20, 25); - - public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); - - public override void OnCastStarted(Actor caster, ActorCastInfo spell) - { - if (spell.Action == WatchedAction) - _aoe = new(donut, Arena.Center, default, Module.CastFinishAt(spell, 1.3f)); - } - - public override void OnActorCreated(Actor actor) + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if ((OID)actor.OID == OID.DeathWall) + if (Casters.Count != 0) { - _aoe = null; - Arena.Bounds = WorthyOfHisBack.DefaultBounds; + var action = actor.Class.GetClassCategory() is ClassCategory.Healer or ClassCategory.Caster ? ActionID.MakeSpell(ClassShared.AID.Surecast) : ActionID.MakeSpell(ClassShared.AID.ArmsLength); + hints.ActionsToExecute.Push(action, actor, ActionQueue.Priority.High); } } } -class Windage(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Windage), 5); -class AfflatusAzem(BossModule module) : Components.StandardChasingAOEs(module, new AOEShapeCircle(5), ActionID.MakeSpell(AID.AfflatusAzemFirst), ActionID.MakeSpell(AID.AfflatusAzemChase), 5, 2.1f, 5, true); -class WindageSlow(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WindageSlow), 5); -class TrueHoly(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.TrueHoly), 20) -{ - public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) - { - var action = actor.Class.GetClassCategory() is ClassCategory.Healer or ClassCategory.Caster ? ActionID.MakeSpell(ClassShared.AID.Surecast) : ActionID.MakeSpell(ClassShared.AID.ArmsLength); - if (Casters.FirstOrDefault()?.CastInfo?.NPCRemainingTime is var t && t < 5) - hints.ActionsToExecute.Push(action, actor, ActionQueue.Priority.High); - } -} -class TrueStoneIV(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TrueStoneIV), 10, maxCasts: 7); -class EnomotosSmall(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EnomotosSmall), 4); +class TrueStoneIV(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TrueStoneIV), 10f, maxCasts: 7); +class EnomotosSmall(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EnomotosSmall), 4f); class Adds(BossModule module) : Components.AddsMulti(module, [(uint)OID.Thelema, (uint)OID.ThelemaAgape], 1); @@ -246,9 +286,9 @@ public WorthyOfHisBackStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() @@ -267,7 +307,7 @@ public WorthyOfHisBackStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69968, NameID = 10586)] public class WorthyOfHisBack(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly WPos arenaCenter = new(-630, 72); - public static readonly ArenaBoundsComplex DefaultBounds = new([new Polygon(arenaCenter, 20, 20)]); + private static readonly WPos arenaCenter = new(-630f, 72f); + public static readonly ArenaBoundsComplex DefaultBounds = new([new Polygon(arenaCenter, 20f, 20)]); private static readonly ArenaBoundsComplex arena = new([new Polygon(arenaCenter, 24.5f, 20)]); } diff --git a/BossMod/Modules/Endwalker/Savage/P12S2PallasAthena/Ekpyrosis.cs b/BossMod/Modules/Endwalker/Savage/P12S2PallasAthena/Ekpyrosis.cs index d693079509..7070fa40e2 100644 --- a/BossMod/Modules/Endwalker/Savage/P12S2PallasAthena/Ekpyrosis.cs +++ b/BossMod/Modules/Endwalker/Savage/P12S2PallasAthena/Ekpyrosis.cs @@ -1,41 +1,45 @@ namespace BossMod.Endwalker.Savage.P12S2PallasAthena; -abstract class Ekpyrosis(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), 19); // TODO: verify falloff +abstract class Ekpyrosis(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), 19f); // TODO: verify falloff class EkpyrosisProximityV(BossModule module) : Ekpyrosis(module, AID.EkpyrosisProximityV); class EkpyrosisProximityH(BossModule module) : Ekpyrosis(module, AID.EkpyrosisProximityH); -class EkpyrosisExaflare(BossModule module) : Components.Exaflare(module, 6) +class EkpyrosisExaflare(BossModule module) : Components.Exaflare(module, 6f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.EkpyrosisExaflareFirst) + if (spell.Action.ID == (uint)AID.EkpyrosisExaflareFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 8 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.1f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = caster.Position, Advance = 8f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2.1f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.EkpyrosisExaflareFirst or AID.EkpyrosisExaflareRest) + if (spell.Action.ID is (uint)AID.EkpyrosisExaflareFirst or (uint)AID.EkpyrosisExaflareRest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } class EkpyrosisSpread : Components.UniformStackSpread { - public EkpyrosisSpread(BossModule module) : base(module, 0, 6) + public EkpyrosisSpread(BossModule module) : base(module, default, 6f) { foreach (var p in Raid.WithoutSlot(true, true, true)) AddSpread(p, module.StateMachine.NextTransitionWithFlag(StateMachine.StateHint.Raidwide)); @@ -43,7 +47,7 @@ public EkpyrosisSpread(BossModule module) : base(module, 0, 6) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.EkpyrosisSpread) + if (spell.Action.ID == (uint)AID.EkpyrosisSpread) Spreads.Clear(); } } diff --git a/BossMod/Modules/Endwalker/Savage/P7SAgdistis/BladesOfAttis.cs b/BossMod/Modules/Endwalker/Savage/P7SAgdistis/BladesOfAttis.cs index 904908f8b2..88abc1dbf3 100644 --- a/BossMod/Modules/Endwalker/Savage/P7SAgdistis/BladesOfAttis.cs +++ b/BossMod/Modules/Endwalker/Savage/P7SAgdistis/BladesOfAttis.cs @@ -1,45 +1,33 @@ namespace BossMod.Endwalker.Savage.P7SAgdistis; -class BladesOfAttis(BossModule module) : Components.Exaflare(module, 7) +class BladesOfAttis(BossModule module) : Components.Exaflare(module, 7f) { - class LineWithActor : Line - { - public Actor Caster; - - public LineWithActor(BossModule module, Actor caster) - { - Next = caster.Position; - Advance = 7 * caster.Rotation.ToDirection(); - NextExplosion = module.CastFinishAt(caster.CastInfo!); - TimeToMove = 2; - ExplosionsLeft = 8; - MaxShownExplosions = 8; - Caster = caster; - } - } - public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.BladesOfAttisFirst) + if (spell.Action.ID == (uint)AID.BladesOfAttisFirst) { - Lines.Add(new LineWithActor(Module, caster)); + Lines.Add(new() { Next = caster.Position, Advance = 7f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2f, ExplosionsLeft = 8, MaxShownExplosions = 8 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.BladesOfAttisFirst or AID.BladesOfAttisRest) + if (spell.Action.ID is (uint)AID.BladesOfAttisFirst or (uint)AID.BladesOfAttisRest) { - var index = Lines.FindIndex(item => ((LineWithActor)item).Caster == caster); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs index 0d8c558312..a04e0c67a2 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs @@ -70,7 +70,7 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - void AddAOEs(ReadOnlySpan angles) + void AddAOEs(Angle[] angles) { _aoes.Add(new(Cone, spell.LocXZ, angles[0], Module.CastFinishAt(spell, 0.8f))); _aoes.Add(new(Cone, spell.LocXZ, angles[1], Module.CastFinishAt(spell, 3.7f))); diff --git a/BossMod/Modules/Endwalker/Ultimate/DSW2/P7ExaflaresEdge.cs b/BossMod/Modules/Endwalker/Ultimate/DSW2/P7ExaflaresEdge.cs index e880bec6e1..3a013258ba 100644 --- a/BossMod/Modules/Endwalker/Ultimate/DSW2/P7ExaflaresEdge.cs +++ b/BossMod/Modules/Endwalker/Ultimate/DSW2/P7ExaflaresEdge.cs @@ -2,7 +2,7 @@ class P7ExaflaresEdge : Components.Exaflare { - public P7ExaflaresEdge(BossModule module) : base(module, 6) + public P7ExaflaresEdge(BossModule module) : base(module, 6f) { ImminentColor = Colors.AOE; } @@ -10,46 +10,76 @@ public P7ExaflaresEdge(BossModule module) : base(module, 6) public override void DrawArenaForeground(int pcSlot, Actor pc) { foreach (var p in SafeSpots()) - Arena.AddCircle(p, 1, Colors.Safe); + Arena.AddCircle(p, 1f, Colors.Safe); } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.ExaflaresEdgeFirst) + if (spell.Action.ID == (uint)AID.ExaflaresEdgeFirst) { - var advance = 7 * spell.Rotation.ToDirection(); - var pos = spell.LocXZ; - var activation = Module.CastFinishAt(spell); - Lines.Add(new() { Next = pos, Advance = advance, NextExplosion = activation, TimeToMove = 1.9f, ExplosionsLeft = 6, MaxShownExplosions = 1 }); - Lines.Add(new() { Next = pos, Advance = advance.OrthoL(), NextExplosion = activation, TimeToMove = 1.9f, ExplosionsLeft = 6, MaxShownExplosions = 1 }); - Lines.Add(new() { Next = pos, Advance = advance.OrthoR(), NextExplosion = activation, TimeToMove = 1.9f, ExplosionsLeft = 6, MaxShownExplosions = 1 }); + var advance = 7f * spell.Rotation.ToDirection(); + AddLine(advance); + AddLine(advance.OrthoL()); + AddLine(advance.OrthoR()); + + void AddLine(WDir dir) + => Lines.Add(new() { Next = caster.Position, Advance = dir, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.9f, ExplosionsLeft = 6, MaxShownExplosions = 1 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.ExaflaresEdgeFirst or AID.ExaflaresEdgeRest) + if (spell.Action.ID is (uint)AID.ExaflaresEdgeFirst or (uint)AID.ExaflaresEdgeRest) { - foreach (var l in Lines.Where(l => l.Next.AlmostEqual(caster.Position, 1))) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - AdvanceLine(l, caster.Position); + var l = Lines[i]; + if (l.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(l, pos); + } } ++NumCasts; } } - private IEnumerable SafeSpots() + private List SafeSpots() { if (NumCasts > 0 || Lines.Count < 9) - yield break; - foreach (var l in MainLines().Where(l => !MainLines().Any(o => o != l && Clips(o, l.Next)))) - yield return l.Next; + return []; + + List safespots = []; + var mainLines = MainLines(); + var len = mainLines.Length; + + for (var i = 0; i < len; ++i) + { + var l = mainLines[i]; + var isSafe = true; + for (var j = 0; j < len; ++j) + { + var o = mainLines[j]; + if (o != l && Clips(o, l.Next)) + { + isSafe = false; + break; + } + } + if (isSafe) + safespots.Add(l.Next); + } + return safespots; } - private IEnumerable MainLines() + private Line[] MainLines() { - for (var i = 0; i < Lines.Count; i += 3) - yield return Lines[i]; + var count = Lines.Count; + var mainlines = new Line[count / 3]; + for (var i = 0; i < count; i += 3) + mainlines[i] = Lines[i]; + return mainlines; } private bool Clips(Line l, WPos p) @@ -57,6 +87,6 @@ private bool Clips(Line l, WPos p) var off = p - l.Next; var px = l.Advance.Dot(off); var pz = l.Advance.OrthoL().Dot(off); - return Math.Abs(px) < 42 || Math.Abs(pz) < 42 && px > 0; // 42 == 6*7 (radius * advance) + return Math.Abs(px) < 42 || Math.Abs(pz) < 42f && px > 0f; // 42 == 6*7 (radius * advance) } } diff --git a/BossMod/Modules/Endwalker/Ultimate/TOP/P6WaveCannon.cs b/BossMod/Modules/Endwalker/Ultimate/TOP/P6WaveCannon.cs index 43295bbf46..456ffe7bb3 100644 --- a/BossMod/Modules/Endwalker/Ultimate/TOP/P6WaveCannon.cs +++ b/BossMod/Modules/Endwalker/Ultimate/TOP/P6WaveCannon.cs @@ -2,31 +2,35 @@ class P6WaveCannonPuddle(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.P6WaveCannonPuddle), 6); -class P6WaveCannonExaflare(BossModule module) : Components.Exaflare(module, 8) +class P6WaveCannonExaflare(BossModule module) : Components.Exaflare(module, 8f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.P6WaveCannonExaflareFirst) + if (spell.Action.ID == (uint)AID.P6WaveCannonExaflareFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 8 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 7, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = caster.Position, Advance = 8f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 7, MaxShownExplosions = 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.P6WaveCannonExaflareFirst or AID.P6WaveCannonExaflareRest) + if (spell.Action.ID is (uint)AID.P6WaveCannonExaflareFirst or (uint)AID.P6WaveCannonExaflareRest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Variant/V01SS/V012Silkie/V012Silkie.cs b/BossMod/Modules/Endwalker/Variant/V01SS/V012Silkie/V012Silkie.cs index f01c223258..ef8a799481 100644 --- a/BossMod/Modules/Endwalker/Variant/V01SS/V012Silkie/V012Silkie.cs +++ b/BossMod/Modules/Endwalker/Variant/V01SS/V012Silkie/V012Silkie.cs @@ -26,30 +26,34 @@ class PuffAndTumble2(BossModule module) : Components.SimpleAOEs(module, ActionID class SqueakyCleanAOE2W(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SqueakyCleanAOE2W), new AOEShapeCone(60, 45.Degrees())); class SqueakyCleanAOE3W(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SqueakyCleanAOE3W), new AOEShapeCone(60, 112.5f.Degrees())); -class EasternEwers(BossModule module) : Components.Exaflare(module, 4) +class EasternEwers(BossModule module) : Components.Exaflare(module, 4f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.BrimOver) + if (spell.Action.ID == (uint)AID.BrimOver) { - Lines.Add(new() { Next = caster.Position, Advance = new(0, 5.1f), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 0.8f, ExplosionsLeft = 11, MaxShownExplosions = int.MaxValue }); + Lines.Add(new() { Next = caster.Position, Advance = new(default, 5.1f), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 0.8f, ExplosionsLeft = 11, MaxShownExplosions = int.MaxValue }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.BrimOver or AID.Rinse) + if (spell.Action.ID is (uint)AID.BrimOver or (uint)AID.Rinse) { - var index = Lines.FindIndex(item => Math.Abs(item.Next.X - caster.Position.X) < 1); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } diff --git a/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/SpearmanOrders.cs b/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/SpearmanOrders.cs index 477c3768d2..11e74c40e0 100644 --- a/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/SpearmanOrders.cs +++ b/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/SpearmanOrders.cs @@ -1,48 +1,58 @@ namespace BossMod.Endwalker.VariantCriterion.V02MR.V022Moko; -class SpearmanOrdersFast(BossModule module) : Components.Exaflare(module, new AOEShapeRect(3, 2, 0.474f)) +class SpearmanOrdersFast(BossModule module) : Components.Exaflare(module, new AOEShapeRect(3f, 2f, 0.474f)) { public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id) { - if ((OID)actor.OID == OID.AshigaruSoheiFast && id == 0x25E9) + if (actor.OID == (uint)OID.AshigaruSoheiFast && id == 0x25E9) Lines.Add(new() { Next = actor.Position, Advance = 3.474f * actor.Rotation.ToDirection(), NextExplosion = WorldState.FutureTime(7.8f), TimeToMove = 0.6f, ExplosionsLeft = 12, MaxShownExplosions = 4 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.SpearpointPushFast) + if (spell.Action.ID == (uint)AID.SpearpointPushFast) { - ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index >= 0) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } } } } -class SpearmanOrdersSlow(BossModule module) : Components.Exaflare(module, new AOEShapeRect(2, 2, 0.316f)) +class SpearmanOrdersSlow(BossModule module) : Components.Exaflare(module, new AOEShapeRect(2f, 2f, 0.316f)) { public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id) { - if ((OID)actor.OID == OID.AshigaruSoheiSlow && id == 0x25E9) + if (actor.OID == (uint)OID.AshigaruSoheiSlow && id == 0x25E9) Lines.Add(new() { Next = actor.Position, Advance = 2.316f * actor.Rotation.ToDirection(), NextExplosion = WorldState.FutureTime(7.8f), TimeToMove = 0.6f, ExplosionsLeft = 18, MaxShownExplosions = 2 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.SpearpointPushSlow) + if (spell.Action.ID == (uint)AID.SpearpointPushSlow) { - ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index >= 0) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } } } diff --git a/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/Upwell.cs b/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/Upwell.cs index c2525181b7..b75109decd 100644 --- a/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/Upwell.cs +++ b/BossMod/Modules/Endwalker/Variant/V02MR/V022Moko/Upwell.cs @@ -5,9 +5,9 @@ // each wave is 5 subsequent lines, except for two horizontal ones that go towards edges - they only have 1 line - meaning there's a total 22 'rest' casts class UpwellFirst : Components.SimpleAOEs { - public UpwellFirst(BossModule module) : base(module, ActionID.MakeSpell(AID.UpwellFirst), new AOEShapeRect(60, 5)) { Color = Colors.Danger; } + public UpwellFirst(BossModule module) : base(module, ActionID.MakeSpell(AID.UpwellFirst), new AOEShapeRect(60f, 5f)) { Color = Colors.Danger; } } -class UpwellRest(BossModule module) : Components.Exaflare(module, new AOEShapeRect(30, 2.5f, 30)) +class UpwellRest(BossModule module) : Components.Exaflare(module, new AOEShapeRect(30f, 2.5f, 30f)) { public override IEnumerable ActiveAOEs(int slot, Actor actor) { @@ -25,47 +25,55 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) { var aoe = futureAOEs[i]; if (aoe.Item2 <= imminentDeadline) - aoes[index++] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, FutureColor); + aoes[index++] = new(Shape, WPos.ClampToGrid(aoe.Item1), aoe.Item3, aoe.Item2, FutureColor); } for (var i = 0; i < imminentCount; ++i) { var aoe = imminentAOEs[i]; if (aoe.Item2 <= imminentDeadline) - aoes[index++] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, ImminentColor); + aoes[index++] = new(Shape, WPos.ClampToGrid(aoe.Item1), aoe.Item3, aoe.Item2, ImminentColor); } return aoes[..index]; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.UpwellFirst) + if (spell.Action.ID == (uint)AID.UpwellFirst) { var pos = caster.Position; - var check = pos.AlmostEqual(Arena.Center, 1); - var isNorth = pos.Z == 530; - var isSouth = pos.Z == 550; + var check = pos.AlmostEqual(Arena.Center, 1f); + var isNorth = pos.Z == 530f; + var isSouth = pos.Z == 550f; var numExplosions1 = check || isSouth ? 5 : 1; var numExplosions2 = check || isNorth ? 5 : 1; var dir = spell.Rotation.ToDirection().OrthoR(); var advance1 = dir * 7.5f; var advance2 = dir * 5; - Lines.Add(new() { Next = pos + advance1, Advance = advance2, Rotation = spell.Rotation, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = numExplosions2, MaxShownExplosions = 2 }); - Lines.Add(new() { Next = pos - advance1, Advance = -advance2, Rotation = (spell.Rotation + 180.Degrees()).Normalized(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2, ExplosionsLeft = numExplosions1, MaxShownExplosions = 2 }); + AddLine(pos + advance1, advance2, default, numExplosions2); + AddLine(pos - advance1, -advance2, 180f.Degrees(), numExplosions1); + void AddLine(WPos first, WDir dir, Angle offset, int explosions) + => Lines.Add(new() { Next = first, Advance = dir, Rotation = spell.Rotation + offset, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 2f, ExplosionsLeft = explosions, MaxShownExplosions = 2 }); } } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.UpwellRest) + if (spell.Action.ID == (uint)AID.UpwellRest) { - ++NumCasts; - var index = Lines.FindIndex(l => l.Next.AlmostEqual(caster.Position, 3) && l.Rotation.AlmostEqual(caster.Rotation, Angle.DegToRad)); - if (index >= 0) + var count = Lines.Count; + var pos = caster.Position; + var rot = caster.Rotation; + for (var i = 0; i < count; ++i) { - AdvanceLine(Lines[index], caster.Position + 2.5f * Lines[index].Rotation.ToDirection().OrthoR()); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 3f) && line.Rotation.AlmostEqual(rot, Angle.DegToRad)) + { + AdvanceLine(line, pos + 2.5f * line.Rotation.ToDirection().OrthoR()); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } } } diff --git a/BossMod/Modules/Heavensward/Dungeon/D13SohrKhai/D133Hraesvelgr.cs b/BossMod/Modules/Heavensward/Dungeon/D13SohrKhai/D133Hraesvelgr.cs index af7e191514..f0ca8b1f00 100644 --- a/BossMod/Modules/Heavensward/Dungeon/D13SohrKhai/D133Hraesvelgr.cs +++ b/BossMod/Modules/Heavensward/Dungeon/D13SohrKhai/D133Hraesvelgr.cs @@ -71,7 +71,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) } } -class HolyOrb(BossModule module) : Components.Exaflare(module, new AOEShapeCircle(6)) +class HolyOrb(BossModule module) : Components.Exaflare(module, 6f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { @@ -83,12 +83,19 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) { if (spell.Action.ID is (uint)AID.HolyOrbFirst or (uint)AID.HolyOrbRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } diff --git a/BossMod/Modules/RealmReborn/Dungeon/D10StoneVigil/D102Koshchei.cs b/BossMod/Modules/RealmReborn/Dungeon/D10StoneVigil/D102Koshchei.cs index 7bc9a8f74c..f7352d9455 100644 --- a/BossMod/Modules/RealmReborn/Dungeon/D10StoneVigil/D102Koshchei.cs +++ b/BossMod/Modules/RealmReborn/Dungeon/D10StoneVigil/D102Koshchei.cs @@ -2,9 +2,9 @@ public enum OID : uint { - Boss = 0x38C7, // x1 - MaelstromVisual = 0x38C8, // spawn during fight - MaelstromHelper = 0x38D0 // x4 + Boss = 0x38C7, // R2.88 + MaelstromVisual = 0x38C8, // R0.8 + MaelstromHelper = 0x38D0 } public enum AID : uint @@ -14,31 +14,35 @@ public enum AID : uint SpikedTail = 28732, // Boss->player, 5.0s cast, single-target, tankbuster SonicStorm = 29053, // Boss->location, 3.0s cast, range 6 circle Typhoon = 28730, // Boss->self, 3.0s cast, single-target, visual - TyphoonAOE = 28731, // MaelstromHelper->self, no cast, range 3 circle + TyphoonAOE = 28731 // MaelstromHelper->self, no cast, range 3 circle } class SpikedTail(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.SpikedTail)); -class SonicStorm(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SonicStorm), 6); +class SonicStorm(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SonicStorm), 6f); -class Typhoon(BossModule module) : Components.Exaflare(module, 3) +class Typhoon(BossModule module) : Components.Exaflare(module, 3f) { - private readonly List _maelstroms = module.Enemies(OID.MaelstromVisual); + private readonly List _maelstroms = module.Enemies((uint)OID.MaelstromVisual); public override void Update() { - foreach (var m in _maelstroms) + var count = _maelstroms.Count; + if (count == 0) + return; + for (var i = 0; i < count; ++i) { + var m = _maelstroms[i]; var line = FindLine(m.Position.Z); if (m.IsDead && line != null) Lines.Remove(line); else if (!m.IsDead && line == null) - Lines.Add(new() { Next = m.Position, Advance = new(-1.745f, 0), TimeToMove = 0.6f, ExplosionsLeft = 4, MaxShownExplosions = 4 }); + Lines.Add(new() { Next = m.Position, Advance = new(-1.745f, default), TimeToMove = 0.6f, ExplosionsLeft = 4, MaxShownExplosions = 4 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.TyphoonAOE && caster.Position.X < 56) + if (spell.Action.ID == (uint)AID.TyphoonAOE && caster.Position.X < 56f) { var line = FindLine(caster.Position.Z); if (line == null) @@ -57,7 +61,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) } } - private Line? FindLine(float z) => Lines.Find(l => Math.Abs(l.Next.Z - z) < 1); + private Line? FindLine(float z) => Lines.Find(l => Math.Abs(l.Next.Z - z) < 1f); } class D102KoshcheiStates : StateMachineBuilder @@ -74,7 +78,7 @@ public D102KoshcheiStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 11, NameID = 1678)] public class D102Koshchei(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly Shape[] union = [new Rectangle(new(44, -80), 13.5f, 10.5f), new Rectangle(new(30.1f, -80), 4.5f, 0.4f, 90.Degrees()), + private static readonly Shape[] union = [new Rectangle(new(44f, -80f), 13.5f, 10.5f), new Rectangle(new(30.1f, -80), 0.4f, 4.5f), new Square(new(30.4f, -75.4f), 0.2f), new Square(new(30.4f, -84.6f), 0.2f)]; public static readonly ArenaBoundsComplex arena = new(union); } diff --git a/BossMod/Modules/RealmReborn/Quest/MSQ/TheStepsOfFaith.cs b/BossMod/Modules/RealmReborn/Quest/MSQ/TheStepsOfFaith.cs index eb89d11dbe..b77f1b6eb1 100644 --- a/BossMod/Modules/RealmReborn/Quest/MSQ/TheStepsOfFaith.cs +++ b/BossMod/Modules/RealmReborn/Quest/MSQ/TheStepsOfFaith.cs @@ -92,55 +92,62 @@ public enum AID : uint FallOfMan = 30187, // Helper->self, 20.0s cast, range 90 width 20 rect } -class RipperClaw(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.RipperClaw), new AOEShapeCone(9, 45.Degrees())); -class Levinshower(BossModule module) : Components.Cleave(module, ActionID.MakeSpell(AID.Levinshower), new AOEShapeCone(6, 60.Degrees()), +class RipperClaw(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.RipperClaw), new AOEShapeCone(9f, 45f.Degrees())); +class Levinshower(BossModule module) : Components.Cleave(module, ActionID.MakeSpell(AID.Levinshower), new AOEShapeCone(6f, 60f.Degrees()), [(uint)OID.HordeBiast2, (uint)OID.HordeBiast4, (uint)OID.HordeBiast5, (uint)OID.HordeBiast6, (uint)OID.HordeBiast7]); -class EarthShakerAOE(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EarthshakerAOE), 31); -class Earthshaker(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Earthshaker), new AOEShapeCone(80, 15.Degrees()), 2); +class EarthShakerAOE(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EarthshakerAOE), 31f); +class Earthshaker(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Earthshaker), new AOEShapeCone(80f, 15f.Degrees()), 2); -class EarthrisingAOE(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EarthrisingAOE), 31); -class Earthrising(BossModule module) : Components.Exaflare(module, 8) +class EarthrisingAOE(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EarthrisingAOE), 31f); +class Earthrising(BossModule module) : Components.Exaflare(module, 8f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.EarthrisingCast) + if (spell.Action.ID == (uint)AID.EarthrisingCast) { - Lines.Add(new() { Next = spell.LocXZ, Advance = new(0, -7.5f), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 5, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = caster.Position, Advance = new(default, -7.5f), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1f, ExplosionsLeft = 5, MaxShownExplosions = 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.EarthrisingRepeat or AID.EarthrisingCast) + if (spell.Action.ID is (uint)AID.EarthrisingRepeat or (uint)AID.EarthrisingCast) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } -class SidewiseSlice(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SidewiseSlice), new AOEShapeCone(50, 60.Degrees())); +class SidewiseSlice(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SidewiseSlice), new AOEShapeCone(50f, 60f.Degrees())); -class FireballSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.FireballSpread), 6); -class FireballAOE(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FireballAOE), 6); -class Flamisphere(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Flamisphere), 10); +class FireballSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.FireballSpread), 6f); +class FireballAOE(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FireballAOE), 6f); +class Flamisphere(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Flamisphere), 10f); -class BodySlam(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.BodySlam), 20, kind: Kind.DirForward, stopAtWall: true); +class BodySlam(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.BodySlam), 20f, kind: Kind.DirForward, stopAtWall: true); class FlameBreath(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.FlameBreathChannel)) { private AOEInstance? _aoe; - private static readonly AOEShapeRect rect = new(500, 10); + private static readonly AOEShapeRect rect = new(500f, 10f); public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.FlameBreath1) + if (spell.Action.ID == (uint)AID.FlameBreath1) _aoe = new(rect, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation, Module.CastFinishAt(spell).AddSeconds(1)); } @@ -158,13 +165,13 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) class FlameBreath2(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.FlameBreathChannel)) { private AOEInstance? _aoe; - private static readonly AOEShapeRect rect = new(60, 10); + private static readonly AOEShapeRect rect = new(60f, 10f); public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.FlameBreath2) + if (spell.Action.ID == (uint)AID.FlameBreath2) { NumCasts = 0; _aoe = new(rect, spell.LocXZ, spell.Rotation, Module.CastFinishAt(spell)); @@ -184,18 +191,18 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) class Cauterize(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.Cauterize)) { private Actor? Source; - private static readonly AOEShapeRect rect = new(160, 22); - private static readonly AOEShapeRect MoveIt = new(40, 22, 38); + private static readonly AOEShapeRect rect = new(160f, 22f); + private static readonly AOEShapeRect MoveIt = new(40f, 22f, 38f); public override IEnumerable ActiveAOEs(int slot, Actor actor) { if (Source == null) - yield break; + return []; - if (Arena.Center.Z > 218) - yield return new(MoveIt, Arena.Center); + if (Arena.Center.Z > 218f) + return [new(MoveIt, Arena.Center)]; else - yield return new(rect, Source.Position, 180.Degrees(), Module.CastFinishAt(Source.CastInfo)); + return [new(rect, Source.Position, 180f.Degrees(), Module.CastFinishAt(Source.CastInfo))]; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) @@ -211,33 +218,36 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) } } -class Touchdown(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.Touchdown), 10, stopAtWall: true); +class Touchdown(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.Touchdown), 10f, stopAtWall: true); class ScorchingBreath(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeRect rect = new(100, 10, 100); + private static readonly AOEShapeRect rect = new(100f, 10f, 100f); + public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.ScorchingBreath) - NumCasts++; + if (spell.Action.ID == (uint)AID.ScorchingBreath) + ++NumCasts; } public override IEnumerable ActiveAOEs(int slot, Actor actor) { - if (NumCasts > 0) - yield return new(rect, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation, Module.CastFinishAt(Module.PrimaryActor.CastInfo)); + if (NumCasts != 0) + return [new(rect, Module.PrimaryActor.Position, Module.PrimaryActor.Rotation, Module.CastFinishAt(Module.PrimaryActor.CastInfo))]; + else + return []; } } class ScrollingBounds(BossModule module) : BossComponent(module) { - public const float HalfHeight = 40; - public const float HalfWidth = 22; + public const float HalfHeight = 40f; + public const float HalfWidth = 22f; public static readonly ArenaBoundsRect Bounds = new(HalfWidth, HalfHeight); private int Phase = 1; - private (float Min, float Max) ZBounds = (120, 300); + private (float Min, float Max) ZBounds = (120f, 300f); public override void OnEventEnvControl(byte index, uint state) { @@ -245,17 +255,17 @@ public override void OnEventEnvControl(byte index, uint state) { if (index == 0x03) { - ZBounds = (120, 200); + ZBounds = (120f, 200f); Phase = 2; } else if (index == 0x04) { - ZBounds = (-40, 40); + ZBounds = (-40f, 40f); Phase = 4; } else if (index == 0x06) { - ZBounds = (-200, -120); + ZBounds = (-200f, -120f); Phase = 6; } } @@ -263,12 +273,12 @@ public override void OnEventEnvControl(byte index, uint state) { if (index == 0x00) { - ZBounds = (-40, 200); + ZBounds = (-40f, 200f); Phase = 3; } else if (index == 0x01) { - ZBounds = (-200, 40); + ZBounds = (-200f, 40f); Phase = 5; } } @@ -278,10 +288,10 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme { // force player to walk south to aggro vishap (status 1268 = In Event, not actionable) if (Phase == 1 && !actor.InCombat && actor.FindStatus(1268) == null) - hints.AddForbiddenZone(ShapeDistance.Rect(Arena.Center, new WDir(0, 1), 38, 22, 40)); + hints.AddForbiddenZone(ShapeDistance.Rect(Arena.Center, new WDir(default, 1f), 38f, 22f, 40f)); // subsequent state transitions don't trigger until player moves into the area - else if (Phase == 3 && actor.Position.Z > 25 || Phase == 5 && actor.Position.Z > -135) - hints.AddForbiddenZone(ShapeDistance.Rect(Arena.Center, new WDir(0, 1), 40, 22, 38)); + else if (Phase == 3 && actor.Position.Z > 25f || Phase == 5 && actor.Position.Z > -135f) + hints.AddForbiddenZone(ShapeDistance.Rect(Arena.Center, new WDir(default, 1f), 40f, 22f, 38f)); } public override void Update() @@ -318,7 +328,7 @@ public VishapStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 70127, NameID = 3330)] -public class Vishap(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, 245), ScrollingBounds.Bounds) +public class Vishap(WorldState ws, Actor primary) : BossModule(ws, primary, new(default, 245f), ScrollingBounds.Bounds) { // vishap doesn't start targetable private static readonly uint[] opponents = [(uint)OID.HordeWyvern1, (uint)OID.HordeWyvern2, (uint)OID.HordeWyvern3, (uint)OID.HordeWyvern4, (uint)OID.HordeWyvern5, diff --git a/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D011ForgivenDissonance.cs b/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D011ForgivenDissonance.cs index c5d5913154..84107b56ef 100644 --- a/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D011ForgivenDissonance.cs +++ b/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D011ForgivenDissonance.cs @@ -21,9 +21,9 @@ public enum AID : uint Pillory = 15812 // Boss->player, 5.0s cast, single-target } -class Thumbscrew(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.Thumbscrew), 4); +class Thumbscrew(BossModule module) : Components.ChargeAOEs(module, ActionID.MakeSpell(AID.Thumbscrew), 4f); class ThePathofLight(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.ThePathOfLight)); -class GibbetCage(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GibbetCage), 8); +class GibbetCage(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GibbetCage), 8f); class HereticsFork(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.HereticsFork), new AOEShapeCross(40f, 3f)); class LightShot(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightShot), new AOEShapeRect(40f, 2f)); class WoodenHorse(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WoodenHorse), new AOEShapeCone(40f, 45f.Degrees())); @@ -48,5 +48,5 @@ public D011ForgivenDissonanceStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "legendoficeman, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 676, NameID = 8299)] public class D011ForgivenDissonance(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly ArenaBoundsComplex arena = new([new Circle(new(-15, 240), 19.5f)], [new Rectangle(new(-15, 260), 20, 1.25f), new Rectangle(new(-15, 220), 20, 1.2f)]); + private static readonly ArenaBoundsComplex arena = new([new Circle(new(-15f, 240f), 19.5f)], [new Rectangle(new(-15f, 260f), 20f, 1.25f), new Rectangle(new(-15f, 220f), 20f, 1.2f)]); } diff --git a/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D012TesleentheForgiven.cs b/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D012TesleentheForgiven.cs index 295ed50aa4..1d6c33e495 100644 --- a/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D012TesleentheForgiven.cs +++ b/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D012TesleentheForgiven.cs @@ -50,6 +50,9 @@ public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) public override void Update() { + var count = CurrentBaits.Count; + if (count == 0) + return; for (var i = 0; i < CurrentBaits.Count; ++i) { var b = CurrentBaits[i]; @@ -59,7 +62,26 @@ public override void Update() } class Exorcise(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.ExorciseA), 6f, 4, 4); -class HolyWater(BossModule module) : Components.PersistentVoidzoneAtCastTarget(module, 6f, ActionID.MakeSpell(AID.HolyWater), m => m.Enemies(OID.HolyWaterVoidzone).Where(z => z.EventState != 7), 0.8f); +class HolyWater(BossModule module) : Components.PersistentVoidzoneAtCastTarget(module, 6f, ActionID.MakeSpell(AID.HolyWater), GetVoidzones, 0.8f) +{ + private static Actor[] GetVoidzones(BossModule module) + { + var enemies = module.Enemies((uint)OID.HolyWaterVoidzone); + var count = enemies.Count; + if (count == 0) + return []; + + var voidzones = new Actor[count]; + var index = 0; + for (var i = 0; i < count; ++i) + { + var z = enemies[i]; + if (z.EventState != 7) + voidzones[index++] = z; + } + return voidzones[..index]; + } +} class D012TesleentheForgivenStates : StateMachineBuilder { @@ -77,5 +99,5 @@ public D012TesleentheForgivenStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "legendoficeman, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 676, NameID = 8300)] public class D012TesleentheForgiven(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { - private static readonly ArenaBoundsComplex arena = new([new Circle(new(78, -82), 19.5f)], [new Rectangle(new(78, -62), 20, 1), new Rectangle(new(78, -102), 20, 1)]); + private static readonly ArenaBoundsComplex arena = new([new Circle(new(78f, -82f), 19.5f)], [new Rectangle(new(78f, -62f), 20f, 1f), new Rectangle(new(78f, -102f), 20f, 1f)]); } diff --git a/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D013Philia.cs b/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D013Philia.cs index 8e456ceda1..1e84d97b2a 100644 --- a/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D013Philia.cs +++ b/BossMod/Modules/Shadowbringers/Dungeon/D01Holminster/D013Philia.cs @@ -49,7 +49,27 @@ public enum SID : uint Fetters = 1849 // none->player, extra=0xEC4 } -class SludgeVoidzone(BossModule module) : Components.PersistentVoidzone(module, 9.8f, m => m.Enemies(OID.SludgeVoidzone).Where(z => z.EventState != 7)); +class SludgeVoidzone(BossModule module) : Components.PersistentVoidzone(module, 9.8f, GetVoidzones) +{ + private static Actor[] GetVoidzones(BossModule module) + { + var enemies = module.Enemies((uint)OID.SludgeVoidzone); + var count = enemies.Count; + if (count == 0) + return []; + + var voidzones = new Actor[count]; + var index = 0; + for (var i = 0; i < count; ++i) + { + var z = enemies[i]; + if (z.EventState != 7) + voidzones[index++] = z; + } + return voidzones[..index]; + } +} + class ScavengersDaughter(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.ScavengersDaughter)); class HeadCrusher(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.HeadCrusher)); @@ -125,7 +145,11 @@ class Aethersup(BossModule module) : Components.GenericAOEs(module) public override IEnumerable ActiveAOEs(int slot, Actor actor) { if (_aoe != default) - return [_aoe with { Risky = Module.Enemies((uint)OID.IronChain).Any(x => x.IsDead) }]; + { + var chainL = Module.Enemies((uint)OID.IronChain); + var count = chainL.Count; + return [_aoe with { Risky = count == 0 || count != 0 && chainL[0].IsDead }]; + } else return []; } @@ -152,19 +176,19 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) } } -class PendulumFlare(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCircle(20), (uint)IconID.SpreadFlare, ActionID.MakeSpell(AID.PendulumAOE1), 5.1f, true) +class PendulumFlare(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCircle(20f), (uint)IconID.SpreadFlare, ActionID.MakeSpell(AID.PendulumAOE1), 5.1f, true) { public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { base.AddAIHints(slot, actor, assignment, hints); - if (ActiveBaits.Any(x => x.Target == actor)) + if (CurrentBaits.Count != 0 && CurrentBaits[0].Target == actor) hints.AddForbiddenZone(ShapeDistance.Circle(D013Philia.ArenaCenter, 18.5f)); } public override void AddHints(int slot, Actor actor, TextHints hints) { base.AddHints(slot, actor, hints); - if (ActiveBaits.Any(x => x.Target == actor)) + if (CurrentBaits.Count != 0 && CurrentBaits[0].Target == actor) hints.Add("Bait away!"); } } @@ -181,7 +205,7 @@ class IntoTheLight(BossModule module) : Components.LineStack(module, ActionID.Ma class CatONineTails(BossModule module) : Components.GenericRotatingAOE(module) { - private static readonly AOEShapeCone _shape = new(25, 60.Degrees()); + private static readonly AOEShapeCone _shape = new(25f, 60f.Degrees()); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { @@ -217,14 +241,14 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) for (var i = 0; i < futureCount; ++i) { var aoe = futureAOEs[i]; - aoes[index++] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, FutureColor); + aoes[index++] = new(Shape, WPos.ClampToGrid(aoe.Item1), aoe.Item3, aoe.Item2, FutureColor); } for (var i = 0; i < imminentCount; ++i) { var aoe = imminentAOEs[i]; - aoes[index++] = new(Shape, aoe.Item1, aoe.Item3, aoe.Item2, ImminentColor); + aoes[index++] = new(Shape, WPos.ClampToGrid(aoe.Item1), aoe.Item3, aoe.Item2, ImminentColor); } - for (var i = 0; i < _aoes.Count; ++i) + for (var i = 0; i < aoesCount; ++i) { var aoe = _aoes[i]; aoes[index++] = aoe; @@ -256,12 +280,18 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) void Advance(WPos pos) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(pos, 1f)); - if (index < 0) - return; - AdvanceLine(Lines[index], pos); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } diff --git a/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE44FamiliarFace.cs b/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE44FamiliarFace.cs index b11eb2a0e3..0f983327d9 100644 --- a/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE44FamiliarFace.cs +++ b/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE44FamiliarFace.cs @@ -41,93 +41,81 @@ public enum AID : uint Hammerfall = 23825 // Helper->self, 8.0s cast, range 37 circle aoe } -class TectonicEruption(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TectonicEruption), 6); +class TectonicEruption(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TectonicEruption), 6f); class RockCutter(BossModule module) : Components.SingleTargetDelayableCast(module, ActionID.MakeSpell(AID.RockCutter)); class AncientQuake(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.AncientQuake)); -class Roxxor(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.Roxxor), 6); -class ControlTowerAppear(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.ControlTowerAppear), 6); +class Roxxor(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.Roxxor), 6f); +class ControlTowerAppear(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.ControlTowerAppear), 6f); // note: we could predict aoes way in advance, when FallingTower actors are created - they immediately have correct rotation // if previous cast was TowerRound, delay is ~24.4s; otherwise if previous cast was ControlTower, delay is ~9.6s; otherwise it is ~13s // however, just watching casts normally gives more than enough time to avoid aoes and does not interfere with mechanics that resolve earlier -class Towerfall(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Towerfall), new AOEShapeRect(40, 5)); +class Towerfall(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Towerfall), new AOEShapeRect(40f, 5f)); -class ExtremeEdge(BossModule module) : Components.GenericAOEs(module) -{ - private readonly List<(Actor caster, float offset)> _casters = []; - - private static readonly AOEShapeRect _shape = new(60, 18); - - public override IEnumerable ActiveAOEs(int slot, Actor actor) - { - foreach (var c in _casters) - yield return new(_shape, c.caster.Position + c.offset * c.caster.CastInfo!.Rotation.ToDirection().OrthoL(), c.caster.CastInfo.Rotation, Module.CastFinishAt(c.caster.CastInfo)); - } - - public override void OnCastStarted(Actor caster, ActorCastInfo spell) - { - var offset = (AID)spell.Action.ID switch - { - AID.ExtremeEdgeL => 12, - AID.ExtremeEdgeR => -12, - _ => 0 - }; - if (offset != 0) - _casters.Add((caster, offset)); - } +abstract class ExtremeEdge(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeRect(60f, 18f)); +class ExtremeEdgeL(BossModule module) : ExtremeEdge(module, AID.ExtremeEdgeL); +class ExtremeEdgeR(BossModule module) : ExtremeEdge(module, AID.ExtremeEdgeR); - public override void OnCastFinished(Actor caster, ActorCastInfo spell) - { - if ((AID)spell.Action.ID is AID.ExtremeEdgeL or AID.ExtremeEdgeR) - _casters.RemoveAll(c => c.caster == caster); - } -} - -class IntractableLand(BossModule module) : Components.Exaflare(module, 8) +class IntractableLand(BossModule module) : Components.Exaflare(module, 8f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.IntractableLandFirst) + if (spell.Action.ID == (uint)AID.IntractableLandFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 8 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 0.8f, ExplosionsLeft = 8, MaxShownExplosions = 4 }); + Lines.Add(new() { Next = caster.Position, Advance = 8f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 0.8f, ExplosionsLeft = 8, MaxShownExplosions = 4 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.IntractableLandFirst or AID.IntractableLandRest) + if (spell.Action.ID is (uint)AID.IntractableLandFirst or (uint)AID.IntractableLandRest) { - var pos = (AID)spell.Action.ID == AID.IntractableLandFirst ? caster.Position : spell.TargetXZ; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(pos, 1)); - if (index == -1) + var count = Lines.Count; + var pos = spell.Action.ID == (uint)AID.IntractableLandFirst ? caster.Position : spell.TargetXZ; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], pos); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } class Hammerfall(BossModule module) : Components.GenericAOEs(module) { - private readonly List _aoes = []; + private readonly List _aoes = new(3); private static readonly AOEShapeCircle _shape = new(37); - public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes.Take(2); + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = _aoes.Count; + if (count == 0) + return []; + var max = count > 2 ? 2 : count; + var aoes = new AOEInstance[max]; + for (var i = 0; i < max; ++i) + { + aoes[i] = _aoes[i]; + } + return aoes; + } public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.Hammer) - _aoes.Add(new(_shape, actor.Position, default, WorldState.FutureTime(12.6f))); + if (actor.OID == (uint)OID.Hammer) + _aoes.Add(new(_shape, actor.Position, default, WorldState.FutureTime(12.6d))); } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.Hammerfall) + if (_aoes.Count != 0 && spell.Action.ID == (uint)AID.Hammerfall) _aoes.RemoveAt(0); } } @@ -143,11 +131,12 @@ public CE44FamiliarFaceStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter(); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.BozjaCE, GroupID = 778, NameID = 29)] // bnpcname=9693 -public class CE44FamiliarFace(WorldState ws, Actor primary) : BossModule(ws, primary, new(330, 390), new ArenaBoundsCircle(30)); +public class CE44FamiliarFace(WorldState ws, Actor primary) : BossModule(ws, primary, new(330f, 390f), new ArenaBoundsCircle(30f)); diff --git a/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE62LooksToDieFor.cs b/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE62LooksToDieFor.cs index e0728d5b95..d7fce1bff4 100644 --- a/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE62LooksToDieFor.cs +++ b/BossMod/Modules/Shadowbringers/Foray/CriticalEngagement/CE62LooksToDieFor.cs @@ -11,6 +11,7 @@ public enum OID : uint public enum AID : uint { AutoAttack = 6499, // Boss->player, no cast, single-target + Thundercall = 23964, // Boss->self, 4.0s cast, single-target, visual (summon orbs) ThundercallVisual = 23965, // Helper->self, 4.5s cast, single-target, ??? ThundercallAOE = 23966, // Helper->self, no cast, ???, raidwide @@ -49,32 +50,32 @@ class LightningBoltDistantClap(BossModule module) : Components.GenericAOEs(modul public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.LightningBoltAOE) + if (spell.Action.ID == (uint)AID.LightningBoltAOE) _aoes.Add(new(_shapeBolt, spell.LocXZ, spell.Rotation, Module.CastFinishAt(spell))); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - switch ((AID)spell.Action.ID) + switch (spell.Action.ID) { - case AID.LightningBoltAOE: - if (_aoes.FindIndex(a => a.Origin.AlmostEqual(spell.TargetXZ, 1)) is var index && index >= 0) - _aoes[index] = new(_shapeClap, spell.TargetXZ, default, WorldState.FutureTime(6.1f)); + case (uint)AID.LightningBoltAOE: + if (_aoes.FindIndex(a => a.Origin.AlmostEqual(spell.TargetXZ, 1f)) is var index && index >= 0) + _aoes[index] = new(_shapeClap, spell.TargetXZ, default, WorldState.FutureTime(6.1d)); break; - case AID.DistantClap: - _aoes.RemoveAll(a => a.Origin.AlmostEqual(caster.Position, 1)); + case (uint)AID.DistantClap: + _aoes.RemoveAll(a => a.Origin.AlmostEqual(caster.Position, 1f)); break; } } } -class TwistingWinds(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TwistingWinds), new AOEShapeRect(40, 5, 40)); +class TwistingWinds(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TwistingWinds), new AOEShapeRect(80, 5)); -class CloudToGround(BossModule module) : Components.Exaflare(module, 5) +class CloudToGround(BossModule module) : Components.Exaflare(module, 5f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.CloudToGroundFirst) + if (spell.Action.ID == (uint)AID.CloudToGroundFirst) { Lines.Add(new() { Next = caster.Position, Advance = 5 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 4, MaxShownExplosions = 2 }); } @@ -82,18 +83,22 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.CloudToGroundFirst or AID.CloudToGroundRest) + if (spell.Action.ID is (uint)AID.CloudToGroundFirst or (uint)AID.CloudToGroundRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + ReportError($"Failed to find entry for {caster.InstanceID:X}"); } } } @@ -102,10 +107,10 @@ class Flame(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSp class Burn(BossModule module) : Components.GenericAOEs(module) { - private readonly List _flames = module.Enemies(OID.BallOfFire); + private readonly List _flames = module.Enemies((uint)OID.BallOfFire); private readonly List<(Actor actor, AOEInstance? aoe)> _casters = []; - private static readonly AOEShapeCircle _shape = new(8); + private static readonly AOEShapeCircle _shape = new(8f); public override IEnumerable ActiveAOEs(int slot, Actor actor) { @@ -115,7 +120,7 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) if (c.aoe == null) continue; if (deadline == default) - deadline = c.aoe.Value.Activation.AddSeconds(1); + deadline = c.aoe.Value.Activation.AddSeconds(1d); if (c.aoe.Value.Activation < deadline) yield return c.aoe.Value; } @@ -125,24 +130,24 @@ public override void Update() { foreach (var f in _flames.Where(f => f.ModelState.AnimState1 == 1 && _casters.FindIndex(c => c.actor == f) < 0)) { - _casters.Add((f, new(_shape, f.Position, default, WorldState.FutureTime(5)))); + _casters.Add((f, new(_shape, WPos.ClampToGrid(f.Position), default, WorldState.FutureTime(5d)))); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.Burn && _casters.FindIndex(f => f.actor == caster) is var index && index >= 0) + if (spell.Action.ID == (uint)AID.Burn && _casters.FindIndex(f => f.actor == caster) is var index && index >= 0) _casters[index] = (caster, null); } } -abstract class Lash(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCone(40, 90.Degrees())); +abstract class Lash(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCone(40f, 90f.Degrees())); class Forelash(BossModule module) : Lash(module, AID.Forelash); class Backlash(BossModule module) : Lash(module, AID.Backlash); class Charybdis(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.Charybdis), "Set hp to 1"); class Roar(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Roar)); -class Levinbolt(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.LevinboltAOE), 6); +class Levinbolt(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.LevinboltAOE), 6f); class SerpentsEdge(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.SerpentsEdge)); class CE62LooksToDieForStates : StateMachineBuilder @@ -166,4 +171,4 @@ public CE62LooksToDieForStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.BozjaCE, GroupID = 778, NameID = 30)] // bnpcname=9925 -public class CE62LooksToDieFor(WorldState ws, Actor primary) : BossModule(ws, primary, new(-200, -580), new ArenaBoundsCircle(20)); +public class CE62LooksToDieFor(WorldState ws, Actor primary) : BossModule(ws, primary, new(-200f, -580f), new ArenaBoundsCircle(20f)); diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel2Lyon/Duel2LyonGenericAttacks.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel2Lyon/Duel2LyonGenericAttacks.cs index f1d5d79a3a..295f5690bc 100644 --- a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel2Lyon/Duel2LyonGenericAttacks.cs +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel2Lyon/Duel2LyonGenericAttacks.cs @@ -15,36 +15,36 @@ public override void AddHints(int slot, Actor actor, TextHints hints) public override void OnStatusGain(Actor actor, ActorStatus status) { - if (actor == Module.PrimaryActor && (SID)status.ID == SID.Enaero) + if (actor == Module.PrimaryActor && status.ID == (uint)SID.Enaero) EnaeroBuff = true; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.RagingWinds1) + if (spell.Action.ID == (uint)AID.RagingWinds1) casting = true; } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.RagingWinds1) + if (spell.Action.ID == (uint)AID.RagingWinds1) casting = false; } public override void OnStatusLose(Actor actor, ActorStatus status) { - if (actor == Module.PrimaryActor && (SID)status.ID == SID.Enaero) + if (actor == Module.PrimaryActor && status.ID == (uint)SID.Enaero) EnaeroBuff = false; } } class HeartOfNatureConcentric(BossModule module) : Components.ConcentricAOEs(module, _shapes) { - private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10), new AOEShapeDonut(10, 20), new AOEShapeDonut(20, 30)]; + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10f), new AOEShapeDonut(10f, 20f), new AOEShapeDonut(20f, 30f)]; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.NaturesPulse1) + if (spell.Action.ID == (uint)AID.NaturesPulse1) AddSequence(spell.LocXZ, Module.CastFinishAt(spell)); } @@ -52,62 +52,63 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (Sequences.Count != 0) { - var order = (AID)spell.Action.ID switch + var order = spell.Action.ID switch { - AID.NaturesPulse1 => 0, - AID.NaturesPulse2 => 1, - AID.NaturesPulse3 => 2, + (uint)AID.NaturesPulse1 => 0, + (uint)AID.NaturesPulse2 => 1, + (uint)AID.NaturesPulse3 => 2, _ => -1 }; - AdvanceSequence(order, spell.LocXZ); + AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2d)); } } } -class TasteOfBlood(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TasteOfBlood), new AOEShapeCone(40, 90.Degrees())); +class TasteOfBlood(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TasteOfBlood), new AOEShapeCone(40f, 90f.Degrees())); class TasteOfBloodHint(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.TasteOfBlood), "Go behind Lyon!"); class RavenousGale(BossModule module) : Components.GenericAOEs(module) { - private bool activeTwister; + private static readonly AOEShapeCircle circle = new(1.5f); + private readonly List _aoes = []; private bool casting; - private DateTime _activation; - private static readonly AOEShapeCircle circle = new(0.5f); public override IEnumerable ActiveAOEs(int slot, Actor actor) { + var count = _aoes.Count; + if (!casting && count == 0) + return []; + List aoes = new(count + 1); if (casting) - yield return new(circle, actor.Position, default, _activation); - if (activeTwister) - foreach (var p in Module.Enemies(OID.RavenousGaleVoidzone)) - yield return new(circle, p.Position, default, _activation); + aoes.Add(new(circle, actor.Position, default)); + aoes.AddRange(_aoes); + return aoes; } public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.RavenousGaleVoidzone) - { - activeTwister = true; - casting = false; - _activation = WorldState.FutureTime(4.6f); - } + if (actor.OID == (uint)OID.RavenousGaleVoidzone) + _aoes.Add(new(circle, WPos.ClampToGrid(actor.Position), default, WorldState.FutureTime(4.6d))); } public override void OnActorDestroyed(Actor actor) { - if ((OID)actor.OID == OID.RavenousGaleVoidzone) - { - activeTwister = false; - casting = false; - } + if (actor.OID == (uint)OID.RavenousGaleVoidzone) + _aoes.RemoveAt(0); } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.RavenousGale) + if (spell.Action.ID == (uint)AID.RavenousGale) casting = true; } + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if (spell.Action.ID == (uint)AID.RavenousGale) + casting = false; + } + public override void AddGlobalHints(GlobalHints hints) { base.AddGlobalHints(hints); @@ -117,7 +118,7 @@ public override void AddGlobalHints(GlobalHints hints) } class TwinAgonies(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.TwinAgonies), "Heavy Tankbuster, use Manawall or tank mitigations"); -class WindsPeak(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WindsPeak1), 5); +class WindsPeak(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WindsPeak1), 5f); class WindsPeakKB(BossModule module) : Components.Knockback(module) { @@ -127,13 +128,15 @@ class WindsPeakKB(BossModule module) : Components.Knockback(module) public override IEnumerable Sources(int slot, Actor actor) { - if (watched && WorldState.CurrentTime < Time.AddSeconds(4.4f)) - yield return new(Module.PrimaryActor.Position, 15, _activation); + if (watched && WorldState.CurrentTime < Time.AddSeconds(4.4d)) + return [new(Module.PrimaryActor.Position, 15f, _activation)]; + else + return []; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.WindsPeak1) + if (spell.Action.ID == (uint)AID.WindsPeak1) { watched = true; Time = WorldState.CurrentTime; @@ -145,107 +148,90 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) class TheKingsNotice(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.TheKingsNotice)); class SplittingRage(BossModule module) : Components.TemporaryMisdirection(module, ActionID.MakeSpell(AID.SplittingRage)); -class NaturesBlood(BossModule module) : Components.Exaflare(module, 4) +class NaturesBlood(BossModule module) : Components.Exaflare(module, 4f) { - class LineWithActor : Line + public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - public Actor Caster; - - public LineWithActor(BossModule module, Actor caster) + if (spell.Action.ID == (uint)AID.NaturesBlood1) { - Next = caster.Position; - Advance = 6 * caster.Rotation.ToDirection(); - NextExplosion = module.CastFinishAt(caster.CastInfo!); - TimeToMove = 1.1f; //note the actual time between exaflare moves seems to vary by upto 100ms, but all 4 exaflares move at the same time - ExplosionsLeft = 7; - MaxShownExplosions = 3; - Caster = caster; + Lines.Add(new() { Next = caster.Position, Advance = 6f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 7, MaxShownExplosions = 3 }); } } - public override void OnCastStarted(Actor caster, ActorCastInfo spell) - { - if ((AID)spell.Action.ID is AID.NaturesBlood1) - Lines.Add(new LineWithActor(Module, caster)); - } - public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (Lines.Count > 0 && (AID)spell.Action.ID is AID.NaturesBlood1 or AID.NaturesBlood2) + if (spell.Action.ID is (uint)AID.NaturesBlood1 or (uint)AID.NaturesBlood2) { - var index = Lines.FindIndex(item => ((LineWithActor)item).Caster == caster); - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } class SpitefulFlameCircleVoidzone(BossModule module) : Components.GenericAOEs(module) { - private bool activeOrb; - private int casts; - private static readonly AOEShapeCircle circle = new(10); + private static readonly AOEShapeCircle circle = new(10f); + private readonly List _aoes = []; - public override IEnumerable ActiveAOEs(int slot, Actor actor) - { - if (activeOrb && casts <= 11 && casts != 0) - foreach (var p in Module.Enemies(OID.VermillionFlame)) - yield return new(circle, p.Position); - } + public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes; public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.VermillionFlame) - activeOrb = true; + if (actor.OID == (uint)OID.VermillionFlame) + _aoes.Add(new(circle, WPos.ClampToGrid(actor.Position))); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.SpitefulFlame1) - casts++; - if (casts == 12) + if (spell.Action.ID == (uint)AID.SpitefulFlame1) { - casts = 0; - activeOrb = false; + if (++NumCasts == 12) + { + NumCasts = 0; + _aoes.Clear(); + } } } } -class SpitefulFlameRect(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SpitefulFlame2), new AOEShapeRect(80, 2)); +class SpitefulFlameRect(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.SpitefulFlame2), new AOEShapeRect(80f, 2f)); -class DynasticFlame(BossModule module) : Components.BaitAwayTethers(module, new AOEShapeCircle(10), (uint)TetherID.fireorbs, centerAtTarget: true) +class DynasticFlame(BossModule module) : Components.BaitAwayTethers(module, new AOEShapeCircle(10f), (uint)TetherID.fireorbs, centerAtTarget: true) { - private ulong target; private int orbcount; - public override void OnCastStarted(Actor caster, ActorCastInfo spell) - { - if ((AID)spell.Action.ID == AID.DynasticFlame1) - target = spell.TargetID; - } - public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - base.AddAIHints(slot, actor, assignment, hints); - if (target == actor.InstanceID && CurrentBaits.Count != 0) - hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 18)); + if (CurrentBaits.Count != 0 && CurrentBaits[0].Target == actor) + hints.AddForbiddenZone(ShapeDistance.Circle(Arena.Center, 18f)); } public override void AddHints(int slot, Actor actor, TextHints hints) { - if (target == actor.InstanceID && CurrentBaits.Count != 0) + if (CurrentBaits.Count != 0 && CurrentBaits[0].Target == actor) hints.Add("Go to the edge and run until 4 orbs are spawned"); } public override void OnActorCreated(Actor actor) { - if ((OID)actor.OID == OID.VermillionFlame) - ++orbcount; - if (orbcount == 4) + if (actor.OID == (uint)OID.VermillionFlame) { - CurrentBaits.Clear(); - orbcount = 0; + if (++orbcount == 4) + { + CurrentBaits.Clear(); + orbcount = 0; + } } } } diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs index 2c2e048554..390ddeaaf1 100644 --- a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/GigaTempest.cs @@ -1,13 +1,11 @@ namespace BossMod.Shadowbringers.Foray.Duel.Duel5Menenius; -abstract class GigaTempest(BossModule module, AOEShapeRect shape, AID aidFirst, AID aidRest) : Components.Exaflare(module, shape) +abstract class GigaTempest(BossModule module, AOEShapeRect shape, uint aidFirst, uint aidRest) : Components.Exaflare(module, shape) { - private readonly AID _aidStart = aidFirst; - private readonly AID _aidRest = aidRest; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == _aidStart) + if (spell.Action.ID == aidFirst) { var advance = GetExaDirection(caster); if (advance == null) @@ -27,14 +25,21 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (Lines.Count > 0 && (AID)spell.Action.ID == _aidStart || (AID)spell.Action.ID == _aidRest) + if (spell.Action.ID == aidFirst || spell.Action.ID == aidRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } @@ -43,22 +48,21 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) private static 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 (caster.Position.Z == 536f) + forwardAngle = 180f.Degrees(); + else if (caster.Position.Z == 504f) + forwardAngle = default; + else if (caster.Position.X == -82f) + forwardAngle = 90f.Degrees(); + else if (caster.Position.X == -794f) + forwardAngle = 270f.Degrees(); if (forwardAngle == null) return null; - const float _advanceDistance = 8; - return _advanceDistance * forwardAngle.Value.ToDirection(); + return 8f * forwardAngle.Value.ToDirection(); } } -class SmallGigaTempest(BossModule module) : GigaTempest(module, new AOEShapeRect(10, 6.5f), AID.GigaTempestSmallStart, AID.GigaTempestSmallMove); -class LargeGigaTempest(BossModule module) : GigaTempest(module, new AOEShapeRect(35, 6.5f), AID.GigaTempestLargeStart, AID.GigaTempestLargeMove); +class SmallGigaTempest(BossModule module) : GigaTempest(module, new AOEShapeRect(10f, 6.5f), (uint)AID.GigaTempestSmallStart, (uint)AID.GigaTempestSmallMove); +class LargeGigaTempest(BossModule module) : GigaTempest(module, new AOEShapeRect(35f, 6.5f), (uint)AID.GigaTempestLargeStart, (uint)AID.GigaTempestLargeMove); diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs index 21d5562881..288a21fbaf 100644 --- a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel5Menenius/Ruination.cs @@ -25,40 +25,33 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) } } -class RuinationExaflare(BossModule module) : Components.Exaflare(module, 4) +class RuinationExaflare(BossModule module) : Components.Exaflare(module, 4f) { - class LineWithActor : Line + public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - public Actor Caster; - - public LineWithActor(BossModule module, Actor caster) + if (spell.Action.ID == (uint)AID.RuinationExaStart) { - Next = caster.Position; - Advance = 4 * caster.Rotation.ToDirection(); - NextExplosion = module.CastFinishAt(caster.CastInfo!); - TimeToMove = 1.1f; - ExplosionsLeft = 6; - MaxShownExplosions = 7; - Caster = caster; + Lines.Add(new() { Next = caster.Position, Advance = 4f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 6, MaxShownExplosions = 6 }); } } - public override void OnCastStarted(Actor caster, ActorCastInfo spell) - { - if ((AID)spell.Action.ID is AID.RuinationExaStart) - Lines.Add(new LineWithActor(Module, caster)); - } - public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (Lines.Count > 0 && (AID)spell.Action.ID is AID.RuinationExaStart or AID.RuinationExaMove) + if (spell.Action.ID is (uint)AID.RuinationExaStart or (uint)AID.RuinationExaMove) { - var index = Lines.FindIndex(item => ((LineWithActor)item).Caster == caster); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } diff --git a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6LyonGenericAttacks.cs b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6LyonGenericAttacks.cs index 04df9e19a6..f001c9d801 100644 --- a/BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6LyonGenericAttacks.cs +++ b/BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6LyonGenericAttacks.cs @@ -9,31 +9,31 @@ public override void AddHints(int slot, Actor actor, TextHints hints) { if (_isCasting) hints.Add("Applies On Fire to Lyon. Use Dispell to remove it"); - if (_hasBuff) + else if (_hasBuff) hints.Add("Lyon has 'On Fire'. Use Dispell to remove it!"); } public override void OnStatusGain(Actor actor, ActorStatus status) { - if (actor == Module.PrimaryActor && (SID)status.ID == SID.OnFire) + if (actor == Module.PrimaryActor && status.ID == (uint)SID.OnFire) _hasBuff = true; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.HarnessFire) + if (spell.Action.ID == (uint)AID.HarnessFire) _isCasting = true; } public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.HarnessFire) + if (spell.Action.ID == (uint)AID.HarnessFire) _isCasting = false; } public override void OnStatusLose(Actor actor, ActorStatus status) { - if (actor == Module.PrimaryActor && (SID)status.ID == SID.OnFire) + if (actor == Module.PrimaryActor && status.ID == (uint)SID.OnFire) _hasBuff = false; } } @@ -44,7 +44,7 @@ class HeavenAndEarth(BossModule module) : Components.GenericRotatingAOE(module) { private Angle _increment; - private static readonly AOEShapeCone _shape = new(20, 15.Degrees()); + private static readonly AOEShapeCone _shape = new(20f, 15f.Degrees()); private int _index; @@ -53,25 +53,23 @@ private void UpdateIncrement(Angle increment) _increment = increment; for (var i = 0; i < Sequences.Count; ++i) { - var sequence = Sequences[i]; - sequence.Increment = _increment; - Sequences[i] = sequence; + Sequences[i] = Sequences[i] with { Increment = _increment }; } } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.HeavenAndEarthCW) + if (spell.Action.ID == (uint)AID.HeavenAndEarthCW) UpdateIncrement(-30.Degrees()); - else if ((AID)spell.Action.ID == AID.HeavenAndEarthCCW) + else if (spell.Action.ID == (uint)AID.HeavenAndEarthCCW) UpdateIncrement(30.Degrees()); - else if ((AID)spell.Action.ID == AID.HeavenAndEarthStart) - Sequences.Add(new(_shape, caster.Position, spell.Rotation, _increment, Module.CastFinishAt(spell), 1.2f, 4)); + else if (spell.Action.ID == (uint)AID.HeavenAndEarthStart) + Sequences.Add(new(_shape, spell.LocXZ, spell.Rotation, _increment, Module.CastFinishAt(spell), 1.2f, 4)); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID == AID.HeavenAndEarthMove && Sequences.Count > 0) + if (spell.Action.ID == (uint)AID.HeavenAndEarthMove) AdvanceSequence(++_index % Sequences.Count, WorldState.CurrentTime); } } @@ -82,7 +80,7 @@ class HeartOfNatureConcentric(BossModule module) : Components.ConcentricAOEs(mod public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.NaturesPulse1) + if (spell.Action.ID == (uint)AID.NaturesPulse1) AddSequence(spell.LocXZ, Module.CastFinishAt(spell)); } @@ -90,21 +88,21 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if (Sequences.Count != 0) { - var order = (AID)spell.Action.ID switch + var order = spell.Action.ID switch { - AID.NaturesPulse1 => 0, - AID.NaturesPulse2 => 1, - AID.NaturesPulse3 => 2, + (uint)AID.NaturesPulse1 => 0, + (uint)AID.NaturesPulse2 => 1, + (uint)AID.NaturesPulse3 => 2, _ => -1 }; - AdvanceSequence(order, spell.LocXZ); + AdvanceSequence(order, spell.LocXZ, WorldState.FutureTime(2d)); } } } -class CagedHeartOfNature(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.CagedHeartOfNature), 6); +class CagedHeartOfNature(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.CagedHeartOfNature), 6f); -class WindsPeak(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WindsPeak1), 5); +class WindsPeak(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.WindsPeak1), 5f); class WindsPeakKB(BossModule module) : Components.Knockback(module) { @@ -114,13 +112,15 @@ class WindsPeakKB(BossModule module) : Components.Knockback(module) public override IEnumerable Sources(int slot, Actor actor) { - if (_watched && WorldState.CurrentTime < _time.AddSeconds(4.4f)) - yield return new(Module.PrimaryActor.Position, 15, _activation); + if (_watched && WorldState.CurrentTime < _time.AddSeconds(4.4d)) + return [new(Module.PrimaryActor.Position, 15f, _activation)]; + else + return []; } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.WindsPeak1) + if (spell.Action.ID == (uint)AID.WindsPeak1) { _watched = true; _time = WorldState.CurrentTime; @@ -131,43 +131,38 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) class SplittingRage(BossModule module) : Components.TemporaryMisdirection(module, ActionID.MakeSpell(AID.SplittingRage)); -class NaturesBlood(BossModule module) : Components.Exaflare(module, 4) +class NaturesBlood(BossModule module) : Components.Exaflare(module, 4f) { - class LineWithActor : Line + public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - public Actor Caster; - - public LineWithActor(BossModule module, Actor caster) + if (spell.Action.ID == (uint)AID.NaturesBlood1) { - Next = caster.Position; - Advance = 6 * caster.Rotation.ToDirection(); - NextExplosion = module.CastFinishAt(caster.CastInfo); - TimeToMove = 1.1f; - ExplosionsLeft = 7; - MaxShownExplosions = 3; - Caster = caster; + Lines.Add(new() { Next = caster.Position, Advance = 6f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 7, MaxShownExplosions = 3 }); } } - public override void OnCastStarted(Actor caster, ActorCastInfo spell) - { - if ((AID)spell.Action.ID is AID.NaturesBlood1) - Lines.Add(new LineWithActor(Module, caster)); - } - public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (Lines.Count > 0 && (AID)spell.Action.ID is AID.NaturesBlood1 or AID.NaturesBlood2) + if (spell.Action.ID is (uint)AID.NaturesBlood1 or (uint)AID.NaturesBlood2) { - var index = Lines.FindIndex(item => ((LineWithActor)item).Caster == caster); - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } -class MoveMountains(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.MoveMountains3), new AOEShapeRect(40, 3)) +class MoveMountains(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.MoveMountains3), new AOEShapeRect(40f, 3f)) { // TODO predict rotation } diff --git a/BossMod/Modules/Shadowbringers/Quest/Job/Dancer/SaveTheLastDanceForMe.cs b/BossMod/Modules/Shadowbringers/Quest/Job/Dancer/SaveTheLastDanceForMe.cs index 7f81b119ae..7cf59f58a9 100644 --- a/BossMod/Modules/Shadowbringers/Quest/Job/Dancer/SaveTheLastDanceForMe.cs +++ b/BossMod/Modules/Shadowbringers/Quest/Job/Dancer/SaveTheLastDanceForMe.cs @@ -16,33 +16,59 @@ public enum AID : uint BitterLove = 15650, // 2AC9->self, 3.0s cast, range 12 120-degree cone } -class Dread(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Dread), 5); -class BitterLove(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.BitterLove), new AOEShapeCone(12, 60.Degrees())); -class WhelmingLoss(BossModule module) : Components.Exaflare(module, new AOEShapeCircle(5)) +class Dread(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Dread), 5f); +class BitterLove(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.BitterLove), new AOEShapeCone(12f, 60f.Degrees())); +class WhelmingLoss(BossModule module) : Components.Exaflare(module, 5f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if (spell.Action.ID == (uint)AID.WhelmingLossFirst) - Lines.Add(new() { Next = spell.LocXZ, Advance = spell.Rotation.ToDirection() * 5, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 7, MaxShownExplosions = 3 }); + Lines.Add(new() { Next = caster.Position, Advance = 5f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1f, ExplosionsLeft = 7, MaxShownExplosions = 3 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { if (spell.Action.ID is (uint)AID.WhelmingLossFirst or (uint)AID.WhelmingLossRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } class Adds(BossModule module) : Components.Adds(module, (uint)OID.ShadowySpume); -class Anguish(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Anguish), 6); +class Anguish(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.Anguish), 6f); -class ForebodingAura(BossModule module) : Components.PersistentVoidzone(module, 8, m => m.Enemies(OID.ForebodingAura).Where(e => !e.IsDead)); +class ForebodingAura(BossModule module) : Components.PersistentVoidzone(module, 8f, GetVoidzones) +{ + private static Actor[] GetVoidzones(BossModule module) + { + var enemies = module.Enemies((uint)OID.ForebodingAura); + var count = enemies.Count; + if (count == 0) + return []; + + var voidzones = new Actor[count]; + var index = 0; + for (var i = 0; i < count; ++i) + { + var z = enemies[i]; + if (!z.IsDead) + voidzones[index++] = z; + } + return voidzones[..index]; + } +} class AethericShadowStates : StateMachineBuilder { @@ -63,7 +89,7 @@ public AethericShadowStates(BossModule module) : base(module) { protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if (actor.FindStatus(DNC.SID.ClosedPosition) == null && Raid.WithoutSlot(false, false).Exclude(actor).FirstOrDefault() is Actor partner) + if (actor.FindStatus((uint)DNC.SID.ClosedPosition) == null && Raid.WithoutSlot(false, false).Exclude(actor).FirstOrDefault() is Actor partner) { hints.ActionsToExecute.Push(ActionID.MakeSpell(DNC.AID.ClosedPosition), partner, ActionQueue.Priority.VeryHigh); } diff --git a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Ardbert.cs b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Ardbert.cs index c32559efa3..d427f55993 100644 --- a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Ardbert.cs +++ b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Ardbert.cs @@ -1,47 +1,119 @@ -namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories; +namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories.Ardbert; -class Overcome(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Overcome), new AOEShapeCone(8, 60.Degrees()), 2); -class Skydrive(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Skydrive), 5); +public enum OID : uint +{ + Boss = 0x2F2E, // R0.5 + Helper = 0x233C +} + +public enum AID : uint +{ + AutoAttack = 871, // Boss->player, no cast, single-target + Teleport = 21128, // Boss->location, no cast, single-target + HeavySwing = 21123, // Boss->player, no cast, single-target + Maim = 21124, // Boss->player, no cast, single-target + Stormwind = 21125, // Boss->player, no cast, single-target + + Overcome = 21126, // Boss->self, 2.5s cast, range 8 120-degree cone + Skydrive = 21127, // Boss->self, 2.5s cast, range 5 circle + + SkyHighDriveCW = 21138, // Boss->self, 4.5s cast, single-target + SkyHighDriveCCW = 21139, // Boss->self, 4.5s cast, single-target + SkyHighDriveFirst = 21140, // Helper->self, 5.0s cast, range 40 width 8 rect + SkyHighDriveRest = 21141, // Helper->self, no cast, range 40 width 8 rect + SkyHighDriveVisual = 21564, // Boss->self, no cast, single-target + + AvalancheAxeVisual = 21142, // Boss->self, 9.0s cast, single-target + AvalanceAxe1 = 21145, // Helper->self, 4.0s cast, range 10 circle + AvalanceAxe2 = 21144, // Helper->self, 7.0s cast, range 10 circle + AvalanceAxe3 = 21143, // Helper->self, 10.0s cast, range 10 circle + + OvercomeAllOddsVisual = 21129, // Boss->self, 3.0s cast, single-target + OvercomeAllOdds = 21130, // Helper->self, 2.5s cast, range 60 30-degree cone + SoulflashVisual = 21135, // Boss->self, 4.0s cast, single-target + Soulflash1 = 21136, // Helper->self, 4.0s cast, range 4 circle + EtesianAxe = 21147, // Helper->self, 6.5s cast, range 80 circle + Soulflash2 = 21137, // Helper->self, 4.0s cast, range 8 circle + + GroundbreakerVisualExa = 21150, // Boss->self, 5.0s cast, single-target + GroundbreakerVisualCone = 21152, // Boss->self, 3.0s cast, single-target + GroundbreakerVisualDonut = 21156, // Boss->self, 3.0s cast, single-target + GroundbreakerVisualCircle = 21154, // Boss->self, 3.0s cast, single-target + GroundbreakerExaFirst = 21563, // Helper->self, 5.0s cast, range 6 circle + GroundbreakerExaRest = 21151, // Helper->self, no cast, range 6 circle + GroundbreakerCone = 21153, // Helper->self, 6.0s cast, range 40 90-degree cone + GroundbreakerDonut = 21157, // Helper->self, 6.0s cast, range 5-20 donut + GroundbreakerCircle = 21155, // Helper->self, 6.0s cast, range 15 circle + + SoulsRelease = 21132, // Boss->self, 18.0s cast, single-target + Shockwave1 = 21133, // Helper->self, no cast, range 40 circle + Shockwave2 = 21134, // Helper->self, no cast, range 40 circle + + LandsOfOldVisual = 21148, // Boss->self, 5.0s cast, single-target + LandsOfOld = 21149 // Helper->self, 5.0s cast, range 60 circle +} + +public enum IconID : uint +{ + RotateCCW = 168, // Boss + RotateCW = 167 // Boss +} + +class Overcome(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Overcome), new AOEShapeCone(8f, 60f.Degrees()), 2); +class Skydrive(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Skydrive), 5f); +class LandsOfOld(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.LandsOfOld)); class SkyHighDrive(BossModule module) : Components.GenericRotatingAOE(module) { - Angle angle; - private static readonly AOEShapeRect rect = new(40, 4); + private Angle _increment; + private DateTime _activation; + private readonly List _rotation = new(2); - public override void OnCastStarted(Actor caster, ActorCastInfo spell) + private static readonly AOEShapeRect rect = new(40f, 4f); + + public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { - switch ((AID)spell.Action.ID) + _increment = iconID switch { - case AID.SkyHighDriveCCW: - angle = -20.Degrees(); - return; - case AID.SkyHighDriveCW: - angle = 20.Degrees(); - return; - case AID.SkyHighDriveFirst: - if (angle != default) - { - Sequences.Add(new(rect, spell.LocXZ, spell.Rotation, angle, Module.CastFinishAt(spell, 0.5f), 0.6f, 10, 4)); - } - break; + (uint)IconID.RotateCW => -20f.Degrees(), + (uint)IconID.RotateCCW => 20f.Degrees(), + _ => default + }; + InitIfReady(actor); + } + + private void InitIfReady(Actor source) + { + if (_rotation.Count == 2 && _increment != default) + { + for (var i = 0; i < 2; ++i) + Sequences.Add(new(rect, WPos.ClampToGrid(source.Position), _rotation[i], _increment, _activation, 0.6f, 10, 4)); + _rotation.Clear(); + _increment = default; } } - public override void OnEventCast(Actor caster, ActorCastEvent spell) + public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.SkyHighDriveFirst or AID.SkyHighDriveRest) + if (spell.Action.ID == (uint)AID.SkyHighDriveFirst) { - AdvanceSequence(caster.Position, caster.Rotation, WorldState.CurrentTime); - if (Sequences.Count == 0) - angle = default; + _rotation.Add(spell.Rotation); + _activation = Module.CastFinishAt(spell); + InitIfReady(caster); } } + + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if (spell.Action.ID is (uint)AID.SkyHighDriveFirst or (uint)AID.SkyHighDriveRest) + AdvanceSequence(caster.Position, spell.Rotation, WorldState.CurrentTime); + } } -class AvalancheAxe1(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.AvalanceAxe1), 10); -class AvalancheAxe2(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.AvalanceAxe2), 10); -class AvalancheAxe3(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.AvalanceAxe3), 10); -class OvercomeAllOdds(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.OvercomeAllOdds), new AOEShapeCone(60, 15.Degrees()), 1) +class AvalancheAxe1(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.AvalanceAxe1), 10f); +class AvalancheAxe2(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.AvalanceAxe2), 10f); +class AvalancheAxe3(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.AvalanceAxe3), 10f); +class OvercomeAllOdds(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.OvercomeAllOdds), new AOEShapeCone(60f, 15f.Degrees()), 1) { public override void OnCastFinished(Actor caster, ActorCastInfo spell) { @@ -50,43 +122,51 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) MaxCasts = 2; } } -class Soulflash(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Soulflash1), 4); -class EtesianAxe(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.EtesianAxe), 15, kind: Kind.DirForward); -class Soulflash2(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Soulflash2), 8); +class Soulflash(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Soulflash1), 4f); +class EtesianAxe(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.EtesianAxe), 15f, kind: Kind.DirForward); +class Soulflash2(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Soulflash2), 8f); -class GroundbreakerExaflares(BossModule module) : Components.Exaflare(module, new AOEShapeCircle(6)) +class GroundbreakerExaflares(BossModule module) : Components.Exaflare(module, 6f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.GroundbreakerExaFirst) + if (spell.Action.ID == (uint)AID.GroundbreakerExaFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = spell.Rotation.ToDirection() * 6, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 8, MaxShownExplosions = 3 }); + Lines.Add(new() { Next = caster.Position, Advance = 6f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1f, ExplosionsLeft = 8, MaxShownExplosions = 3 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.GroundbreakerExaFirst or AID.GroundbreakerExaRest) + if (spell.Action.ID is (uint)AID.GroundbreakerExaFirst or (uint)AID.GroundbreakerExaRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index < 0) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } -class GroundbreakerCone(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GroundbreakerCone), new AOEShapeCone(40, 45.Degrees())); -class GroundbreakerDonut(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GroundbreakerDonut), new AOEShapeDonut(5, 20)); -class GroundbreakerCircle(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GroundbreakerCircle), new AOEShapeCircle(15)); +class GroundbreakerCone(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GroundbreakerCone), new AOEShapeCone(40f, 45f.Degrees())); +class GroundbreakerDonut(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GroundbreakerDonut), new AOEShapeDonut(5f, 20f)); +class GroundbreakerCircle(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.GroundbreakerCircle), 15f); class ArdbertStates : StateMachineBuilder { public ArdbertStates(BossModule module) : base(module) { - TrivialPhase(0) + TrivialPhase() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() @@ -104,5 +184,5 @@ public ArdbertStates(BossModule module) : base(module) } } -[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 8258, PrimaryActorOID = (uint)OID.Ardbert)] -public class Ardbert(WorldState ws, Actor primary) : BossModule(ws, primary, new(-392, 780), new ArenaBoundsCircle(20)); +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 8258)] +public class Ardbert(WorldState ws, Actor primary) : BossModule(ws, primary, new(-392f, 780f), new ArenaBoundsCircle(20f)); diff --git a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FadedMemories.cs b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FadedMemories.cs deleted file mode 100644 index 8944c958e7..0000000000 --- a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FadedMemories.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories; - -public enum OID : uint -{ - KingThordan = 0x2F1D, - FlameGeneralAldynn = 0x2F1E, - Nidhogg = 0x2F21, - Zenos = 0x2F28, - Ardbert = 0x2F2E, - Helper = 0x233C -} - -public enum AID : uint -{ - // raubahn - FlamingTizona = 21094, // player->location, 4.0s cast, range 6 circle - - // thordan - TheDragonsGaze = 21090, // 2F1D->self, 4.0s cast, range 80 circle - - // nidhogg - HighJump = 21299, // player->self, 4.0s cast, range 8 circle - Geirskogul = 21098, // 2F22/2F21->self, 4.0s cast, range 62 width 8 rect - - // zenos - EntropicFlame = 21117, // Helper->self, 5.0s cast, range 50 width 8 rect - VeinSplitter = 21118, // 2F29->self, 5.0s cast, range 10 circle - - // ardbert - Overcome = 21126, // Ardbert->self, 2.5s cast, range 8 120-degree cone - Skydrive = 21127, // Ardbert->self, 2.5s cast, range 5 circle - SkyHighDriveCCW = 21138, // Ardbert->self, 4.5s cast, single-target - SkyHighDriveCW = 21139, // Ardbert->self, 4.5s cast, single-target - SkyHighDriveFirst = 21140, // 233C->self, 5.0s cast, range 40 width 8 rect - SkyHighDriveRest = 21141, // 233C->self, no cast, range 40 width 8 rect - AvalanceAxe1 = 21145, // 233C->self, 4.0s cast, range 10 circle - AvalanceAxe2 = 21144, // 233C->self, 7.0s cast, range 10 circle - AvalanceAxe3 = 21143, // 233C->self, 10.0s cast, range 10 circle - OvercomeAllOdds = 21130, // 233C->self, 2.5s cast, range 60 30-degree cone - Soulflash1 = 21136, // 233C->self, 4.0s cast, range 4 circle - EtesianAxe = 21147, // 233C->self, 6.5s cast, range 80 circle - Soulflash2 = 21137, // 233C->self, 4.0s cast, range 8 circle - GroundbreakerExaFirst = 21563, // 233C->self, 5.0s cast, range 6 circle - GroundbreakerExaRest = 21151, // 233C->self, no cast, range 6 circle - GroundbreakerCone = 21153, // 233C->self, 6.0s cast, range 40 90-degree cone - GroundbreakerDonut = 21157, // 233C->self, 6.0s cast, range 5-20 donut - GroundbreakerCircle = 21155, // 233C->self, 6.0s cast, range 15 circle -} - -public enum SID : uint -{ - Invincibility = 671 -} diff --git a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FlameGeneralAldynn.cs b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FlameGeneralAldynn.cs index 16c9ff7bfe..593c2832dd 100644 --- a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FlameGeneralAldynn.cs +++ b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/FlameGeneralAldynn.cs @@ -1,19 +1,43 @@ -namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories; +namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories.Aldynn; -class FlamingTizona(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FlamingTizona), 6); +public enum OID : uint +{ + Boss = 0x2F1E, // R0.59 + Lucia = 0x2F20, // R0.5 + Aymeric = 0x2F1F // R0.5 +} + +public enum AID : uint +{ + AutoAttack = 6497, // Lucia/Aymeric/Boss/->player, no cast, single-target + Teleport = 21092, // Boss->location, no cast, single-target + + FlamingTizonaVisual = 21093, // Boss->self, 4.0s cast, single-target + FlamingTizona = 21094, // player->location, 4.0s cast, range 6 circle + HolyBladedance = 21096 // Lucia->self, 4.0s cast, range 5 width 3 rect +} + +class FlamingTizona(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FlamingTizona), 6f); +class HolyBladedance(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.HolyBladedance), new AOEShapeRect(5f, 1.5f)); class FlameGeneralAldynnStates : StateMachineBuilder { public FlameGeneralAldynnStates(BossModule module) : base(module) { TrivialPhase() - .ActivateOnEnter(); + .ActivateOnEnter() + .ActivateOnEnter(); } } -[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 4739, PrimaryActorOID = (uint)OID.FlameGeneralAldynn)] -public class FlameGeneralAldynn(WorldState ws, Actor primary) : BossModule(ws, primary, new(-143, 357), new ArenaBoundsCircle(20)) +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 4739)] +public class FlameGeneralAldynn(WorldState ws, Actor primary) : BossModule(ws, primary, new(-143f, 357f), new ArenaBoundsCircle(20f)) { - protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + public static readonly uint[] opponents = [(uint)OID.Boss, (uint)OID.Lucia, (uint)OID.Aymeric]; + + protected override void DrawEnemies(int pcSlot, Actor pc) + { + Arena.Actors(Enemies(opponents)); + } } diff --git a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/KingThordan.cs b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/KingThordan.cs index 45b8e8a317..4dc14555c8 100644 --- a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/KingThordan.cs +++ b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/KingThordan.cs @@ -1,27 +1,61 @@ -namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories; +namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories.KingThordan; + +public enum OID : uint +{ + Boss = 0x2F1D, // 3.8 + SerGrinnaux = 0x2F1A, // R2.2 + SerZephirin = 0x2F1C, // R2.2 + SerCharibert = 0x2F1B // R2.2 +} + +public enum AID : uint +{ + AutoAttack1 = 6497, // SerGrinnaux/SerZephirin->player, no cast, single-target + AutoAttack2 = 21399, // Boss->player, no cast, single-target + + Fire = 20613, // SerCharibert->player, 1.0s cast, single-target + SacredCross = 21089, // SerZephirin->self, 5.0s cast, range 80 circle + AltarCandle = 21088, // SerCharibert->player, no cast, single-target + AscalonsMight = 21091, // Boss->self, no cast, range 8 90-degree cone + TheDragonsGaze = 21090, // Boss->self, 4.0s cast, range 80 circle + HyperdimensionalSlash = 21086 // SerGrinnaux->self, 3.5s cast, range 45 width 8 rect +} + +public enum SID : uint +{ + Invincibility = 671 +} class DragonsGaze(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.TheDragonsGaze)); +class HyperdimensionalSlash(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.HyperdimensionalSlash), new AOEShapeRect(45f, 4f)); class KingThordanStates : StateMachineBuilder { public KingThordanStates(BossModule module) : base(module) { TrivialPhase() - .ActivateOnEnter(); + .ActivateOnEnter() + .ActivateOnEnter(); } } -[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 3632, PrimaryActorOID = (uint)OID.KingThordan)] -public class KingThordan(WorldState ws, Actor primary) : BossModule(ws, primary, new(-247, 321), new ArenaBoundsCircle(20)) +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 3632)] +public class KingThordan(WorldState ws, Actor primary) : BossModule(ws, primary, new(-247f, 321f), new ArenaBoundsCircle(20f)) { - protected override void DrawEnemies(int pcSlot, Actor pc) => Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + public static readonly uint[] opponents = [(uint)OID.Boss, (uint)OID.SerGrinnaux, (uint)OID.SerZephirin, (uint)OID.SerCharibert]; + + protected override void DrawEnemies(int pcSlot, Actor pc) + { + Arena.Actors(Enemies(opponents)); + } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - for (var i = 0; i < hints.PotentialTargets.Count; ++i) + var count = hints.PotentialTargets.Count; + for (var i = 0; i < count; ++i) { var h = hints.PotentialTargets[i]; - h.Priority = h.Actor.FindStatus(SID.Invincibility) == null ? 1 : 0; + h.Priority = h.Actor.FindStatus((uint)SID.Invincibility) == null ? 1 : AIHints.Enemy.PriorityInvincible; } } } diff --git a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Nidhogg.cs b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Nidhogg.cs index 9a40b095ad..b8a24b0ed3 100644 --- a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Nidhogg.cs +++ b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Nidhogg.cs @@ -1,7 +1,22 @@ -namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories; +namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories.Nidhogg; -class HighJump(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.HighJump), 8); -class Geirskogul(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Geirskogul), 62, 4); +public enum OID : uint +{ + Boss = 0x2F21, // R2.7 + Clone = 0x2F22 // R2.7 +} + +public enum AID : uint +{ + AutoAttack = 6498, // Boss->player, no cast, single-target + + HighJumpVisual = 21099, // Clone/Boss->self, 4.0s cast, single-target + HighJump = 21299, // player->self, 4.0s cast, range 8 circle + Geirskogul = 21098 // Clone/Boss->self, 4.0s cast, range 62 width 8 rect +} + +class HighJump(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.HighJump), 8f); +class Geirskogul(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.Geirskogul), new AOEShapeRect(62f, 4f)); class NidhoggStates : StateMachineBuilder { @@ -13,5 +28,5 @@ public NidhoggStates(BossModule module) : base(module) } } -[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 3458, PrimaryActorOID = (uint)OID.Nidhogg)] +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 3458)] public class Nidhogg(WorldState ws, Actor primary) : BossModule(ws, primary, new(-242, 436.5f), new ArenaBoundsCircle(20)); diff --git a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Zenos.cs b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Zenos.cs index 1d8f589817..b887886126 100644 --- a/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Zenos.cs +++ b/BossMod/Modules/Shadowbringers/Quest/MSQ/FadedMemories/Zenos.cs @@ -1,20 +1,62 @@ -namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories; +namespace BossMod.Shadowbringers.Quest.MSQ.FadedMemories.Zenos; -class Swords(BossModule module) : Components.AddsMulti(module, [0x2F2A, 0x2F2B, 0x2F2C]); +public enum OID : uint +{ + Boss = 0x2F28, // R0.92 + SpecterOfZenos = 0x2F29, // R0.92 + TheStorm = 0x2F2B, // R3.0 + TheSwell = 0x2F2A, // R3.0 + AmeNoHabakiri = 0x2F2C, // R3.0 + DimensionalTear = 0x2F2D // R1.5 +} -class EntropicFlame(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EntropicFlame), new AOEShapeRect(50, 4)); -class VeinSplitter(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.VeinSplitter), 10); +public enum AID : uint +{ + EntropicFlameVisual = 21116, // SpecterOfZenos->self, 5.0s cast, single-target + EntropicFlame = 21117, // Helper->self, 5.0s cast, range 50 width 8 rect + VeinSplitter = 21118, // SpecterOfZenos->self, 5.0s cast, range 10 circle + + TheFinalArtVisual = 21120, // Boss->self, no cast, single-target + TheFinalArt = 21121, // player->self, 7.0s cast, range 100 circle + SwordDespawn = 21333, // TheSwell/TheStorm/AmeNoHabakiri->self, no cast, single-target + Darkblight = 21122 // DimensionalTear->self, no cast, range 100 circle +} + +class EntropicFlame(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EntropicFlame), new AOEShapeRect(50f, 4f)); +class VeinSplitter(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.VeinSplitter), 10f); +class TheFinalArt(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.TheFinalArt)); class ZenosYaeGalvusStates : StateMachineBuilder { public ZenosYaeGalvusStates(BossModule module) : base(module) { TrivialPhase() - .ActivateOnEnter() .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter(); } } -[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 6039, PrimaryActorOID = (uint)OID.Zenos)] -public class ZenosYaeGalvus(WorldState ws, Actor primary) : BossModule(ws, primary, new(-321.03f, 617.73f), new ArenaBoundsCircle(20)); +[ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69311, NameID = 6039)] +public class ZenosYaeGalvus(WorldState ws, Actor primary) : BossModule(ws, primary, new(-321.03f, 617.73f), new ArenaBoundsCircle(20)) +{ + public static readonly uint[] swords = [(uint)OID.TheStorm, (uint)OID.TheSwell, (uint)OID.AmeNoHabakiri]; + + protected override void DrawEnemies(int pcSlot, Actor pc) + { + Arena.Actors(Enemies(swords)); + } + + protected override bool CheckPull() + { + var enemies = Enemies(swords); + var count = enemies.Count; + for (var i = 0; i < count; ++i) + { + var enemy = enemies[i]; + if (enemy.InCombat) + return true; + } + return false; + } +} diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs index 521e9d37c3..2a8dcc2054 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs @@ -46,17 +46,13 @@ class PelagicCleaverRotation(BossModule module) : Components.GenericRotatingAOE( public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { - var increment = iconID switch + _increment = iconID switch { (uint)IconID.RotateCW => -60.Degrees(), (uint)IconID.RotateCCW => 60.Degrees(), _ => default }; - if (increment != default) - { - _increment = increment; - InitIfReady(actor); - } + InitIfReady(actor); } public override void OnCastStarted(Actor caster, ActorCastInfo spell) @@ -79,7 +75,7 @@ private void InitIfReady(Actor source) { if (_rotation != default && _increment != default) { - Sequences.Add(new(_shape, source.Position, _rotation, _increment, _activation, 2.1f, 6)); + Sequences.Add(new(_shape, WPos.ClampToGrid(source.Position), _rotation, _increment, _activation, 2.1f, 6)); _rotation = default; _increment = default; } diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs index 0e9f487b63..0c15df65b4 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs @@ -59,14 +59,21 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (Lines.Count != 0 && spell.Action.ID == (uint)AID.SweepRest) + if (spell.Action.ID == (uint)AID.SweepRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index == -1) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } diff --git a/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/CloudToGround.cs b/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/CloudToGround.cs index 8f604bd719..aa58b833c9 100644 --- a/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/CloudToGround.cs +++ b/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/CloudToGround.cs @@ -15,12 +15,19 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) { if (spell.Action.ID is (uint)AID.CloudToGroundFirst or (uint)AID.CloudToGroundRest) { - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1f)); - if (index == -1) - return; - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } diff --git a/BossMod/Modules/Stormblood/Quest/MSQ/ARequiemForHeroes/P2.cs b/BossMod/Modules/Stormblood/Quest/MSQ/ARequiemForHeroes/P2.cs index 1637b906d4..6e7aea1efc 100644 --- a/BossMod/Modules/Stormblood/Quest/MSQ/ARequiemForHeroes/P2.cs +++ b/BossMod/Modules/Stormblood/Quest/MSQ/ARequiemForHeroes/P2.cs @@ -4,51 +4,72 @@ class StormUnbound(BossModule module) : Components.Exaflare(module, 5) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID == AID.TheStormUnboundCast) - Lines.Add(new() { Next = spell.LocXZ, Advance = caster.Rotation.ToDirection() * 5, NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 4, MaxShownExplosions = 2 }); + if (spell.Action.ID == (uint)AID.TheStormUnboundCast) + Lines.Add(new() { Next = caster.Position, Advance = 5f * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1f, ExplosionsLeft = 4, MaxShownExplosions = 2 }); } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.TheStormUnboundCast or AID.TheStormUnboundRepeat) + if (spell.Action.ID is (uint)AID.TheStormUnboundCast or (uint)AID.TheStormUnboundRepeat) { - foreach (var l in Lines.Where(l => l.Next.AlmostEqual(caster.Position, 1))) - AdvanceLine(l, caster.Position); + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) + { + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } + } } } } -class LightlessSpark2(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightlessSparkAdds), new AOEShapeCone(40, 45.Degrees())); +class LightlessSpark2(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightlessSparkAdds), new AOEShapeCone(40f, 45f.Degrees())); -class ArtOfTheStorm(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.ArtOfTheStorm), 8); -class EntropicFlame(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EntropicFlame), new AOEShapeRect(50, 4)); +class ArtOfTheStorm(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.ArtOfTheStorm), 8f); +class EntropicFlame(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.EntropicFlame), new AOEShapeRect(50f, 4f)); -class FloodOfDarkness(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FloodOfDarkness), 6); -class VeinSplitter(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.VeinSplitter), 10); -class LightlessSpark(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightlessSpark), new AOEShapeCone(40, 45.Degrees())); -class SwellUnbound(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TheSwellUnbound), new AOEShapeDonut(8, 20)); -class Swell(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.ArtOfTheSwell), 8) +class FloodOfDarkness(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.FloodOfDarkness), 6f); +class VeinSplitter(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.VeinSplitter), 10f); +class LightlessSpark(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.LightlessSpark), new AOEShapeCone(40f, 45f.Degrees())); +class SwellUnbound(BossModule module) : Components.SimpleAOEs(module, ActionID.MakeSpell(AID.TheSwellUnbound), new AOEShapeDonut(8f, 20f)); +class Swell(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.ArtOfTheSwell), 8f) { public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if (Casters.Count > 0) - hints.AddForbiddenZone(new AOEShapeDonut(8, 50), Arena.Center); + if (Casters.Count != 0) + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Arena.Center, 8)); } } -abstract class ArtOfTheSword(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeRect(40, 3)); +abstract class ArtOfTheSword(BossModule module, AID aid) : Components.SimpleAOEs(module, ActionID.MakeSpell(aid), new AOEShapeRect(40f, 3f)); class ArtOfTheSword1(BossModule module) : ArtOfTheSword(module, AID.ArtOfTheSword1); class ArtOfTheSword2(BossModule module) : ArtOfTheSword(module, AID.ArtOfTheSword2); class ArtOfTheSword3(BossModule module) : ArtOfTheSword(module, AID.ArtOfTheSword3); -class DarkAether(BossModule module) : Components.GenericAOEs(module) +class DarkAether(BossModule module) : Components.PersistentVoidzone(module, 1.5f, GetVoidzones, 3) { - public override IEnumerable ActiveAOEs(int slot, Actor actor) => Module.Enemies(OID.DarkAether).Select(e => new AOEInstance(new AOEShapeCircle(1.5f), e.Position, e.Rotation)); - - public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + private static Actor[] GetVoidzones(BossModule module) { - foreach (var c in ActiveAOEs(slot, actor)) - hints.AddForbiddenZone(new AOEShapeRect(3, 1.5f, 1.5f), c.Origin, c.Rotation, c.Activation); + var enemies = module.Enemies((uint)OID.DarkAether); + var count = enemies.Count; + if (count == 0) + return []; + + var voidzones = new Actor[count]; + var index = 0; + for (var i = 0; i < count; ++i) + { + var z = enemies[i]; + if (!z.IsDead) + voidzones[index++] = z; + } + return voidzones[..index]; } } @@ -77,4 +98,4 @@ public ZenosP2States(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 68721, NameID = 6039, PrimaryActorOID = (uint)OID.BossP2)] -public class ZenosP2(WorldState ws, Actor primary) : BossModule(ws, primary, new(233, -93.25f), new ArenaBoundsCircle(20)); +public class ZenosP2(WorldState ws, Actor primary) : BossModule(ws, primary, new(233, -93.25f), new ArenaBoundsCircle(20f)); diff --git a/BossMod/Modules/Stormblood/Trial/T07Byakko/T07Byakko.cs b/BossMod/Modules/Stormblood/Trial/T07Byakko/T07Byakko.cs index 391d878662..a9c78c6850 100644 --- a/BossMod/Modules/Stormblood/Trial/T07Byakko/T07Byakko.cs +++ b/BossMod/Modules/Stormblood/Trial/T07Byakko/T07Byakko.cs @@ -16,38 +16,40 @@ class HighestStakes(BossModule module) : Components.StackWithIcon(module, (uint) class AratamaForce(BossModule module) : Components.GenericAOEs(module) { - private readonly List _bubbles = module.Enemies(OID.AratamaForce); + private readonly List _bubbles = module.Enemies((uint)OID.AratamaForce); private static readonly AOEShapeCircle _shape = new(2); public override IEnumerable ActiveAOEs(int slot, Actor actor) => _bubbles.Where(actor => !actor.IsDead).Select(b => new AOEInstance(_shape, b.Position)); } -class HundredfoldHavoc(BossModule module) : Components.Exaflare(module, 5) +class HundredfoldHavoc(BossModule module) : Components.Exaflare(module, 5f) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.HundredfoldHavocFirst) + if (spell.Action.ID == (uint)AID.HundredfoldHavocFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 5 * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1, ExplosionsLeft = 10, MaxShownExplosions = 2 }); + Lines.Add(new() { Next = caster.Position, Advance = 5f * caster.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1f, ExplosionsLeft = 10, MaxShownExplosions = 2 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.HundredfoldHavocFirst or AID.HundredfoldHavocRest) + if (spell.Action.ID is (uint)AID.HundredfoldHavocFirst or (uint)AID.HundredfoldHavocRest) { - ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(spell.TargetXZ, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], spell.TargetXZ); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); } } } diff --git a/BossMod/Modules/Stormblood/Ultimate/UCOB/P5Exaflare.cs b/BossMod/Modules/Stormblood/Ultimate/UCOB/P5Exaflare.cs index f380738f9f..cad45b5827 100644 --- a/BossMod/Modules/Stormblood/Ultimate/UCOB/P5Exaflare.cs +++ b/BossMod/Modules/Stormblood/Ultimate/UCOB/P5Exaflare.cs @@ -1,32 +1,35 @@ namespace BossMod.Stormblood.Ultimate.UCOB; -class P5Exaflare(BossModule module) : Components.Exaflare(module, 6) +class P5Exaflare(BossModule module) : Components.Exaflare(module, 6f) { public void Reset() => NumCasts = 0; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - if ((AID)spell.Action.ID is AID.ExaflareFirst) + if (spell.Action.ID == (uint)AID.ExaflareFirst) { - Lines.Add(new() { Next = spell.LocXZ, Advance = 8 * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.5f, ExplosionsLeft = 6, MaxShownExplosions = 4 }); + Lines.Add(new() { Next = caster.Position, Advance = 8f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.5f, ExplosionsLeft = 6, MaxShownExplosions = 4 }); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if ((AID)spell.Action.ID is AID.ExaflareFirst or AID.ExaflareRest) + if (spell.Action.ID is (uint)AID.ExaflareFirst or (uint)AID.ExaflareRest) { ++NumCasts; - var index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1)); - if (index == -1) + var count = Lines.Count; + var pos = caster.Position; + for (var i = 0; i < count; ++i) { - ReportError($"Failed to find entry for {caster.InstanceID:X}"); - return; + var line = Lines[i]; + if (line.Next.AlmostEqual(pos, 1f)) + { + AdvanceLine(line, pos); + if (line.ExplosionsLeft == 0) + Lines.RemoveAt(i); + return; + } } - - AdvanceLine(Lines[index], caster.Position); - if (Lines[index].ExplosionsLeft == 0) - Lines.RemoveAt(index); } } }