Skip to content

Commit

Permalink
Merge pull request #518 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
sphene ex improvements
  • Loading branch information
CarnifexOptimus authored Dec 26, 2024
2 parents c6bb1f8 + 634f8cb commit 64d8d56
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,32 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)
}
}

class AbsoluteAuthorityHeel(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.AbsoluteAuthorityHeel))
class AbsoluteAuthorityHeel(BossModule module) : Components.GenericStackSpread(module)
{
private BitMask _targets;

private const float Radius = 3; // TODO: verify
public int NumCasts;

public override void AddHints(int slot, Actor actor, TextHints hints)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (_targets[slot] && !Raid.WithoutSlot().InRadiusExcluding(actor, Radius).Any())
hints.Add("Stack with someone!");
if ((IconID)iconID == IconID.AuthoritysHeel && Stacks.Count == 0)
Stacks.Add(new(actor, 1.5f, 8, 8, activation: WorldState.FutureTime(5.1f)));
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
public override void Update()
{
if (_targets[pcSlot])
Arena.AddCircle(pc.Position, Radius, Colors.Safe);
if (Stacks.Count != 0)
{
var player = Raid.Player()!;
var actor = Raid.WithoutSlot().Exclude(player).OrderBy(a => (player.Position - a.Position).LengthSq()).First();
Stacks[0] = Stacks[0] with { Target = actor ?? player };
}
}

public override void OnStatusGain(Actor actor, ActorStatus status)
public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((SID)status.ID == SID.AuthoritysHeel)
if ((AID)spell.Action.ID is AID.AbsoluteAuthorityHeel or AID.AbsoluteAuthorityHeelFail)
{
_targets.Set(Raid.FindSlot(actor.InstanceID));
Stacks.Clear();
++NumCasts;
}
}
}
Expand Down
48 changes: 48 additions & 0 deletions BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/ArenaChanges.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace BossMod.Dawntrail.Extreme.Ex3QueenEternal;

