diff --git a/BossMod/Modules/Endwalker/Ultimate/TOP/P1Pantokrator.cs b/BossMod/Modules/Endwalker/Ultimate/TOP/P1Pantokrator.cs index 1fd22684d..9e2034cb6 100644 --- a/BossMod/Modules/Endwalker/Ultimate/TOP/P1Pantokrator.cs +++ b/BossMod/Modules/Endwalker/Ultimate/TOP/P1Pantokrator.cs @@ -25,7 +25,13 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) var group = _pantokrator != null ? _pantokrator.PlayerStates[pcSlot].Group : 0; if (group > 0) { - var dir = (Casters[0].CastInfo!.Rotation - Module.PrimaryActor.Rotation).Normalized().Deg switch + var nesw = Service.Config.Get().P1PantokratorNESW; + var flame1Dir = Casters[0].CastInfo!.Rotation - Module.PrimaryActor.Rotation; + // if ne/sw, set of safe cones is offset by 1 rotation + if (nesw) + flame1Dir += 60.Degrees(); + + var dir = flame1Dir.Normalized().Deg switch { (> 15 and < 45) or (> -165 and < -135) => -60.Degrees(), (> 45 and < 75) or (> -135 and < -105) => -30.Degrees(), @@ -34,6 +40,9 @@ public override void DrawArenaForeground(int pcSlot, Actor pc) (> 135 and < 165) or (> -45 and < -15) => 60.Degrees(), _ => -90.Degrees(), // assume groups go CW }; + // undo direction adjustment to correct target safe spot + if (nesw) + dir -= 60.Degrees(); var offset = 12 * (Module.PrimaryActor.Rotation + dir).ToDirection(); var pos = group == 1 ? Module.Center + offset : Module.Center - offset; Arena.AddCircle(pos, 1, ArenaColor.Safe); diff --git a/BossMod/Modules/Endwalker/Ultimate/TOP/P2PartySynergy.cs b/BossMod/Modules/Endwalker/Ultimate/TOP/P2PartySynergy.cs index 7fde53984..b9f19e2a5 100644 --- a/BossMod/Modules/Endwalker/Ultimate/TOP/P2PartySynergy.cs +++ b/BossMod/Modules/Endwalker/Ultimate/TOP/P2PartySynergy.cs @@ -84,6 +84,27 @@ public override void OnEventIcon(Actor actor, uint iconID) Glitch.Remote => (34, 50), _ => (0, 50) }; + + // determine north => south order for player based on what glitch is active, used for flare stacks + public int GetNorthSouthOrder(PlayerState st) + { + if (st.Group is < 1 or > 2) + return 0; + + if (st.Group == 1 || ActiveGlitch == Glitch.Mid) + return st.Order; + + var reverseAll = Service.Config.Get().P2PartySynergyG2ReverseAll; + + return st.Order switch + { + 1 => 4, + 2 => reverseAll ? 3 : 2, + 3 => reverseAll ? 2 : 3, + 4 => 1, + _ => 0 + }; + } } class P2PartySynergyDoubleAOEs(BossModule module) : Components.GenericAOEs(module) @@ -176,20 +197,14 @@ private WDir AssignedPosition(int slot) return new(); var ps = _synergy.PlayerStates[slot]; - if (ps.Order == 0 || ps.Group == 0) + if (ps.Order == 0 || ps.Group == 0 || _synergy.ActiveGlitch == P2PartySynergy.Glitch.Unknown) return new(); var eyeOffset = _source.Position - Module.Center; - switch (_synergy.ActiveGlitch) - { - case P2PartySynergy.Glitch.Mid: - var toRelNorth = eyeOffset.Normalized(); - return 10 * (2.5f - ps.Order) * toRelNorth + 11 * (ps.Group == 1 ? toRelNorth.OrthoL() : toRelNorth.OrthoR()); - case P2PartySynergy.Glitch.Remote: - return 19 * (Angle.FromDirection(eyeOffset) + ps.Order * 40.Degrees() - 10.Degrees() + (ps.Group == 1 ? 0.Degrees() : 180.Degrees())).ToDirection(); - default: - return new(); - } + var toRelNorth = eyeOffset.Normalized(); + var order = _synergy.GetNorthSouthOrder(ps); + var centerOffset = _synergy.ActiveGlitch == P2PartySynergy.Glitch.Remote && order is 2 or 3 ? 17.5f : 11; + return 10 * (2.5f - order) * toRelNorth + centerOffset * (ps.Group == 1 ? toRelNorth.OrthoL() : toRelNorth.OrthoR()); } } @@ -209,6 +224,7 @@ class P2PartySynergyEfficientBladework : Components.GenericAOEs private int _firstStackSlot = -1; private BitMask _firstGroup; private string _swaps = ""; + private readonly TOPConfig _config = Service.Config.Get(); private static readonly AOEShapeCircle _shape = new(10); @@ -275,8 +291,17 @@ public override void OnEventIcon(Actor actor, uint iconID) var s2 = _synergy.PlayerStates[slot]; if (s1.Group == s2.Group) { - // ok, we need adjusts - assume whoever is more S adjusts - that is higher order in G1 or G2 with mid glitch, or lower order in G2 with remote glitch - var adjustOrder = s1.Group == 2 && _synergy.ActiveGlitch == P2PartySynergy.Glitch.Remote ? Math.Min(s1.Order, s2.Order) : Math.Max(s1.Order, s2.Order); + // need adjust + var s1Order = _synergy.GetNorthSouthOrder(s1); + var s2Order = _synergy.GetNorthSouthOrder(s2); + int adjustOrder; + if (_config.P2PartySynergyStackSwapSouth) + // south = higher order will swap + adjustOrder = s1Order > s2Order ? s1.Order : s2.Order; + else + // north = lower + adjustOrder = s1Order > s2Order ? s2.Order : s1.Order; + for (int s = 0; s < _synergy.PlayerStates.Length; ++s) { if (_synergy.PlayerStates[s].Order == adjustOrder) diff --git a/BossMod/Modules/Endwalker/Ultimate/TOP/P3OversampledWaveCannon.cs b/BossMod/Modules/Endwalker/Ultimate/TOP/P3OversampledWaveCannon.cs index b00dc13bd..cb58ff921 100644 --- a/BossMod/Modules/Endwalker/Ultimate/TOP/P3OversampledWaveCannon.cs +++ b/BossMod/Modules/Endwalker/Ultimate/TOP/P3OversampledWaveCannon.cs @@ -85,20 +85,30 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) if (_numPlayerAngles < 3 || _bossAngle == default) yield break; + var m3South = Service.Config.Get().P3LastMonitorSouth; + WPos adjust(float x, float z) => Module.Center + new WDir(_bossAngle.Rad < 0 ? -x : x, z); if (IsMonitor(slot)) { - yield return (adjust(10, -11), _playerOrder[slot] == 1); - yield return (adjust(-11, -9), _playerOrder[slot] == 2); - yield return (adjust(-11, +9), _playerOrder[slot] == 3); + var nextSlot = 0; + if (!m3South) + yield return (adjust(10, -11), _playerOrder[slot] == ++nextSlot); + yield return (adjust(-11, -9), _playerOrder[slot] == ++nextSlot); + yield return (adjust(-11, +9), _playerOrder[slot] == ++nextSlot); + if (m3South) + yield return (adjust(10, 11), _playerOrder[slot] == ++nextSlot); } else { - yield return (adjust(1, -15), _playerOrder[slot] == 1); - yield return (adjust(15, -4), _playerOrder[slot] == 2); - yield return (adjust(15, +4), _playerOrder[slot] == 3); - yield return (adjust(10, 11), _playerOrder[slot] == 4); - yield return (adjust(1, 15), _playerOrder[slot] == 5); + var nextSlot = 0; + yield return (adjust(1, -15), _playerOrder[slot] == ++nextSlot); + if (m3South) + yield return (adjust(10, -11), _playerOrder[slot] == ++nextSlot); + yield return (adjust(15, -4), _playerOrder[slot] == ++nextSlot); + yield return (adjust(15, +4), _playerOrder[slot] == ++nextSlot); + if (!m3South) + yield return (adjust(10, 11), _playerOrder[slot] == ++nextSlot); + yield return (adjust(1, 15), _playerOrder[slot] == ++nextSlot); } } diff --git a/BossMod/Modules/Endwalker/Ultimate/TOP/TOPConfig.cs b/BossMod/Modules/Endwalker/Ultimate/TOP/TOPConfig.cs index e55f96d6a..340aaca09 100644 --- a/BossMod/Modules/Endwalker/Ultimate/TOP/TOPConfig.cs +++ b/BossMod/Modules/Endwalker/Ultimate/TOP/TOPConfig.cs @@ -16,6 +16,10 @@ public class TOPConfig() : CooldownPlanningConfigNode(90) [GroupPreset("LPDU (light parties): flex T>M>R", [0, 4, 3, 7, 1, 5, 2, 6])] public GroupAssignmentUnique P1PantokratorAssignments = new() { Assignments = [0, 4, 3, 7, 1, 5, 2, 6] }; + [PropertyDisplay("P1 Pantokrator: group positions")] + [PropertyCombo("North/South", "NE/SW")] + public bool P1PantokratorNESW = false; + [PropertyDisplay("P1 Pantokrator: use global priority instead - consider G1 lower-numbered than G2 (so G1 more likely to flex)")] public bool P1PantokratorGlobalPriority = false; @@ -27,6 +31,14 @@ public class TOPConfig() : CooldownPlanningConfigNode(90) [PropertyDisplay("P2 Party Synergy: use global priority instead - consider G1 lower-numbered than G2 (so G1 more likely to flex)")] public bool P2PartySynergyGlobalPriority = false; + [PropertyDisplay("P2 Party Synergy: G2 order for Remote Glitch (far tether)")] + [PropertyCombo("GPOB (only B and G swap)", "GOPB (reverse order)")] + public bool P2PartySynergyG2ReverseAll = true; + + [PropertyDisplay("P2 Party Synergy: Swap priority if both stacks are in the same group")] + [PropertyCombo("Northernmost pair", "Southernmost pair")] + public bool P2PartySynergyStackSwapSouth = true; + [PropertyDisplay("P3 Intermission: spread/stack spot assignments, from West to East")] [GroupDetails(["1", "2", "3", "4", "5", "6", "7", "8"])] [GroupPreset("LPDU (RMTH HTMR)", [2, 5, 3, 4, 1, 6, 0, 7])] @@ -41,6 +53,10 @@ public class TOPConfig() : CooldownPlanningConfigNode(90) [GroupPreset("LPDU (HTMR)", [2, 3, 0, 1, 4, 5, 6, 7])] public GroupAssignmentUnique P3MonitorsAssignments = new() { Assignments = [2, 3, 0, 1, 4, 5, 6, 7] }; + [PropertyDisplay("P3 Monitors: position for safe side monitor")] + [PropertyCombo("North (LPDU)", "South (Aether)")] + public bool P3LastMonitorSouth = false; + [PropertyDisplay("P4 Wave Cannon: priority, from North to South (assuming south flex)")] [GroupDetails(["W1", "E1", "W2", "E2", "W3", "E3", "W4", "E4"])] [GroupPreset("LPDU (TRHM)", [0, 1, 4, 5, 6, 7, 2, 3])]