From f93a2fdb5e7ae107a2665271b6867ca28c22ca3e Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:30:23 +0100 Subject: [PATCH] experiment pathfinding improvements + various other improvements --- BossMod/AI/AIConfig.cs | 2 +- BossMod/AI/AIController.cs | 2 +- BossMod/BossModule/AIHints.cs | 7 +- BossMod/BossModule/BossModule.cs | 15 +- BossMod/BossModule/MiniArena.cs | 8 + BossMod/BossModule/TreasureHuntTemplate.cs | 7 + BossMod/Components/StackSpread.cs | 4 +- BossMod/Components/Tethers.cs | 9 +- BossMod/Components/Towers.cs | 18 +- BossMod/Data/WorldState.cs | 2 +- BossMod/Framework/IPCProvider.cs | 1 + .../Alliance/A10Trash/A10Aquarius.cs | 6 +- .../Dawntrail/Alliance/A10Trash/A10Despot.cs | 9 +- .../Alliance/A10Trash/A10Groundskeeper.cs | 5 +- .../A10Trash/A10VanguardPathfinder.cs | 5 +- .../Dawntrail/Alliance/A11Prishe/A11Prishe.cs | 1 - .../Alliance/A11Prishe/AuroralUppercut.cs | 10 +- .../Dawntrail/Alliance/A11Prishe/Explosion.cs | 32 ++ .../Alliance/A12Fafnir/HurricaneWing.cs | 28 +- .../Alliance/A13ArkAngels/A13ArkAngels.cs | 35 ++- .../A13ArkAngels/A13ArkAngelsEnums.cs | 1 + .../A13ArkAngels/A13ArkAngelsStates.cs | 7 +- .../A13ArkAngels/ArroganceIncarnate.cs | 2 +- .../Alliance/A13ArkAngels/ConeDonutCross.cs | 34 +++ .../Alliance/A13ArkAngels/DecisiveBattle.cs | 14 +- .../Alliance/A13ArkAngels/DominionSlash.cs | 3 +- .../Alliance/A13ArkAngels/HavokSpiral.cs | 24 +- .../Alliance/A13ArkAngels/Utsusemi.cs | 37 --- .../Alliance/A14ShadowLord/A14ShadowLord.cs | 1 + .../A14ShadowLord/A14ShadowLordStates.cs | 1 + .../Alliance/A14ShadowLord/CthonicFury.cs | 2 +- .../Alliance/A14ShadowLord/DamningStrikes.cs | 4 +- .../Dungeon/D01Ihuykatumu/D011PrimePunutiy.cs | 83 ++++-- .../Dungeon/D01Ihuykatumu/D012Drowsie.cs | 8 +- .../Dungeon/D01Ihuykatumu/D013Apollyon.cs | 12 +- .../D04Vanguard/D040VanguardSentryR7.cs | 6 +- .../Dungeon/D04Vanguard/D042Protector.cs | 45 ++- .../D091LindblumZaghnal.cs | 59 +++- .../D092OverseerKanilokka.cs | 41 ++- .../D093Lunipyati.cs | 7 +- .../D09YuweyawataFieldStation/D90ForestBat.cs | 8 +- .../D90ForestWoolback.cs | 8 +- .../D90RottenHound.cs | 9 +- .../D90SprightlyClayGolem.cs | 8 +- .../D90SprightlyPhoebad.cs | 8 +- .../D90StationSpecter.cs | 9 +- .../Dawntrail/Hunt/RankA/Keheniheyamewi.cs | 2 +- .../CenoteJaJaGural/BullApollyon.cs | 11 +- .../CenoteJaJaGural/GoldenMolter.cs | 15 +- .../CenoteJaJaGural/SharedArenaBounds.cs | 6 + .../T03QueenEternal/WaltzOfTheRegalia.cs | 2 +- .../TheExcitatron6000/LuckyFace.cs | 13 +- .../GymnasiouAcheloios.cs | 11 +- .../GymnasiouLeon.cs | 11 +- .../GymnasiouMandragoras.cs | 11 +- .../GymnasiouMegakantha.cs | 15 +- .../GymnasiouMeganereis.cs | 11 +- .../GymnasiouPithekos.cs | 11 +- .../GymnasiouSatyros.cs | 9 +- .../GymnasiouSphinx.cs | 11 +- .../GymnasiouStyphnolobion.cs | 11 +- .../GymnasiouTigris.cs | 11 +- .../GymnasiouTriton.cs | 13 +- .../LampasChrysine.cs | 6 +- .../LyssaChrysine.cs | 9 +- .../TheShiftingGymnasionAgonon/Narkissos.cs | 9 +- .../D053TheEverlivingBibliotaph.cs | 4 +- .../Dungeon/D02DohnMheg/D023AencThon.cs | 33 +- .../TheDungeonsOfLyheGhiah/Goliath.cs | 30 +- .../DaenOseTheAvariciousTyphon.cs | 2 +- .../FuathTroublemaker.cs | 2 +- .../GreedyPixie.cs | 2 +- .../SecretBasket.cs | 2 +- .../SecretCladoselache.cs | 2 +- .../SecretDjinn.cs | 2 +- .../SecretKeeper.cs | 2 +- .../SecretKorrigan.cs | 2 +- .../SecretPegasus.cs | 2 +- .../SecretPorxie.cs | 2 +- .../SecretSerpent.cs | 6 +- .../SecretSwallow.cs | 2 +- .../SecretUndine.cs | 2 +- .../SecretWorm.cs | 2 +- .../TheLostCanalsOfUznair/CanalIcebeast.cs | 6 +- .../AltarAiravata.cs | 2 +- .../TheShiftingAltarsOfUznair/AltarArachne.cs | 2 +- .../TheShiftingAltarsOfUznair/AltarBeast.cs | 2 +- .../TheShiftingAltarsOfUznair/AltarChimera.cs | 2 +- .../AltarDiresaur.cs | 2 +- .../AltarDullahan.cs | 2 +- .../TheShiftingAltarsOfUznair/AltarKelpie.cs | 2 +- .../AltarMandragora.cs | 2 +- .../TheShiftingAltarsOfUznair/AltarSkatene.cs | 2 +- .../TheShiftingAltarsOfUznair/AltarTotem.cs | 2 +- .../TheShiftingAltarsOfUznair/Hati.cs | 2 +- .../TheGreatGoldWhisker.cs | 2 +- .../TheShiftingAltarsOfUznair/TheOlderOne.cs | 2 +- .../TheShiftingAltarsOfUznair/TheWinged.cs | 2 +- BossMod/Pathfinding/ThetaStar.cs | 281 ++++++++++++++---- BossMod/Pathfinding/WaypointManager.cs | 76 ----- 100 files changed, 877 insertions(+), 458 deletions(-) create mode 100644 BossMod/BossModule/TreasureHuntTemplate.cs create mode 100644 BossMod/Modules/Dawntrail/Alliance/A11Prishe/Explosion.cs create mode 100644 BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ConeDonutCross.cs delete mode 100644 BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/Utsusemi.cs create mode 100644 BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/SharedArenaBounds.cs delete mode 100644 BossMod/Pathfinding/WaypointManager.cs diff --git a/BossMod/AI/AIConfig.cs b/BossMod/AI/AIConfig.cs index db248351c7..3879fbfec6 100644 --- a/BossMod/AI/AIConfig.cs +++ b/BossMod/AI/AIConfig.cs @@ -52,7 +52,7 @@ sealed class AIConfig : ConfigNode [PropertyDisplay("Enable auto AFK", tooltip: "Enables auto AFK if out of combat. While AFK AI will not use autorotation or target anything")] public bool AutoAFK = false; - [PropertyDisplay("Enable out of combat AFK mode", tooltip: "Time in seconds out of combat until AFK mode enables. Any movement will reset timer or disable AFK mode if already active.")] + [PropertyDisplay("Auto AFK timer", tooltip: "Time in seconds out of combat until AFK mode enables. Any movement will reset timer or disable AFK mode if already active.")] public float AFKModeTimer = 10; public string? AIAutorotPresetName; diff --git a/BossMod/AI/AIController.cs b/BossMod/AI/AIController.cs index e7f29b09ab..bd82f3ae2b 100644 --- a/BossMod/AI/AIController.cs +++ b/BossMod/AI/AIController.cs @@ -49,7 +49,7 @@ public void Update(Actor? player, AIHints hints, DateTime now) var moveRequested = _movement.IsMoveRequested(); var castInProgress = player.CastInfo != null && !player.CastInfo.EventHappened; var forbidMovement = moveRequested || !AllowInterruptingCastByMovement && _amex.MoveMightInterruptCast; - if (NaviTargetPos != null && !forbidMovement && (NaviTargetPos.Value - player.Position).LengthSq() > 0.01f) + if (NaviTargetPos != null && !forbidMovement && (NaviTargetPos.Value - player.Position).LengthSq() > 0.001f) { var y = NaviTargetVertical != null && IsVerticalAllowed ? NaviTargetVertical.Value : player.PosRot.Y; desiredPosition = new(NaviTargetPos.Value.X, y, NaviTargetPos.Value.Z); diff --git a/BossMod/BossModule/AIHints.cs b/BossMod/BossModule/AIHints.cs index 3c317023f8..42dbe86a40 100644 --- a/BossMod/BossModule/AIHints.cs +++ b/BossMod/BossModule/AIHints.cs @@ -1,4 +1,6 @@ -namespace BossMod; +using BossMod.AST; + +namespace BossMod; // information relevant for AI decision making process for a specific player public sealed class AIHints @@ -37,7 +39,6 @@ public enum SpecialMode // information needed to build base pathfinding map (onto which forbidden/goal zones are later rasterized), if needed (lazy, since it's somewhat expensive and not always needed) public WPos PathfindMapCenter; public ArenaBounds PathfindMapBounds = DefaultBounds; - public WaypointManager WaypointManager { get; private set; } = new WaypointManager(); public Bitmap.Region PathfindMapObstacles; // list of potential targets @@ -134,7 +135,7 @@ public void FillPotentialTargets(WorldState ws, bool playerIsDefaultTank) { // fate mob in fate we are NOT a part of, skip entirely. it's okay to "attack" these (i.e., they won't be added as forbidden targets) because we can't even hit them // (though aggro'd mobs will continue attacking us after we unsync, but who really cares) - if (actor.FateID > 0 && actor.FateID != allowedFateID) + if (actor.FateID != 0 && actor.FateID != allowedFateID) continue; // target is dying; skip it so that AI retargets, but ensure that it's not marked as a forbidden target diff --git a/BossMod/BossModule/BossModule.cs b/BossMod/BossModule/BossModule.cs index c19c22ecf9..86d6c64e40 100644 --- a/BossMod/BossModule/BossModule.cs +++ b/BossMod/BossModule/BossModule.cs @@ -29,9 +29,22 @@ public abstract class BossModule : IDisposable public IReadOnlyList Enemies(uint oid) { IReadOnlyList? entry = _relevantEnemies.GetValueOrDefault(oid); - entry ??= _relevantEnemies[oid] = WorldState.Actors.Where(actor => actor.OID == oid).ToList(); + entry ??= _relevantEnemies[oid] = [.. WorldState.Actors.Where(actor => actor.OID == oid)]; return entry; } + + public IReadOnlyList Enemies(ReadOnlySpan enemies) + { + List relevantenemies = []; + for (var i = 0; i < enemies.Length; ++i) + { + var enemy = enemies[i]; + IReadOnlyList? entry = _relevantEnemies.GetValueOrDefault(enemy); + entry ??= _relevantEnemies[enemy] = [.. WorldState.Actors.Where(actor => actor.OID == enemy)]; + relevantenemies.AddRange(entry); + } + return relevantenemies; + } public IReadOnlyList 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/BossModule/MiniArena.cs b/BossMod/BossModule/MiniArena.cs index 9bcd0b8623..bf5e8f8d85 100644 --- a/BossMod/BossModule/MiniArena.cs +++ b/BossMod/BossModule/MiniArena.cs @@ -456,6 +456,14 @@ public void Actors(IEnumerable actors, uint color = 0, bool allowDeadAndU Actor(a, color == 0 ? Colors.Enemy : color, allowDeadAndUntargetable); } + public void Actors(IReadOnlyList actors, uint color = 0, bool allowDeadAndUntargetable = false) + { + for (var i = 0; i < actors.Count; ++i) + { + Actor(actors[i], color == 0 ? Colors.Enemy : color, allowDeadAndUntargetable); + } + } + public static void End() { ImGui.GetWindowDrawList().PopClipRect(); diff --git a/BossMod/BossModule/TreasureHuntTemplate.cs b/BossMod/BossModule/TreasureHuntTemplate.cs new file mode 100644 index 0000000000..47e7581fa0 --- /dev/null +++ b/BossMod/BossModule/TreasureHuntTemplate.cs @@ -0,0 +1,7 @@ +namespace BossMod; + +// for treasure hunt roulettes +public abstract class THTemplate(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) +{ + private static readonly ArenaBoundsComplex arena = new([new Polygon(new(100, 100), 19, 48)]); +} diff --git a/BossMod/Components/StackSpread.cs b/BossMod/Components/StackSpread.cs index 61c611c7f3..e326fd8bba 100644 --- a/BossMod/Components/StackSpread.cs +++ b/BossMod/Components/StackSpread.cs @@ -165,14 +165,14 @@ public override PlayerPriority CalcPriority(int pcSlot, Actor pc, int playerSlot public override void DrawArenaForeground(int pcSlot, Actor pc) { + void DrawCircle(WPos position, float radius, uint color = 0) => Arena.AddCircle(position, radius, color); if (!AlwaysShowSpreads && Spreads.FindIndex(s => s.Target == pc) is var iSpread && iSpread >= 0) { // Draw only own circle if spreading; no one should be inside. - Arena.AddCircle(pc.Position, Spreads[iSpread].Radius); + DrawCircle(pc.Position, Spreads[iSpread].Radius); } else { - void DrawCircle(WPos position, float radius, uint color = 0) => Arena.AddCircle(position, radius, color); // Handle safe stack circles foreach (var s in ActiveStacks.Where(x => x.Target == pc || !x.ForbiddenPlayers[pcSlot] && !IsSpreadTarget(pc) && !IsStackTarget(pc) && (x.IsInside(pc) diff --git a/BossMod/Components/Tethers.cs b/BossMod/Components/Tethers.cs index 0b8f297a89..c05d42fd49 100644 --- a/BossMod/Components/Tethers.cs +++ b/BossMod/Components/Tethers.cs @@ -281,7 +281,7 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) } } - private bool IsTether(Actor actor, uint tetherID) => TetherOnActor.Contains((actor, tetherID)); + protected bool IsTether(Actor actor, uint tetherID) => TetherOnActor.Contains((actor, tetherID)); private void DrawTetherLines(Actor target, uint color = 0) { @@ -390,4 +390,11 @@ public override void AddHints(int slot, Actor actor, TextHints hints) else base.AddHints(slot, actor, hints); } + + public override void DrawArenaForeground(int pcSlot, Actor pc) + { + base.DrawArenaForeground(pcSlot, pc); + if (needToKite && IsTether(pc, TIDBad)) + Arena.Actor(ActiveBaits.FirstOrDefault(x => x.Target == pc).Source, Colors.Object, true); + } } diff --git a/BossMod/Components/Towers.cs b/BossMod/Components/Towers.cs index 710ebf026c..bb74ad9255 100644 --- a/BossMod/Components/Towers.cs +++ b/BossMod/Components/Towers.cs @@ -1,6 +1,6 @@ namespace BossMod.Components; -public class GenericTowers(BossModule module, ActionID aid = default) : CastCounter(module, aid) +public class GenericTowers(BossModule module, ActionID aid = default, bool prioritizeInsufficient = false) : CastCounter(module, aid) { public struct Tower(WPos position, float radius, int minSoakers = 1, int maxSoakers = 1, BitMask forbiddenSoakers = default, DateTime activation = default) { @@ -20,6 +20,7 @@ public struct Tower(WPos position, float radius, int minSoakers = 1, int maxSoak } public List Towers = []; + public bool PrioritizeInsufficient = prioritizeInsufficient; // give priority to towers with more than 0 but less than min soakers // default tower styling public static void DrawTower(MiniArena arena, WPos pos, float radius, bool safe) @@ -57,15 +58,22 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if (Towers.Count == 0) + var count = Towers.Count; + if (count == 0) return; var forbiddenInverted = new List>(); var forbidden = new List>(); if (!Towers.Any(x => x.ForbiddenSoakers[slot])) { - if (Raid.WithoutSlot(true).Count() <= 8) // don't do this in unorganized content where people do whatever - foreach (var t in Towers.Where(x => !x.IsInside(actor) && x.InsufficientAmountInside(Module) && x.NumInside(Module) > 0)) - forbiddenInverted.Add(ShapeDistance.InvertedCircle(t.Position, t.Radius)); + if (PrioritizeInsufficient) + { + List insufficientTowers = []; + foreach (var t in Towers.Where(x => x.InsufficientAmountInside(Module) && x.NumInside(Module) > 0)) + insufficientTowers.Add(t); + var mostRelevantTower = insufficientTowers.OrderByDescending(x => x.NumInside(Module)).ThenBy(x => (x.Position - actor.Position).LengthSq()).FirstOrDefault(); + if (insufficientTowers.Count > 0) + forbiddenInverted.Add(ShapeDistance.InvertedCircle(mostRelevantTower.Position, mostRelevantTower.Radius)); + } var inTower = Towers.Any(x => x.IsInside(actor) && x.CorrectAmountInside(Module)); var missingSoakers = !inTower && Towers.Any(x => x.InsufficientAmountInside(Module)); if (forbiddenInverted.Count == 0) diff --git a/BossMod/Data/WorldState.cs b/BossMod/Data/WorldState.cs index 0f12a2e772..ed3ae8759e 100644 --- a/BossMod/Data/WorldState.cs +++ b/BossMod/Data/WorldState.cs @@ -110,7 +110,7 @@ public sealed record class OpRSVData(string Key, string Value) : Operation { protected override void Exec(WorldState ws) { - Service.LuminaRSV[Key] = System.Text.Encoding.UTF8.GetBytes(Value); // TODO: reconsider... + Service.LuminaRSV[Key] = Encoding.UTF8.GetBytes(Value); // TODO: reconsider... ws.RSVEntries[Key] = Value; ws.RSVDataReceived.Fire(this); } diff --git a/BossMod/Framework/IPCProvider.cs b/BossMod/Framework/IPCProvider.cs index e3572ed552..f7e2732e7c 100644 --- a/BossMod/Framework/IPCProvider.cs +++ b/BossMod/Framework/IPCProvider.cs @@ -85,6 +85,7 @@ public IPCProvider(RotationModuleManager autorotation, ActionManagerEx amex, Mov Register("AI.GetPreset", () => ai.GetAIPreset); Register("AI.GetPotentialTargets", () => autorotation.Hints.PotentialTargets); Register("AI.GetSpecialMode", () => autorotation.Hints.ImminentSpecialMode); + Register("AI.ForbiddenDirections", () => autorotation.Hints.ForbiddenDirections); Register("AI.ForcedTarget", () => autorotation.Hints.ForcedTarget); Register("AI.SetPositional", (Positional positional) => autorotation.Hints.SetPositional(positional)); } diff --git a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Aquarius.cs b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Aquarius.cs index cbfd485a4b..87c361d7c9 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Aquarius.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Aquarius.cs @@ -51,7 +51,7 @@ public A10AquariusStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(A10Aquarius.Trash).All(x => x.IsDeadOrDestroyed); } } @@ -97,9 +97,11 @@ public class A10Aquarius(WorldState ws, Actor primary) : BossModule(ws, primary, new(-503.55f, 696.32f), new(-504.41f, 695.37f), new(-504.92f, 695.35f), new(-506.16f, 695.72f), new(-506.72f, 695.68f), new(-505.95f, 688.81f), new(-505.83f, 688.18f), new(-505.76f, 687.58f), new(-505.52f, 686.9f), new(-500.62f, 686.9f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.ElderGobbue, (uint)OID.RobberCrab1, (uint)OID.RobberCrab2, (uint)OID.DeathCap, + (uint)OID.BarkSpider1, (uint)OID.BarkSpider2, (uint)OID.Skimmer1, (uint)OID.Skimmer2]; protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + Arena.Actors(Enemies(Trash)); } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Despot.cs b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Despot.cs index 8af751bade..26d271e7f7 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Despot.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Despot.cs @@ -19,7 +19,7 @@ public enum AID : uint IsleDrop = 41699, // Boss->location, 3.0s cast, range 6 circle Peck = 41695, // Flamingo2->player, no cast, single-target Panzerfaust = 41698, // Boss->player, 5.0s cast, single-target, interruptible tankbuster - PanzerfaustRepeats = 41353, // Boss->player, no cast, single-target, knockback 10, apply concussion + PanzerfaustRepeats = 41353 // Boss->player, no cast, single-target, knockback 10, apply concussion } class IsleDrop(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.IsleDrop), 6); @@ -77,7 +77,7 @@ public A10DespotStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(A10Despot.Trash).All(x => x.IsDeadOrDestroyed); } } @@ -120,10 +120,11 @@ public class A10Despot(WorldState ws, Actor primary) : BossModule(ws, primary, a new(-586.45f, -635.02f), new(-587.01f, -635.5f), new(-587.48f, -636.06f), new(-587.43f, -636.72f), new(-586.1f, -639.84f), new(-585.55f, -640.25f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.Flamingo1, (uint)OID.Flamingo2]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.PosRot.Y > -960 & x.InCombat); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + Arena.Actors(Enemies(Trash)); } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Groundskeeper.cs b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Groundskeeper.cs index 2e162deb04..d14ac351ea 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Groundskeeper.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Groundskeeper.cs @@ -26,7 +26,7 @@ public A10GroundskeeperStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable && x.InCombat).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(A10Groundskeeper.Trash).All(x => x.IsDeadOrDestroyed); } } @@ -52,9 +52,10 @@ public class A10Groundskeeper(WorldState ws, Actor primary) : BossModule(ws, pri new(-557.2f, -616.93f), new(-556.69f, -616.59f), new(-556.17f, -616.37f), new(-555.61f, -616.44f), new(-555.32f, -616.94f), new(-544.54f, -642.34f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.Groundskeeper, (uint)OID.Sprinkler]; protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + Arena.Actors(Enemies(Trash)); } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10VanguardPathfinder.cs b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10VanguardPathfinder.cs index f0f88619d8..e1ed9dd43c 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10VanguardPathfinder.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A10Trash/A10VanguardPathfinder.cs @@ -28,7 +28,7 @@ public A10VanguardPathfinderStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(A10VanguardPathfinder.Trash).All(x => x.IsDeadOrDestroyed); } } @@ -54,9 +54,10 @@ public class A10VanguardPathfinder(WorldState ws, Actor primary) : BossModule(ws new(790.4f, 624.3f), new(790.66f, 623.66f), new(790.86f, 623), new(790.61f, 622.49f), new(791.1f, 622.42f), new(791.62f, 622.18f), new(791.71f, 621.53f), new(802.28f, 621.41f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.VanguardsSlime1, (uint)OID.VanguardsSlime2, (uint)OID.GoblinReplica]; protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + Arena.Actors(Enemies(Trash)); } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/A11Prishe.cs b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/A11Prishe.cs index e1d2148b2e..9c8f396aa2 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/A11Prishe.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/A11Prishe.cs @@ -2,7 +2,6 @@ class NullifyingDropkick(BossModule module) : Components.CastSharedTankbuster(module, ActionID.MakeSpell(AID.NullifyingDropkick), 6); class Holy(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.Holy), 6); -class Explosion(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Explosion), new AOEShapeCircle(8)); class BanishgaIV(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.BanishgaIV)); class Banishga(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Banishga)); diff --git a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/AuroralUppercut.cs b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/AuroralUppercut.cs index 0268d3d380..391a4279c9 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/AuroralUppercut.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/AuroralUppercut.cs @@ -30,20 +30,20 @@ public override void OnStatusLose(Actor actor, ActorStatus status) class AuroralUppercutHint(BossModule module) : Components.GenericAOEs(module) { - private static readonly Angle a45 = 45.Degrees(), a135 = 135.Degrees(), a44 = 44.Degrees(), a10 = 10.Degrees(), a60 = 60.Degrees(); + private static readonly Angle a45 = 45.Degrees(), a135 = 135.Degrees(), a44 = 44.Degrees(), a10 = 10.Degrees(), a59 = 59.Degrees(); private static readonly WPos center = A11Prishe.ArenaCenter; private AOEInstance? _aoe; - private static readonly AOEShapeCustom hintENVC00020001KB25 = new([new ConeHA(center, 10, -144.Degrees(), a44), new ConeHA(center, 10, 36.Degrees(), a44)], + private static readonly AOEShapeCustom hintENVC00020001KB25 = new([new DonutSegmentHA(center, 4, 10, -144.Degrees(), a44), new DonutSegmentHA(center, 4, 10, 36.Degrees(), a44)], [new ConeHA(center, 10, -a135, a10), new ConeHA(center, 10, a45, a10)], InvertForbiddenZone: true); - private static readonly AOEShapeCustom hintENVC02000100KB25 = new([new ConeHA(center, 10, 126.Degrees(), a44), new ConeHA(center, 10, -54.Degrees(), a44)], + private static readonly AOEShapeCustom hintENVC02000100KB25 = new([new DonutSegmentHA(center, 4, 10, 126.Degrees(), a44), new DonutSegmentHA(center, 4, 10, -54.Degrees(), a44)], [new ConeHA(center, 10, a135, a10), new ConeHA(center, 10, -a45, a10)], InvertForbiddenZone: true); private static readonly AOEShapeCustom hintENVC00020001KB38 = new([new ConeHA(center, 5, -a135, a10), new ConeHA(center, 5, a45, a10)], InvertForbiddenZone: true); private static readonly AOEShapeCustom hintENVC02000100KB38 = new([new ConeHA(center, 5, a135, a10), new ConeHA(center, 5, -a45, a10)], InvertForbiddenZone: true); - private static readonly AOEShapeCustom hintENVC00020001KB12 = new([new ConeHA(center, 5, a135, a60), new ConeHA(center, 5, -a45, a60), + private static readonly AOEShapeCustom hintENVC00020001KB12 = new([new ConeHA(center, 5, a135, a59), new ConeHA(center, 5, -a45, a59), new ConeV(ArenaChanges.MiddleENVC00020001[0].Center + new WDir(-9, -9), 3, -a135, a45, 3), new ConeV(ArenaChanges.MiddleENVC00020001[1].Center + new WDir(9, 9), 3, a45, a45, 3), new Rectangle(center + new WDir(-3, -15), 5, 1), new Rectangle(center + new WDir(3, 15), 5, 1)], InvertForbiddenZone: true); - private static readonly AOEShapeCustom hintENVC02000100KB12 = new([new ConeHA(center, 5, -a135, a60), new ConeHA(center, 5, a45, a60), + private static readonly AOEShapeCustom hintENVC02000100KB12 = new([new ConeHA(center, 5, -a135, a59), new ConeHA(center, 5, a45, a59), new ConeV(ArenaChanges.MiddleENVC02000100[0].Center + new WDir(9, -9), 3, -a45, a45, 3), new ConeV(ArenaChanges.MiddleENVC02000100[1].Center + new WDir(-9, 9), 3, a135, a45, 3), new Rectangle(center + new WDir(-15, 3), 1, 5), new Rectangle(center + new WDir(15, -3), 1, 5)], InvertForbiddenZone: true); diff --git a/BossMod/Modules/Dawntrail/Alliance/A11Prishe/Explosion.cs b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/Explosion.cs new file mode 100644 index 0000000000..b24f59f3a1 --- /dev/null +++ b/BossMod/Modules/Dawntrail/Alliance/A11Prishe/Explosion.cs @@ -0,0 +1,32 @@ +namespace BossMod.Dawntrail.Alliance.A11Prishe; + +class Explosion(BossModule module) : Components.GenericAOEs(module) +{ + private static readonly AOEShapeCircle circle = new(8); + private readonly List _aoes = []; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = _aoes.Count; + if (count > 0) + { + for (var i = 0; i < count; ++i) + { + var aoe = _aoes[i]; + yield return (aoe.Activation - _aoes[0].Activation).TotalSeconds <= 1 ? aoe with { Color = Colors.Danger } : aoe with { Risky = false }; + } + } + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == AID.Explosion) + _aoes.Add(new(circle, caster.Position, spell.Rotation, Module.CastFinishAt(spell))); + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.Explosion) + _aoes.RemoveAt(0); + } +} diff --git a/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs b/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs index bb3a30cbf6..d4a074cdfa 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A12Fafnir/HurricaneWing.cs @@ -14,7 +14,7 @@ class HurricaneWingAOE(BossModule module) : Components.GenericAOEs(module) public override void OnCastStarted(Actor caster, ActorCastInfo spell) { - AOEShape? shape = ShapeForAction(spell.Action); + var shape = ShapeForAction(spell.Action); if (shape != null) { NumCasts = 0; @@ -25,7 +25,7 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - AOEShape? shape = ShapeForAction(spell.Action); + var shape = ShapeForAction(spell.Action); if (shape != null) { AOEs.RemoveAll(aoe => aoe.Shape == shape); @@ -50,8 +50,11 @@ class Whirlwinds(BossModule module) : Components.GenericAOEs(module) { private const int Length = 5; private static readonly AOEShapeCapsule capsuleSmall = new(3, Length), capsuleBig = new(9, Length); + private static readonly AOEShapeCircle circleSmall = new(3), circleBig = new(9); private readonly List _smallWhirldwinds = [], _bigWhirldwinds = []; - public bool Active => _smallWhirldwinds.Count is not 0 or not 0; + public bool Active => _smallWhirldwinds.Count != 0 || _bigWhirldwinds.Count != 0; + private static readonly Angle a180 = 180.Degrees(); + private bool moving; public override IEnumerable ActiveAOEs(int slot, Actor actor) { @@ -62,19 +65,22 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) for (var i = 0; i < countSmall; ++i) { var w = _smallWhirldwinds[i]; - yield return new(capsuleSmall, w.Position, w.Rotation); + yield return new(moving ? capsuleSmall : circleSmall, w.Position, w.Rotation); } for (var i = 0; i < countBig; ++i) { var w = _bigWhirldwinds[i]; - yield return new(capsuleBig, w.Position, w.Rotation); + yield return new(moving ? capsuleBig : circleBig, w.Position, w.Rotation); } } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if ((AID)spell.Action.ID == AID.GreatWhirlwindLarge) + { _bigWhirldwinds.Add(caster); + moving = false; + } else if ((AID)spell.Action.ID == AID.GreatWhirlwindSmall) _smallWhirldwinds.Add(caster); } @@ -87,6 +93,12 @@ public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id) _bigWhirldwinds.Remove(actor); } + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if (!moving && (AID)spell.Action.ID is AID.GreatWhirlwindLargeAOE or AID.GreatWhirlwindSmallAOE) + moving = true; + } + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { var countSmall = _smallWhirldwinds.Count; @@ -94,15 +106,17 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme if (countSmall == 0 && countBig == 0) return; var forbidden = new List>(); // merging all forbidden zones into one to make pathfinding less demanding + + const int length = Length + 5; for (var i = 0; i < countBig; ++i) { var w = _bigWhirldwinds[i]; - forbidden.Add(ShapeDistance.Capsule(w.Position, w.Rotation, Length, 9)); + forbidden.Add(ShapeDistance.Capsule(w.Position, !moving ? w.Rotation + a180 : w.Rotation, length, 10)); } for (var i = 0; i < countSmall; ++i) { var w = _smallWhirldwinds[i]; - forbidden.Add(ShapeDistance.Capsule(w.Position, w.Rotation, Length, 3)); + forbidden.Add(ShapeDistance.Capsule(w.Position, !moving ? w.Rotation + a180 : w.Rotation, length, 5)); } hints.AddForbiddenZone(p => forbidden.Min(f => f(p))); } diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngels.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngels.cs index 3948d6aefa..4df2d3fddf 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngels.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngels.cs @@ -15,16 +15,15 @@ class Meteor(BossModule module) : Components.CastInterruptHint(module, ActionID. class TachiGekko(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.TachiGekko)); class TachiKasha(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TachiKasha), new AOEShapeCircle(20)); class TachiYukikaze(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TachiYukikaze), new AOEShapeRect(50, 2.5f)); -class ConcertedDissolution(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ConcertedDissolution), new AOEShapeCone(40, 15.Degrees())); -class LightsChain(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LightsChain), new AOEShapeDonut(4, 40)); -class DivineDominion(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.DivineDominion), new AOEShapeCircle(6)); -class CrossReaver(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.CrossReaverAOE), new AOEShapeCross(50, 6)); -class Holy(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Holy)); class Raiton(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Raiton)); +class Utsusemi(BossModule module) : Components.StretchTetherSingle(module, (uint)TetherID.Utsusemi, 10, needToKite: true); [ModuleInfo(BossModuleInfo.Maturity.Verified, PrimaryActorOID = (uint)OID.BossGK, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13640, SortOrder = 7)] public class A13ArkAngels(WorldState ws, Actor primary) : BossModule(ws, primary, new(865, -820), new ArenaBoundsCircle(34.5f)) { + public static readonly ArenaBoundsCircle DefaultBounds = new(25); + public static readonly uint[] Bosses = [(uint)OID.BossHM, (uint)OID.BossEV, (uint)OID.BossTT, (uint)OID.BossMR, (uint)OID.BossGK]; + private Actor? _bossHM; private Actor? _bossEV; private Actor? _bossMR; @@ -45,9 +44,31 @@ protected override void UpdateModule() _bossTT ??= StateMachine.ActivePhaseIndex >= 0 ? Enemies(OID.BossTT).FirstOrDefault() : null; } - public static readonly ArenaBoundsCircle DefaultBounds = new(25); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly)); + var comp = FindComponent(); + if (comp != null) + { + var slot = comp.AssignedBoss[pcSlot]; + if (slot != null) + Arena.Actor(slot); + } + else if (Enemies(OID.ArkShield).Any(x => !x.IsDead)) + Arena.Actor(Enemies(OID.ArkShield)[0]); + else + Arena.Actors(Enemies(Bosses)); + } + + protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + for (var i = 0; i < hints.PotentialTargets.Count; ++i) + { + var e = hints.PotentialTargets[i]; + if (e.Actor.FindStatus(SID.Invincibility) != null) + { + e.Priority = AIHints.Enemy.PriorityForbidFully; + break; + } + } } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsEnums.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsEnums.cs index 5b1defc1b5..942a0b9921 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsEnums.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsEnums.cs @@ -91,6 +91,7 @@ public enum SID : uint FatedVillain = 4195, // none->BossGK, extra=0x335 VauntedHero = 4196, // none->player, extra=0x0 VauntedVillain = 4197, // none->BossTT, extra=0x336 + Invincibility = 4410, // none->BossHM, extra=0x0 } public enum IconID : uint diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsStates.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsStates.cs index dcc22df5fa..f24d7999bd 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsStates.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/A13ArkAngelsStates.cs @@ -8,7 +8,8 @@ public A13ArkAngelsStates(A13ArkAngels module) : base(module) { _module = module; DeathPhase(0, SinglePhase) - .ActivateOnEnter(); + .ActivateOnEnter() + .Raw.Update = () => module.Enemies(A13ArkAngels.Bosses).All(x => x.IsDeadOrDestroyed); } private void SinglePhase(uint id) @@ -89,7 +90,7 @@ private void MeikyoShisui(uint id, float delay) .DeactivateOnExit(); ComponentCondition(id + 0x17, 0.6f, comp => comp.Casters.Count > 0) .ActivateOnEnter(); - ComponentCondition(id + 0x18, 2.5f, comp => comp.NumCasts > 0, "Cones") + ComponentCondition(id + 0x18, 2.5f, comp => comp.NumCasts == 6, "Cones") .DeactivateOnExit(); ComponentCondition(id + 0x19, 5.5f, comp => comp.NumCasts > 0, "Donut") .DeactivateOnExit(); @@ -204,7 +205,7 @@ private void MeikyoShisuiCrossReaverMeteor(uint id, float delay) .ActivateOnEnter(); ActorCastStart(id + 0x20, _module.BossHM, AID.CrossReaver, 1.9f, true); - ComponentCondition(id + 0x21, 0.5f, comp => comp.NumCasts > 0, "Cones") + ComponentCondition(id + 0x21, 0.5f, comp => comp.NumCasts == 6, "Cones") .DeactivateOnExit(); ActorCastEnd(id + 0x22, _module.BossHM, 2.5f, true); ComponentCondition(id + 0x23, 1, comp => comp.Casters.Count > 0) diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ArroganceIncarnate.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ArroganceIncarnate.cs index 5ee214fef2..f5eca80f6d 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ArroganceIncarnate.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ArroganceIncarnate.cs @@ -1,6 +1,6 @@ namespace BossMod.Dawntrail.Alliance.A13ArkAngels; -class ArroganceIncarnate(BossModule module) : Components.StackWithIcon(module, (uint)IconID.ArroganceIncarnate, ActionID.MakeSpell(AID.ArroganceIncarnateAOE), 6, 5.8f, 8, 24) +class ArroganceIncarnate(BossModule module) : Components.StackWithIcon(module, (uint)IconID.ArroganceIncarnate, ActionID.MakeSpell(AID.ArroganceIncarnateAOE), 6, 5.8f, PartyState.MaxAllianceSize, PartyState.MaxAllianceSize) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ConeDonutCross.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ConeDonutCross.cs new file mode 100644 index 0000000000..7605aa2bcf --- /dev/null +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/ConeDonutCross.cs @@ -0,0 +1,34 @@ +namespace BossMod.Dawntrail.Alliance.A13ArkAngels; + +class ConcertedDissolution(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ConcertedDissolution), new AOEShapeCone(40, 20.Degrees())) +{ + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var chain = Module.FindComponent(); + var check = chain != null && chain.Casters.Count != 0; + return ActiveCasters.Select(c => new AOEInstance(Shape, c.Position, c.CastInfo!.Rotation, Module.CastFinishAt(c.CastInfo), check ? Colors.Danger : Colors.AOE)); + } +} + +class LightsChain(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LightsChain), new AOEShapeDonut(4, 40)) +{ + private readonly ConcertedDissolution? _aoe = module.FindComponent(); + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var reaver = Module.FindComponent(); + var check = _aoe != null && _aoe.Casters.Count != 0; + var check2 = reaver != null && reaver.Casters.Count != 0; + return ActiveCasters.Select(c => new AOEInstance(Shape, c.Position, c.CastInfo!.Rotation, Module.CastFinishAt(c.CastInfo), check2 ? Colors.Danger : Colors.AOE, !check)); + } +} + +class CrossReaver(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.CrossReaverAOE), new AOEShapeCross(50, 6)) +{ + private readonly LightsChain? _aoe = module.FindComponent(); + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var check = _aoe != null && _aoe.Casters.Count != 0; + return ActiveCasters.Select(c => new AOEInstance(Shape, c.Position, c.CastInfo!.Rotation, Module.CastFinishAt(c.CastInfo), Risky: !check)); + } +} diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DecisiveBattle.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DecisiveBattle.cs index ecb96f3c35..b94492eacf 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DecisiveBattle.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DecisiveBattle.cs @@ -2,15 +2,15 @@ namespace BossMod.Dawntrail.Alliance.A13ArkAngels; class DecisiveBattle(BossModule module) : BossComponent(module) { - private readonly Actor?[] _assignedBoss = new Actor?[PartyState.MaxAllianceSize]; + public readonly Actor?[] AssignedBoss = new Actor?[PartyState.MaxAllianceSize]; public override void AddHints(int slot, Actor actor, TextHints hints) { - if (slot < _assignedBoss.Length && _assignedBoss[slot] != null) + if (slot < PartyState.MaxAllianceSize && AssignedBoss[slot] != null) { var target = WorldState.Actors.Find(actor.TargetID); - if (target != null && target != _assignedBoss[slot] && (OID)target.OID is OID.BossMR or OID.BossTT or OID.BossGK) - hints.Add($"Target {_assignedBoss[slot]?.Name}!"); + if (target != null && target != AssignedBoss[slot] && (OID)target.OID is OID.BossMR or OID.BossTT or OID.BossGK) + hints.Add($"Target {AssignedBoss[slot]?.Name}!"); } } @@ -18,17 +18,17 @@ public override void OnTethered(Actor source, ActorTetherInfo tether) { if (tether.ID == (uint)TetherID.DecisiveBattle && Raid.FindSlot(source.InstanceID) is var slot && slot >= 0) { - _assignedBoss[slot] = WorldState.Actors.Find(tether.Target); + AssignedBoss[slot] = WorldState.Actors.Find(tether.Target); } } public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if (slot < _assignedBoss.Length && _assignedBoss[slot] != null) + if (slot < AssignedBoss.Length && AssignedBoss[slot] != null) for (var i = 0; i < hints.PotentialTargets.Count; ++i) { var enemy = hints.PotentialTargets[i]; - if (enemy.Actor != _assignedBoss[slot]) + if (enemy.Actor != AssignedBoss[slot]) enemy.Priority = AIHints.Enemy.PriorityForbidFully; } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DominionSlash.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DominionSlash.cs index 499b1e56f3..97be3bdf1a 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DominionSlash.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/DominionSlash.cs @@ -1,6 +1,5 @@ namespace BossMod.Dawntrail.Alliance.A13ArkAngels; -// TODO: how does it really work? class DominionSlash(BossModule module) : Components.GenericAOEs(module) { public readonly List AOEs = []; @@ -20,7 +19,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if ((AID)spell.Action.ID is AID.DivineDominion or AID.DivineDominionFail) { ++NumCasts; - AOEs.RemoveAll(aoe => aoe.Origin.AlmostEqual(caster.Position, 1)); + AOEs.RemoveAll(aoe => aoe.Origin == caster.Position); } } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs index 70c25fdc7e..eefdee4583 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/HavokSpiral.cs @@ -3,35 +3,47 @@ namespace BossMod.Dawntrail.Alliance.A13ArkAngels; class HavokSpiral(BossModule module) : Components.GenericRotatingAOE(module) { private Angle _increment; + private DateTime _activation; + private readonly List _rotation = []; private static readonly AOEShapeCone _shape = new(30, 15.Degrees()); public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { - var increment = (IconID)iconID switch + _increment = (IconID)iconID switch { IconID.RotateCW => -30.Degrees(), IconID.RotateCCW => 30.Degrees(), _ => default }; - if (increment != default) - _increment = increment; + _activation = WorldState.FutureTime(5.5f); + InitIfReady(); + } + + 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)); + _rotation.Clear(); + _increment = default; + } } public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if ((AID)spell.Action.ID == AID.HavocSpiralFirst) { - Sequences.Add(new(_shape, caster.Position, spell.Rotation, _increment, Module.CastFinishAt(spell), 1.2f, 8)); + _rotation.Add(spell.Rotation); + InitIfReady(); } } public override void OnEventCast(Actor caster, ActorCastEvent spell) { if ((AID)spell.Action.ID is AID.HavocSpiralFirst or AID.HavocSpiralRest) - { AdvanceSequence(caster.Position, spell.Rotation, WorldState.CurrentTime); - } } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/Utsusemi.cs b/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/Utsusemi.cs deleted file mode 100644 index 1a5626f262..0000000000 --- a/BossMod/Modules/Dawntrail/Alliance/A13ArkAngels/Utsusemi.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace BossMod.Dawntrail.Alliance.A13ArkAngels; - -class Utsusemi(BossModule module) : BossComponent(module) -{ - private readonly List _clones = []; - - public override void AddHints(int slot, Actor actor, TextHints hints) - { - var source = TetheredClone(actor); - if (source != null) - hints.Add("Kite the add!", (source.Position - actor.Position).LengthSq() < 100); - } - - public override void DrawArenaForeground(int pcSlot, Actor pc) - { - var source = TetheredClone(pc); - if (source != null) - { - Arena.Actor(source, Colors.Object, true); - Arena.AddLine(source.Position, pc.Position, Colors.Danger); - } - } - - public override void OnTethered(Actor source, ActorTetherInfo tether) - { - if (tether.ID == (uint)TetherID.Utsusemi) - _clones.Add(source); - } - - public override void OnUntethered(Actor source, ActorTetherInfo tether) - { - if (tether.ID == (uint)TetherID.Utsusemi) - _clones.Remove(source); - } - - private Actor? TetheredClone(Actor target) => _clones.Find(c => c.Tether.Target == target.InstanceID); -} diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLord.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLord.cs index 31f9c06260..5211577d6d 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLord.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLord.cs @@ -1,6 +1,7 @@ namespace BossMod.Dawntrail.Alliance.A14ShadowLord; class TeraSlash(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.TeraSlash)); +class DoomArc(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.DoomArc)); class UnbridledRage(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeRect(100, 4), (uint)IconID.UnbridledRage, ActionID.MakeSpell(AID.UnbridledRageAOE), 5.9f); class DarkNova(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.DarkNova), 6); public class StayInBounds(BossModule module) : BossComponent(module) diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLordStates.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLordStates.cs index 95c20a181a..3c7b64e5dd 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLordStates.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/A14ShadowLordStates.cs @@ -22,6 +22,7 @@ public A14ShadowLordStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter(); } } diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs index 55779a1ab7..2d2537b0e5 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/CthonicFury.cs @@ -65,7 +65,7 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) } } -class EchoesOfAgony(BossModule module) : Components.StackWithIcon(module, (uint)IconID.EchoesOfAgony, ActionID.MakeSpell(AID.EchoesOfAgonyAOE), 5, 9.2f, 8, PartyState.MaxAllianceSize) +class EchoesOfAgony(BossModule module) : Components.StackWithIcon(module, (uint)IconID.EchoesOfAgony, ActionID.MakeSpell(AID.EchoesOfAgonyAOE), 5, 9.2f, PartyState.MaxAllianceSize, PartyState.MaxAllianceSize) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { diff --git a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/DamningStrikes.cs b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/DamningStrikes.cs index 3c51610f69..1af7dee26e 100644 --- a/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/DamningStrikes.cs +++ b/BossMod/Modules/Dawntrail/Alliance/A14ShadowLord/DamningStrikes.cs @@ -6,7 +6,7 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if ((AID)spell.Action.ID is AID.DamningStrikesImpact1 or AID.DamningStrikesImpact2 or AID.DamningStrikesImpact3) { - Towers.Add(new(caster.Position, 3, 8, 12, default, Module.CastFinishAt(spell))); + Towers.Add(new(caster.Position, 3, 8, 8, default, Module.CastFinishAt(spell))); } } @@ -15,7 +15,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) if ((AID)spell.Action.ID is AID.DamningStrikesImpact1 or AID.DamningStrikesImpact2 or AID.DamningStrikesImpact3) { ++NumCasts; - Towers.RemoveAll(t => t.Position.AlmostEqual(caster.Position, 1)); + Towers.RemoveAll(t => t.Position == caster.Position); var forbidden = Raid.WithSlot().WhereActor(a => spell.Targets.Any(t => t.ID == a.InstanceID)).Mask(); for (var i = 0; i < Towers.Count; ++i) { diff --git a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D011PrimePunutiy.cs b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D011PrimePunutiy.cs index d51c03764f..eb8d135a43 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D011PrimePunutiy.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D011PrimePunutiy.cs @@ -103,16 +103,68 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) class PunutiyPress(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.PunutiyPress)); class Hydrowave(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Hydrowave), new AOEShapeCone(60, 15.Degrees())); -class Bury1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury1), new AOEShapeCircle(12)); -class Bury2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury2), new AOEShapeRect(35, 5)); -class Bury3(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury3), new AOEShapeCircle(8)); -class Bury4(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury4), new AOEShapeCircle(4)); -class Bury5(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury5), new AOEShapeRect(25, 3)); -class Bury6(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury6), new AOEShapeCircle(6)); -class Bury7(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury7), new AOEShapeRect(25, 3)); -class Bury8(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Bury8), new AOEShapeRect(35, 5)); +class BuryDecay(BossModule module) : Components.GenericAOEs(module) +{ + private readonly List _aoes = []; + private static readonly AOEShape[] _shapes = [new AOEShapeCircle(12), new AOEShapeRect(35, 5), new AOEShapeCircle(8), new AOEShapeCircle(4), + new AOEShapeRect(25, 3), new AOEShapeCircle(6), new AOEShapeRect(25, 3), new AOEShapeRect(35, 5), new AOEShapeDonut(6, 40)]; + private static readonly HashSet castEnd = [AID.Bury1, AID.Bury2, AID.Bury3, AID.Bury4, AID.Bury5, AID.Bury6, AID.Bury7, AID.Bury8, AID.Decay]; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = _aoes.Count; + for (var i = 0; i < count; ++i) + { + var aoe = _aoes[i]; + if (i < 2) + yield return count > 1 ? aoe with { Color = Colors.Danger } : aoe; + else if (i > 1) + yield return aoe; + } + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + void AddAOE(AOEShape shape) + => _aoes.Add(new(shape, caster.Position, spell.Rotation, Module.CastFinishAt(spell))); + switch ((AID)spell.Action.ID) + { + case AID.Bury1: + AddAOE(_shapes[0]); + break; + case AID.Bury2: + AddAOE(_shapes[1]); + break; + case AID.Bury3: + AddAOE(_shapes[2]); + break; + case AID.Bury4: + AddAOE(_shapes[3]); + break; + case AID.Bury5: + AddAOE(_shapes[4]); + break; + case AID.Bury6: + AddAOE(_shapes[5]); + break; + case AID.Bury7: + AddAOE(_shapes[6]); + break; + case AID.Bury8: + AddAOE(_shapes[7]); + break; + case AID.Decay: + AddAOE(_shapes[8]); + break; + } + } -class Decay(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Decay), new AOEShapeDonut(6, 40)); + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if (_aoes.Count != 0 && castEnd.Contains((AID)spell.Action.ID)) + _aoes.RemoveAt(0); + } +} class PunutiyFlop1(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.PunutiyFlop1), 14); class PunutiyFlop2(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.PunutiyFlop2), 6); @@ -153,15 +205,7 @@ public D011PrimePunutiyStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter(); @@ -171,9 +215,10 @@ public D011PrimePunutiyStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 826, NameID = 12723)] public class D011PrimePunutiy(WorldState ws, Actor primary) : BossModule(ws, primary, new(35, -95), new ArenaBoundsSquare(19.5f)) { + private static readonly uint[] adds = [(uint)OID.Punutiy, (uint)OID.PetitPunutiy, (uint)OID.ProdigiousPunutiy]; protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.Punutiy).Concat(Enemies(OID.PetitPunutiy)).Concat(Enemies(OID.ProdigiousPunutiy))); + Arena.Actors(Enemies(adds)); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D012Drowsie.cs b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D012Drowsie.cs index 0c82d06852..5826747ed8 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D012Drowsie.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D012Drowsie.cs @@ -74,12 +74,12 @@ public D012DrowsieStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 826, NameID = 12716)] public class D012Drowsie(WorldState ws, Actor primary) : BossModule(ws, primary, DefaultBounds.Center, DefaultBounds) { - public static readonly ArenaBoundsComplex DefaultBounds = new([new Circle(new(80, 53), 19.5f)], [new Rectangle(new(65.5f, 38), 20, 1.75f, 130.Degrees()), new Rectangle(new(80, 74), 20, 2)]); - + public static readonly ArenaBoundsComplex DefaultBounds = new([new Polygon(new(80, 53), 19.5f, 32)], [new Rectangle(new(65.5f, 38), 20, 1.8f, 130.Degrees()), + new Rectangle(new(80, 74), 20, 2.15f)]); + private static readonly uint[] adds = [(uint)OID.Mimiclot1, (uint)OID.Mimiclot2, (uint)OID.Mimiclot3, (uint)OID.Mimiclot4, (uint)OID.Mimiclot5, (uint)OID.Mimiclot6]; protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.Mimiclot1).Concat(Enemies(OID.Mimiclot2)).Concat(Enemies(OID.Mimiclot3)).Concat(Enemies(OID.Mimiclot4)) - .Concat(Enemies(OID.Mimiclot5)).Concat(Enemies(OID.Mimiclot6))); + Arena.Actors(Enemies(adds)); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs index 0ff3bd7ff6..c90fdaf022 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D01Ihuykatumu/D013Apollyon.cs @@ -51,7 +51,7 @@ public enum AID : uint BitingWind = 36761 // Helper->player, no cast, single-target } -class Whirlwind(BossModule module) : Components.PersistentVoidzone(module, 4, m => m.Enemies(OID.Whirlwind), 5); +class Whirlwind(BossModule module) : Components.PersistentVoidzone(module, 4.5f, m => m.Enemies(OID.Whirlwind), 5); class RazorZephyr(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.RazorZephyr), new AOEShapeRect(50, 6)); class Blade(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.Blade)); class HighWind(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.HighWind)); @@ -86,13 +86,13 @@ class CuttingWind(BossModule module) : Components.GenericAOEs(module) [new WPos(-93, 251)] = [new(-111.688f, 253.942f), new(-102.276f, 264.313f), new(-108.922f, 276.528f)] // NW whirlwind }; - private static readonly float[] delays = [8.9f, 16.9f, 24.9f]; + private static readonly float[] delays = [8.6f, 16.7f, 24.7f]; private static readonly Angle[] angles = [89.999f.Degrees(), 44.998f.Degrees(), 134.999f.Degrees(), -0.003f.Degrees()]; private void AddAOEs(WPos pos, float delay) { - foreach (var angle in angles) - _aoes.Add(new(rect, pos, angle, WorldState.FutureTime(delay))); + for (var i = 0; i < angles.Length; ++i) + _aoes.Add(new(rect, pos, angles[i], WorldState.FutureTime(delay))); } public override void OnActorCreated(Actor actor) @@ -109,7 +109,7 @@ public override void OnActorCreated(Actor actor) public override void OnEventCast(Actor caster, ActorCastEvent spell) { - if (_aoes.Count > 0 && (AID)spell.Action.ID == AID.CuttingWind) + if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.CuttingWind) _aoes.RemoveAt(0); } } @@ -138,5 +138,5 @@ public D013ApollyonStates(BossModule module) : base(module) [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 826, NameID = 12711)] public class D013Apollyon(WorldState ws, Actor primary) : BossModule(ws, primary, DefaultBounds.Center, DefaultBounds) { - public static readonly ArenaBoundsComplex DefaultBounds = new([new Circle(new(-107, 265), 19.5f)], [new Rectangle(new(-107, 285.75f), 20, 2)]); + public static readonly ArenaBoundsComplex DefaultBounds = new([new Polygon(new(-107, 265), 19.5f, 32)], [new Rectangle(new(-107, 285.75f), 20, 2)]); } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D040VanguardSentryR7.cs b/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D040VanguardSentryR7.cs index 21e477e13e..bf6c621de8 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D040VanguardSentryR7.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D040VanguardSentryR7.cs @@ -27,7 +27,7 @@ public D040VanguardSentryR7States(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => module.Enemies(OID.SentryR7).Concat([module.PrimaryActor]).All(e => e.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(D040VanguardSentryR7.Trash).All(e => e.IsDeadOrDestroyed); } } @@ -38,10 +38,10 @@ public class D040VanguardSentryR7(WorldState ws, Actor primary) : BossModule(ws, new(-90.005f, 307.76f), new(-74.234f, 318.768f), new(-48.199f, 318.908f), new(-47.511f, 333.532f), new(-97.889f, 334.552f), new(-97.019f, 331.294f), new(-109.076f, 316.539f), new(-108.71f, 287.42f), new(-117.732f, 287.449f), new(-118.057f, 273.564f), new(-109.382f, 273.519f), new(-109.449f, 263.178f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.SentryR7]; protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.SentryR7)); + Arena.Actors(Enemies(Trash)); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D042Protector.cs b/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D042Protector.cs index cf78701dbf..68f170509a 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D042Protector.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D04Vanguard/D042Protector.cs @@ -130,7 +130,7 @@ public override void Update() new { AOE = electricFences00200010AOE, Bounds = electricFences00200010Arena } }; - for (var i = 0; i < aoeChecks.Length; ++i) + for (var i = 0; i < 4; ++i) { var aoe = aoeChecks[i]; if (ActiveAOEs(0, Raid.Player()!).Any(c => c.Shape == aoe.AOE && c.Activation <= WorldState.CurrentTime)) @@ -156,16 +156,16 @@ public override void OnEventEnvControl(byte index, uint state) switch (state) { case 0x08000400: - _aoe = new(electricFences08000400AOE, Module.Center, default, activation); + _aoe = new(electricFences08000400AOE, Arena.Center, default, activation); break; case 0x01000080: - _aoe = new(electricFences01000080AOE, Module.Center, default, activation); + _aoe = new(electricFences01000080AOE, Arena.Center, default, activation); break; case 0x00020001: - _aoe = new(electricFences00020001AOE, Module.Center, default, activation); + _aoe = new(electricFences00020001AOE, Arena.Center, default, activation); break; case 0x00200010: - _aoe = new(electricFences00200010AOE, Module.Center, default, activation); + _aoe = new(electricFences00200010AOE, Arena.Center, default, activation); break; case 0x02000004 or 0x10000004 or 0x00080004 or 0x00400004: Arena.Bounds = defaultBounds; @@ -209,10 +209,41 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) } class Shock(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Shock), 3); -class HomingCannon(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HomingCannon), new AOEShapeRect(50, 1)); + +class HomingCannon(BossModule module) : Components.GenericAOEs(module) +{ + private static readonly AOEShapeRect rect = new(50, 1); + private readonly List _aoes = []; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = _aoes.Count; + if (count > 0) + { + for (var i = 0; i < count; ++i) + { + var aoe = _aoes[i]; + yield return (aoe.Activation - _aoes[0].Activation).TotalSeconds <= 1 ? aoe with { Color = Colors.Danger } : aoe with { Risky = false }; + } + } + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == AID.HomingCannon) + _aoes.Add(new(rect, caster.Position, spell.Rotation, Module.CastFinishAt(spell))); + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.HomingCannon) + _aoes.RemoveAt(0); + } +} + class Bombardment(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Bombardment), 5); -class Electrowhirl(BossModule module, AID aid) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCircle(6)); +abstract class Electrowhirl(BossModule module, AID aid) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(aid), new AOEShapeCircle(6)); class Electrowhirl1(BossModule module) : Electrowhirl(module, AID.Electrowhirl1); class Electrowhirl2(BossModule module) : Electrowhirl(module, AID.Electrowhirl2); diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D091LindblumZaghnal.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D091LindblumZaghnal.cs index 3b0dd85af3..04d02ccaf8 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D091LindblumZaghnal.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D091LindblumZaghnal.cs @@ -35,11 +35,43 @@ public enum AID : uint Electrify = 40634, // RawElectrope->self, 16.0s cast, range 40 circle } -abstract class LineVoltage(BossModule module, AID aid, float halfWidth) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(aid), new AOEShapeRect(50, halfWidth)); -class LineVoltageWide1(BossModule module) : LineVoltage(module, AID.LineVoltageWide1, 5); -class LineVoltageWide2(BossModule module) : LineVoltage(module, AID.LineVoltageWide2, 5); -class LineVoltageNarrow1(BossModule module) : LineVoltage(module, AID.LineVoltageNarrow1, 2.5f); -class LineVoltageNarrow2(BossModule module) : LineVoltage(module, AID.LineVoltageNarrow2, 2.5f); +abstract class LineVoltage(BossModule module, AID narrow, float delay, AID? wide1 = null, AID? wide2 = null) : Components.GenericAOEs(module) +{ + private static readonly AOEShapeRect rectNarrow = new(50, 2.5f), rectWide = new(50, 5); + public readonly List AOEs = []; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = AOEs.Count; + if (count > 0) + { + for (var i = 0; i < count; ++i) + { + var aoe = AOEs[i]; + yield return (aoe.Activation - AOEs[0].Activation).TotalSeconds <= delay ? aoe with { Color = Colors.Danger } : aoe with { Risky = false }; + } + } + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == narrow) + AOEs.Add(new(rectNarrow, caster.Position, spell.Rotation, Module.CastFinishAt(spell))); + else if ((AID)spell.Action.ID == wide1 || (AID)spell.Action.ID == wide2) + AOEs.Add(new(rectWide, caster.Position, spell.Rotation, Module.CastFinishAt(spell))); + if (AOEs.Count > 1) + AOEs.SortBy(x => x.Activation); + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if (AOEs.Count != 0 && (AID)spell.Action.ID == narrow || (AID)spell.Action.ID == wide1 || (AID)spell.Action.ID == wide2) + AOEs.RemoveAt(0); + } +} + +class LineVoltage1(BossModule module) : LineVoltage(module, AID.LineVoltageNarrow1, 1); +class LineVoltage2(BossModule module) : LineVoltage(module, AID.LineVoltageNarrow2, 2, AID.LineVoltageWide1, AID.LineVoltageWide2); class LightningBolt(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.LightningBolt), 6); class LightningStorm(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.LightningStorm), 5); @@ -49,8 +81,9 @@ class SparkingFissureFirst(BossModule module) : Components.RaidwideCast(module, class CellShock(BossModule module) : Components.GenericAOEs(module) { - private static readonly AOEShapeCircle Circle = new(26); + private static readonly AOEShapeCircle circle = new(26); private AOEInstance? _aoe; + private readonly LineVoltage1 _aoes = module.FindComponent()!; private static readonly Dictionary initialPositions = new() { @@ -63,7 +96,7 @@ class CellShock(BossModule module) : Components.GenericAOEs(module) { 0x0D, 0x10 }, { 0x0E, 0x0F }, { 0x0F, 0x0E }, { 0x10, 0x0D } }; - public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); + public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes.AOEs.Count == 0 ? Utils.ZeroOrOne(_aoe) : []; public override void OnEventEnvControl(byte index, uint state) { @@ -73,7 +106,7 @@ public override void OnEventEnvControl(byte index, uint state) index = remappedIndex; if (initialPositions.TryGetValue(index, out var position)) - _aoe = new(Circle, position, default, WorldState.FutureTime(8)); + _aoe = new(circle, position, default, WorldState.FutureTime(8)); } } @@ -89,16 +122,14 @@ class D091LindblumZaghnalStates : StateMachineBuilder public D091LindblumZaghnalStates(BossModule module) : base(module) { TrivialPhase() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .ActivateOnEnter(); + .ActivateOnEnter() + .ActivateOnEnter(); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D092OverseerKanilokka.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D092OverseerKanilokka.cs index c8ed4d5c7e..a20da36a7a 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D092OverseerKanilokka.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D092OverseerKanilokka.cs @@ -81,9 +81,36 @@ private void SetArena(ArenaBounds bounds, WPos center) } } -abstract class Soulweave(BossModule module, AID aid) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(aid), new AOEShapeDonut(28, 32)); -class Soulweave1(BossModule module) : Soulweave(module, AID.Soulweave1); -class Soulweave2(BossModule module) : Soulweave(module, AID.Soulweave2); +class Soulweave(BossModule module) : Components.GenericAOEs(module) +{ + private static readonly AOEShapeDonut donut = new(28, 32); + private readonly List _aoes = []; + + public override IEnumerable ActiveAOEs(int slot, Actor actor) + { + var count = _aoes.Count; + if (count > 0) + { + for (var i = 0; i < count; ++i) + { + var aoe = _aoes[i]; + yield return (aoe.Activation - _aoes[0].Activation).TotalSeconds <= 1.2 ? aoe with { Color = Colors.Danger } : aoe with { Risky = false }; + } + } + } + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID is AID.Soulweave1 or AID.Soulweave2) + _aoes.Add(new(donut, spell.LocXZ, default, Module.CastFinishAt(spell))); + } + + public override void OnCastFinished(Actor caster, ActorCastInfo spell) + { + if (_aoes.Count != 0 && (AID)spell.Action.ID is AID.Soulweave1 or AID.Soulweave2) + _aoes.RemoveAt(0); + } +} class FreeSpirits(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.FreeSpirits)); class Bloodburst(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Bloodburst)); @@ -124,8 +151,7 @@ public D092OverseerKanilokkaStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .ActivateOnEnter() - .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() @@ -140,7 +166,6 @@ public D092OverseerKanilokkaStates(BossModule module) : base(module) public class D092OverseerKanilokka(WorldState ws, Actor primary) : BossModule(ws, primary, StartingBounds.Center, StartingBounds) { private const int Edges = 60; - private const float Offset = -0.5f; // pathfinding offset public static readonly WPos ArenaCenter = new(116, -66); public static readonly Polygon[] StartingPolygon = [new Polygon(ArenaCenter, 19.5f * CosPI.Pi60th, Edges)]; public static readonly Polygon[] TinyPolygon = [new Polygon(ArenaCenter, 5, Edges)]; @@ -172,7 +197,7 @@ public class D092OverseerKanilokka(WorldState ws, Actor primary) : BossModule(ws public static readonly ArenaBoundsComplex TinyArena = new(TinyPolygon, MapResolution: 0.1f); private static readonly DonutV[] difference = [new DonutV(ArenaCenter, 19.5f, 22, Edges)]; public static readonly ArenaBoundsComplex ArenaENVC00800040 = new([new PolygonCustom(vertices00800040North), new PolygonCustom(vertices00800040East), - new PolygonCustom(vertices00800040South), new PolygonCustom(vertices00800040West), ..TinyPolygon], difference, Offset: Offset); + new PolygonCustom(vertices00800040South), new PolygonCustom(vertices00800040West), ..TinyPolygon], difference); public static readonly ArenaBoundsComplex ArenaENVC02000100 = new([new PolygonCustom(vertices02000100East), new PolygonCustom(vertices02000100North), - new PolygonCustom(vertices02000100West), ..TinyPolygon], difference, Offset: Offset); + new PolygonCustom(vertices02000100West), ..TinyPolygon], difference); } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D093Lunipyati.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D093Lunipyati.cs index 063c3a4982..324cfd34c7 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D093Lunipyati.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D093Lunipyati.cs @@ -147,7 +147,8 @@ class LeapingEarth(BossModule module) : Components.GenericAOEs(module) new(40, -711), new(38.7f, -705), new(34, -701.5f), new(28, -701.4f), new(24, -704.399f), new(22, -709.7f), new(23.1f, -715.099f), new(26.5f, -719.499f), new(32, -721.699f), new(38, -721.5f), new(43, -717.999f), new(45.7f, -712.699f), new(45.9f, -706.699f), new(42.9f, -701.2f), new(38.5f, -697), new(32.5f, -695.199f)]; - public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes.Take(16); + private int maxCasts; + public override IEnumerable ActiveAOEs(int slot, Actor actor) => _aoes.Take(maxCasts); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { @@ -167,12 +168,14 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) GenerateAOEsForMixedPattern(45, -45); _aoes.SortBy(x => x.Activation); angles.Clear(); + maxCasts = 16; } else if ((AID)spell.Action.ID == AID.LeapingEarthVisual2) { var rotatedPoints = WPos.GenerateRotatedVertices(D093Lunipyati.ArenaCenter, spiralBigPoints, spell.Rotation.Rad * Angle.RadToDeg); for (var i = 0; i < 20; ++i) - _aoes.Add(new(circle, rotatedPoints[i], default, WorldState.FutureTime(4.5f + 0.2f * i))); + _aoes.Add(new(circle, rotatedPoints[i], default, WorldState.FutureTime(4.5f + 0.25f * i))); + maxCasts = 10; } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestBat.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestBat.cs index 9c5a781a6f..ba9f62cf64 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestBat.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestBat.cs @@ -26,8 +26,7 @@ public D90ForestBatStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly && x.Position.AlmostEqual(Module.Arena.Center, Module.Bounds.Radius)) - .All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(D90ForestBat.Trash).Where(x => x.Position.AlmostEqual(module.Arena.Center, module.Bounds.Radius)).All(x => x.IsDeadOrDestroyed); } } @@ -63,11 +62,12 @@ public class D90ForestBat(WorldState ws, Actor primary) : BossModule(ws, primary new(-35.64f, 482.3f), new(-35.43f, 481.68f), new(-35.42f, 479.49f), new(-34.93f, 471.44f), new(-34.95f, 470.74f), new(-34.79f, 470.06f), new(-34.9f, 469.45f), new(-35.73f, 468.5f), new(-16.78f, 468.11f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.Electrogolem1, (uint)OID.Electrogolem1, (uint)OID.ForestWoolback]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); + Arena.Actors(Enemies(Trash).Where(x => x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestWoolback.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestWoolback.cs index 5cbe9b37a5..9158cbd445 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestWoolback.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90ForestWoolback.cs @@ -26,8 +26,7 @@ public D90ForestWoolbackStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly && x.Position.AlmostEqual(Module.Arena.Center, Module.Bounds.Radius)) - .All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(D90ForestWoolback.Trash).Where(x => x.Position.AlmostEqual(module.Arena.Center, module.Bounds.Radius)).All(x => x.IsDeadOrDestroyed); } } @@ -74,11 +73,12 @@ public class D90ForestWoolback(WorldState ws, Actor primary) : BossModule(ws, pr new(51.35f, 360.61f), new(51.7f, 360.07f), new(51.99f, 359.46f), new(52.32f, 359.07f), new(52.99f, 358.92f), new(53.49f, 358.63f), new(53.9f, 358.33f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.ForestAxeBeak, (uint)OID.ForestWoolback, (uint)OID.Electrogolem]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); + Arena.Actors(Enemies(Trash).Where(x => x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90RottenHound.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90RottenHound.cs index d0e6f539b3..9a5ef7f2f6 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90RottenHound.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90RottenHound.cs @@ -23,8 +23,7 @@ class D90RottenResearcherStates : StateMachineBuilder public D90RottenResearcherStates(BossModule module) : base(module) { TrivialPhase() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Module.Arena.Center, Module.Bounds.Radius)) - .All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(D90RottenResearcher.Trash).Where(x => x.Position.AlmostEqual(module.Arena.Center, module.Bounds.Radius)).All(x => x.IsDeadOrDestroyed); } } @@ -76,11 +75,13 @@ public class D90RottenResearcher(WorldState ws, Actor primary) : BossModule(ws, new(46.76f, 60.97f), new(46.88f, 60.43f), new(47.34f, 60.15f), new(50.18f, 60.28f), new(50.73f, 59.9f), new(54.67f, 59.58f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.RottenResearcher1, (uint)OID.RottenResearcher2, (uint)OID.RottenResearcher3, + (uint)OID.RottenHound1, (uint)OID.RottenHound2]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); + Arena.Actors(Enemies(Trash).Where(x => x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyClayGolem.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyClayGolem.cs index a95f303bb9..7f8033c651 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyClayGolem.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyClayGolem.cs @@ -27,8 +27,7 @@ public D90SprightlyClayGolemStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly && x.Position.AlmostEqual(Module.Arena.Center, Module.Bounds.Radius)) - .All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(D90SprightlyClayGolem.Trash).Where(x => x.Position.AlmostEqual(module.Arena.Center, module.Bounds.Radius)).All(x => x.IsDeadOrDestroyed); } } @@ -79,11 +78,12 @@ public class D90SprightlyClayGolem(WorldState ws, Actor primary) : BossModule(ws new(93.72f, -413.32f), new(93.46f, -413.92f), new(93, -414.4f), new(93.22f, -414.95f), new(106.99f, -419.44f), new(107.5f, -419.32f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.SprightlyStone1, (uint)OID.SprightlyStone2, (uint)OID.SprightlyDhara]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); + Arena.Actors(Enemies(Trash).Where(x => x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyPhoebad.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyPhoebad.cs index 4684e1b13c..688c26882c 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyPhoebad.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90SprightlyPhoebad.cs @@ -28,8 +28,7 @@ public D90SprightlyPhoebadStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly && x.Position.AlmostEqual(Module.Arena.Center, Module.Bounds.Radius)) - .All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(D90SprightlyPhoebad.Trash).Where(x => x.Position.AlmostEqual(module.Arena.Center, module.Bounds.Radius)).All(x => x.IsDeadOrDestroyed); } } @@ -57,11 +56,12 @@ public class D90SprightlyPhoebad(WorldState ws, Actor primary) : BossModule(ws, new(108.36f, -289.32f), new(108.72f, -289.86f), new(108.89f, -290.36f), new(110.23f, -292.79f), new(110.57f, -293.24f), new(110.83f, -293.82f), new(111.77f, -295.5f), new(112.26f, -296.71f), new(113.35f, -298.45f), new(113.98f, -298.78f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.SprightlyMole, (uint)OID.SprightlyStone, (uint)OID.SprightlyLoamkeep]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); + Arena.Actors(Enemies(Trash).Where(x => x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); } } diff --git a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90StationSpecter.cs b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90StationSpecter.cs index 95db110121..c42e438e44 100644 --- a/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90StationSpecter.cs +++ b/BossMod/Modules/Dawntrail/Dungeon/D09YuweyawataFieldStation/D90StationSpecter.cs @@ -29,8 +29,7 @@ public D90StationSpecterStates(BossModule module) : base(module) TrivialPhase() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Module.Arena.Center, Module.Bounds.Radius)) - .All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => Module.Enemies(D90StationSpecter.Trash).Where(x => x.Position.AlmostEqual(module.Arena.Center, module.Bounds.Radius)).All(x => x.IsDeadOrDestroyed); } } @@ -75,11 +74,13 @@ public class D90StationSpecter(WorldState ws, Actor primary) : BossModule(ws, pr new(110.65f, -18.55f), new(110.54f, -19.18f), new(110.07f, -19.71f), new(109.97f, -23.91f), new(110.22f, -24.35f), new(110.66f, -24.76f), new(111.26f, -25.17f), new(111.95f, -25.35f), new(117.43f, -25.65f)]; private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + public static readonly uint[] Trash = [(uint)OID.Boss, (uint)OID.RottenResearcher1, (uint)OID.RottenResearcher2, (uint)OID.RottenResearcher3, + (uint)OID.GiantCorse, (uint)OID.StationSpecter]; - protected override bool CheckPull() => WorldState.Actors.Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); + protected override bool CheckPull() => Enemies(Trash).Any(x => x.InCombat && x.Position.AlmostEqual(Arena.Center, Bounds.Radius)); protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); + Arena.Actors(Enemies(Trash).Where(x => x.Position.AlmostEqual(Arena.Center, Bounds.Radius))); } } diff --git a/BossMod/Modules/Dawntrail/Hunt/RankA/Keheniheyamewi.cs b/BossMod/Modules/Dawntrail/Hunt/RankA/Keheniheyamewi.cs index b9e32fd6fc..ca226915f7 100644 --- a/BossMod/Modules/Dawntrail/Hunt/RankA/Keheniheyamewi.cs +++ b/BossMod/Modules/Dawntrail/Hunt/RankA/Keheniheyamewi.cs @@ -155,4 +155,4 @@ public KeheniheyamewiStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Shinryin", GroupType = BossModuleInfo.GroupType.Hunt, GroupID = (uint)BossModuleInfo.HuntRank.A, NameID = 13401)] -public class Keheniheyamewi(WorldState ws, Actor primary) : SimpleBossModule(ws, primary); \ No newline at end of file +public class Keheniheyamewi(WorldState ws, Actor primary) : SimpleBossModule(ws, primary); diff --git a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/BullApollyon.cs b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/BullApollyon.cs index 227d3a579f..65e2b9a22e 100644 --- a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/BullApollyon.cs +++ b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/BullApollyon.cs @@ -90,18 +90,21 @@ public BullApollyonStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(BullApollyon.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Kismet, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 993, NameID = 13247)] -public class BullApollyon(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -372), new ArenaBoundsCircle(20)) +public class BullApollyon(WorldState ws, Actor primary) : SharedBounds(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.TuraliEggplant, (uint)OID.TuraliTomato, (uint)OID.TuligoraQueen, (uint)OID.TuraliGarlic, + (uint)OID.TuraliOnion, (uint)OID.UolonOfFortune]; + public static readonly uint[] All = [(uint)OID.Boss, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.TuraliEggplant).Concat(Enemies(OID.TuraliTomato)).Concat(Enemies(OID.TuligoraQueen)).Concat(Enemies(OID.TuraliGarlic)) - .Concat(Enemies(OID.TuraliOnion)).Concat(Enemies(OID.UolonOfFortune)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/GoldenMolter.cs b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/GoldenMolter.cs index 81f4614299..ca5cd85197 100644 --- a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/GoldenMolter.cs +++ b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/GoldenMolter.cs @@ -64,10 +64,12 @@ public enum SID : uint class Lap(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.Lap)); class Lightburst(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Lightburst)); + class Crypsis(BossModule module) : BossComponent(module) { private bool IsConcealed; - private readonly int RevealDistance = 9; + private const int RevealDistance = 9; + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { if (IsConcealed) @@ -160,18 +162,21 @@ public GoldenMolterStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.OID != (uint)OID.Helper).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GoldenMolter.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Contributed, Contributors = "Kismet, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 993, NameID = 13248)] -public class GoldenMolter(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -372), new ArenaBoundsCircle(20)) +public class GoldenMolter(WorldState ws, Actor primary) : SharedBounds(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.TuraliEggplant, (uint)OID.TuraliTomato, (uint)OID.TuligoraQueen, (uint)OID.TuraliGarlic, + (uint)OID.TuraliOnion, (uint)OID.UolonOfFortune, (uint)OID.AlpacaOfFortune]; + public static readonly uint[] All = [(uint)OID.Boss, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor, allowDeadAndUntargetable: true); - Arena.Actors(Enemies(OID.TuraliEggplant).Concat(Enemies(OID.TuraliTomato)).Concat(Enemies(OID.TuligoraQueen)).Concat(Enemies(OID.TuraliGarlic)) - .Concat(Enemies(OID.TuraliOnion)).Concat(Enemies(OID.UolonOfFortune)).Concat(Enemies(OID.AlpacaOfFortune)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/SharedArenaBounds.cs b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/SharedArenaBounds.cs new file mode 100644 index 0000000000..0bb14c5f35 --- /dev/null +++ b/BossMod/Modules/Dawntrail/TreasureHunt/CenoteJaJaGural/SharedArenaBounds.cs @@ -0,0 +1,6 @@ +namespace BossMod.Dawntrail.TreasureHunt.CenoteJaJaGural; + +public abstract class SharedBounds(WorldState ws, Actor primary) : BossModule(ws, primary, ArenaBounds.Center, ArenaBounds) +{ + public static readonly ArenaBoundsComplex ArenaBounds = new([new Polygon(new(0, -372), 19.5f * CosPI.Pi32th, 32)], [new Rectangle(new(0, -352), 20, 1.65f)]); +} diff --git a/BossMod/Modules/Dawntrail/Trial/T03QueenEternal/WaltzOfTheRegalia.cs b/BossMod/Modules/Dawntrail/Trial/T03QueenEternal/WaltzOfTheRegalia.cs index 7110500861..0fb5415c4b 100644 --- a/BossMod/Modules/Dawntrail/Trial/T03QueenEternal/WaltzOfTheRegalia.cs +++ b/BossMod/Modules/Dawntrail/Trial/T03QueenEternal/WaltzOfTheRegalia.cs @@ -31,7 +31,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell) public override void OnActorDestroyed(Actor actor) { // not sure if needed, just a safeguard incase the removal by OnEventCast failed for whatever reason - if (_targets.Count > 0 && (OID)actor.OID == OID.QueenEternal3) + if (_targets.Count != 0 && (OID)actor.OID == OID.QueenEternal3) _targets.RemoveAll(x => x.Item1 == actor); } } diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheExcitatron6000/LuckyFace.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheExcitatron6000/LuckyFace.cs index 1449e25944..761f1f88cd 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheExcitatron6000/LuckyFace.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheExcitatron6000/LuckyFace.cs @@ -106,17 +106,22 @@ public LuckyFaceStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(LuckyFace.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 819, NameID = 10831)] -public class LuckyFace(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -460), new ArenaBoundsCircle(20)) +public class LuckyFace(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { + private static readonly ArenaBoundsComplex arena = new([new Polygon(new(0, -460), 19.5f, 32)], [new Rectangle(new(0, -440), 20, 1)]); + private static readonly uint[] bonusAdds = [(uint)OID.ExcitingEgg, (uint)OID.ExcitingQueen, (uint)OID.ExcitingOnion, (uint)OID.ExcitingTomato, + (uint)OID.ExcitingGarlic]; + public static readonly uint[] All = [(uint)OID.Boss, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.ExcitingEgg).Concat(Enemies(OID.ExcitingTomato)).Concat(Enemies(OID.ExcitingQueen)).Concat(Enemies(OID.ExcitingGarlic)).Concat(Enemies(OID.ExcitingOnion)), Colors.Vulnerable); + Arena.Actors(Enemies(OID.Boss)); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs index 24f96de096..ace542f23a 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouAcheloios.cs @@ -160,19 +160,22 @@ public GymnasiouAcheloiosStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouAcheloios.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12019)] -public class GymnasiouAcheloios(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouAcheloios(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa, (uint)OID.GymnasiouLampas]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouSouchos, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouSouchos)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)).Concat(Enemies(OID.GymnasiouLyssa)).Concat(Enemies(OID.GymnasiouLampas)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouLeon.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouLeon.cs index 789a6d1d95..4ba2a46070 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouLeon.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouLeon.cs @@ -71,19 +71,22 @@ public GymnasiouLeonStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouLeon.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 11997)] -public class GymnasiouLeon(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouLeon(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouLeonMikros, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouLeonMikros)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)).Concat(Enemies(OID.GymnasiouLyssa)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMandragoras.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMandragoras.cs index ad05469a6c..bae3fcdefc 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMandragoras.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMandragoras.cs @@ -52,19 +52,22 @@ public GymnasiouMandragorasStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouMandragoras.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12022)] -public class GymnasiouMandragoras(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouMandragoras(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouKorrigan, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouKorrigan)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMegakantha.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMegakantha.cs index 1977a596c2..94b9537a99 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMegakantha.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMegakantha.cs @@ -103,19 +103,22 @@ public GymnasiouMegakanthaStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouMegakantha.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12009)] -public class GymnasiouMegakantha(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouMegakantha(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa, (uint)OID.GymnasiouLampas]; + private static readonly uint[] rest = [(uint)OID.Boss, (uint)OID.GymnasiouSinapi, (uint)OID.GymnasiouAkantha]; + public static readonly uint[] All = [.. rest, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.GymnasiouSinapi).Concat(Enemies(OID.GymnasiouAkantha))); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)).Concat(Enemies(OID.GymnasiouLyssa)).Concat(Enemies(OID.GymnasiouLampas)), Colors.Vulnerable); + Arena.Actors(Enemies(rest)); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMeganereis.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMeganereis.cs index 08eb01d5ee..438674f584 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMeganereis.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouMeganereis.cs @@ -83,19 +83,22 @@ public GymnasiouMeganereisStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouMeganereis.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12014)] -public class GymnasiouMeganereis(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouMeganereis(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa, (uint)OID.GymnasiouLampas]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouNereis, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouNereis)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)).Concat(Enemies(OID.GymnasiouLyssa)).Concat(Enemies(OID.GymnasiouLampas)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouPithekos.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouPithekos.cs index ad8cc69b29..00d9d5268e 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouPithekos.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouPithekos.cs @@ -108,19 +108,22 @@ public GymnasiouPithekosStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouPithekos.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12001)] -public class GymnasiouPithekos(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouPithekos(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouPithekosMikros, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouPithekosMikros)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)).Concat(Enemies(OID.GymnasiouLyssa)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSatyros.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSatyros.cs index 568e309671..8611351f21 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSatyros.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSatyros.cs @@ -49,18 +49,21 @@ public GymnasiouSatyrosStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouSatyros.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12003)] -public class GymnasiouSatyros(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouSatyros(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasiouLampas, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouElaphos, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouElaphos)); - Arena.Actors(Enemies(OID.GymnasiouLampas).Concat(Enemies(OID.GymnasiouLyssa)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSphinx.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSphinx.cs index 0a29b9c2b7..24e37ffdac 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSphinx.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouSphinx.cs @@ -78,19 +78,22 @@ public GymnasiouSphinxStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouSphinx.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12016)] -public class GymnasiouSphinx(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouSphinx(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa, (uint)OID.GymnasiouLampas]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouGryps, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouGryps)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion)).Concat(Enemies(OID.GymnasiouLyssa)).Concat(Enemies(OID.GymnasiouLampas)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouStyphnolobion.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouStyphnolobion.cs index 80b4a4067c..45b2000f76 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouStyphnolobion.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouStyphnolobion.cs @@ -94,19 +94,22 @@ public GymnasiouStyphnolobionStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouStyphnolobion.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12012)] -public class GymnasiouStyphnolobion(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouStyphnolobion(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouHippogryph, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouHippogryph)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion).Concat(Enemies(OID.GymnasiouLyssa))), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTigris.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTigris.cs index 63e0602937..d807f0c0b4 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTigris.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTigris.cs @@ -63,19 +63,22 @@ public GymnasiouTigrisStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouTigris.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 11999)] -public class GymnasiouTigris(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouTigris(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouTigrisMikra, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); Arena.Actors(Enemies(OID.GymnasiouTigrisMikra)); - Arena.Actors(Enemies(OID.GymnasticEggplant).Concat(Enemies(OID.GymnasticTomato)).Concat(Enemies(OID.GymnasticQueen)).Concat(Enemies(OID.GymnasticGarlic)) - .Concat(Enemies(OID.GymnasticOnion).Concat(Enemies(OID.GymnasiouLyssa))), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTriton.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTriton.cs index 4993b3b3a6..4a59964e74 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTriton.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/GymnasiouTriton.cs @@ -60,17 +60,22 @@ public GymnasiouTritonStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(GymnasiouTriton.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12006)] -public class GymnasiouTriton(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GymnasiouTriton(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasticEggplant, (uint)OID.GymnasticGarlic, (uint)OID.GymnasticOnion, (uint)OID.GymnasticTomato, + (uint)OID.GymnasticQueen]; + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouEcheneis, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.HitboxRadius > 0.85f)); - Arena.Actors(WorldState.Actors.Where(x => !x.IsAlly && x.HitboxRadius < 0.85f), Colors.Vulnerable); + Arena.Actor(PrimaryActor); + Arena.Actors(Enemies(OID.GymnasiouEcheneis)); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LampasChrysine.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LampasChrysine.cs index 907eeb41bf..465d6a2239 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LampasChrysine.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LampasChrysine.cs @@ -51,13 +51,15 @@ public LampasChrysineStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(LampasChrysine.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12021)] -public class LampasChrysine(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class LampasChrysine(WorldState ws, Actor primary) : THTemplate(ws, primary) { + public static readonly uint[] All = [(uint)OID.Boss, (uint)OID.GymnasiouLampas]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs index 2e111ace10..3d6555b82c 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/LyssaChrysine.cs @@ -124,17 +124,20 @@ public LyssaChrysineStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(LyssaChrysine.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12024)] -public class LyssaChrysine(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class LyssaChrysine(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasiouLampas, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.GymnasiouLyssa).Concat(Enemies(OID.GymnasiouLampas)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/Narkissos.cs b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/Narkissos.cs index ce770342c2..a05beccbbc 100644 --- a/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/Narkissos.cs +++ b/BossMod/Modules/Endwalker/TreasureHunt/TheShiftingGymnasionAgonon/Narkissos.cs @@ -112,17 +112,20 @@ public NarkissosStates(BossModule module) : base(module) .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() - .Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable).All(x => x.IsDeadOrDestroyed); + .Raw.Update = () => module.Enemies(Narkissos.All).All(x => x.IsDeadOrDestroyed); } } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 909, NameID = 12029)] -public class Narkissos(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class Narkissos(WorldState ws, Actor primary) : THTemplate(ws, primary) { + private static readonly uint[] bonusAdds = [(uint)OID.GymnasiouLampas, (uint)OID.GymnasiouLyssa]; + public static readonly uint[] All = [(uint)OID.Boss, .. bonusAdds]; + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); - Arena.Actors(Enemies(OID.GymnasiouLyssa).Concat(Enemies(OID.GymnasiouLampas)), Colors.Vulnerable); + Arena.Actors(Enemies(bonusAdds), Colors.Vulnerable); } protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) diff --git a/BossMod/Modules/Heavensward/Dungeon/D05GreatGubalLibrary/D053TheEverlivingBibliotaph.cs b/BossMod/Modules/Heavensward/Dungeon/D05GreatGubalLibrary/D053TheEverlivingBibliotaph.cs index a321b677be..0729562ee8 100644 --- a/BossMod/Modules/Heavensward/Dungeon/D05GreatGubalLibrary/D053TheEverlivingBibliotaph.cs +++ b/BossMod/Modules/Heavensward/Dungeon/D05GreatGubalLibrary/D053TheEverlivingBibliotaph.cs @@ -84,7 +84,7 @@ public override void OnActorCreated(Actor actor) public override void OnCastFinished(Actor caster, ActorCastInfo spell) { - if (_aoes.Count > 0 && (AID)spell.Action.ID == AID.VoidSpark) + if (_aoes.Count != 0 && (AID)spell.Action.ID == AID.VoidSpark) _aoes.RemoveAt(0); } } @@ -95,7 +95,7 @@ class VoidBlizzardIIIAOE(BossModule module) : Components.LocationTargetedAOEs(mo class AbyssalSwing(BossModule module) : Components.Cleave(module, ActionID.MakeSpell(AID.AbyssalSwing), new AOEShapeCone(7.5f, 45.Degrees()), (uint)OID.Biblioklept); class AbyssalCharge(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.AbyssalCharge), new AOEShapeRect(41, 2)); -class VoidCall(BossModule module) : Components.GenericTowers(module) +class VoidCall(BossModule module) : Components.GenericTowers(module, prioritizeInsufficient: true) { public override void OnActorCreated(Actor actor) { diff --git a/BossMod/Modules/Shadowbringers/Dungeon/D02DohnMheg/D023AencThon.cs b/BossMod/Modules/Shadowbringers/Dungeon/D02DohnMheg/D023AencThon.cs index 750835f689..4a6438d7cf 100644 --- a/BossMod/Modules/Shadowbringers/Dungeon/D02DohnMheg/D023AencThon.cs +++ b/BossMod/Modules/Shadowbringers/Dungeon/D02DohnMheg/D023AencThon.cs @@ -56,10 +56,6 @@ class BileBombardment(BossModule module) : Components.LocationTargetedAOEs(modul class FunambulistsFantasia(BossModule module) : BossComponent(module) { - private bool WaypointsAdded; - private static readonly WPos[] waypoints = [new(-142.88f, -233.2f), new(-140.89f, -246.16f), new(-129.9f, -242.38f), new(-114.19f, -244.35f), - new(-125.81f, -249.33f), new(-123.5f, -256.17f)]; - public override void OnCastFinished(Actor caster, ActorCastInfo spell) { if ((AID)spell.Action.ID == AID.FunambulistsFantasia) @@ -68,41 +64,14 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell) Arena.Bounds = D033AencThon.arena; } - public override void OnStatusGain(Actor actor, ActorStatus status) - { - if ((SID)status.ID == SID.FoolsTumble && actor == Module.Raid.Player()) - WaypointsAdded = false; - } - public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if (AI.AIManager.Instance?.Beh == null) - return; var lyre = Module.Enemies(OID.LiarsLyre).FirstOrDefault(); - hints.WaypointManager.module = Module; - hints.WaypointManager.UpdateCurrentWaypoint(actor.Position); - if (hints.WaypointManager.HasWaypoints) - { - var currentWaypoint = hints.WaypointManager.CurrentWaypoint; - if (currentWaypoint.HasValue) - { - hints.ForcedMovement = (currentWaypoint.Value - actor.Position).ToVec3(); - } - } if (Arena.Bounds == D033AencThon.chasmArena && lyre != null) { hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Sprint), actor, ActionQueue.Priority.High); hints.AddForbiddenZone(ShapeDistance.InvertedCircle(lyre.Position, 3)); - if (!WaypointsAdded) - { - hints.WaypointManager.ClearWaypoints(); - hints.WaypointManager.WaypointTimeLimit = 10; - WaypointsAdded = true; - hints.WaypointManager.AddWaypointsWithRandomization(waypoints, 0.1f, 10); - } } - else if (Arena.Bounds == D033AencThon.arena) - hints.WaypointManager.ClearWaypoints(); } } @@ -192,7 +161,7 @@ public class D033AencThon(WorldState ws, Actor primary) : BossModule(ws, primary private static readonly PolygonCustom[] union2 = [new([new(-142.32f, -233.89f), new(-140.52f, -245.64f), new(-129.91f, -241.9f), new(-113.72f, -243.84f), new(-113.81f, -244.74f), new(-125.19f, -249.54f), new(-123.72f, -254.08f), new(-124.58f, -254.05f), new(-126.13f, -249.73f), new(-126.39f, -249.05f), new(-115.51f, -244.47f), new(-129.9f, -242.73f), new(-140.47f, -246.47f), new(-141.19f, -246.74f), new(-143.12f, -233.92f)])]; - public static readonly ArenaBoundsComplex chasmArena = new(union, [.. difference, new Rectangle(new(-128.5f, -244), 20, 10)], union2); + public static readonly ArenaBoundsComplex chasmArena = new(union, [.. difference, new Rectangle(new(-128.5f, -244), 20, 10)], union2, 0.25f); protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheDungeonsOfLyheGhiah/Goliath.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheDungeonsOfLyheGhiah/Goliath.cs index ed4b6c7e7d..72d59d1404 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheDungeonsOfLyheGhiah/Goliath.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheDungeonsOfLyheGhiah/Goliath.cs @@ -77,8 +77,36 @@ public GoliathStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 688, NameID = 8953)] -public class Goliath(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -390), new ArenaBoundsCircle(20)) +public class Goliath(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { + private static readonly WPos[] vertices = [new(14.82f, -416.43f), new(15.44f, -416.52f), new(15.93f, -416.35f), new(16.07f, -415.68f), new(16.5f, -415.19f), + new(16.96f, -414.83f), new(17.51f, -414.6f), new(18.1f, -414.65f), new(18.68f, -414.76f), new(19.2f, -415.17f), + new(19.58f, -415.61f), new(20.2f, -415.48f), new(25.7f, -409.9f), new(25.42f, -409.38f), new(24.96f, -408.85f), + new(24.7f, -408.28f), new(24.59f, -407.65f), new(24.77f, -407.03f), new(25.06f, -406.47f), new(25.53f, -406.09f), + new(26.12f, -405.86f), new(26.31f, -405.2f), new(26.42f, -404.55f), new(26.82f, -403.14f), new(26.89f, -402.41f), + new(26.23f, -402.21f), new(25.56f, -402.16f), new(24.96f, -402.45f), new(23.87f, -403.13f), new(23.28f, -403.4f), + new(22.6f, -403.55f), new(21.94f, -403.64f), new(20.7f, -403.52f), new(20.06f, -403.35f), new(19.47f, -403.15f), + new(18.28f, -402.6f), new(16.62f, -401.59f), new(16.22f, -401.18f), new(16.43f, -400.63f), new(17.95f, -397.79f), + new(18.19f, -397.2f), new(19.1f, -394.21f), new(19.24f, -393.55f), new(19.57f, -390.25f), new(19.22f, -386.22f), + new(18.08f, -382.45f), new(16.2f, -379.01f), new(13.69f, -376.02f), new(11.09f, -373.88f), new(10.54f, -373.53f), + new(4.02f, -370.55f), new(-3.7f, -370.53f), new(-4.26f, -370.63f), new(-7.46f, -371.88f), new(-10.85f, -373.69f), + new(-13.99f, -376.31f), new(-16.19f, -378.99f), new(-18.17f, -382.71f), new(-19.22f, -386.21f), new(-19.58f, -390.05f), + new(-19.18f, -393.95f), new(-18.2f, -397.19f), new(-17.93f, -397.82f), new(-16.9f, -399.73f), new(-16.4f, -400.11f), + new(-19.26f, -403.06f), new(-19.84f, -403.32f), new(-20.51f, -403.49f), new(-21.14f, -403.59f), new(-21.81f, -403.63f), + new(-22.45f, -403.58f), new(-23.15f, -403.43f), new(-23.74f, -403.23f), new(-25.37f, -402.2f), new(-26.07f, -402.17f), + new(-26.73f, -402.35f), new(-26.88f, -402.99f), new(-26.35f, -404.91f), new(-26.43f, -405.51f), new(-25.94f, -405.9f), + new(-25.4f, -406.21f), new(-24.91f, -406.65f), new(-24.69f, -407.24f), new(-24.6f, -407.86f), new(-24.71f, -408.52f), + new(-25.06f, -409), new(-25.63f, -410), new(-19.96f, -415.7f), new(-19.46f, -415.43f), new(-19.01f, -415.03f), + new(-18.48f, -414.74f), new(-17.82f, -414.61f), new(-17.22f, -414.76f), new(-16.66f, -414.99f), new(-16.28f, -415.44f), + new(-16, -415.95f), new(-15.89f, -416.49f), new(-15.38f, -416.25f), new(-14.79f, -416.26f), new(-12.91f, -416.85f), + new(-12.48f, -416.31f), new(-12.34f, -415.66f), new(-12.57f, -415.01f), new(-13.56f, -413.38f), new(-13.85f, -412.06f), + new(-13.75f, -410.77f), new(-13.4f, -409.53f), new(-12.85f, -408.34f), new(-12.16f, -407.17f), new(-11.35f, -406.27f), + new(-10.81f, -406.33f), new(-7.5f, -408.1f), new(-3.67f, -409.23f), new(0.15f, -409.58f), new(3.94f, -409.18f), + new(7.16f, -408.2f), new(7.77f, -407.95f), new(10.71f, -406.38f), new(11.27f, -406.26f), new(11.72f, -406.65f), + new(12.16f, -407.15f), new(12.85f, -408.31f), new(13.38f, -409.48f), new(13.72f, -410.7f), new(13.83f, -411.98f), + new(13.6f, -413.3f), new(12.62f, -414.9f), new(12.36f, -415.55f), new(12.47f, -416.28f), new(12.81f, -416.95f)]; + private static readonly ArenaBoundsComplex arena = new([new PolygonCustom(vertices)]); + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/DaenOseTheAvariciousTyphon.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/DaenOseTheAvariciousTyphon.cs index 725c343ede..e99aa348db 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/DaenOseTheAvariciousTyphon.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/DaenOseTheAvariciousTyphon.cs @@ -162,7 +162,7 @@ public DaenOseTheAvariciousTyphonStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9808)] -public class DaenOseTheAvariciousTyphon(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class DaenOseTheAvariciousTyphon(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/FuathTroublemaker.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/FuathTroublemaker.cs index bdf5c3f885..d623ad015e 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/FuathTroublemaker.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/FuathTroublemaker.cs @@ -43,7 +43,7 @@ public FuathTroublemakerStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9786)] -public class FuathTroublemaker(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class FuathTroublemaker(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/GreedyPixie.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/GreedyPixie.cs index b8521015ef..9cc501f6e7 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/GreedyPixie.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/GreedyPixie.cs @@ -89,7 +89,7 @@ public GreedyPixieStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9797)] -public class GreedyPixie(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class GreedyPixie(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretBasket.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretBasket.cs index ad37aab694..349682bb45 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretBasket.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretBasket.cs @@ -112,7 +112,7 @@ public SecretBasketStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9784)] -public class SecretBasket(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretBasket(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs index 544655030d..cbe3395ba1 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretCladoselache.cs @@ -116,7 +116,7 @@ public SecretCladoselacheStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9778)] -public class SecretCladoselache(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretCladoselache(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs index 5c29a8455b..c4e0347c55 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretDjinn.cs @@ -61,7 +61,7 @@ public SecretDjinnStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9788)] -public class SecretDjinn(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretDjinn(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKeeper.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKeeper.cs index 4f93723b64..6c95db4748 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKeeper.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKeeper.cs @@ -56,7 +56,7 @@ public SecretKeeperStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9807)] -public class SecretKeeper(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretKeeper(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKorrigan.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKorrigan.cs index 3e1378f197..3f9b6ba571 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKorrigan.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretKorrigan.cs @@ -60,7 +60,7 @@ public SecretKorriganStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9806)] -public class SecretKorrigan(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretKorrigan(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs index 5a461176b9..b3ee4b7a57 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPegasus.cs @@ -51,7 +51,7 @@ public SecretPegasusStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9793)] -public class SecretPegasus(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretPegasus(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs index 3e09f0413a..1a94d4253f 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretPorxie.cs @@ -101,7 +101,7 @@ public SecretPorxieStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9795)] -public class SecretPorxie(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretPorxie(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSerpent.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSerpent.cs index c3ffdef4e3..95c10545d0 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSerpent.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSerpent.cs @@ -32,7 +32,8 @@ public enum AID : uint Telega = 9630 // Mandragoras->self, no cast, single-target, bonus adds disappear } -class Douse(BossModule module) : Components.PersistentVoidzoneAtCastTarget(module, 8, ActionID.MakeSpell(AID.Douse), m => m.Enemies(OID.WaterVoidzone).Where(z => z.EventState != 7), 0); +class DouseVoidzone(BossModule module) : Components.PersistentVoidzone(module, 7, m => m.Enemies(OID.WaterVoidzone).Where(z => z.EventState != 7), 0); +class Douse(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Douse), 8); class FangsEnd(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.FangsEnd)); class Drench1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Drench1), new AOEShapeCone(15.29f, 45.Degrees())); class Drench2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Drench2), new AOEShapeCone(13.45f, 45.Degrees())); @@ -51,6 +52,7 @@ public SecretSerpentStates(BossModule module) : base(module) { TrivialPhase() .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() .ActivateOnEnter() @@ -65,7 +67,7 @@ public SecretSerpentStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9776)] -public class SecretSerpent(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretSerpent(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSwallow.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSwallow.cs index 98bf4db6f3..5cf9688bcd 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSwallow.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretSwallow.cs @@ -65,7 +65,7 @@ public SecretSwallowStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9782)] -public class SecretSwallow(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretSwallow(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretUndine.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretUndine.cs index 35d3f68543..32e256a190 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretUndine.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretUndine.cs @@ -81,7 +81,7 @@ public SecretUndineStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9790)] -public class SecretUndine(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretUndine(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretWorm.cs b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretWorm.cs index 83497f80b7..b0ae541ecf 100644 --- a/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretWorm.cs +++ b/BossMod/Modules/Shadowbringers/TreasureHunt/TheShiftingOubliettesOfLyheGhiah/SecretWorm.cs @@ -101,7 +101,7 @@ public SecretWormStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 745, NameID = 9780)] -public class SecretWorm(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class SecretWorm(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheLostCanalsOfUznair/CanalIcebeast.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheLostCanalsOfUznair/CanalIcebeast.cs index e38eff03b2..95dd160433 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheLostCanalsOfUznair/CanalIcebeast.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheLostCanalsOfUznair/CanalIcebeast.cs @@ -53,8 +53,12 @@ public CanalIcebeastStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 268, NameID = 6650)] -public class CanalIcebeast(WorldState ws, Actor primary) : BossModule(ws, primary, new(0, -420), new ArenaBoundsCircle(20)) +public class CanalIcebeast(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena) { + private static readonly WPos center = new(0, -420); + private static readonly ArenaBoundsComplex arena = new([new Polygon(center, 19.51f * CosPI.Pi8th, 8, 22.5f.Degrees()), new Rectangle(center, 27.66f, 5.5f), + new Rectangle(new(0, -440), 5.5f, 7.5f)], [new Rectangle(new(0, -400), 20, 3.35f)]); + protected override void DrawEnemies(int pcSlot, Actor pc) { Arena.Actor(PrimaryActor); diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarAiravata.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarAiravata.cs index f324ac9c68..2d1f358d46 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarAiravata.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarAiravata.cs @@ -118,7 +118,7 @@ public AltarAiravataStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7601)] -public class AltarAiravata(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarAiravata(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarArachne.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarArachne.cs index 4891c483a4..61a031bed1 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarArachne.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarArachne.cs @@ -63,7 +63,7 @@ public AltarArachneStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7623)] -public class AltarArachne(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarArachne(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarBeast.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarBeast.cs index feb90143ad..c7f3cb3867 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarBeast.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarBeast.cs @@ -80,7 +80,7 @@ public AltarBeastStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7588)] -public class AltarBeast(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarBeast(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarChimera.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarChimera.cs index 6e59a58d4c..f0de08ba33 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarChimera.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarChimera.cs @@ -100,7 +100,7 @@ public AltarChimeraStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7591)] -public class AltarChimera(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarChimera(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDiresaur.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDiresaur.cs index f8b3a534ca..4baf549cea 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDiresaur.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDiresaur.cs @@ -105,7 +105,7 @@ public AltarDiresaurStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7627)] -public class AltarDiresaur(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarDiresaur(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDullahan.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDullahan.cs index 1f92f53a6e..00a26a2465 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDullahan.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarDullahan.cs @@ -82,7 +82,7 @@ public AltarDullahanStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7585)] -public class AltarDullahan(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarDullahan(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarKelpie.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarKelpie.cs index 096cbdc609..addad2a3c8 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarKelpie.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarKelpie.cs @@ -114,7 +114,7 @@ public AltarKelpieStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7589)] -public class AltarKelpie(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarKelpie(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarMandragora.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarMandragora.cs index cc7e58b902..4428aa898a 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarMandragora.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarMandragora.cs @@ -61,7 +61,7 @@ public AltarMandragoraStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7600)] -public class AltarMandragora(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarMandragora(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarSkatene.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarSkatene.cs index 58ba138861..02ff001827 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarSkatene.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarSkatene.cs @@ -52,7 +52,7 @@ public AltarSkateneStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7587)] -public class AltarSkatene(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarSkatene(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarTotem.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarTotem.cs index ece6f95de2..638aba54b3 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarTotem.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/AltarTotem.cs @@ -98,7 +98,7 @@ public AltarTotemStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7586)] -public class AltarTotem(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class AltarTotem(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/Hati.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/Hati.cs index dc557dc154..1e14074278 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/Hati.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/Hati.cs @@ -66,7 +66,7 @@ public HatiStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7590)] -public class Hati(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class Hati(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheGreatGoldWhisker.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheGreatGoldWhisker.cs index 6881cb8687..874a134b40 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheGreatGoldWhisker.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheGreatGoldWhisker.cs @@ -34,7 +34,7 @@ public TheGreatGoldWhiskerStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7599)] -public class TheGreatGoldWhisker(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class TheGreatGoldWhisker(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheOlderOne.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheOlderOne.cs index 505b8c7263..aae1f3eb51 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheOlderOne.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheOlderOne.cs @@ -85,7 +85,7 @@ public TheOlderOneStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7597)] -public class TheOlderOne(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class TheOlderOne(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheWinged.cs b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheWinged.cs index b5f9e96f94..f876e7661f 100644 --- a/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheWinged.cs +++ b/BossMod/Modules/Stormblood/TreasureHunt/TheShiftingAltarsOfUznair/TheWinged.cs @@ -80,7 +80,7 @@ public TheWingedStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 586, NameID = 7595)] -public class TheWinged(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(19)) +public class TheWinged(WorldState ws, Actor primary) : THTemplate(ws, primary) { protected override void DrawEnemies(int pcSlot, Actor pc) { diff --git a/BossMod/Pathfinding/ThetaStar.cs b/BossMod/Pathfinding/ThetaStar.cs index c587873992..3da395575f 100644 --- a/BossMod/Pathfinding/ThetaStar.cs +++ b/BossMod/Pathfinding/ThetaStar.cs @@ -15,15 +15,18 @@ public struct Node private Map _map = new(); private readonly List<(int x, int y)> _goals = []; private Node[] _nodes = []; + private float[] _distances = []; private readonly List _openList = []; private float _deltaGSide; private float _deltaGDiag; private const float Epsilon = 1e-5f; private readonly object _lock = new(); - + private (int dx, int dy, float cost)[] NeighborOffsets = []; public ref Node NodeByIndex(int index) => ref _nodes[index]; public int CellIndex(int x, int y) => y * _map.Width + x; public WPos CellCenter(int index) => _map.GridToWorld(index % _map.Width, index / _map.Width, 0.5f, 0.5f); + private float _mapResolution; + private float _mapHalfResolution; // gMultiplier is typically inverse speed, which turns g-values into time public void Start(Map map, IEnumerable<(int x, int y)> goals, (int x, int y) start, float gMultiplier) @@ -37,14 +40,30 @@ public void Start(Map map, IEnumerable<(int x, int y)> goals, (int x, int y) sta if (_nodes == null || _nodes.Length < numPixels) _nodes = new Node[numPixels]; Array.Fill(_nodes, default, 0, numPixels); + if (_distances == null || _distances.Length < numPixels) + _distances = new float[numPixels]; _openList.Clear(); _deltaGSide = map.Resolution * gMultiplier; _deltaGDiag = _deltaGSide * 1.414214f; + _mapResolution = map.Resolution; + _mapHalfResolution = map.Resolution * 0.5f; + NeighborOffsets = + [ + (-1, 0, _deltaGSide), + (1, 0, _deltaGSide), + (0, -1, _deltaGSide), + (0, 1, _deltaGSide), + (-1, -1, _deltaGDiag), + (-1, 1, _deltaGDiag), + (1, -1, _deltaGDiag), + (1, 1, _deltaGDiag), + ]; + DijkstraDistance(); start = map.ClampToGrid(start); var startIndex = CellIndex(start.x, start.y); _nodes[startIndex].GScore = 0; - _nodes[startIndex].HScore = HeuristicDistance(start.x, start.y); + _nodes[startIndex].HScore = _distances[startIndex]; _nodes[startIndex].ParentX = start.x; // start's parent is self _nodes[startIndex].ParentY = start.y; _nodes[startIndex].PathLeeway = float.MaxValue; // min diff along path between node's g-value and cell's g-value @@ -110,94 +129,240 @@ public int Execute() private void VisitNeighbour(int parentX, int parentY, int parentIndex, int nodeX, int nodeY, int nodeIndex, float deltaG) { - if (_nodes[nodeIndex].OpenHeapIndex < 0) + ref var node = ref _nodes[nodeIndex]; + if (node.OpenHeapIndex < 0) return; // in closed list already - var nodeG = _nodes[parentIndex].GScore + deltaG; - var nodeLeeway = _map.Pixels[nodeIndex].MaxG - nodeG; + + ref var parentnode = ref _nodes[parentIndex]; + + var tentativeG = parentnode.GScore + deltaG; + var nodeLeeway = _map.Pixels[nodeIndex].MaxG - tentativeG; if (nodeLeeway < 0) return; // node is blocked along this path - if (_nodes[nodeIndex].OpenHeapIndex == 0) + var grandParentX = parentnode.ParentX; + var grandParentY = parentnode.ParentY; + var grandParentIndex = CellIndex(grandParentX, grandParentY); + + var newG = tentativeG; + if (LineOfSight(grandParentX, grandParentY, nodeX, nodeY, parentnode.GScore)) { - // first time we're visiting this node, calculate heuristic - _nodes[nodeIndex].GScore = float.MaxValue; - _nodes[nodeIndex].HScore = HeuristicDistance(nodeX, nodeY); - - // check LoS from grandparent - var grandParentX = _nodes[parentIndex].ParentX; - var grandParentY = _nodes[parentIndex].ParentY; - var losLeeway = LineOfSight(grandParentX, grandParentY, nodeX, nodeY, nodeG); - if (losLeeway >= 0) - { - parentX = grandParentX; - parentY = grandParentY; - parentIndex = CellIndex(parentX, parentY); - nodeG = _nodes[parentIndex].GScore + _deltaGSide * MathF.Sqrt(DistanceSq(nodeX, nodeY, parentX, parentY)); - nodeLeeway = losLeeway; - } + // If there is line of sight, set the parent to the grandparent + parentX = grandParentX; + parentY = grandParentY; + parentIndex = grandParentIndex; + + // Recalculate newG based on the grandparent + var distance = MathF.Sqrt(DistanceSq(nodeX, nodeY, parentX, parentY)); + newG = _nodes[parentIndex].GScore + _deltaGSide * distance; + } - if (nodeG + Epsilon < _nodes[nodeIndex].GScore) + if (node.OpenHeapIndex == 0) + { + node.GScore = float.MaxValue; + node.HScore = _distances[nodeIndex]; + } + + if (newG + Epsilon < node.GScore) + { + node.GScore = newG; + node.ParentX = parentX; + node.ParentY = parentY; + node.PathLeeway = MathF.Min(parentnode.PathLeeway, nodeLeeway); + + if (node.OpenHeapIndex == 0) { - _nodes[nodeIndex].GScore = nodeG; - _nodes[nodeIndex].ParentX = parentX; - _nodes[nodeIndex].ParentY = parentY; - _nodes[nodeIndex].PathLeeway = MathF.Min(_nodes[parentIndex].PathLeeway, nodeLeeway); AddToOpen(nodeIndex); } + else + { + PercolateUp(node.OpenHeapIndex - 1); + } } } - private float LineOfSight(int x1, int y1, int x2, int y2, float maxG) + private bool LineOfSight(int x0, int y0, int x1, int y1, float parentGScore) { - var minLeeway = float.MaxValue; - int dx = Math.Abs(x2 - x1), sx = x1 < x2 ? 1 : -1; - int dy = -Math.Abs(y2 - y1), sy = y1 < y2 ? 1 : -1; - int err = dx + dy, e2; + var dx = x1 - x0; + var dy = y1 - y0; - while (true) + var stepX = Math.Sign(dx); + var stepY = Math.Sign(dy); + + dx = Math.Abs(dx); + dy = Math.Abs(dy); + + var invdx = (dx != 0) ? 1f / dx : float.MaxValue; + var invdy = (dy != 0) ? 1f / dy : float.MaxValue; + + var tMaxX = _mapHalfResolution * invdx; + var tMaxY = _mapHalfResolution * invdy; + + var tDeltaX = _mapResolution * invdx; + var tDeltaY = _mapResolution * invdy; + + var x = x0; + var y = y0; + var cumulativeG = parentGScore; + + while (x != x1 || y != y1) { - minLeeway = Math.Min(minLeeway, _map[x1, y1].MaxG - maxG); - if (minLeeway < 0) - return minLeeway; - if (x1 == x2 && y1 == y2) - break; - e2 = 2 * err; - if (e2 >= dy) + var nodeIndex = CellIndex(x, y); + + // Check if the node is entirely blocked + if (_map.Pixels[nodeIndex].MaxG <= 0) + return false; + + // Determine the movement cost based on the direction + float movementCost; + + if (tMaxX < tMaxY) { - err += dy; - x1 += sx; + tMaxX += tDeltaX; + x += stepX; + movementCost = _deltaGSide; } - if (e2 <= dx) + else if (tMaxY < tMaxX) { - err += dx; - y1 += sy; + tMaxY += tDeltaY; + y += stepY; + movementCost = _deltaGSide; } + else // tMaxX == tMaxY, moving diagonally + { + tMaxX += tDeltaX; + tMaxY += tDeltaY; + x += stepX; + y += stepY; + movementCost = _deltaGDiag; + } + + cumulativeG += movementCost; + + // Check if the cumulative G-score exceeds MaxG + if (cumulativeG - Epsilon > _map.Pixels[nodeIndex].MaxG) + return false; } - return minLeeway; + + // Check the last node + return cumulativeG - Epsilon <= _map.Pixels[CellIndex(x1, y1)].MaxG; } - private float HeuristicDistance(int x, int y) + private void DijkstraDistance() { - if (_goals.Count == 0) - return float.MaxValue; + var numPixels = _map.Width * _map.Height; + Array.Fill(_distances, float.MaxValue, 0, numPixels); - var deltaGDiagMinus2DeltaGSide = _deltaGDiag - 2 * _deltaGSide; - var minDistance = float.MaxValue; + var openList = new List(); + var inOpenHeapIndex = new int[numPixels]; + Array.Fill(inOpenHeapIndex, 0, 0, numPixels); for (var i = 0; i < _goals.Count; ++i) { var goal = _goals[i]; - var dx = Math.Abs(x - goal.x); - var dy = Math.Abs(y - goal.y); - var distance = _deltaGSide * (dx + dy) + deltaGDiagMinus2DeltaGSide * Math.Min(dx, dy); - if (distance < minDistance) - minDistance = distance; + var goalIndex = CellIndex(goal.x, goal.y); + _distances[goalIndex] = 0; + openList.Add(goalIndex); + inOpenHeapIndex[goalIndex] = openList.Count; } - return minDistance; + while (openList.Count != 0) + { + var currentIndex = PopMinOpen(openList, inOpenHeapIndex); + var currentX = currentIndex % _map.Width; + var currentY = currentIndex / _map.Width; + + for (var i = 0; i < 8; ++i) + { + var (dx, dy, cost) = NeighborOffsets[i]; + var neighborX = currentX + dx; + var neighborY = currentY + dy; + + if (neighborX < 0 || neighborX >= _map.Width || neighborY < 0 || neighborY >= _map.Height) + continue; + + var neighborIndex = CellIndex(neighborX, neighborY); + var neighborMaxG = _map.Pixels[neighborIndex].MaxG; + + if (neighborMaxG <= 0) + continue; + + var newDistance = _distances[currentIndex] + cost; + + // If the new distance exceeds MaxG, skip this neighbor + if (newDistance > neighborMaxG) + continue; + + if (newDistance < _distances[neighborIndex]) + { + _distances[neighborIndex] = newDistance; + if (inOpenHeapIndex[neighborIndex] == 0) + { + openList.Add(neighborIndex); + inOpenHeapIndex[neighborIndex] = openList.Count; + } + PercolateUp(openList, inOpenHeapIndex, neighborIndex); + } + } + } + } + + private int PopMinOpen(List openList, int[] inOpenHeapIndex) + { + var nodeIndex = openList[0]; + openList[0] = openList[^1]; + inOpenHeapIndex[nodeIndex] = -1; + openList.RemoveAt(openList.Count - 1); + if (openList.Count > 0) + { + inOpenHeapIndex[openList[0]] = 1; + PercolateDown(openList, inOpenHeapIndex, 0); + } + return nodeIndex; + } + + private void PercolateUp(List openList, int[] inOpenHeapIndex, int nodeIndex) + { + var heapIndex = inOpenHeapIndex[nodeIndex] - 1; + var parent = (heapIndex - 1) >> 1; + while (heapIndex > 0 && _distances[openList[heapIndex]] < _distances[openList[parent]]) + { + (openList[parent], openList[heapIndex]) = (openList[heapIndex], openList[parent]); + inOpenHeapIndex[openList[heapIndex]] = heapIndex + 1; + inOpenHeapIndex[openList[parent]] = parent + 1; + + heapIndex = parent; + parent = (heapIndex - 1) >> 1; + } + } + + private void PercolateDown(List openList, int[] inOpenHeapIndex, int heapIndex) + { + var maxSize = openList.Count; + while (true) + { + var leftChild = (heapIndex << 1) + 1; + if (leftChild >= maxSize) + break; + var rightChild = leftChild + 1; + var smallest = heapIndex; + + if (_distances[openList[leftChild]] < _distances[openList[smallest]]) + smallest = leftChild; + if (rightChild < maxSize && _distances[openList[rightChild]] < _distances[openList[smallest]]) + smallest = rightChild; + if (smallest == heapIndex) + break; + + (openList[smallest], openList[heapIndex]) = (openList[heapIndex], openList[smallest]); + inOpenHeapIndex[openList[heapIndex]] = heapIndex + 1; + inOpenHeapIndex[openList[smallest]] = smallest + 1; + + heapIndex = smallest; + } } - private float DistanceSq(int x1, int y1, int x2, int y2) + private static float DistanceSq(int x1, int y1, int x2, int y2) { var dx = x1 - x2; var dy = y1 - y2; diff --git a/BossMod/Pathfinding/WaypointManager.cs b/BossMod/Pathfinding/WaypointManager.cs deleted file mode 100644 index 41a2bfceae..0000000000 --- a/BossMod/Pathfinding/WaypointManager.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Security.Cryptography; - -namespace BossMod; -// for optional waypoints that have priority over automated pathfinding eg. Dohn Mheg boss 3 -public class WaypointManager -{ - public readonly Queue waypoints = new(); - private WPos? activeWaypoint; - private DateTime activeWaypointSetTime; - public float WaypointTimeLimit { get; set; } - public BossModule? module; - public WPos? CurrentWaypoint => activeWaypoint; - public bool HasWaypoints => activeWaypoint.HasValue || waypoints.Count > 0; - - public void AddWaypoint(WPos waypoint) - { - waypoints.Enqueue(waypoint); - } - - public void AddWaypointsWithRandomization(WPos[] waypointsList, float radius, int numRandomWaypoints) - { - for (var i = 0; i < waypointsList.Length - 1; ++i) - { - AddWaypoint(waypointsList[i]); - for (var j = 0; j < numRandomWaypoints; ++j) - AddWaypoint(GenerateRandomPointNearWaypoint(waypointsList[i + 1], radius)); - } - AddWaypoint(waypointsList[^1]); - } - - public void UpdateCurrentWaypoint(WPos actorPosition, float threshold = 0.1f) - { - if (activeWaypoint.HasValue) - { - if ((activeWaypoint.Value - actorPosition).Length() <= threshold) - { - activeWaypoint = waypoints.Count > 0 ? waypoints.Dequeue() : null; - activeWaypointSetTime = module!.WorldState.CurrentTime; - } - else if ((module!.WorldState.CurrentTime - activeWaypointSetTime).TotalSeconds > WaypointTimeLimit) - { - ClearWaypoints(); - Service.Log("Waypoints cleared due to time limit"); - } - } - else if (waypoints.Count > 0) - { - activeWaypoint = waypoints.Dequeue(); - activeWaypointSetTime = module!.WorldState.CurrentTime; - } - } - - public void ClearWaypoints() - { - waypoints.Clear(); - activeWaypoint = null; - } - - public static WPos GenerateRandomPointNearWaypoint(WPos waypoint, float radius) - { - using var rng = RandomNumberGenerator.Create(); - var angle = GetRandomFloat(rng) * Angle.DoublePI; - var distance = GetRandomFloat(rng) * radius; - var (sin, cos) = ((float, float))MathF.SinCos(angle); - var offsetX = distance * cos; - var offsetZ = distance * sin; - return new(waypoint.X + offsetX, waypoint.Z + offsetZ); - } - - private static float GetRandomFloat(RandomNumberGenerator rng) - { - var bytes = new byte[4]; - rng.GetBytes(bytes); - return BitConverter.ToUInt32(bytes, 0) / (float)uint.MaxValue; - } -}