class ArenaChanges(BossModule module) : BossComponent(module)
{
public override bool KeepOnPhaseChange => true;
private bool firstEarthArena = true;

public override void OnEventDirectorUpdate(uint updateID, uint param1, uint param2, uint param3, uint param4)
{
if (updateID != 0x8000000D || param1 > 0x08)
return;
switch (param1)
{
case 0x01: // default arena
SetArena(Ex3QueenEternal.NormalBounds, Ex3QueenEternal.ArenaCenter);
break;
case 0x02: // x arena (wind)
SetArena(Ex3QueenEternal.WindBounds, Ex3QueenEternal.WindBounds.Center);
break;
case 0x04: // disjointed rect (Earth) arena
if (firstEarthArena)
firstEarthArena = false; // don't want to switch arena here because of gravity stuff
else
SetArena(Ex3QueenEternal.EarthBounds, Ex3QueenEternal.EarthBounds.Center);
break;
case 0x08: // ice arena
SetArena(Ex3QueenEternal.IceBounds, Ex3QueenEternal.IceBounds.Center);
break;
}
}

public override void OnEventEnvControl(byte index, uint state)
{
if (index == 0x08)
{
if (state == 0x01000080)
SetArena(Ex3QueenEternal.HalfBounds, Ex3QueenEternal.HalfBoundsCenter);
else if (state == 0x02000001)
SetArena(Ex3QueenEternal.NormalBounds, Ex3QueenEternal.ArenaCenter);
}
}

private void SetArena(ArenaBounds bounds, WPos center)
{
Arena.Bounds = bounds;
Arena.Center = center;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ public class Ex3QueenEternal(WorldState ws, Actor primary) : BossModule(ws, prim
public static readonly ArenaBoundsComplex EarthBounds = Trial.T03QueenEternal.T03QueenEternal.SplitArena;
private static readonly Rectangle[] iceRects = [new(new(112, 95), 4, 15), new(new(88, 95), 4, 15), new(ArenaCenter, 2, 10)];
public static readonly Rectangle[] IceRectsAll = [.. iceRects, new(new(100, 96), 8, 2), new(new(100, 104), 8, 2)];
public static readonly ArenaBoundsComplex IceBounds = new(iceRects, Offset: Trial.T03QueenEternal.T03QueenEternal.OffSet);
public static readonly ArenaBoundsComplex IceBridgeBounds = new(IceRectsAll, Offset: Trial.T03QueenEternal.T03QueenEternal.OffSet);
public static readonly ArenaBoundsComplex IceBounds = new(iceRects);

private Actor? _bossP2;
public Actor? BossP1() => PrimaryActor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ class Ex3QueenEternalConfig() : ConfigNode()
{
[PropertyDisplay("Absolute Authority: ignore flares, stack together")]
public bool AbsoluteAuthorityIgnoreFlares = true;

[PropertyDisplay("Fixed bridge tether spots", tooltip: "Side tethers stretch without crossing is typically used on EU/NA partyfinder, the other option is usually used by JP")]
[PropertyCombo("Side tethers stretch without crossing", "Side tethers stretch cross (JP)")]
public bool SideTethersNoCrossing = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public Ex3QueenEternalStates(Ex3QueenEternal module) : base(module)
{
_module = module;
SimplePhase(0, Phase1, "P1")
.ActivateOnEnter<ArenaChanges>()
.Raw.Update = () => Module.PrimaryActor.IsDeadOrDestroyed || Module.PrimaryActor.HPMP.CurHP == 1 && (Module.PrimaryActor.CastInfo?.IsSpell(AID.AuthorityEternal) ?? false);
SimplePhase(1, Phase2, "P2")
.Raw.Update = () => Module.PrimaryActor.IsDeadOrDestroyed && (_module.BossP2()?.IsDeadOrDestroyed ?? true);
Expand Down Expand Up @@ -116,10 +117,10 @@ private void P1AbsoluteAuthority(uint id, float delay)
.ActivateOnEnter<AbsoluteAuthorityPuddles>();
ComponentCondition<AbsoluteAuthorityExpansionBoot>(id + 0x20, 10, comp => comp.NumCasts > 0, "Spread/stack")
.ActivateOnEnter<AbsoluteAuthorityExpansionBoot>()
.ActivateOnEnter<AbsoluteAuthorityHeel>()
.DeactivateOnExit<AbsoluteAuthorityPuddles>() // last puddle resolves right before stack/spread
.DeactivateOnExit<AbsoluteAuthorityExpansionBoot>();
ComponentCondition<AbsoluteAuthorityHeel>(id + 0x30, 4, comp => comp.NumCasts > 0, "Stack")
.ActivateOnEnter<AbsoluteAuthorityHeel>()
.DeactivateOnExit<AbsoluteAuthorityHeel>();
ComponentCondition<AbsoluteAuthorityKnockback>(id + 0x40, 6.9f, comp => comp.NumCasts > 0, "Knockback")
.ActivateOnEnter<AbsoluteAuthorityKnockback>()
Expand All @@ -129,8 +130,6 @@ private void P1AbsoluteAuthority(uint id, float delay)
private void P1VirtualShiftWind(uint id, float delay)
{
Cast(id, AID.VirtualShiftWind, delay, 5, "Raidwide (wind platform)")
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.WindBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.WindBounds.Center)
.SetHint(StateMachine.StateHint.Raidwide);
Cast(id + 0x10, AID.LawsOfWind, 5.2f, 4);
ComponentCondition<Aeroquell>(id + 0x20, 0.1f, comp => comp.Active)
Expand All @@ -153,8 +152,6 @@ private void P1VirtualShiftWind(uint id, float delay)
.DeactivateOnExit<MissingLink>();

Cast(id + 0x300, AID.WorldShatterP1, 3, 5, "Raidwide + platform end")
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.NormalBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.ArenaCenter)
.SetHint(StateMachine.StateHint.Raidwide);
ComponentCondition<AeroquellTwister>(id + 0x310, 2.6f, comp => !comp.Sources(Module).Any())
.DeactivateOnExit<AeroquellTwister>();
Expand Down Expand Up @@ -209,16 +206,11 @@ private void P1VirtualShiftIce(uint id, float delay)
{
Cast(id, AID.VirtualShiftIce, delay, 5, "Raidwide (ice platform)")
.ActivateOnEnter<VirtualShiftIce>()
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.IceBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.IceBounds.Center)
.SetHint(StateMachine.StateHint.Raidwide);
CastStart(id + 0x10, AID.LawsOfIce, 5.2f);
CastEnd(id + 0x11, 4)
.ActivateOnEnter<LawsOfIce>();
ComponentCondition<LawsOfIce>(id + 0x12, 1, comp => comp.NumCasts > 0, "Move")
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.IceBridgeBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.IceBridgeBounds.Center);

ComponentCondition<LawsOfIce>(id + 0x12, 1, comp => comp.NumCasts > 0, "Move");
ComponentCondition<Rush>(id + 0x100, 4.3f, comp => comp.Activation != default)
.ActivateOnEnter<Rush>()
.DeactivateOnExit<LawsOfIce>();
Expand All @@ -245,8 +237,6 @@ private void P1VirtualShiftIce(uint id, float delay)

Cast(id + 0x300, AID.WorldShatterP1, 3.1f, 5, "Raidwide + platform end")
.DeactivateOnExit<VirtualShiftIce>()
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.NormalBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.ArenaCenter)
.SetHint(StateMachine.StateHint.Raidwide);
}

Expand All @@ -264,21 +254,17 @@ private void P2RadicalShift(uint id, float delay)
ActorCast(id, _module.BossP2, AID.RadicalShift, delay, 11, true, "Raidwide (platform change)")
.ActivateOnEnter<RadicalShift>()
.ActivateOnEnter<VirtualShiftIce>()
.ActivateOnEnter<RadicalShiftAOE>()
.SetHint(StateMachine.StateHint.Raidwide);
ComponentCondition<RadicalShiftAOE>(id + 0x10, 5.2f, comp => comp.NumFinishedSpreads > 0, "Spread")
.ActivateOnEnter<RadicalShiftAOE>()
.DeactivateOnExit<RadicalShiftAOE>();

.ResetComp<RadicalShiftAOE>();
ActorCast(id + 0x100, _module.BossP2, AID.RadicalShift, 3, 11, true, "Raidwide (platform change)")
.SetHint(StateMachine.StateHint.Raidwide);
ComponentCondition<RadicalShiftAOE>(id + 0x110, 5.2f, comp => comp.NumFinishedSpreads > 0, "Spread")
.ActivateOnEnter<RadicalShiftAOE>()
.DeactivateOnExit<RadicalShiftAOE>();
ActorCast(id + 0x200, _module.BossP2, AID.WorldShatterP2, 3, 5, true, "Raidwide + platform end")
.DeactivateOnExit<RadicalShift>()
.DeactivateOnExit<VirtualShiftIce>()
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.NormalBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.ArenaCenter)
.SetHint(StateMachine.StateHint.Raidwide);
}

