Skip to content

Commit

Permalink
Merge pull request #550 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
merge vbm autorotation update
  • Loading branch information
CarnifexOptimus authored Jan 11, 2025
2 parents c4d355f + 6f3b823 commit 08313df
Show file tree
Hide file tree
Showing 6 changed files with 967 additions and 822 deletions.
405 changes: 205 additions & 200 deletions BossMod/Autorotation/akechi/AkechiGNB.cs

Large diffs are not rendered by default.

1,268 changes: 694 additions & 574 deletions BossMod/Autorotation/akechi/AkechiPLD.cs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions BossMod/Autorotation/akechi/AkechiSCH.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,9 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay)
private bool ShouldUseBio(Actor? target, BioStrategy strategy) => strategy switch
{
BioStrategy.Bio3 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target),
BioStrategy.Bio6 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target),
BioStrategy.Bio9 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target),
BioStrategy.Bio0 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target),
BioStrategy.Bio6 => Player.InCombat && target != null && bioLeft <= 6 && In25y(target),
BioStrategy.Bio9 => Player.InCombat && target != null && bioLeft <= 9 && In25y(target),
BioStrategy.Bio0 => Player.InCombat && target != null && bioLeft is 0 && In25y(target),
BioStrategy.Force => true,
BioStrategy.Delay => false,
_ => false
Expand Down
8 changes: 5 additions & 3 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,13 @@ private void P2LightRampant(uint id, float delay)
ComponentCondition<P2HolyLightBurst>(id + 0x38, 3.2f, comp => comp.Casters.Count > 0)
.ActivateOnEnter<P2HolyLightBurst>()
.ActivateOnEnter<P2PowerfulLight>()
.ActivateOnEnter<P2LightRampantAIStack>()
.ActivateOnEnter<P2LightRampantAIStackPrepos>()
.DeactivateOnExit<P2LightRampantAIStackPrepos>()
.DeactivateOnExit<P2LuminousHammer>(); // last puddle is baited right before holy light burst casts start
ComponentCondition<P2PowerfulLight>(id + 0x40, 2.5f, comp => !comp.Active, "Stack")
.DeactivateOnExit<P2PowerfulLight>()
.DeactivateOnExit<P2LightRampantAIStack>();
.ActivateOnEnter<P2LightRampantAIStackResolve>()
.DeactivateOnExit<P2LightRampantAIStackResolve>()
.DeactivateOnExit<P2PowerfulLight>();
ComponentCondition<P2HolyLightBurst>(id + 0x50, 2.4f, comp => comp.NumCasts > 0, "Orbs 1")
.ActivateOnEnter<P2LightRampantAIOrbs>()
.ActivateOnEnter<P2BrightHunger2>();
Expand Down
98 changes: 58 additions & 40 deletions BossMod/Modules/Dawntrail/Ultimate/FRU/P2LightRampant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,65 +225,70 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
}
}

// movement to preposition for resolving stacks
class P2LightRampantAIStack(BossModule module) : BossComponent(module)
// movement to stack N/S after towers (and bait last two puddles)
class P2LightRampantAIStackPrepos(BossModule module) : BossComponent(module)
{
private readonly P2LuminousHammer? _puddles = module.FindComponent<P2LuminousHammer>();

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
var isPuddleBaiter = _puddles?.ActiveBaitsOn(actor).Any() ?? false;
var northCamp = isPuddleBaiter ? actor.Position.X < Module.Center.X : actor.Position.Z < Module.Center.Z; // this assumes CW movement for baiter
var dest = Module.Center + new WDir(0, northCamp ? -18 : 18);
if (isPuddleBaiter)
{
var maxDist = _puddles?.BaitsPerPlayer[slot] == 4 ? 7 : 13;
if (dest.InCircle(actor.Position, maxDist))
return; // don't move _too_ fast as a baiter
}
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(dest, 1), DateTime.MaxValue);
}
}

// movement to resolve stacks
class P2LightRampantAIStackResolve(BossModule module) : BossComponent(module)
{
private readonly P2PowerfulLight? _stack = module.FindComponent<P2PowerfulLight>();
private readonly P2HolyLightBurst? _orbs = module.FindComponent<P2HolyLightBurst>();

public const float Radius = 18;

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (_puddles == null || _stack == null || _orbs == null)
if (_stack == null || _orbs == null)
return;

// initially we don't know whether first orbs will cover N/S, puddle baiter is still relatively far away and has two baits left
// when orbs start, baiter has just finished his puddles; he can still be relatively far away from N/S center, so we might need to wait for him
var northCamp = IsNorthCamp(actor);
var startingDir = (northCamp ? 180 : 0).Degrees();
var startingPos = Module.Center + new WDir(0, northCamp ? -Radius : Radius);
if (_puddles.ActiveBaits.Any())
var centerDangerous = _orbs.ActiveCasters.Any(c => c.Position.Z - Module.Center.Z is var off && (northCamp ? off < -15 : off > 15));
var destDir = (northCamp ? 180 : 0).Degrees() - (centerDangerous ? 40 : 20).Degrees();
var destPos = Module.Center + Radius * destDir.ToDirection();
if (_stack.IsStackTarget(actor))
{
// just move to starting position, until all puddles are resolved
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(startingPos, 1), DateTime.MaxValue);
return;
}

var isStackTarget = _stack.IsStackTarget(actor);
var haveOrbs = _orbs.Casters.Count > 0;
var centerDangerous = haveOrbs && _orbs.ActiveCasters.Any(c => actor.Position.Z - Module.Center.Z is var off && (northCamp ? off < -15 : off > 15));
var idealDestDir = startingDir - (centerDangerous ? 40 : 20).Degrees(); // alt: haveOrbs ? 20 : 30 (but i don't think it's how people really move...)
var idealPos = Module.Center + Radius * idealDestDir.ToDirection();

