From ac516c681cb0b1e3b80b7f975fbabfeabaf0b1fc Mon Sep 17 00:00:00 2001 From: Andrew Gilewsky Date: Wed, 1 Jan 2025 15:55:10 +0000 Subject: [PATCH 1/4] Chaotic improvements. --- .../Chaotic/Ch01CloudOfDarkness/Break.cs | 2 +- .../Ch01CloudOfDarkness.cs | 7 +- .../Ch01CloudOfDarknessEnums.cs | 5 +- .../Ch01CloudOfDarknessStates.cs | 31 ++--- .../DiffusiveForceParticleBeam.cs | 9 +- .../Chaotic/Ch01CloudOfDarkness/EvilSeed.cs | 12 +- .../ParticleConcentration.cs | 109 +++++++++++++++++- .../Ch01CloudOfDarkness/ThirdArtOfDarkness.cs | 25 +++- BossMod/Replay/Analysis/TetherInfo.cs | 58 +++++++++- 9 files changed, 215 insertions(+), 43 deletions(-) diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs index ed046b4ec4..75c7dc1f3c 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Break.cs @@ -4,7 +4,7 @@ class Break(BossModule module) : Components.GenericGaze(module) { public readonly List Eyes = []; - public override IEnumerable ActiveEyes(int slot, Actor actor) => Eyes;//_casters.Where(c => c.CastInfo?.TargetID != actor.InstanceID).Select(c => new Eye(EyePosition(c), Module.CastFinishAt(c.CastInfo), Range: range)); + public override IEnumerable ActiveEyes(int slot, Actor actor) => Eyes; public override void OnCastStarted(Actor caster, ActorCastInfo spell) { diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs index 89b282ccea..bf237c14e0 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs @@ -10,10 +10,9 @@ class Atomos(BossModule module) : Components.Adds(module, (uint)OID.Atomos); class LoomingChaos(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.LoomingChaosAOE)); // TODO: tankswap hints component for phase1 -// TODO: phase 2 squares, break timer, teleport zones -// TODO: particle concentration towers -// TODO: evil seed -[ModuleInfo(BossModuleInfo.Maturity.WIP, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1010, NameID = 13624)] +// TODO: phase 2 squares, break timer, teleport zones, outer ring safety +// TODO: grim embrace / curse of darkness prevent turning +[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1010, NameID = 13624)] public class Ch01CloudOfDarkness(WorldState ws, Actor primary) : BossModule(ws, primary, DefaultCenter, InitialBounds) { public static readonly WPos DefaultCenter = new(100, 100); diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs index 4a57119ea5..123d9e9d39 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs @@ -140,18 +140,17 @@ public enum AID : uint public enum SID : uint { //_Gen_ArcaneDesign = 4180, // Boss->Boss, extra=0x0 + //_Gen_VeilOfDarkness = 4179, // Boss->Boss, extra=0x0 //_Gen_LightningResistanceDown = 4386, // Helper/Boss->player, extra=0x1/0x2/0x3/0x4/0x5/0x6/0x7/0x8/0x9/0xA/0xB/0xC/0xD/0xE/0xF/0x10 DeadlyEmbrace = 4181, // none->player, extra=0x0 AbyssalEdge = 4182, // Boss->Boss, extra=0x0 (endeath/enaero stored) - //_Gen_VeilOfDarkness = 4179, // Boss->Boss, extra=0x0 - //_Gen_CloyingCondensation = 2532, // none->player, extra=0x0 + //_Gen_CloyingCondensation = 2532, // none->player, extra=0x0, prevent jumps? //_Gen_ = 4388, // none->StygianShadow, extra=0x1052 //_Gen_ = 4387, // none->Boss, extra=0x1051 InnerDarkness = 4177, // none->player, extra=0x0, on main platform OuterDarkness = 4178, // none->player, extra=0x0, on side platform //_Gen_Rehabilitation = 4191, // none->Boss, extra=0x1/0x4/0x3/0x2 //_Gen_LifeDrain = 1377, // none->player, extra=0x0 - //_Gen_CraftersGrace = 45, // player->player, extra=0x50 CurseOfDarkness = 2387, // none->player, extra=0x0 //_Gen_StabWound = 3061, // none->player, extra=0x0 //_Gen_StabWound = 3062, // none->player, extra=0x0 diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs index 0797a8333c..9443f0c0de 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs @@ -76,8 +76,7 @@ private void Subphase2(uint id, float delay) ThirdArtOfDarknessParticleConcentration(id + 0x20000, 4); GhastlyGloom(id + 0x30000, 12.3f); CurseOfDarkness(id + 0x40000, 8.3f); - EvilSeed(id + 0x50000, 9.9f); - ChaosCondensedDiffusiveForceParticleBeam(id + 0x60000, 8.1f); + EvilSeedChaosCondensedDiffusiveForceParticleBeam(id + 0x50000, 9.9f); ActivePivotParticleBeam(id + 0x70000, 4.4f); LoomingChaos(id + 0x80000, 6.2f); @@ -246,7 +245,8 @@ private void ThirdArtOfDarknessParticleConcentration(uint id, float delay) .DeactivateOnExit() .DeactivateOnExit() .DeactivateOnExit(); - ComponentCondition(id + 0x50, 3.6f, comp => comp.NumCasts > 0, "Towers") + ComponentCondition(id + 0x50, 3.6f, comp => comp.Towers.Count == 0, "Towers") + .ExecOnEnter(comp => comp.ShowOuterTowers()) .DeactivateOnExit(); } @@ -276,29 +276,29 @@ private State FloodOfDarknessAdds(uint id, float delay) .DeactivateOnExit(); } - // TODO: this one needs a lot of thought - private void EvilSeed(uint id, float delay) + private void EvilSeedChaosCondensedDiffusiveForceParticleBeam(uint id, float delay) { ComponentCondition(id, delay, comp => comp.Baiters.Any()) .ActivateOnEnter(); ComponentCondition(id + 0x10, 8.1f, comp => comp.Casters.Count > 0, "Seed plant") .ActivateOnEnter() + .ActivateOnEnter() .DeactivateOnExit(); + GhastlyGloom(id + 0x1000, 2.8f) .DeactivateOnExit(); + ComponentCondition(id + 0x2000, 14, comp => comp.Targets.Any()) .ActivateOnEnter(); - ComponentCondition(id + 0x2010, 3, comp => comp.HaveTethers); - FloodOfDarknessAdds(id + 0x2020, 2.2f) - .DeactivateOnExit(); - } + ComponentCondition(id + 0x2010, 3, comp => comp.TethersAssigned, "Tethers"); + FloodOfDarknessAdds(id + 0x2020, 2.2f); - private void ChaosCondensedDiffusiveForceParticleBeam(uint id, float delay) - { - CastMulti(id, [AID.ChaosCondensedParticleBeam, AID.DiffusiveForceParticleBeam], delay, 8) + CastMulti(id + 0x3000, [AID.ChaosCondensedParticleBeam, AID.DiffusiveForceParticleBeam], 8.1f, 8) .ActivateOnEnter() - .ActivateOnEnter(); - Condition(id + 0x10, 0.7f, () => Module.FindComponent()?.NumCasts > 0 || Module.FindComponent()?.Spreads.Count == 0, "Spread/line stacks") + .ActivateOnEnter() + .DeactivateOnExit() + .DeactivateOnExit(); + Condition(id + 0x3010, 0.7f, () => Module.FindComponent()?.NumCasts > 0 || Module.FindComponent()?.Spreads.Count == 0, "Spread/line stacks") .DeactivateOnExit() .DeactivateOnExit(); // TODO: show second wave ... } @@ -341,7 +341,8 @@ private void ParticleConcentrationPhaser(uint id, float delay) .ActivateOnEnter(); // TODO: towers appear 1s after cast end ComponentCondition(id + 0x11, 1.5f, comp => comp.NumCasts >= 6, "Adds sides/front") .DeactivateOnExit(); - ComponentCondition(id + 0x20, 6.6f, comp => comp.NumCasts > 0, "Towers") + ComponentCondition(id + 0x20, 6.6f, comp => comp.Towers.Count == 0, "Towers") + .ExecOnEnter(comp => comp.ShowOuterTowers()) .DeactivateOnExit(); } diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DiffusiveForceParticleBeam.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DiffusiveForceParticleBeam.cs index 9e92c4d428..5c5166af97 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DiffusiveForceParticleBeam.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DiffusiveForceParticleBeam.cs @@ -1,13 +1,16 @@ namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness; -// TODO: who gets radius 7 and who gets radius 5? -// TODO: show for second wave too... +// note: it seems that normally first wave (radius 7) hits people inside, and second wave (radius 5) hits people outside +// however, if some of the players in the mid are dead, some players on the outside will be hit by first wave (up to a total of 12 hits) +// if there are <= 12 players alive, everyone will be hit by the first wave, and the second wave will never happen +// so for safety we just show larger radius around everyone +// TODO: show second wave for players not hit by first wave class DiffusiveForceParticleBeam(BossModule module) : Components.UniformStackSpread(module, 0, 7) { public override void OnCastStarted(Actor caster, ActorCastInfo spell) { if ((AID)spell.Action.ID == AID.DiffusiveForceParticleBeam) - AddSpreads(Raid.WithoutSlot(), Module.CastFinishAt(spell, 0.7f)); + AddSpreads(Raid.WithoutSlot(true), Module.CastFinishAt(spell, 0.7f)); } public override void OnEventCast(Actor caster, ActorCastEvent spell) diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/EvilSeed.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/EvilSeed.cs index a5e0d983f5..28ab8b2bda 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/EvilSeed.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/EvilSeed.cs @@ -19,21 +19,15 @@ public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) class EvilSeedAOE(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.EvilSeedAOE), 5); -// todo: should be chains... -class ThornyVine(BossModule module) : BossComponent(module) +class EvilSeedVoidzone(BossModule module) : Components.PersistentVoidzone(module, 5, module => module.Enemies(OID.EvilSeed).Where(z => z.EventState != 7)); + +class ThornyVine(BossModule module) : Components.Chains(module, (uint)TetherID.ThornyVine, default, 25) { public BitMask Targets; - public bool HaveTethers; public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { if (iconID == (uint)IconID.ThornyVineBait) Targets.Set(Raid.FindSlot(actor.InstanceID)); } - - public override void OnTethered(Actor source, ActorTetherInfo tether) - { - if (tether.ID == (uint)TetherID.ThornyVine) - HaveTethers = true; - } } diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs index 37fc1876ae..5b7848001e 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs @@ -1,4 +1,6 @@ -namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness; +using System.Collections.Specialized; + +namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness; // envcontrols: // 1F-2E = 1-man towers @@ -44,4 +46,107 @@ // 47-56 = 1-man tower falling orb // 57-66 = 2-man tower falling orb // 67-6E = 3-man tower falling orb -class ParticleConcentration(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.ParticleBeam1)) { } +class ParticleConcentration(BossModule module) : Components.GenericTowers(module) +{ + private BitMask _innerPlayers; + private BitMask _outerPlayers; + private readonly List _outerTowers = []; // note: initially we don't show outer towers, as players resolve different mechanics first + + public void ShowOuterTowers() + { + var activation = Towers.Count > 0 ? Towers[0].Activation : default; + Towers.AddRange(_outerTowers.Select(p => new Tower(p, 3, 3, 3, _innerPlayers, activation))); + } + + public override void OnStatusGain(Actor actor, ActorStatus status) + { + switch ((SID)status.ID) + { + case SID.InnerDarkness: + _innerPlayers.Set(Raid.FindSlot(actor.InstanceID)); + break; + case SID.OuterDarkness: + _outerPlayers.Set(Raid.FindSlot(actor.InstanceID)); + break; + } + } + + public override void OnStatusLose(Actor actor, ActorStatus status) + { + switch ((SID)status.ID) + { + case SID.InnerDarkness: + _innerPlayers.Clear(Raid.FindSlot(actor.InstanceID)); + break; + case SID.OuterDarkness: + _outerPlayers.Clear(Raid.FindSlot(actor.InstanceID)); + break; + } + } + + public override void OnEventEnvControl(byte index, uint state) + { + if (state != 0x00020001) // appear + return; + + var (offset, count) = index switch + { + 0x1F => (new(-9, -15), 1), + 0x20 => (new(+9, -15), 1), + 0x21 => (new(-21, -15), 1), + 0x22 => (new(+21, -15), 1), + 0x23 => (new(-15, -9), 1), + 0x24 => (new(+15, -9), 1), + 0x25 => (new(-15, -21), 1), + 0x26 => (new(+15, -21), 1), + 0x27 => (new(-9, +15), 1), + 0x28 => (new(+9, +15), 1), + 0x29 => (new(-21, +15), 1), + 0x2A => (new(+21, +15), 1), + 0x2B => (new(-15, +9), 1), + 0x2C => (new(+15, +9), 1), + 0x2D => (new(-15, +21), 1), + 0x2E => (new(+15, +21), 1), + 0x2F => (new(-12, -15), 2), + 0x30 => (new(+12, -15), 2), + 0x31 => (new(-18, -15), 2), + 0x32 => (new(+18, -15), 2), + 0x33 => (new(-15, -12), 2), + 0x34 => (new(+15, -12), 2), + 0x35 => (new(-15, -18), 2), + 0x36 => (new(+15, -18), 2), + 0x37 => (new(-12, +15), 2), + 0x38 => (new(+12, +15), 2), + 0x39 => (new(-18, +15), 2), + 0x3A => (new(+18, +15), 2), + 0x3B => (new(-15, +12), 2), + 0x3C => (new(+15, +12), 2), + 0x3D => (new(-15, +18), 2), + 0x3E => (new(+15, +18), 2), + 0x3F => (new(-26.5f, -4.5f), 3), + 0x40 => (new(-22, 0), 3), + 0x41 => (new(-26.5f, +4.5f), 3), + 0x42 => (new(-31, 0), 3), + 0x43 => (new(+26.5f, -4.5f), 3), + 0x44 => (new(+22, 0), 3), + 0x45 => (new(+26.5f, +4.5f), 3), + 0x46 => (new(+31, 0), 3), + _ => (default(WDir), 0) + }; + + if (count == 3) + _outerTowers.Add(Module.Center + offset); + else if (count > 0) + Towers.Add(new(Module.Center + offset, 3, count, count, _outerPlayers, WorldState.FutureTime(10.1f))); + } + + public override void OnEventCast(Actor caster, ActorCastEvent spell) + { + if ((AID)spell.Action.ID is AID.ParticleBeam1 or AID.ParticleBeam2 or AID.ParticleBeam3) + { + ++NumCasts; + if (Towers.RemoveAll(t => t.Position.AlmostEqual(caster.Position, 1)) != 1) + ReportError($"Unexpected tower position @ {caster.Position}"); + } + } +} diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ThirdArtOfDarkness.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ThirdArtOfDarkness.cs index a1049e5f56..e58095e50d 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ThirdArtOfDarkness.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ThirdArtOfDarkness.cs @@ -5,6 +5,7 @@ class ThirdArtOfDarknessCleave(BossModule module) : Components.GenericAOEs(modul public enum Mechanic { None, Left, Right, Stack, Spread } public readonly Dictionary> Mechanics = []; + public BitMask PlatformPlayers; private static readonly AOEShapeCone _shape = new(15, 90.Degrees()); @@ -25,12 +26,28 @@ public override IEnumerable ActiveAOEs(int slot, Actor actor) public override void AddHints(int slot, Actor actor, TextHints hints) { - var (a, m) = Mechanics.FirstOrDefault(kv => kv.Key.InstanceID == actor.TargetID); - if (a != null && m.Count > 0) - hints.Add($"Order: {string.Join(" > ", m.Select(m => m.mechanic))}", false); + if (PlatformPlayers[slot]) + { + var playerSide = actor.Position.X - Module.Center.X; + var (a, m) = Mechanics.FirstOrDefault(kv => (kv.Key.Position.X - Module.Center.X) * playerSide > 0); + if (a != null && m.Count > 0) + hints.Add($"Order: {string.Join(" > ", m.Select(m => m.mechanic))}", false); + } base.AddHints(slot, actor, hints); } + public override void OnStatusGain(Actor actor, ActorStatus status) + { + if ((SID)status.ID == SID.OuterDarkness) + PlatformPlayers.Set(Raid.FindSlot(actor.InstanceID)); + } + + public override void OnStatusLose(Actor actor, ActorStatus status) + { + if ((SID)status.ID == SID.OuterDarkness) + PlatformPlayers.Clear(Raid.FindSlot(actor.InstanceID)); + } + public override void OnEventIcon(Actor actor, uint iconID, ulong targetID) { if ((OID)actor.OID == OID.StygianShadow) @@ -100,7 +117,7 @@ public override void Update() foreach (var (a, m) in _main.Mechanics) if (m.Count > 0 && m[0].mechanic == ThirdArtOfDarknessCleave.Mechanic.Stack) foreach (var p in Raid.WithoutSlot().SortedByRange(a.Position).Take(3)) - AddStack(p, m[0].activation); + AddStack(p, m[0].activation, ~_main.PlatformPlayers); base.Update(); } } diff --git a/BossMod/Replay/Analysis/TetherInfo.cs b/BossMod/Replay/Analysis/TetherInfo.cs index 44859e906c..27b68f2a73 100644 --- a/BossMod/Replay/Analysis/TetherInfo.cs +++ b/BossMod/Replay/Analysis/TetherInfo.cs @@ -6,10 +6,54 @@ namespace BossMod.ReplayAnalysis; class TetherInfo : CommonEnumInfo { + public readonly record struct Instance(Replay Replay, Replay.Encounter Enc, Replay.Tether Tether) + { + public string TimestampString() => $"{Replay.Path} @ {Enc.Time.Start:O}+{(Tether.Time.Start - Enc.Time.Start).TotalSeconds:f4}"; + + public override string ToString() => $"{TimestampString()}: {ReplayUtils.ParticipantPosRotString(Tether.Source, Tether.Time.Start)} -> {ReplayUtils.ParticipantPosRotString(Tether.Target, Tether.Time.Start)}, active for {Tether.Time}s"; + } + + class BreakAnalysis + { + private readonly UIPlot _plot = new(); + private readonly List<(Instance inst, Vector2 startEnd)> _points = []; + + public BreakAnalysis(List infos) + { + _plot.DataMin = new(float.MaxValue, float.MaxValue); + _plot.DataMax = new(float.MinValue, float.MinValue); + _plot.TickAdvance = new(5, 5); + foreach (var inst in infos) + { + var s = (inst.Tether.Target.PosRotAt(inst.Tether.Time.Start).XZ() - inst.Tether.Source.PosRotAt(inst.Tether.Time.Start).XZ()).Length(); + var e = (inst.Tether.Target.PosRotAt(inst.Tether.Time.End).XZ() - inst.Tether.Source.PosRotAt(inst.Tether.Time.End).XZ()).Length(); + _plot.DataMin.X = Math.Min(_plot.DataMin.X, s); + _plot.DataMin.Y = Math.Min(_plot.DataMin.Y, e); + _plot.DataMax.X = Math.Max(_plot.DataMax.X, s); + _plot.DataMax.Y = Math.Max(_plot.DataMax.Y, e); + _points.Add((inst, new(s, e))); + } + _plot.DataMin.X -= 1; + _plot.DataMin.Y -= 1; + _plot.DataMax.X += 1; + _plot.DataMax.Y += 1; + } + + public void Draw() + { + _plot.Begin(); + foreach (var i in _points) + _plot.Point(i.startEnd, 0xff808080, i.inst.TimestampString); + _plot.End(); + } + } + private class TetherData { - public HashSet SourceOIDs = []; - public HashSet TargetOIDs = []; + public readonly List Instances = []; + public readonly HashSet SourceOIDs = []; + public readonly HashSet TargetOIDs = []; + public BreakAnalysis? BreakAnalysis; } private readonly Type? _tidType; @@ -27,6 +71,7 @@ public TetherInfo(List replays, uint oid) foreach (var tether in replay.EncounterTethers(enc)) { var data = _data.GetOrAdd(tether.ID); + data.Instances.Add(new(replay, enc, tether)); data.SourceOIDs.Add(tether.Source.Type != ActorType.DutySupport ? tether.Source.OID : 0); data.TargetOIDs.Add(tether.Target.Type != ActorType.DutySupport ? tether.Target.OID : 0); } @@ -46,6 +91,15 @@ UITree.NodeProperties map(KeyValuePair kv) tree.LeafNode($"Source IDs: {OIDListString(data.SourceOIDs)}"); tree.LeafNode($"Target IDs: {OIDListString(data.TargetOIDs)}"); tree.LeafNode($"VFX: {Service.LuminaRow(tid)?.File}"); + foreach (var n in tree.Node("Instances", data.Instances.Count == 0)) + { + tree.LeafNodes(data.Instances, inst => inst.ToString()); + } + foreach (var an in tree.Node("Break distance analysis")) + { + data.BreakAnalysis ??= new(data.Instances); + data.BreakAnalysis.Draw(); + } } } From d11ba6353bfd2b4868a8d34099ec01f211839cd2 Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:14:39 +0100 Subject: [PATCH 2/4] code analyzer fix? --- .../Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs b/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs index ba0d81a3f5..0879f6dd5d 100644 --- a/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs +++ b/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs @@ -104,7 +104,7 @@ private static Stampede ResetStampede(Stampede stampede, (WPos, Angle) position) stampede.Rotation = position.Item2; stampede.Count = 0; stampede.Reset = default; - stampede.Beasts = []; + stampede.Beasts.Clear(); return stampede; } @@ -147,7 +147,7 @@ private void UpdateStampede(ref Stampede stampede) if (b.Position.InRect(stampede.Position, stampede.Rotation, 0, 10, 5) && !updatedBeasts.Contains(b) && stampede.Active) updatedBeasts.Add(b); } - stampede = new Stampede(stampede.Active, stampede.Position, stampede.Rotation, updatedBeasts); + stampede = new(stampede.Active, stampede.Position, stampede.Rotation, updatedBeasts); } private void ResetStampede(ref Stampede stampede) @@ -185,6 +185,7 @@ public record struct Stampede(bool Active, WPos Position, Angle Rotation, List Beasts { get; } = Beasts; } class Icebreaker(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Icebreaker), 17, targetIsLocation: true); From 9557523e5bf8a1f87b1988c92e7cc2cc312a088b Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Wed, 1 Jan 2025 20:36:41 +0100 Subject: [PATCH 3/4] albion update --- .../Dungeon/D11LapisManalis/D111Albion.cs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs b/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs index 0879f6dd5d..66164b1773 100644 --- a/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs +++ b/BossMod/Modules/Endwalker/Dungeon/D11LapisManalis/D111Albion.cs @@ -38,33 +38,30 @@ public enum AID : uint class WildlifeCrossing(BossModule module) : Components.GenericAOEs(module) { private static readonly AOEShapeRect rect = new(20, 5, 20); - private static readonly Angle Rot90 = 90.Degrees(); - private static readonly Angle RotM90 = -90.Degrees(); + private Queue stampedes = new(); private static readonly uint[] animals = [(uint)OID.WildBeasts1, (uint)OID.WildBeasts2, (uint)OID.WildBeasts3, (uint)OID.WildBeasts4]; - private static readonly (WPos, Angle)[] stampedePositions = + private static readonly WPos[] stampedePositions = [ - (new(4, -759), Rot90), (new(44, -759), RotM90), - (new(4, -749), Rot90), (new(44, -749), RotM90), - (new(4, -739), Rot90), (new(44, -739), RotM90), - (new(4, -729), Rot90), (new(44, -729), RotM90) + new(4, -759), new(44, -759), + new(4, -749), new(44, -749), + new(4, -739), new(44, -739), + new(4, -729), new(44, -729) ]; - private static Stampede NewStampede((WPos, Angle) stampede) => new(true, stampede.Item1, stampede.Item2, []); + private static Stampede NewStampede(WPos stampede) => new(true, stampede, stampede.X == 4 ? Angle.AnglesCardinals[3] : Angle.AnglesCardinals[0], new(40)); public override IEnumerable ActiveAOEs(int slot, Actor actor) { foreach (var stampede in stampedes) if (stampede.Active) - yield return stampede.Beasts.Count > 0 ? CreateAOEInstance(stampede) : new(rect, stampede.Position, Rot90); + yield return stampede.Beasts.Count > 0 ? CreateAOEInstance(stampede) : new(rect, stampede.Position, Angle.AnglesCardinals[3]); } private static AOEInstance CreateAOEInstance(Stampede stampede) { - var length = CalculateStampedeLength(stampede.Beasts) + 30; - var position = new WPos(stampede.Beasts[^1].Position.X, stampede.Position.Z); - return new(new AOEShapeRect(length, 5), position, stampede.Rotation); + return new(new AOEShapeRect(CalculateStampedeLength(stampede.Beasts) + 30, 5), new(stampede.Beasts[^1].Position.X, stampede.Position.Z), stampede.Rotation); } private static float CalculateStampedeLength(List beasts) => (beasts[0].Position - beasts[^1].Position).Length(); @@ -83,7 +80,7 @@ public override void OnEventEnvControl(byte index, uint state) stampedes.Enqueue(stampede); } - private Stampede GetOrCreateStampede((WPos, Angle) stampedePosition) + private Stampede GetOrCreateStampede(WPos stampedePosition) { var inactiveStampede = stampedes.FirstOrDefault(s => !s.Active); @@ -97,18 +94,18 @@ private Stampede GetOrCreateStampede((WPos, Angle) stampedePosition) return NewStampede(stampedePosition); } - private static Stampede ResetStampede(Stampede stampede, (WPos, Angle) position) + private static Stampede ResetStampede(Stampede stampede, WPos position) { stampede.Active = true; - stampede.Position = position.Item1; - stampede.Rotation = position.Item2; - stampede.Count = 0; + stampede.Position = position; + stampede.Rotation = stampede.Position.X == 4 ? Angle.AnglesCardinals[3] : Angle.AnglesCardinals[0]; + stampede.Count = default; stampede.Reset = default; stampede.Beasts.Clear(); return stampede; } - private static (WPos, Angle)? GetStampedePosition(byte index) + private static WPos? GetStampedePosition(byte index) { return index switch { @@ -140,14 +137,14 @@ public override void Update() private void UpdateStampede(ref Stampede stampede) { var beasts = Module.Enemies(animals); - var updatedBeasts = stampede.Beasts.ToList(); + var updatedBeasts = stampede.Beasts == null ? new HashSet(40) : [.. stampede.Beasts]; // use HashSet to easily prevent duplicates for (var i = 0; i < beasts.Count; ++i) { var b = beasts[i]; - if (b.Position.InRect(stampede.Position, stampede.Rotation, 0, 10, 5) && !updatedBeasts.Contains(b) && stampede.Active) + if (b.Position.InRect(stampede.Position, stampede.Rotation, 0, 10, 5) && stampede.Active) updatedBeasts.Add(b); } - stampede = new(stampede.Active, stampede.Position, stampede.Rotation, updatedBeasts); + stampede = stampede with { Beasts = [.. updatedBeasts] }; } private void ResetStampede(ref Stampede stampede) @@ -185,7 +182,7 @@ public record struct Stampede(bool Active, WPos Position, Angle Rotation, List Beasts { get; } = Beasts; + public List Beasts = Beasts; } class Icebreaker(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Icebreaker), 17, targetIsLocation: true); @@ -196,8 +193,11 @@ class IcyThroes2(BossModule module) : IcyThroes(module, AID.IcyThroes2); class IcyThroesSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.IcyThroesSpread), 6); class KnockOnIce(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.KnockOnIce), new AOEShapeCircle(5)); -class RightSlam(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.RightSlam), new AOEShapeRect(20, 80, 0, -90.Degrees())); // full width = half width in this case + angle is detected incorrectly, length and width are also switched -class LeftSlam(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LeftSlam), new AOEShapeRect(20, 80, 0, 90.Degrees())); // full width = half width in this case + angle is detected incorrectly, length and width are also switched + +abstract class Slam(BossModule module, AID aid) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(aid), new AOEShapeRect(80, 10)); +class RightSlam(BossModule module) : Slam(module, AID.RightSlam); +class LeftSlam(BossModule module) : Slam(module, AID.LeftSlam); + class AlbionsEmbrace(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.AlbionsEmbrace)); class RoarOfAlbion(BossModule module) : Components.CastLineOfSightAOE(module, ActionID.MakeSpell(AID.RoarOfAlbion), 60) From 0d9d1b7c2e42ced938693ad8f58824b61a2affad Mon Sep 17 00:00:00 2001 From: CarnifexOptimus <156172553+CarnifexOptimus@users.noreply.github.com> Date: Wed, 1 Jan 2025 21:10:16 +0100 Subject: [PATCH 4/4] merge fix --- .../Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs index 5b7848001e..c1d932f7d6 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/ParticleConcentration.cs @@ -1,6 +1,4 @@ -using System.Collections.Specialized; - -namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness; +namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness; // envcontrols: // 1F-2E = 1-man towers