Expand All @@ -291,8 +277,6 @@ private void P2DimensionalDistortion(uint id, float delay)
ActorCast(id + 0x100, _module.BossP2, AID.TyrannysGrasp, 5.2f, 5, true, "Front half cleave")
.ActivateOnEnter<TyrannysGraspAOE>()
.ActivateOnEnter<TyrannysGraspTowers>()
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.HalfBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.HalfBoundsCenter)
.DeactivateOnExit<DimensionalDistortion>()
.DeactivateOnExit<TyrannysGraspAOE>();
ComponentCondition<TyrannysGraspTowers>(id + 0x110, 1.2f, comp => comp.NumCasts >= 1, "Tankbuster tower 1")
Expand Down Expand Up @@ -320,8 +304,6 @@ private void P2DyingMemory(uint id, float delay)
ComponentCondition<RoyalBanishment>(id + 0x120, 6, comp => comp.NumCasts >= 7);
ComponentCondition<RoyalBanishment>(id + 0x130, 3, comp => comp.NumCasts >= 8, "Line stack 8")
.DeactivateOnExit<RoyalBanishment>()
.OnExit(() => _module.Arena.Bounds = Ex3QueenEternal.NormalBounds)
.OnExit(() => _module.Arena.Center = Ex3QueenEternal.ArenaCenter)
.SetHint(StateMachine.StateHint.Raidwide);
}
}
23 changes: 11 additions & 12 deletions BossMod/Modules/Dawntrail/Extreme/Ex3QueenEternal/RadicalShift.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public enum Rotation { None, Left, Right }