if (isStackTarget)
{
// as a stack target, our responsibility is to wait for everyone to stack up, then carefully move towards ideal dir
// as a stack target, our responsibility is to wait for everyone to stack up, then carefully move towards destination
// note that we need to be careful to avoid oscillations
var toIdeal = idealPos - actor.Position;
foreach (var partner in Raid.WithoutSlot().Exclude(actor).Where(p => IsNorthCamp(p) == northCamp))
var toDest = destPos - actor.Position;
bool needToWaitFor(Actor partner)
{
if (partner == actor || IsNorthCamp(partner) != northCamp)
return false; // this is not our partner, we don't need to wait for him
var toPartner = partner.Position - actor.Position;
var distSq = toPartner.LengthSq();
if (distSq > 9 && toIdeal.Dot(toPartner) < 0)
{
// partner is far enough away, and moving towards ideal pos will not bring us closer => just stay where we are
return;
}
return distSq > 9 && toDest.Dot(toPartner) < 0; // partner is far enough away, and moving towards destination will not bring us closer
}
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(idealPos, 1), DateTime.MaxValue);
if (!Raid.WithoutSlot().Any(needToWaitFor))
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(destPos, 1), DateTime.MaxValue);
// else: we still have someone we need to wait for, just stay where we are...
}
else if (_stack.Stacks.FirstOrDefault(s => IsNorthCamp(s.Target) == northCamp).Target is var stackTarget && stackTarget != null)
{
// otherwise we just want to stay close to the stack target, slightly offset to the ideal position
var dirToIdeal = idealPos - stackTarget.Position;
var dest = dirToIdeal.LengthSq() <= 4 ? idealPos : stackTarget.Position + 2 * dirToIdeal.Normalized();
// we just want to stay close to the stack target, slightly offset to the destination
var dirToDest = destPos - stackTarget.Position;
var dest = dirToDest.LengthSq() <= 4 ? destPos : stackTarget.Position + 2 * dirToDest.Normalized();
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(dest, 1), DateTime.MaxValue);
}
}

private bool IsNorthCamp(Actor actor) => (_puddles?.ActiveBaitsOn(actor).Any() ?? false) ? actor.Position.X < Module.Center.X : actor.Position.Z < Module.Center.Z;
private bool IsNorthCamp(Actor actor) => actor.Position.Z < Module.Center.Z;
}

// movement to dodge orbs after resolving stack
Expand All @@ -296,20 +301,32 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
if (_orbs == null || _orbs.Casters.Count == 0)
return;

// actual orb aoes
//var resolve = Module.CastFinishAt(_orbs.Casters[0].CastInfo, _orbs.NumCasts == 0 ? 0 : -1); // for second set, we want to dodge out asap without weird movement to the center
foreach (var c in _orbs.ActiveCasters)
hints.AddForbiddenZone(_orbs.Shape.Distance(c.Position, default), Module.CastFinishAt(c.CastInfo));

if (_orbs.NumCasts == 0)
{
// dodge first orbs, while staying near edge
hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 16));
// ... and close to the aoes
var cushioned = ShapeDistance.Union([.. _orbs.ActiveCasters.Select(c => ShapeDistance.Circle(c.Position, 13))]);
hints.AddForbiddenZone(p => -cushioned(p), DateTime.MaxValue);
}
else if (_orbs.Casters.Any(c => _orbs.Shape.Check(actor.Position, c)))
{
// dodge second orbs while staying near edge (tethers are still up for a bit after first dodge)
hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 16));
}
else
{
// dodge second orbs, while trying to come closer to the center
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 6), DateTime.MaxValue);
// now that we're safe, move closer to the center (this is a bit sus, but whatever...)
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 16), WorldState.FutureTime(30));
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 13), WorldState.FutureTime(40));
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 10), WorldState.FutureTime(50));
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 7), DateTime.MaxValue);
}

// actual orb aoes
foreach (var c in _orbs.ActiveCasters)
hints.AddForbiddenZone(_orbs.Shape.Distance(c.Position, default), Module.CastFinishAt(c.CastInfo, -1));
}
}

Expand All @@ -326,6 +343,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme

ref var t = ref _tower.Towers.Ref(0);
hints.AddForbiddenZone(t.ForbiddenSoakers[slot] ? ShapeDistance.Circle(t.Position, t.Radius) : ShapeDistance.InvertedCircle(t.Position, t.Radius), t.Activation);
hints.AddForbiddenZone(t.ForbiddenSoakers[slot] ? ShapeDistance.Circle(t.Position, t.Radius + 2) : ShapeDistance.InvertedCircle(t.Position, t.Radius - 2), DateTime.MaxValue);

var clockspot = _config.P2Banish2SpreadSpots[assignment];
if (clockspot >= 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ class LateralZantetsuken1(BossModule module) : LateralZantetsuken(module, AID.La
class LateralZantetsuken2(BossModule module) : LateralZantetsuken(module, AID.LateralZantetsuken2);

class BitterBarbs(BossModule module) : Components.Chains(module, (uint)TetherID.Chains, ActionID.MakeSpell(AID.BitterBarbs));
class BoomingLament(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.BoomingLament), new AOEShapeCircle(10));
class SilentLevin(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.SilentLevin), new AOEShapeCircle(5));
class BoomingLament(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.BoomingLament), 10);
class SilentLevin(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.SilentLevin), 5);

class UltimateZantetsuken(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.UltimateZantetsuken), "Enrage, kill the adds!", true);

Expand Down

0 comments on commit 08313df

Please sign in to comment.