public override void OnEventEnvControl(byte index, uint state)
{
if (index == 12)
if (index == 0x0C)
{
var rot = state switch
{
Expand All @@ -32,9 +32,9 @@ public override void OnEventEnvControl(byte index, uint state)
{
var platform = index switch
{
9 => Ex3QueenEternal.WindBounds,
10 => Ex3QueenEternal.EarthBounds,
11 => Ex3QueenEternal.IceBridgeBounds,
0x09 => Ex3QueenEternal.WindBounds,
0x0A => Ex3QueenEternal.EarthBounds,
0x0B => Ex3QueenEternal.IceBounds,
_ => null
};
if (platform != null)
Expand All @@ -45,19 +45,18 @@ public override void OnEventEnvControl(byte index, uint state)
}
}

public override void OnEventDirectorUpdate(uint updateID, uint param1, uint param2, uint param3, uint param4)
{
if (_aoe != null && updateID == 0x8000000D && param1 is 0x02 or 0x04 or 0x08)
_aoe = null;
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.RadicalShift)
{
var platform = NextPlatform;
if (platform != null)
{
Arena.Bounds = platform;
Arena.Center = platform.Center;
}
_left = _right = null;
_nextRotation = Rotation.None;
_aoe = null;
}
}

Expand All @@ -76,7 +75,7 @@ private void UpdateAOE(ArenaBoundsComplex? platform)
aoe = new(defaultSquare, Trial.T03QueenEternal.T03QueenEternal.XArenaRects, Origin: center);
else if (platform == Ex3QueenEternal.EarthBounds)
aoe = new(defaultSquare, Trial.T03QueenEternal.T03QueenEternal.SplitArenaRects, Origin: center);
else if (platform == Ex3QueenEternal.IceBridgeBounds)
else if (platform == Ex3QueenEternal.IceBounds)
aoe = new(defaultSquare, Ex3QueenEternal.IceRectsAll, Origin: center);
if (aoe != null)
_aoe = new(aoe, center, default, WorldState.FutureTime(6));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

class VirtualShiftIce(BossModule module) : Components.GenericAOEs(module, default, "GTFO from broken bridge!")
{
private readonly List<AOEInstance> _unsafeBridges = [];
private readonly List<Rectangle> _destroyedBridges = [];
private readonly List<AOEInstance> _unsafeBridges = new(4);
private readonly List<Rectangle> _destroyedBridges = [new(new(95, 96), 3, 2), new(new(95, 104), 3, 2), new(new(105, 96), 3, 2), new(new(95, 104), 3, 2)];

private static readonly AOEShapeRect _shape = new(2, 3, 2);

Expand All @@ -13,10 +13,10 @@ public override void OnEventEnvControl(byte index, uint state)
{
WDir offset = index switch
{
4 => new(-5, -4),
5 => new(-5, +4),
6 => new(+5, -4),
7 => new(+5, +4),
0x04 => new(-5, -4),
0x05 => new(-5, +4),
0x06 => new(+5, -4),
0x07 => new(+5, +4),
_ => default
};
if (offset == default)
Expand All @@ -25,18 +25,26 @@ public override void OnEventEnvControl(byte index, uint state)
var center = Ex3QueenEternal.ArenaCenter + offset;
switch (state)
{
case 0x00200010:
case 0x00020001: // destroyed bridge respawns
_destroyedBridges.RemoveAll(s => s.Center == center);
UpdateArena();
break;
case 0x00200010: // bridge gets damaged
_unsafeBridges.Add(new(_shape, center));
break;
case 0x00400001:
_unsafeBridges.RemoveAll(s => s.Origin == center);
case 0x00400001: // damaged bridge gets repaired
case 0x00080004: // bridges despawn
RemoveUnsafeBridges();
break;
case 0x00800004:
_unsafeBridges.RemoveAll(s => s.Origin == center);
case 0x00800004: // bridge gets destroyed
RemoveUnsafeBridges();
_destroyedBridges.Add(new(center, 3, 2));
Arena.Bounds = new ArenaBoundsComplex(Ex3QueenEternal.IceRectsAll, [.. _destroyedBridges], Offset: Trial.T03QueenEternal.T03QueenEternal.OffSet);
UpdateArena();
break;
}

void RemoveUnsafeBridges() => _unsafeBridges.RemoveAll(s => s.Origin == center);
void UpdateArena() => Arena.Bounds = new ArenaBoundsComplex(Ex3QueenEternal.IceRectsAll, [.. _destroyedBridges]);
}
}

Expand Down Expand Up @@ -67,6 +75,7 @@ class Rush(BossModule module) : Components.GenericBaitAway(module)
{
public DateTime Activation;
private BitMask _unstretched;
private readonly Ex3QueenEternalConfig _config = Service.Config.Get<Ex3QueenEternalConfig>();

private static readonly AOEShapeRect _shapeTether = new(80, 2);
private static readonly AOEShapeCircle _shapeUntethered = new(8); // if there is no tether, pillar will just explode; this can happen if someone is dead
Expand All @@ -88,7 +97,7 @@ public override void DrawArenaForeground(int pcSlot, Actor pc)
if (b.Target == pc)
{
Arena.AddLine(b.Source.Position, b.Target.Position, _unstretched[pcSlot] ? Colors.Danger : Colors.Safe);
Arena.AddCircle(SafeSpot(b.Source), 1, Colors.Safe);
Arena.AddCircle(SafeSpot(b.Source, _config), 1, Colors.Safe);
}
}
}
Expand Down Expand Up @@ -133,7 +142,7 @@ public override void OnUntethered(Actor source, ActorTetherInfo tether)
}
}

private WPos SafeSpot(Actor source)
private static WPos SafeSpot(Actor source, Ex3QueenEternalConfig config)
{
var center = Ex3QueenEternal.ArenaCenter;
var safeSide = source.Position.X > center.X ? -1 : +1;
Expand All @@ -148,7 +157,8 @@ private WPos SafeSpot(Actor source)
{
// second order
var central = source.Position.Z < 96;
return center + new WDir(safeSide * 15, central ? -2 : 10);
var strat = !config.SideTethersNoCrossing ? (central ? -2 : 9) : (central ? 9 : -9);
return center + new WDir(safeSide * 15, strat);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,12 @@ public T03QueenEternalStates(BossModule module) : base(module)
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 984, NameID = 13029)]
public class T03QueenEternal(WorldState ws, Actor primary) : BossModule(ws, primary, ArenaCenter, DefaultBounds)
{
public const float OffSet = -0.5f; // pathfinding offset
public static readonly WPos ArenaCenter = new(100, 100), FinalCenter = new(100, 105), LeftSplitCenter = new(108, 94), RightSplitCenter = new(92, 94);
public static readonly ArenaBoundsRect FinalBounds = new(20, 15), SplitGravityBounds = new(12, 8);
public static readonly ArenaBoundsSquare DefaultBounds = new(20);
public static readonly Shape[] XArenaRects = [new Rectangle(new(100, 82.5f), 12.5f, 2.5f), new Rectangle(new(100, 102.5f), 12.5f, 2.5f),
new Cross(new(100, 92.5f), 15, 2.5f, 45.Degrees())];
public static readonly ArenaBoundsComplex XArena = new(XArenaRects, Offset: OffSet);
public static readonly ArenaBoundsComplex XArena = new(XArenaRects);
public static readonly Rectangle[] SplitArenaRects = [new Rectangle(LeftSplitCenter, 4, 8), new Rectangle(RightSplitCenter, 4, 8)];
public static readonly ArenaBoundsComplex SplitArena = new(SplitArenaRects);
}

0 comments on commit 64d8d56

Please sign in to comment.